From ca007d2335202bc90b07adb1d084f791fedf3da0 Mon Sep 17 00:00:00 2001 From: Roland Geider Date: Sat, 25 Oct 2025 02:08:34 +0200 Subject: [PATCH] Move more usages to new exercise provider --- lib/providers/exercise_data.dart | 18 ++++++ lib/providers/exercise_state.dart | 18 ++++++ lib/providers/exercise_state_notifier.dart | 58 ++++++++++++++++++- lib/screens/add_exercise_screen.dart | 28 +++++++-- .../add_exercise/steps/step_1_basics.dart | 30 ++++++---- .../steps/step_4_translations.dart | 14 +++-- lib/widgets/routines/gym_mode/gym_mode.dart | 5 +- 7 files changed, 146 insertions(+), 25 deletions(-) diff --git a/lib/providers/exercise_data.dart b/lib/providers/exercise_data.dart index 37417713..a0a3c70c 100644 --- a/lib/providers/exercise_data.dart +++ b/lib/providers/exercise_data.dart @@ -1,3 +1,21 @@ +/* + * This file is part of wger Workout Manager . + * Copyright (C) 2020, 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:drift/drift.dart'; import 'package:logging/logging.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; diff --git a/lib/providers/exercise_state.dart b/lib/providers/exercise_state.dart index 0eb6d38e..fb863f8d 100644 --- a/lib/providers/exercise_state.dart +++ b/lib/providers/exercise_state.dart @@ -1,3 +1,21 @@ +/* + * This file is part of wger Workout Manager . + * Copyright (C) 2020, 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:wger/models/exercises/category.dart'; import 'package:wger/models/exercises/equipment.dart'; import 'package:wger/models/exercises/exercise.dart'; diff --git a/lib/providers/exercise_state_notifier.dart b/lib/providers/exercise_state_notifier.dart index 5e7b6e8c..9ebd452a 100644 --- a/lib/providers/exercise_state_notifier.dart +++ b/lib/providers/exercise_state_notifier.dart @@ -1,3 +1,21 @@ +/* + * This file is part of wger Workout Manager . + * Copyright (C) 2020, 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:logging/logging.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:wger/models/exercises/category.dart'; @@ -5,7 +23,6 @@ import 'package:wger/models/exercises/equipment.dart'; import 'package:wger/models/exercises/exercise.dart'; import 'package:wger/providers/exercise_data.dart'; import 'package:wger/providers/exercise_state.dart'; -import 'package:wger/providers/exercises.dart'; part 'exercise_state_notifier.g.dart'; @@ -130,4 +147,43 @@ final class ExerciseStateNotifier extends _$ExerciseStateNotifier { return null; } } + + Future> searchExercise( + String term, { + bool useServer = false, + String languageCode = 'en', + bool searchEnglish = false, + }) async { + if (term.trim().length <= 1) { + return []; + } + + // Local mode: search in the sqlite db + if (!useServer) { + final languages = [languageCode]; + if (searchEnglish && languageCode != 'en') { + languages.add('en'); + } + + final List out = []; + + for (final e in state.exercises) { + var matched = false; + for (final lang in languages) { + final title = (e.getTranslation(lang).name ?? '').toLowerCase(); + if (title.contains(term.toLowerCase())) { + matched = true; + break; + } + } + if (matched) { + out.add(e); + } + } + return out; + } + + // Online mode, use server-side search API + return []; + } } diff --git a/lib/screens/add_exercise_screen.dart b/lib/screens/add_exercise_screen.dart index 83dd447e..2da40f1a 100644 --- a/lib/screens/add_exercise_screen.dart +++ b/lib/screens/add_exercise_screen.dart @@ -1,4 +1,23 @@ +/* + * This file is part of wger Workout Manager . + * Copyright (C) 2020, 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:provider/provider.dart'; import 'package:wger/exceptions/http_exception.dart'; import 'package:wger/helpers/consts.dart'; @@ -6,7 +25,7 @@ import 'package:wger/helpers/errors.dart'; import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/exercises/exercise.dart'; import 'package:wger/providers/add_exercise.dart'; -import 'package:wger/providers/exercises.dart'; +import 'package:wger/providers/exercise_state_notifier.dart'; import 'package:wger/providers/user.dart'; import 'package:wger/screens/exercise_screen.dart'; import 'package:wger/widgets/add_exercise/steps/step_1_basics.dart'; @@ -33,7 +52,7 @@ class AddExerciseScreen extends StatelessWidget { } } -class AddExerciseStepper extends StatefulWidget { +class AddExerciseStepper extends ConsumerStatefulWidget { const AddExerciseStepper({super.key}); static const STEPS_IN_FORM = 6; @@ -42,7 +61,7 @@ class AddExerciseStepper extends StatefulWidget { _AddExerciseStepperState createState() => _AddExerciseStepperState(); } -class _AddExerciseStepperState extends State { +class _AddExerciseStepperState extends ConsumerState { int _currentStep = 0; int lastStepIndex = AddExerciseStepper.STEPS_IN_FORM - 1; bool _isLoading = false; @@ -81,12 +100,11 @@ class _AddExerciseStepperState extends State { errorWidget = const SizedBox.shrink(); }); final addExerciseProvider = context.read(); - final exerciseProvider = context.read(); Exercise? exercise; try { final exerciseId = await addExerciseProvider.postExerciseToServer(); - exercise = await exerciseProvider.fetchAndSetExercise(exerciseId); + exercise = ref.read(exerciseStateProvider.notifier).getById(exerciseId); } on WgerHttpException catch (error) { if (context.mounted) { setState(() { diff --git a/lib/widgets/add_exercise/steps/step_1_basics.dart b/lib/widgets/add_exercise/steps/step_1_basics.dart index 12eb38dd..26475f70 100644 --- a/lib/widgets/add_exercise/steps/step_1_basics.dart +++ b/lib/widgets/add_exercise/steps/step_1_basics.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart' hide Consumer; import 'package:provider/provider.dart'; import 'package:wger/helpers/consts.dart'; import 'package:wger/helpers/exercises/validators.dart'; @@ -6,33 +7,40 @@ import 'package:wger/helpers/i18n.dart'; import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/exercises/category.dart'; import 'package:wger/models/exercises/equipment.dart'; +import 'package:wger/models/exercises/language.dart'; import 'package:wger/models/exercises/muscle.dart'; import 'package:wger/providers/add_exercise.dart'; -import 'package:wger/providers/exercises.dart'; +import 'package:wger/providers/core_data.dart'; +import 'package:wger/providers/exercise_data.dart'; import 'package:wger/providers/user.dart'; import 'package:wger/widgets/add_exercise/add_exercise_multiselect_button.dart'; import 'package:wger/widgets/add_exercise/add_exercise_text_area.dart'; import 'package:wger/widgets/exercises/exercises.dart'; import 'package:wger/widgets/exercises/forms.dart'; -class Step1Basics extends StatelessWidget { +class Step1Basics extends ConsumerWidget { final GlobalKey formkey; const Step1Basics({required this.formkey}); @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { final userProvider = context.read(); final addExerciseProvider = context.read(); - final exerciseProvider = context.read(); - final categories = exerciseProvider.categories; - final muscles = exerciseProvider.muscles; - final equipment = exerciseProvider.equipment; - // There mus be a better way to ensure this... - addExerciseProvider.languageEn = exerciseProvider.languages.firstWhere( - (l) => l.shortName == LANGUAGE_SHORT_ENGLISH, - ); + final languagesAsync = ref.watch(languageProvider); + final categoriesAsync = ref.watch(exerciseCategoryProvider); + final musclesAsync = ref.watch(exerciseMuscleProvider); + final equipmentAsync = ref.watch(exerciseEquipmentProvider); + + final categories = categoriesAsync.asData?.value ?? []; + final muscles = musclesAsync.asData?.value ?? []; + final equipment = equipmentAsync.asData?.value ?? []; + final languages = languagesAsync.asData?.value ?? []; + + // Doing it like this because the languages list is empty before the stream loads + final matched = languages.where((l) => l.shortName == LANGUAGE_SHORT_ENGLISH); + addExerciseProvider.languageEn = matched.isNotEmpty ? matched.first : null; return Form( key: formkey, diff --git a/lib/widgets/add_exercise/steps/step_4_translations.dart b/lib/widgets/add_exercise/steps/step_4_translations.dart index d8f4a778..18490ff3 100644 --- a/lib/widgets/add_exercise/steps/step_4_translations.dart +++ b/lib/widgets/add_exercise/steps/step_4_translations.dart @@ -1,31 +1,33 @@ import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart' hide Consumer; import 'package:provider/provider.dart'; import 'package:wger/helpers/exercises/validators.dart'; import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/exercises/language.dart'; import 'package:wger/providers/add_exercise.dart'; -import 'package:wger/providers/exercises.dart'; +import 'package:wger/providers/core_data.dart'; import 'package:wger/widgets/add_exercise/add_exercise_text_area.dart'; import 'package:wger/widgets/exercises/forms.dart'; -class Step4Translation extends StatefulWidget { +class Step4Translation extends ConsumerStatefulWidget { final GlobalKey formkey; const Step4Translation({required this.formkey}); @override - State createState() => _Step4TranslationState(); + ConsumerState createState() => _Step4TranslationState(); } -class _Step4TranslationState extends State { +class _Step4TranslationState extends ConsumerState { bool translate = false; @override Widget build(BuildContext context) { final i18n = AppLocalizations.of(context); final addExerciseProvider = context.read(); - final exerciseProvider = context.read(); - final languages = exerciseProvider.languages; + + final languagesAsync = ref.watch(languageProvider); + final languages = languagesAsync.asData?.value ?? []; return Form( key: widget.formkey, diff --git a/lib/widgets/routines/gym_mode/gym_mode.dart b/lib/widgets/routines/gym_mode/gym_mode.dart index e769ed9b..42beef09 100644 --- a/lib/widgets/routines/gym_mode/gym_mode.dart +++ b/lib/widgets/routines/gym_mode/gym_mode.dart @@ -23,6 +23,7 @@ import 'package:logging/logging.dart'; import 'package:provider/provider.dart' as provider; import 'package:wger/models/exercises/exercise.dart'; import 'package:wger/models/workouts/day_data.dart'; +import 'package:wger/providers/exercise_state_notifier.dart'; import 'package:wger/providers/exercises.dart'; import 'package:wger/providers/gym_state.dart'; import 'package:wger/providers/routines.dart'; @@ -131,7 +132,7 @@ class _GymModeState extends ConsumerState { List getContent() { final state = ref.watch(gymStateProvider); - final exerciseProvider = context.read(); + final exercisesAsync = ref.watch(exerciseStateProvider.notifier); final routinesProvider = context.read(); var currentElement = 1; final List out = []; @@ -140,7 +141,7 @@ class _GymModeState extends ConsumerState { var firstPage = true; for (final config in slotData.setConfigs) { final ratioCompleted = currentElement / _totalElements; - final exercise = exerciseProvider.findExerciseById(config.exerciseId); + final exercise = exercisesAsync.getById(config.exerciseId)!; currentElement++; if (firstPage && state.showExercisePages) {