Debug options

This commit is contained in:
2025-08-09 00:44:37 +08:00
parent 5e1a15ada2
commit bb00b1bc6a
4 changed files with 320 additions and 249 deletions

View File

@@ -786,5 +786,6 @@
"links": "Links", "links": "Links",
"addLink": "Add link", "addLink": "Add link",
"linkKey": "Link Name", "linkKey": "Link Name",
"linkValue": "URL" "linkValue": "URL",
"debugOptions": "Debug Options"
} }

View File

@@ -102,235 +102,243 @@ class _AboutScreenState extends ConsumerState<AboutScreen> {
? const Center(child: CircularProgressIndicator()) ? const Center(child: CircularProgressIndicator())
: _errorMessage != null : _errorMessage != null
? Center(child: Text(_errorMessage!)) ? Center(child: Text(_errorMessage!))
: SingleChildScrollView( : Center(
child: Column( child: ConstrainedBox(
crossAxisAlignment: CrossAxisAlignment.center, constraints: const BoxConstraints(maxWidth: 540),
children: [ child: SingleChildScrollView(
const SizedBox(height: 24), child: Column(
// App Icon and Name crossAxisAlignment: CrossAxisAlignment.center,
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(),
children: [ children: [
_buildInfoItem( const SizedBox(height: 24),
context, // App Icon and Name
icon: Symbols.info, CircleAvatar(
label: 'aboutScreenPackageNameLabel'.tr(), radius: 50,
value: _packageInfo.packageName, backgroundColor: theme.colorScheme.primary
), .withOpacity(0.1),
_buildInfoItem( child: Image.asset(
context, 'assets/icons/icon.png',
icon: Symbols.update, width: 56,
label: 'aboutScreenVersionLabel'.tr(), height: 56,
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, const SizedBox(height: 16),
icon: Symbols.fingerprint, Text(
label: 'aboutDeviceIdentifier'.tr(), _packageInfo.appName,
value: _deviceUdid ?? 'N/A', style: theme.textTheme.headlineSmall?.copyWith(
copyable: true, 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), // App Info Card
_buildSection(
// Links Card
_buildSection(
context,
title: 'aboutScreenLinksSectionTitle'.tr(),
children: [
_buildListTile(
context, context,
icon: Symbols.system_update, title: 'aboutScreenAppInfoSectionTitle'.tr(),
title: 'Check for updates', children: [
onTap: () async { _buildInfoItem(
// Fetch latest release and show the unified sheet context,
final svc = UpdateService(); icon: Symbols.info,
// Reuse service fetch + compare to decide content label: 'aboutScreenPackageNameLabel'.tr(),
final release = await svc.fetchLatestRelease(); value: _packageInfo.packageName,
if (release != null) { ),
await svc.showUpdateSheet(context, release); _buildInfoItem(
} else { context,
// Fallback: show a simple sheet indicating no info icon: Symbols.update,
// Use your SheetScaffold for consistent styling label: 'aboutScreenVersionLabel'.tr(),
// Show a minimal message value: _packageInfo.version,
// ignore: use_build_context_synchronously ),
showModalBottomSheet( _buildInfoItem(
context: context, context,
isScrollControlled: true, icon: Symbols.build,
useSafeArea: true, label: 'aboutScreenBuildNumberLabel'.tr(),
showDragHandle: true, value: _packageInfo.buildNumber,
backgroundColor: ),
Theme.of(context).colorScheme.surface, ],
builder: ),
(_) => const SheetScaffold(
titleText: 'Update', if (_deviceInfo != null) const SizedBox(height: 16),
child: Center(
child: Padding( if (_deviceInfo != null)
padding: EdgeInsets.all(24), _buildSection(
child: Text( context,
'Unable to fetch release info at this time.', 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, _buildListTile(
textAlign: TextAlign.center, context,
), icon: Symbols.privacy_tip,
const Gap(1), title: 'aboutScreenPrivacyPolicyTitle'.tr(),
Text( onTap:
'aboutScreenMadeWith'.tr(), () => _launchURL(
textAlign: TextAlign.center, 'https://solsynth.dev/terms/privacy-policy',
).fontSize(10).opacity(0.8), ),
], ),
), _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),
],
),
),
), ),
), ),
); );

View File

@@ -1,12 +1,8 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:flutter/services.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.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/pods/userinfo.dart';
import 'package:island/screens/notification.dart'; import 'package:island/screens/notification.dart';
import 'package:island/services/responsive.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/account/leveling_progress.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/content/cloud_files.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:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
@@ -276,30 +273,6 @@ class AccountScreen extends HookConsumerWidget {
context.pushNamed('accountSettings'); 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), const Divider(height: 1).padding(vertical: 8),
ListTile( ListTile(
minTileHeight: 48, minTileHeight: 48,
@@ -311,6 +284,19 @@ class AccountScreen extends HookConsumerWidget {
context.pushNamed('about'); 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( ListTile(
minTileHeight: 48, minTileHeight: 48,
leading: const Icon(Symbols.logout), leading: const Icon(Symbols.logout),

View File

@@ -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();
},
),
],
),
);
}
}