import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../services/notification_service.dart'; /// 未读消息数(我的页角标):登录后加载,标记已读后同步 final unreadCountProvider = StateNotifierProvider((_) => UnreadCountNotifier()); class UnreadCountNotifier extends StateNotifier { UnreadCountNotifier() : super(0); Future refresh() async { try { state = await NotificationService.fetchUnreadCount(); } catch (_) { // 未登录/网络异常:保持原值 } } /// 本地 decrement(标记单条已读后角标即时 -1,后台再 refresh 校准) void decrement([int count = 1]) { state = state > count ? state - count : 0; } void clear() => state = 0; } /// 通知列表状态:type 筛选 + 触底分页 class NotificationsState { const NotificationsState({ this.items = const [], this.total = 0, this.type, this.isLoading = false, this.loadFailed = false, this.loadingMore = false, this.markingAll = false, }); final List items; final int total; /// null = 全部 final int? type; final bool isLoading; final bool loadFailed; final bool loadingMore; final bool markingAll; bool get hasMore => items.length < total; int get unreadCount => items.where((e) => !e.isRead).length; NotificationsState copyWith({ List? items, int? total, int? Function()? type, bool? isLoading, bool? loadFailed, bool? loadingMore, bool? markingAll, }) => NotificationsState( items: items ?? this.items, total: total ?? this.total, type: type != null ? type() : this.type, isLoading: isLoading ?? this.isLoading, loadFailed: loadFailed ?? this.loadFailed, loadingMore: loadingMore ?? this.loadingMore, markingAll: markingAll ?? this.markingAll, ); } class NotificationsNotifier extends StateNotifier { NotificationsNotifier(this._ref) : super(const NotificationsState()) { load(); } final Ref _ref; static const _pageSize = 20; Future load() async { state = state.copyWith(isLoading: true, loadFailed: false); try { final page = await NotificationService.fetchNotifications( page: 1, pageSize: _pageSize, type: state.type, ); state = state.copyWith( items: page.items, total: page.total, isLoading: false, ); } catch (_) { state = state.copyWith(isLoading: false, loadFailed: true); } } /// 切换类型筛选并重载 Future setType(int? type) async { if (type == state.type) return; state = state.copyWith(type: () => type); await load(); } /// 触底加载下一页 Future loadMore() async { if (state.loadingMore || !state.hasMore || state.isLoading) return; state = state.copyWith(loadingMore: true); try { final page = await NotificationService.fetchNotifications( page: state.items.length ~/ _pageSize + 1, pageSize: _pageSize, type: state.type, ); state = state.copyWith( items: [...state.items, ...page.items], total: page.total, loadingMore: false, ); } catch (_) { state = state.copyWith(loadingMore: false); } } /// 单条已读:乐观更新本地,失败静默(下次进入重拉) Future markOneRead(NotificationItem item) async { if (item.isRead) return; state = state.copyWith( items: [ for (final e in state.items) if (e.id == item.id) e.copyWith(isRead: true) else e, ], ); _ref.read(unreadCountProvider.notifier).decrement(); try { await NotificationService.markRead(ids: [item.id]); } catch (_) { // 静默:以服务端为准,下次进入页面会重拉 } } /// 全部已读 Future markAllRead() async { if (state.markingAll) return; state = state.copyWith(markingAll: true); try { await NotificationService.markRead(all: true); if (!mounted) return; state = state.copyWith( items: [for (final e in state.items) e.copyWith(isRead: true)], markingAll: false, ); _ref.read(unreadCountProvider.notifier).clear(); } catch (_) { if (mounted) state = state.copyWith(markingAll: false); rethrow; } } } final notificationsProvider = StateNotifierProvider.autoDispose( (ref) => NotificationsNotifier(ref), );