forked from eatvio/eatvio_app
Merge remote-tracking branch 'upstream/main'
This commit is contained in:
@@ -36,9 +36,29 @@ class OAuthCubit extends Cubit<OAuthState> with RegistrationDateCacheMixin {
|
||||
final emailErrors = errors?['email'] as List<dynamic>?;
|
||||
final phoneErrors = errors?['phone'] as List<dynamic>?;
|
||||
|
||||
if ((emailErrors != null && emailErrors.isNotEmpty) || (phoneErrors != null && phoneErrors.isNotEmpty)) {
|
||||
// Это ошибка валидации - нужно показать экран подтверждения
|
||||
emit(const OAuthState.error(errorMessage: 'validation_error'));
|
||||
final errorsList = <RegistrationValidationErrors>[];
|
||||
if (emailErrors != null && emailErrors.isNotEmpty) {
|
||||
for (final error in emailErrors) {
|
||||
final errorStr = error.toString();
|
||||
if (errorStr.contains('Некорректный формат email')) {
|
||||
errorsList.add(RegistrationValidationErrors.incorrectEmailFormat);
|
||||
}
|
||||
if (errorStr.contains('почта уже существует') || errorStr.contains('Эл. почта уже существует')) {
|
||||
errorsList.add(RegistrationValidationErrors.emailExist);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (phoneErrors != null && phoneErrors.isNotEmpty) {
|
||||
for (final error in phoneErrors) {
|
||||
final errorStr = error.toString();
|
||||
if (errorStr.contains('phone уже существует') || errorStr.contains('телефон уже существует')) {
|
||||
errorsList.add(RegistrationValidationErrors.phoneExist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (errorsList.isNotEmpty) {
|
||||
emit(OAuthState.error(errorMessage: 'validation_error', registrationValidationErrors: errorsList));
|
||||
} else {
|
||||
emit(OAuthState.error(errorMessage: e.toString()));
|
||||
}
|
||||
@@ -126,7 +146,7 @@ class OAuthCubit extends Cubit<OAuthState> with RegistrationDateCacheMixin {
|
||||
try {
|
||||
emit(const OAuthState.loading());
|
||||
log('OAuthCubit: Requesting verification code for email: $email, phone: $phone');
|
||||
final response = await authRepository.requestOAuthVerification(email: null, phone: phone);
|
||||
final response = await authRepository.requestOAuthVerification(email: email, phone: phone);
|
||||
if (response != null) {
|
||||
log('OAuthCubit: Verification code requested successfully, masked email: ${response.email}');
|
||||
}
|
||||
@@ -188,4 +208,3 @@ class OAuthCubit extends Cubit<OAuthState> with RegistrationDateCacheMixin {
|
||||
}
|
||||
}
|
||||
|
||||
enum RegistrationValidationErrors { incorrectEmailFormat, emailExist, phoneExist, incorrectDateOfBirthFormat }
|
||||
|
||||
@@ -10,5 +10,10 @@ abstract class OAuthState with _$OAuthState {
|
||||
|
||||
const factory OAuthState.loaded({required String token}) = OAuthStateLoaded;
|
||||
|
||||
const factory OAuthState.error({required String errorMessage}) = OAuthStateError;
|
||||
const factory OAuthState.error({
|
||||
required String errorMessage,
|
||||
List<RegistrationValidationErrors>? registrationValidationErrors,
|
||||
}) = OAuthStateError;
|
||||
}
|
||||
|
||||
enum RegistrationValidationErrors { incorrectEmailFormat, emailExist, phoneExist, incorrectDateOfBirthFormat }
|
||||
|
||||
@@ -58,6 +58,39 @@ class _CreateDietScreenContentState extends State<CreateDietScreenContent> {
|
||||
final GlobalKey<MarkdownEditorState> _mdKey = GlobalKey<MarkdownEditorState>();
|
||||
bool _mdToolbarVisible = false;
|
||||
|
||||
final GlobalKey _nameKey = GlobalKey();
|
||||
final GlobalKey _durationKey = GlobalKey();
|
||||
final GlobalKey _statusKey = GlobalKey();
|
||||
final GlobalKey _typeKey = GlobalKey();
|
||||
final GlobalKey _imageKey = GlobalKey();
|
||||
final GlobalKey _planKey = GlobalKey();
|
||||
|
||||
void _scrollToFirstError(CreateDietState state) {
|
||||
GlobalKey? key;
|
||||
if (state.dietNameError != null) {
|
||||
key = _nameKey;
|
||||
} else if (state.dietDescriptionError != null) {
|
||||
key = _mdKey;
|
||||
} else if (state.dietDurationError != null) {
|
||||
key = _durationKey;
|
||||
} else if (state.dietStateError != null) {
|
||||
key = _statusKey;
|
||||
} else if (state.dietImageError != null) {
|
||||
key = _imageKey;
|
||||
} else if (state.dailyScheduleErrors.any((e) => e.isNotEmpty)) {
|
||||
key = _planKey;
|
||||
}
|
||||
|
||||
if (key != null && key.currentContext != null) {
|
||||
Scrollable.ensureVisible(
|
||||
key.currentContext!,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
curve: Curves.easeInOut,
|
||||
alignment: 0.1,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -118,6 +151,7 @@ class _CreateDietScreenContentState extends State<CreateDietScreenContent> {
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
CustomTextFormField(
|
||||
key: _nameKey,
|
||||
hintText: s.enter_diet_name,
|
||||
controller: nameController,
|
||||
hasError: state.dietNameError != null,
|
||||
@@ -143,19 +177,20 @@ class _CreateDietScreenContentState extends State<CreateDietScreenContent> {
|
||||
),
|
||||
if (state.dietDescriptionError != null) ...[const SizedBox(height: 4), ValidationErrorWidget(error: state.dietDescriptionError ?? '')],
|
||||
const SizedBox(height: 12),
|
||||
const DietDurationDropDownWidget(),
|
||||
DietDurationDropDownWidget(key: _durationKey),
|
||||
const SizedBox(height: 12),
|
||||
StatusDropDownWidget(dietState: widget.diet?.state),
|
||||
StatusDropDownWidget(key: _statusKey, dietState: widget.diet?.state),
|
||||
const SizedBox(height: 16),
|
||||
const DietTypeChoiceWidget(),
|
||||
DietTypeChoiceWidget(key: _typeKey),
|
||||
const SizedBox(height: 16),
|
||||
const DietImagePickerWidget(),
|
||||
DietImagePickerWidget(key: _imageKey),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
MainBackgroundBouble(
|
||||
key: _planKey,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||
child: Center(
|
||||
@@ -244,7 +279,11 @@ class _CreateDietScreenContentState extends State<CreateDietScreenContent> {
|
||||
? () {}
|
||||
: () async {
|
||||
final diet = await context.read<CreateDietCubit>().getDietForPreview();
|
||||
if (diet == null) return;
|
||||
if (diet == null) {
|
||||
// ignore: use_build_context_synchronously
|
||||
_scrollToFirstError(context.read<CreateDietCubit>().state);
|
||||
return;
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
GoRouter.of(
|
||||
context,
|
||||
@@ -283,7 +322,10 @@ class _CreateDietScreenContentState extends State<CreateDietScreenContent> {
|
||||
}
|
||||
} else {
|
||||
final result = await context.read<CreateDietCubit>().createDiet();
|
||||
if (result == 'error') {
|
||||
if (result == 'validation') {
|
||||
// ignore: use_build_context_synchronously
|
||||
_scrollToFirstError(context.read<CreateDietCubit>().state);
|
||||
} else if (result == 'error') {
|
||||
if (!context.mounted) return;
|
||||
showErrorDialog(context, title: '${s.could_not_publish_diet} ${state.dietData.name}');
|
||||
}
|
||||
|
||||
@@ -436,11 +436,11 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
result = false;
|
||||
emit(state.copyWith(dietStateError: 'Статус диеты не выбран'));
|
||||
}
|
||||
if (state.dietData.image == null) {
|
||||
if (state.dietData.image == null && state.dietData.imageUrl == null) {
|
||||
result = false;
|
||||
emit(state.copyWith(dietImageError: 'Не добавлена обложка'));
|
||||
}
|
||||
result = _isDailyScheduleValid();
|
||||
result = _isDailyScheduleValid() && result;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -287,8 +287,7 @@ void main() {
|
||||
|
||||
const verificationResponse = OAuthVerificationRequestResponse(email: 't***@example.com', status: 'sent');
|
||||
|
||||
// OAuthCubit всегда передает email: null в репозиторий, независимо от переданного параметра
|
||||
when(mockAuthRepository.requestOAuthVerification(email: null, phone: null)).thenAnswer((_) async => verificationResponse);
|
||||
when(mockAuthRepository.requestOAuthVerification(email: anyNamed('email'), phone: anyNamed('phone'))).thenAnswer((_) async => verificationResponse);
|
||||
|
||||
final result = await oauthCubit!.requestOAuthVerification(email: 'test@example.com');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user