diff --git a/lib/providers/exercises.dart b/lib/providers/exercises.dart index 252f0ff4..719065c2 100644 --- a/lib/providers/exercises.dart +++ b/lib/providers/exercises.dart @@ -37,6 +37,7 @@ import 'package:wger/models/exercises/translation.dart'; import 'package:wger/models/exercises/variation.dart'; import 'package:wger/models/exercises/video.dart'; import 'package:wger/providers/base_provider.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class ExercisesProvider with ChangeNotifier { final WgerBaseProvider baseProvider; @@ -115,7 +116,7 @@ class ExercisesProvider with ChangeNotifier { } // Initialize filters for exercises search in exercises list - void _initFilters() { + void _initFilters(BuildContext context) { if (_muscles.isEmpty || _equipment.isEmpty || _filters != null) { return; } @@ -123,7 +124,7 @@ class ExercisesProvider with ChangeNotifier { setFilters( Filters( exerciseCategories: FilterCategory( - title: 'Category', + title: AppLocalizations.of(context).category, items: Map.fromEntries( _categories.map( (category) => MapEntry(category, false), @@ -131,7 +132,7 @@ class ExercisesProvider with ChangeNotifier { ), ), equipment: FilterCategory( - title: 'Equipment', + title: AppLocalizations.of(context).equipment, items: Map.fromEntries( _equipment.map( (singleEquipment) => MapEntry(singleEquipment, false), @@ -362,7 +363,7 @@ class ExercisesProvider with ChangeNotifier { } } - Future fetchAndSetExercises() async { + Future fetchAndSetExercises(BuildContext context) async { clear(); // Load exercises from cache, if available @@ -379,7 +380,7 @@ class ExercisesProvider with ChangeNotifier { cacheData['variations'].forEach((e) => _variations.add(Variation.fromJson(e))); cacheData['bases'].forEach((e) => _exerciseBases.add(readExerciseBaseFromBaseInfo(e))); - _initFilters(); + _initFilters(context); log("Read ${_exerciseBases.length} exercises from cache. Valid till ${cacheData['expiresIn']}"); return; } @@ -415,7 +416,7 @@ class ExercisesProvider with ChangeNotifier { log("Saved ${_exerciseBases.length} exercises to cache. Valid till ${cacheData['expiresIn']}"); await prefs.setString(PREFS_EXERCISES, json.encode(cacheData)); - _initFilters(); + _initFilters(context); notifyListeners(); } on MissingRequiredKeysException catch (error) { log(error.missingKeys.toString()); diff --git a/lib/screens/home_tabs_screen.dart b/lib/screens/home_tabs_screen.dart index 3012770a..0dc3ca29 100644 --- a/lib/screens/home_tabs_screen.dart +++ b/lib/screens/home_tabs_screen.dart @@ -89,7 +89,7 @@ class _HomeTabsScreenState extends State with SingleTickerProvid userProvider.fetchAndSetProfile(), workoutPlansProvider.fetchAndSetUnits(), nutritionPlansProvider.fetchIngredientsFromCache(), - exercisesProvider.fetchAndSetExercises(), + exercisesProvider.fetchAndSetExercises(context), ]); // Plans, weight and gallery diff --git a/lib/screens/splash_screen.dart b/lib/screens/splash_screen.dart index c3b4df92..9dc14a7e 100644 --- a/lib/screens/splash_screen.dart +++ b/lib/screens/splash_screen.dart @@ -17,13 +17,14 @@ */ import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class SplashScreen extends StatelessWidget { @override Widget build(BuildContext context) { - return const Scaffold( + return Scaffold( body: Center( - child: Text('Loading...'), + child: Text(AppLocalizations.of(context).loadingText), ), ); } diff --git a/lib/widgets/add_exercise/add_exercise_html_editor.dart b/lib/widgets/add_exercise/add_exercise_html_editor.dart new file mode 100644 index 00000000..9014ed5b --- /dev/null +++ b/lib/widgets/add_exercise/add_exercise_html_editor.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:html_editor_enhanced/html_editor.dart'; +import 'package:provider/provider.dart'; +import 'package:wger/providers/add_exercise.dart'; + +class AddExerciseHtmlEditor extends StatefulWidget { + const AddExerciseHtmlEditor({ + Key? key, + this.helperText = '', + }) : super(key: key); + + final String helperText; + + @override + _AddExerciseHtmlEditorState createState() => _AddExerciseHtmlEditorState(); +} + +class _AddExerciseHtmlEditorState extends State { + @override + Widget build(BuildContext context) { + final addExerciseProvider = context.read(); + final HtmlEditorController editorController = HtmlEditorController( + processInputHtml: true, processNewLineAsBr: false, processOutputHtml: true); + return Padding( + padding: const EdgeInsets.all(8.0), + child: HtmlEditor( + controller: editorController, + htmlToolbarOptions: const HtmlToolbarOptions( + toolbarPosition: ToolbarPosition.belowEditor, + toolbarType: ToolbarType.nativeScrollable, + defaultToolbarButtons: [ + FontButtons( + bold: true, + underline: true, + italic: true, + strikethrough: false, + superscript: false, + subscript: false, + clearAll: false), + ListButtons(ol: true, ul: true, listStyles: false), + ParagraphButtons( + textDirection: false, + lineHeight: false, + caseConverter: false, + increaseIndent: false, + decreaseIndent: false, + alignLeft: false, + alignCenter: false, + alignRight: false, + alignJustify: false), + ]), + htmlEditorOptions: HtmlEditorOptions( + hint: widget.helperText, + shouldEnsureVisible: true, + ), + otherOptions: const OtherOptions( + height: 200, + ), + callbacks: Callbacks( + onChangeContent: (String? currentHtml) { + addExerciseProvider.descriptionEn = currentHtml!; + }, + ), + ), + ); + } +} diff --git a/lib/widgets/add_exercise/steps/step3description.dart b/lib/widgets/add_exercise/steps/step3description.dart index fad46ba8..9ac474a9 100644 --- a/lib/widgets/add_exercise/steps/step3description.dart +++ b/lib/widgets/add_exercise/steps/step3description.dart @@ -1,29 +1,20 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:provider/provider.dart'; -import 'package:wger/helpers/exercises/forms.dart'; -import 'package:wger/providers/add_exercise.dart'; -import 'package:wger/widgets/add_exercise/add_exercise_text_area.dart'; +import 'package:wger/widgets/add_exercise/add_exercise_html_editor.dart'; class Step3Description extends StatelessWidget { final GlobalKey formkey; + const Step3Description({required this.formkey}); @override Widget build(BuildContext context) { - final addExerciseProvider = context.read(); - return Form( key: formkey, child: Column( children: [ - AddExerciseTextArea( - onChange: (value) => {}, - title: '${AppLocalizations.of(context).description}*', - isRequired: true, - isMultiline: true, - validator: (name) => validateDescription(name, context), - onSaved: (String? description) => addExerciseProvider.descriptionEn = description!, + AddExerciseHtmlEditor( + helperText: AppLocalizations.of(context).description, ), ], ), diff --git a/pubspec.lock b/pubspec.lock index 32e405de..519afb13 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -345,6 +345,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.4" + file_picker: + dependency: transitive + description: + name: file_picker + sha256: d090ae03df98b0247b82e5928f44d1b959867049d18d73635e2e0bc3f49542b9 + url: "https://pub.dev" + source: hosted + version: "5.2.5" fixnum: dependency: transitive description: @@ -353,6 +361,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + flex_color_picker: + dependency: transitive + description: + name: flex_color_picker + sha256: "607c9fdb26be84d4a5a0931ab42a7eda725372e4f5ebaa2526ab6b22ead752f9" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + flex_seed_scheme: + dependency: transitive + description: + name: flex_seed_scheme + sha256: e61950ccadfb8d43ce5cdef382e8f689edc053ce6b837e277539410ecfb3b3e5 + url: "https://pub.dev" + source: hosted + version: "1.2.2" flutter: dependency: "direct main" description: flutter @@ -387,6 +411,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.1" + flutter_inappwebview: + dependency: transitive + description: + name: flutter_inappwebview + sha256: f73505c792cf083d5566e1a94002311be497d984b5607f25be36d685cf6361cf + url: "https://pub.dev" + source: hosted + version: "5.7.2+3" flutter_keyboard_visibility: dependency: transitive description: @@ -559,6 +591,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.15.1" + html_editor_enhanced: + dependency: "direct main" + description: + name: html_editor_enhanced + sha256: c2a0d0a50970fe4aa565e79e57081c8a69c3b2e94162e8f1df3e1b750c7aeb5e + url: "https://pub.dev" + source: hosted + version: "2.5.1" http: dependency: "direct main" description: @@ -631,6 +671,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.6.2" + infinite_listview: + dependency: transitive + description: + name: infinite_listview + sha256: f6062c1720eb59be553dfa6b89813d3e8dd2f054538445aaa5edaddfa5195ce6 + url: "https://pub.dev" + source: hosted + version: "1.1.0" integration_test: dependency: "direct dev" description: flutter @@ -780,6 +828,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + numberpicker: + dependency: transitive + description: + name: numberpicker + sha256: "73723bd13c940ebcd9e5f0ed56b4874588c1748a9a6a38254f97ad627715142e" + url: "https://pub.dev" + source: hosted + version: "2.1.1" numerus: dependency: transitive description: @@ -852,6 +908,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.3" + pedantic: + dependency: transitive + description: + name: pedantic + sha256: "67fc27ed9639506c856c840ccce7594d0bdcd91bc8d53d6e52359449a1d50602" + url: "https://pub.dev" + source: hosted + version: "1.11.1" petitparser: dependency: transitive description: @@ -876,6 +940,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.3" + pointer_interceptor: + dependency: transitive + description: + name: pointer_interceptor + sha256: "6aa680b30d96dccef496933d00208ad25f07e047f644dc98ce03ec6141633a9a" + url: "https://pub.dev" + source: hosted + version: "0.9.3+4" pool: dependency: transitive description: @@ -1273,6 +1345,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.13" + visibility_detector: + dependency: transitive + description: + name: visibility_detector + sha256: "15c54a459ec2c17b4705450483f3d5a2858e733aee893dcee9d75fd04814940d" + url: "https://pub.dev" + source: hosted + version: "0.3.3" vm_service: dependency: transitive description: @@ -1411,4 +1491,4 @@ packages: version: "3.1.1" sdks: dart: ">=2.19.0 <3.0.0" - flutter: ">=3.0.0" + flutter: ">=3.7.0" diff --git a/pubspec.yaml b/pubspec.yaml index e4fc442c..a4e473f9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -58,6 +58,7 @@ dependencies: carousel_slider: ^4.2.1 multi_select_flutter: ^4.1.3 flutter_svg: ^0.23.0+1 + html_editor_enhanced: ^2.5.0 dev_dependencies: flutter_test: diff --git a/test/workout/gym_mode_screen_test.mocks.dart b/test/workout/gym_mode_screen_test.mocks.dart index 63fb4e2f..8f737717 100644 --- a/test/workout/gym_mode_screen_test.mocks.dart +++ b/test/workout/gym_mode_screen_test.mocks.dart @@ -358,7 +358,7 @@ class MockExercisesProvider extends _i1.Mock implements _i8.ExercisesProvider { returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); @override - _i9.Future fetchAndSetExercises() => (super.noSuchMethod( + _i9.Future fetchAndSetExercises(context) => (super.noSuchMethod( Invocation.method( #fetchAndSetExercises, [], diff --git a/test/workout/workout_set_form_test.mocks.dart b/test/workout/workout_set_form_test.mocks.dart index a87bd4e3..b8865320 100644 --- a/test/workout/workout_set_form_test.mocks.dart +++ b/test/workout/workout_set_form_test.mocks.dart @@ -358,7 +358,7 @@ class MockExercisesProvider extends _i1.Mock implements _i8.ExercisesProvider { returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); @override - _i9.Future fetchAndSetExercises() => (super.noSuchMethod( + _i9.Future fetchAndSetExercises(context) => (super.noSuchMethod( Invocation.method( #fetchAndSetExercises, [],