From ada99e6c3a547cf75b2afe6c8a237510b55c8318 Mon Sep 17 00:00:00 2001 From: wangjia <809946525@qq.com> Date: Mon, 29 Jun 2026 19:58:53 +0800 Subject: [PATCH] =?UTF-8?q?fix(client):=20=E8=87=AA=E5=8A=A8=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5=E6=AD=BB=E9=94=81=E5=85=9C=E5=BA=95(=E8=BD=AE?= =?UTF-8?q?=E8=AF=A2)+=20=E9=80=90=E6=AC=A1=E9=97=A8=E6=8E=A7=E6=97=A5?= =?UTF-8?q?=E5=BF=97(1.0.27)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 复现:armed→设置已加载后无 ready。死锁:selLoaded 靠 selectedNode controller 的 loaded 标志, 而该标志完成时若选中值未变(==默认)不会发通知,若 nodeSub 已先于它触发则此后无人再调 tryGo。 修:_maybeAutoConnect 加 Timer.periodic(400ms,≤12s)兜底复查,decided 后停;并恢复每次 tryGo 打 check 日志(settingsLoaded/selLoaded/node uuid)以确认卡点。 Co-Authored-By: Claude Opus 4.8 --- client/lib/main.dart | 23 ++++++++++++++++++++--- client/pubspec.yaml | 2 +- client/windows/installer/pangolin.iss | 2 +- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/client/lib/main.dart b/client/lib/main.dart index 86cd093..e4d4bbf 100644 --- a/client/lib/main.dart +++ b/client/lib/main.dart @@ -1,5 +1,6 @@ // main.dart — 入口(演示态) // 串接:登录 → 引导 → 主框架(4 Tab);状态层统一走 Riverpod。 +import 'dart:async'; import 'dart:io'; import 'dart:ui' as ui; @@ -133,7 +134,14 @@ class _RootFlowState extends ConsumerState { ProviderSubscription>>? nodeSub; ProviderSubscription? settingsSub; ProviderSubscription? selSub; + Timer? poll; var decided = false; + void finish() { + nodeSub?.close(); + settingsSub?.close(); + selSub?.close(); + poll?.cancel(); + } void tryGo() { if (decided) return; final settings = ref.read(settingsProvider); @@ -142,11 +150,11 @@ class _RootFlowState extends ConsumerState { // 否则可能在 effectiveNode 尚是占位/未就绪时就触发,_connect 报「节点尚未就绪」。 // 还要等设置 + 选中节点都从持久化恢复完,否则可能用到未恢复的默认(智能)而非上次选择。 final node = ref.read(effectiveNodeProvider); + logLine('AutoConnect', + 'check: settingsLoaded=${settings.loaded} selLoaded=$selLoaded node=${node.code} uuid=${node.uuid.isEmpty ? "EMPTY" : "ok"}'); if (!settings.loaded || !selLoaded || node.uuid.isEmpty) return; decided = true; - nodeSub?.close(); - settingsSub?.close(); - selSub?.close(); + finish(); final phase = ref.read(connectionProvider).phase; logLine('AutoConnect', 'ready: autoConnect=${settings.autoConnect} phase=$phase node=${node.code} uuid=${node.uuid.isEmpty ? "EMPTY" : "ok"}'); if (settings.autoConnect && phase == VpnPhase.off) { @@ -163,6 +171,15 @@ class _RootFlowState extends ConsumerState { nodeSub = ref.listenManual(nodesProvider, (_, __) => tryGo()); settingsSub = ref.listenManual(settingsProvider, (_, __) => tryGo()); selSub = ref.listenManual(selectedNodeCodeProvider, (_, __) => tryGo()); + // 兜底轮询:有些就绪信号(如选中节点 _load 完成但值未变,不会发通知)不会触发上面的监听, + // 靠定时复查避免「明明都就绪了却没人再调 tryGo」的死锁。最多等 ~12s。 + poll = Timer.periodic(const Duration(milliseconds: 400), (t) { + if (decided || t.tick > 30) { + t.cancel(); + return; + } + tryGo(); + }); tryGo(); } diff --git a/client/pubspec.yaml b/client/pubspec.yaml index 5437232..5ac6526 100644 --- a/client/pubspec.yaml +++ b/client/pubspec.yaml @@ -1,7 +1,7 @@ name: pangolin_vpn description: 穿山甲 · Pangolin — 极简、稳定、跨平台网络加速客户端。 publish_to: "none" -version: 1.0.26+27 +version: 1.0.27+28 environment: sdk: ^3.5.0 diff --git a/client/windows/installer/pangolin.iss b/client/windows/installer/pangolin.iss index 3fcd251..56e1a77 100644 --- a/client/windows/installer/pangolin.iss +++ b/client/windows/installer/pangolin.iss @@ -3,7 +3,7 @@ ; 前置: 先在 client/ 跑 `flutter build windows`(Release),产物在 ; ..\..\build\windows\x64\runner\Release #define MyAppName "穿山甲 Pangolin" -#define MyAppVersion "1.0.26" +#define MyAppVersion "1.0.27" #define MyAppPublisher "Pangolin" #define MyAppExeName "pangolin_vpn.exe" #define BuildDir "..\..\build\windows\x64\runner\Release"