From 4c99579f13af3ee5e3678fa27289ce4a7cd4f0ad Mon Sep 17 00:00:00 2001 From: NaiJi Date: Mon, 2 May 2022 14:56:46 +0300 Subject: [PATCH 1/3] Fix username validation and exception handling 1. Refactor string validation classes 2. Rename string validation assets for length 3. Improve exception handling when server is not able to create requested server --- assets/translations/en.json | 15 ++-- assets/translations/ru.json | 5 +- lib/logic/api_maps/server.dart | 74 +++++++++++-------- .../initializing/backblaze_form_cubit.dart | 6 -- .../initializing/cloudflare_form_cubit.dart | 10 +-- .../initializing/hetzner_form_cubit.dart | 6 +- .../cubit/forms/user/user_form_cubit.dart | 14 ++-- .../cubit/forms/validations/validations.dart | 29 ++++++-- lib/logic/cubit/users/users_cubit.dart | 5 ++ 9 files changed, 95 insertions(+), 69 deletions(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index 45faa3e8..73c3575e 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -320,12 +320,13 @@ "delete_ssh_key": "Delete SSH key for {}" }, "validations": { - "required": "Required", - "invalid_format": "Invalid format", - "root_name": "User name cannot be 'root'", - "key_format": "Invalid key format", - "length": "Length is [] should be {}", - "user_already_exist": "Already exists", - "key_already_exists": "This key already exists" + "required": "Required.", + "invalid_format": "Invalid format.", + "root_name": "User name cannot be 'root'.", + "key_format": "Invalid key format.", + "length_not_equal": "Length is []. Should be {}.", + "length_longer": "Length is []. Should be shorter than or equal to {}.", + "user_already_exist": "This user already exists.", + "key_already_exists": "This key already exists." } } diff --git a/assets/translations/ru.json b/assets/translations/ru.json index 619096a7..ee085ed4 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -323,9 +323,10 @@ "validations": { "required": "Обязательное поле.", "invalid_format": "Неверный формат.", - "root_name": "Имя пользователя не может быть'root'.", + "root_name": "Имя пользователя не может быть 'root'.", "key_format": "Неверный формат.", - "length": "Длина строки [] должна быть {}.", + "length_not_equal": "Длина строки []. Должно быть равно {}.", + "length_longer": "Длина строки []. Должно быть меньше либо равно {}.", "user_already_exist": "Имя уже используется.", "key_already_exists": "Этот ключ уже добавлен." } diff --git a/lib/logic/api_maps/server.dart b/lib/logic/api_maps/server.dart index 0e8930e5..1d2c3bce 100644 --- a/lib/logic/api_maps/server.dart +++ b/lib/logic/api_maps/server.dart @@ -18,7 +18,7 @@ class ApiResponse { final String? errorMessage; final D data; - get isSuccess => statusCode >= 200 && statusCode < 300; + bool get isSuccess => statusCode >= 200 && statusCode < 300; ApiResponse({ required this.statusCode, @@ -65,27 +65,48 @@ class ServerApi extends ApiMap { } Future> createUser(User user) async { - Response response; - var client = await getClient(); // POST request with JSON body containing username and password - response = await client.post( - '/users', - data: { - 'username': user.login, - 'password': user.password, - }, - options: Options( - contentType: 'application/json', - ), - ); - - close(client); - - if (response.statusCode == HttpStatus.created) { + var makeErrorApiReponse = (int status) { return ApiResponse( - statusCode: response.statusCode ?? HttpStatus.internalServerError, + statusCode: status, + data: User( + login: user.login, + password: user.password, + isFoundOnServer: false, + ), + ); + }; + + late Response response; + + try { + response = await client.post( + '/users', + data: { + 'username': user.login, + 'password': user.password, + }, + options: Options( + contentType: 'application/json', + receiveDataWhenStatusError: true, + followRedirects: false, + validateStatus: (status) { + return (status != null) && + (status < HttpStatus.internalServerError); + }), + ); + } catch (e) { + return makeErrorApiReponse(HttpStatus.internalServerError); + } finally { + close(client); + } + + if ((response.statusCode != null) && + (response.statusCode == HttpStatus.created)) { + return ApiResponse( + statusCode: response.statusCode!, data: User( login: user.login, password: user.password, @@ -93,18 +114,11 @@ class ServerApi extends ApiMap { ), ); } else { - return ApiResponse( - statusCode: response.statusCode ?? HttpStatus.internalServerError, - data: User( - login: user.login, - password: user.password, - isFoundOnServer: false, - note: response.data['message'] ?? null, - ), - errorMessage: response.data?.containsKey('error') ?? false - ? response.data['error'] - : null, - ); + print(response.statusCode.toString() + + ": " + + (response.statusMessage ?? "")); + return makeErrorApiReponse( + response.statusCode ?? HttpStatus.internalServerError); } } diff --git a/lib/logic/cubit/forms/initializing/backblaze_form_cubit.dart b/lib/logic/cubit/forms/initializing/backblaze_form_cubit.dart index eda06939..d8777fa8 100644 --- a/lib/logic/cubit/forms/initializing/backblaze_form_cubit.dart +++ b/lib/logic/cubit/forms/initializing/backblaze_form_cubit.dart @@ -12,9 +12,6 @@ class BackblazeFormCubit extends FormCubit { initalValue: '', validations: [ RequiredStringValidation('validations.required'.tr()), - //ValidationModel( - //(s) => regExp.hasMatch(s), 'invalid key format'), - //LegnthStringValidationWithLenghShowing(64, 'length is [] shoud be 64') ], ); @@ -22,9 +19,6 @@ class BackblazeFormCubit extends FormCubit { initalValue: '', validations: [ RequiredStringValidation('required'), - //ValidationModel( - //(s) => regExp.hasMatch(s), 'invalid key format'), - //LegnthStringValidationWithLenghShowing(64, 'length is [] shoud be 64') ], ); diff --git a/lib/logic/cubit/forms/initializing/cloudflare_form_cubit.dart b/lib/logic/cubit/forms/initializing/cloudflare_form_cubit.dart index 7ee8c8fa..d811843b 100644 --- a/lib/logic/cubit/forms/initializing/cloudflare_form_cubit.dart +++ b/lib/logic/cubit/forms/initializing/cloudflare_form_cubit.dart @@ -4,8 +4,7 @@ import 'package:cubit_form/cubit_form.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:selfprivacy/logic/api_maps/cloudflare.dart'; import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; - -import '../validations/validations.dart'; +import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart'; class CloudFlareFormCubit extends FormCubit { CloudFlareFormCubit(this.initializingCubit) { @@ -16,12 +15,7 @@ class CloudFlareFormCubit extends FormCubit { RequiredStringValidation('validations.required'.tr()), ValidationModel( (s) => regExp.hasMatch(s), 'validations.key_format'.tr()), - LengthStringValidationWithLengthShowing( - 40, - 'validations.length'.tr( - args: ["40"], - ), - ) + LengthStringNotEqualValidation(40) ], ); diff --git a/lib/logic/cubit/forms/initializing/hetzner_form_cubit.dart b/lib/logic/cubit/forms/initializing/hetzner_form_cubit.dart index 55af50d9..ce3e5aa9 100644 --- a/lib/logic/cubit/forms/initializing/hetzner_form_cubit.dart +++ b/lib/logic/cubit/forms/initializing/hetzner_form_cubit.dart @@ -4,8 +4,7 @@ import 'package:cubit_form/cubit_form.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:selfprivacy/logic/api_maps/hetzner.dart'; import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; - -import '../validations/validations.dart'; +import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart'; class HetznerFormCubit extends FormCubit { HetznerFormCubit(this.initializingCubit) { @@ -16,8 +15,7 @@ class HetznerFormCubit extends FormCubit { RequiredStringValidation('validations.required'.tr()), ValidationModel( (s) => regExp.hasMatch(s), 'validations.key_format'.tr()), - LengthStringValidationWithLengthShowing( - 64, 'validations.length'.tr(args: ["64"])) + LengthStringNotEqualValidation(64) ], ); diff --git a/lib/logic/cubit/forms/user/user_form_cubit.dart b/lib/logic/cubit/forms/user/user_form_cubit.dart index 24f67437..4f0e6488 100644 --- a/lib/logic/cubit/forms/user/user_form_cubit.dart +++ b/lib/logic/cubit/forms/user/user_form_cubit.dart @@ -2,6 +2,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/jobs/jobs_cubit.dart'; import 'package:selfprivacy/logic/models/job.dart'; import 'package:selfprivacy/logic/models/user.dart'; @@ -15,8 +16,10 @@ class UserFormCubit extends FormCubit { }) { var isEdit = user != null; - var userRegExp = RegExp(r"\W"); - var passwordRegExp = RegExp(r"[\n\r\s]+"); + 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 : '', @@ -28,8 +31,9 @@ class UserFormCubit extends FormCubit { 'validations.user_already_exist'.tr(), ), RequiredStringValidation('validations.required'.tr()), - ValidationModel( - (s) => userRegExp.hasMatch(s), 'validations.invalid_format'.tr()), + LengthStringLongerValidation(userMaxLength), + ValidationModel((s) => !userAllowedRegExp.hasMatch(s), + 'validations.invalid_format'.tr()), ], ); @@ -38,7 +42,7 @@ class UserFormCubit extends FormCubit { isEdit ? (user?.password ?? '') : StringGenerators.userPassword(), validations: [ RequiredStringValidation('validations.required'.tr()), - ValidationModel((s) => passwordRegExp.hasMatch(s), + ValidationModel((s) => passwordForbiddenRegExp.hasMatch(s), 'validations.invalid_format'.tr()), ], ); diff --git a/lib/logic/cubit/forms/validations/validations.dart b/lib/logic/cubit/forms/validations/validations.dart index aff4ec92..34338559 100644 --- a/lib/logic/cubit/forms/validations/validations.dart +++ b/lib/logic/cubit/forms/validations/validations.dart @@ -1,13 +1,28 @@ import 'package:cubit_form/cubit_form.dart'; +import 'package:easy_localization/easy_localization.dart'; -class LengthStringValidationWithLengthShowing extends ValidationModel { - LengthStringValidationWithLengthShowing(int length, String errorText) - : super((n) => n.length != length, errorText); +abstract class LengthStringValidation extends ValidationModel { + LengthStringValidation(bool Function(String) predicate, String errorMessage) + : super(predicate, errorMessage); @override - String? check(String val) { - var length = val.length; - var errorMassage = this.errorMassage.replaceAll("[]", length.toString()); - return test(val) ? errorMassage : null; + String? check(String value) { + var length = value.length; + var errorMessage = this.errorMassage.replaceAll("[]", length.toString()); + return test(value) ? errorMessage : null; } } + +// String must be equal to [length] +class LengthStringNotEqualValidation extends LengthStringValidation { + LengthStringNotEqualValidation(int length) + : super((n) => n.length != length, + 'validations.length_not_equal'.tr(args: [length.toString()])); +} + +// String must be shorter than or equal to [length] +class LengthStringLongerValidation extends LengthStringValidation { + LengthStringLongerValidation(int length) + : super((n) => n.length > length, + 'validations.length_longer'.tr(args: [length.toString()])); +} diff --git a/lib/logic/cubit/users/users_cubit.dart b/lib/logic/cubit/users/users_cubit.dart index 65967a9a..0fd27064 100644 --- a/lib/logic/cubit/users/users_cubit.dart +++ b/lib/logic/cubit/users/users_cubit.dart @@ -160,7 +160,12 @@ class UsersCubit extends AppConfigDependendCubit { if (user.login == 'root' || user.login == state.primaryUser.login) { return; } + // If API returned error, do nothing final result = await api.createUser(user); + if (!result.isSuccess) { + return; + } + var loadedUsers = List.from(state.users); loadedUsers.add(result.data); await box.clear(); From 4c7cf055780b8f8b3a604d09f443d0662614a2a3 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Wed, 4 May 2022 19:58:47 +0300 Subject: [PATCH 2/3] 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'; From 8d6cbfdfc9b7c0c41b36050ffa624eac7947dae8 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Wed, 4 May 2022 22:32:24 +0300 Subject: [PATCH 3/3] Fix docstyle comments --- .../forms/factories/field_cubit_factory.dart | 24 +++++++++---------- .../cubit/forms/validations/validations.dart | 4 ++-- lib/main.dart | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/logic/cubit/forms/factories/field_cubit_factory.dart b/lib/logic/cubit/forms/factories/field_cubit_factory.dart index c1b8abe7..d3255a5f 100644 --- a/lib/logic/cubit/forms/factories/field_cubit_factory.dart +++ b/lib/logic/cubit/forms/factories/field_cubit_factory.dart @@ -7,14 +7,14 @@ 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 - */ + /// 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; @@ -35,10 +35,10 @@ class FieldCubitFactory { ); } - /* A common user password field. - * - Must fail on the regural expression of invalid matches: [\n\r\s]+ - * - Must not be empty - */ + /// 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( diff --git a/lib/logic/cubit/forms/validations/validations.dart b/lib/logic/cubit/forms/validations/validations.dart index 34338559..91a8f75c 100644 --- a/lib/logic/cubit/forms/validations/validations.dart +++ b/lib/logic/cubit/forms/validations/validations.dart @@ -13,15 +13,15 @@ abstract class LengthStringValidation extends ValidationModel { } } -// String must be equal to [length] class LengthStringNotEqualValidation extends LengthStringValidation { + /// String must be equal to [length] LengthStringNotEqualValidation(int length) : super((n) => n.length != length, 'validations.length_not_equal'.tr(args: [length.toString()])); } -// String must be shorter than or equal to [length] class LengthStringLongerValidation extends LengthStringValidation { + /// String must be shorter than or equal to [length] LengthStringLongerValidation(int length) : super((n) => n.length > length, 'validations.length_longer'.tr(args: [length.toString()])); diff --git a/lib/main.dart b/lib/main.dart index db3f3b96..e5af3656 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -22,8 +22,8 @@ void main() async { await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); try { - /* Wakelock support for Linux - * desktop is not yet implemented */ + /// Wakelock support for Linux + /// desktop is not yet implemented await Wakelock.enable(); } on PlatformException catch (e) { print(e);