From c1e2b1c312ef197c2344d3e76df64e14a66f10df Mon Sep 17 00:00:00 2001 From: Username1548 Date: Tue, 20 May 2025 22:21:10 +0700 Subject: [PATCH] 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; + } + } +}