From 4c7cf055780b8f8b3a604d09f443d0662614a2a3 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Wed, 4 May 2022 19:58:47 +0300 Subject: [PATCH] Refactor FieldCubit creation for user forms Move all cubit fields to a special factory which encapsulates all logic related to their default properties, which leaves possibility for future dependency inversion on that factory or future factories of other cubit fields (in case we will have to replace it with other implementations). --- lib/logic/api_maps/server.dart | 1 - .../forms/factories/field_cubit_factory.dart | 56 +++++++++++++++++++ .../initializing/root_user_form_cubit.dart | 29 ++-------- .../cubit/forms/user/user_form_cubit.dart | 40 +++---------- lib/logic/cubit/users/users_state.dart | 6 ++ lib/ui/pages/initializing/initializing.dart | 4 +- lib/ui/pages/users/new_user.dart | 2 +- lib/ui/pages/users/users.dart | 1 + 8 files changed, 79 insertions(+), 60 deletions(-) create mode 100644 lib/logic/cubit/forms/factories/field_cubit_factory.dart diff --git a/lib/logic/api_maps/server.dart b/lib/logic/api_maps/server.dart index 1d2c3bce..6302611e 100644 --- a/lib/logic/api_maps/server.dart +++ b/lib/logic/api_maps/server.dart @@ -66,7 +66,6 @@ class ServerApi extends ApiMap { Future> createUser(User user) async { var client = await getClient(); - // POST request with JSON body containing username and password var makeErrorApiReponse = (int status) { return ApiResponse( diff --git a/lib/logic/cubit/forms/factories/field_cubit_factory.dart b/lib/logic/cubit/forms/factories/field_cubit_factory.dart new file mode 100644 index 00000000..c1b8abe7 --- /dev/null +++ b/lib/logic/cubit/forms/factories/field_cubit_factory.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:cubit_form/cubit_form.dart'; +import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; + +class FieldCubitFactory { + FieldCubitFactory(this.context); + + /* A common user login field. + * - Available characters are lowercase a-z, digits and underscore _ + * - Must start with either a-z or underscore + * - Must be no longer than 'userMaxLength' characters + * - Must not be empty + * - Must not be a reserved root login + * - Must be unique + */ + FieldCubit createUserLoginField() { + final userAllowedRegExp = RegExp(r"^[a-z_][a-z0-9_]+$"); + const userMaxLength = 31; + return FieldCubit( + initalValue: '', + validations: [ + ValidationModel( + (s) => s.toLowerCase() == 'root', 'validations.root_name'.tr()), + ValidationModel( + (login) => context.read().state.isLoginRegistered(login), + 'validations.user_already_exist'.tr(), + ), + RequiredStringValidation('validations.required'.tr()), + LengthStringLongerValidation(userMaxLength), + ValidationModel((s) => !userAllowedRegExp.hasMatch(s), + 'validations.invalid_format'.tr()), + ], + ); + } + + /* A common user password field. + * - Must fail on the regural expression of invalid matches: [\n\r\s]+ + * - Must not be empty + */ + FieldCubit createUserPasswordField() { + var passwordForbiddenRegExp = RegExp(r"[\n\r\s]+"); + return FieldCubit( + initalValue: '', + validations: [ + RequiredStringValidation('validations.required'.tr()), + ValidationModel( + (password) => passwordForbiddenRegExp.hasMatch(password), + 'validations.invalid_format'.tr()), + ], + ); + } + + final BuildContext context; +} diff --git a/lib/logic/cubit/forms/initializing/root_user_form_cubit.dart b/lib/logic/cubit/forms/initializing/root_user_form_cubit.dart index 41b26582..102d7ac7 100644 --- a/lib/logic/cubit/forms/initializing/root_user_form_cubit.dart +++ b/lib/logic/cubit/forms/initializing/root_user_form_cubit.dart @@ -2,33 +2,14 @@ import 'dart:async'; import 'package:cubit_form/cubit_form.dart'; import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; +import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart'; import 'package:selfprivacy/logic/models/user.dart'; -import 'package:easy_localization/easy_localization.dart'; class RootUserFormCubit extends FormCubit { - RootUserFormCubit(this.initializingCubit) { - var userRegExp = RegExp(r"\W"); - var passwordRegExp = RegExp(r"[\n\r\s]+"); - - userName = FieldCubit( - initalValue: '', - validations: [ - ValidationModel( - (s) => s.toLowerCase() == 'root', 'validations.root_name'.tr()), - RequiredStringValidation('validations.required'.tr()), - ValidationModel( - (s) => userRegExp.hasMatch(s), 'validations.invalid_format'.tr()), - ], - ); - - password = FieldCubit( - initalValue: '', - validations: [ - RequiredStringValidation('validations.required'.tr()), - ValidationModel((s) => passwordRegExp.hasMatch(s), - 'validations.invalid_format'.tr()), - ], - ); + RootUserFormCubit( + this.initializingCubit, final FieldCubitFactory fieldFactory) { + userName = fieldFactory.createUserLoginField(); + password = fieldFactory.createUserPasswordField(); isVisible = FieldCubit(initalValue: false); diff --git a/lib/logic/cubit/forms/user/user_form_cubit.dart b/lib/logic/cubit/forms/user/user_form_cubit.dart index 4f0e6488..b65cfb47 100644 --- a/lib/logic/cubit/forms/user/user_form_cubit.dart +++ b/lib/logic/cubit/forms/user/user_form_cubit.dart @@ -1,8 +1,7 @@ import 'dart:async'; import 'package:cubit_form/cubit_form.dart'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart'; +import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart'; import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; import 'package:selfprivacy/logic/models/job.dart'; import 'package:selfprivacy/logic/models/user.dart'; @@ -11,41 +10,16 @@ import 'package:selfprivacy/utils/password_generator.dart'; class UserFormCubit extends FormCubit { UserFormCubit({ required this.jobsCubit, - required List users, + required FieldCubitFactory fieldFactory, User? user, }) { var isEdit = user != null; - var userAllowedRegExp = RegExp(r"^[a-z_][a-z0-9_]+$"); - const userMaxLength = 31; - - var passwordForbiddenRegExp = RegExp(r"[\n\r\s]+"); - - login = FieldCubit( - initalValue: isEdit ? user!.login : '', - validations: [ - ValidationModel( - (s) => s.toLowerCase() == 'root', 'validations.root_name'.tr()), - ValidationModel( - (login) => users.any((user) => user.login == login), - 'validations.user_already_exist'.tr(), - ), - RequiredStringValidation('validations.required'.tr()), - LengthStringLongerValidation(userMaxLength), - ValidationModel((s) => !userAllowedRegExp.hasMatch(s), - 'validations.invalid_format'.tr()), - ], - ); - - password = FieldCubit( - initalValue: - isEdit ? (user?.password ?? '') : StringGenerators.userPassword(), - validations: [ - RequiredStringValidation('validations.required'.tr()), - ValidationModel((s) => passwordForbiddenRegExp.hasMatch(s), - 'validations.invalid_format'.tr()), - ], - ); + login = fieldFactory.createUserLoginField(); + login.setValue(isEdit ? user!.login : ''); + password = fieldFactory.createUserPasswordField(); + password.setValue( + isEdit ? (user?.password ?? '') : StringGenerators.userPassword()); super.addFields([login, password]); } diff --git a/lib/logic/cubit/users/users_state.dart b/lib/logic/cubit/users/users_state.dart index 1ee1903f..d15789c9 100644 --- a/lib/logic/cubit/users/users_state.dart +++ b/lib/logic/cubit/users/users_state.dart @@ -22,5 +22,11 @@ class UsersState extends AppConfigDependendState { ); } + bool isLoginRegistered(String login) { + return users.any((user) => user.login == login) || + login == rootUser.login || + login == primaryUser.login; + } + bool get isEmpty => users.isEmpty; } diff --git a/lib/ui/pages/initializing/initializing.dart b/lib/ui/pages/initializing/initializing.dart index 95ba575c..d30569ca 100644 --- a/lib/ui/pages/initializing/initializing.dart +++ b/lib/ui/pages/initializing/initializing.dart @@ -3,6 +3,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; +import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart'; import 'package:selfprivacy/logic/cubit/forms/initializing/backblaze_form_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/initializing/cloudflare_form_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/initializing/domain_cloudflare.dart'; @@ -352,7 +353,8 @@ class InitializingPage extends StatelessWidget { Widget _stepUser(AppConfigCubit initializingCubit) { return BlocProvider( - create: (context) => RootUserFormCubit(initializingCubit), + create: (context) => + RootUserFormCubit(initializingCubit, FieldCubitFactory(context)), child: Builder(builder: (context) { var formCubitState = context.watch().state; diff --git a/lib/ui/pages/users/new_user.dart b/lib/ui/pages/users/new_user.dart index 4f6da178..58559c8f 100644 --- a/lib/ui/pages/users/new_user.dart +++ b/lib/ui/pages/users/new_user.dart @@ -24,7 +24,7 @@ class _NewUser extends StatelessWidget { } return UserFormCubit( jobsCubit: jobCubit, - users: users, + fieldFactory: FieldCubitFactory(context), ); }, child: Builder(builder: (context) { diff --git a/lib/ui/pages/users/users.dart b/lib/ui/pages/users/users.dart index 72c519a0..013f65b0 100644 --- a/lib/ui/pages/users/users.dart +++ b/lib/ui/pages/users/users.dart @@ -6,6 +6,7 @@ import 'package:selfprivacy/config/brand_colors.dart'; import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/config/text_themes.dart'; import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; +import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart'; import 'package:selfprivacy/logic/cubit/forms/user/user_form_cubit.dart'; import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';