183 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			183 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
| import 'package:flutter/material.dart';
 | |
| import 'package:flutter/services.dart';
 | |
| import 'package:flutter_cache_manager/flutter_cache_manager.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/websocket.dart';
 | |
| import 'package:island/services/update_service.dart';
 | |
| import 'package:island/widgets/alert.dart';
 | |
| import 'package:island/widgets/content/network_status_sheet.dart';
 | |
| import 'package:island/widgets/content/sheet.dart';
 | |
| import 'package:material_symbols_icons/symbols.dart';
 | |
| import 'package:island/pods/config.dart';
 | |
| import 'package:talker_flutter/talker_flutter.dart';
 | |
| import 'package:island/talker.dart';
 | |
| 
 | |
| Future<void> _showSetTokenDialog(BuildContext context, WidgetRef ref) async {
 | |
|   final TextEditingController controller = TextEditingController();
 | |
|   final prefs = ref.read(sharedPreferencesProvider);
 | |
| 
 | |
|   return showDialog<void>(
 | |
|     context: context,
 | |
|     builder: (BuildContext context) {
 | |
|       return AlertDialog(
 | |
|         title: const Text('Set Access Token'),
 | |
|         content: TextField(
 | |
|           controller: controller,
 | |
|           decoration: const InputDecoration(
 | |
|             hintText: 'Enter access token',
 | |
|             border: OutlineInputBorder(),
 | |
|           ),
 | |
|           autofocus: true,
 | |
|         ),
 | |
|         actions: <Widget>[
 | |
|           TextButton(
 | |
|             child: const Text('Cancel'),
 | |
|             onPressed: () {
 | |
|               Navigator.of(context).pop();
 | |
|             },
 | |
|           ),
 | |
|           TextButton(
 | |
|             child: const Text('Set'),
 | |
|             onPressed: () async {
 | |
|               final token = controller.text.trim();
 | |
|               if (token.isNotEmpty) {
 | |
|                 await setToken(prefs, token);
 | |
|                 ref.invalidate(tokenProvider);
 | |
|                 // Store context in local variable to avoid async gap issue
 | |
|                 final navigatorContext = context;
 | |
|                 if (navigatorContext.mounted) {
 | |
|                   Navigator.of(navigatorContext).pop();
 | |
|                 }
 | |
|               }
 | |
|             },
 | |
|           ),
 | |
|         ],
 | |
|       );
 | |
|     },
 | |
|   );
 | |
| }
 | |
| 
 | |
| 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',
 | |
|       heightFactor: 0.6,
 | |
|       child: SingleChildScrollView(
 | |
|         child: Column(
 | |
|           children: [
 | |
|             const Gap(4),
 | |
|             ListTile(
 | |
|               minTileHeight: 48,
 | |
|               leading: const Icon(Symbols.update),
 | |
|               trailing: const Icon(Symbols.chevron_right),
 | |
|               title: Text('Force Update'),
 | |
|               contentPadding: const EdgeInsets.symmetric(horizontal: 24),
 | |
|               onTap: () async {
 | |
|                 // Fetch latest release and show the unified sheet
 | |
|                 final svc = UpdateService();
 | |
|                 // Reuse service fetch + compare to decide content
 | |
|                 showLoadingModal(context);
 | |
|                 final release = await svc.fetchLatestRelease();
 | |
|                 if (!context.mounted) return;
 | |
|                 hideLoadingModal(context);
 | |
|                 if (release != null) {
 | |
|                   await svc.showUpdateSheet(context, release);
 | |
|                 } else {
 | |
|                   showInfoAlert(
 | |
|                     'Currently cannot get update from the GitHub.',
 | |
|                     'Unable to check for updates',
 | |
|                   );
 | |
|                 }
 | |
|               }
 | |
|             ),
 | |
|             const Divider(height: 8),
 | |
|             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: 8),
 | |
|             ListTile(
 | |
|               minTileHeight: 48,
 | |
|               leading: const Icon(Symbols.bug_report),
 | |
|               trailing: const Icon(Symbols.chevron_right),
 | |
|               title: Text('Logs'),
 | |
|               contentPadding: EdgeInsets.symmetric(horizontal: 24),
 | |
|               onTap: () {
 | |
|                 Navigator.of(context).push(
 | |
|                   MaterialPageRoute(
 | |
|                     builder: (context) => TalkerScreen(talker: talker),
 | |
|                   ),
 | |
|                 );
 | |
|               },
 | |
|             ),
 | |
|             const Divider(height: 8),
 | |
|             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.edit),
 | |
|               trailing: const Icon(Symbols.chevron_right),
 | |
|               contentPadding: EdgeInsets.symmetric(horizontal: 24),
 | |
|               title: Text('Set access token'),
 | |
|               onTap: () async {
 | |
|                 await _showSetTokenDialog(context, ref);
 | |
|               },
 | |
|             ),
 | |
|             const Divider(height: 8),
 | |
|             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();
 | |
|               },
 | |
|             ),
 | |
|           ],
 | |
|         ),
 | |
|       ),
 | |
|     );
 | |
|   }
 | |
| }
 |