import 'package:flutter/material.dart'; import '../application/app_bootstrap.dart'; import '../domain/domain.dart'; import 'exercise_library_screen.dart'; import 'history_screen.dart'; import 'profile_screen.dart'; import 'program_screen.dart'; import 'theme.dart'; import 'workout_execution_screen.dart'; import 'workout_template_screen.dart'; final homeRouteObserver = RouteObserver>(); final class HomeScreen extends StatefulWidget { const HomeScreen({ required this.bootstrap, this.themeMode = ThemeMode.system, this.onThemeModeChanged, super.key, }); final AppDependencies bootstrap; final ThemeMode themeMode; final ValueChanged? onThemeModeChanged; @override State createState() => _HomeScreenState(); } final class _HomeScreenState extends State with RouteAware { late Future _openSession; ModalRoute? _route; @override void initState() { super.initState(); _openSession = _findOpenSession(); } @override void didChangeDependencies() { super.didChangeDependencies(); final route = ModalRoute.of(context); if (route == null || identical(route, _route)) { return; } final previousRoute = _route; if (previousRoute != null) { homeRouteObserver.unsubscribe(this); } _route = route; homeRouteObserver.subscribe(this, route); } @override void dispose() { homeRouteObserver.unsubscribe(this); super.dispose(); } @override void didPopNext() { _reloadOpenSession(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const GameTimeLogo(), actions: [ PopupMenuButton( tooltip: 'Thème', initialValue: widget.themeMode, icon: const Icon(Icons.contrast), onSelected: widget.onThemeModeChanged, itemBuilder: (context) => const [ PopupMenuItem(value: ThemeMode.system, child: Text('Système')), PopupMenuItem(value: ThemeMode.light, child: Text('Clair')), PopupMenuItem(value: ThemeMode.dark, child: Text('Sombre')), ], ), ], ), body: ListView( children: [ FutureBuilder( future: _openSession, builder: (context, snapshot) { final session = snapshot.data; if (session == null) { return const SizedBox.shrink(); } final plan = WorkoutExecutionPlan.fromSession(session); final position = ExecutionPosition( programIndex: session.currentProgramIndex, exerciseIndex: session.currentExerciseIndex, setIndex: session.currentSetIndex, ); final exercise = plan.exerciseAt(position); return CourtBlazerAccentPanel( margin: const EdgeInsets.all(12), child: MaterialBanner( content: Text( 'Séance en cours — Reprendre · ${exercise.name}, ' 'série ${position.setIndex + 1}/${exercise.setsCount}', ), actions: [ TextButton( onPressed: () => _resume(session), child: const Text('Reprendre'), ), ], ), ); }, ), ListTile( leading: const Icon(Icons.fitness_center), title: const Text('Exercices'), trailing: const Icon(Icons.chevron_right), onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (context) => ExerciseLibraryScreen( exerciseUseCases: widget.bootstrap.exerciseUseCases, mediaUseCases: widget.bootstrap.mediaUseCases, ), ), ), ), ListTile( leading: const Icon(Icons.list_alt), title: const Text('Programmes'), trailing: const Icon(Icons.chevron_right), onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (context) => ProgramListScreen( programUseCases: widget.bootstrap.programUseCases, exerciseUseCases: widget.bootstrap.exerciseUseCases, ), ), ), ), ListTile( leading: const Icon(Icons.event_note), title: const Text('Séances'), trailing: const Icon(Icons.chevron_right), onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (context) => WorkoutTemplateListScreen( workoutTemplateUseCases: widget.bootstrap.workoutTemplateUseCases, programUseCases: widget.bootstrap.programUseCases, activeUseCases: widget.bootstrap.activeWorkoutSessionUseCases, stepUseCases: widget.bootstrap.activeExerciseStepUseCases, closeUseCase: widget.bootstrap.closeWorkoutSessionUseCase, historyUseCases: widget.bootstrap.workoutHistoryUseCases, mediaUseCases: widget.bootstrap.mediaUseCases, ), ), ), ), ListTile( leading: const Icon(Icons.history), title: const Text('Historique'), trailing: const Icon(Icons.chevron_right), onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (context) => HistoryListScreen( historyUseCases: widget.bootstrap.workoutHistoryUseCases, workoutTemplateUseCases: widget.bootstrap.workoutTemplateUseCases, activeUseCases: widget.bootstrap.activeWorkoutSessionUseCases, stepUseCases: widget.bootstrap.activeExerciseStepUseCases, closeUseCase: widget.bootstrap.closeWorkoutSessionUseCase, mediaUseCases: widget.bootstrap.mediaUseCases, ), ), ), ), ListTile( leading: FutureBuilder( future: widget.bootstrap.authUseCases.currentSession(), builder: (context, snapshot) { final session = snapshot.data; if (session == null || !session.isLoggedIn) { return const Icon(Icons.account_circle_outlined); } final displayName = session.displayName?.trim(); final label = displayName == null || displayName.isEmpty ? session.email : displayName; return CircleAvatar( radius: 12, child: Text(_profileInitials(label)), ); }, ), title: const Text('Profil'), subtitle: const Text('Compte, synchronisation et partages'), trailing: const Icon(Icons.chevron_right), onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (context) => ProfileScreen( authUseCases: widget.bootstrap.authUseCases, syncUseCases: widget.bootstrap.syncUseCases, ), ), ), ), ], ), ); } Future _resume(ActiveWorkoutSession session) async { await Navigator.of(context).push( MaterialPageRoute( builder: (context) => WorkoutExecutionScreen( initialSession: session, activeUseCases: widget.bootstrap.activeWorkoutSessionUseCases, stepUseCases: widget.bootstrap.activeExerciseStepUseCases, closeUseCase: widget.bootstrap.closeWorkoutSessionUseCase, historyUseCases: widget.bootstrap.workoutHistoryUseCases, workoutTemplateUseCases: widget.bootstrap.workoutTemplateUseCases, mediaUseCases: widget.bootstrap.mediaUseCases, ), ), ); if (!mounted) return; _reloadOpenSession(); } Future _findOpenSession() { return widget.bootstrap.activeWorkoutSessionUseCases.findOpen(); } void _reloadOpenSession() { setState(() { _openSession = _findOpenSession(); }); } } String _profileInitials(String value) { final trimmed = value.trim(); if (trimmed.isEmpty) return '?'; final words = trimmed.split(RegExp(r'\s+')); if (words.length == 1) { return words.first.substring(0, 1).toUpperCase(); } return '${words.first.substring(0, 1)}${words.last.substring(0, 1)}' .toUpperCase(); }