diff --git a/integration_test/3_gym_mode.dart b/integration_test/3_gym_mode.dart index fd35fc43..6c3bd9d0 100644 --- a/integration_test/3_gym_mode.dart +++ b/integration_test/3_gym_mode.dart @@ -9,7 +9,7 @@ import 'package:wger/screens/gym_mode.dart'; import 'package:wger/screens/routine_screen.dart'; import 'package:wger/theme/theme.dart'; -import '../test/routine/gym_mode_test.mocks.dart'; +import '../test/routine/gym_mode/gym_mode_test.mocks.dart'; import '../test_data/exercises.dart'; import '../test_data/routines.dart'; diff --git a/lib/providers/gym_state.dart b/lib/providers/gym_state.dart index 56bdb3fd..556c02c0 100644 --- a/lib/providers/gym_state.dart +++ b/lib/providers/gym_state.dart @@ -90,7 +90,7 @@ class SlotPageEntry { /// Whether the log page has been marked as done final bool logDone; - /// The associated SetConfigData, only available for SlotPageType.log + /// The associated SetConfigData final SetConfigData? setConfigData; SlotPageEntry({ @@ -338,6 +338,18 @@ class GymStateNotifier extends _$GymStateNotifier { } for (final config in slotData.setConfigs) { + // Log page + slotEntries.add( + SlotPageEntry( + type: SlotPageType.log, + setIndex: setIndex, + pageIndex: pageIndex, + setConfigData: config, + ), + ); + pageIndex++; + setIndex++; + // Timer page if (state.showTimerPages) { slotEntries.add( @@ -350,19 +362,6 @@ class GymStateNotifier extends _$GymStateNotifier { ); pageIndex++; } - - // Log page - slotEntries.add( - SlotPageEntry( - type: SlotPageType.log, - setIndex: setIndex, - pageIndex: pageIndex, - setConfigData: config, - ), - ); - - pageIndex++; - setIndex++; } pages.add( @@ -380,7 +379,7 @@ class GymStateNotifier extends _$GymStateNotifier { ); state = state.copyWith(pages: pages); - debugStructure(); + readPageStructure(); _logger.finer('Initialized ${state.pages.length} pages'); } @@ -418,20 +417,23 @@ class GymStateNotifier extends _$GymStateNotifier { } state = state.copyWith(pages: updatedPages); - debugStructure(); + // _logger.fine(readPageStructure()); _logger.fine('Recalculated page indices'); } - void debugStructure() { - _logger.fine('GymModeState structure:'); + String readPageStructure() { + final List out = []; + out.add('GymModeState structure:'); for (final page in state.pages) { - _logger.fine('Page ${page.pageIndex}: ${page.type}'); + out.add('Page ${page.pageIndex}: ${page.type}'); for (final slotPage in page.slotPages) { - _logger.fine( + out.add( ' SlotPage ${slotPage.pageIndex.toString().padLeft(2, ' ')} (set index ${slotPage.setIndex}): ${slotPage.type}', ); } } + + return out.join('\n'); } int initData(Routine routine, int dayId, int iteration) { @@ -439,7 +441,8 @@ class GymStateNotifier extends _$GymStateNotifier { final currentPage = state.currentPage; final shouldReset = - (state.isInitialized && dayId != state.dayId) || validUntil.isBefore(DateTime.now()); + (!state.isInitialized || state.isInitialized && dayId != state.dayId) || + validUntil.isBefore(DateTime.now()); if (shouldReset) { _logger.fine('Day ID mismatch or expired validUntil date. Resetting to page 0.'); } @@ -454,7 +457,9 @@ class GymStateNotifier extends _$GymStateNotifier { currentPage: initialPage, ); - // Calculate the pages + // Calculate the pages. + // Note that this is only done if we need to reset, otherwise we keep the + // existing state like the exercises that have already been done if (shouldReset) { calculatePages(); } @@ -469,11 +474,13 @@ class GymStateNotifier extends _$GymStateNotifier { void setShowExercisePages(bool value) { state = state.copyWith(showExercisePages: value); + calculatePages(); _savePrefs(); } void setShowTimerPages(bool value) { state = state.copyWith(showTimerPages: value); + calculatePages(); _savePrefs(); } diff --git a/lib/widgets/routines/gym_mode/exercise_overview.dart b/lib/widgets/routines/gym_mode/exercise_overview.dart index b4d01fc0..e823816a 100644 --- a/lib/widgets/routines/gym_mode/exercise_overview.dart +++ b/lib/widgets/routines/gym_mode/exercise_overview.dart @@ -16,32 +16,41 @@ * along with this program. If not, see . */ import 'package:flutter/material.dart'; -import 'package:wger/models/exercises/exercise.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:logging/logging.dart'; +import 'package:wger/providers/gym_state.dart'; import 'package:wger/widgets/exercises/exercises.dart'; import 'package:wger/widgets/routines/gym_mode/navigation.dart'; -class ExerciseOverview extends StatelessWidget { +class ExerciseOverview extends ConsumerWidget { + final _logger = Logger('ExerciseOverview'); final PageController _controller; - final Exercise _exercise; - const ExerciseOverview( - this._controller, - this._exercise, - ); + ExerciseOverview(this._controller); @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { + final page = ref.watch(gymStateProvider).getSlotEntryPageByIndex(); + + if (page == null) { + _logger.info( + 'getPageByIndex returned null, showing empty container.', + ); + return Container(); + } + final exercise = page.setConfigData!.exercise; + return Column( children: [ NavigationHeader( - _exercise.getTranslation(Localizations.localeOf(context).languageCode).name, + exercise.getTranslation(Localizations.localeOf(context).languageCode).name, _controller, ), Expanded( child: SingleChildScrollView( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 10), - child: ExerciseDetail(_exercise), + child: ExerciseDetail(exercise), ), ), ), diff --git a/lib/widgets/routines/gym_mode/gym_mode.dart b/lib/widgets/routines/gym_mode/gym_mode.dart index 941175e2..fbdc6370 100644 --- a/lib/widgets/routines/gym_mode/gym_mode.dart +++ b/lib/widgets/routines/gym_mode/gym_mode.dart @@ -83,24 +83,23 @@ class _GymModeState extends ConsumerState { out.add(StartPage(_controller)); // Sets - for (final slotData in state.dayDataGym.slots) { - var firstPage = true; - for (final config in slotData.setConfigs) { - if (firstPage && state.showExercisePages) { - out.add(ExerciseOverview(_controller, config.exercise)); + for (final page in state.pages) { + for (final slotPage in page.slotPages) { + if (slotPage.type == SlotPageType.exerciseOverview) { + out.add(ExerciseOverview(_controller)); } - out.add(LogPage(_controller)); + if (slotPage.type == SlotPageType.log) { + out.add(LogPage(_controller)); + } - if (state.showTimerPages) { - if (config.restTime != null) { - out.add(TimerCountdownWidget(_controller, config.restTime!.toInt())); + if (slotPage.type == SlotPageType.timer) { + if (slotPage.setConfigData!.restTime != null) { + out.add(TimerCountdownWidget(_controller, slotPage.setConfigData!.restTime!.toInt())); } else { out.add(TimerWidget(_controller)); } } - - firstPage = false; } } diff --git a/lib/widgets/routines/gym_mode/log_page.dart b/lib/widgets/routines/gym_mode/log_page.dart index a2ad204b..e9682ef0 100644 --- a/lib/widgets/routines/gym_mode/log_page.dart +++ b/lib/widgets/routines/gym_mode/log_page.dart @@ -73,13 +73,17 @@ class _LogPageState extends ConsumerState { final page = state.getPageByIndex(); if (page == null) { - widget._logger.warning('getPageByIndex returned null, showing empty container'); + widget._logger.info( + 'getPageByIndex for ${state.currentPage} returned null, showing empty container.', + ); return Container(); } final slotEntryPage = state.getSlotEntryPageByIndex(); if (slotEntryPage == null) { - widget._logger.warning('getSlotPageByIndex returned null, showing empty container'); + widget._logger.info( + 'getSlotPageByIndex for ${state.currentPage} returned null, showing empty container', + ); return Container(); } @@ -129,7 +133,7 @@ class _LogPageState extends ConsumerState { ], ), Text( - '${slotEntryPage.setIndex + 1} / ${page.slotPages.length}', + '${slotEntryPage.setIndex + 1} / ${page.slotPages.where((e) => e.type == SlotPageType.log).length}', style: theme.textTheme.bodyLarge?.copyWith( color: Theme.of(context).colorScheme.primary, ), diff --git a/test/providers/gym_state_test.dart b/test/providers/gym_state_test.dart index 67dbad49..fbd870f9 100644 --- a/test/providers/gym_state_test.dart +++ b/test/providers/gym_state_test.dart @@ -43,7 +43,7 @@ void main() { group('GymStateNotifier.markSlotPageAsDone', () { test('Correctly changes the flag', () { // Arrange - final slotPage = notifier.state.pages[1].slotPages[2]; + final slotPage = notifier.state.pages[1].slotPages[1]; expect(slotPage.type, SlotPageType.log); expect( notifier.state.pages.every((p) => p.slotPages.every((s) => !s.logDone)), @@ -148,12 +148,13 @@ void main() { group('GymStateNotifier.replaceExercises', () { test('Correctly swaps an exercise', () { // Arrange - final slotPage = notifier.state.pages[1].slotPages[2]; - notifier.state.pages.every((p) => p.exercises.every((s) => s.id != testSquats.id)); + final slotPage = notifier.state.pages[1].slotPages[1]; expect(slotPage.type, SlotPageType.log); + notifier.state.pages.every((p) => p.exercises.every((s) => s.id != testSquats.id)); // Act notifier.replaceExercises(slotPage.uuid, originalExerciseId: 1, newExercise: testSquats); + // print(notifier.readPageStructure()); // Assert expect(notifier.state.pages[1].exercises.first.id, testSquats.id); @@ -193,8 +194,8 @@ void main() { reason: 'One exercise overview at the start', ); expect(setEntry.slotPages[0].type, SlotPageType.exerciseOverview); - expect(setEntry.slotPages[1].type, SlotPageType.timer); - expect(setEntry.slotPages[2].type, SlotPageType.log); + expect(setEntry.slotPages[1].type, SlotPageType.log); + expect(setEntry.slotPages[2].type, SlotPageType.timer); expect(notifier.state.totalPages, 16); }, ); diff --git a/test/routine/gym_mode/workout_menu_test.dart b/test/routine/gym_mode/workout_menu_test.dart index 6d34d4d6..6171deac 100644 --- a/test/routine/gym_mode/workout_menu_test.dart +++ b/test/routine/gym_mode/workout_menu_test.dart @@ -76,7 +76,7 @@ void main() { tags: ['golden'], ); - testWidgets('Opens the exercise swap', (WidgetTester tester) async { + testWidgets('Opens the exercise swap widget', (WidgetTester tester) async { await tester.pumpWidget(renderWidget()); expect(find.byType(ExerciseSwapWidget), findsNothing); @@ -85,4 +85,14 @@ void main() { await tester.pumpAndSettle(); expect(find.byType(ExerciseSwapWidget), findsOne); }); + + testWidgets('Opens the add exercise widget', (WidgetTester tester) async { + await tester.pumpWidget(renderWidget()); + + expect(find.byType(ExerciseAddWidget), findsNothing); + + await tester.tap(find.byKey(Key('add-icon-${notifier.state.pages[1].uuid}'))); + await tester.pumpAndSettle(); + expect(find.byType(ExerciseAddWidget), findsOne); + }); }