fix(client): 自动连接竞态真因 — 等设置加载完再判(1.0.22)

日志实证:节点就绪(~450ms)与设置加载几乎同时完成,节点回调早 1ms 读到还没加载完的
autoConnect=false → 不连。AppSettings 加 loaded 标志(_load 完成置 true,失败也置);
_maybeAutoConnect 同时监听 nodes + settings,二者都就绪(settings.loaded && 节点 uuid 非空)
才决策一次。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-29 19:08:49 +08:00
parent ff2f2f3c07
commit 28f8e5ebcd
4 changed files with 40 additions and 28 deletions
+13 -6
View File
@@ -20,17 +20,21 @@ class AppSettings {
this.autoConnect = false,
this.smartRoute = true,
this.killSwitch = true,
this.loaded = false,
});
final bool autostart;
final bool autoConnect; // 启动 app 时自动连接(opt-in,默认关)
final bool smartRoute;
final bool killSwitch;
final bool loaded; // 是否已从持久化加载完(自动连接要等它 true 再读 autoConnect,避免竞态)
AppSettings copyWith({bool? autostart, bool? autoConnect, bool? smartRoute, bool? killSwitch}) => AppSettings(
AppSettings copyWith({bool? autostart, bool? autoConnect, bool? smartRoute, bool? killSwitch, bool? loaded}) =>
AppSettings(
autostart: autostart ?? this.autostart,
autoConnect: autoConnect ?? this.autoConnect,
smartRoute: smartRoute ?? this.smartRoute,
killSwitch: killSwitch ?? this.killSwitch,
loaded: loaded ?? this.loaded,
);
}
@@ -51,22 +55,25 @@ class SettingsController extends StateNotifier<AppSettings> {
final bool _desktop = Platform.isMacOS || Platform.isWindows || Platform.isLinux;
Future<void> _load() async {
var s = const AppSettings(loaded: true); // 失败也标 loaded:true,自动连接才不会永久等
try {
final p = await SharedPreferences.getInstance();
if (!mounted) return; // 异步初始化期间已 dispose:别再写 state。
state = AppSettings(
s = AppSettings(
autostart: p.getBool(_kAutostart) ?? true,
autoConnect: p.getBool(_kAutoConnect) ?? false,
smartRoute: p.getBool(_kSmartRoute) ?? true,
killSwitch: p.getBool(_kKillSwitch) ?? true,
loaded: true,
);
logLine('Settings', 'loaded autoConnect=${state.autoConnect} autostart=${state.autostart}');
} catch (_) {
// 测试/无平台:保留默认值。
// 测试/无平台:默认值(已标 loaded)
}
if (!mounted) return;
_applyKillSwitch(state.killSwitch);
if (_desktop) await _applyAutostart(state.autostart);
state = s;
logLine('Settings', 'loaded autoConnect=${s.autoConnect} autostart=${s.autostart}');
_applyKillSwitch(s.killSwitch);
if (_desktop) await _applyAutostart(s.autostart);
}
Future<void> setAutostart(bool v) async {