Skip to content

Commit 1d896c6

Browse files
committed
Allow searching for exercise names in English as well
See wger-project/wger/issues/1238
1 parent cd6fdd7 commit 1d896c6

File tree

5 files changed

+126
-93
lines changed

5 files changed

+126
-93
lines changed

lib/l10n/app_en.arb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@
122122
"@searchExercise": {
123123
"description": "Label on set form. Selected exercises are added to the set"
124124
},
125+
"searchNamesInEnglish": "Also search for names in English",
125126
"supersetWith": "superset with",
126127
"@supersetWith": {
127128
"description": "Text used between exercise cards when adding a new set. Translate as something like 'in a superset with'"

lib/providers/exercises.dart

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -426,16 +426,22 @@ class ExercisesProvider with ChangeNotifier {
426426
///
427427
/// We could do this locally, but the server has better text searching capabilities
428428
/// with postgresql.
429-
Future<List<ExerciseBase>> searchExercise(String name, [String languageCode = 'en']) async {
429+
Future<List<ExerciseBase>> searchExercise(String name,
430+
{String languageCode = LANGUAGE_SHORT_ENGLISH, bool searchEnglish = false}) async {
430431
if (name.length <= 1) {
431432
return [];
432433
}
433434

435+
final languages = [languageCode];
436+
if (searchEnglish && languageCode != LANGUAGE_SHORT_ENGLISH) {
437+
languages.add(LANGUAGE_SHORT_ENGLISH);
438+
}
439+
434440
// Send the request
435441
final result = await baseProvider.fetch(
436442
baseProvider.makeUrl(
437443
_exerciseSearchPath,
438-
query: {'term': name, 'language': languageCode},
444+
query: {'term': name, 'language': languages.join(',')},
439445
),
440446
);
441447

lib/widgets/workouts/forms.dart

Lines changed: 101 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ class SetFormWidget extends StatefulWidget {
270270
class _SetFormWidgetState extends State<SetFormWidget> {
271271
double _currentSetSliderValue = Set.DEFAULT_NR_SETS.toDouble();
272272
bool _detailed = false;
273+
bool _searchEnglish = true;
273274

274275
// Form stuff
275276
final GlobalKey<FormState> _formKey = GlobalKey();
@@ -292,6 +293,8 @@ class _SetFormWidgetState extends State<SetFormWidget> {
292293

293294
/// Adds settings to the set
294295
void addSettings() {
296+
final workoutProvider = context.read<WorkoutPlansProvider>();
297+
295298
widget._set.settings = [];
296299
int order = 0;
297300
for (final exercise in widget._set.exerciseBasesObj) {
@@ -300,10 +303,8 @@ class _SetFormWidgetState extends State<SetFormWidget> {
300303
final Setting setting = Setting.empty();
301304
setting.order = order;
302305
setting.exerciseBase = exercise;
303-
setting.weightUnit =
304-
Provider.of<WorkoutPlansProvider>(context, listen: false).defaultWeightUnit;
305-
setting.repetitionUnit =
306-
Provider.of<WorkoutPlansProvider>(context, listen: false).defaultRepetitionUnit;
306+
setting.weightUnit = workoutProvider.defaultWeightUnit;
307+
setting.repetitionUnit = workoutProvider.defaultRepetitionUnit;
307308

308309
widget._set.settings.add(setting);
309310
}
@@ -355,84 +356,104 @@ class _SetFormWidgetState extends State<SetFormWidget> {
355356
child: Column(
356357
children: [
357358
Card(
358-
child: TypeAheadFormField<ExerciseBase>(
359-
key: const Key('field-typeahead'),
360-
textFieldConfiguration: TextFieldConfiguration(
361-
controller: _exercisesController,
362-
decoration: InputDecoration(
363-
labelText: AppLocalizations.of(context).searchExercise,
364-
prefixIcon: const Icon(Icons.search),
365-
suffixIcon: IconButton(
366-
icon: const Icon(Icons.help),
367-
onPressed: () {
368-
showDialog(
369-
context: context,
370-
builder: (context) => AlertDialog(
371-
content: Column(
372-
mainAxisSize: MainAxisSize.min,
373-
children: [
374-
Text(AppLocalizations.of(context).selectExercises),
375-
const SizedBox(height: 10),
376-
Text(AppLocalizations.of(context).sameRepetitions)
377-
],
378-
),
379-
actions: [
380-
TextButton(
381-
child: Text(MaterialLocalizations.of(context).closeButtonLabel),
382-
onPressed: () {
383-
Navigator.of(context).pop();
384-
},
359+
child: Column(
360+
children: [
361+
TypeAheadFormField<ExerciseBase>(
362+
key: const Key('field-typeahead'),
363+
textFieldConfiguration: TextFieldConfiguration(
364+
controller: _exercisesController,
365+
decoration: InputDecoration(
366+
labelText: AppLocalizations.of(context).searchExercise,
367+
prefixIcon: const Icon(Icons.search),
368+
suffixIcon: IconButton(
369+
icon: const Icon(Icons.help),
370+
onPressed: () {
371+
showDialog(
372+
context: context,
373+
builder: (context) => AlertDialog(
374+
content: Column(
375+
mainAxisSize: MainAxisSize.min,
376+
children: [
377+
Text(AppLocalizations.of(context).selectExercises),
378+
const SizedBox(height: 10),
379+
Text(AppLocalizations.of(context).sameRepetitions)
380+
],
381+
),
382+
actions: [
383+
TextButton(
384+
child: Text(
385+
MaterialLocalizations.of(context).closeButtonLabel),
386+
onPressed: () {
387+
Navigator.of(context).pop();
388+
},
389+
),
390+
],
385391
),
386-
],
387-
),
388-
);
389-
},
392+
);
393+
},
394+
),
395+
errorMaxLines: 2,
396+
border: InputBorder.none,
397+
),
390398
),
391-
errorMaxLines: 2,
399+
suggestionsCallback: (pattern) {
400+
return context.read<ExercisesProvider>().searchExercise(
401+
pattern,
402+
languageCode: Localizations.localeOf(context).languageCode,
403+
searchEnglish: _searchEnglish,
404+
);
405+
},
406+
itemBuilder: (BuildContext context, ExerciseBase exerciseSuggestion) {
407+
return ListTile(
408+
leading: SizedBox(
409+
width: 45,
410+
child: ExerciseImageWidget(image: exerciseSuggestion.getMainImage),
411+
),
412+
title: Text(
413+
exerciseSuggestion
414+
.getExercise(Localizations.localeOf(context).languageCode)
415+
.name,
416+
),
417+
subtitle: Text(
418+
'${exerciseSuggestion.category.name} / ${exerciseSuggestion.equipment.map((e) => e.name).join(', ')}',
419+
),
420+
);
421+
},
422+
transitionBuilder: (context, suggestionsBox, controller) {
423+
return suggestionsBox;
424+
},
425+
onSuggestionSelected: (ExerciseBase exerciseSuggestion) {
426+
addExercise(exerciseSuggestion);
427+
this._exercisesController.text = '';
428+
},
429+
validator: (value) {
430+
// At least one exercise must be selected
431+
if (widget._set.exerciseBasesIds.isEmpty) {
432+
return AppLocalizations.of(context).selectExercise;
433+
}
434+
435+
// At least one setting has to be filled in
436+
if (widget._set.settings
437+
.where((s) => s.weight == null && s.reps == null)
438+
.length ==
439+
widget._set.settings.length) {
440+
return AppLocalizations.of(context).enterRepetitionsOrWeight;
441+
}
442+
return null;
443+
},
392444
),
393-
),
394-
suggestionsCallback: (pattern) {
395-
return Provider.of<ExercisesProvider>(context, listen: false).searchExercise(
396-
pattern,
397-
Localizations.localeOf(context).languageCode,
398-
);
399-
},
400-
itemBuilder: (BuildContext context, ExerciseBase exerciseSuggestion) {
401-
return ListTile(
402-
leading: SizedBox(
403-
width: 45,
404-
child: ExerciseImageWidget(image: exerciseSuggestion.getMainImage),
405-
),
406-
title: Text(exerciseSuggestion
407-
.getExercise(Localizations.localeOf(context).languageCode)
408-
.name),
409-
subtitle: Text(
410-
'${exerciseSuggestion.category.name} / ${exerciseSuggestion.equipment.map((e) => e.name).join(', ')}',
411-
),
412-
);
413-
},
414-
transitionBuilder: (context, suggestionsBox, controller) {
415-
return suggestionsBox;
416-
},
417-
onSuggestionSelected: (ExerciseBase exerciseSuggestion) {
418-
addExercise(exerciseSuggestion);
419-
this._exercisesController.text = '';
420-
},
421-
validator: (value) {
422-
// At least one exercise must be selected
423-
if (widget._set.exerciseBasesIds.isEmpty) {
424-
return AppLocalizations.of(context).selectExercise;
425-
}
426-
427-
// At least one setting has to be filled in
428-
if (widget._set.settings
429-
.where((s) => s.weight == null && s.reps == null)
430-
.length ==
431-
widget._set.settings.length) {
432-
return AppLocalizations.of(context).enterRepetitionsOrWeight;
433-
}
434-
return null;
435-
},
445+
if (Localizations.localeOf(context).languageCode != LANGUAGE_SHORT_ENGLISH)
446+
SwitchListTile(
447+
title: Text(AppLocalizations.of(context).searchNamesInEnglish),
448+
value: _searchEnglish,
449+
onChanged: (_) {
450+
setState(() {
451+
_searchEnglish = !_searchEnglish;
452+
});
453+
},
454+
dense: true,
455+
)
456+
],
436457
),
437458
),
438459
const SizedBox(height: 10),
@@ -859,6 +880,7 @@ class _WeightUnitInputWidgetState extends State<WeightUnitInputWidget> {
859880
/// Can be used with a Setting or a Log object
860881
class RepetitionUnitInputWidget extends StatefulWidget {
861882
final dynamic _setting;
883+
862884
const RepetitionUnitInputWidget(this._setting);
863885

864886
@override

test/workout/gym_mode_screen_test.mocks.dart

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -561,16 +561,18 @@ class MockExercisesProvider extends _i1.Mock implements _i11.ExercisesProvider {
561561
) as _i10.Future<void>);
562562
@override
563563
_i10.Future<List<_i5.ExerciseBase>> searchExercise(
564-
String? name, [
564+
String? name, {
565565
String? languageCode = r'en',
566-
]) =>
566+
bool? searchEnglish = false,
567+
}) =>
567568
(super.noSuchMethod(
568569
Invocation.method(
569570
#searchExercise,
570-
[
571-
name,
572-
languageCode,
573-
],
571+
[name],
572+
{
573+
#languageCode: languageCode,
574+
#searchEnglish: searchEnglish,
575+
},
574576
),
575577
returnValue: _i10.Future<List<_i5.ExerciseBase>>.value(<_i5.ExerciseBase>[]),
576578
) as _i10.Future<List<_i5.ExerciseBase>>);

test/workout/workout_set_form_test.mocks.dart

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -500,16 +500,18 @@ class MockExercisesProvider extends _i1.Mock implements _i18.ExercisesProvider {
500500
) as _i19.Future<void>);
501501
@override
502502
_i19.Future<List<_i3.ExerciseBase>> searchExercise(
503-
String? name, [
503+
String? name, {
504504
String? languageCode = r'en',
505-
]) =>
505+
bool? searchEnglish = false,
506+
}) =>
506507
(super.noSuchMethod(
507508
Invocation.method(
508509
#searchExercise,
509-
[
510-
name,
511-
languageCode,
512-
],
510+
[name],
511+
{
512+
#languageCode: languageCode,
513+
#searchEnglish: searchEnglish,
514+
},
513515
),
514516
returnValue: _i19.Future<List<_i3.ExerciseBase>>.value(<_i3.ExerciseBase>[]),
515517
) as _i19.Future<List<_i3.ExerciseBase>>);

0 commit comments

Comments
 (0)