From c1e2b1c312ef197c2344d3e76df64e14a66f10df Mon Sep 17 00:00:00 2001 From: Username1548 Date: Tue, 20 May 2025 22:21:10 +0700 Subject: [PATCH 1/4] created notifications model and logic of type based parsing --- lib/core/websockets/ws_manager.dart | 3 +- .../models/notification_model.dart | 307 +++++++++++++++++- .../notifications_cubit.dart | 2 +- .../notifications_state.dart | 2 +- .../widgets/notification_widget.dart | 3 +- .../repository/notifications_repository.dart | 15 + .../service/notifications_service.dart | 41 +++ 7 files changed, 350 insertions(+), 23 deletions(-) create mode 100644 lib/features/notifications/repository/notifications_repository.dart create mode 100644 lib/features/notifications/service/notifications_service.dart diff --git a/lib/core/websockets/ws_manager.dart b/lib/core/websockets/ws_manager.dart index 3a64ff14..ce47f26a 100644 --- a/lib/core/websockets/ws_manager.dart +++ b/lib/core/websockets/ws_manager.dart @@ -18,8 +18,7 @@ class WsManager { PrivateChannel? _privateDirectChatChannel; Stream? get newMessagesStream => _privateMessagesChannel?.bind('App\\Events\\ChatMessageUpdated'); - - // TODO: change name of event to a right one + Stream? get newNotificationsStream => _privateNotificationChannel?.bind('Illuminate\\Notifications\\Events\\BroadcastNotificationCreated'); Stream? get directChatStream => _privateDirectChatChannel?.bind('App\\Events\\ChatMessageUpdated'); diff --git a/lib/features/notifications/models/notification_model.dart b/lib/features/notifications/models/notification_model.dart index 2b757de1..47942287 100644 --- a/lib/features/notifications/models/notification_model.dart +++ b/lib/features/notifications/models/notification_model.dart @@ -1,25 +1,298 @@ import 'package:freezed_annotation/freezed_annotation.dart'; -part 'notification_model.freezed.dart'; -part 'notification_model.g.dart'; - -@freezed -class NotificationModel with _$NotificationModel { - - factory NotificationModel({ - required NotificationType type - }) = _NotificationModel; - - factory NotificationModel.fromJson(Map json) => _$NotificationModelFromJson(json); +abstract class BaseNotificationModel { + abstract final NotificationType type; } +class PlanSubscriptionStartedToAuthorNotification implements BaseNotificationModel { + @override + final NotificationType type; + final String planName; + final String customerName; + final String chatId; + + PlanSubscriptionStartedToAuthorNotification({ + required this.type, + required this.planName, + required this.customerName, + required this.chatId, + }); + + factory PlanSubscriptionStartedToAuthorNotification.fromRest(Map json) { + throw UnimplementedError(); + } + + factory PlanSubscriptionStartedToAuthorNotification.fromWebSocket(Map json) { + throw UnimplementedError(); + } +} + +class PlanSubscriptionCanceledToAuthorNotification implements BaseNotificationModel { + @override + final NotificationType type; + final String planName; + final String customerName; + + PlanSubscriptionCanceledToAuthorNotification({ + required this.type, + required this.planName, + required this.customerName, + }); + + factory PlanSubscriptionCanceledToAuthorNotification.fromRest(Map json) { + throw UnimplementedError(); + } + + factory PlanSubscriptionCanceledToAuthorNotification.fromWebSocket(Map json) { + throw UnimplementedError(); + } +} + +class ChosenPlanStartedToClientNotification implements BaseNotificationModel { + @override + final NotificationType type; + final String planName; + final int planId; + + ChosenPlanStartedToClientNotification({ + required this.type, + required this.planName, + required this.planId, + }); + + factory ChosenPlanStartedToClientNotification.fromRest(Map json) { + throw UnimplementedError(); + } + + factory ChosenPlanStartedToClientNotification.fromWebSocket(Map json) { + throw UnimplementedError(); + } +} + +class ChosenPlanTomorrowReminderToClientNotification implements BaseNotificationModel { + @override + final NotificationType type; + final String planName; + + ChosenPlanTomorrowReminderToClientNotification({ + required this.type, + required this.planName, + }); + + factory ChosenPlanTomorrowReminderToClientNotification.fromRest(Map json) { + throw UnimplementedError(); + } + + factory ChosenPlanTomorrowReminderToClientNotification.fromWebSocket(Map json) { + throw UnimplementedError(); + } +} + +class DiaryReminderNotification implements BaseNotificationModel { + @override + final NotificationType type; + + DiaryReminderNotification({required this.type}); + + factory DiaryReminderNotification.fromRest(Map json) { + throw UnimplementedError(); + } + + factory DiaryReminderNotification.fromWebSocket(Map json) { + throw UnimplementedError(); + } +} + +class ConsultantLeftCommentNotification implements BaseNotificationModel { + @override + final NotificationType type; + final DateTime date; + + ConsultantLeftCommentNotification({ + required this.type, + required this.date, + }); + + factory ConsultantLeftCommentNotification.fromRest(Map json) { + throw UnimplementedError(); + } + + factory ConsultantLeftCommentNotification.fromWebSocket(Map json) { + throw UnimplementedError(); + } +} + +class PersonalizedDietIsDefinedNotification implements BaseNotificationModel { + @override + final NotificationType type; + + PersonalizedDietIsDefinedNotification({required this.type}); + + factory PersonalizedDietIsDefinedNotification.fromRest(Map json) { + throw UnimplementedError(); + } + + factory PersonalizedDietIsDefinedNotification.fromWebSocket(Map json) { + throw UnimplementedError(); + } +} + +class ChosenPlanFinishedToClientNotification implements BaseNotificationModel { + @override + final NotificationType type; + final String planName; + ChosenPlanFinishedToClientNotification({ + required this.type, + required this.planName, + }); + + factory ChosenPlanFinishedToClientNotification.fromRest(Map json) { + throw UnimplementedError(); + } + + factory ChosenPlanFinishedToClientNotification.fromWebSocket(Map json) { + throw UnimplementedError(); + } +} + +class PlanSubscriptionCanceledToCustomerNotification implements BaseNotificationModel { + @override + final NotificationType type; + final String planName; + PlanSubscriptionCanceledToCustomerNotification({ + required this.type, + required this.planName, + }); + + factory PlanSubscriptionCanceledToCustomerNotification.fromRest(Map json) { + throw UnimplementedError(); + } + + factory PlanSubscriptionCanceledToCustomerNotification.fromWebSocket(Map json) { + throw UnimplementedError(); + } +} + +class ChosenPlanStartedToConsultantNotification implements BaseNotificationModel { + @override + final NotificationType type; + final String planName; + final String customerName; + ChosenPlanStartedToConsultantNotification({ + required this.type, + required this.planName, + required this.customerName, + }); + + factory ChosenPlanStartedToConsultantNotification.fromRest(Map json) { + throw UnimplementedError(); + } + + factory ChosenPlanStartedToConsultantNotification.fromWebSocket(Map json) { + throw UnimplementedError(); + } +} + +class ChosenPlanTomorrowReminderToConsultantNotification implements BaseNotificationModel { + @override + final NotificationType type; + final String planName; + final String customerName; + ChosenPlanTomorrowReminderToConsultantNotification({ + required this.type, + required this.planName, + required this.customerName, + }); + + factory ChosenPlanTomorrowReminderToConsultantNotification.fromRest(Map json) { + throw UnimplementedError(); + } + + factory ChosenPlanTomorrowReminderToConsultantNotification.fromWebSocket(Map json) { + throw UnimplementedError(); + } +} + +class PlanSubscriptionPaidToAuthorNotification implements BaseNotificationModel { + @override + final NotificationType type; + final String planName; + final String customerName; + PlanSubscriptionPaidToAuthorNotification({ + required this.type, + required this.planName, + required this.customerName, + }); + + factory PlanSubscriptionPaidToAuthorNotification.fromRest(Map json) { + throw UnimplementedError(); + } + + factory PlanSubscriptionPaidToAuthorNotification.fromWebSocket(Map json) { + throw UnimplementedError(); + } +} + +class ChosenPlanFinishedToConsultantNotification implements BaseNotificationModel { + @override + final NotificationType type; + final String planName; + final String customerName; + final int planId; + ChosenPlanFinishedToConsultantNotification({ + required this.type, + required this.planName, + required this.customerName, + required this.planId, + }); + + factory ChosenPlanFinishedToConsultantNotification.fromRest(Map json) { + throw UnimplementedError(); + } + + factory ChosenPlanFinishedToConsultantNotification.fromWebSocket(Map json) { + throw UnimplementedError(); + } +} + +class UnknownNotification implements BaseNotificationModel { + @override + final NotificationType type; + UnknownNotification({ + required this.type, + }); +} enum NotificationType { - @JsonValue('App\\Notifications\\UserSubscribedNotification') - userSubscribedNotification, - @JsonValue('App\\Notifications\\Plan\\PlanSubscriptionStartedToAuthor') planSubscriptionStartedToAuthor, - @JsonValue('App\\Notifications\\Plan\\PlanSubscriptionCanceledToAuthor') planSubscriptionCanceledToAuthor, - -} \ No newline at end of file + chosenPlanStartedToClientNotification, + chosenPlanTomorrowReminderToClientNotification, + diaryReminderNotification, + consultantLeftComment, + personalizedDietIsDefined, + chosenPlanFinishedToClient, + planSubscriptionCanceledToCustomer, + chosenPlanStartedToConsultantNotification, + chosenPlanTomorrowReminderToConsultantNotification, + planSubscriptionPaidToAuthor, + chosenPlanFinishedToConsultant, + unknown; + + static NotificationType getTypeFromString(String type) => switch (type) { + 'App\\Notifications\\Plan\\PlanSubscriptionStartedToAuthor' => NotificationType.planSubscriptionStartedToAuthor, + 'App\\Notifications\\Plan\\PlanSubscriptionCanceledToAuthor' => NotificationType.planSubscriptionCanceledToAuthor, + 'App\\Notifications\\Plan\\ChosenPlanStartedToClientNotification' => NotificationType.chosenPlanStartedToClientNotification, + 'App\\Notifications\\Plan\\ChosenPlanTomorrowReminderToClientNotification' => NotificationType.chosenPlanStartedToClientNotification, + 'App\\Notifications\\DiaryReminderNotification' => NotificationType.diaryReminderNotification, + 'App\\Notifications\\Plan\\ConsultantLeftComment' => NotificationType.consultantLeftComment, + 'App\\Notifications\\Plan\\PersonalizedDietIsDefined' => NotificationType.personalizedDietIsDefined, + 'App\\Notifications\\Plan\\ChosenPlanFinishedToClient' => NotificationType.chosenPlanFinishedToClient, + 'App\\Notifications\\Plan\\PlanSubscriptionCanceledToCustomer' => NotificationType.planSubscriptionCanceledToCustomer, + 'App\\Notifications\\Plan\\ChosenPlanStartedToConsultantNotification' => NotificationType.chosenPlanStartedToConsultantNotification, + 'App\\Notifications\\Plan\\ChosenPlanTomorrowReminderToConsultantNotification' => NotificationType.chosenPlanTomorrowReminderToConsultantNotification, + 'App\\Notifications\\Plan\\PlanSubscriptionPaidToAuthor' => NotificationType.planSubscriptionPaidToAuthor, + 'App\\Notifications\\Plan\\ChosenPlanFinishedToConsultant' => NotificationType.chosenPlanFinishedToConsultant, + _ => NotificationType.unknown, + }; +} diff --git a/lib/features/notifications/presentation/notifications_cubit/notifications_cubit.dart b/lib/features/notifications/presentation/notifications_cubit/notifications_cubit.dart index 506865b6..bded5181 100644 --- a/lib/features/notifications/presentation/notifications_cubit/notifications_cubit.dart +++ b/lib/features/notifications/presentation/notifications_cubit/notifications_cubit.dart @@ -4,7 +4,7 @@ import 'package:eatvio_app/features/notifications/presentation/notifications_cub import 'package:flutter_bloc/flutter_bloc.dart'; class NotificationsCubit extends Cubit { - final List _notifications = []; + final List _notifications = []; final WsManager wsManager; NotificationsCubit({required this.wsManager}) : super(const NotificationsState.empty()) { diff --git a/lib/features/notifications/presentation/notifications_cubit/notifications_state.dart b/lib/features/notifications/presentation/notifications_cubit/notifications_state.dart index b47dc762..63d899b5 100644 --- a/lib/features/notifications/presentation/notifications_cubit/notifications_state.dart +++ b/lib/features/notifications/presentation/notifications_cubit/notifications_state.dart @@ -10,7 +10,7 @@ abstract class NotificationsState with _$NotificationsState { const factory NotificationsState.empty() = NotificationsStateEmpty; const factory NotificationsState.loaded({ - required List notifications, + required List notifications, required int notificationsCount, }) = NotificationsStateLoaded; diff --git a/lib/features/notifications/presentation/widgets/notification_widget.dart b/lib/features/notifications/presentation/widgets/notification_widget.dart index cc0a3a2a..5da2aa6c 100644 --- a/lib/features/notifications/presentation/widgets/notification_widget.dart +++ b/lib/features/notifications/presentation/widgets/notification_widget.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; class NotificationWidget extends StatelessWidget { const NotificationWidget({super.key, required this.notification}); - final NotificationModel notification; + final BaseNotificationModel notification; @override Widget build(BuildContext context) { // return (notification.type) { @@ -12,4 +12,3 @@ class NotificationWidget extends StatelessWidget { return Container(); } } - diff --git a/lib/features/notifications/repository/notifications_repository.dart b/lib/features/notifications/repository/notifications_repository.dart new file mode 100644 index 00000000..d9ea934d --- /dev/null +++ b/lib/features/notifications/repository/notifications_repository.dart @@ -0,0 +1,15 @@ +import 'package:eatvio_app/features/notifications/models/notification_model.dart'; +import 'package:eatvio_app/features/notifications/service/notifications_service.dart'; + +class NotificationsRepository { + final NotificationsService notificationsService; + NotificationsRepository({required this.notificationsService}); + + Future?> getNotifications({required int page}) { + try { + return notificationsService.getNotifications(page: page); + } catch (e) { + rethrow; + } + } +} diff --git a/lib/features/notifications/service/notifications_service.dart b/lib/features/notifications/service/notifications_service.dart new file mode 100644 index 00000000..d5bd7354 --- /dev/null +++ b/lib/features/notifications/service/notifications_service.dart @@ -0,0 +1,41 @@ +import 'package:dio/dio.dart'; +import 'package:eatvio_app/features/notifications/models/notification_model.dart'; + +class NotificationsService { + final Dio dio; + NotificationsService(this.dio); + + Future?> getNotifications({required int page}) async { + try { + final response = await dio.get('api/profile/notifications?page=$page'); + if (response.statusCode == 200) { + final data = response.data['data'] as List>; + final notifications = data.map((notificationJson) { + final stringifiedType = notificationJson['data']['notification_class_name']; + final type = NotificationType.getTypeFromString(stringifiedType); + return switch (type) { + NotificationType.planSubscriptionStartedToAuthor => PlanSubscriptionStartedToAuthorNotification.fromRest(notificationJson), + NotificationType.planSubscriptionCanceledToAuthor => PlanSubscriptionCanceledToAuthorNotification.fromRest(notificationJson), + NotificationType.chosenPlanStartedToClientNotification => ChosenPlanStartedToClientNotification.fromRest(notificationJson), + NotificationType.chosenPlanTomorrowReminderToClientNotification => ChosenPlanTomorrowReminderToClientNotification.fromRest(notificationJson), + NotificationType.diaryReminderNotification => DiaryReminderNotification.fromRest(notificationJson), + NotificationType.consultantLeftComment => ConsultantLeftCommentNotification.fromRest(notificationJson), + NotificationType.personalizedDietIsDefined => PersonalizedDietIsDefinedNotification.fromRest(notificationJson), + NotificationType.chosenPlanFinishedToClient => ChosenPlanFinishedToClientNotification.fromRest(notificationJson), + NotificationType.planSubscriptionCanceledToCustomer => PlanSubscriptionCanceledToAuthorNotification.fromRest(notificationJson), + NotificationType.chosenPlanStartedToConsultantNotification => ChosenPlanStartedToConsultantNotification.fromRest(notificationJson), + NotificationType.chosenPlanTomorrowReminderToConsultantNotification => + ChosenPlanTomorrowReminderToConsultantNotification.fromRest(notificationJson), + NotificationType.planSubscriptionPaidToAuthor => PlanSubscriptionPaidToAuthorNotification.fromRest(notificationJson), + NotificationType.chosenPlanFinishedToConsultant => ChosenPlanFinishedToConsultantNotification.fromRest(notificationJson), + NotificationType.unknown => UnknownNotification(type: type), + }; + }).toList(); + return notifications; + } + return null; + } catch (e) { + rethrow; + } + } +} -- 2.49.1 From 272637078a5b894f5cd940cf93bc581842cc7900 Mon Sep 17 00:00:00 2001 From: Username1548 Date: Wed, 21 May 2025 21:45:22 +0700 Subject: [PATCH 2/4] feat(notifications): added json parsing for notifications and tested impplementation of notifications layoute --- lib/core/app_router.dart | 10 +- lib/core/locator_service.dart | 6 +- .../feed/models/plan_pagination_model.dart | 2 - .../models/notification_model.dart | 146 ++++++++---------- .../notifications_cubit.dart | 89 ++++++++++- .../widgets/notification_widget.dart | 15 +- .../service/notifications_service.dart | 49 +++--- lib/main.dart | 2 +- 8 files changed, 203 insertions(+), 116 deletions(-) diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart index 1c7210ac..ecd35ce9 100644 --- a/lib/core/app_router.dart +++ b/lib/core/app_router.dart @@ -103,7 +103,15 @@ class AppRouter { return const NoTransitionPage(child: ProfileScreen()); }, ), - GoRoute(path: '/feed-screen', name: 'feedScreen', pageBuilder: (context, state) => const NoTransitionPage(child: FeedScreen())), + GoRoute( + path: '/feed-screen', + name: 'feedScreen', + pageBuilder: (context, state) => const NoTransitionPage(child: FeedScreen()), + redirect: (context, state) async { + await sl().initialize(); + return null; + }, + ), GoRoute(path: '/diary-screen', name: 'diaryScreen', pageBuilder: (context, state) => const NoTransitionPage(child: DiaryScreen())), GoRoute( path: ConsultationsScreen.path, diff --git a/lib/core/locator_service.dart b/lib/core/locator_service.dart index 15bb5270..57f922e5 100644 --- a/lib/core/locator_service.dart +++ b/lib/core/locator_service.dart @@ -64,6 +64,8 @@ import 'package:eatvio_app/features/my_finance/services/cards_service.dart'; import 'package:eatvio_app/features/my_plans/presentation/cubit/my_plans_cubit.dart'; import 'package:eatvio_app/features/my_plans/repositories/my_diets_repository.dart'; import 'package:eatvio_app/features/my_plans/services/my_plans_service.dart'; +import 'package:eatvio_app/features/notifications/repository/notifications_repository.dart'; +import 'package:eatvio_app/features/notifications/service/notifications_service.dart'; import 'package:eatvio_app/features/order_info/cubit/order_info_cubit.dart'; import 'package:eatvio_app/features/plans/repositories/plans_repository.dart'; import 'package:eatvio_app/features/plans/services/plans_service.dart'; @@ -97,7 +99,7 @@ Future init() async { // Cubit singletons sl.registerLazySingleton(() => WsManager(wsService: sl())); sl.registerLazySingleton(() => NewMessagesCubit()); - sl.registerLazySingleton(() => NotificationsCubit(wsManager: sl())); + sl.registerLazySingleton(() => NotificationsCubit(wsManager: sl(), notificationsRepository: sl())); sl.registerLazySingleton(() => ProfileAvatarCubit(profileRepository: sl())); sl.registerLazySingleton(() => ChatsCubit(chatRepository: sl(), wsManager: sl(), profileRepository: sl())); @@ -161,6 +163,7 @@ Future init() async { sl.registerLazySingleton(() => ConsultationsRepository(consultationService: sl())); sl.registerLazySingleton(() => CancelPlanRepository(cancelService: sl())); sl.registerLazySingleton(() => CreatePlanRepository(createPlanService: sl())); + sl.registerLazySingleton(() => NotificationsRepository(notificationsService: sl())); // Services sl.registerLazySingleton(() => ChatService(sl())); @@ -188,6 +191,7 @@ Future init() async { sl.registerLazySingleton(() => ConsultationService(sl())); sl.registerLazySingleton(() => CancelPlanService(sl())); sl.registerLazySingleton(() => CreatePlanService(sl())); + sl.registerLazySingleton(() => NotificationsService(sl())); // External sl.registerLazySingleton( diff --git a/lib/features/feed/models/plan_pagination_model.dart b/lib/features/feed/models/plan_pagination_model.dart index 41c15983..e87d32c4 100644 --- a/lib/features/feed/models/plan_pagination_model.dart +++ b/lib/features/feed/models/plan_pagination_model.dart @@ -1,5 +1,3 @@ -import 'dart:ffi'; - import 'package:eatvio_app/core/models/pagination_model.dart'; import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; import 'package:eatvio_app/features/my_finance/models/card_model.dart'; diff --git a/lib/features/notifications/models/notification_model.dart b/lib/features/notifications/models/notification_model.dart index 47942287..f0b7b8b3 100644 --- a/lib/features/notifications/models/notification_model.dart +++ b/lib/features/notifications/models/notification_model.dart @@ -1,5 +1,3 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - abstract class BaseNotificationModel { abstract final NotificationType type; } @@ -18,12 +16,13 @@ class PlanSubscriptionStartedToAuthorNotification implements BaseNotificationMod required this.chatId, }); - factory PlanSubscriptionStartedToAuthorNotification.fromRest(Map json) { - throw UnimplementedError(); - } - - factory PlanSubscriptionStartedToAuthorNotification.fromWebSocket(Map json) { - throw UnimplementedError(); + factory PlanSubscriptionStartedToAuthorNotification.fromJson(Map json, NotificationType type) { + return PlanSubscriptionStartedToAuthorNotification( + type: type, + planName: json['plan_name'], + customerName: json['user_name'], + chatId: json['chat_id'], + ); } } @@ -39,12 +38,12 @@ class PlanSubscriptionCanceledToAuthorNotification implements BaseNotificationMo required this.customerName, }); - factory PlanSubscriptionCanceledToAuthorNotification.fromRest(Map json) { - throw UnimplementedError(); - } - - factory PlanSubscriptionCanceledToAuthorNotification.fromWebSocket(Map json) { - throw UnimplementedError(); + factory PlanSubscriptionCanceledToAuthorNotification.fromJson(Map json, NotificationType type) { + return PlanSubscriptionCanceledToAuthorNotification( + type: type, + planName: json['plan_name'], + customerName: json['user_name'], + ); } } @@ -60,12 +59,12 @@ class ChosenPlanStartedToClientNotification implements BaseNotificationModel { required this.planId, }); - factory ChosenPlanStartedToClientNotification.fromRest(Map json) { - throw UnimplementedError(); - } - - factory ChosenPlanStartedToClientNotification.fromWebSocket(Map json) { - throw UnimplementedError(); + factory ChosenPlanStartedToClientNotification.fromJson(Map json, NotificationType type) { + return ChosenPlanStartedToClientNotification( + type: type, + planName: json['plan_name'], + planId: json['plan_id'], + ); } } @@ -79,12 +78,11 @@ class ChosenPlanTomorrowReminderToClientNotification implements BaseNotification required this.planName, }); - factory ChosenPlanTomorrowReminderToClientNotification.fromRest(Map json) { - throw UnimplementedError(); - } - - factory ChosenPlanTomorrowReminderToClientNotification.fromWebSocket(Map json) { - throw UnimplementedError(); + factory ChosenPlanTomorrowReminderToClientNotification.fromJson(Map json, NotificationType type) { + return ChosenPlanTomorrowReminderToClientNotification( + type: type, + planName: json['plan_name'], + ); } } @@ -94,12 +92,10 @@ class DiaryReminderNotification implements BaseNotificationModel { DiaryReminderNotification({required this.type}); - factory DiaryReminderNotification.fromRest(Map json) { - throw UnimplementedError(); - } - - factory DiaryReminderNotification.fromWebSocket(Map json) { - throw UnimplementedError(); + factory DiaryReminderNotification.fromJson(Map json, NotificationType type) { + return DiaryReminderNotification( + type: type, + ); } } @@ -113,12 +109,8 @@ class ConsultantLeftCommentNotification implements BaseNotificationModel { required this.date, }); - factory ConsultantLeftCommentNotification.fromRest(Map json) { - throw UnimplementedError(); - } - - factory ConsultantLeftCommentNotification.fromWebSocket(Map json) { - throw UnimplementedError(); + factory ConsultantLeftCommentNotification.fromJson(Map json, NotificationType type) { + return ConsultantLeftCommentNotification(type: type, date: json['diary_date']); } } @@ -128,12 +120,10 @@ class PersonalizedDietIsDefinedNotification implements BaseNotificationModel { PersonalizedDietIsDefinedNotification({required this.type}); - factory PersonalizedDietIsDefinedNotification.fromRest(Map json) { - throw UnimplementedError(); - } - - factory PersonalizedDietIsDefinedNotification.fromWebSocket(Map json) { - throw UnimplementedError(); + factory PersonalizedDietIsDefinedNotification.fromJson(Map json, NotificationType type) { + return PersonalizedDietIsDefinedNotification( + type: type, + ); } } @@ -146,12 +136,8 @@ class ChosenPlanFinishedToClientNotification implements BaseNotificationModel { required this.planName, }); - factory ChosenPlanFinishedToClientNotification.fromRest(Map json) { - throw UnimplementedError(); - } - - factory ChosenPlanFinishedToClientNotification.fromWebSocket(Map json) { - throw UnimplementedError(); + factory ChosenPlanFinishedToClientNotification.fromJson(Map json, NotificationType type) { + return ChosenPlanFinishedToClientNotification(type: type, planName: json['plan_name']); } } @@ -164,12 +150,11 @@ class PlanSubscriptionCanceledToCustomerNotification implements BaseNotification required this.planName, }); - factory PlanSubscriptionCanceledToCustomerNotification.fromRest(Map json) { - throw UnimplementedError(); - } - - factory PlanSubscriptionCanceledToCustomerNotification.fromWebSocket(Map json) { - throw UnimplementedError(); + factory PlanSubscriptionCanceledToCustomerNotification.fromJson(Map json, NotificationType type) { + return PlanSubscriptionCanceledToCustomerNotification( + type: type, + planName: json['plan_name'], + ); } } @@ -184,12 +169,12 @@ class ChosenPlanStartedToConsultantNotification implements BaseNotificationModel required this.customerName, }); - factory ChosenPlanStartedToConsultantNotification.fromRest(Map json) { - throw UnimplementedError(); - } - - factory ChosenPlanStartedToConsultantNotification.fromWebSocket(Map json) { - throw UnimplementedError(); + factory ChosenPlanStartedToConsultantNotification.fromJson(Map json, NotificationType type) { + return ChosenPlanStartedToConsultantNotification( + type: type, + planName: json['plan_name'], + customerName: json['client_name'], + ); } } @@ -204,12 +189,12 @@ class ChosenPlanTomorrowReminderToConsultantNotification implements BaseNotifica required this.customerName, }); - factory ChosenPlanTomorrowReminderToConsultantNotification.fromRest(Map json) { - throw UnimplementedError(); - } - - factory ChosenPlanTomorrowReminderToConsultantNotification.fromWebSocket(Map json) { - throw UnimplementedError(); + factory ChosenPlanTomorrowReminderToConsultantNotification.fromJson(Map json, NotificationType type) { + return ChosenPlanTomorrowReminderToConsultantNotification( + type: type, + planName: json['plan_name'], + customerName: json['client_name'], + ); } } @@ -224,12 +209,12 @@ class PlanSubscriptionPaidToAuthorNotification implements BaseNotificationModel required this.customerName, }); - factory PlanSubscriptionPaidToAuthorNotification.fromRest(Map json) { - throw UnimplementedError(); - } - - factory PlanSubscriptionPaidToAuthorNotification.fromWebSocket(Map json) { - throw UnimplementedError(); + factory PlanSubscriptionPaidToAuthorNotification.fromJson(Map json, NotificationType type) { + return PlanSubscriptionPaidToAuthorNotification( + type: type, + planName: json['plan_name'], + customerName: json['user_name'], + ); } } @@ -246,12 +231,13 @@ class ChosenPlanFinishedToConsultantNotification implements BaseNotificationMode required this.planId, }); - factory ChosenPlanFinishedToConsultantNotification.fromRest(Map json) { - throw UnimplementedError(); - } - - factory ChosenPlanFinishedToConsultantNotification.fromWebSocket(Map json) { - throw UnimplementedError(); + factory ChosenPlanFinishedToConsultantNotification.fromJson(Map json, NotificationType type) { + return ChosenPlanFinishedToConsultantNotification( + type: type, + planName: json['plan_name'], + customerName: json['client_name'], + planId: json['plan_id'], + ); } } diff --git a/lib/features/notifications/presentation/notifications_cubit/notifications_cubit.dart b/lib/features/notifications/presentation/notifications_cubit/notifications_cubit.dart index bded5181..6bb8c2c0 100644 --- a/lib/features/notifications/presentation/notifications_cubit/notifications_cubit.dart +++ b/lib/features/notifications/presentation/notifications_cubit/notifications_cubit.dart @@ -1,22 +1,55 @@ +import 'dart:convert'; +import 'dart:developer'; + import 'package:eatvio_app/core/websockets/ws_manager.dart'; import 'package:eatvio_app/features/notifications/models/notification_model.dart'; import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_state.dart'; +import 'package:eatvio_app/features/notifications/repository/notifications_repository.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class NotificationsCubit extends Cubit { final List _notifications = []; final WsManager wsManager; - - NotificationsCubit({required this.wsManager}) : super(const NotificationsState.empty()) { + final NotificationsRepository notificationsRepository; + NotificationsCubit({ + required this.wsManager, + required this.notificationsRepository, + }) : super(const NotificationsState.empty()) { wsManager.newNotificationsStream?.listen((event) { _handleNotification(event.data); }); } + BaseNotificationModel _parseNotification(Map notificationJson, NotificationType type) { + try { + return switch (type) { + NotificationType.planSubscriptionStartedToAuthor => PlanSubscriptionStartedToAuthorNotification.fromJson(notificationJson, type), + NotificationType.planSubscriptionCanceledToAuthor => PlanSubscriptionCanceledToAuthorNotification.fromJson(notificationJson, type), + NotificationType.chosenPlanStartedToClientNotification => ChosenPlanStartedToClientNotification.fromJson(notificationJson, type), + NotificationType.chosenPlanTomorrowReminderToClientNotification => + ChosenPlanTomorrowReminderToClientNotification.fromJson(notificationJson, type), + NotificationType.diaryReminderNotification => DiaryReminderNotification.fromJson(notificationJson, type), + NotificationType.consultantLeftComment => ConsultantLeftCommentNotification.fromJson(notificationJson, type), + NotificationType.personalizedDietIsDefined => PersonalizedDietIsDefinedNotification.fromJson(notificationJson, type), + NotificationType.chosenPlanFinishedToClient => ChosenPlanFinishedToClientNotification.fromJson(notificationJson, type), + NotificationType.planSubscriptionCanceledToCustomer => PlanSubscriptionCanceledToAuthorNotification.fromJson(notificationJson, type), + NotificationType.chosenPlanStartedToConsultantNotification => ChosenPlanStartedToConsultantNotification.fromJson(notificationJson, type), + NotificationType.chosenPlanTomorrowReminderToConsultantNotification => + ChosenPlanTomorrowReminderToConsultantNotification.fromJson(notificationJson, type), + NotificationType.planSubscriptionPaidToAuthor => PlanSubscriptionPaidToAuthorNotification.fromJson(notificationJson, type), + NotificationType.chosenPlanFinishedToConsultant => ChosenPlanFinishedToConsultantNotification.fromJson(notificationJson, type), + NotificationType.unknown => UnknownNotification(type: type), + }; + } catch (e) { + return UnknownNotification(type: NotificationType.unknown); + } + } void _handleNotification(String notificationData) { - // final data = jsonDecode(notificationData) as Map; - // final notification = NotificationModel.fromJson(data); - // _notifications.add(notification); + final json = jsonDecode(notificationData) as Map; + final stringifiedType = json['notification_class_name']; + final type = NotificationType.getTypeFromString(stringifiedType); + final notification = _parseNotification(json, type); + _notifications.insert(0, notification); emit( NotificationsState.loaded( notifications: _notifications, @@ -24,4 +57,50 @@ class NotificationsCubit extends Cubit { ), ); } + + Future getNotifications() async { + try { + final notifications = await notificationsRepository.getNotifications(page: 1); + _notifications.clear(); + _notifications.addAll(notifications ?? []); + emit( + NotificationsState.loaded( + notifications: _notifications, + notificationsCount: _notifications.length, + ), + ); + } catch (e, st) { + log(e.toString(), stackTrace: st); + } + } } + +final qwe = { + 'event': 'IlluminateNotificationsEventsBroadcastNotificationCreated', + 'channel': 'private-message-events.36', + 'data': { + "plan_name": "Little Cheeseburger", + "plan_id": 18, + "user_name": "\u041b\u044f\u043f\u0438\u0441 \u041b\u0430\u043f\u0448\u0430", + "user_id": 53, + "user_photo_path": "https://dev2.eatvio.com/storage/profile-photos/X2aHwD6IOIFxEK8HM2d2oLuqio5BmocDLeWoH3Gs.jpg", + "chat_id": "3d14615d-1388-4833-8165-b40daa78f96a", + "chat_url": "https://dev2.eatvio.com/chat/3d14615d-1388-4833-8165-b40daa78f96a", + "notification_class_name": "App\\Notifications\\Plan\\PlanSubscriptionStartedToAuthor", + "database_type": "client-start-plan-subscription-to-author", + "id": "65cbea6d-1734-406e-b5da-79db270aed90", + "type": "client-start-plan-subscription-to-author" + } +}; +final qwe2 = { + "plan_name": "Little Cheeseburger", + "plan_id": 18, + "user_name": "\u041b\u044f\u043f\u0438\u0441 \u041b\u0430\u043f\u0448\u0430", + "user_id": 53, + "user_photo_path": "https://dev2.eatvio.com/storage/profile-photos/X2aHwD6IOIFxEK8HM2d2oLuqio5BmocDLeWoH3Gs.jpg", + "chat_url": "https://dev2.eatvio.com/chat/3d14615d-1388-4833-8165-b40daa78f96a", + "notification_class_name": "App\\Notifications\\Plan\\PlanSubscriptionCanceledToAuthor", + "database_type": "client-cancel-plan-subscription-to-author", + "id": "cf86d0e7-8ebb-4360-9b8a-b6df97e12aed", + "type": "client-cancel-plan-subscription-to-author" +}; diff --git a/lib/features/notifications/presentation/widgets/notification_widget.dart b/lib/features/notifications/presentation/widgets/notification_widget.dart index 5da2aa6c..4cff1067 100644 --- a/lib/features/notifications/presentation/widgets/notification_widget.dart +++ b/lib/features/notifications/presentation/widgets/notification_widget.dart @@ -1,3 +1,4 @@ +import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/features/notifications/models/notification_model.dart'; import 'package:flutter/material.dart'; @@ -6,9 +7,15 @@ class NotificationWidget extends StatelessWidget { final BaseNotificationModel notification; @override Widget build(BuildContext context) { - // return (notification.type) { - // NotificationType.userSubscribedNotification => const _UserSubscribedNotification(), - // }; - return Container(); + return Container( + width: MediaQuery.sizeOf(context).width, + height: 60, + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + decoration: BoxDecoration( + color: appTheme.green, + borderRadius: BorderRadius.circular(18), + ), + child: Center(child: Text(notification.type.name)), + ); } } diff --git a/lib/features/notifications/service/notifications_service.dart b/lib/features/notifications/service/notifications_service.dart index d5bd7354..b5b46e39 100644 --- a/lib/features/notifications/service/notifications_service.dart +++ b/lib/features/notifications/service/notifications_service.dart @@ -7,31 +7,36 @@ class NotificationsService { Future?> getNotifications({required int page}) async { try { - final response = await dio.get('api/profile/notifications?page=$page'); + final response = await dio.get('/api/profile/notifications?page=$page'); if (response.statusCode == 200) { - final data = response.data['data'] as List>; + final data = response.data['data'] as List; final notifications = data.map((notificationJson) { - final stringifiedType = notificationJson['data']['notification_class_name']; - final type = NotificationType.getTypeFromString(stringifiedType); - return switch (type) { - NotificationType.planSubscriptionStartedToAuthor => PlanSubscriptionStartedToAuthorNotification.fromRest(notificationJson), - NotificationType.planSubscriptionCanceledToAuthor => PlanSubscriptionCanceledToAuthorNotification.fromRest(notificationJson), - NotificationType.chosenPlanStartedToClientNotification => ChosenPlanStartedToClientNotification.fromRest(notificationJson), - NotificationType.chosenPlanTomorrowReminderToClientNotification => ChosenPlanTomorrowReminderToClientNotification.fromRest(notificationJson), - NotificationType.diaryReminderNotification => DiaryReminderNotification.fromRest(notificationJson), - NotificationType.consultantLeftComment => ConsultantLeftCommentNotification.fromRest(notificationJson), - NotificationType.personalizedDietIsDefined => PersonalizedDietIsDefinedNotification.fromRest(notificationJson), - NotificationType.chosenPlanFinishedToClient => ChosenPlanFinishedToClientNotification.fromRest(notificationJson), - NotificationType.planSubscriptionCanceledToCustomer => PlanSubscriptionCanceledToAuthorNotification.fromRest(notificationJson), - NotificationType.chosenPlanStartedToConsultantNotification => ChosenPlanStartedToConsultantNotification.fromRest(notificationJson), - NotificationType.chosenPlanTomorrowReminderToConsultantNotification => - ChosenPlanTomorrowReminderToConsultantNotification.fromRest(notificationJson), - NotificationType.planSubscriptionPaidToAuthor => PlanSubscriptionPaidToAuthorNotification.fromRest(notificationJson), - NotificationType.chosenPlanFinishedToConsultant => ChosenPlanFinishedToConsultantNotification.fromRest(notificationJson), - NotificationType.unknown => UnknownNotification(type: type), - }; + try { + final stringifiedType = notificationJson['data']['notification_class_name']; + final type = NotificationType.getTypeFromString(stringifiedType); + return switch (type) { + NotificationType.planSubscriptionStartedToAuthor => PlanSubscriptionStartedToAuthorNotification.fromJson(notificationJson['data'], type), + NotificationType.planSubscriptionCanceledToAuthor => PlanSubscriptionCanceledToAuthorNotification.fromJson(notificationJson['data'], type), + NotificationType.chosenPlanStartedToClientNotification => ChosenPlanStartedToClientNotification.fromJson(notificationJson['data'], type), + NotificationType.chosenPlanTomorrowReminderToClientNotification => + ChosenPlanTomorrowReminderToClientNotification.fromJson(notificationJson['data'], type), + NotificationType.diaryReminderNotification => DiaryReminderNotification.fromJson(notificationJson['data'], type), + NotificationType.consultantLeftComment => ConsultantLeftCommentNotification.fromJson(notificationJson['data'], type), + NotificationType.personalizedDietIsDefined => PersonalizedDietIsDefinedNotification.fromJson(notificationJson['data'], type), + NotificationType.chosenPlanFinishedToClient => ChosenPlanFinishedToClientNotification.fromJson(notificationJson['data'], type), + NotificationType.planSubscriptionCanceledToCustomer => PlanSubscriptionCanceledToAuthorNotification.fromJson(notificationJson['data'], type), + NotificationType.chosenPlanStartedToConsultantNotification => ChosenPlanStartedToConsultantNotification.fromJson(notificationJson['data'], type), + NotificationType.chosenPlanTomorrowReminderToConsultantNotification => + ChosenPlanTomorrowReminderToConsultantNotification.fromJson(notificationJson['data'], type), + NotificationType.planSubscriptionPaidToAuthor => PlanSubscriptionPaidToAuthorNotification.fromJson(notificationJson['data'], type), + NotificationType.chosenPlanFinishedToConsultant => ChosenPlanFinishedToConsultantNotification.fromJson(notificationJson['data'], type), + NotificationType.unknown => UnknownNotification(type: type), + }; + } catch (e) { + return UnknownNotification(type: NotificationType.unknown); + } }).toList(); - return notifications; + return notifications.where((notification) => notification.type != NotificationType.unknown).toList(); } return null; } catch (e) { diff --git a/lib/main.dart b/lib/main.dart index 29f7bdf8..d7d37793 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -39,7 +39,7 @@ class MyApp extends StatelessWidget { return MultiBlocProvider( providers: [ BlocProvider(create: (context) => sl()), - BlocProvider(create: (context) => sl()), + BlocProvider(create: (context) => sl()..getNotifications()), BlocProvider(create: (context) => sl()..loadFeedBlogPostList()), BlocProvider(create: (context) => sl()..getMyProfile()), BlocProvider(create: (context) => sl()..getChatRoomsWithLoading()), -- 2.49.1 From 3485e1fe5d1424450c74e7cad148a06da1a6458e Mon Sep 17 00:00:00 2001 From: Username1548 Date: Thu, 22 May 2025 22:00:01 +0700 Subject: [PATCH 3/4] added notifications layoute --- .../models/notification_model.dart | 2 +- .../notifications_cubit.dart | 30 --- .../presentation/notifications_screen.dart | 41 ++- .../widgets/notification_widget.dart | 254 +++++++++++++++++- lib/generated/intl/messages_en.dart | 40 +++ lib/generated/l10n.dart | 110 ++++++++ lib/l10n/intl_en.arb | 13 +- 7 files changed, 441 insertions(+), 49 deletions(-) diff --git a/lib/features/notifications/models/notification_model.dart b/lib/features/notifications/models/notification_model.dart index f0b7b8b3..5bcdf18c 100644 --- a/lib/features/notifications/models/notification_model.dart +++ b/lib/features/notifications/models/notification_model.dart @@ -1,4 +1,4 @@ -abstract class BaseNotificationModel { +sealed class BaseNotificationModel { abstract final NotificationType type; } diff --git a/lib/features/notifications/presentation/notifications_cubit/notifications_cubit.dart b/lib/features/notifications/presentation/notifications_cubit/notifications_cubit.dart index 6bb8c2c0..091d1f9d 100644 --- a/lib/features/notifications/presentation/notifications_cubit/notifications_cubit.dart +++ b/lib/features/notifications/presentation/notifications_cubit/notifications_cubit.dart @@ -74,33 +74,3 @@ class NotificationsCubit extends Cubit { } } } - -final qwe = { - 'event': 'IlluminateNotificationsEventsBroadcastNotificationCreated', - 'channel': 'private-message-events.36', - 'data': { - "plan_name": "Little Cheeseburger", - "plan_id": 18, - "user_name": "\u041b\u044f\u043f\u0438\u0441 \u041b\u0430\u043f\u0448\u0430", - "user_id": 53, - "user_photo_path": "https://dev2.eatvio.com/storage/profile-photos/X2aHwD6IOIFxEK8HM2d2oLuqio5BmocDLeWoH3Gs.jpg", - "chat_id": "3d14615d-1388-4833-8165-b40daa78f96a", - "chat_url": "https://dev2.eatvio.com/chat/3d14615d-1388-4833-8165-b40daa78f96a", - "notification_class_name": "App\\Notifications\\Plan\\PlanSubscriptionStartedToAuthor", - "database_type": "client-start-plan-subscription-to-author", - "id": "65cbea6d-1734-406e-b5da-79db270aed90", - "type": "client-start-plan-subscription-to-author" - } -}; -final qwe2 = { - "plan_name": "Little Cheeseburger", - "plan_id": 18, - "user_name": "\u041b\u044f\u043f\u0438\u0441 \u041b\u0430\u043f\u0448\u0430", - "user_id": 53, - "user_photo_path": "https://dev2.eatvio.com/storage/profile-photos/X2aHwD6IOIFxEK8HM2d2oLuqio5BmocDLeWoH3Gs.jpg", - "chat_url": "https://dev2.eatvio.com/chat/3d14615d-1388-4833-8165-b40daa78f96a", - "notification_class_name": "App\\Notifications\\Plan\\PlanSubscriptionCanceledToAuthor", - "database_type": "client-cancel-plan-subscription-to-author", - "id": "cf86d0e7-8ebb-4360-9b8a-b6df97e12aed", - "type": "client-cancel-plan-subscription-to-author" -}; diff --git a/lib/features/notifications/presentation/notifications_screen.dart b/lib/features/notifications/presentation/notifications_screen.dart index 73c244f1..7c3e4581 100644 --- a/lib/features/notifications/presentation/notifications_screen.dart +++ b/lib/features/notifications/presentation/notifications_screen.dart @@ -1,3 +1,5 @@ +import 'package:eatvio_app/core/theme/theme_helper.dart'; +import 'package:eatvio_app/core/widgets/custom_divider.dart'; import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_cubit.dart'; import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_state.dart'; import 'package:eatvio_app/features/notifications/presentation/widgets/notification_widget.dart'; @@ -12,19 +14,42 @@ class NotificationsScreen extends StatelessWidget { return BlocBuilder( builder: (BuildContext context, NotificationsState state) { return Scaffold( + backgroundColor: appTheme.gray100, appBar: AppBar( title: const Text('Уведомления'), ), body: state is NotificationsStateLoaded - ? CustomScrollView( - slivers: [ - SliverList.builder( - itemBuilder: (context, index) => NotificationWidget( - notification: state.notifications[index], + ? Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Container( + padding: const EdgeInsets.only(top: 16), + decoration: BoxDecoration( + color: appTheme.whiteA700, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(12), + topRight: Radius.circular(12), ), - itemCount: state.notifications.length, - ) - ], + ), + child: CustomScrollView( + slivers: [ + SliverList.separated( + separatorBuilder: (context, index) => const Padding( + padding: EdgeInsets.symmetric(vertical: 12, horizontal: 16), + child: CustomDivider(), + ), + itemBuilder: (context, index) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: NotificationWidget( + notification: state.notifications[index], + ), + ); + }, + itemCount: state.notifications.length, + ) + ], + ), + ), ) : const Placeholder(), ); diff --git a/lib/features/notifications/presentation/widgets/notification_widget.dart b/lib/features/notifications/presentation/widgets/notification_widget.dart index 4cff1067..21534b0b 100644 --- a/lib/features/notifications/presentation/widgets/notification_widget.dart +++ b/lib/features/notifications/presentation/widgets/notification_widget.dart @@ -1,21 +1,257 @@ +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_outlined_button.dart'; +import 'package:eatvio_app/features/my_finance/presentation/pages/card_settings.dart'; import 'package:eatvio_app/features/notifications/models/notification_model.dart'; +import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; class NotificationWidget extends StatelessWidget { const NotificationWidget({super.key, required this.notification}); final BaseNotificationModel notification; @override Widget build(BuildContext context) { - return Container( - width: MediaQuery.sizeOf(context).width, - height: 60, - margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - decoration: BoxDecoration( - color: appTheme.green, - borderRadius: BorderRadius.circular(18), - ), - child: Center(child: Text(notification.type.name)), + return switch (notification) { + PlanSubscriptionStartedToAuthorNotification() => _PlanSubscriptionStartedToAuthorWidget( + notification: notification as PlanSubscriptionStartedToAuthorNotification, + ), + PlanSubscriptionCanceledToAuthorNotification() => _PlanSubscriptionCanceledToAuthorWidget( + notification: notification as PlanSubscriptionCanceledToAuthorNotification, + ), + ChosenPlanStartedToClientNotification() => _ChosenPlanStartedToClientWidget( + notification: notification as ChosenPlanStartedToClientNotification, + ), + ChosenPlanTomorrowReminderToClientNotification() => _ChosenPlanTomorrowReminderToClientWidget( + notification: notification as ChosenPlanTomorrowReminderToClientNotification, + ), + DiaryReminderNotification() => _DiaryReminderWidget( + notification: notification as DiaryReminderNotification, + ), + ConsultantLeftCommentNotification() => _ConsultantLeftCommentWidget( + notification: notification as ConsultantLeftCommentNotification, + ), + PersonalizedDietIsDefinedNotification() => _PersonalizedDietIsDefinedWidget( + notification: notification as PersonalizedDietIsDefinedNotification, + ), + ChosenPlanFinishedToClientNotification() => _ChosenPlanFinishedToClientWidget( + notification: notification as ChosenPlanFinishedToClientNotification, + ), + PlanSubscriptionCanceledToCustomerNotification() => _PlanSubscriptionCanceledToCustomerWidget( + notification: notification as PlanSubscriptionCanceledToCustomerNotification, + ), + ChosenPlanStartedToConsultantNotification() => _ChosenPlanStartedToConsultantWidget( + notification: notification as ChosenPlanStartedToConsultantNotification, + ), + ChosenPlanTomorrowReminderToConsultantNotification() => _ChosenPlanTomorrowReminderToConsultantWidget( + notification: notification as ChosenPlanTomorrowReminderToConsultantNotification, + ), + PlanSubscriptionPaidToAuthorNotification() => throw _PlanSubscriptionPaidToAuthorWidget( + notification: notification as PlanSubscriptionPaidToAuthorNotification, + ), + ChosenPlanFinishedToConsultantNotification() => _ChosenPlanFinishedToConsultantWidget( + notification: notification as ChosenPlanFinishedToConsultantNotification, + ), + UnknownNotification() => const SizedBox.shrink(), + }; + } +} + +class _PlanSubscriptionStartedToAuthorWidget extends StatelessWidget { + const _PlanSubscriptionStartedToAuthorWidget({required this.notification}); + final PlanSubscriptionStartedToAuthorNotification notification; + @override + Widget build(BuildContext context) { + final s = S.of(context); + + return _BaseNotificationWidget( + notificationsText: s.userSubscribedToPlan(notification.customerName, notification.planName), + ); + } +} + +class _PlanSubscriptionCanceledToAuthorWidget extends StatelessWidget { + const _PlanSubscriptionCanceledToAuthorWidget({required this.notification}); + final PlanSubscriptionCanceledToAuthorNotification notification; + @override + Widget build(BuildContext context) { + final s = S.of(context); + + return _BaseNotificationWidget( + notificationsText: s.userCanceldThePlanSubscription(notification.customerName, notification.planName), + ); + } +} + +class _ChosenPlanStartedToClientWidget extends StatelessWidget { + const _ChosenPlanStartedToClientWidget({required this.notification}); + final ChosenPlanStartedToClientNotification notification; + @override + Widget build(BuildContext context) { + final s = S.of(context); + + return _BaseNotificationWidget( + notificationsText: s.planStarted(notification.planName), + ); + } +} + +class _ChosenPlanTomorrowReminderToClientWidget extends StatelessWidget { + const _ChosenPlanTomorrowReminderToClientWidget({required this.notification}); + final ChosenPlanTomorrowReminderToClientNotification notification; + @override + Widget build(BuildContext context) { + final s = S.of(context); + + return _BaseNotificationWidget( + notificationsText: s.tomorrowYourPlanWillStart(notification.planName), + ); + } +} + +class _DiaryReminderWidget extends StatelessWidget { + const _DiaryReminderWidget({required this.notification}); + final DiaryReminderNotification notification; + @override + Widget build(BuildContext context) { + final s = S.of(context); + return _BaseNotificationWidget( + notificationsText: s.fillYasterday, + ); + } +} + +class _ConsultantLeftCommentWidget extends StatelessWidget { + const _ConsultantLeftCommentWidget({required this.notification}); + final ConsultantLeftCommentNotification notification; + @override + Widget build(BuildContext context) { + final s = S.of(context); + + return _BaseNotificationWidget( + notificationsText: s.consultantLeftComment(notification.date), + ); + } +} + +class _PersonalizedDietIsDefinedWidget extends StatelessWidget { + const _PersonalizedDietIsDefinedWidget({required this.notification}); + final PersonalizedDietIsDefinedNotification notification; + @override + Widget build(BuildContext context) { + return Container(); + } +} + +class _ChosenPlanFinishedToClientWidget extends StatelessWidget { + const _ChosenPlanFinishedToClientWidget({required this.notification}); + final ChosenPlanFinishedToClientNotification notification; + @override + Widget build(BuildContext context) { + final s = S.of(context); + + return _BaseNotificationWidget( + notificationsText: '${s.congratsYouHaveFinishedPlan} «${notification.planName}»', + ); + } +} + +class _PlanSubscriptionCanceledToCustomerWidget extends StatelessWidget { + const _PlanSubscriptionCanceledToCustomerWidget({required this.notification}); + final PlanSubscriptionCanceledToCustomerNotification notification; + @override + Widget build(BuildContext context) { + final s = S.of(context); + + return _BaseNotificationWidget( + notificationsText: s.youCanceledSubscriptionToPlan(notification.planName), + ); + } +} + +class _ChosenPlanStartedToConsultantWidget extends StatelessWidget { + const _ChosenPlanStartedToConsultantWidget({required this.notification}); + final ChosenPlanStartedToConsultantNotification notification; + @override + Widget build(BuildContext context) { + final s = S.of(context); + + return _BaseNotificationWidget( + notificationsText: s.planWithUserStarted(notification.planName, notification.customerName), + ); + } +} + +class _ChosenPlanTomorrowReminderToConsultantWidget extends StatelessWidget { + const _ChosenPlanTomorrowReminderToConsultantWidget({required this.notification}); + final ChosenPlanTomorrowReminderToConsultantNotification notification; + @override + Widget build(BuildContext context) { + final s = S.of(context); + + return _BaseNotificationWidget( + notificationsText: s.tommorowPlanWithUserWillStart(notification.planName, notification.customerName), + ); + } +} + +class _PlanSubscriptionPaidToAuthorWidget extends StatelessWidget { + const _PlanSubscriptionPaidToAuthorWidget({required this.notification}); + final PlanSubscriptionPaidToAuthorNotification notification; + @override + Widget build(BuildContext context) { + final s = S.of(context); + + return _BaseNotificationWidget( + notificationsText: s.userPaidThePlan(notification.customerName, notification.planName), + ); + } +} + +class _ChosenPlanFinishedToConsultantWidget extends StatelessWidget { + const _ChosenPlanFinishedToConsultantWidget({required this.notification}); + final ChosenPlanFinishedToConsultantNotification notification; + @override + Widget build(BuildContext context) { + final s = S.of(context); + return Container( + child: Column(children: [ + Text( + '${s.congratsYouHaveFinishedPlan} «${notification.planName}»', + style: TextStyles.textMSemibold.copyWith(color: appTheme.black), + ), + const SizedBox(height: 12), + Text( + s.userConfirmedPlanCompletionWithoutSum, + style: TextStyles.textSMedium.copyWith(color: appTheme.black), + ), + const SizedBox(height: 12), + CustomOutlinedButton( + text: s.collectCharge, + buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), + buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.green)), + onPressed: () { + GoRouter.of(context).pushNamed(CardSettingsScreen.name, extra: notification.planId); + }, + ), + const SizedBox(height: 12), + ]), + ); + } +} + +// CardSettingsScreen +class _BaseNotificationWidget extends StatelessWidget { + const _BaseNotificationWidget({required this.notificationsText}); + final String notificationsText; + @override + Widget build(BuildContext context) { + return SizedBox( + width: MediaQuery.sizeOf(context).width * 0.6, + child: Text( + notificationsText, + style: TextStyles.textMMedium.copyWith(color: appTheme.black), + ), ); } } diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index dc980241..5b68cf0c 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -20,6 +20,30 @@ typedef String MessageIfAbsent(String messageStr, List args); class MessageLookup extends MessageLookupByLibrary { String get localeName => 'en'; + static String m0(date) => "Консультант прокомментировал день ${date}"; + + static String m1(planName) => "План «${planName}» начался"; + + static String m2(planName, name) => + "План «${planName}» с пользователем ${name} начался"; + + static String m3(planName, name) => + "Завтра начинается план «${planName}» с пользователем ${name}"; + + static String m4(planName) => + "Завтра начнется прохождение плана «${planName}»"; + + static String m5(name, planName) => + "Пользователь ${name} отменил подписку на план «${planName}»"; + + static String m6(name, planName) => + "Пользователь ${name} оплатил план «${planName}»"; + + static String m7(name, planName) => + "Пользователь ${name} подписался на план «${planName}»"; + + static String m8(planName) => "Вы отменили подписку на план «${planName}»"; + final messages = _notInlinedMessages(_notInlinedMessages); static Map _notInlinedMessages(_) => { "Moscow": MessageLookupByLibrary.simpleMessage("Москва"), @@ -153,6 +177,7 @@ class MessageLookup extends MessageLookupByLibrary { "congratsYouHaveFinishedPlan": MessageLookupByLibrary.simpleMessage( "Поздравляем, Вы закончили план", ), + "consultantLeftComment": m0, "consultations_count": MessageLookupByLibrary.simpleMessage( "Кол-во проведенных консулдьтаций", ), @@ -301,6 +326,9 @@ class MessageLookup extends MessageLookupByLibrary { "Ваши любимые/нелюбимые продукты питания?", ), "female": MessageLookupByLibrary.simpleMessage("Женский"), + "fillYasterday": MessageLookupByLibrary.simpleMessage( + "Заполните вчерашний день", + ), "fill_in_information": MessageLookupByLibrary.simpleMessage( "Заполните информацию:", ), @@ -504,9 +532,11 @@ class MessageLookup extends MessageLookupByLibrary { "Физическая форма", ), "place_of_living": MessageLookupByLibrary.simpleMessage("Место проживания"), + "planStarted": m1, "planWillBeConfirmedAfterThreeDays": MessageLookupByLibrary.simpleMessage( "Если вы не подтвердите, то план через три дня подтвердится автоматически", ), + "planWithUserStarted": m2, "plan_author": MessageLookupByLibrary.simpleMessage("Автор плана"), "plan_desc": MessageLookupByLibrary.simpleMessage("Описание плана"), "plan_desc_example": MessageLookupByLibrary.simpleMessage( @@ -640,6 +670,8 @@ class MessageLookup extends MessageLookupByLibrary { "toOffer": MessageLookupByLibrary.simpleMessage("Предложить"), "to_draft": MessageLookupByLibrary.simpleMessage("В черновик"), "to_order": MessageLookupByLibrary.simpleMessage("Заказать"), + "tommorowPlanWithUserWillStart": m3, + "tomorrowYourPlanWillStart": m4, "try_again": MessageLookupByLibrary.simpleMessage("Попробовать снова"), "try_again_2": MessageLookupByLibrary.simpleMessage("Попробуйте ещё раз"), "typeAmoutOfReturn": MessageLookupByLibrary.simpleMessage( @@ -664,9 +696,16 @@ class MessageLookup extends MessageLookupByLibrary { "Верхние показатели давления:", ), "upper": MessageLookupByLibrary.simpleMessage("Верхнее"), + "userCanceldThePlanSubscription": m5, "userConfirmedPlanCompletion": MessageLookupByLibrary.simpleMessage( "Пользователь подтвердил прохождения плана. В ближайшее время Вы получите", ), + "userConfirmedPlanCompletionWithoutSum": + MessageLookupByLibrary.simpleMessage( + "Пользователь подтвердил прохождения плана.", + ), + "userPaidThePlan": m6, + "userSubscribedToPlan": m7, "user_authorization": MessageLookupByLibrary.simpleMessage( "Аутентификация", ), @@ -716,6 +755,7 @@ class MessageLookup extends MessageLookupByLibrary { "youCanComplain": MessageLookupByLibrary.simpleMessage( "Если вас что-то не устроило, вы можете подать жалобу", ), + "youCanceledSubscriptionToPlan": m8, "youShureYouWantToCancelOrder": MessageLookupByLibrary.simpleMessage( "Вы уверены, что хотите отменить заказ?", ), diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 8da944a3..1e319c1d 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -3095,6 +3095,16 @@ class S { ); } + /// `Пользователь подтвердил прохождения плана.` + String get userConfirmedPlanCompletionWithoutSum { + return Intl.message( + 'Пользователь подтвердил прохождения плана.', + name: 'userConfirmedPlanCompletionWithoutSum', + desc: '', + args: [], + ); + } + /// `Получить выплату` String get collectCharge { return Intl.message( @@ -3224,6 +3234,106 @@ class S { args: [], ); } + + /// `Пользователь {name} оплатил план «{planName}»` + String userPaidThePlan(Object name, Object planName) { + return Intl.message( + 'Пользователь $name оплатил план «$planName»', + name: 'userPaidThePlan', + desc: '', + args: [name, planName], + ); + } + + /// `Завтра начинается план «{planName}» с пользователем {name}` + String tommorowPlanWithUserWillStart(Object planName, Object name) { + return Intl.message( + 'Завтра начинается план «$planName» с пользователем $name', + name: 'tommorowPlanWithUserWillStart', + desc: '', + args: [planName, name], + ); + } + + /// `План «{planName}» с пользователем {name} начался` + String planWithUserStarted(Object planName, Object name) { + return Intl.message( + 'План «$planName» с пользователем $name начался', + name: 'planWithUserStarted', + desc: '', + args: [planName, name], + ); + } + + /// `Вы отменили подписку на план «{planName}»` + String youCanceledSubscriptionToPlan(Object planName) { + return Intl.message( + 'Вы отменили подписку на план «$planName»', + name: 'youCanceledSubscriptionToPlan', + desc: '', + args: [planName], + ); + } + + /// `Консультант прокомментировал день {date}` + String consultantLeftComment(Object date) { + return Intl.message( + 'Консультант прокомментировал день $date', + name: 'consultantLeftComment', + desc: '', + args: [date], + ); + } + + /// `Заполните вчерашний день` + String get fillYasterday { + return Intl.message( + 'Заполните вчерашний день', + name: 'fillYasterday', + desc: '', + args: [], + ); + } + + /// `Завтра начнется прохождение плана «{planName}»` + String tomorrowYourPlanWillStart(Object planName) { + return Intl.message( + 'Завтра начнется прохождение плана «$planName»', + name: 'tomorrowYourPlanWillStart', + desc: '', + args: [planName], + ); + } + + /// `План «{planName}» начался` + String planStarted(Object planName) { + return Intl.message( + 'План «$planName» начался', + name: 'planStarted', + desc: '', + args: [planName], + ); + } + + /// `Пользователь {name} отменил подписку на план «{planName}»` + String userCanceldThePlanSubscription(Object name, Object planName) { + return Intl.message( + 'Пользователь $name отменил подписку на план «$planName»', + name: 'userCanceldThePlanSubscription', + desc: '', + args: [name, planName], + ); + } + + /// `Пользователь {name} подписался на план «{planName}»` + String userSubscribedToPlan(Object name, Object planName) { + return Intl.message( + 'Пользователь $name подписался на план «$planName»', + name: 'userSubscribedToPlan', + desc: '', + args: [name, planName], + ); + } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index d6038cb7..08de0607 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -373,6 +373,7 @@ "mealComment": "Комментарий к приему пищи", "congratsYouHaveFinishedPlan": "Поздравляем, Вы закончили план", "userConfirmedPlanCompletion": "Пользователь подтвердил прохождения плана. В ближайшее время Вы получите", + "userConfirmedPlanCompletionWithoutSum": "Пользователь подтвердил прохождения плана.", "collectCharge": "Получить выплату", "chargeApproved": "Выплата одобрена", "moneySentToCard": "Средства отправлены на карту", @@ -385,6 +386,16 @@ "pleaseConfirmPlan": "Просим вас подтвердить прохождение плана", "planWillBeConfirmedAfterThreeDays": "Если вы не подтвердите, то план через три дня подтвердится автоматически", "youCanComplain": "Если вас что-то не устроило, вы можете подать жалобу", - "leaveComplainRequest": "Подать жалобу" + "leaveComplainRequest": "Подать жалобу", + "userPaidThePlan": "Пользователь {name} оплатил план «{planName}»", + "tommorowPlanWithUserWillStart": "Завтра начинается план «{planName}» с пользователем {name}", + "planWithUserStarted": "План «{planName}» с пользователем {name} начался", + "youCanceledSubscriptionToPlan": "Вы отменили подписку на план «{planName}»", + "consultantLeftComment": "Консультант прокомментировал день {date}", + "fillYasterday": "Заполните вчерашний день", + "tomorrowYourPlanWillStart": "Завтра начнется прохождение плана «{planName}»", + "planStarted": "План «{planName}» начался", + "userCanceldThePlanSubscription": "Пользователь {name} отменил подписку на план «{planName}»", + "userSubscribedToPlan": "Пользователь {name} подписался на план «{planName}»" } -- 2.49.1 From b4f00a9868fe36e30c295c53020563a4b23f6100 Mon Sep 17 00:00:00 2001 From: Username1548 Date: Fri, 23 May 2025 11:58:18 +0700 Subject: [PATCH 4/4] fixed analyzer issue --- .../widgets/notification_widget.dart | 44 +++++++++---------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/lib/features/notifications/presentation/widgets/notification_widget.dart b/lib/features/notifications/presentation/widgets/notification_widget.dart index 21534b0b..f4d2b22b 100644 --- a/lib/features/notifications/presentation/widgets/notification_widget.dart +++ b/lib/features/notifications/presentation/widgets/notification_widget.dart @@ -214,29 +214,27 @@ class _ChosenPlanFinishedToConsultantWidget extends StatelessWidget { @override Widget build(BuildContext context) { final s = S.of(context); - return Container( - child: Column(children: [ - Text( - '${s.congratsYouHaveFinishedPlan} «${notification.planName}»', - style: TextStyles.textMSemibold.copyWith(color: appTheme.black), - ), - const SizedBox(height: 12), - Text( - s.userConfirmedPlanCompletionWithoutSum, - style: TextStyles.textSMedium.copyWith(color: appTheme.black), - ), - const SizedBox(height: 12), - CustomOutlinedButton( - text: s.collectCharge, - buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), - buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.green)), - onPressed: () { - GoRouter.of(context).pushNamed(CardSettingsScreen.name, extra: notification.planId); - }, - ), - const SizedBox(height: 12), - ]), - ); + return Column(children: [ + Text( + '${s.congratsYouHaveFinishedPlan} «${notification.planName}»', + style: TextStyles.textMSemibold.copyWith(color: appTheme.black), + ), + const SizedBox(height: 12), + Text( + s.userConfirmedPlanCompletionWithoutSum, + style: TextStyles.textSMedium.copyWith(color: appTheme.black), + ), + const SizedBox(height: 12), + CustomOutlinedButton( + text: s.collectCharge, + buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), + buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.green)), + onPressed: () { + GoRouter.of(context).pushNamed(CardSettingsScreen.name, extra: notification.planId); + }, + ), + const SizedBox(height: 12), + ]); } } -- 2.49.1