fix(client): 设备页每 15s 静默刷新列表,其他设备状态变化 ~15s 内可见
原设备列表打开时加载一次就不动(感觉十几分钟才同步)。加透传式轮询包装器 _DevicesAutoRefresh:在设备页期间每 15s 调 DevicesNotifier.refresh()(静默、不闪 loading、 失败保留旧列表)。配合服务端会话轮询刷 last_seen + 90s 窗口,其他设备上/下线 ~15s 内反映。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -89,6 +89,18 @@ class DevicesNotifier extends AsyncNotifier<List<Device>> {
|
||||
await future;
|
||||
}
|
||||
|
||||
/// 静默刷新(不闪 loading):重新拉列表、成功才替换。供设备页 ~15s 轮询,让其他设备的
|
||||
/// 在线/状态变化 ~15s 内可见(配合服务端会话轮询刷 last_seen)。
|
||||
Future<void> refresh() async {
|
||||
if (!ref.read(authProvider).isLoggedIn) return;
|
||||
try {
|
||||
final list = await ref.read(accountApiProvider).devices();
|
||||
state = AsyncData(list);
|
||||
} catch (_) {
|
||||
// 失败保留旧列表,不闪错。
|
||||
}
|
||||
}
|
||||
|
||||
/// 强制退出(吊销该设备会话,设备保留)后刷新列表。
|
||||
Future<void> forceLogout(String uuid) async {
|
||||
await ref.read(accountApiProvider).forceLogout(uuid);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
//
|
||||
// 文案全部经 AppText(l10n,单显)。套餐数字以 design/CLAUDE.md §7 为准。
|
||||
// App 内无任何支付表单——购买仅引导至外部渠道。
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
@@ -162,7 +164,8 @@ class DevicesScreen extends ConsumerWidget {
|
||||
]),
|
||||
);
|
||||
|
||||
return _SubScaffold(title: t.myDevices, onBack: onBack, embedded: embedded, child: content());
|
||||
return _SubScaffold(
|
||||
title: t.myDevices, onBack: onBack, embedded: embedded, child: _DevicesAutoRefresh(child: content()));
|
||||
}
|
||||
|
||||
Widget _row(BuildContext context, WidgetRef ref, PangolinScheme c, Device d, bool isLocal, bool divider) {
|
||||
@@ -493,3 +496,32 @@ class ContactScreen extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 透传式轮询包装器:在设备页期间每 15s 静默刷新设备列表(不闪 loading),
|
||||
/// 让其他设备的在线/状态变化 ~15s 内可见。原样返回 child,不改布局。
|
||||
class _DevicesAutoRefresh extends ConsumerStatefulWidget {
|
||||
const _DevicesAutoRefresh({required this.child});
|
||||
final Widget child;
|
||||
@override
|
||||
ConsumerState<_DevicesAutoRefresh> createState() => _DevicesAutoRefreshState();
|
||||
}
|
||||
|
||||
class _DevicesAutoRefreshState extends ConsumerState<_DevicesAutoRefresh> {
|
||||
Timer? _poll;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_poll = Timer.periodic(const Duration(seconds: 15), (_) {
|
||||
if (mounted) ref.read(devicesProvider.notifier).refresh();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_poll?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => widget.child;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user