Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion lib/helpers/json.dart
Original file line number Diff line number Diff line change
@@ -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) 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
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -262,6 +263,7 @@
},
"selectExercises": "If you want to do a superset you can search for several exercises, they will be grouped together",
"@selectExercises": {},
"personalRecords": "Personal records",
"gymMode": "Gym mode",
"@gymMode": {
"description": "Label when starting the gym mode"
Expand Down
103 changes: 60 additions & 43 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -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) 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.
*
* 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.
Expand Down Expand Up @@ -35,6 +35,7 @@ import 'package:wger/providers/measurement.dart';
import 'package:wger/providers/nutrition.dart';
import 'package:wger/providers/routines.dart';
import 'package:wger/providers/user.dart';
import 'package:wger/providers/wger_base_riverpod.dart';
import 'package:wger/screens/add_exercise_screen.dart';
import 'package:wger/screens/auth_screen.dart';
import 'package:wger/screens/configure_plates_screen.dart';
Expand All @@ -57,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';
Expand Down Expand Up @@ -129,7 +131,7 @@ void main() async {
};

// Application
runApp(const riverpod.ProviderScope(child: MainApp()));
runApp(const MainApp());
}

class MainApp extends StatelessWidget {
Expand Down Expand Up @@ -217,46 +219,61 @@ class MainApp extends StatelessWidget {
),
],
child: Consumer<AuthProvider>(
builder: (ctx, auth, _) => Consumer<UserProvider>(
builder: (ctx, user, _) => MaterialApp(
title: 'wger',
navigatorKey: navigatorKey,
theme: wgerLightTheme,
darkTheme: wgerDarkTheme,
highContrastTheme: wgerLightThemeHc,
highContrastDarkTheme: wgerDarkThemeHc,
themeMode: user.themeMode,
home: _getHomeScreen(auth),
routes: {
DashboardScreen.routeName: (ctx) => const DashboardScreen(),
FormScreen.routeName: (ctx) => const FormScreen(),
GalleryScreen.routeName: (ctx) => const GalleryScreen(),
GymModeScreen.routeName: (ctx) => const GymModeScreen(),
HomeTabsScreen.routeName: (ctx) => HomeTabsScreen(),
MeasurementCategoriesScreen.routeName: (ctx) => const MeasurementCategoriesScreen(),
MeasurementEntriesScreen.routeName: (ctx) => const MeasurementEntriesScreen(),
NutritionalPlansScreen.routeName: (ctx) => const NutritionalPlansScreen(),
NutritionalDiaryScreen.routeName: (ctx) => const NutritionalDiaryScreen(),
NutritionalPlanScreen.routeName: (ctx) => const NutritionalPlanScreen(),
LogMealsScreen.routeName: (ctx) => const LogMealsScreen(),
LogMealScreen.routeName: (ctx) => const LogMealScreen(),
WeightScreen.routeName: (ctx) => const WeightScreen(),
RoutineScreen.routeName: (ctx) => const RoutineScreen(),
RoutineEditScreen.routeName: (ctx) => const RoutineEditScreen(),
WorkoutLogsScreen.routeName: (ctx) => const WorkoutLogsScreen(),
RoutineListScreen.routeName: (ctx) => const RoutineListScreen(),
ExercisesScreen.routeName: (ctx) => const ExercisesScreen(),
ExerciseDetailScreen.routeName: (ctx) => const ExerciseDetailScreen(),
AddExerciseScreen.routeName: (ctx) => const AddExerciseScreen(),
AboutPage.routeName: (ctx) => const AboutPage(),
SettingsPage.routeName: (ctx) => const SettingsPage(),
LogOverviewPage.routeName: (ctx) => const LogOverviewPage(),
ConfigurePlatesScreen.routeName: (ctx) => const ConfigurePlatesScreen(),
},
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
),
),
builder: (ctx, auth, _) {
final baseInstance = WgerBaseProvider(Provider.of(ctx, listen: false));

return Consumer<UserProvider>(
builder: (ctx, user, _) => riverpod.ProviderScope(
overrides: [
wgerBaseProvider.overrideWithValue(baseInstance),
],
child: riverpod.Consumer(
builder: (rpCtx, ref, _) {
return MaterialApp(
title: 'wger',
navigatorKey: navigatorKey,
theme: wgerLightTheme,
darkTheme: wgerDarkTheme,
highContrastTheme: wgerLightThemeHc,
highContrastDarkTheme: wgerDarkThemeHc,
themeMode: user.themeMode,
home: _getHomeScreen(auth),
routes: {
DashboardScreen.routeName: (ctx) => const DashboardScreen(),
FormScreen.routeName: (ctx) => const FormScreen(),
GalleryScreen.routeName: (ctx) => const GalleryScreen(),
GymModeScreen.routeName: (ctx) => const GymModeScreen(),
HomeTabsScreen.routeName: (ctx) => HomeTabsScreen(),
MeasurementCategoriesScreen.routeName: (ctx) =>
const MeasurementCategoriesScreen(),
MeasurementEntriesScreen.routeName: (ctx) => const MeasurementEntriesScreen(),
NutritionalPlansScreen.routeName: (ctx) => const NutritionalPlansScreen(),
NutritionalDiaryScreen.routeName: (ctx) => const NutritionalDiaryScreen(),
NutritionalPlanScreen.routeName: (ctx) => const NutritionalPlanScreen(),
LogMealsScreen.routeName: (ctx) => const LogMealsScreen(),
LogMealScreen.routeName: (ctx) => const LogMealScreen(),
WeightScreen.routeName: (ctx) => const WeightScreen(),
RoutineScreen.routeName: (ctx) => const RoutineScreen(),
RoutineEditScreen.routeName: (ctx) => const RoutineEditScreen(),
WorkoutLogsScreen.routeName: (ctx) => const WorkoutLogsScreen(),
RoutineListScreen.routeName: (ctx) => const RoutineListScreen(),
ExercisesScreen.routeName: (ctx) => const ExercisesScreen(),
ExerciseDetailScreen.routeName: (ctx) => const ExerciseDetailScreen(),
AddExerciseScreen.routeName: (ctx) => const AddExerciseScreen(),
AboutPage.routeName: (ctx) => const AboutPage(),
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,
);
},
),
),
);
},
),
);
}
Expand Down
66 changes: 66 additions & 0 deletions lib/models/trophies/trophy.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* This file is part of wger Workout Manager <https://github.com/wger-project>.
* 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 <http://www.gnu.org/licenses/>.
*/

