mirror of
https://github.com/wger-project/flutter.git
synced 2026-02-18 00:17:48 +01:00
Also show the PR trophies in the regular log page
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of wger Workout Manager <https://github.com/wger-project>.
|
||||
* Copyright (c) 2025 wger Team
|
||||
* Copyright (c) 2026 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
|
||||
@@ -28,6 +28,38 @@ import 'base_provider.dart';
|
||||
|
||||
part 'trophies.g.dart';
|
||||
|
||||
class TrophyState {
|
||||
final _logger = Logger('TrophyState');
|
||||
|
||||
final List<Trophy> trophies;
|
||||
final List<UserTrophy> userTrophies;
|
||||
final List<UserTrophyProgression> trophyProgression;
|
||||
|
||||
TrophyState({
|
||||
this.trophies = const [],
|
||||
this.userTrophies = const [],
|
||||
this.trophyProgression = const [],
|
||||
});
|
||||
|
||||
TrophyState copyWith({
|
||||
List<Trophy>? trophies,
|
||||
List<UserTrophy>? userTrophies,
|
||||
List<UserTrophyProgression>? trophyProgression,
|
||||
}) {
|
||||
return TrophyState(
|
||||
trophies: trophies ?? this.trophies,
|
||||
userTrophies: userTrophies ?? this.userTrophies,
|
||||
trophyProgression: trophyProgression ?? this.trophyProgression,
|
||||
);
|
||||
}
|
||||
|
||||
List<UserTrophy> get prTrophies =>
|
||||
userTrophies.where((t) => t.trophy.type == TrophyType.pr).toList();
|
||||
|
||||
List<UserTrophy> get nonPrTrophies =>
|
||||
userTrophies.where((t) => t.trophy.type != TrophyType.pr).toList();
|
||||
}
|
||||
|
||||
class TrophyRepository {
|
||||
final _logger = Logger('TrophyRepository');
|
||||
|
||||
@@ -96,40 +128,52 @@ TrophyRepository trophyRepository(Ref ref) {
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
final class TrophyStateNotifier extends _$TrophyStateNotifier {
|
||||
final _logger = Logger('TrophyStateNotifier');
|
||||
|
||||
@override
|
||||
void build() {}
|
||||
TrophyState build() {
|
||||
return TrophyState();
|
||||
}
|
||||
|
||||
Future<void> fetchAll({String? language}) async {
|
||||
await Future.wait([
|
||||
fetchTrophies(language: language),
|
||||
fetchUserTrophies(language: language),
|
||||
fetchTrophyProgression(language: language),
|
||||
]);
|
||||
}
|
||||
|
||||
/// Fetch all available trophies
|
||||
Future<List<Trophy>> fetchTrophies() async {
|
||||
Future<List<Trophy>> fetchTrophies({String? language}) async {
|
||||
_logger.finer('Fetching trophies');
|
||||
|
||||
final repo = ref.read(trophyRepositoryProvider);
|
||||
return repo.fetchTrophies();
|
||||
final result = await repo.fetchTrophies(language: language);
|
||||
state = state.copyWith(trophies: result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Fetch trophies awarded to the user.
|
||||
/// Excludes hidden trophies as well as repeatable (PR) trophies since they are
|
||||
/// handled separately
|
||||
/// Fetch trophies awarded to the user, excludes hidden trophies
|
||||
Future<List<UserTrophy>> fetchUserTrophies({String? language}) async {
|
||||
final repo = ref.read(trophyRepositoryProvider);
|
||||
return repo.fetchUserTrophies(
|
||||
filterQuery: {'trophy__is_hidden': 'false', 'trophy__is_repeatable': 'false'},
|
||||
language: language,
|
||||
);
|
||||
}
|
||||
_logger.finer('Fetching user trophies');
|
||||
|
||||
/// Fetch PR trophies awarded to the user
|
||||
Future<List<UserTrophy>> fetchUserPRTrophies({String? language}) async {
|
||||
final repo = ref.read(trophyRepositoryProvider);
|
||||
return repo.fetchUserTrophies(
|
||||
filterQuery: {'trophy__trophy_type': TrophyType.pr.name},
|
||||
final result = await repo.fetchUserTrophies(
|
||||
filterQuery: {'trophy__is_hidden': 'false'}, //'trophy__is_repeatable': 'false'
|
||||
language: language,
|
||||
);
|
||||
state = state.copyWith(userTrophies: result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Fetch trophy progression for the user
|
||||
Future<List<UserTrophyProgression>> fetchTrophyProgression({String? language}) async {
|
||||
final repo = ref.read(trophyRepositoryProvider);
|
||||
_logger.finer('Fetching user trophy progression');
|
||||
|
||||
// Note that repeatable trophies are filtered out in the backend
|
||||
return repo.fetchProgression(language: language);
|
||||
final repo = ref.read(trophyRepositoryProvider);
|
||||
final result = await repo.fetchProgression(language: language);
|
||||
state = state.copyWith(trophyProgression: result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,8 @@ String _$trophyRepositoryHash() => r'0699f0c0f7f324f3ba9b21420d9845a3e3096b61';
|
||||
@ProviderFor(TrophyStateNotifier)
|
||||
const trophyStateProvider = TrophyStateNotifierProvider._();
|
||||
|
||||
final class TrophyStateNotifierProvider extends $NotifierProvider<TrophyStateNotifier, void> {
|
||||
final class TrophyStateNotifierProvider
|
||||
extends $NotifierProvider<TrophyStateNotifier, TrophyState> {
|
||||
const TrophyStateNotifierProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
@@ -73,25 +74,31 @@ final class TrophyStateNotifierProvider extends $NotifierProvider<TrophyStateNot
|
||||
TrophyStateNotifier create() => TrophyStateNotifier();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(void value) {
|
||||
Override overrideWithValue(TrophyState value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<void>(value),
|
||||
providerOverride: $SyncValueProvider<TrophyState>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$trophyStateNotifierHash() => r'47b4babf337bbb8bf60142ebe59dc760fa08dce3';
|
||||
String _$trophyStateNotifierHash() => r'c80c732272cf843b698f28152f60b9a5f37ee449';
|
||||
|
||||
abstract class _$TrophyStateNotifier extends $Notifier<void> {
|
||||
void build();
|
||||
abstract class _$TrophyStateNotifier extends $Notifier<TrophyState> {
|
||||
TrophyState build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
build();
|
||||
final ref = this.ref as $Ref<void, void>;
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<TrophyState, TrophyState>;
|
||||
final element =
|
||||
ref.element as $ClassProviderElement<AnyNotifier<void, void>, void, Object?, Object?>;
|
||||
element.handleValue(ref, null);
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<TrophyState, TrophyState>,
|
||||
TrophyState,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of wger Workout Manager <https://github.com/wger-project>.
|
||||
* Copyright (C) 2020, 2021 wger Team
|
||||
* Copyright (c) 2026 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
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
@@ -30,6 +31,7 @@ import 'package:wger/providers/gallery.dart';
|
||||
import 'package:wger/providers/measurement.dart';
|
||||
import 'package:wger/providers/nutrition.dart';
|
||||
import 'package:wger/providers/routines.dart';
|
||||
import 'package:wger/providers/trophies.dart';
|
||||
import 'package:wger/providers/user.dart';
|
||||
import 'package:wger/screens/dashboard.dart';
|
||||
import 'package:wger/screens/gallery_screen.dart';
|
||||
@@ -37,7 +39,7 @@ import 'package:wger/screens/nutritional_plans_screen.dart';
|
||||
import 'package:wger/screens/routine_list_screen.dart';
|
||||
import 'package:wger/screens/weight_screen.dart';
|
||||
|
||||
class HomeTabsScreen extends StatefulWidget {
|
||||
class HomeTabsScreen extends ConsumerStatefulWidget {
|
||||
final _logger = Logger('HomeTabsScreen');
|
||||
|
||||
HomeTabsScreen();
|
||||
@@ -48,25 +50,20 @@ class HomeTabsScreen extends StatefulWidget {
|
||||
_HomeTabsScreenState createState() => _HomeTabsScreenState();
|
||||
}
|
||||
|
||||
class _HomeTabsScreenState extends State<HomeTabsScreen> with SingleTickerProviderStateMixin {
|
||||
late Future<void> _initialData;
|
||||
class _HomeTabsScreenState extends ConsumerState<HomeTabsScreen>
|
||||
with SingleTickerProviderStateMixin {
|
||||
Future<void>? _initialData;
|
||||
bool _errorHandled = false;
|
||||
int _selectedIndex = 0;
|
||||
bool _isWideScreen = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Loading data here, since the build method can be called more than once
|
||||
_initialData = _loadEntries();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
|
||||
final size = MediaQuery.sizeOf(context);
|
||||
_isWideScreen = size.width > MATERIAL_XS_BREAKPOINT;
|
||||
_initialData ??= _loadEntries();
|
||||
}
|
||||
|
||||
void _onItemTapped(int index) {
|
||||
@@ -85,7 +82,9 @@ class _HomeTabsScreenState extends State<HomeTabsScreen> with SingleTickerProvid
|
||||
|
||||
/// Load initial data from the server
|
||||
Future<void> _loadEntries() async {
|
||||
final languageCode = Localizations.localeOf(context).languageCode;
|
||||
final authProvider = context.read<AuthProvider>();
|
||||
final trophyNotifier = ref.read(trophyStateProvider.notifier);
|
||||
|
||||
if (!authProvider.dataInit) {
|
||||
final routinesProvider = context.read<RoutinesProvider>();
|
||||
@@ -127,6 +126,7 @@ class _HomeTabsScreenState extends State<HomeTabsScreen> with SingleTickerProvid
|
||||
// routinesProvider.fetchAndSetAllRoutinesFull(),
|
||||
weightProvider.fetchAndSetEntries(),
|
||||
measurementProvider.fetchAndSetAllCategoriesAndEntries(),
|
||||
trophyNotifier.fetchAll(language: languageCode),
|
||||
]);
|
||||
|
||||
//
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/*
|
||||
* This file is part of wger Workout Manager <https://github.com/wger-project>.
|
||||
* Copyright (C) 2020, 2025 wger Team
|
||||
* Copyright (c) 2020 - 2026 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.
|
||||
*
|
||||
* wger Workout Manager is distributed in the hope that it will be useful,
|
||||
* 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.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of wger Workout Manager <https://github.com/wger-project>.
|
||||
* Copyright (c) 2020 - 2025 wger Team
|
||||
* Copyright (c) 2020 - 2026 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
|
||||
@@ -21,64 +21,45 @@ 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 {
|
||||
const DashboardTrophiesWidget();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final provider = ref.watch(trophyStateProvider.notifier);
|
||||
final languageCode = Localizations.localeOf(context).languageCode;
|
||||
final trophiesState = ref.read(trophyStateProvider);
|
||||
|
||||
return FutureBuilder(
|
||||
future: provider.fetchUserTrophies(language: languageCode),
|
||||
builder: (context, asyncSnapshot) {
|
||||
if (asyncSnapshot.connectionState != ConnectionState.done) {
|
||||
return const Card(child: BoxedProgressIndicator());
|
||||
}
|
||||
return Card(
|
||||
color: Colors.transparent,
|
||||
shadowColor: Colors.transparent,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (trophiesState.nonPrTrophies.isEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text('No trophies yet', style: Theme.of(context).textTheme.bodyMedium),
|
||||
)
|
||||
else
|
||||
SizedBox(
|
||||
height: 140,
|
||||
child: ListView.separated(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: trophiesState.nonPrTrophies.length,
|
||||
separatorBuilder: (context, index) => const SizedBox(width: 12),
|
||||
itemBuilder: (context, index) {
|
||||
final userTrophy = trophiesState.nonPrTrophies[index];
|
||||
|
||||
final userTrophies = asyncSnapshot.data ?? [];
|
||||
|
||||
return Card(
|
||||
color: Colors.transparent,
|
||||
shadowColor: Colors.transparent,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// ListTile(
|
||||
// title: Text(
|
||||
// 'Trophies',
|
||||
// style: Theme.of(context).textTheme.headlineSmall,
|
||||
// ),
|
||||
// ),
|
||||
if (userTrophies.isEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text('No trophies yet', style: Theme.of(context).textTheme.bodyMedium),
|
||||
)
|
||||
else
|
||||
SizedBox(
|
||||
height: 140,
|
||||
child: ListView.separated(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: userTrophies.length,
|
||||
separatorBuilder: (context, index) => const SizedBox(width: 12),
|
||||
itemBuilder: (context, index) {
|
||||
final userTrophy = userTrophies[index];
|
||||
|
||||
return SizedBox(
|
||||
width: 220,
|
||||
child: TrophyCard(trophy: userTrophy.trophy),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
return SizedBox(
|
||||
width: 220,
|
||||
child: TrophyCard(trophy: userTrophy.trophy),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of wger Workout Manager <https://github.com/wger-project>.
|
||||
* Copyright (c) 2020 - 2025 wger Team
|
||||
* Copyright (c) 2020 - 2026 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of wger Workout Manager <https://github.com/wger-project>.
|
||||
* Copyright (c) 2020 - 2025 wger Team
|
||||
* Copyright (c) 2020 - 2026 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
|
||||
@@ -49,7 +49,6 @@ class WorkoutSummary extends ConsumerStatefulWidget {
|
||||
class _WorkoutSummaryState extends ConsumerState<WorkoutSummary> {
|
||||
late Future<void> _initData;
|
||||
late Routine _routine;
|
||||
late List<UserTrophy> _userPrTrophies;
|
||||
bool _didInit = false;
|
||||
|
||||
@override
|
||||
@@ -76,11 +75,13 @@ class _WorkoutSummaryState extends ConsumerState<WorkoutSummary> {
|
||||
);
|
||||
|
||||
final trophyNotifier = ref.read(trophyStateProvider.notifier);
|
||||
_userPrTrophies = await trophyNotifier.fetchUserPRTrophies(language: languageCode);
|
||||
await trophyNotifier.fetchUserTrophies(language: languageCode);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final trophyState = ref.watch(trophyStateProvider);
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
NavigationHeader(
|
||||
@@ -102,10 +103,8 @@ class _WorkoutSummaryState extends ConsumerState<WorkoutSummary> {
|
||||
final apiSession = _routine.sessions.firstWhereOrNull(
|
||||
(s) => s.session.date.isSameDayAs(clock.now()),
|
||||
);
|
||||
final userTrophies = _userPrTrophies
|
||||
.where(
|
||||
(t) => t.contextData!.sessionId == apiSession?.session.id,
|
||||
)
|
||||
final userTrophies = trophyState.prTrophies
|
||||
.where((t) => t.contextData?.sessionId == apiSession?.session.id)
|
||||
.toList();
|
||||
|
||||
return WorkoutSessionStats(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of wger Workout Manager <https://github.com/wger-project>.
|
||||
* Copyright (c) 2020, 2025 wger Team
|
||||
* Copyright (c) 2020 - 2026 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
|
||||
@@ -17,33 +17,59 @@
|
||||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:wger/helpers/date.dart';
|
||||
import 'package:wger/helpers/errors.dart';
|
||||
import 'package:wger/l10n/generated/app_localizations.dart';
|
||||
import 'package:wger/models/workouts/routine.dart';
|
||||
import 'package:wger/providers/trophies.dart';
|
||||
|
||||
import '../gym_mode/summary.dart';
|
||||
import 'exercise_log_chart.dart';
|
||||
import 'muscle_groups.dart';
|
||||
import 'session_info.dart';
|
||||
|
||||
class DayLogWidget extends StatelessWidget {
|
||||
class DayLogWidget extends ConsumerWidget {
|
||||
final DateTime _date;
|
||||
final Routine _routine;
|
||||
final _logger = Logger('DayLogWidget');
|
||||
|
||||
const DayLogWidget(this._date, this._routine);
|
||||
DayLogWidget(this._date, this._routine);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final i18n = AppLocalizations.of(context);
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final trophyState = ref.read(trophyStateProvider);
|
||||
|
||||
final sessionApi = _routine.sessions.firstWhere(
|
||||
(sessionApi) => sessionApi.session.date.isSameDayAs(_date),
|
||||
);
|
||||
final exercises = sessionApi.exercises;
|
||||
|
||||
final prTrophies = trophyState.prTrophies
|
||||
.where((t) => t.contextData?.sessionId == sessionApi.session.id)
|
||||
.toList();
|
||||
|
||||
_logger.info(trophyState.prTrophies);
|
||||
_logger.info(prTrophies);
|
||||
|
||||
return Column(
|
||||
spacing: 10,
|
||||
children: [
|
||||
Card(child: SessionInfo(sessionApi.session)),
|
||||
if (prTrophies.isNotEmpty)
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: InfoCard(
|
||||
title: i18n.personalRecords,
|
||||
value: prTrophies.length.toString(),
|
||||
color: theme.colorScheme.tertiaryContainer,
|
||||
),
|
||||
),
|
||||
MuscleGroupsCard(sessionApi.logs),
|
||||
|
||||
Column(
|
||||
spacing: 10,
|
||||
children: [
|
||||
@@ -66,7 +92,17 @@ class DayLogWidget extends StatelessWidget {
|
||||
(log) => Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(log.repTextNoNl(context)),
|
||||
Row(
|
||||
children: [
|
||||
if (prTrophies.any((t) => t.contextData?.logId == log.id))
|
||||
Icon(
|
||||
Icons.emoji_events,
|
||||
color: theme.colorScheme.primary,
|
||||
size: 20,
|
||||
),
|
||||
Text(log.repTextNoNl(context)),
|
||||
],
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.delete),
|
||||
key: ValueKey('delete-log-${log.id}'),
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/*
|
||||
* This file is part of wger Workout Manager <https://github.com/wger-project>.
|
||||
* Copyright (C) 2020, 2025 wger Team
|
||||
* Copyright (c) 2026 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.
|
||||
*
|
||||
* wger Workout Manager is distributed in the hope that it will be useful,
|
||||
* 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.
|
||||
@@ -18,20 +18,26 @@
|
||||
|
||||
import 'package:clock/clock.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:table_calendar/table_calendar.dart';
|
||||
import 'package:wger/helpers/consts.dart';
|
||||
import 'package:wger/l10n/generated/app_localizations.dart';
|
||||
import 'package:wger/models/workouts/routine.dart';
|
||||
import 'package:wger/providers/trophies.dart';
|
||||
import 'package:wger/theme/theme.dart';
|
||||
import 'package:wger/widgets/routines/logs/day_logs_container.dart';
|
||||
|
||||
class WorkoutLogs extends StatelessWidget {
|
||||
class WorkoutLogs extends ConsumerWidget {
|
||||
final Routine _routine;
|
||||
|
||||
const WorkoutLogs(this._routine);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final languageCode = Localizations.localeOf(context).languageCode;
|
||||
final trophyNotifier = ref.read(trophyStateProvider.notifier);
|
||||
trophyNotifier.fetchUserTrophies(language: languageCode);
|
||||
|
||||
return ListView(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 8),
|
||||
children: [
|
||||
|
||||
@@ -21,15 +21,13 @@ 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);
|
||||
final languageCode = Localizations.localeOf(context).languageCode;
|
||||
final trophyState = ref.watch(trophyStateProvider);
|
||||
|
||||
// Responsive grid: determine columns based on screen width
|
||||
final width = MediaQuery.widthOf(context);
|
||||
@@ -44,52 +42,32 @@ class TrophiesOverview extends ConsumerWidget {
|
||||
crossAxisCount = 5;
|
||||
}
|
||||
|
||||
return FutureBuilder<List<UserTrophyProgression>>(
|
||||
future: notifier.fetchTrophyProgression(language: languageCode),
|
||||
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]);
|
||||
},
|
||||
// If empty, show placeholder
|
||||
if (trophyState.trophyProgression.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: trophyState.trophyProgression.length,
|
||||
itemBuilder: (context, index) {
|
||||
return _TrophyCardImage(userProgression: trophyState.trophyProgression[index]);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/*
|
||||
* This file is part of wger Workout Manager <https://github.com/wger-project>.
|
||||
* Copyright (C) 2020, 2021 wger Team
|
||||
* Copyright (c) 2026 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.
|
||||
*
|
||||
* wger Workout Manager is distributed in the hope that it will be useful,
|
||||
* 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.
|
||||
@@ -20,6 +20,7 @@ import 'dart:io';
|
||||
|
||||
import 'package:clock/clock.dart';
|
||||
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';
|
||||
@@ -27,14 +28,16 @@ import 'package:provider/provider.dart';
|
||||
import 'package:wger/l10n/generated/app_localizations.dart';
|
||||
import 'package:wger/models/workouts/routine.dart';
|
||||
import 'package:wger/providers/routines.dart';
|
||||
import 'package:wger/providers/trophies.dart';
|
||||
import 'package:wger/screens/routine_logs_screen.dart';
|
||||
import 'package:wger/screens/routine_screen.dart';
|
||||
import 'package:wger/widgets/routines/logs/log_overview_routine.dart';
|
||||
|
||||
import '../../test_data/routines.dart';
|
||||
import '../test_data/trophies.dart';
|
||||
import 'routine_logs_screen_test.mocks.dart';
|
||||
|
||||
@GenerateMocks([RoutinesProvider])
|
||||
@GenerateMocks([RoutinesProvider, TrophyRepository])
|
||||
void main() {
|
||||
late Routine routine;
|
||||
final mockRoutinesProvider = MockRoutinesProvider();
|
||||
@@ -49,25 +52,39 @@ void main() {
|
||||
Widget renderWidget({locale = 'en'}) {
|
||||
final key = GlobalKey<NavigatorState>();
|
||||
|
||||
return ChangeNotifierProvider<RoutinesProvider>(
|
||||
create: (context) => mockRoutinesProvider,
|
||||
child: MaterialApp(
|
||||
locale: Locale(locale),
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
navigatorKey: key,
|
||||
home: TextButton(
|
||||
onPressed: () => key.currentState!.push(
|
||||
MaterialPageRoute<void>(
|
||||
settings: RouteSettings(arguments: routine.id),
|
||||
builder: (_) => const WorkoutLogsScreen(),
|
||||
// Arrange
|
||||
final mockRepository = MockTrophyRepository();
|
||||
when(
|
||||
mockRepository.fetchUserTrophies(
|
||||
filterQuery: anyNamed('filterQuery'),
|
||||
language: anyNamed('language'),
|
||||
),
|
||||
).thenAnswer((_) async => getUserTrophies());
|
||||
|
||||
return ProviderScope(
|
||||
overrides: [
|
||||
trophyRepositoryProvider.overrideWithValue(mockRepository),
|
||||
],
|
||||
child: ChangeNotifierProvider<RoutinesProvider>(
|
||||
create: (context) => mockRoutinesProvider,
|
||||
child: MaterialApp(
|
||||
locale: Locale(locale),
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
navigatorKey: key,
|
||||
home: TextButton(
|
||||
onPressed: () => key.currentState!.push(
|
||||
MaterialPageRoute<void>(
|
||||
settings: RouteSettings(arguments: routine.id),
|
||||
builder: (_) => const WorkoutLogsScreen(),
|
||||
),
|
||||
),
|
||||
child: const SizedBox(),
|
||||
),
|
||||
child: const SizedBox(),
|
||||
routes: {
|
||||
RoutineScreen.routeName: (ctx) => const WorkoutLogsScreen(),
|
||||
},
|
||||
),
|
||||
routes: {
|
||||
RoutineScreen.routeName: (ctx) => const WorkoutLogsScreen(),
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@ import 'dart:ui' as _i17;
|
||||
import 'package:mockito/mockito.dart' as _i1;
|
||||
import 'package:mockito/src/dummies.dart' as _i16;
|
||||
import 'package:wger/models/exercises/exercise.dart' as _i15;
|
||||
import 'package:wger/models/trophies/trophy.dart' as _i19;
|
||||
import 'package:wger/models/trophies/user_trophy.dart' as _i20;
|
||||
import 'package:wger/models/trophies/user_trophy_progression.dart' as _i21;
|
||||
import 'package:wger/models/workouts/base_config.dart' as _i9;
|
||||
import 'package:wger/models/workouts/day.dart' as _i6;
|
||||
import 'package:wger/models/workouts/day_data.dart' as _i14;
|
||||
@@ -21,6 +24,7 @@ import 'package:wger/models/workouts/slot_entry.dart' as _i8;
|
||||
import 'package:wger/models/workouts/weight_unit.dart' as _i3;
|
||||
import 'package:wger/providers/base_provider.dart' as _i2;
|
||||
import 'package:wger/providers/routines.dart' as _i12;
|
||||
import 'package:wger/providers/trophies.dart' as _i18;
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: avoid_redundant_argument_values
|
||||
@@ -592,3 +596,107 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
}
|
||||
|
||||
/// A class which mocks [TrophyRepository].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockTrophyRepository extends _i1.Mock implements _i18.TrophyRepository {
|
||||
MockTrophyRepository() {
|
||||
_i1.throwOnMissingStub(this);
|
||||
}
|
||||
|
||||
@override
|
||||
_i2.WgerBaseProvider get base =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#base),
|
||||
returnValue: _FakeWgerBaseProvider_0(
|
||||
this,
|
||||
Invocation.getter(#base),
|
||||
),
|
||||
)
|
||||
as _i2.WgerBaseProvider);
|
||||
|
||||
@override
|
||||
String get trophiesPath =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#trophiesPath),
|
||||
returnValue: _i16.dummyValue<String>(
|
||||
this,
|
||||
Invocation.getter(#trophiesPath),
|
||||
),
|
||||
)
|
||||
as String);
|
||||
|
||||
@override
|
||||
String get userTrophiesPath =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#userTrophiesPath),
|
||||
returnValue: _i16.dummyValue<String>(
|
||||
this,
|
||||
Invocation.getter(#userTrophiesPath),
|
||||
),
|
||||
)
|
||||
as String);
|
||||
|
||||
@override
|
||||
String get userTrophyProgressionPath =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#userTrophyProgressionPath),
|
||||
returnValue: _i16.dummyValue<String>(
|
||||
this,
|
||||
Invocation.getter(#userTrophyProgressionPath),
|
||||
),
|
||||
)
|
||||
as String);
|
||||
|
||||
@override
|
||||
_i13.Future<List<_i19.Trophy>> fetchTrophies({String? language}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#fetchTrophies, [], {#language: language}),
|
||||
returnValue: _i13.Future<List<_i19.Trophy>>.value(<_i19.Trophy>[]),
|
||||
)
|
||||
as _i13.Future<List<_i19.Trophy>>);
|
||||
|
||||
@override
|
||||
_i13.Future<List<_i20.UserTrophy>> fetchUserTrophies({
|
||||
Map<String, String>? filterQuery,
|
||||
String? language,
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#fetchUserTrophies, [], {
|
||||
#filterQuery: filterQuery,
|
||||
#language: language,
|
||||
}),
|
||||
returnValue: _i13.Future<List<_i20.UserTrophy>>.value(
|
||||
<_i20.UserTrophy>[],
|
||||
),
|
||||
)
|
||||
as _i13.Future<List<_i20.UserTrophy>>);
|
||||
|
||||
@override
|
||||
_i13.Future<List<_i21.UserTrophyProgression>> fetchProgression({
|
||||
Map<String, String>? filterQuery,
|
||||
String? language,
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#fetchProgression, [], {
|
||||
#filterQuery: filterQuery,
|
||||
#language: language,
|
||||
}),
|
||||
returnValue: _i13.Future<List<_i21.UserTrophyProgression>>.value(
|
||||
<_i21.UserTrophyProgression>[],
|
||||
),
|
||||
)
|
||||
as _i13.Future<List<_i21.UserTrophyProgression>>);
|
||||
|
||||
@override
|
||||
List<_i19.Trophy> filterByType(
|
||||
List<_i19.Trophy>? list,
|
||||
_i19.TrophyType? type,
|
||||
) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#filterByType, [list, type]),
|
||||
returnValue: <_i19.Trophy>[],
|
||||
)
|
||||
as List<_i19.Trophy>);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of wger Workout Manager <https://github.com/wger-project>.
|
||||
* Copyright (c) 2025 wger Team
|
||||
* Copyright (c) 2026 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
|
||||
@@ -19,33 +19,25 @@
|
||||
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/dashboard/widgets/trophies.dart';
|
||||
|
||||
import '../../test_data/trophies.dart';
|
||||
import 'dashboard_trophies_widget_test.mocks.dart';
|
||||
|
||||
@GenerateMocks([TrophyRepository])
|
||||
void main() {
|
||||
testWidgets('DashboardTrophiesWidget shows trophies', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
final mockRepository = MockTrophyRepository();
|
||||
when(
|
||||
mockRepository.fetchUserTrophies(
|
||||
filterQuery: anyNamed('filterQuery'),
|
||||
language: anyNamed('language'),
|
||||
),
|
||||
).thenAnswer((_) async => getUserTrophies());
|
||||
|
||||
// Act
|
||||
await mockNetworkImagesFor(() async {
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
overrides: [
|
||||
trophyRepositoryProvider.overrideWithValue(mockRepository),
|
||||
trophyStateProvider.overrideWithValue(
|
||||
TrophyState(
|
||||
userTrophies: getUserTrophies(),
|
||||
trophies: getTestTrophies(),
|
||||
),
|
||||
),
|
||||
],
|
||||
child: const MaterialApp(
|
||||
home: Scaffold(
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
// Mocks generated by Mockito 5.4.6 from annotations
|
||||
// in wger/test/trophies/widgets/dashboard_trophies_widget_test.dart.
|
||||
// Do not manually edit this file.
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:async' as _i5;
|
||||
|
||||
import 'package:mockito/mockito.dart' as _i1;
|
||||
import 'package:mockito/src/dummies.dart' as _i4;
|
||||
import 'package:wger/models/trophies/trophy.dart' as _i6;
|
||||
import 'package:wger/models/trophies/user_trophy.dart' as _i7;
|
||||
import 'package:wger/models/trophies/user_trophy_progression.dart' as _i8;
|
||||
import 'package:wger/providers/base_provider.dart' as _i2;
|
||||
import 'package:wger/providers/trophies.dart' as _i3;
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: avoid_redundant_argument_values
|
||||
// ignore_for_file: avoid_setters_without_getters
|
||||
// ignore_for_file: comment_references
|
||||
// ignore_for_file: deprecated_member_use
|
||||
// ignore_for_file: deprecated_member_use_from_same_package
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: invalid_use_of_visible_for_testing_member
|
||||
// ignore_for_file: must_be_immutable
|
||||
// ignore_for_file: prefer_const_constructors
|
||||
// ignore_for_file: unnecessary_parenthesis
|
||||
// ignore_for_file: camel_case_types
|
||||
// ignore_for_file: subtype_of_sealed_class
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
class _FakeWgerBaseProvider_0 extends _i1.SmartFake implements _i2.WgerBaseProvider {
|
||||
_FakeWgerBaseProvider_0(Object parent, Invocation parentInvocation)
|
||||
: super(parent, parentInvocation);
|
||||
}
|
||||
|
||||
/// A class which mocks [TrophyRepository].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockTrophyRepository extends _i1.Mock implements _i3.TrophyRepository {
|
||||
MockTrophyRepository() {
|
||||
_i1.throwOnMissingStub(this);
|
||||
}
|
||||
|
||||
@override
|
||||
_i2.WgerBaseProvider get base =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#base),
|
||||
returnValue: _FakeWgerBaseProvider_0(
|
||||
this,
|
||||
Invocation.getter(#base),
|
||||
),
|
||||
)
|
||||
as _i2.WgerBaseProvider);
|
||||
|
||||
@override
|
||||
String get trophiesPath =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#trophiesPath),
|
||||
returnValue: _i4.dummyValue<String>(
|
||||
this,
|
||||
Invocation.getter(#trophiesPath),
|
||||
),
|
||||
)
|
||||
as String);
|
||||
|
||||
@override
|
||||
String get userTrophiesPath =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#userTrophiesPath),
|
||||
returnValue: _i4.dummyValue<String>(
|
||||
this,
|
||||
Invocation.getter(#userTrophiesPath),
|
||||
),
|
||||
)
|
||||
as String);
|
||||
|
||||
@override
|
||||
String get userTrophyProgressionPath =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#userTrophyProgressionPath),
|
||||
returnValue: _i4.dummyValue<String>(
|
||||
this,
|
||||
Invocation.getter(#userTrophyProgressionPath),
|
||||
),
|
||||
)
|
||||
as String);
|
||||
|
||||
@override
|
||||
_i5.Future<List<_i6.Trophy>> fetchTrophies({String? language}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#fetchTrophies, [], {#language: language}),
|
||||
returnValue: _i5.Future<List<_i6.Trophy>>.value(<_i6.Trophy>[]),
|
||||
)
|
||||
as _i5.Future<List<_i6.Trophy>>);
|
||||
|
||||
@override
|
||||
_i5.Future<List<_i7.UserTrophy>> fetchUserTrophies({
|
||||
Map<String, String>? filterQuery,
|
||||
String? language,
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#fetchUserTrophies, [], {
|
||||
#filterQuery: filterQuery,
|
||||
#language: language,
|
||||
}),
|
||||
returnValue: _i5.Future<List<_i7.UserTrophy>>.value(
|
||||
<_i7.UserTrophy>[],
|
||||
),
|
||||
)
|
||||
as _i5.Future<List<_i7.UserTrophy>>);
|
||||
|
||||
@override
|
||||
_i5.Future<List<_i8.UserTrophyProgression>> fetchProgression({
|
||||
Map<String, String>? filterQuery,
|
||||
String? language,
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#fetchProgression, [], {
|
||||
#filterQuery: filterQuery,
|
||||
#language: language,
|
||||
}),
|
||||
returnValue: _i5.Future<List<_i8.UserTrophyProgression>>.value(
|
||||
<_i8.UserTrophyProgression>[],
|
||||
),
|
||||
)
|
||||
as _i5.Future<List<_i8.UserTrophyProgression>>);
|
||||
|
||||
@override
|
||||
List<_i6.Trophy> filterByType(List<_i6.Trophy>? list, _i6.TrophyType? type) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#filterByType, [list, type]),
|
||||
returnValue: <_i6.Trophy>[],
|
||||
)
|
||||
as List<_i6.Trophy>);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of wger Workout Manager <https://github.com/wger-project>.
|
||||
* Copyright (c) 2025 wger Team
|
||||
* Copyright (c) 2026 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
|
||||
@@ -19,33 +19,26 @@
|
||||
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(
|
||||
filterQuery: anyNamed('filterQuery'),
|
||||
language: anyNamed('language'),
|
||||
),
|
||||
).thenAnswer((_) async => getUserTrophyProgression());
|
||||
|
||||
// Act
|
||||
await mockNetworkImagesFor(() async {
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
overrides: [
|
||||
trophyRepositoryProvider.overrideWithValue(mockRepository),
|
||||
trophyStateProvider.overrideWithValue(
|
||||
TrophyState(
|
||||
trophyProgression: getUserTrophyProgression(),
|
||||
userTrophies: getUserTrophies(),
|
||||
trophies: getTestTrophies(),
|
||||
),
|
||||
),
|
||||
],
|
||||
child: const MaterialApp(
|
||||
home: Scaffold(body: TrophiesOverview()),
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
// Mocks generated by Mockito 5.4.6 from annotations
|
||||
// in wger/test/trophies/widgets/trophies_overview_test.dart.
|
||||
// Do not manually edit this file.
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:async' as _i5;
|
||||
|
||||
import 'package:mockito/mockito.dart' as _i1;
|
||||
import 'package:mockito/src/dummies.dart' as _i4;
|
||||
import 'package:wger/models/trophies/trophy.dart' as _i6;
|
||||
import 'package:wger/models/trophies/user_trophy.dart' as _i7;
|
||||
import 'package:wger/models/trophies/user_trophy_progression.dart' as _i8;
|
||||
import 'package:wger/providers/base_provider.dart' as _i2;
|
||||
import 'package:wger/providers/trophies.dart' as _i3;
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: avoid_redundant_argument_values
|
||||
// ignore_for_file: avoid_setters_without_getters
|
||||
// ignore_for_file: comment_references
|
||||
// ignore_for_file: deprecated_member_use
|
||||
// ignore_for_file: deprecated_member_use_from_same_package
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: invalid_use_of_visible_for_testing_member
|
||||
// ignore_for_file: must_be_immutable
|
||||
// ignore_for_file: prefer_const_constructors
|
||||
// ignore_for_file: unnecessary_parenthesis
|
||||
// ignore_for_file: camel_case_types
|
||||
// ignore_for_file: subtype_of_sealed_class
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
class _FakeWgerBaseProvider_0 extends _i1.SmartFake implements _i2.WgerBaseProvider {
|
||||
_FakeWgerBaseProvider_0(Object parent, Invocation parentInvocation)
|
||||
: super(parent, parentInvocation);
|
||||
}
|
||||
|
||||
/// A class which mocks [TrophyRepository].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockTrophyRepository extends _i1.Mock implements _i3.TrophyRepository {
|
||||
MockTrophyRepository() {
|
||||
_i1.throwOnMissingStub(this);
|
||||
}
|
||||
|
||||
@override
|
||||
_i2.WgerBaseProvider get base =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#base),
|
||||
returnValue: _FakeWgerBaseProvider_0(
|
||||
this,
|
||||
Invocation.getter(#base),
|
||||
),
|
||||
)
|
||||
as _i2.WgerBaseProvider);
|
||||
|
||||
@override
|
||||
String get trophiesPath =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#trophiesPath),
|
||||
returnValue: _i4.dummyValue<String>(
|
||||
this,
|
||||
Invocation.getter(#trophiesPath),
|
||||
),
|
||||
)
|
||||
as String);
|
||||
|
||||
@override
|
||||
String get userTrophiesPath =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#userTrophiesPath),
|
||||
returnValue: _i4.dummyValue<String>(
|
||||
this,
|
||||
Invocation.getter(#userTrophiesPath),
|
||||
),
|
||||
)
|
||||
as String);
|
||||
|
||||
@override
|
||||
String get userTrophyProgressionPath =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#userTrophyProgressionPath),
|
||||
returnValue: _i4.dummyValue<String>(
|
||||
this,
|
||||
Invocation.getter(#userTrophyProgressionPath),
|
||||
),
|
||||
)
|
||||
as String);
|
||||
|
||||
@override
|
||||
_i5.Future<List<_i6.Trophy>> fetchTrophies({String? language}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#fetchTrophies, [], {#language: language}),
|
||||
returnValue: _i5.Future<List<_i6.Trophy>>.value(<_i6.Trophy>[]),
|
||||
)
|
||||
as _i5.Future<List<_i6.Trophy>>);
|
||||
|
||||
@override
|
||||
_i5.Future<List<_i7.UserTrophy>> fetchUserTrophies({
|
||||
Map<String, String>? filterQuery,
|
||||
String? language,
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#fetchUserTrophies, [], {
|
||||
#filterQuery: filterQuery,
|
||||
#language: language,
|
||||
}),
|
||||
returnValue: _i5.Future<List<_i7.UserTrophy>>.value(
|
||||
<_i7.UserTrophy>[],
|
||||
),
|
||||
)
|
||||
as _i5.Future<List<_i7.UserTrophy>>);
|
||||
|
||||
@override
|
||||
_i5.Future<List<_i8.UserTrophyProgression>> fetchProgression({
|
||||
Map<String, String>? filterQuery,
|
||||
String? language,
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#fetchProgression, [], {
|
||||
#filterQuery: filterQuery,
|
||||
#language: language,
|
||||
}),
|
||||
returnValue: _i5.Future<List<_i8.UserTrophyProgression>>.value(
|
||||
<_i8.UserTrophyProgression>[],
|
||||
),
|
||||
)
|
||||
as _i5.Future<List<_i8.UserTrophyProgression>>);
|
||||
|
||||
@override
|
||||
List<_i6.Trophy> filterByType(List<_i6.Trophy>? list, _i6.TrophyType? type) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#filterByType, [list, type]),
|
||||
returnValue: <_i6.Trophy>[],
|
||||
)
|
||||
as List<_i6.Trophy>);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of wger Workout Manager <https://github.com/wger-project>.
|
||||
* Copyright (c) 2025 - 2025 wger Team
|
||||
* Copyright (c) 2025 - 2026 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
|
||||
@@ -189,15 +189,15 @@ void main() {
|
||||
|
||||
testWidgets('save button calls addLog on RoutinesProvider', (tester) async {
|
||||
// Arrange
|
||||
final notifier = container.read(gymStateProvider.notifier);
|
||||
final gymNotifier = container.read(gymStateProvider.notifier);
|
||||
final routine = testdata.getTestRoutine();
|
||||
notifier.state = notifier.state.copyWith(
|
||||
gymNotifier.state = gymNotifier.state.copyWith(
|
||||
dayId: routine.days.first.id,
|
||||
routine: routine,
|
||||
iteration: 1,
|
||||
);
|
||||
notifier.calculatePages();
|
||||
notifier.state = notifier.state.copyWith(currentPage: 2);
|
||||
gymNotifier.calculatePages();
|
||||
gymNotifier.state = gymNotifier.state.copyWith(currentPage: 2);
|
||||
final mockRoutines = MockRoutinesProvider();
|
||||
|
||||
// Act
|
||||
@@ -229,10 +229,10 @@ void main() {
|
||||
expect(capturedLog!.repetitions, equals(7));
|
||||
expect(capturedLog!.weight, equals(77));
|
||||
|
||||
final currentSlotPage = notifier.state.getSlotEntryPageByIndex()!;
|
||||
final currentSlotPage = gymNotifier.state.getSlotEntryPageByIndex()!;
|
||||
expect(capturedLog!.slotEntryId, equals(currentSlotPage.setConfigData!.slotEntryId));
|
||||
expect(capturedLog!.routineId, equals(notifier.state.routine.id));
|
||||
expect(capturedLog!.iteration, equals(notifier.state.iteration));
|
||||
expect(capturedLog!.routineId, equals(gymNotifier.state.routine.id));
|
||||
expect(capturedLog!.iteration, equals(gymNotifier.state.iteration));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user