Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add simple WYSIWYG editor for exercise descriptions #189 #283

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions lib/helpers/i18n.dart
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ String getTranslation(String value, BuildContext context) {

case 'Legs':
return AppLocalizations.of(context).legs;

default:
return 'NOT TRANSLATED';
}
Expand Down
14 changes: 8 additions & 6 deletions lib/providers/exercises.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import 'package:json_annotation/json_annotation.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:wger/exceptions/no_such_entry_exception.dart';
import 'package:wger/helpers/consts.dart';
import 'package:wger/main.dart';
import 'package:wger/models/exercises/alias.dart';
import 'package:wger/models/exercises/base.dart';
import 'package:wger/models/exercises/category.dart';
Expand All @@ -37,6 +38,7 @@ import 'package:wger/models/exercises/muscle.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;
Expand Down Expand Up @@ -104,23 +106,23 @@ 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;
}

setFilters(
Filters(
exerciseCategories: FilterCategory<ExerciseCategory>(
title: 'Category',
title: AppLocalizations.of(context).category,
items: Map.fromEntries(
_categories.map(
(category) => MapEntry<ExerciseCategory, bool>(category, false),
),
),
),
equipment: FilterCategory<Equipment>(
title: 'Equipment',
title: AppLocalizations.of(context).equipment,
items: Map.fromEntries(
_equipment.map(
(singleEquipment) => MapEntry<Equipment, bool>(singleEquipment, false),
Expand Down Expand Up @@ -367,7 +369,7 @@ class ExercisesProvider with ChangeNotifier {
}
}

Future<void> fetchAndSetExercises() async {
Future<void> fetchAndSetExercises(BuildContext context) async {
clear();

// Load exercises from cache, if available
Expand All @@ -384,7 +386,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;
}
Expand Down Expand Up @@ -420,7 +422,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());
Expand Down
2 changes: 1 addition & 1 deletion lib/screens/home_tabs_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class _HomeTabsScreenState extends State<HomeTabsScreen> with SingleTickerProvid
userProvider.fetchAndSetProfile(),
workoutPlansProvider.fetchAndSetUnits(),
nutritionPlansProvider.fetchIngredientsFromCache(),
exercisesProvider.fetchAndSetExercises(),
exercisesProvider.fetchAndSetExercises(context),
]);

// Plans, weight and gallery
Expand Down
5 changes: 3 additions & 2 deletions lib/screens/splash_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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),
),
);
}
Expand Down
77 changes: 77 additions & 0 deletions lib/widgets/add_exercise/add_exercise_html_editor.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import 'dart:async';
import 'dart:ffi';
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<AddExerciseHtmlEditor> {
@override
Widget build(BuildContext context) {
final addExerciseProvider = context.read<AddExerciseProvider>();
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!;
},
),
),
);
}
}
12 changes: 3 additions & 9 deletions lib/widgets/add_exercise/steps/step3description.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
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<FormState> formkey;
Expand All @@ -17,13 +16,8 @@ class Step3Description extends StatelessWidget {
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,
),
],
),
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ dependencies:
carousel_slider: ^4.1.1
multi_select_flutter: ^4.1.2
flutter_svg: ^0.23.0+1
html_editor_enhanced: ^2.5.0

dev_dependencies:
flutter_test:
Expand Down
2 changes: 1 addition & 1 deletion test/workout/gym_mode_screen_test.mocks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ class MockExercisesProvider extends _i1.Mock implements _i8.ExercisesProvider {
returnValueForMissingStub: _i9.Future<void>.value(),
) as _i9.Future<void>);
@override
_i9.Future<void> fetchAndSetExercises() => (super.noSuchMethod(
_i9.Future<void> fetchAndSetExercises(context) => (super.noSuchMethod(
Invocation.method(
#fetchAndSetExercises,
[],
Expand Down
2 changes: 1 addition & 1 deletion test/workout/workout_set_form_test.mocks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ class MockExercisesProvider extends _i1.Mock implements _i8.ExercisesProvider {
returnValueForMissingStub: _i9.Future<void>.value(),
) as _i9.Future<void>);
@override
_i9.Future<void> fetchAndSetExercises() => (super.noSuchMethod(
_i9.Future<void> fetchAndSetExercises(context) => (super.noSuchMethod(
Invocation.method(
#fetchAndSetExercises,
[],
Expand Down