From e320d54b38f7309567d7c2cf2a84334710a42235 Mon Sep 17 00:00:00 2001 From: rzuasti Date: Wed, 27 May 2026 10:43:10 -0400 Subject: [PATCH] Add mark as unread action to notifications list Adds a "Mark as unread" option to the notification popup menu (shown only for already-read items) and a swipe-right gesture, mirroring the existing "Mark as read" actions. Swipe/menu actions respect the active filter: items are removed from the list only when filtered to New or Old, and snap back when viewing All. Co-Authored-By: Claude Sonnet 4.6 --- .../lib/notifications/notification_list.dart | 65 +++++++++++++++++-- frontend/lib/utils/oott_api.dart | 5 ++ 2 files changed, 63 insertions(+), 7 deletions(-) diff --git a/frontend/lib/notifications/notification_list.dart b/frontend/lib/notifications/notification_list.dart index c36e226..2365039 100644 --- a/frontend/lib/notifications/notification_list.dart +++ b/frontend/lib/notifications/notification_list.dart @@ -47,9 +47,6 @@ class _NotificationListState extends State { return false; } await BackendAPI.instance.markNotificationAsRead(item.id); - _pagingController.value = _pagingController.value.filterItems( - (n) => n.id != item.id, - ); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Event marked as read'), @@ -57,7 +54,44 @@ class _NotificationListState extends State { showCloseIcon: true, ), ); - return true; + if (_filterChoice != 3) { + _pagingController.value = _pagingController.value.filterItems( + (n) => n.id != item.id, + ); + return true; + } + return false; + } + + Future _markAsNew( + BuildContext context, + oott_model.Notification item, + ) async { + if (item.isNew) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Notification was already marked as unread'), + behavior: SnackBarBehavior.floating, + showCloseIcon: true, + ), + ); + return false; + } + await BackendAPI.instance.markNotificationAsNew(item.id); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Event marked as unread'), + behavior: SnackBarBehavior.floating, + showCloseIcon: true, + ), + ); + if (_filterChoice != 3) { + _pagingController.value = _pagingController.value.filterItems( + (n) => n.id != item.id, + ); + return true; + } + return false; } @override @@ -116,16 +150,26 @@ class _NotificationListState extends State { Dismissible( key: UniqueKey(), confirmDismiss: (direction) => - _markAsRead(context, item), + direction == DismissDirection.startToEnd + ? _markAsNew(context, item) + : _markAsRead(context, item), background: Container( + color: Theme.of( + context, + ).colorScheme.tertiaryContainer, + alignment: Alignment.centerLeft, + padding: const EdgeInsets.only(left: 16), + child: Icon(Icons.mark_email_unread), + ), + secondaryBackground: Container( color: Theme.of( context, ).colorScheme.primaryContainer, - alignment: Alignment.center, + alignment: Alignment.centerRight, + padding: const EdgeInsets.only(right: 16), child: Icon(Icons.done), ), child: ListTile( - // tileColor: item.isNew ? Colors.amber : null, leading: Icon(item.notificationType.icon), title: Text( '${FriendlyDateFormatter().format(item.createdOn)} - ${item.title}', @@ -136,6 +180,8 @@ class _NotificationListState extends State { onSelected: (value) async { if (value == 'mark_read') { await _markAsRead(context, item); + } else if (value == 'mark_new') { + await _markAsNew(context, item); } }, itemBuilder: (context) => [ @@ -144,6 +190,11 @@ class _NotificationListState extends State { value: 'mark_read', child: Text('Mark as read'), ), + if (!item.isNew) + const PopupMenuItem( + value: 'mark_new', + child: Text('Mark as unread'), + ), ], ), onTap: () {}, diff --git a/frontend/lib/utils/oott_api.dart b/frontend/lib/utils/oott_api.dart index 4a99d92..0447166 100644 --- a/frontend/lib/utils/oott_api.dart +++ b/frontend/lib/utils/oott_api.dart @@ -71,6 +71,11 @@ class BackendAPI { await _dio.get('/notifications/$id'); } + Future markNotificationAsNew(int id) async { + print('About to call /notifications/$id/mark_as_new'); + await _dio.post('/notifications/$id/mark_as_new'); + } + Future> listNotifications(bool? isNew, int offset) async { print('About to call /notifications');