From 9c1a9834663fe42ef6956484b188e8f599c272b8 Mon Sep 17 00:00:00 2001
From: LittleSheep <littlesheep.code@hotmail.com>
Date: Wed, 28 May 2025 01:52:44 +0800
Subject: [PATCH] :sparkles: Reactive notification unread count

---
 lib/pods/websocket.dart       | 15 +--------------
 lib/screens/notification.dart | 31 +++++++++++++++++++++++++++++--
 2 files changed, 30 insertions(+), 16 deletions(-)

diff --git a/lib/pods/websocket.dart b/lib/pods/websocket.dart
index f6ed332..afd8155 100644
--- a/lib/pods/websocket.dart
+++ b/lib/pods/websocket.dart
@@ -51,8 +51,8 @@ class WebSocketService {
 
   Future<void> connect(Ref ref) async {
     _ref = ref;
-
     _statusStreamController.sink.add(WebSocketState.connecting());
+
     final baseUrl = ref.watch(serverUrlProvider);
     final atk = await getFreshAtk(
       ref.watch(tokenPairProvider),
@@ -141,19 +141,6 @@ class WebSocketStateNotifier extends StateNotifier<WebSocketState> {
     state = const WebSocketState.connecting();
     try {
       final service = ref.read(websocketProvider);
-      final baseUrl = ref.watch(serverUrlProvider);
-      final atk = await getFreshAtk(
-        ref.watch(tokenPairProvider),
-        baseUrl,
-        onRefreshed: (atk, rtk) {
-          setTokenPair(ref.watch(sharedPreferencesProvider), atk, rtk);
-          ref.invalidate(tokenPairProvider);
-        },
-      );
-      if (atk == null) {
-        state = const WebSocketState.error('Unauthorized');
-        return;
-      }
       await service.connect(ref);
       state = const WebSocketState.connected();
       service.statusStream.listen((event) {
diff --git a/lib/screens/notification.dart b/lib/screens/notification.dart
index d10299b..3bab117 100644
--- a/lib/screens/notification.dart
+++ b/lib/screens/notification.dart
@@ -1,3 +1,4 @@
+import 'dart:async';
 import 'dart:math' as math;
 
 import 'package:auto_route/auto_route.dart';
@@ -7,6 +8,7 @@ import 'package:flutter/services.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:island/models/user.dart';
 import 'package:island/pods/network.dart';
+import 'package:island/pods/websocket.dart';
 import 'package:island/widgets/alert.dart';
 import 'package:island/widgets/app_scaffold.dart';
 import 'package:island/widgets/content/markdown.dart';
@@ -21,8 +23,18 @@ part 'notification.g.dart';
 @riverpod
 class NotificationUnreadCountNotifier
     extends _$NotificationUnreadCountNotifier {
+  StreamSubscription<WebSocketPacket>? _subscription;
+
   @override
   Future<int> build() async {
+    // Subscribe to websocket events when this provider is built
+    _subscribeToWebSocket();
+
+    // Dispose the subscription when this provider is disposed
+    ref.onDispose(() {
+      _subscription?.cancel();
+    });
+
     try {
       final client = ref.read(apiClientProvider);
       final response = await client.get('/notifications/count');
@@ -32,9 +44,23 @@ class NotificationUnreadCountNotifier
     }
   }
 
+  void _subscribeToWebSocket() {
+    final webSocketService = ref.read(websocketProvider);
+    _subscription = webSocketService.dataStream.listen((packet) {
+      if (packet.type == 'notifications.new') {
+        _incrementCounter();
+      }
+    });
+  }
+
+  Future<void> _incrementCounter() async {
+    final current = await future;
+    state = AsyncData(current + 1);
+  }
+
   Future<void> decrement(int count) async {
     final current = await future;
-    state = AsyncData(math.min(current - count, 0));
+    state = AsyncData(math.max(current - count, 0));
   }
 }
 
@@ -94,6 +120,7 @@ class NotificationScreen extends HookConsumerWidget {
         notifierRefreshable: notificationListNotifierProvider.notifier,
         contentBuilder:
             (data, widgetCount, endItemView) => ListView.builder(
+              padding: EdgeInsets.zero,
               itemCount: widgetCount,
               itemBuilder: (context, index) {
                 if (index == widgetCount - 1) {
@@ -142,7 +169,7 @@ class NotificationScreen extends HookConsumerWidget {
                     ],
                   ),
                   trailing:
-                      notification.viewedAt == null
+                      notification.viewedAt != null
                           ? null
                           : Container(
                             width: 12,