diff --git a/lib/models/workouts/log.dart b/lib/models/workouts/log.dart
index 8ca7762c..b48f75af 100644
--- a/lib/models/workouts/log.dart
+++ b/lib/models/workouts/log.dart
@@ -16,6 +16,8 @@
* along with this program. If not, see .
*/
+import 'dart:ui';
+
import 'package:json_annotation/json_annotation.dart';
import 'package:wger/helpers/json.dart';
import 'package:wger/helpers/misc.dart';
@@ -107,4 +109,29 @@ class Log {
String get singleLogRepText {
return repText(reps, repetitionUnitObj, weight, weightUnitObj, rir);
}
+
+ /// Override the equals operator
+ ///
+ /// Two logs are considered equal if their content is equal. This is used e.g.
+ /// in lists where we want to have unique values
+ bool operator ==(o) {
+ return o is Log &&
+ exerciseId == o.exerciseId &&
+ weight == o.weight &&
+ weightUnitId == o.weightUnitId &&
+ reps == o.reps &&
+ repetitionUnitId == o.repetitionUnitId &&
+ rir == o.rir;
+ }
+
+ @override
+ int get hashCode => hashValues(exerciseId, weight, weightUnitId, reps, repetitionUnitId, rir);
+
+ //@override
+ //int get hashCode => super.hashCode;
+
+ @override
+ String toString() {
+ return 'Log(id: $id, ex: $exerciseId, weightU: $weightUnitId, w: $weight, repU: $repetitionUnitId, rep: $reps, rir: $rir)';
+ }
}
diff --git a/lib/models/workouts/workout_plan.dart b/lib/models/workouts/workout_plan.dart
index c5fd8aa8..110989c6 100644
--- a/lib/models/workouts/workout_plan.dart
+++ b/lib/models/workouts/workout_plan.dart
@@ -69,8 +69,18 @@ class WorkoutPlan {
Map toJson() => _$WorkoutPlanToJson(this);
/// Filters the workout logs by exercise and sorts them by date
- List filterLogsByExercise(Exercise exercise) {
+ ///
+ /// Optionally, filters list so that only unique logs are returned. "Unique"
+ /// means here that the values are the same, i.e. logs with the same weight,
+ /// reps, etc. are considered equal. Workout ID, Log ID and date are not
+ /// considered.
+ List filterLogsByExercise(Exercise exercise, {bool unique = false}) {
var out = logs.where((element) => element.exerciseId == exercise.id).toList();
+
+ if (unique) {
+ out = out.toSet().toList();
+ }
+
out.sort((a, b) => b.date.compareTo(a.date));
return out;
}
diff --git a/lib/widgets/workouts/gym_mode.dart b/lib/widgets/workouts/gym_mode.dart
index e81347c7..e9974d61 100644
--- a/lib/widgets/workouts/gym_mode.dart
+++ b/lib/widgets/workouts/gym_mode.dart
@@ -361,17 +361,21 @@ class _LogPageState extends State {
? ListView(
children: [
Text(
- 'Logs',
+ AppLocalizations.of(context)!.labelWorkoutLogs,
style: Theme.of(context).textTheme.headline6,
textAlign: TextAlign.center,
),
- ...widget._workoutPlan.filterLogsByExercise(widget._exercise).map((log) {
+ ...widget._workoutPlan
+ .filterLogsByExercise(widget._exercise, unique: true)
+ .map((log) {
return ListTile(
+ //title: Text(log.id.toString()),
title: Text(log.singleLogRepText.replaceAll('\n', '')),
subtitle: Text(
DateFormat.yMd(Localizations.localeOf(context).languageCode)
.format(log.date)),
- trailing: Icon(Icons.arrow_forward),
+
+ trailing: Icon(Icons.copy),
onTap: () {
setState(() {
// Text field
diff --git a/test/gym_mode_screen_test.dart b/test/gym_mode_screen_test.dart
index d3d552bf..94f4d1cd 100644
--- a/test/gym_mode_screen_test.dart
+++ b/test/gym_mode_screen_test.dart
@@ -165,9 +165,9 @@ void main() {
expect(find.text('Workout session'), findsOneWidget);
expect(find.byType(SessionPage), findsOneWidget);
expect(find.byType(Form), findsOneWidget);
- expect(find.byIcon(Icons.sentiment_dissatisfied), findsOneWidget);
+ expect(find.byIcon(Icons.sentiment_very_dissatisfied), findsOneWidget);
expect(find.byIcon(Icons.sentiment_neutral), findsOneWidget);
- expect(find.byIcon(Icons.sentiment_satisfied), findsOneWidget);
+ expect(find.byIcon(Icons.sentiment_very_satisfied), findsOneWidget);
expect(find.byIcon(Icons.chevron_left), findsOneWidget);
expect(find.byIcon(Icons.close), findsOneWidget);
expect(find.byIcon(Icons.chevron_right), findsNothing);
diff --git a/test/nutritional_plan_form_test.mocks.dart b/test/nutritional_plan_form_test.mocks.dart
index 42d4d235..80c72bd7 100644
--- a/test/nutritional_plan_form_test.mocks.dart
+++ b/test/nutritional_plan_form_test.mocks.dart
@@ -39,57 +39,68 @@ class _FakeResponse extends _i1.Fake implements _i8.Response {}
/// A class which mocks [NutritionPlansProvider].
///
/// See the documentation for Mockito's code generation for more information.
-class MockNutritionPlansProvider extends _i1.Mock implements _i9.NutritionPlansProvider {
+class MockNutritionPlansProvider extends _i1.Mock
+ implements _i9.NutritionPlansProvider {
MockNutritionPlansProvider() {
_i1.throwOnMissingStub(this);
}
@override
List<_i4.NutritionalPlan> get items =>
- (super.noSuchMethod(Invocation.getter(#items), returnValue: <_i4.NutritionalPlan>[])
- as List<_i4.NutritionalPlan>);
+ (super.noSuchMethod(Invocation.getter(#items),
+ returnValue: <_i4.NutritionalPlan>[]) as List<_i4.NutritionalPlan>);
@override
bool get hasListeners =>
- (super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false) as bool);
+ (super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false)
+ as bool);
@override
- _i2.AuthProvider get auth =>
- (super.noSuchMethod(Invocation.getter(#auth), returnValue: _FakeAuthProvider())
- as _i2.AuthProvider);
+ _i2.AuthProvider get auth => (super.noSuchMethod(Invocation.getter(#auth),
+ returnValue: _FakeAuthProvider()) as _i2.AuthProvider);
@override
set auth(_i2.AuthProvider? _auth) =>
- super.noSuchMethod(Invocation.setter(#auth, _auth), returnValueForMissingStub: null);
+ super.noSuchMethod(Invocation.setter(#auth, _auth),
+ returnValueForMissingStub: null);
@override
- _i3.Client get client =>
- (super.noSuchMethod(Invocation.getter(#client), returnValue: _FakeClient()) as _i3.Client);
+ _i3.Client get client => (super.noSuchMethod(Invocation.getter(#client),
+ returnValue: _FakeClient()) as _i3.Client);
@override
set client(_i3.Client? _client) =>
- super.noSuchMethod(Invocation.setter(#client, _client), returnValueForMissingStub: null);
+ super.noSuchMethod(Invocation.setter(#client, _client),
+ returnValueForMissingStub: null);
@override
_i4.NutritionalPlan findById(int? id) =>
- (super.noSuchMethod(Invocation.method(#findById, [id]), returnValue: _FakeNutritionalPlan())
- as _i4.NutritionalPlan);
+ (super.noSuchMethod(Invocation.method(#findById, [id]),
+ returnValue: _FakeNutritionalPlan()) as _i4.NutritionalPlan);
@override
_i5.Meal? findMealById(int? id) =>
(super.noSuchMethod(Invocation.method(#findMealById, [id])) as _i5.Meal?);
@override
+ _i10.Future fetchAndSetAllPlansSparse() =>
+ (super.noSuchMethod(Invocation.method(#fetchAndSetAllPlansSparse, []),
+ returnValue: Future.value(null),
+ returnValueForMissingStub: Future.value()) as _i10.Future);
+ @override
_i10.Future fetchAndSetAllPlansFull() =>
- (super.noSuchMethod(Invocation.method(#fetchAndSetAllPlans, []),
+ (super.noSuchMethod(Invocation.method(#fetchAndSetAllPlansFull, []),
returnValue: Future.value(null),
returnValueForMissingStub: Future.value()) as _i10.Future);
@override
_i10.Future<_i4.NutritionalPlan> fetchAndSetPlanSparse(int? planId) =>
(super.noSuchMethod(Invocation.method(#fetchAndSetPlanSparse, [planId]),
- returnValue: Future<_i4.NutritionalPlan>.value(_FakeNutritionalPlan()))
+ returnValue:
+ Future<_i4.NutritionalPlan>.value(_FakeNutritionalPlan()))
as _i10.Future<_i4.NutritionalPlan>);
@override
_i10.Future<_i4.NutritionalPlan> fetchAndSetPlanFull(int? planId) =>
- (super.noSuchMethod(Invocation.method(#fetchAndSetPlan, [planId]),
- returnValue: Future<_i4.NutritionalPlan>.value(_FakeNutritionalPlan()))
+ (super.noSuchMethod(Invocation.method(#fetchAndSetPlanFull, [planId]),
+ returnValue:
+ Future<_i4.NutritionalPlan>.value(_FakeNutritionalPlan()))
as _i10.Future<_i4.NutritionalPlan>);
@override
_i10.Future<_i4.NutritionalPlan> addPlan(_i4.NutritionalPlan? planData) =>
(super.noSuchMethod(Invocation.method(#addPlan, [planData]),
- returnValue: Future<_i4.NutritionalPlan>.value(_FakeNutritionalPlan()))
+ returnValue:
+ Future<_i4.NutritionalPlan>.value(_FakeNutritionalPlan()))
as _i10.Future<_i4.NutritionalPlan>);
@override
_i10.Future editPlan(_i4.NutritionalPlan? plan) =>
@@ -97,93 +108,105 @@ class MockNutritionPlansProvider extends _i1.Mock implements _i9.NutritionPlansP
returnValue: Future.value(null),
returnValueForMissingStub: Future.value()) as _i10.Future);
@override
- _i10.Future deletePlan(int? id) => (super.noSuchMethod(Invocation.method(#deletePlan, [id]),
- returnValue: Future.value(null),
- returnValueForMissingStub: Future.value()) as _i10.Future);
+ _i10.Future deletePlan(int? id) =>
+ (super.noSuchMethod(Invocation.method(#deletePlan, [id]),
+ returnValue: Future.value(null),
+ returnValueForMissingStub: Future.value()) as _i10.Future);
@override
_i10.Future<_i5.Meal> addMeal(_i5.Meal? meal, int? planId) =>
(super.noSuchMethod(Invocation.method(#addMeal, [meal, planId]),
- returnValue: Future<_i5.Meal>.value(_FakeMeal())) as _i10.Future<_i5.Meal>);
+ returnValue: Future<_i5.Meal>.value(_FakeMeal()))
+ as _i10.Future<_i5.Meal>);
@override
_i10.Future<_i5.Meal> editMeal(_i5.Meal? meal) =>
(super.noSuchMethod(Invocation.method(#editMeal, [meal]),
- returnValue: Future<_i5.Meal>.value(_FakeMeal())) as _i10.Future<_i5.Meal>);
+ returnValue: Future<_i5.Meal>.value(_FakeMeal()))
+ as _i10.Future<_i5.Meal>);
@override
_i10.Future deleteMeal(_i5.Meal? meal) =>
(super.noSuchMethod(Invocation.method(#deleteMeal, [meal]),
returnValue: Future.value(null),
returnValueForMissingStub: Future.value()) as _i10.Future);
@override
- _i10.Future<_i6.MealItem> addMealItem(_i6.MealItem? mealItem, _i5.Meal? meal) =>
+ _i10.Future<_i6.MealItem> addMealItem(
+ _i6.MealItem? mealItem, _i5.Meal? meal) =>
(super.noSuchMethod(Invocation.method(#addMealItem, [mealItem, meal]),
- returnValue: Future<_i6.MealItem>.value(_FakeMealItem())) as _i10.Future<_i6.MealItem>);
+ returnValue: Future<_i6.MealItem>.value(_FakeMealItem()))
+ as _i10.Future<_i6.MealItem>);
@override
_i10.Future deleteMealItem(_i6.MealItem? mealItem) =>
(super.noSuchMethod(Invocation.method(#deleteMealItem, [mealItem]),
returnValue: Future.value(null),
returnValueForMissingStub: Future.value()) as _i10.Future);
@override
- _i10.Future<_i7.Ingredient> fetchIngredient(int? ingredientId) => (super.noSuchMethod(
- Invocation.method(#fetchIngredient, [ingredientId]),
- returnValue: Future<_i7.Ingredient>.value(_FakeIngredient())) as _i10.Future<_i7.Ingredient>);
+ _i10.Future<_i7.Ingredient> fetchIngredient(int? ingredientId) =>
+ (super.noSuchMethod(Invocation.method(#fetchIngredient, [ingredientId]),
+ returnValue: Future<_i7.Ingredient>.value(_FakeIngredient()))
+ as _i10.Future<_i7.Ingredient>);
@override
_i10.Future fetchIngredientsFromCache() =>
(super.noSuchMethod(Invocation.method(#fetchIngredientsFromCache, []),
returnValue: Future.value(null),
returnValueForMissingStub: Future.value()) as _i10.Future);
@override
- _i10.Future> searchIngredient(String? name, [String? languageCode = r'en']) =>
- (super.noSuchMethod(Invocation.method(#searchIngredient, [name, languageCode]),
- returnValue: Future>.value([])) as _i10.Future>);
+ _i10.Future> searchIngredient(String? name,
+ [String? languageCode = r'en']) =>
+ (super.noSuchMethod(
+ Invocation.method(#searchIngredient, [name, languageCode]),
+ returnValue: Future>.value([]))
+ as _i10.Future>);
@override
_i10.Future logMealToDiary(_i5.Meal? meal) =>
(super.noSuchMethod(Invocation.method(#logMealToDiary, [meal]),
returnValue: Future.value(null),
returnValueForMissingStub: Future.value()) as _i10.Future);
@override
- _i10.Future fetchAndSetAllLogs() =>
- (super.noSuchMethod(Invocation.method(#fetchAndSetAllLogs, []),
- returnValue: Future.value(null),
- returnValueForMissingStub: Future.value()) as _i10.Future);
- @override
_i10.Future fetchAndSetLogs(_i4.NutritionalPlan? plan) =>
(super.noSuchMethod(Invocation.method(#fetchAndSetLogs, [plan]),
returnValue: Future.value(null),
returnValueForMissingStub: Future.value()) as _i10.Future);
@override
- void addListener(_i11.VoidCallback? listener) => super
- .noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null);
+ void addListener(_i11.VoidCallback? listener) =>
+ super.noSuchMethod(Invocation.method(#addListener, [listener]),
+ returnValueForMissingStub: null);
@override
void removeListener(_i11.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#removeListener, [listener]),
returnValueForMissingStub: null);
@override
- void dispose() =>
- super.noSuchMethod(Invocation.method(#dispose, []), returnValueForMissingStub: null);
+ void dispose() => super.noSuchMethod(Invocation.method(#dispose, []),
+ returnValueForMissingStub: null);
@override
void notifyListeners() =>
- super.noSuchMethod(Invocation.method(#notifyListeners, []), returnValueForMissingStub: null);
+ super.noSuchMethod(Invocation.method(#notifyListeners, []),
+ returnValueForMissingStub: null);
@override
- dynamic makeUrl(String? path, {int? id, String? objectMethod, Map? query}) =>
- super.noSuchMethod(Invocation.method(
- #makeUrl, [path], {#id: id, #objectMethod: objectMethod, #query: query}));
+ dynamic makeUrl(String? path,
+ {int? id, String? objectMethod, Map? query}) =>
+ super.noSuchMethod(Invocation.method(#makeUrl, [path],
+ {#id: id, #objectMethod: objectMethod, #query: query}));
@override
- _i10.Future