merge(main): bandeau de reprise de séance en cours (ticket #32)
Fusionne feature/#32-bandeau-reprise — analyze propre, 45/45 tests verts, build APK debug validé. Ticket laissé en statut QA : validation manuelle sur APK par l'utilisateur avant clôture. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
issueRef: "#32"
|
issueRef: "#32"
|
||||||
version: 4
|
version: 6
|
||||||
updatedBy: {"kind":"user"}
|
updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"}
|
||||||
updatedAt: 1784329025027
|
updatedAt: 1784361147125
|
||||||
---
|
---
|
||||||
|
Correctif : l'écran d'accueil recharge maintenant l'état de la séance active à chaque retour sur l'accueil (via RouteObserver/RouteAware.didPopNext), pas seulement au lancement de l'app. Fonctionne après "Quitter et sauvegarder" (bandeau apparaît) et après "Abandonner la séance" (bandeau reste absent). Vérifié : analyze propre, 45/45 tests verts, build APK réussi.
|
||||||
|
|
||||||
|
À tester sur le téléphone : lancer une séance, la mettre en pause et "Quitter et sauvegarder" sans redémarrer l'app — le bandeau "Séance en cours" doit apparaître immédiatement sur l'accueil.
|
||||||
@ -2,15 +2,15 @@
|
|||||||
id: "a3fe5103-8b5c-4cce-80af-f1dd08b76794"
|
id: "a3fe5103-8b5c-4cce-80af-f1dd08b76794"
|
||||||
number: 32
|
number: 32
|
||||||
title: "[Bug] Reprendre séance en cours pas toujours affiché"
|
title: "[Bug] Reprendre séance en cours pas toujours affiché"
|
||||||
status: "open"
|
status: "qa"
|
||||||
priority: "medium"
|
priority: "medium"
|
||||||
sprint: "abc4f969-b169-45f7-988c-daeeab762201"
|
sprint: "abc4f969-b169-45f7-988c-daeeab762201"
|
||||||
links: []
|
links: []
|
||||||
agentRefs: [{"agentId":"57695b92-24d0-4876-837c-76116e70a6ae","role":"assigned"}]
|
agentRefs: [{"agentId":"57695b92-24d0-4876-837c-76116e70a6ae","role":"assigned"}]
|
||||||
createdBy: {"kind":"user"}
|
createdBy: {"kind":"user"}
|
||||||
updatedBy: {"kind":"user"}
|
updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"}
|
||||||
createdAt: 1784328903547
|
createdAt: 1784328903547
|
||||||
updatedAt: 1784329025027
|
updatedAt: 1784361147125
|
||||||
version: 4
|
version: 6
|
||||||
---
|
---
|
||||||
Si je quitte une seance en cours, pour voir le bandeau de reprise de seance s'afficher je dois quitter totalement l'application et la relancer. J'aimerais qu'il y soit automatiquement quand une séance et en cours et que je ne suis pas dans ma séance
|
Si je quitte une seance en cours, pour voir le bandeau de reprise de seance s'afficher je dois quitter totalement l'application et la relancer. J'aimerais qu'il y soit automatiquement quand une séance et en cours et que je ne suis pas dans ma séance
|
||||||
@ -359,13 +359,13 @@
|
|||||||
"issueRef": "#32",
|
"issueRef": "#32",
|
||||||
"path": "32",
|
"path": "32",
|
||||||
"title": "[Bug] Reprendre séance en cours pas toujours affiché",
|
"title": "[Bug] Reprendre séance en cours pas toujours affiché",
|
||||||
"status": "open",
|
"status": "qa",
|
||||||
"priority": "medium",
|
"priority": "medium",
|
||||||
"sprint": "abc4f969-b169-45f7-988c-daeeab762201",
|
"sprint": "abc4f969-b169-45f7-988c-daeeab762201",
|
||||||
"assignedAgentIds": [
|
"assignedAgentIds": [
|
||||||
"57695b92-24d0-4876-837c-76116e70a6ae"
|
"57695b92-24d0-4876-837c-76116e70a6ae"
|
||||||
],
|
],
|
||||||
"updatedAt": 1784329025027
|
"updatedAt": 1784361147125
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"issueRef": "#33",
|
"issueRef": "#33",
|
||||||
|
|||||||
@ -1,7 +1,17 @@
|
|||||||
import '../infrastructure/local/local.dart';
|
import '../infrastructure/local/local.dart';
|
||||||
import 'application.dart';
|
import 'application.dart';
|
||||||
|
|
||||||
final class AppBootstrap {
|
abstract interface class AppDependencies {
|
||||||
|
ExerciseUseCases get exerciseUseCases;
|
||||||
|
MediaUseCases get mediaUseCases;
|
||||||
|
ProgramUseCases get programUseCases;
|
||||||
|
WorkoutTemplateUseCases get workoutTemplateUseCases;
|
||||||
|
ActiveWorkoutSessionUseCases get activeWorkoutSessionUseCases;
|
||||||
|
CloseWorkoutSessionUseCase get closeWorkoutSessionUseCase;
|
||||||
|
WorkoutHistoryUseCases get workoutHistoryUseCases;
|
||||||
|
}
|
||||||
|
|
||||||
|
final class AppBootstrap implements AppDependencies {
|
||||||
AppBootstrap._({
|
AppBootstrap._({
|
||||||
required this.database,
|
required this.database,
|
||||||
required this.exerciseUseCases,
|
required this.exerciseUseCases,
|
||||||
|
|||||||
@ -23,6 +23,7 @@ final class _GameTimeAppState extends State<GameTimeApp> {
|
|||||||
theme: courtBlazerLightTheme(),
|
theme: courtBlazerLightTheme(),
|
||||||
darkTheme: courtBlazerDarkTheme(),
|
darkTheme: courtBlazerDarkTheme(),
|
||||||
themeMode: _themeMode,
|
themeMode: _themeMode,
|
||||||
|
navigatorObservers: [homeRouteObserver],
|
||||||
home: HomeScreen(
|
home: HomeScreen(
|
||||||
bootstrap: widget.bootstrap,
|
bootstrap: widget.bootstrap,
|
||||||
themeMode: _themeMode,
|
themeMode: _themeMode,
|
||||||
|
|||||||
@ -9,6 +9,8 @@ import 'theme.dart';
|
|||||||
import 'workout_execution_screen.dart';
|
import 'workout_execution_screen.dart';
|
||||||
import 'workout_template_screen.dart';
|
import 'workout_template_screen.dart';
|
||||||
|
|
||||||
|
final homeRouteObserver = RouteObserver<ModalRoute<void>>();
|
||||||
|
|
||||||
final class HomeScreen extends StatefulWidget {
|
final class HomeScreen extends StatefulWidget {
|
||||||
const HomeScreen({
|
const HomeScreen({
|
||||||
required this.bootstrap,
|
required this.bootstrap,
|
||||||
@ -17,7 +19,7 @@ final class HomeScreen extends StatefulWidget {
|
|||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final AppBootstrap bootstrap;
|
final AppDependencies bootstrap;
|
||||||
final ThemeMode themeMode;
|
final ThemeMode themeMode;
|
||||||
final ValueChanged<ThemeMode>? onThemeModeChanged;
|
final ValueChanged<ThemeMode>? onThemeModeChanged;
|
||||||
|
|
||||||
@ -25,13 +27,40 @@ final class HomeScreen extends StatefulWidget {
|
|||||||
State<HomeScreen> createState() => _HomeScreenState();
|
State<HomeScreen> createState() => _HomeScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
final class _HomeScreenState extends State<HomeScreen> {
|
final class _HomeScreenState extends State<HomeScreen> with RouteAware {
|
||||||
late Future<ActiveWorkoutSession?> _openSession;
|
late Future<ActiveWorkoutSession?> _openSession;
|
||||||
|
ModalRoute<void>? _route;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_openSession = widget.bootstrap.activeWorkoutSessionUseCases.findOpen();
|
_openSession = _findOpenSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
final route = ModalRoute.of<void>(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
|
@override
|
||||||
@ -163,8 +192,16 @@ final class _HomeScreenState extends State<HomeScreen> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
_reloadOpenSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<ActiveWorkoutSession?> _findOpenSession() {
|
||||||
|
return widget.bootstrap.activeWorkoutSessionUseCases.findOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _reloadOpenSession() {
|
||||||
setState(() {
|
setState(() {
|
||||||
_openSession = widget.bootstrap.activeWorkoutSessionUseCases.findOpen();
|
_openSession = _findOpenSession();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
417
test/presentation/home_screen_test.dart
Normal file
417
test/presentation/home_screen_test.dart
Normal file
@ -0,0 +1,417 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:gametime/application/application.dart';
|
||||||
|
import 'package:gametime/application/app_bootstrap.dart';
|
||||||
|
import 'package:gametime/domain/domain.dart';
|
||||||
|
import 'package:gametime/presentation/home_screen.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('le bandeau de reprise apparaît au retour sur l’accueil', (
|
||||||
|
tester,
|
||||||
|
) async {
|
||||||
|
final navigatorKey = GlobalKey<NavigatorState>();
|
||||||
|
final activeRepository = _FakeActiveSessionRepository();
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
navigatorKey: navigatorKey,
|
||||||
|
navigatorObservers: [homeRouteObserver],
|
||||||
|
home: HomeScreen(bootstrap: _FakeBootstrap(activeRepository)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.textContaining('Séance en cours'), findsNothing);
|
||||||
|
|
||||||
|
unawaited(
|
||||||
|
navigatorKey.currentState!.push<void>(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => const Scaffold(body: Text('Séance active')),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump(const Duration(milliseconds: 350));
|
||||||
|
|
||||||
|
activeRepository.session = ActiveWorkoutSession(
|
||||||
|
metadata: _metadata('session-1'),
|
||||||
|
sourceWorkoutTemplateId: 'template-1',
|
||||||
|
status: ActiveWorkoutStatus.paused,
|
||||||
|
startedAt: DateTime.utc(2026, 7, 17, 12),
|
||||||
|
pausedAt: DateTime.utc(2026, 7, 17, 12, 5),
|
||||||
|
lastPersistedAt: DateTime.utc(2026, 7, 17, 12, 5),
|
||||||
|
elapsedActiveMs: 300000,
|
||||||
|
currentProgramIndex: 0,
|
||||||
|
currentExerciseIndex: 0,
|
||||||
|
currentSetIndex: 1,
|
||||||
|
resolvedTemplateSnapshotJson: _sessionSnapshot(),
|
||||||
|
);
|
||||||
|
|
||||||
|
navigatorKey.currentState!.pop();
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump(const Duration(milliseconds: 350));
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
find.text('Séance en cours — Reprendre · Squat, série 2/3'),
|
||||||
|
findsOneWidget,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('le bandeau de reprise disparaît au retour après abandon', (
|
||||||
|
tester,
|
||||||
|
) async {
|
||||||
|
final navigatorKey = GlobalKey<NavigatorState>();
|
||||||
|
final activeRepository = _FakeActiveSessionRepository()
|
||||||
|
..session = ActiveWorkoutSession(
|
||||||
|
metadata: _metadata('session-1'),
|
||||||
|
sourceWorkoutTemplateId: 'template-1',
|
||||||
|
status: ActiveWorkoutStatus.paused,
|
||||||
|
startedAt: DateTime.utc(2026, 7, 17, 12),
|
||||||
|
pausedAt: DateTime.utc(2026, 7, 17, 12, 5),
|
||||||
|
lastPersistedAt: DateTime.utc(2026, 7, 17, 12, 5),
|
||||||
|
elapsedActiveMs: 300000,
|
||||||
|
currentProgramIndex: 0,
|
||||||
|
currentExerciseIndex: 0,
|
||||||
|
currentSetIndex: 1,
|
||||||
|
resolvedTemplateSnapshotJson: _sessionSnapshot(),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
navigatorKey: navigatorKey,
|
||||||
|
navigatorObservers: [homeRouteObserver],
|
||||||
|
home: HomeScreen(bootstrap: _FakeBootstrap(activeRepository)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.textContaining('Séance en cours'), findsOneWidget);
|
||||||
|
|
||||||
|
unawaited(
|
||||||
|
navigatorKey.currentState!.push<void>(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => const Scaffold(body: Text('Séance active')),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump(const Duration(milliseconds: 350));
|
||||||
|
|
||||||
|
activeRepository.session = null;
|
||||||
|
navigatorKey.currentState!.pop();
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump(const Duration(milliseconds: 350));
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.textContaining('Séance en cours'), findsNothing);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _FakeBootstrap implements AppDependencies {
|
||||||
|
_FakeBootstrap(_FakeActiveSessionRepository activeRepository)
|
||||||
|
: activeWorkoutSessionUseCases = ActiveWorkoutSessionUseCases(
|
||||||
|
sessionRepository: activeRepository,
|
||||||
|
templateRepository: _FakeWorkoutTemplateRepository(),
|
||||||
|
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
|
||||||
|
ids: _FakeIds(),
|
||||||
|
originDeviceId: 'device-1',
|
||||||
|
),
|
||||||
|
closeWorkoutSessionUseCase = CloseWorkoutSessionUseCase(
|
||||||
|
sessionRepository: activeRepository,
|
||||||
|
historyRepository: _FakeWorkoutHistoryRepository(),
|
||||||
|
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
|
||||||
|
ids: _FakeIds(),
|
||||||
|
originDeviceId: 'device-1',
|
||||||
|
),
|
||||||
|
exerciseUseCases = ExerciseUseCases(
|
||||||
|
repository: _FakeExerciseRepository(),
|
||||||
|
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
|
||||||
|
ids: _FakeIds(),
|
||||||
|
originDeviceId: 'device-1',
|
||||||
|
),
|
||||||
|
mediaUseCases = MediaUseCases(
|
||||||
|
mediaRepository: _FakeMediaAssetRepository(),
|
||||||
|
exerciseRepository: _FakeExerciseRepository(),
|
||||||
|
storage: _FakeMediaStorage(),
|
||||||
|
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
|
||||||
|
ids: _FakeIds(),
|
||||||
|
originDeviceId: 'device-1',
|
||||||
|
),
|
||||||
|
programUseCases = ProgramUseCases(
|
||||||
|
programRepository: _FakeProgramRepository(),
|
||||||
|
exerciseRepository: _FakeExerciseRepository(),
|
||||||
|
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
|
||||||
|
ids: _FakeIds(),
|
||||||
|
originDeviceId: 'device-1',
|
||||||
|
),
|
||||||
|
workoutTemplateUseCases = WorkoutTemplateUseCases(
|
||||||
|
templateRepository: _FakeWorkoutTemplateRepository(),
|
||||||
|
programRepository: _FakeProgramRepository(),
|
||||||
|
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
|
||||||
|
ids: _FakeIds(),
|
||||||
|
originDeviceId: 'device-1',
|
||||||
|
),
|
||||||
|
workoutHistoryUseCases = WorkoutHistoryUseCases(
|
||||||
|
repository: _FakeWorkoutHistoryRepository(),
|
||||||
|
clock: _FakeClock(DateTime.utc(2026, 7, 17, 12)),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
final ExerciseUseCases exerciseUseCases;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final MediaUseCases mediaUseCases;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final ProgramUseCases programUseCases;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final WorkoutTemplateUseCases workoutTemplateUseCases;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final ActiveWorkoutSessionUseCases activeWorkoutSessionUseCases;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final CloseWorkoutSessionUseCase closeWorkoutSessionUseCase;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final WorkoutHistoryUseCases workoutHistoryUseCases;
|
||||||
|
}
|
||||||
|
|
||||||
|
String _sessionSnapshot() {
|
||||||
|
return jsonEncode({
|
||||||
|
'name': 'Séance jambes',
|
||||||
|
'programs': [
|
||||||
|
{
|
||||||
|
'id': 'template-program-1',
|
||||||
|
'programNameSnapshot': 'Programme jambes',
|
||||||
|
'programSnapshotJson': jsonEncode({
|
||||||
|
'exercises': [
|
||||||
|
{
|
||||||
|
'id': 'exercise-1',
|
||||||
|
'exerciseNameSnapshot': 'Squat',
|
||||||
|
'setsCount': 3,
|
||||||
|
'timeEnabled': true,
|
||||||
|
'repsEnabled': false,
|
||||||
|
'scoreEnabled': false,
|
||||||
|
'restSecondsOverride': 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'overrides': const [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityMetadata _metadata(String id) {
|
||||||
|
return EntityMetadata(
|
||||||
|
id: id,
|
||||||
|
createdAt: DateTime.utc(2026, 7, 17),
|
||||||
|
updatedAt: DateTime.utc(2026, 7, 17),
|
||||||
|
originDeviceId: 'device-1',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _FakeClock implements Clock {
|
||||||
|
const _FakeClock(this.value);
|
||||||
|
|
||||||
|
final DateTime value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
DateTime now() => value;
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _FakeIds implements IdGenerator {
|
||||||
|
var _next = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String newId() {
|
||||||
|
_next += 1;
|
||||||
|
return 'id-$_next';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _FakeActiveSessionRepository implements ActiveSessionRepository {
|
||||||
|
ActiveWorkoutSession? session;
|
||||||
|
final restStates = <ActiveRestState>[];
|
||||||
|
final scoreStopwatchStates = <ActiveScoreStopwatchState>[];
|
||||||
|
final results = <ActiveSetResult>[];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<ActiveWorkoutSession?> findById(String id) async {
|
||||||
|
return session?.metadata.id == id ? session : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<ActiveWorkoutSession?> findOpen() async => session;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<ActiveRestState?> findRestStateById(String id) async => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<ActiveScoreStopwatchState?> findScoreStopwatchState({
|
||||||
|
required String sessionId,
|
||||||
|
required int programIndex,
|
||||||
|
required int exerciseIndex,
|
||||||
|
required int setIndex,
|
||||||
|
}) async {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<ActiveRestState>> listRestStates(String sessionId) async {
|
||||||
|
return restStates;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<ActiveScoreStopwatchState>> listScoreStopwatchStates(
|
||||||
|
String sessionId,
|
||||||
|
) async {
|
||||||
|
return scoreStopwatchStates;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<ActiveSetResult>> listSetResults(String sessionId) async {
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> save(ActiveWorkoutSession session) async {
|
||||||
|
this.session = session;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> saveRestState(ActiveRestState restState) async {
|
||||||
|
restStates.add(restState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> saveScoreStopwatchState(ActiveScoreStopwatchState state) async {
|
||||||
|
scoreStopwatchStates.add(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> deleteScoreStopwatchState({
|
||||||
|
required String sessionId,
|
||||||
|
required int programIndex,
|
||||||
|
required int exerciseIndex,
|
||||||
|
required int setIndex,
|
||||||
|
required DateTime deletedAt,
|
||||||
|
}) async {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> saveSetResult(ActiveSetResult result) async {
|
||||||
|
results.add(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _FakeExerciseRepository implements ExerciseRepository {
|
||||||
|
@override
|
||||||
|
Future<Exercise?> findById(String id) async => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> isReferencedByProgram(String id) async => false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<Exercise>> listActive() async => const [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> save(Exercise exercise) async {}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _FakeMediaAssetRepository implements MediaAssetRepository {
|
||||||
|
@override
|
||||||
|
Future<MediaAsset?> findById(String id) async => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<MediaAsset>> listActive() async => const [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> save(MediaAsset mediaAsset) async {}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _FakeMediaStorage implements LocalMediaStorage {
|
||||||
|
@override
|
||||||
|
Future<void> deleteByLocalUri(String localUri) async {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<StoredMediaFile> importFile({
|
||||||
|
required String sourcePath,
|
||||||
|
required MediaKind kind,
|
||||||
|
required String stableFileName,
|
||||||
|
}) async {
|
||||||
|
return StoredMediaFile(
|
||||||
|
localUri: 'file:///$stableFileName',
|
||||||
|
mimeType: 'image/png',
|
||||||
|
sizeBytes: 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Set<String>> listManagedLocalUris() async => const {};
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _FakeProgramRepository implements ProgramRepository {
|
||||||
|
@override
|
||||||
|
Future<Program?> findById(String id) async => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<Program>> listActive() async => const [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> replaceExercises(Program program, DateTime deletedAt) async {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> save(Program program) async {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> saveExercise(ProgramExercise exercise) async {}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _FakeWorkoutTemplateRepository
|
||||||
|
implements WorkoutTemplateRepository {
|
||||||
|
@override
|
||||||
|
Future<WorkoutTemplate?> findById(String id) async => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<WorkoutTemplate>> listActive() async => const [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> replaceComposition(
|
||||||
|
WorkoutTemplate template,
|
||||||
|
DateTime deletedAt,
|
||||||
|
) async {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> save(WorkoutTemplate template) async {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> saveOverride(WorkoutTemplateExerciseOverride override) async {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> saveProgram(WorkoutTemplateProgram program) async {}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _FakeWorkoutHistoryRepository implements WorkoutHistoryRepository {
|
||||||
|
@override
|
||||||
|
Future<void> delete(String id, DateTime deletedAt) async {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<WorkoutHistory?> findById(String id) async => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<WorkoutHistory>> listActive() async => const [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> save(WorkoutHistory history) async {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> saveSetResult(WorkoutHistorySetResult result) async {}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user