diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 0199bc61..e269052e 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -88,7 +88,7 @@ "@noWorkoutPlans": { "description": "Message shown when the user has no workout plans" }, - "addExercise": "Add exercise to this day", + "addExercise": "Add exercise", "repetitions": "Repetitions", "@repetitions": { "description": "Repetitions for an exercise set" diff --git a/lib/models/workouts/day.dart b/lib/models/workouts/day.dart index fcb2243d..abe6f8a1 100644 --- a/lib/models/workouts/day.dart +++ b/lib/models/workouts/day.dart @@ -5,6 +5,16 @@ part 'day.g.dart'; @JsonSerializable() class Day { + static const Map weekdays = { + 1: 'Monday', + 2: 'Tuesday', + 3: 'Wednesday', + 4: 'Thursday', + 5: 'Friday', + 6: 'Saturday', + 7: 'Sunday', + }; + @JsonKey(required: true) int? id; @@ -28,27 +38,6 @@ class Day { this.sets = []; } - Day.withData({ - required this.id, - required this.workoutId, - required this.description, - List? daysOfWeek, - List? sets, - }) { - this.daysOfWeek = daysOfWeek ?? []; - this.sets = sets ?? []; - } - - static const Map weekdays = { - 1: 'Monday', - 2: 'Tuesday', - 3: 'Wednesday', - 4: 'Thursday', - 5: 'Friday', - 6: 'Saturday', - 7: 'Sunday', - }; - String getDayName(int weekDay) { return weekdays[weekDay]!; } diff --git a/lib/models/workouts/set.dart b/lib/models/workouts/set.dart index eda7ea40..e23c0f5a 100644 --- a/lib/models/workouts/set.dart +++ b/lib/models/workouts/set.dart @@ -60,6 +60,23 @@ class Set { } } + /// Return only one setting object per exercise, this makes rendering workout + /// plans easier and the gym mode uses the synthetic settings anyway. + List get settingsFiltered { + List out = []; + + settings.forEach((setting) { + final foundSettings = out.where( + (element) => element.exerciseId == setting.exerciseId, + ); + + if (foundSettings.length == 0) { + out.add(setting); + } + }); + return out; + } + void addExercise(Exercise exercise) { exercisesObj.add(exercise); exercisesIds.add(exercise.id); diff --git a/lib/models/workouts/setting.dart b/lib/models/workouts/setting.dart index 63d37ee7..6f4a143f 100644 --- a/lib/models/workouts/setting.dart +++ b/lib/models/workouts/setting.dart @@ -8,7 +8,8 @@ part 'setting.g.dart'; @JsonSerializable() class Setting { - static final possibleValues = ['1', '1.5', '2', '2.5', '3', '3.5']; + static final possibleRiRValues = ['1', '1.5', '2', '2.5', '3', '3.5']; + static final defaultRiR = '2'; @JsonKey(required: true) int? id; @@ -87,7 +88,7 @@ class Setting { } void setRir(String rir) { - if (possibleValues.contains(rir)) { + if (possibleRiRValues.contains(rir)) { this.rir = rir; } else { throw Exception('RiR value not allowed'); diff --git a/lib/providers/workout_plans.dart b/lib/providers/workout_plans.dart index e01d1192..40ab6934 100644 --- a/lib/providers/workout_plans.dart +++ b/lib/providers/workout_plans.dart @@ -140,6 +140,7 @@ class WorkoutPlans extends WgerBaseProvider with ChangeNotifier { final setData = await fetch(makeUrl(_setsUrlPath, query: {'exerciseday': day.id.toString()})); for (final setEntry in setData['results']) { final workoutSet = Set.fromJson(setEntry); + fetchComputedSettings(workoutSet); // Settings List settings = []; @@ -182,18 +183,6 @@ class WorkoutPlans extends WgerBaseProvider with ChangeNotifier { notifyListeners(); } - Future fetchAndSetWorkouts() async { - final data = await fetch(makeUrl(_workoutPlansUrlPath, query: {'ordering': '-creation_date'})); - final List loadedWorkoutPlans = []; - - for (final entry in data['results']) { - loadedWorkoutPlans.add(WorkoutPlan.fromJson(entry)); - } - - _workoutPlans = loadedWorkoutPlans; - notifyListeners(); - } - Future addWorkout(WorkoutPlan workout) async { final data = await post(workout.toJson(), makeUrl(_workoutPlansUrlPath)); final plan = WorkoutPlan.fromJson(data); @@ -325,10 +314,38 @@ class WorkoutPlans extends WgerBaseProvider with ChangeNotifier { Future addSet(Set workoutSet) async { final data = await post(workoutSet.toJson(), makeUrl(_setsUrlPath)); final set = Set.fromJson(data); + fetchComputedSettings(set); notifyListeners(); return set; } + Future fetchComputedSettings(Set workoutSet) async { + final data = await fetch(makeUrl( + _setsUrlPath, + id: workoutSet.id!, + objectMethod: 'computed_settings', + )); + + List settings = []; + data['results'].forEach((e) => settings.add(Setting.fromJson(e))); + + workoutSet.settingsComputed = settings; + notifyListeners(); + } + + Future fetchSmartText(Set workoutSet, Exercise exercise) async { + final data = await fetch( + makeUrl( + _setsUrlPath, + id: workoutSet.id!, + objectMethod: 'smart_text', + query: {'exercise': exercise.id.toString()}, + ), + ); + + return data['results']; + } + Future deleteSet(Set workoutSet) async { await deleteRequest(_setsUrlPath, workoutSet.id!); diff --git a/lib/screens/gym_mode.dart b/lib/screens/gym_mode.dart index 2959fdab..49dca77a 100644 --- a/lib/screens/gym_mode.dart +++ b/lib/screens/gym_mode.dart @@ -35,6 +35,7 @@ class _GymModeScreenState extends State { final _day = ModalRoute.of(context)!.settings.arguments as Day; return Scaffold( + backgroundColor: Colors.green, body: Consumer( builder: (context, value, child) => GymMode(_day), ), diff --git a/lib/widgets/dashboard/widgets.dart b/lib/widgets/dashboard/widgets.dart index 788772ca..5117a6d5 100644 --- a/lib/widgets/dashboard/widgets.dart +++ b/lib/widgets/dashboard/widgets.dart @@ -258,7 +258,7 @@ class _DashboardWorkoutWidgetState extends State { day.sets.forEach((set) { out.add(Column( children: [ - ...set.settings.map((s) { + ...set.settingsFiltered.map((s) { return Column( children: [ Text(s.exerciseObj.name), diff --git a/lib/widgets/workouts/day.dart b/lib/widgets/workouts/day.dart index 29f8a383..39fcbf57 100644 --- a/lib/widgets/workouts/day.dart +++ b/lib/widgets/workouts/day.dart @@ -75,7 +75,7 @@ class _WorkoutDayWidgetState extends State { Expanded( child: Column( children: [ - ...set.settings + ...set.settingsFiltered .map( (setting) => SettingWidget( setting: setting, diff --git a/lib/widgets/workouts/forms.dart b/lib/widgets/workouts/forms.dart index 019f8d2d..d0d0755b 100644 --- a/lib/widgets/workouts/forms.dart +++ b/lib/widgets/workouts/forms.dart @@ -606,13 +606,15 @@ class _RiRInputWidgetState extends State { return DropdownButtonFormField( decoration: InputDecoration(labelText: AppLocalizations.of(context)!.rir), value: dropdownValue, + onSaved: (String? newValue) { + widget._setting.setRir(newValue!); + }, onChanged: (String? newValue) { setState(() { dropdownValue = newValue!; - widget._setting.setRir(newValue); }); }, - items: Setting.possibleValues.map>((String value) { + items: Setting.possibleRiRValues.map>((String value) { return DropdownMenuItem( value: value, child: Text(value), diff --git a/lib/widgets/workouts/gym_mode.dart b/lib/widgets/workouts/gym_mode.dart index 79866d16..e80dcf07 100644 --- a/lib/widgets/workouts/gym_mode.dart +++ b/lib/widgets/workouts/gym_mode.dart @@ -117,7 +117,7 @@ class StartPage extends StatelessWidget { (set) { return Column( children: [ - ...set.settings.map((s) { + ...set.settingsFiltered.map((s) { return Column( children: [ Text(s.exerciseObj.name, style: TextStyle(fontWeight: FontWeight.bold)), @@ -142,14 +142,13 @@ class StartPage extends StatelessWidget { } } -class LogPage extends StatelessWidget { +class LogPage extends StatefulWidget { PageController _controller; Setting _setting; Exercise _exercise; WorkoutPlan _workoutPlan; Log _log = Log.empty(); - final _form = GlobalKey(); final _repsController = TextEditingController(); final _weightController = TextEditingController(); final _rirController = TextEditingController(); @@ -174,6 +173,14 @@ class LogPage extends StatelessWidget { _log.weightUnit = 1; } + @override + _LogPageState createState() => _LogPageState(); +} + +class _LogPageState extends State { + final _form = GlobalKey(); + String rirValue = Setting.defaultRiR; + @override Widget build(BuildContext context) { return Container( @@ -184,7 +191,7 @@ class LogPage extends StatelessWidget { Padding( padding: const EdgeInsets.symmetric(vertical: 10), child: Text( - _exercise.name, + widget._exercise.name, style: Theme.of(context).textTheme.headline5, ), ), @@ -196,11 +203,28 @@ class LogPage extends StatelessWidget { children: [ TextFormField( decoration: InputDecoration(labelText: AppLocalizations.of(context)!.repetitions), - controller: _repsController, + controller: widget._repsController, keyboardType: TextInputType.number, onFieldSubmitted: (_) {}, onSaved: (newValue) { - _log.reps = int.parse(newValue!); + widget._log.reps = int.parse(newValue!); + }, + validator: (value) { + try { + int.parse(value!); + } catch (error) { + return AppLocalizations.of(context)!.enterValidNumber; + } + return null; + }, + ), + TextFormField( + decoration: InputDecoration(labelText: AppLocalizations.of(context)!.weight), + controller: widget._weightController, + keyboardType: TextInputType.number, + onFieldSubmitted: (_) {}, + onSaved: (newValue) { + widget._log.weight = double.parse(newValue!); }, validator: (value) { try { @@ -211,21 +235,23 @@ class LogPage extends StatelessWidget { return null; }, ), - TextFormField( - decoration: InputDecoration(labelText: AppLocalizations.of(context)!.weight), - controller: _weightController, - keyboardType: TextInputType.number, - onFieldSubmitted: (_) {}, - onSaved: (newValue) { - _log.weight = double.parse(newValue!); - }, - ), - TextFormField( + DropdownButtonFormField( decoration: InputDecoration(labelText: AppLocalizations.of(context)!.rir), - controller: _rirController, - keyboardType: TextInputType.number, - onFieldSubmitted: (_) {}, - onSaved: (newValue) {}, + value: rirValue, + onSaved: (String? newValue) { + widget._log.rir = newValue!; + }, + onChanged: (String? newValue) { + setState(() { + rirValue = newValue!; + }); + }, + items: Setting.possibleRiRValues.map>((String value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), ), /* TextFormField( @@ -252,10 +278,10 @@ class LogPage extends StatelessWidget { // Save the entry on the server try { - await Provider.of(context, listen: false).addLog(_log); + await Provider.of(context, listen: false).addLog(widget._log); //final snackBar = SnackBar(content: Text('Yay! A SnackBar!')); //ScaffoldMessenger.of(context).showSnackBar(snackBar); - _controller.nextPage( + widget._controller.nextPage( duration: Duration(milliseconds: 200), curve: Curves.bounceIn, ); @@ -269,7 +295,7 @@ class LogPage extends StatelessWidget { ], ), ), - NavigationFooter(_controller), + NavigationFooter(widget._controller), ], ), );