import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../data/bible.dart'; import '../models/tracker.dart'; import '../services/last_location.dart'; import '../services/tracker_service.dart'; import '../widgets/theme.dart'; import '../widgets/tracker_tile.dart'; import 'book_screen.dart'; import 'create_tracker_screen.dart'; import 'tracker_screen.dart'; class HomeScreen extends ConsumerStatefulWidget { const HomeScreen({super.key}); @override ConsumerState createState() => _HomeScreenState(); } class _HomeScreenState extends ConsumerState { bool _restoreAttempted = false; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) => _maybeRestore()); } void _maybeRestore() { if (_restoreAttempted || !mounted) return; _restoreAttempted = true; final LastLocation? pending = ref.read(pendingRestoreProvider); if (pending == null) return; final List trackers = ref.read(trackerServiceProvider); final Tracker? tracker = trackers.firstWhereOrNull( (Tracker t) => t.id == pending.trackerId, ); if (tracker == null) return; final NavigatorState navigator = Navigator.of(context); navigator.push( MaterialPageRoute( settings: RouteSettings(name: '/tracker/${tracker.id}'), builder: (_) => TrackerScreen(trackerId: tracker.id), ), ); final int? bookIndex = pending.bookIndex; if (bookIndex != null && bookIndex >= 0 && bookIndex < Bible.books.length) { navigator.push( MaterialPageRoute( settings: RouteSettings( name: '/tracker/${tracker.id}/book/$bookIndex', ), builder: (_) => BookScreen(trackerId: tracker.id, bookIndex: bookIndex), ), ); } } @override Widget build(BuildContext context) { final List trackers = ref.watch(trackerServiceProvider); return Scaffold( appBar: AppBar(title: const Text('My Bible Tracker')), body: trackers.isEmpty ? const _EmptyState() : ListView.separated( padding: const EdgeInsets.all(AppTokens.spaceL), itemCount: trackers.length, separatorBuilder: (_, _) => const SizedBox(height: AppTokens.spaceM), itemBuilder: (BuildContext context, int index) { final Tracker tracker = trackers[index]; return TrackerTile( tracker: tracker, onTap: () => Navigator.of(context).push( MaterialPageRoute( settings: RouteSettings(name: '/tracker/${tracker.id}'), builder: (_) => TrackerScreen(trackerId: tracker.id), ), ), onLongPress: () => _showTrackerMenu(context, ref, tracker), ); }, ), floatingActionButton: FloatingActionButton.extended( onPressed: () => Navigator.of(context).push( MaterialPageRoute(builder: (_) => const CreateTrackerScreen()), ), icon: const Icon(Icons.add), label: const Text('New Tracker'), ), ); } Future _showTrackerMenu( BuildContext context, WidgetRef ref, Tracker tracker, ) async { final _TrackerMenuAction? action = await showModalBottomSheet<_TrackerMenuAction>( context: context, showDragHandle: true, builder: (BuildContext context) { return SafeArea( child: Column( mainAxisSize: MainAxisSize.min, children: [ ListTile( leading: const Icon(Icons.edit_outlined), title: const Text('Rename / recolor'), onTap: () => Navigator.of(context).pop(_TrackerMenuAction.edit), ), ListTile( leading: const Icon(Icons.refresh), title: const Text('Reset progress'), onTap: () => Navigator.of(context).pop(_TrackerMenuAction.reset), ), ListTile( leading: const Icon(Icons.delete_outline), iconColor: Theme.of(context).colorScheme.error, textColor: Theme.of(context).colorScheme.error, title: const Text('Delete'), onTap: () => Navigator.of(context).pop(_TrackerMenuAction.delete), ), const SizedBox(height: AppTokens.spaceS), ], ), ); }, ); if (action == null || !context.mounted) return; switch (action) { case _TrackerMenuAction.edit: await Navigator.of(context).push( MaterialPageRoute( builder: (_) => CreateTrackerScreen(existing: tracker), ), ); case _TrackerMenuAction.reset: await _confirmAndRun( context, title: 'Reset progress?', message: 'All checked chapters for "${tracker.name}" will be cleared.', destructive: true, run: () => ref.read(trackerServiceProvider.notifier).reset(tracker.id), ); case _TrackerMenuAction.delete: await _confirmAndRun( context, title: 'Delete tracker?', message: '"${tracker.name}" will be removed permanently.', destructive: true, run: () => ref.read(trackerServiceProvider.notifier).delete(tracker.id), ); } } Future _confirmAndRun( BuildContext context, { required String title, required String message, required bool destructive, required Future Function() run, }) async { final bool? confirmed = await showDialog( context: context, builder: (BuildContext context) => AlertDialog( title: Text(title), content: Text(message), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), child: const Text('Cancel'), ), FilledButton( style: destructive ? FilledButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.error, ) : null, onPressed: () => Navigator.of(context).pop(true), child: const Text('Confirm'), ), ], ), ); if (confirmed ?? false) { await run(); } } } enum _TrackerMenuAction { edit, reset, delete } class _EmptyState extends StatelessWidget { const _EmptyState(); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(AppTokens.spaceXXL), child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.menu_book_outlined, size: 72, color: Theme.of(context).colorScheme.primary, ), const SizedBox(height: AppTokens.spaceL), Text( 'Start your first tracker', style: Theme.of(context).textTheme.titleLarge, textAlign: TextAlign.center, ), const SizedBox(height: AppTokens.spaceS), Text( 'Create a tracker to mark the chapters you have read. ' 'Read at your own pace — there are no deadlines.', textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Theme.of(context).colorScheme.onSurfaceVariant, ), ), ], ), ), ); } }