From ddf7f543ffb5e6d5fd90f2f756dcf80a4f697be6 Mon Sep 17 00:00:00 2001 From: stopflock Date: Sat, 14 Mar 2026 18:04:57 -0500 Subject: [PATCH] Button to trigger nuke reset in dev mode --- COMMENT | 11 -- lib/screens/about_screen.dart | 235 ++++++++++++++++++++++++++++++++++ 2 files changed, 235 insertions(+), 11 deletions(-) delete mode 100644 COMMENT diff --git a/COMMENT b/COMMENT deleted file mode 100644 index 4a57ce4..0000000 --- a/COMMENT +++ /dev/null @@ -1,11 +0,0 @@ ---- -An alternative approach to addressing this issue could be adjusting the `optionsBuilder` logic to avoid returning any suggestions when the input text field is empty, rather than guarding `onFieldSubmitted`. For instance: - -```dart -optionsBuilder: (TextEditingValue textEditingValue) { - if (textEditingValue.text.isEmpty) return []; - return suggestions.where((s) => s.contains(textEditingValue.text)); -} -``` - -This ensures that the `RawAutocomplete` widget doesn't offer any options to auto-select on submission when the field is cleared, potentially simplifying the implementation and avoiding the need for additional boolean flags (`guardOnSubmitted`). This pattern can be seen in some implementations "in the wild." \ No newline at end of file diff --git a/lib/screens/about_screen.dart b/lib/screens/about_screen.dart index 94e0fef..1554b55 100644 --- a/lib/screens/about_screen.dart +++ b/lib/screens/about_screen.dart @@ -1,6 +1,8 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; import '../services/localization_service.dart'; +import '../services/nuclear_reset_service.dart'; import '../widgets/welcome_dialog.dart'; import '../widgets/submission_guide_dialog.dart'; @@ -83,6 +85,13 @@ class AboutScreen extends StatelessWidget { _buildDialogButtons(context), const SizedBox(height: 24), _buildHelpLinks(context), + + // Dev-only nuclear reset button at very bottom + if (kDebugMode) ...[ + const SizedBox(height: 32), + _buildDevNuclearResetButton(context), + const SizedBox(height: 16), + ], ], ), ), @@ -203,5 +212,231 @@ class AboutScreen extends StatelessWidget { ); } + /// Dev-only nuclear reset button (only visible in debug mode) + Widget _buildDevNuclearResetButton(BuildContext context) { + return Card( + color: Theme.of(context).colorScheme.errorContainer.withOpacity(0.1), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + Icons.developer_mode, + color: Theme.of(context).colorScheme.primary, + size: 20, + ), + const SizedBox(width: 8), + Text( + 'Developer Tools', + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: Theme.of(context).colorScheme.primary, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + const SizedBox(height: 12), + Text( + 'These tools are only available in debug mode for development and troubleshooting.', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7), + ), + ), + const SizedBox(height: 16), + SizedBox( + width: double.infinity, + child: OutlinedButton.icon( + onPressed: () => _showNuclearResetConfirmation(context), + icon: const Icon(Icons.delete_forever, color: Colors.red), + label: const Text( + 'Nuclear Reset (Clear All Data)', + style: TextStyle(color: Colors.red), + ), + style: OutlinedButton.styleFrom( + side: const BorderSide(color: Colors.red), + ), + ), + ), + ], + ), + ), + ); + } + + /// Show confirmation dialog for nuclear reset + Future _showNuclearResetConfirmation(BuildContext context) async { + final confirmed = await showDialog( + context: context, + barrierDismissible: false, + builder: (context) => AlertDialog( + title: const Row( + children: [ + Icon(Icons.warning, color: Colors.red), + SizedBox(width: 8), + Text('Nuclear Reset'), + ], + ), + content: const Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'This will completely clear ALL app data:', + style: TextStyle(fontWeight: FontWeight.w500), + ), + SizedBox(height: 12), + Text('• All settings and preferences'), + Text('• OAuth login credentials'), + Text('• Custom profiles and operators'), + Text('• Upload queue and cached data'), + Text('• Downloaded offline areas'), + Text('• Everything else'), + SizedBox(height: 16), + Text( + 'The app will behave exactly like a fresh install after this operation.', + style: TextStyle( + fontWeight: FontWeight.w500, + color: Colors.red, + ), + ), + SizedBox(height: 12), + Text( + 'This action cannot be undone.', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(false), + child: const Text('Cancel'), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(true), + style: TextButton.styleFrom(foregroundColor: Colors.red), + child: const Text('Nuclear Reset'), + ), + ], + ), + ); + + if (confirmed == true && context.mounted) { + await _performNuclearReset(context); + } + } + + /// Perform the nuclear reset operation + Future _performNuclearReset(BuildContext context) async { + // Show progress dialog + if (!context.mounted) return; + + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => const AlertDialog( + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + CircularProgressIndicator(), + SizedBox(height: 16), + Text('Clearing all app data...'), + ], + ), + ), + ); + + try { + // Perform the nuclear reset + await NuclearResetService.clearEverything(); + + if (!context.mounted) return; + + // Close progress dialog + Navigator.of(context).pop(); + + // Show completion dialog + await showDialog( + context: context, + barrierDismissible: false, + builder: (context) => AlertDialog( + title: const Row( + children: [ + Icon(Icons.check_circle, color: Colors.green), + SizedBox(width: 8), + Text('Reset Complete'), + ], + ), + content: const Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'All app data has been cleared successfully.', + style: TextStyle(fontWeight: FontWeight.w500), + ), + SizedBox(height: 12), + Text( + 'Please close and restart the app to continue with a fresh state.', + style: TextStyle(fontWeight: FontWeight.w500), + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('OK'), + ), + ], + ), + ); + } catch (e) { + if (!context.mounted) return; + + // Close progress dialog if it's still open + Navigator.of(context).pop(); + + // Show error dialog + await showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Row( + children: [ + Icon(Icons.error, color: Colors.red), + SizedBox(width: 8), + Text('Reset Failed'), + ], + ), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'An error occurred during the nuclear reset:', + style: TextStyle(fontWeight: FontWeight.w500), + ), + const SizedBox(height: 12), + Text( + e.toString(), + style: const TextStyle(fontFamily: 'monospace'), + ), + const SizedBox(height: 12), + const Text( + 'Some data may have been partially cleared. You may want to manually clear app data through device settings.', + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('OK'), + ), + ], + ), + ); + } + } } \ No newline at end of file