forked from eatvio/eatvio_app
merge upstream
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 }
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:badges/badges.dart' as badges;
|
||||
import 'package:eatvio_app/core/app_config.dart';
|
||||
import 'package:eatvio_app/core/env_config.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/utils/image_constant.dart';
|
||||
import 'package:eatvio_app/features/dev_tools/presentation/dev_tools_screen.dart';
|
||||
@@ -19,10 +20,7 @@ import 'package:go_router/go_router.dart';
|
||||
class BottomNavigationBarWidget extends StatefulWidget {
|
||||
final Widget child;
|
||||
|
||||
const BottomNavigationBarWidget({
|
||||
super.key,
|
||||
required this.child,
|
||||
});
|
||||
const BottomNavigationBarWidget({super.key, required this.child});
|
||||
|
||||
@override
|
||||
State<BottomNavigationBarWidget> createState() => _BottomNavigationBarWidgetState();
|
||||
@@ -35,24 +33,9 @@ class _BottomNavigationBarWidgetState extends State<BottomNavigationBarWidget> {
|
||||
|
||||
List<String> _getPages(bool isConsultant) {
|
||||
if (isConsultant) {
|
||||
return [
|
||||
'/my-profile-screen',
|
||||
'/feed-screen',
|
||||
'/diary-screen',
|
||||
'/consultations-screen',
|
||||
'/chat-select-screen',
|
||||
'/notifications-screen',
|
||||
'/menu-screen',
|
||||
];
|
||||
return ['/my-profile-screen', '/feed-screen', '/diary-screen', '/consultations-screen', '/chat-select-screen', '/notifications-screen', '/menu-screen'];
|
||||
} else {
|
||||
return [
|
||||
'/my-profile-screen',
|
||||
'/feed-screen',
|
||||
'/diary-screen',
|
||||
'/chat-select-screen',
|
||||
'/notifications-screen',
|
||||
'/menu-screen',
|
||||
];
|
||||
return ['/my-profile-screen', '/feed-screen', '/diary-screen', '/chat-select-screen', '/notifications-screen', '/menu-screen'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +66,49 @@ class _BottomNavigationBarWidgetState extends State<BottomNavigationBarWidget> {
|
||||
}
|
||||
}
|
||||
|
||||
static const Color _devAccentColor = Color(0xFFF59E0B); // amber-500
|
||||
|
||||
Widget _buildLogoIcon() {
|
||||
final isProduction = sl<AppConfig>().baseUrl == EnvConfig.baseUrl;
|
||||
final logo = SvgPicture.asset(
|
||||
ImageConstant.imgLogo,
|
||||
height: 32,
|
||||
colorFilter: isProduction ? null : const ColorFilter.mode(_devAccentColor, BlendMode.srcIn),
|
||||
);
|
||||
if (isProduction) return logo;
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: _devAccentColor.withValues(alpha: 0.6), width: 1.5),
|
||||
color: _devAccentColor.withValues(alpha: 0.08),
|
||||
),
|
||||
child: logo,
|
||||
),
|
||||
Positioned(
|
||||
top: -6,
|
||||
right: -4,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1),
|
||||
decoration: BoxDecoration(
|
||||
color: _devAccentColor,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
boxShadow: [BoxShadow(color: _devAccentColor.withValues(alpha: 0.4), blurRadius: 4, offset: const Offset(0, 1))],
|
||||
),
|
||||
child: const Text(
|
||||
'DEV',
|
||||
style: TextStyle(fontSize: 8, fontWeight: FontWeight.w700, color: Colors.white, letterSpacing: 0.3, height: 1.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = S.of(context);
|
||||
@@ -91,10 +117,10 @@ class _BottomNavigationBarWidgetState extends State<BottomNavigationBarWidget> {
|
||||
if (state is ProfileAvatarStateLoaded) {
|
||||
final profileModel = state.profile;
|
||||
final isConsultant = profileModel.roles.contains('consultant');
|
||||
|
||||
|
||||
// Инициализируем pages на основе profileModel
|
||||
pages ??= _getPages(isConsultant);
|
||||
|
||||
|
||||
return Scaffold(
|
||||
body: widget.child,
|
||||
bottomNavigationBar: BottomNavigationBar(
|
||||
@@ -117,52 +143,26 @@ class _BottomNavigationBarWidgetState extends State<BottomNavigationBarWidget> {
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
label: 'Лента',
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 2),
|
||||
child: SvgPicture.asset(
|
||||
colorFilter: const ColorFilter.mode(Colors.grey, BlendMode.srcIn),
|
||||
width: 20,
|
||||
height: 20,
|
||||
ImageConstant.icnFeed,
|
||||
),
|
||||
),
|
||||
activeIcon: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 2),
|
||||
child: SvgPicture.asset(
|
||||
ImageConstant.icnFeed,
|
||||
width: 20,
|
||||
height: 20,
|
||||
colorFilter: ColorFilter.mode(
|
||||
appTheme.blueA400,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
),
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 2),
|
||||
child: SvgPicture.asset(colorFilter: const ColorFilter.mode(Colors.grey, BlendMode.srcIn), width: 20, height: 20, ImageConstant.icnFeed),
|
||||
),
|
||||
activeIcon: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 2),
|
||||
child: SvgPicture.asset(ImageConstant.icnFeed, width: 20, height: 20, colorFilter: ColorFilter.mode(appTheme.blueA400, BlendMode.srcIn)),
|
||||
),
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
label: s.diary,
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 2),
|
||||
child: SvgPicture.asset(
|
||||
colorFilter: const ColorFilter.mode(Colors.grey, BlendMode.srcIn),
|
||||
ImageConstant.icnDiary,
|
||||
width: 20,
|
||||
height: 20,
|
||||
),
|
||||
),
|
||||
activeIcon: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 2),
|
||||
child: SvgPicture.asset(
|
||||
ImageConstant.icnDiary,
|
||||
width: 20,
|
||||
height: 20,
|
||||
colorFilter: ColorFilter.mode(
|
||||
appTheme.blueA400,
|
||||
BlendMode.srcIn,
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 2),
|
||||
child: SvgPicture.asset(colorFilter: const ColorFilter.mode(Colors.grey, BlendMode.srcIn), ImageConstant.icnDiary, width: 20, height: 20),
|
||||
),
|
||||
activeIcon: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 2),
|
||||
child: SvgPicture.asset(ImageConstant.icnDiary, width: 20, height: 20, colorFilter: ColorFilter.mode(appTheme.blueA400, BlendMode.srcIn)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (profileModel.roles.contains('consultant'))
|
||||
BottomNavigationBarItem(
|
||||
label: 'Консультации',
|
||||
@@ -181,97 +181,69 @@ class _BottomNavigationBarWidgetState extends State<BottomNavigationBarWidget> {
|
||||
'assets/icons/consultations.svg',
|
||||
width: 20,
|
||||
height: 20,
|
||||
colorFilter: ColorFilter.mode(
|
||||
appTheme.blueA400,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
colorFilter: ColorFilter.mode(appTheme.blueA400, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
label: s.chat,
|
||||
icon: BlocBuilder<ChatsCubit, ChatsState>(
|
||||
builder: (BuildContext context, ChatsState state) {
|
||||
final hasUnreadMessages = state.chatsWithUnreadMessagesCount > 0;
|
||||
return badges.Badge(
|
||||
position: badges.BadgePosition.topEnd(),
|
||||
badgeAnimation: const badges.BadgeAnimation.fade(),
|
||||
badgeStyle: badges.BadgeStyle(
|
||||
badgeColor: hasUnreadMessages ? Colors.red : Colors.transparent,
|
||||
icon: BlocBuilder<ChatsCubit, ChatsState>(
|
||||
builder: (BuildContext context, ChatsState state) {
|
||||
final hasUnreadMessages = state.chatsWithUnreadMessagesCount > 0;
|
||||
return badges.Badge(
|
||||
position: badges.BadgePosition.topEnd(),
|
||||
badgeAnimation: const badges.BadgeAnimation.fade(),
|
||||
badgeStyle: badges.BadgeStyle(badgeColor: hasUnreadMessages ? Colors.red : Colors.transparent),
|
||||
badgeContent: Text(
|
||||
hasUnreadMessages ? state.chatsWithUnreadMessagesCount.toString() : '',
|
||||
style: TextStyle(color: hasUnreadMessages ? Colors.white : Colors.transparent, fontSize: 8),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 2),
|
||||
child: SvgPicture.asset(
|
||||
ImageConstant.icnChat,
|
||||
width: 20,
|
||||
height: 20,
|
||||
colorFilter: pages![currentBottomNavBarIndex] == '/chat-select-screen'
|
||||
? ColorFilter.mode(appTheme.blueA400, BlendMode.srcIn)
|
||||
: const ColorFilter.mode(Colors.grey, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
badgeContent: Text(
|
||||
hasUnreadMessages ? state.chatsWithUnreadMessagesCount.toString() : '',
|
||||
style: TextStyle(
|
||||
color: hasUnreadMessages ? Colors.white : Colors.transparent,
|
||||
fontSize: 8,
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 2),
|
||||
child: SvgPicture.asset(
|
||||
ImageConstant.icnChat,
|
||||
width: 20,
|
||||
height: 20,
|
||||
colorFilter: pages![currentBottomNavBarIndex] == '/chat-select-screen'
|
||||
? ColorFilter.mode(
|
||||
appTheme.blueA400,
|
||||
BlendMode.srcIn,
|
||||
)
|
||||
: const ColorFilter.mode(Colors.grey, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
label: 'Уведомления',
|
||||
icon: BlocBuilder<NotificationsCubit, NotificationsState>(
|
||||
builder: (BuildContext context, NotificationsState state) {
|
||||
return badges.Badge(
|
||||
position: badges.BadgePosition.topEnd(),
|
||||
badgeAnimation: const badges.BadgeAnimation.fade(),
|
||||
showBadge: state is NotificationsStateLoaded && state.hasUnreadNotifications,
|
||||
badgeContent: const Text(
|
||||
'',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 8,
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 2),
|
||||
child: SvgPicture.asset(
|
||||
ImageConstant.icnBell,
|
||||
colorFilter: pages![currentBottomNavBarIndex] == '/notifications-screen'
|
||||
? ColorFilter.mode(
|
||||
appTheme.blueA400,
|
||||
BlendMode.srcIn,
|
||||
)
|
||||
: const ColorFilter.mode(Colors.grey, BlendMode.srcIn),
|
||||
width: 20,
|
||||
height: 20,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
label: '',
|
||||
icon: SvgPicture.asset(
|
||||
ImageConstant.imgLogo,
|
||||
height: 32,
|
||||
icon: BlocBuilder<NotificationsCubit, NotificationsState>(
|
||||
builder: (BuildContext context, NotificationsState state) {
|
||||
return badges.Badge(
|
||||
position: badges.BadgePosition.topEnd(),
|
||||
badgeAnimation: const badges.BadgeAnimation.fade(),
|
||||
showBadge: state is NotificationsStateLoaded && state.hasUnreadNotifications,
|
||||
badgeContent: const Text('', style: TextStyle(color: Colors.white, fontSize: 8)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 2),
|
||||
child: SvgPicture.asset(
|
||||
ImageConstant.icnBell,
|
||||
colorFilter: pages![currentBottomNavBarIndex] == '/notifications-screen'
|
||||
? ColorFilter.mode(appTheme.blueA400, BlendMode.srcIn)
|
||||
: const ColorFilter.mode(Colors.grey, BlendMode.srcIn),
|
||||
width: 20,
|
||||
height: 20,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
BottomNavigationBarItem(label: '', icon: _buildLogoIcon()),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
// Показываем child даже когда профиль еще загружается, чтобы экраны могли инициализироваться
|
||||
return Scaffold(
|
||||
body: widget.child,
|
||||
);
|
||||
return Scaffold(body: widget.child);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
@@ -185,7 +220,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).pushNamed(DietPreviewScreen.name, extra: diet);
|
||||
},
|
||||
@@ -204,7 +243,10 @@ class _CreateDietScreenContentState extends State<CreateDietScreenContent> {
|
||||
CustomOutlinedButton(
|
||||
onPressed: () async {
|
||||
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}');
|
||||
}
|
||||
|
||||
@@ -394,11 +394,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -132,7 +132,12 @@ class _AddProductModalState extends State<AddProductModal> {
|
||||
subtitle: glossariesList[i].grInPortion == 0
|
||||
? null
|
||||
: Text('${glossariesList[i].grInPortion} г', style: TextStyle(height: 1, color: Colors.grey.shade600)),
|
||||
trailing: Icon(Icons.add_rounded, color: Colors.grey.shade400),
|
||||
trailing: Icon(
|
||||
productNameController.text.trim().toLowerCase() == glossariesList[i].name.toLowerCase()
|
||||
? Icons.radio_button_checked
|
||||
: Icons.radio_button_off,
|
||||
color: Colors.blueAccent,
|
||||
),
|
||||
contentPadding: const EdgeInsets.only(left: 12),
|
||||
);
|
||||
},
|
||||
@@ -166,7 +171,9 @@ class _AddProductModalState extends State<AddProductModal> {
|
||||
InkWell(
|
||||
onTap: () {
|
||||
// Автоматически выбираем первый элемент, если текст точно совпадает
|
||||
if (selectedGlossary == null && glossariesList.isNotEmpty && productNameController.text.trim().toLowerCase() == glossariesList[0].name.toLowerCase()) {
|
||||
if (selectedGlossary == null &&
|
||||
glossariesList.isNotEmpty &&
|
||||
productNameController.text.trim().toLowerCase() == glossariesList[0].name.toLowerCase()) {
|
||||
selectedGlossary = glossariesList[0];
|
||||
}
|
||||
|
||||
|
||||
@@ -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