diff --git a/lib/helpers/consts.dart b/lib/helpers/consts.dart index ffc72506..4dcfb723 100644 --- a/lib/helpers/consts.dart +++ b/lib/helpers/consts.dart @@ -95,3 +95,7 @@ enum EXERCISE_IMAGE_ART_STYLE { /// Colors used for muscles const COLOR_MAIN_MUSCLES = Colors.red; const COLOR_SECONDARY_MUSCLES = Colors.orange; + +// Min account age to contribute exercises. Needs to be kept in sync with +// the value on the backend +const MIN_ACCOUNT_AGE = 14; diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 7eba6907..14e6759e 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -619,6 +619,16 @@ "description": "The base data for an exercise such as category, trained muscles, etc." }, "aboutPageTitle": "About Wger", + "contributeExerciseWarning": "You can only contribute exercises if your account is older than {days} days and have verified your email", + "@contributeExerciseWarning": { + "description": "Number of days before which a person can add exercise", + "placeholders": { + "days": { + "type": "String", + "example": "14" + } + } + }, "abs": "Abs", "arms": "Arms", "back": "Back", diff --git a/lib/providers/user.dart b/lib/providers/user.dart index 58bb2593..cb547c49 100644 --- a/lib/providers/user.dart +++ b/lib/providers/user.dart @@ -26,8 +26,8 @@ class UserProvider with ChangeNotifier { final WgerBaseProvider baseProvider; UserProvider(this.baseProvider); - static const _profileUrlPath = 'userprofile'; - static const _verifyEmail = 'verify-email'; + static const PROFILE_URL = 'userprofile'; + static const VERIFY_EMAIL = 'verify-email'; Profile? profile; @@ -38,7 +38,7 @@ class UserProvider with ChangeNotifier { /// Fetch the current user's profile Future fetchAndSetProfile() async { - final userData = await baseProvider.fetch(baseProvider.makeUrl(_profileUrlPath)); + final userData = await baseProvider.fetch(baseProvider.makeUrl(PROFILE_URL)); try { profile = Profile.fromJson(userData); } catch (error) { @@ -50,15 +50,15 @@ class UserProvider with ChangeNotifier { Future saveProfile() async { final data = await baseProvider.post( profile!.toJson(), - baseProvider.makeUrl(_profileUrlPath), + baseProvider.makeUrl(PROFILE_URL), ); } /// Verify the user's email Future verifyEmail() async { final verificationData = await baseProvider.fetch(baseProvider.makeUrl( - _profileUrlPath, - objectMethod: _verifyEmail, + PROFILE_URL, + objectMethod: VERIFY_EMAIL, )); //log(verificationData.toString()); } diff --git a/lib/screens/add_exercise_screen.dart b/lib/screens/add_exercise_screen.dart index 8c00645e..fc9b0b86 100644 --- a/lib/screens/add_exercise_screen.dart +++ b/lib/screens/add_exercise_screen.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:wger/helpers/consts.dart'; import 'package:wger/providers/add_exercise.dart'; import 'package:wger/providers/exercises.dart'; +import 'package:wger/providers/user.dart'; import 'package:wger/screens/exercise_screen.dart'; import 'package:wger/widgets/add_exercise/steps/step1basics.dart'; import 'package:wger/widgets/add_exercise/steps/step2variations.dart'; @@ -10,6 +12,9 @@ import 'package:wger/widgets/add_exercise/steps/step3description.dart'; import 'package:wger/widgets/add_exercise/steps/step4translations.dart'; import 'package:wger/widgets/add_exercise/steps/step5images.dart'; import 'package:wger/widgets/core/app_bar.dart'; +import 'package:wger/widgets/user/forms.dart'; + +import 'form_screen.dart'; class AddExerciseScreen extends StatefulWidget { const AddExerciseScreen({Key? key}) : super(key: key); @@ -40,9 +45,8 @@ class _AddExerciseScreenState extends State { mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ OutlinedButton( - onPressed: details.onStepCancel, - child: Text(AppLocalizations.of(context).previous), - ), + onPressed: details.onStepCancel, + child: Text(AppLocalizations.of(context).previous)), if (_currentStep == lastStepIndex) ElevatedButton( onPressed: () async { @@ -66,56 +70,112 @@ class _AddExerciseScreenState extends State { @override Widget build(BuildContext context) { - return Scaffold( - appBar: EmptyAppBar(AppLocalizations.of(context).contributeExercise), - body: Stepper( - controlsBuilder: _controlsBuilder, - steps: [ - Step( - title: Text(AppLocalizations.of(context).baseData), - content: Step1Basics(formkey: _keys[0]), - ), - Step( - title: Text(AppLocalizations.of(context).variations), - content: Step2Variations(formkey: _keys[1]), - ), - Step( - title: Text(AppLocalizations.of(context).description), - content: Step3Description(formkey: _keys[2]), - ), - Step( - title: Text(AppLocalizations.of(context).translation), - content: Step4Translation(formkey: _keys[3]), - ), - Step( - title: Text(AppLocalizations.of(context).images), - content: Step5Images(formkey: _keys[4]), - ), - ], - currentStep: _currentStep, - onStepContinue: () { - if (_keys[_currentStep].currentState?.validate() ?? false) { - _keys[_currentStep].currentState?.save(); + final user = context.read().profile; - if (_currentStep != lastStepIndex) { - setState(() { - _currentStep += 1; - }); - } - } - }, - onStepCancel: () => setState(() { - if (_currentStep != 0) { - _currentStep -= 1; - } - }), - /* + return !user!.emailVerified + ? const EmailNotVerified() + : Scaffold( + appBar: EmptyAppBar(AppLocalizations.of(context).contributeExercise), + body: Stepper( + controlsBuilder: _controlsBuilder, + steps: [ + Step( + title: Text(AppLocalizations.of(context).baseData), + content: Step1Basics(formkey: _keys[0]), + ), + Step( + title: Text(AppLocalizations.of(context).variations), + content: Step2Variations(formkey: _keys[1]), + ), + Step( + title: Text(AppLocalizations.of(context).description), + content: Step3Description(formkey: _keys[2]), + ), + Step( + title: Text(AppLocalizations.of(context).translation), + content: Step4Translation(formkey: _keys[3]), + ), + Step( + title: Text(AppLocalizations.of(context).images), + content: Step5Images(formkey: _keys[4]), + ), + ], + currentStep: _currentStep, + onStepContinue: () { + if (_keys[_currentStep].currentState?.validate() ?? false) { + _keys[_currentStep].currentState?.save(); + + if (_currentStep != lastStepIndex) { + setState(() { + _currentStep += 1; + }); + } + } + }, + onStepCancel: () => setState(() { + if (_currentStep != 0) { + _currentStep -= 1; + } + }), + /* onStepTapped: (int index) { setState(() { _currentStep = index; }); }, */ + ), + ); + } +} + +class EmailNotVerified extends StatelessWidget { + const EmailNotVerified({ + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final user = context.read().profile; + + return Scaffold( + appBar: EmptyAppBar(AppLocalizations.of(context).unVerifiedEmail), + body: Container( + padding: const EdgeInsets.all(25), + child: Center( + child: Card( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + leading: const Icon(Icons.warning), + title: Text(AppLocalizations.of(context).unVerifiedEmail), + subtitle: Text(AppLocalizations.of(context) + .contributeExerciseWarning(MIN_ACCOUNT_AGE.toString())), + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () { + Navigator.pushReplacementNamed( + context, + FormScreen.routeName, + arguments: FormScreenArguments( + AppLocalizations.of(context).userProfile, + UserProfileForm(user!), + ), + ); + }, + child: Text(AppLocalizations.of(context).userProfile), + ), + ], + ), + ], + ), + ), + ), ), ); } diff --git a/lib/widgets/core/app_bar.dart b/lib/widgets/core/app_bar.dart index 4dc28fbe..6e420c79 100644 --- a/lib/widgets/core/app_bar.dart +++ b/lib/widgets/core/app_bar.dart @@ -63,8 +63,7 @@ class MainAppBar extends StatelessWidget with PreferredSizeWidget { //dense: true, leading: const Icon(Icons.person), title: Text(AppLocalizations.of(context).userProfile), - onTap: () async { - await context.read().fetchAndSetProfile(); + onTap: () { Navigator.pushNamed( context, FormScreen.routeName,