From 6e82f2c73fa9dc4c06aeea9f5ac7a7ab12a4bce3 Mon Sep 17 00:00:00 2001 From: amirrezaghabeli Date: Sun, 28 Sep 2025 14:28:18 +0330 Subject: [PATCH] mark single notification as read --- lib/core/config/dependencies.dart | 6 +- .../repositories/notification_repository.dart | 6 + lib/data/services/api/notification_api.dart | 14 ++- lib/data/services/notification_service.dart | 10 +- lib/view_models/notification_view_model.dart | 114 ++++++++++++++++-- .../widgets/notification_card.dart | 7 +- 6 files changed, 130 insertions(+), 27 deletions(-) diff --git a/lib/core/config/dependencies.dart b/lib/core/config/dependencies.dart index a8bc333..1b1a4d8 100644 --- a/lib/core/config/dependencies.dart +++ b/lib/core/config/dependencies.dart @@ -159,8 +159,10 @@ List get viewModels { ), ChangeNotifierProvider( create: - (context) => - NotificationViewModel(repositoryViewModel: context.read()), + (context) => NotificationViewModel( + repositoryViewModel: context.read(), + homeViewModel: context.read(), + ), ), ]; } diff --git a/lib/data/repositories/notification_repository.dart b/lib/data/repositories/notification_repository.dart index 4a0db4f..c11ea45 100644 --- a/lib/data/repositories/notification_repository.dart +++ b/lib/data/repositories/notification_repository.dart @@ -39,4 +39,10 @@ class NotificationsRepository { offset: offset, ); } + + Future> markAsRead({ + required String notificationId, + }) { + return _notificationsService.markAsRead(notificationId: notificationId); + } } diff --git a/lib/data/services/api/notification_api.dart b/lib/data/services/api/notification_api.dart index 07c4d57..b4403ea 100644 --- a/lib/data/services/api/notification_api.dart +++ b/lib/data/services/api/notification_api.dart @@ -6,14 +6,20 @@ class NotificationApi { } static const String markAllAsRead = '/api/v1/notifications/mark'; - static const String markAsRead = '/api/v1/notifications/mark-as-read'; + static const String markAsRead = '/api/v1/notifications/mark'; static const String unreadNotifications = '/api/v1/notifications?seen=false'; - static String getUnreadNotifications({required int limit, required int offset}) { + static String getUnreadNotifications({ + required int limit, + required int offset, + }) { return '/api/v1/notifications?seen=false&limit=$limit&offset=$offset'; } - static String getImportantNotifications({required int limit, required int offset}) { + static String getImportantNotifications({ + required int limit, + required int offset, + }) { return '/api/v1/notifications?priority=important&limit=$limit&offset=$offset'; } -} \ No newline at end of file +} diff --git a/lib/data/services/notification_service.dart b/lib/data/services/notification_service.dart index 14eab95..d1cde07 100644 --- a/lib/data/services/notification_service.dart +++ b/lib/data/services/notification_service.dart @@ -1,8 +1,8 @@ // notification_service.dart -import 'dart:convert'; import 'package:tm_app/core/utils/result.dart'; import 'package:tm_app/data/services/api/notification_api.dart'; import 'package:tm_app/data/services/model/notification__response/notification_response_model.dart'; + import '../../core/network/network_manager.dart'; class NotificationsService { @@ -34,13 +34,11 @@ class NotificationsService { Future> markAsRead({ required String notificationId, }) async { - final data = jsonEncode({'notification_id': notificationId}); final result = await _networkManager.makeRequest( - NotificationApi.markAsRead, - method: 'POST', + '${NotificationApi.markAsRead}/$notificationId', + method: 'GET', (json) => NotificationResponseModel.fromJson(json), - data: data, - ); + ); return result; } diff --git a/lib/view_models/notification_view_model.dart b/lib/view_models/notification_view_model.dart index 65a65ae..448fa2a 100644 --- a/lib/view_models/notification_view_model.dart +++ b/lib/view_models/notification_view_model.dart @@ -1,6 +1,5 @@ // notification_view_model.dart import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; import 'package:tm_app/core/utils/app_toast.dart'; import 'package:tm_app/core/utils/result.dart'; import 'package:tm_app/data/repositories/notification_repository.dart'; @@ -10,9 +9,13 @@ import 'package:tm_app/views/notification/strings/notification_strings.dart'; class NotificationViewModel with ChangeNotifier { final NotificationsRepository _repository; + final HomeViewModel _homeViewModel; - NotificationViewModel({required NotificationsRepository repositoryViewModel}) - : _repository = repositoryViewModel { + NotificationViewModel({ + required NotificationsRepository repositoryViewModel, + required HomeViewModel homeViewModel, + }) : _repository = repositoryViewModel, + _homeViewModel = homeViewModel { WidgetsBinding.instance.addPostFrameCallback((_) { init(); }); @@ -214,10 +217,10 @@ class NotificationViewModel with ChangeNotifier { void _updateImportantFromAll() { final all = allNotificationResponse?.data ?? []; final filtered = - (all.where((n) { + all.where((n) { final p = n.priority ?? ''; return p.toLowerCase() == 'important'; - }).toList()); + }).toList(); if (allNotificationResponse != null) { importantNotificationResponse = allNotificationResponse!.copyWith( @@ -294,6 +297,9 @@ class NotificationViewModel with ChangeNotifier { } } + // Ensure unread list is empty after marking all as read + _checkAndUpdateUnreadState(); + if (context.mounted) { AppToast.success( context, @@ -341,12 +347,100 @@ class NotificationViewModel with ChangeNotifier { } void setNotificationFalse(BuildContext context) { - final homeViewModel = context.read(); - homeViewModel.setUnreadNotificationFalse(); + _homeViewModel.setUnreadNotificationFalse(); } - void checkNotifications(BuildContext context) { - final homeViewModel = context.read(); - homeViewModel.checkUnreadNotifications(); + void _checkAndUpdateUnreadState() { + // Check if all notifications are read + final allNotifications = allNotificationResponse?.data ?? []; + final hasUnreadNotifications = allNotifications.any((n) => n.seen != true); + + if (!hasUnreadNotifications) { + // Clear unread list if all notifications are read + unreadNotificationResponse = unreadNotificationResponse?.copyWith( + data: [], + ); + } + } + + int get unreadCount { + return unreadNotificationResponse?.data?.length ?? 0; + } + + int get importantCount { + return importantNotificationResponse?.data?.length ?? 0; + } + + bool get hasUnreadNotifications { + return unreadCount > 0; + } + + bool get hasImportantNotifications { + return importantCount > 0; + } + + void checkNotifications() { + _homeViewModel.checkUnreadNotifications(); + } + + Future markAsRead({required String notificationId}) async { + final result = await _repository.markAsRead(notificationId: notificationId); + + switch (result) { + case Ok(): + // Find and update only the specific notification that matches the ID + final updatedNotifications = + allNotificationResponse?.data?.map((n) { + if (n.id == notificationId) { + return n.copyWith(seen: true); + } + return n; + }).toList(); + + if (updatedNotifications != null) { + allNotificationResponse = allNotificationResponse!.copyWith( + data: updatedNotifications, + ); + } + + // Update unread list - remove the notification that was marked as read + if (unreadNotificationResponse?.data != null) { + final updatedUnreadList = + unreadNotificationResponse!.data! + .where((n) => n.id != notificationId) + .toList(); + + unreadNotificationResponse = unreadNotificationResponse!.copyWith( + data: updatedUnreadList, + ); + } + + // Update important list - mark the notification as seen if it exists + if (importantNotificationResponse?.data != null) { + final updatedImportantList = + importantNotificationResponse!.data!.map((n) { + if (n.id == notificationId) { + return n.copyWith(seen: true); + } + return n; + }).toList(); + + importantNotificationResponse = importantNotificationResponse! + .copyWith(data: updatedImportantList); + } + + // Check if all notifications are now read + _checkAndUpdateUnreadState(); + + notifyListeners(); + checkNotifications(); + + break; + case Error(): + _errorMessage = result.error.toString(); + break; + } + + notifyListeners(); } } diff --git a/lib/views/notification/widgets/notification_card.dart b/lib/views/notification/widgets/notification_card.dart index b3d95fd..0d743f2 100644 --- a/lib/views/notification/widgets/notification_card.dart +++ b/lib/views/notification/widgets/notification_card.dart @@ -123,6 +123,7 @@ class NotificationCard extends StatelessWidget { const Spacer(), TextButton( onPressed: () { + viewModel.markAsRead(notificationId: notification.id!); showDialog( context: context, builder: (context) { @@ -131,11 +132,7 @@ class NotificationCard extends StatelessWidget { viewModel: viewModel, ); }, - ).then((value) { - if (context.mounted) { - viewModel.checkNotifications(context); - } - }); + ); }, child: Row( children: [