add
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../services/notification_service.dart';
|
||||
|
||||
/// 未读消息数(我的页角标):登录后加载,标记已读后同步
|
||||
final unreadCountProvider =
|
||||
StateNotifierProvider<UnreadCountNotifier, int>((_) => UnreadCountNotifier());
|
||||
|
||||
class UnreadCountNotifier extends StateNotifier<int> {
|
||||
UnreadCountNotifier() : super(0);
|
||||
|
||||
Future<void> 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<NotificationItem> 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<NotificationItem>? 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<NotificationsState> {
|
||||
NotificationsNotifier(this._ref) : super(const NotificationsState()) {
|
||||
load();
|
||||
}
|
||||
|
||||
final Ref _ref;
|
||||
static const _pageSize = 20;
|
||||
|
||||
Future<void> 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<void> setType(int? type) async {
|
||||
if (type == state.type) return;
|
||||
state = state.copyWith(type: () => type);
|
||||
await load();
|
||||
}
|
||||
|
||||
/// 触底加载下一页
|
||||
Future<void> 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<void> 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<void> 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<NotificationsNotifier, NotificationsState>(
|
||||
(ref) => NotificationsNotifier(ref),
|
||||
);
|
||||
Reference in New Issue
Block a user