forked from eatvio/eatvio_app
merge upstream
This commit is contained in:
@@ -15,7 +15,7 @@ class AppRouter {
|
||||
static GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||
|
||||
// Домены для deep links (в т.ч. ссылки из пушей dev2.eatvio.com)
|
||||
static const List<String> _deepLinkHosts = ['eatvio.ru', 'www.eatvio.ru', 'dev2.eatvio.com', 'www.dev2.eatvio.com'];
|
||||
static const List<String> _deepLinkHosts = ['eatvio.ru', 'www.eatvio.ru'];
|
||||
|
||||
static GoRouter appRouter = GoRouter(
|
||||
initialLocation: RouteConstants.updateScreen,
|
||||
|
||||
@@ -10,17 +10,14 @@ import 'package:eatvio_app/features/registration/presentation/pages/verify_email
|
||||
import 'package:eatvio_app/features/registration_v2/presentation/pages/registration_v2_page.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
/// Маршруты для аутентификации
|
||||
class AuthRoutes {
|
||||
/// Создание маршрутов аутентификации
|
||||
|
||||
static List<RouteBase> createRoutes() {
|
||||
return [
|
||||
GoRoute(
|
||||
path: RouteConstants.authScreen,
|
||||
name: 'authScreen',
|
||||
builder: (context, state) => const AuthScreen(),
|
||||
// registerAnonymousPushToken вызывается в AuthCubit.initializeUser()
|
||||
// и не должен блокировать навигацию
|
||||
routes: [
|
||||
GoRoute(path: RouteConstants.registrationScreen, name: 'registrationScreen', builder: (context, state) => const RegistrationScreen()),
|
||||
GoRoute(path: RouteConstants.registrationScreenV2, name: 'registrationScreenV2', builder: (context, state) => const RegistrationV2Page()),
|
||||
|
||||
@@ -47,6 +47,7 @@ class DiaryCubit extends Cubit<DiaryState> {
|
||||
}
|
||||
|
||||
String comment = '';
|
||||
bool commentNeedsToBeFilled = false;
|
||||
|
||||
void setInitialDate(DateTime? date) {
|
||||
if (date == null) return;
|
||||
@@ -68,7 +69,7 @@ class DiaryCubit extends Cubit<DiaryState> {
|
||||
return canSelect;
|
||||
}
|
||||
|
||||
Future<bool> fetchDiaryV2({bool needLoading = false, int? diaryId, DateTime? date}) async {
|
||||
Future<bool> fetchDiaryV2({bool needLoading = false, int? diaryId}) async {
|
||||
final currentState = state;
|
||||
try {
|
||||
if (needLoading) {
|
||||
@@ -283,9 +284,15 @@ class DiaryCubit extends Cubit<DiaryState> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> finishDiaryDay({required int diaryId, required String wakeUp, required String retire}) async {
|
||||
await diaryRepository.finishDiaryDay(diaryId: diaryId, wakeUp: wakeUp, retire: retire, comment: comment.isEmpty ? null : comment);
|
||||
await updateCurrentDiary();
|
||||
Future<bool> finishDiaryDay({required int diaryId, required String wakeUp, required String retire}) async {
|
||||
try {
|
||||
await diaryRepository.finishDiaryDay(diaryId: diaryId, wakeUp: wakeUp, retire: retire, comment: comment.isEmpty ? null : comment);
|
||||
await updateCurrentDiary();
|
||||
return true;
|
||||
} catch (e, st) {
|
||||
log(e.toString(), stackTrace: st);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<NutritionDataModel> getNutrition(int index) async {
|
||||
@@ -422,6 +429,7 @@ class DiaryCubit extends Cubit<DiaryState> {
|
||||
|
||||
void fillComment(String comment) {
|
||||
this.comment = comment;
|
||||
commentNeedsToBeFilled = comment.isNotEmpty && comment.length < 3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
|
||||
import 'package:eatvio_app/core/widgets/data_error_widget.dart';
|
||||
import 'package:eatvio_app/core/widgets/loader.dart';
|
||||
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
|
||||
import 'package:eatvio_app/core/widgets/show_error_dialog.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/bloc/consultant_comments_cubit/consultant_comments_cubit.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
|
||||
@@ -39,12 +40,12 @@ class UserDiaryScreen extends StatelessWidget {
|
||||
final int diaryId;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DiaryScreenContent(diaryId: diaryId,);
|
||||
return DiaryScreenContent(diaryId: diaryId);
|
||||
}
|
||||
}
|
||||
|
||||
class DiaryScreenContent extends StatefulWidget {
|
||||
const DiaryScreenContent({super.key, this.diaryId,this.date});
|
||||
const DiaryScreenContent({super.key, this.diaryId, this.date});
|
||||
final int? diaryId;
|
||||
final DateTime? date;
|
||||
@override
|
||||
@@ -134,11 +135,14 @@ class _DiaryScreenContentState extends State<DiaryScreenContent> {
|
||||
setState(() {
|
||||
_isDiaryFinishing = true;
|
||||
});
|
||||
await context.read<DiaryCubit>().finishDiaryDay(
|
||||
final isFinished = await context.read<DiaryCubit>().finishDiaryDay(
|
||||
diaryId: state.diary.id,
|
||||
wakeUp: state.diary.wakeup.toString(),
|
||||
retire: state.diary.retire.toString(),
|
||||
);
|
||||
if (!isFinished && context.mounted) {
|
||||
showErrorDialog(context);
|
||||
}
|
||||
setState(() {
|
||||
_isDiaryFinishing = false;
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:easy_debounce/easy_debounce.dart';
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@@ -15,10 +16,14 @@ class CommentForConsultantWidget extends StatefulWidget {
|
||||
|
||||
class _CommentForConsultantWidgetState extends State<CommentForConsultantWidget> {
|
||||
final controller = TextEditingController();
|
||||
bool _commentNeedsToBeFilled = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.comment != null) controller.text = widget.comment ?? '';
|
||||
if (widget.comment != null) {
|
||||
controller.text = widget.comment ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -41,13 +46,27 @@ class _CommentForConsultantWidgetState extends State<CommentForConsultantWidget>
|
||||
CustomTextFormField(
|
||||
controller: controller,
|
||||
onChanged: (value) {
|
||||
EasyDebounce.debounce('client-typing', const Duration(milliseconds: 500), () => context.read<DiaryCubit>().fillComment(value));
|
||||
EasyDebounce.debounce(
|
||||
'client-typing',
|
||||
const Duration(milliseconds: 500),
|
||||
() => context.read<DiaryCubit>().fillComment(value),
|
||||
);
|
||||
if (widget.comment == null) {
|
||||
setState(() {
|
||||
_commentNeedsToBeFilled = value.isNotEmpty && value.length < 3;
|
||||
});
|
||||
}
|
||||
},
|
||||
hasError: _commentNeedsToBeFilled,
|
||||
hintText: 'Укажите дополнительную информацию',
|
||||
minLines: 4,
|
||||
maxLines: 4,
|
||||
readOnly: widget.comment != null,
|
||||
),
|
||||
if (_commentNeedsToBeFilled) ...[
|
||||
const SizedBox(height: 4),
|
||||
const ValidationErrorWidget(error: 'Минимум 3 символа'),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -4,7 +4,9 @@ import 'package:eatvio_app/core/widgets/bottom_sheet_handler_bar.dart';
|
||||
import 'package:eatvio_app/core/widgets/bottom_sheet_with_blur.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
|
||||
import 'package:eatvio_app/core/widgets/show_error_dialog.dart';
|
||||
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class FinishDayButtonWidget extends StatefulWidget {
|
||||
final Future<void> Function() onPressed;
|
||||
@@ -18,6 +20,14 @@ class FinishDayButtonWidget extends StatefulWidget {
|
||||
|
||||
class _FinishDayButtonWidgetState extends State<FinishDayButtonWidget> {
|
||||
void _onTap() {
|
||||
if (context.read<DiaryCubit>().commentNeedsToBeFilled) {
|
||||
showErrorDialog(
|
||||
context,
|
||||
title: 'Комментарий слишком короткий',
|
||||
description: 'Минимум 3 символа в комментарии для консультанта.',
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!widget.hasProducts) {
|
||||
showErrorDialog(
|
||||
context,
|
||||
|
||||
@@ -22,6 +22,11 @@ class PortionCommentWidget extends StatefulWidget {
|
||||
class _PortionCommentWidgetState extends State<PortionCommentWidget> {
|
||||
final controller = TextEditingController();
|
||||
@override
|
||||
void dispose() {
|
||||
controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
@override
|
||||
void didUpdateWidget(PortionCommentWidget oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (widget.portionId != oldWidget.portionId) {
|
||||
@@ -57,17 +62,19 @@ class _PortionCommentWidgetState extends State<PortionCommentWidget> {
|
||||
hintText: s.typePortionComment,
|
||||
hasError: hasError,
|
||||
onChanged: (value) {
|
||||
final cubit = context.read<ConsultantCommentsCubit>();
|
||||
EasyDebounce.debounce(
|
||||
'client-typing',
|
||||
'portion-comment-${widget.portionId}',
|
||||
const Duration(milliseconds: 500),
|
||||
() {
|
||||
final hasPortionComment = state.commentsModel.portions.any((portion) => portion.id == widget.portionId);
|
||||
final currentState = cubit.state;
|
||||
final hasPortionComment = currentState.commentsModel.portions.any((portion) => portion.id == widget.portionId);
|
||||
if (hasPortionComment) {
|
||||
final portion = state.commentsModel.portions.firstWhere((portion) => portion.id == widget.portionId);
|
||||
context.read<ConsultantCommentsCubit>().updatePortionComment(portion.copyWith(comment: value));
|
||||
final portion = currentState.commentsModel.portions.firstWhere((portion) => portion.id == widget.portionId);
|
||||
cubit.updatePortionComment(portion.copyWith(comment: value));
|
||||
return;
|
||||
}
|
||||
context.read<ConsultantCommentsCubit>().addPortionComment(PortionCommentModel(id: widget.portionId, comment: value));
|
||||
cubit.addPortionComment(PortionCommentModel(id: widget.portionId, comment: value));
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
@@ -133,7 +133,7 @@ class NotificationWidget extends StatelessWidget {
|
||||
CommentComplaintNotification() => _CommentComplaintWidget(notification: notification as CommentComplaintNotification),
|
||||
CommentDeletedNotification() => _CommentDeletedWidget(notification: notification as CommentDeletedNotification),
|
||||
UserSubscribedNotification() => _UserSubscribedWidget(notification: notification as UserSubscribedNotification),
|
||||
UnknownNotification() => _UnknownNotificationWidget(notification: notification as UnknownNotification),
|
||||
UnknownNotification() => const SizedBox.shrink(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -594,14 +594,6 @@ class _EntityCommentedWidget extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _UnknownNotificationWidget extends StatelessWidget {
|
||||
const _UnknownNotificationWidget({required this.notification});
|
||||
final UnknownNotification notification;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _BaseNotificationWidget(isRead: false, notificationsText: 'Неизвестное уведомление', createdAt: DateTime.now(), onTap: null);
|
||||
}
|
||||
}
|
||||
|
||||
// Новые виджеты для дополнительных типов уведомлений
|
||||
class _PlanSubscriptionExpiredWidget extends StatelessWidget {
|
||||
|
||||
@@ -88,7 +88,7 @@ void main() {
|
||||
when(diaryCubit.state).thenReturn(const DiaryState.loading());
|
||||
when(diaryCubit.stream).thenAnswer((_) => const Stream<DiaryState>.empty());
|
||||
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||
when(diaryCubit.fetchDiaryV2(needLoading: anyNamed('needLoading'), diaryId: anyNamed('diaryId'), date: anyNamed('date'))).thenAnswer((_) async => true);
|
||||
when(diaryCubit.fetchDiaryV2(needLoading: anyNamed('needLoading'), diaryId: anyNamed('diaryId'))).thenAnswer((_) async => true);
|
||||
|
||||
final commentsCubit = MockConsultantCommentsCubit();
|
||||
when(commentsCubit.state).thenReturn(ConsultantCommentsState(commentsModel: ConsultantCommentModel(comment: '')));
|
||||
@@ -109,7 +109,7 @@ void main() {
|
||||
when(diaryCubit.state).thenReturn(loadedState(hasChosenPlan: true));
|
||||
when(diaryCubit.stream).thenAnswer((_) => const Stream<DiaryState>.empty());
|
||||
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||
when(diaryCubit.fetchDiaryV2(needLoading: anyNamed('needLoading'), diaryId: anyNamed('diaryId'), date: anyNamed('date'))).thenAnswer((_) async => true);
|
||||
when(diaryCubit.fetchDiaryV2(needLoading: anyNamed('needLoading'), diaryId: anyNamed('diaryId'))).thenAnswer((_) async => true);
|
||||
|
||||
final commentsCubit = MockConsultantCommentsCubit();
|
||||
when(commentsCubit.state).thenReturn(ConsultantCommentsState(commentsModel: ConsultantCommentModel(comment: ''), dayCommentNeedsToBeFilled: false));
|
||||
@@ -132,7 +132,7 @@ void main() {
|
||||
when(diaryCubit.state).thenReturn(const DiaryState.error(errorMessage: 'err'));
|
||||
when(diaryCubit.stream).thenAnswer((_) => const Stream<DiaryState>.empty());
|
||||
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||
when(diaryCubit.fetchDiaryV2(needLoading: anyNamed('needLoading'), diaryId: anyNamed('diaryId'), date: anyNamed('date'))).thenAnswer((_) async => true);
|
||||
when(diaryCubit.fetchDiaryV2(needLoading: anyNamed('needLoading'), diaryId: anyNamed('diaryId'))).thenAnswer((_) async => true);
|
||||
|
||||
final commentsCubit = MockConsultantCommentsCubit();
|
||||
when(commentsCubit.state).thenReturn(ConsultantCommentsState(commentsModel: ConsultantCommentModel(comment: '')));
|
||||
@@ -161,7 +161,7 @@ void main() {
|
||||
when(diaryCubit.state).thenReturn(DiaryState.loadedTomorrowWithDiet(diary: futureDiary, selectedDay: DateTime(2025, 1, 2), selectedMeal: 1));
|
||||
when(diaryCubit.stream).thenAnswer((_) => const Stream<DiaryState>.empty());
|
||||
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||
when(diaryCubit.fetchDiaryV2(needLoading: anyNamed('needLoading'), diaryId: anyNamed('diaryId'), date: anyNamed('date'))).thenAnswer((_) async => true);
|
||||
when(diaryCubit.fetchDiaryV2(needLoading: anyNamed('needLoading'), diaryId: anyNamed('diaryId'))).thenAnswer((_) async => true);
|
||||
|
||||
final commentsCubit = MockConsultantCommentsCubit();
|
||||
when(commentsCubit.state).thenReturn(ConsultantCommentsState(commentsModel: ConsultantCommentModel(comment: '')));
|
||||
@@ -193,7 +193,7 @@ void main() {
|
||||
when(diaryCubit.state).thenReturn(state);
|
||||
when(diaryCubit.stream).thenAnswer((_) => const Stream<DiaryState>.empty());
|
||||
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||
when(diaryCubit.fetchDiaryV2(needLoading: anyNamed('needLoading'), diaryId: anyNamed('diaryId'), date: anyNamed('date'))).thenAnswer((_) async => true);
|
||||
when(diaryCubit.fetchDiaryV2(needLoading: anyNamed('needLoading'), diaryId: anyNamed('diaryId'))).thenAnswer((_) async => true);
|
||||
|
||||
final commentsCubit = MockConsultantCommentsCubit();
|
||||
when(commentsCubit.state).thenReturn(ConsultantCommentsState(commentsModel: ConsultantCommentModel(comment: ''), dayCommentNeedsToBeFilled: false));
|
||||
@@ -217,7 +217,7 @@ void main() {
|
||||
when(diaryCubit.state).thenReturn(state);
|
||||
when(diaryCubit.stream).thenAnswer((_) => const Stream<DiaryState>.empty());
|
||||
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
|
||||
when(diaryCubit.fetchDiaryV2(needLoading: anyNamed('needLoading'), diaryId: anyNamed('diaryId'), date: anyNamed('date'))).thenAnswer((_) async => true);
|
||||
when(diaryCubit.fetchDiaryV2(needLoading: anyNamed('needLoading'), diaryId: anyNamed('diaryId'))).thenAnswer((_) async => true);
|
||||
|
||||
final commentsCubit = MockConsultantCommentsCubit();
|
||||
when(commentsCubit.state).thenReturn(ConsultantCommentsState(commentsModel: ConsultantCommentModel(comment: '')));
|
||||
|
||||
Reference in New Issue
Block a user