import 'package:json_annotation/json_annotation.dart';

part 'trophy.g.dart';

enum TrophyType { time, volume, count, sequence, date, pr, other }

@JsonSerializable()
class Trophy {
@JsonKey(required: true)
final int id;

@JsonKey(required: true)
final String uuid;

@JsonKey(required: true)
final String name;

@JsonKey(required: true)
final String description;

@JsonKey(required: true)
final String image;

@JsonKey(required: true, name: 'trophy_type')
final TrophyType type;

@JsonKey(required: true, name: 'is_hidden')
final bool isHidden;

@JsonKey(required: true, name: 'is_progressive')
final bool isProgressive;

Trophy({
required this.id,
required this.uuid,
required this.name,
required this.description,
required this.image,
required this.type,
required this.isHidden,
required this.isProgressive,
});

// Boilerplate
factory Trophy.fromJson(Map<String, dynamic> json) => _$TrophyFromJson(json);

Map<String, dynamic> toJson() => _$TrophyToJson(this);
}
72 changes: 72 additions & 0 deletions lib/models/trophies/trophy.g.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* This file is part of wger Workout Manager <https://github.com/wger-project>.
* 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 <http://www.gnu.org/licenses/>.
*/

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'trophy.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

Trophy _$TrophyFromJson(Map<String, dynamic> json) {
$checkKeys(
json,
requiredKeys: const [
'id',
'uuid',
'name',
'description',
'image',
'trophy_type',
'is_hidden',
'is_progressive',
],
);
return Trophy(
id: (json['id'] as num).toInt(),
uuid: json['uuid'] as String,
name: json['name'] as String,
description: json['description'] as String,
image: json['image'] as String,
type: $enumDecode(_$TrophyTypeEnumMap, json['trophy_type']),
isHidden: json['is_hidden'] as bool,
isProgressive: json['is_progressive'] as bool,
);
}

Map<String, dynamic> _$TrophyToJson(Trophy instance) => <String, dynamic>{
'id': instance.id,
'uuid': instance.uuid,
'name': instance.name,
'description': instance.description,
'image': instance.image,
'trophy_type': _$TrophyTypeEnumMap[instance.type]!,
'is_hidden': instance.isHidden,
'is_progressive': instance.isProgressive,
};

const _$TrophyTypeEnumMap = {
TrophyType.time: 'time',
TrophyType.volume: 'volume',
TrophyType.count: 'count',
TrophyType.sequence: 'sequence',
TrophyType.date: 'date',
TrophyType.pr: 'pr',
TrophyType.other: 'other',
};
62 changes: 62 additions & 0 deletions lib/models/trophies/user_trophy.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* This file is part of wger Workout Manager <https://github.com/wger-project>.
* 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 <http://www.gnu.org/licenses/>.
*/

import 'package:json_annotation/json_annotation.dart';
import 'package:wger/helpers/json.dart';

import 'trophy.dart';
import 'user_trophy_context_data.dart';

part 'user_trophy.g.dart';

/// A trophy awarded to a user for achieving a specific milestone.

@JsonSerializable()
class UserTrophy {
@JsonKey(required: true)
final int id;

@JsonKey(required: true)
final Trophy trophy;

@JsonKey(required: true, name: 'earned_at', fromJson: utcIso8601ToLocalDate)
final DateTime earnedAt;

@JsonKey(required: true)
final num progress;

@JsonKey(required: true, name: 'is_notified')
final bool isNotified;

@JsonKey(required: true, name: 'context_data')
final ContextData? contextData;

UserTrophy({
required this.id,
required this.trophy,
required this.earnedAt,
required this.progress,
required this.isNotified,
this.contextData,
});

// Boilerplate
factory UserTrophy.fromJson(Map<String, dynamic> json) => _$UserTrophyFromJson(json);

Map<String, dynamic> toJson() => _$UserTrophyToJson(this);
}
Loading