Cleanup, tests, etc

This commit is contained in:
Roland Geider
2025-11-16 21:54:26 +01:00
parent 02a0f89807
commit 3119429a02
7 changed files with 83 additions and 53 deletions

View File

@@ -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';

View File

@@ -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<String> 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();
}

View File

@@ -16,32 +16,41 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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),
),
),
),

View File

@@ -83,24 +83,23 @@ class _GymModeState extends ConsumerState<GymMode> {
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;
}
}

View File

@@ -73,13 +73,17 @@ class _LogPageState extends ConsumerState<LogPage> {
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<LogPage> {
],
),
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,
),

View File

@@ -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);
},
);

View File

@@ -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);
});
}