From bb00b1bc6acb8991bb552af23547fd272fcb2396 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sat, 9 Aug 2025 00:44:37 +0800 Subject: [PATCH] :sparkles: Debug options --- assets/i18n/en-US.json | 3 +- lib/screens/about.dart | 448 ++++++++++++++++++----------------- lib/screens/account.dart | 42 ++-- lib/widgets/debug_sheet.dart | 76 ++++++ 4 files changed, 320 insertions(+), 249 deletions(-) create mode 100644 lib/widgets/debug_sheet.dart diff --git a/assets/i18n/en-US.json b/assets/i18n/en-US.json index dbd66f8..f0cb1ea 100644 --- a/assets/i18n/en-US.json +++ b/assets/i18n/en-US.json @@ -786,5 +786,6 @@ "links": "Links", "addLink": "Add link", "linkKey": "Link Name", - "linkValue": "URL" + "linkValue": "URL", + "debugOptions": "Debug Options" } diff --git a/lib/screens/about.dart b/lib/screens/about.dart index 55d3862..533c306 100644 --- a/lib/screens/about.dart +++ b/lib/screens/about.dart @@ -102,235 +102,243 @@ class _AboutScreenState extends ConsumerState { ? const Center(child: CircularProgressIndicator()) : _errorMessage != null ? Center(child: Text(_errorMessage!)) - : SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const SizedBox(height: 24), - // App Icon and Name - CircleAvatar( - radius: 50, - backgroundColor: theme.colorScheme.primary.withOpacity( - 0.1, - ), - child: Image.asset( - 'assets/icons/icon.png', - width: 56, - height: 56, - ), - ), - const SizedBox(height: 16), - Text( - _packageInfo.appName, - style: theme.textTheme.headlineSmall?.copyWith( - fontWeight: FontWeight.bold, - ), - ), - Text( - 'aboutScreenVersionInfo'.tr( - args: [_packageInfo.version, _packageInfo.buildNumber], - ), - style: theme.textTheme.bodyMedium?.copyWith( - color: theme.textTheme.bodySmall?.color, - ), - ), - const SizedBox(height: 32), - - // App Info Card - _buildSection( - context, - title: 'aboutScreenAppInfoSectionTitle'.tr(), + : Center( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 540), + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, children: [ - _buildInfoItem( - context, - icon: Symbols.info, - label: 'aboutScreenPackageNameLabel'.tr(), - value: _packageInfo.packageName, - ), - _buildInfoItem( - context, - icon: Symbols.update, - label: 'aboutScreenVersionLabel'.tr(), - value: _packageInfo.version, - ), - _buildInfoItem( - context, - icon: Symbols.build, - label: 'aboutScreenBuildNumberLabel'.tr(), - value: _packageInfo.buildNumber, - ), - ], - ), - - if (_deviceInfo != null) const SizedBox(height: 16), - - if (_deviceInfo != null) - _buildSection( - context, - title: 'Device Information', - children: [ - _buildInfoItem( - context, - icon: Symbols.label, - label: 'aboutDeviceName'.tr(), - value: _deviceInfo?.data['name'], + const SizedBox(height: 24), + // App Icon and Name + CircleAvatar( + radius: 50, + backgroundColor: theme.colorScheme.primary + .withOpacity(0.1), + child: Image.asset( + 'assets/icons/icon.png', + width: 56, + height: 56, ), - _buildInfoItem( - context, - icon: Symbols.fingerprint, - label: 'aboutDeviceIdentifier'.tr(), - value: _deviceUdid ?? 'N/A', - copyable: true, + ), + const SizedBox(height: 16), + Text( + _packageInfo.appName, + style: theme.textTheme.headlineSmall?.copyWith( + fontWeight: FontWeight.bold, ), - ], - ), + ), + Text( + 'aboutScreenVersionInfo'.tr( + args: [ + _packageInfo.version, + _packageInfo.buildNumber, + ], + ), + style: theme.textTheme.bodyMedium?.copyWith( + color: theme.textTheme.bodySmall?.color, + ), + ), + const SizedBox(height: 32), - const SizedBox(height: 16), - - // Links Card - _buildSection( - context, - title: 'aboutScreenLinksSectionTitle'.tr(), - children: [ - _buildListTile( + // App Info Card + _buildSection( context, - icon: Symbols.system_update, - title: 'Check for updates', - onTap: () async { - // Fetch latest release and show the unified sheet - final svc = UpdateService(); - // Reuse service fetch + compare to decide content - final release = await svc.fetchLatestRelease(); - if (release != null) { - await svc.showUpdateSheet(context, release); - } else { - // Fallback: show a simple sheet indicating no info - // Use your SheetScaffold for consistent styling - // Show a minimal message - // ignore: use_build_context_synchronously - showModalBottomSheet( - context: context, - isScrollControlled: true, - useSafeArea: true, - showDragHandle: true, - backgroundColor: - Theme.of(context).colorScheme.surface, - builder: - (_) => const SheetScaffold( - titleText: 'Update', - child: Center( - child: Padding( - padding: EdgeInsets.all(24), - child: Text( - 'Unable to fetch release info at this time.', + title: 'aboutScreenAppInfoSectionTitle'.tr(), + children: [ + _buildInfoItem( + context, + icon: Symbols.info, + label: 'aboutScreenPackageNameLabel'.tr(), + value: _packageInfo.packageName, + ), + _buildInfoItem( + context, + icon: Symbols.update, + label: 'aboutScreenVersionLabel'.tr(), + value: _packageInfo.version, + ), + _buildInfoItem( + context, + icon: Symbols.build, + label: 'aboutScreenBuildNumberLabel'.tr(), + value: _packageInfo.buildNumber, + ), + ], + ), + + if (_deviceInfo != null) const SizedBox(height: 16), + + if (_deviceInfo != null) + _buildSection( + context, + title: 'Device Information', + children: [ + _buildInfoItem( + context, + icon: Symbols.label, + label: 'aboutDeviceName'.tr(), + value: _deviceInfo?.data['name'], + ), + _buildInfoItem( + context, + icon: Symbols.fingerprint, + label: 'aboutDeviceIdentifier'.tr(), + value: _deviceUdid ?? 'N/A', + copyable: true, + ), + ], + ), + + const SizedBox(height: 16), + + // Links Card + _buildSection( + context, + title: 'aboutScreenLinksSectionTitle'.tr(), + children: [ + _buildListTile( + context, + icon: Symbols.system_update, + title: 'Check for updates', + onTap: () async { + // Fetch latest release and show the unified sheet + final svc = UpdateService(); + // Reuse service fetch + compare to decide content + final release = await svc.fetchLatestRelease(); + if (release != null) { + await svc.showUpdateSheet(context, release); + } else { + // Fallback: show a simple sheet indicating no info + // Use your SheetScaffold for consistent styling + // Show a minimal message + // ignore: use_build_context_synchronously + showModalBottomSheet( + context: context, + isScrollControlled: true, + useSafeArea: true, + showDragHandle: true, + backgroundColor: + Theme.of(context).colorScheme.surface, + builder: + (_) => const SheetScaffold( + titleText: 'Update', + child: Center( + child: Padding( + padding: EdgeInsets.all(24), + child: Text( + 'Unable to fetch release info at this time.', + ), + ), ), ), - ), - ), - ); - } - }, - ), - _buildListTile( - context, - icon: Symbols.privacy_tip, - title: 'aboutScreenPrivacyPolicyTitle'.tr(), - onTap: - () => _launchURL( - 'https://solsynth.dev/terms/privacy-policy', - ), - ), - _buildListTile( - context, - icon: Symbols.description, - title: 'aboutScreenTermsOfServiceTitle'.tr(), - onTap: - () => _launchURL( - 'https://solsynth.dev/terms/user-agreement', - ), - ), - _buildListTile( - context, - icon: Symbols.code, - title: 'aboutScreenOpenSourceLicensesTitle'.tr(), - onTap: () { - showLicensePage( - context: context, - applicationName: _packageInfo.appName, - applicationVersion: - 'Version ${_packageInfo.version}', - ); - }, - ), - ], - ), - - const SizedBox(height: 16), - - // Developer Info - _buildSection( - context, - title: 'aboutScreenDeveloperSectionTitle'.tr(), - children: [ - _buildListTile( - context, - icon: Symbols.email, - title: 'aboutScreenContactUsTitle'.tr(), - subtitle: 'lily@solsynth.dev', - onTap: () => _launchURL('mailto:lily@solsynth.dev'), - ), - _buildListTile( - context, - icon: Symbols.copyright, - title: 'aboutScreenLicenseTitle'.tr(), - subtitle: 'aboutScreenLicenseContent'.tr( - args: [DateTime.now().year.toString()], - ), - onTap: - () => _launchURL( - 'https://github.com/Solsynth/Solian/blob/v3/LICENSE.txt', - ), - ), - if (kIsWeb || !(Platform.isMacOS || Platform.isIOS)) - _buildListTile( - context, - icon: Symbols.favorite, - title: 'donate'.tr(), - subtitle: 'donateDescription'.tr(), - onTap: () { - launchUrlString( - 'https://afdian.com/@littlesheep', - ); - }, - ), - ], - ), - - const SizedBox(height: 32), - - // Copyright - Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - children: [ - Text( - 'aboutScreenCopyright'.tr( - args: [DateTime.now().year.toString()], + ); + } + }, ), - style: theme.textTheme.bodySmall, - textAlign: TextAlign.center, - ), - const Gap(1), - Text( - 'aboutScreenMadeWith'.tr(), - textAlign: TextAlign.center, - ).fontSize(10).opacity(0.8), - ], - ), - ), + _buildListTile( + context, + icon: Symbols.privacy_tip, + title: 'aboutScreenPrivacyPolicyTitle'.tr(), + onTap: + () => _launchURL( + 'https://solsynth.dev/terms/privacy-policy', + ), + ), + _buildListTile( + context, + icon: Symbols.description, + title: 'aboutScreenTermsOfServiceTitle'.tr(), + onTap: + () => _launchURL( + 'https://solsynth.dev/terms/user-agreement', + ), + ), + _buildListTile( + context, + icon: Symbols.code, + title: 'aboutScreenOpenSourceLicensesTitle'.tr(), + onTap: () { + showLicensePage( + context: context, + applicationName: _packageInfo.appName, + applicationVersion: + 'Version ${_packageInfo.version}', + ); + }, + ), + ], + ), - Gap(MediaQuery.of(context).padding.bottom + 16), - ], + const SizedBox(height: 16), + + // Developer Info + _buildSection( + context, + title: 'aboutScreenDeveloperSectionTitle'.tr(), + children: [ + _buildListTile( + context, + icon: Symbols.email, + title: 'aboutScreenContactUsTitle'.tr(), + subtitle: 'lily@solsynth.dev', + onTap: + () => _launchURL('mailto:lily@solsynth.dev'), + ), + _buildListTile( + context, + icon: Symbols.copyright, + title: 'aboutScreenLicenseTitle'.tr(), + subtitle: 'aboutScreenLicenseContent'.tr( + args: [DateTime.now().year.toString()], + ), + onTap: + () => _launchURL( + 'https://github.com/Solsynth/Solian/blob/v3/LICENSE.txt', + ), + ), + if (kIsWeb || !(Platform.isMacOS || Platform.isIOS)) + _buildListTile( + context, + icon: Symbols.favorite, + title: 'donate'.tr(), + subtitle: 'donateDescription'.tr(), + onTap: () { + launchUrlString( + 'https://afdian.com/@littlesheep', + ); + }, + ), + ], + ), + + const SizedBox(height: 32), + + // Copyright + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + Text( + 'aboutScreenCopyright'.tr( + args: [DateTime.now().year.toString()], + ), + style: theme.textTheme.bodySmall, + textAlign: TextAlign.center, + ), + const Gap(1), + Text( + 'aboutScreenMadeWith'.tr(), + textAlign: TextAlign.center, + ).fontSize(10).opacity(0.8), + ], + ), + ), + + Gap(MediaQuery.of(context).padding.bottom + 16), + ], + ), + ), ), ), ); diff --git a/lib/screens/account.dart b/lib/screens/account.dart index 65702e8..4850286 100644 --- a/lib/screens/account.dart +++ b/lib/screens/account.dart @@ -1,12 +1,8 @@ import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; -import 'package:flutter/services.dart'; import 'package:gap/gap.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:island/pods/message.dart'; -import 'package:island/pods/network.dart'; import 'package:island/pods/userinfo.dart'; import 'package:island/screens/notification.dart'; import 'package:island/services/responsive.dart'; @@ -15,6 +11,7 @@ import 'package:island/widgets/account/status.dart'; import 'package:island/widgets/account/leveling_progress.dart'; import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/content/cloud_files.dart'; +import 'package:island/widgets/debug_sheet.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:styled_widget/styled_widget.dart'; @@ -276,30 +273,6 @@ class AccountScreen extends HookConsumerWidget { context.pushNamed('accountSettings'); }, ), - if (kDebugMode) const Divider(height: 1).padding(vertical: 8), - if (kDebugMode) - ListTile( - minTileHeight: 48, - leading: const Icon(Symbols.copy_all), - trailing: const Icon(Symbols.chevron_right), - contentPadding: EdgeInsets.symmetric(horizontal: 24), - title: Text('Copy access token'), - onTap: () async { - final tk = ref.watch(tokenProvider); - Clipboard.setData(ClipboardData(text: tk!.token)); - }, - ), - if (kDebugMode) - ListTile( - minTileHeight: 48, - leading: const Icon(Symbols.delete), - trailing: const Icon(Symbols.chevron_right), - contentPadding: EdgeInsets.symmetric(horizontal: 24), - title: Text('Reset database'), - onTap: () async { - resetDatabase(ref); - }, - ), const Divider(height: 1).padding(vertical: 8), ListTile( minTileHeight: 48, @@ -311,6 +284,19 @@ class AccountScreen extends HookConsumerWidget { context.pushNamed('about'); }, ), + ListTile( + minTileHeight: 48, + leading: const Icon(Symbols.bug_report), + trailing: const Icon(Symbols.chevron_right), + contentPadding: EdgeInsets.symmetric(horizontal: 24), + title: Text('debugOptions').tr(), + onTap: () { + showModalBottomSheet( + context: context, + builder: (context) => DebugSheet(), + ); + }, + ), ListTile( minTileHeight: 48, leading: const Icon(Symbols.logout), diff --git a/lib/widgets/debug_sheet.dart b/lib/widgets/debug_sheet.dart new file mode 100644 index 0000000..7487718 --- /dev/null +++ b/lib/widgets/debug_sheet.dart @@ -0,0 +1,76 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:island/pods/message.dart'; +import 'package:island/pods/network.dart'; +import 'package:island/pods/websocket.dart'; +import 'package:island/widgets/content/network_status_sheet.dart'; +import 'package:island/widgets/content/sheet.dart'; +import 'package:material_symbols_icons/symbols.dart'; + +class DebugSheet extends HookConsumerWidget { + const DebugSheet({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final wsNotifier = ref.watch(websocketStateProvider.notifier); + + return SheetScaffold( + titleText: 'Debug', + child: Column( + children: [ + ListTile( + minTileHeight: 48, + leading: const Icon(Symbols.wifi), + trailing: const Icon(Symbols.chevron_right), + title: Text('Connection Status'), + contentPadding: EdgeInsets.symmetric(horizontal: 24), + onTap: () { + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: + (context) => NetworkStatusSheet( + onReconnect: () => wsNotifier.connect(), + ), + ); + }, + ), + const Divider(height: 1), + ListTile( + minTileHeight: 48, + leading: const Icon(Symbols.copy_all), + trailing: const Icon(Symbols.chevron_right), + contentPadding: EdgeInsets.symmetric(horizontal: 24), + title: Text('Copy access token'), + onTap: () async { + final tk = ref.watch(tokenProvider); + Clipboard.setData(ClipboardData(text: tk!.token)); + }, + ), + ListTile( + minTileHeight: 48, + leading: const Icon(Symbols.delete), + trailing: const Icon(Symbols.chevron_right), + contentPadding: EdgeInsets.symmetric(horizontal: 24), + title: Text('Reset database'), + onTap: () async { + resetDatabase(ref); + }, + ), + ListTile( + minTileHeight: 48, + leading: const Icon(Symbols.clear), + trailing: const Icon(Symbols.chevron_right), + contentPadding: EdgeInsets.symmetric(horizontal: 24), + title: Text('Clear cache'), + onTap: () async { + DefaultCacheManager().emptyCache(); + }, + ), + ], + ), + ); + } +}