From 36a3e7ef4aaa9f2cb696a1b10ee9a93d16eaef85 Mon Sep 17 00:00:00 2001 From: Roland Geider Date: Sat, 20 Dec 2025 19:53:55 +0100 Subject: [PATCH] Add overview page for the trophies --- lib/helpers/json.dart | 10 +- lib/l10n/app_en.arb | 1 + lib/main.dart | 2 + lib/models/trophies/user_trophy.g.dart | 18 -- .../trophies/user_trophy_progression.dart | 7 +- .../trophies/user_trophy_progression.g.dart | 22 +- lib/models/workouts/session.g.dart | 18 -- lib/models/workouts/slot_data.g.dart | 18 -- lib/providers/gym_state.g.dart | 18 -- lib/providers/trophies.dart | 12 +- lib/providers/trophies.g.dart | 18 -- lib/screens/trophy_screen.dart | 39 ++++ lib/widgets/dashboard/widgets/trophies.dart | 78 +++---- lib/widgets/trophies/trophies_overview.dart | 198 ++++++++++++++++++ test/core/validators_test.mocks.dart | 29 +-- test/test_data/trophies.dart | 9 + .../trophies_provider_test.mocks.dart | 18 -- .../widgets/trophies_overview_test.dart | 60 ++++++ 18 files changed, 387 insertions(+), 188 deletions(-) create mode 100644 lib/screens/trophy_screen.dart create mode 100644 lib/widgets/trophies/trophies_overview.dart create mode 100644 test/trophies/widgets/trophies_overview_test.dart diff --git a/lib/helpers/json.dart b/lib/helpers/json.dart index 91cd0756..b0202ca3 100644 --- a/lib/helpers/json.dart +++ b/lib/helpers/json.dart @@ -1,6 +1,6 @@ /* * This file is part of wger Workout Manager . - * Copyright (C) 2020, 2021 wger Team + * Copyright (c) 2025 wger Team * * wger Workout Manager is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -70,6 +70,14 @@ DateTime utcIso8601ToLocalDate(String dateTime) { return DateTime.parse(dateTime).toLocal(); } +DateTime? utcIso8601ToLocalDateNull(String? dateTime) { + if (dateTime == null) { + return null; + } + + return utcIso8601ToLocalDate(dateTime); +} + /* * Converts a time to a date object. * Needed e.g. when the wger api only sends a time but no date information. diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 003c43ea..335df77c 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -175,6 +175,7 @@ "slotEntryTypeTut": "Time under Tension", "slotEntryTypeIso": "Isometric hold", "slotEntryTypeJump": "Jump", + "trophies": "Trophies", "routines": "Routines", "newRoutine": "New routine", "noRoutines": "You have no routines", diff --git a/lib/main.dart b/lib/main.dart index b36666c8..0eda236e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -58,6 +58,7 @@ import 'package:wger/screens/routine_list_screen.dart'; import 'package:wger/screens/routine_logs_screen.dart'; import 'package:wger/screens/routine_screen.dart'; import 'package:wger/screens/splash_screen.dart'; +import 'package:wger/screens/trophy_screen.dart'; import 'package:wger/screens/update_app_screen.dart'; import 'package:wger/screens/weight_screen.dart'; import 'package:wger/theme/theme.dart'; @@ -263,6 +264,7 @@ class MainApp extends StatelessWidget { SettingsPage.routeName: (ctx) => const SettingsPage(), LogOverviewPage.routeName: (ctx) => const LogOverviewPage(), ConfigurePlatesScreen.routeName: (ctx) => const ConfigurePlatesScreen(), + TrophyScreen.routeName: (ctx) => const TrophyScreen(), }, localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, diff --git a/lib/models/trophies/user_trophy.g.dart b/lib/models/trophies/user_trophy.g.dart index 62d85ac3..3208ebf6 100644 --- a/lib/models/trophies/user_trophy.g.dart +++ b/lib/models/trophies/user_trophy.g.dart @@ -1,21 +1,3 @@ -/* - * This file is part of wger Workout Manager . - * Copyright (c) 2025 wger Team - * - * wger Workout Manager is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - // GENERATED CODE - DO NOT MODIFY BY HAND part of 'user_trophy.dart'; diff --git a/lib/models/trophies/user_trophy_progression.dart b/lib/models/trophies/user_trophy_progression.dart index e84104a9..6cf115ba 100644 --- a/lib/models/trophies/user_trophy_progression.dart +++ b/lib/models/trophies/user_trophy_progression.dart @@ -30,19 +30,22 @@ class UserTrophyProgression { @JsonKey(required: true, name: 'is_earned') final bool isEarned; - @JsonKey(required: true, name: 'earned_at', fromJson: utcIso8601ToLocalDate) - final DateTime earnedAt; + @JsonKey(required: true, name: 'earned_at', fromJson: utcIso8601ToLocalDateNull) + final DateTime? earnedAt; /// Progress towards earning the trophy (0-100%) @JsonKey(required: true) final num progress; + /// Current value towards the trophy goal (e.g., number of workouts completed) @JsonKey(required: true, name: 'current_value', fromJson: stringToNumNull) num? currentValue; + /// Target value to achieve the trophy goal @JsonKey(required: true, name: 'target_value', fromJson: stringToNumNull) num? targetValue; + /// Human-readable progress display (e.g., "3 / 10" or "51%") @JsonKey(required: true, name: 'progress_display') String? progressDisplay; diff --git a/lib/models/trophies/user_trophy_progression.g.dart b/lib/models/trophies/user_trophy_progression.g.dart index ac23b913..de2a49e7 100644 --- a/lib/models/trophies/user_trophy_progression.g.dart +++ b/lib/models/trophies/user_trophy_progression.g.dart @@ -1,21 +1,3 @@ -/* - * This file is part of wger Workout Manager . - * Copyright (c) 2025 wger Team - * - * wger Workout Manager is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - // GENERATED CODE - DO NOT MODIFY BY HAND part of 'user_trophy_progression.dart'; @@ -42,7 +24,7 @@ UserTrophyProgression _$UserTrophyProgressionFromJson( return UserTrophyProgression( trophy: Trophy.fromJson(json['trophy'] as Map), isEarned: json['is_earned'] as bool, - earnedAt: utcIso8601ToLocalDate(json['earned_at'] as String), + earnedAt: utcIso8601ToLocalDateNull(json['earned_at'] as String?), progress: json['progress'] as num, currentValue: stringToNumNull(json['current_value'] as String?), targetValue: stringToNumNull(json['target_value'] as String?), @@ -55,7 +37,7 @@ Map _$UserTrophyProgressionToJson( ) => { 'trophy': instance.trophy, 'is_earned': instance.isEarned, - 'earned_at': instance.earnedAt.toIso8601String(), + 'earned_at': instance.earnedAt?.toIso8601String(), 'progress': instance.progress, 'current_value': instance.currentValue, 'target_value': instance.targetValue, diff --git a/lib/models/workouts/session.g.dart b/lib/models/workouts/session.g.dart index 9e3a52f7..bf061fa1 100644 --- a/lib/models/workouts/session.g.dart +++ b/lib/models/workouts/session.g.dart @@ -1,21 +1,3 @@ -/* - * This file is part of wger Workout Manager . - * Copyright (c) 2025 wger Team - * - * wger Workout Manager is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - // GENERATED CODE - DO NOT MODIFY BY HAND part of 'session.dart'; diff --git a/lib/models/workouts/slot_data.g.dart b/lib/models/workouts/slot_data.g.dart index 589b2b99..756717e6 100644 --- a/lib/models/workouts/slot_data.g.dart +++ b/lib/models/workouts/slot_data.g.dart @@ -1,21 +1,3 @@ -/* - * This file is part of wger Workout Manager . - * Copyright (c) 2025 wger Team - * - * wger Workout Manager is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - // GENERATED CODE - DO NOT MODIFY BY HAND part of 'slot_data.dart'; diff --git a/lib/providers/gym_state.g.dart b/lib/providers/gym_state.g.dart index 7596fa4b..1899e808 100644 --- a/lib/providers/gym_state.g.dart +++ b/lib/providers/gym_state.g.dart @@ -1,21 +1,3 @@ -/* - * This file is part of wger Workout Manager . - * Copyright (c) 2025 wger Team - * - * wger Workout Manager is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - // GENERATED CODE - DO NOT MODIFY BY HAND part of 'gym_state.dart'; diff --git a/lib/providers/trophies.dart b/lib/providers/trophies.dart index 13ee1df7..bf1104b2 100644 --- a/lib/providers/trophies.dart +++ b/lib/providers/trophies.dart @@ -48,9 +48,15 @@ class TrophyRepository { } Future> fetchProgression() async { - final url = base.makeUrl(userTrophyProgressionPath, query: {'limit': API_MAX_PAGE_SIZE}); - final data = await base.fetchPaginated(url); - return data.map((e) => UserTrophyProgression.fromJson(e)).toList(); + try { + final url = base.makeUrl(userTrophyProgressionPath); + final List data = await base.fetch(url); + return data.map((e) => UserTrophyProgression.fromJson(e)).toList(); + } catch (e, stck) { + print('Error fetching trophy progression: $e'); + print(stck); + return []; + } } List filterByType(List list, TrophyType type) => diff --git a/lib/providers/trophies.g.dart b/lib/providers/trophies.g.dart index 04df81d6..81c82726 100644 --- a/lib/providers/trophies.g.dart +++ b/lib/providers/trophies.g.dart @@ -1,21 +1,3 @@ -/* - * This file is part of wger Workout Manager . - * Copyright (c) 2025 wger Team - * - * wger Workout Manager is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - // GENERATED CODE - DO NOT MODIFY BY HAND part of 'trophies.dart'; diff --git a/lib/screens/trophy_screen.dart b/lib/screens/trophy_screen.dart new file mode 100644 index 00000000..0cc3cc02 --- /dev/null +++ b/lib/screens/trophy_screen.dart @@ -0,0 +1,39 @@ +/* + * This file is part of wger Workout Manager . + * Copyright (c) 2025 wger Team + * + * wger Workout Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import 'package:flutter/material.dart'; +import 'package:wger/core/wide_screen_wrapper.dart'; +import 'package:wger/l10n/generated/app_localizations.dart'; +import 'package:wger/widgets/core/app_bar.dart'; +import 'package:wger/widgets/trophies/trophies_overview.dart'; + +class TrophyScreen extends StatelessWidget { + const TrophyScreen(); + + static const routeName = '/trophies'; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: EmptyAppBar(AppLocalizations.of(context).trophies), + body: WidescreenWrapper( + child: TrophiesOverview(), + ), + ); + } +} diff --git a/lib/widgets/dashboard/widgets/trophies.dart b/lib/widgets/dashboard/widgets/trophies.dart index c17df4ae..b0e1bb62 100644 --- a/lib/widgets/dashboard/widgets/trophies.dart +++ b/lib/widgets/dashboard/widgets/trophies.dart @@ -20,6 +20,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:wger/models/trophies/trophy.dart'; import 'package:wger/providers/trophies.dart'; +import 'package:wger/screens/trophy_screen.dart'; import 'package:wger/widgets/core/progress_indicator.dart'; class DashboardTrophiesWidget extends ConsumerWidget { @@ -92,43 +93,48 @@ class TrophyCard extends StatelessWidget { @override Widget build(BuildContext context) { return Card.filled( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - CircleAvatar( - radius: 30, - backgroundImage: NetworkImage(trophy.image), - ), - const SizedBox(width: 10), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text( - trophy.name, - style: Theme.of(context).textTheme.titleMedium, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - const SizedBox(height: 4), - Text( - trophy.description, - style: Theme.of(context).textTheme.bodySmall, - maxLines: 3, - overflow: TextOverflow.ellipsis, - ), - ], + child: InkWell( + onTap: () { + Navigator.of(context).pushNamed(TrophyScreen.routeName); + }, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + CircleAvatar( + radius: 30, + backgroundImage: NetworkImage(trophy.image), ), - ), - ], - ), - ], + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + trophy.name, + style: Theme.of(context).textTheme.titleMedium, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 4), + Text( + trophy.description, + style: Theme.of(context).textTheme.bodySmall, + maxLines: 3, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + ], + ), + ], + ), ), ), ); diff --git a/lib/widgets/trophies/trophies_overview.dart b/lib/widgets/trophies/trophies_overview.dart new file mode 100644 index 00000000..2db561e7 --- /dev/null +++ b/lib/widgets/trophies/trophies_overview.dart @@ -0,0 +1,198 @@ +/* + * This file is part of wger Workout Manager . + * Copyright (c) 2025 wger Team + * + * wger Workout Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:wger/helpers/material.dart'; +import 'package:wger/models/trophies/user_trophy_progression.dart'; +import 'package:wger/providers/trophies.dart'; +import 'package:wger/widgets/core/progress_indicator.dart'; + +class TrophiesOverview extends ConsumerWidget { + const TrophiesOverview({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final notifier = ref.watch(trophyStateProvider.notifier); + + // Responsive grid: determine columns based on screen width + final width = MediaQuery.widthOf(context); + int crossAxisCount = 1; + if (width <= MATERIAL_XS_BREAKPOINT) { + crossAxisCount = 2; + } else if (width > MATERIAL_XS_BREAKPOINT && width < MATERIAL_MD_BREAKPOINT) { + crossAxisCount = 3; + } else if (width >= MATERIAL_MD_BREAKPOINT && width < MATERIAL_LG_BREAKPOINT) { + crossAxisCount = 4; + } else { + crossAxisCount = 5; + } + + return FutureBuilder>( + future: notifier.fetchTrophyProgression(), + builder: (context, snapshot) { + if (snapshot.connectionState != ConnectionState.done) { + return const Card(child: BoxedProgressIndicator()); + } + + if (snapshot.hasError) { + return Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Text('Error loading trophies', style: Theme.of(context).textTheme.bodyLarge), + ), + ); + } + + final userTrophyProgression = snapshot.data ?? []; + + // If empty, show placeholder + if (userTrophyProgression.isEmpty) { + return Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + 'No trophies yet', + style: Theme.of(context).textTheme.bodyLarge, + textAlign: TextAlign.center, + ), + ), + ); + } + + return RepaintBoundary( + child: GridView.builder( + padding: const EdgeInsets.all(12), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: crossAxisCount, + ), + key: const ValueKey('trophy-grid'), + itemCount: userTrophyProgression.length, + itemBuilder: (context, index) { + return _TrophyCardImage(userProgression: userTrophyProgression[index]); + }, + ), + ); + }, + ); + } +} + +class _TrophyCardImage extends StatelessWidget { + final UserTrophyProgression userProgression; + + const _TrophyCardImage({required this.userProgression}); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final colorScheme = theme.colorScheme; + + final double progress = (userProgression.progress.toDouble() / 100.0).clamp(0.0, 1.0); + + return Opacity( + opacity: userProgression.isEarned ? 1.0 : 0.5, + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + side: BorderSide( + color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.18), + width: userProgression.isEarned ? 1.2 : 0, + ), + ), + child: Stack( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6.0), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + width: 70, + height: 70, + child: ClipOval( + child: Image.network( + userProgression.trophy.image, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) => Center( + child: Icon(Icons.emoji_events, size: 28, color: colorScheme.primary), + ), + ), + ), + ), + + Text( + userProgression.trophy.name, + style: theme.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), + textAlign: TextAlign.center, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 4), + + Text( + userProgression.trophy.description, + style: theme.textTheme.bodySmall?.copyWith( + color: theme.textTheme.bodySmall?.color, + ), + textAlign: TextAlign.center, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 8), + + if (userProgression.trophy.isProgressive) + Tooltip( + message: 'Progress: ${userProgression.progressDisplay}', + child: SizedBox( + height: 6, + child: ClipRRect( + borderRadius: BorderRadius.circular(6), + child: LinearProgressIndicator( + value: progress, + minHeight: 6, + valueColor: AlwaysStoppedAnimation(colorScheme.primary), + backgroundColor: colorScheme.onSurface.withAlpha((0.06 * 255).round()), + ), + ), + ), + ), + ], + ), + ), + if (userProgression.isEarned) + Positioned( + top: 6, + right: 6, + child: Container( + width: 28, + height: 28, + decoration: const BoxDecoration( + color: Colors.green, + shape: BoxShape.circle, + ), + child: const Icon(Icons.check, size: 16, color: Colors.white), + ), + ), + ], + ), + ), + ); + } +} diff --git a/test/core/validators_test.mocks.dart b/test/core/validators_test.mocks.dart index 8791d455..389f1556 100644 --- a/test/core/validators_test.mocks.dart +++ b/test/core/validators_test.mocks.dart @@ -1,21 +1,3 @@ -/* - * This file is part of wger Workout Manager . - * Copyright (c) 2025 wger Team - * - * wger Workout Manager is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - // Mocks generated by Mockito 5.4.6 from annotations // in wger/test/core/validators_test.dart. // Do not manually edit this file. @@ -785,6 +767,17 @@ class MockAppLocalizations extends _i1.Mock implements _i2.AppLocalizations { ) as String); + @override + String get trophies => + (super.noSuchMethod( + Invocation.getter(#trophies), + returnValue: _i3.dummyValue( + this, + Invocation.getter(#trophies), + ), + ) + as String); + @override String get routines => (super.noSuchMethod( diff --git a/test/test_data/trophies.dart b/test/test_data/trophies.dart index 698e26a5..8fc13d28 100644 --- a/test/test_data/trophies.dart +++ b/test/test_data/trophies.dart @@ -58,6 +58,15 @@ List getUserTrophyProgression() { targetValue: null, progressDisplay: null, ), + UserTrophyProgression( + trophy: trophies[1], + progress: 40, + isEarned: false, + earnedAt: null, + currentValue: 12, + targetValue: 30, + progressDisplay: '12 / 30', + ), ]; } diff --git a/test/trophies/provider/trophies_provider_test.mocks.dart b/test/trophies/provider/trophies_provider_test.mocks.dart index bebf3d1d..9830acb4 100644 --- a/test/trophies/provider/trophies_provider_test.mocks.dart +++ b/test/trophies/provider/trophies_provider_test.mocks.dart @@ -1,21 +1,3 @@ -/* - * This file is part of wger Workout Manager . - * Copyright (c) 2025 wger Team - * - * wger Workout Manager is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - // Mocks generated by Mockito 5.4.6 from annotations // in wger/test/trophies/provider/trophies_provider_test.dart. // Do not manually edit this file. diff --git a/test/trophies/widgets/trophies_overview_test.dart b/test/trophies/widgets/trophies_overview_test.dart new file mode 100644 index 00000000..7d7d65aa --- /dev/null +++ b/test/trophies/widgets/trophies_overview_test.dart @@ -0,0 +1,60 @@ +/* + * This file is part of wger Workout Manager . + * Copyright (c) 2025 wger Team + * + * wger Workout Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:network_image_mock/network_image_mock.dart'; +import 'package:wger/providers/trophies.dart'; +import 'package:wger/widgets/trophies/trophies_overview.dart'; + +import '../../test_data/trophies.dart'; +import 'dashboard_trophies_widget_test.mocks.dart'; + +@GenerateMocks([TrophyRepository]) +void main() { + testWidgets('TrophiesOverview shows trophies', (WidgetTester tester) async { + // Arrange + final mockRepository = MockTrophyRepository(); + when(mockRepository.fetchProgression()).thenAnswer((_) async => getUserTrophyProgression()); + + // Act + await mockNetworkImagesFor(() async { + await tester.pumpWidget( + ProviderScope( + overrides: [ + trophyRepositoryProvider.overrideWithValue(mockRepository), + ], + child: const MaterialApp( + home: Scaffold(body: TrophiesOverview()), + ), + ), + ); + await tester.pumpAndSettle(); + + // Assert + expect(find.text('New Year, New Me'), findsOneWidget); + expect(find.text('Work out on January 1st'), findsOneWidget); + + expect(find.text('Unstoppable'), findsOneWidget); + expect(find.text('Maintain a 30-day workout streak'), findsOneWidget); + }); + }); +}