feat(client/nodes): 节点页刷新 — 下拉刷新 + 刷新按钮 + 进页刷新 + 10s 限流
- nodesProvider.refresh:10s 限流(下拉/按钮/进页多次触发只打一次远端); 不清空当前列表(后台拉、好了再换,失败保留旧数据),避免进页/下拉闪空 - nodes_page:initState 进页自动刷一次;RefreshIndicator 下拉刷新(移动端); 搜索框右侧 _RefreshButton 点击刷新(刷新中 spinner) servers 页 golden 重生成(搜索行加按钮);CI 闸 golden 未动。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,10 @@ import 'auth_provider.dart';
|
||||
|
||||
class NodesNotifier extends AsyncNotifier<List<Node>> {
|
||||
bool _disposed = false;
|
||||
DateTime? _lastFetch;
|
||||
|
||||
/// 刷新限流:距上次成功拉取不足此间隔则跳过远端(下拉/按钮/进页多次触发只打一次)。
|
||||
static const _minInterval = Duration(seconds: 10);
|
||||
|
||||
@override
|
||||
Future<List<Node>> build() async {
|
||||
@@ -24,22 +28,30 @@ class NodesNotifier extends AsyncNotifier<List<Node>> {
|
||||
final auth = ref.watch(authProvider);
|
||||
if (!auth.isLoggedIn) return const [];
|
||||
final list = await _fetchNodes(auth.accessToken!);
|
||||
_lastFetch = DateTime.now();
|
||||
unawaited(_measure(list));
|
||||
return list;
|
||||
}
|
||||
|
||||
/// 刷新节点列表(下拉/按钮/进页调用)。10s 限流;不清空当前列表(后台拉、好了再换),
|
||||
/// 避免进页/下拉时列表闪空;失败保留旧数据。
|
||||
Future<void> refresh() async {
|
||||
final auth = ref.read(authProvider);
|
||||
if (!auth.isLoggedIn) {
|
||||
state = const AsyncData([]);
|
||||
return;
|
||||
}
|
||||
state = const AsyncLoading();
|
||||
state = await AsyncValue.guard(() async {
|
||||
if (_lastFetch != null && DateTime.now().difference(_lastFetch!) < _minInterval) {
|
||||
return; // 限流:10s 内不重复请求远端
|
||||
}
|
||||
final result = await AsyncValue.guard(() async {
|
||||
final list = await _fetchNodes(auth.accessToken!);
|
||||
_lastFetch = DateTime.now();
|
||||
unawaited(_measure(list));
|
||||
return list;
|
||||
});
|
||||
if (_disposed) return;
|
||||
if (result.hasValue) state = result; // 成功才换;失败保留旧列表
|
||||
}
|
||||
|
||||
/// 后台实测各节点延迟,完成后回填(notifier 未销毁才更新)。
|
||||
|
||||
Reference in New Issue
Block a user