chore(client): 1.0.19 — 自动连接诊断日志

排查「自动连接没生效」:在 _RootFlow 自动连接路径(HomeShell 显示 / 节点就绪 / autoConnect/phase 判定 / 触发)
与 Settings(_load 读到的 autoConnect、setAutoConnect 落盘)加 logLine 日志,定位卡在哪一步。诊断完移除。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-29 18:26:47 +08:00
parent 6beed211b9
commit f734126eb6
4 changed files with 22 additions and 5 deletions
+17 -3
View File
@@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
import 'bridge/log.dart';
import 'bridge/vpn_bridge_provider.dart'; import 'bridge/vpn_bridge_provider.dart';
import 'pangolin_theme.dart'; import 'pangolin_theme.dart';
import 'services/token_store.dart'; import 'services/token_store.dart';
@@ -117,22 +118,31 @@ class _RootFlowState extends ConsumerState<RootFlow> {
final _store = const TokenStore(); final _store = const TokenStore();
bool _forceOnboarding = false; bool _forceOnboarding = false;
bool _autoConnectArmed = false; // 自动连接每进程只尝试一次 bool _autoConnectArmed = false; // 自动连接每进程只尝试一次
bool _homeLogged = false; // 仅日志:HomeShell 首次显示打一行
// 进入登录态主界面后:若开了「自动连接」且当前未连接,等节点就绪后自动连一次。 // 进入登录态主界面后:若开了「自动连接」且当前未连接,等节点就绪后自动连一次。
void _maybeAutoConnect() { void _maybeAutoConnect() {
if (_autoConnectArmed) return; if (_autoConnectArmed) return;
_autoConnectArmed = true; _autoConnectArmed = true;
logLine('AutoConnect', 'armed; waiting for nodes ready');
// 注意:不在此处早读 settingsProvider.autoConnect —— 其 _load 是异步(SharedPreferences), // 注意:不在此处早读 settingsProvider.autoConnect —— 其 _load 是异步(SharedPreferences),
// 启动这一刻多半还没加载完,会读到默认 false 而永久跳过。改到「节点就绪」回调里再判: // 启动这一刻多半还没加载完,会读到默认 false 而永久跳过。改到「节点就绪」回调里再判:
// 节点要走网络拉取,远慢于本地设置读取,那时 autoConnect 必已加载到位。 // 节点要走网络拉取,远慢于本地设置读取,那时 autoConnect 必已加载到位。
late final ProviderSubscription sub; late final ProviderSubscription sub;
sub = ref.listenManual(nodesProvider, (prev, next) { sub = ref.listenManual(nodesProvider, (prev, next) {
final ready = (next.valueOrNull ?? const []).any((n) => n.uuid.isNotEmpty); final nodes = next.valueOrNull ?? const [];
final ready = nodes.any((n) => n.uuid.isNotEmpty);
logLine('AutoConnect', 'nodes update: total=${nodes.length} ready=$ready loading=${next.isLoading} err=${next.hasError}');
if (!ready) return; if (!ready) return;
sub.close(); sub.close();
if (ref.read(settingsProvider).autoConnect && final ac = ref.read(settingsProvider).autoConnect;
ref.read(connectionProvider).phase == VpnPhase.off) { final phase = ref.read(connectionProvider).phase;
logLine('AutoConnect', 'nodes ready → autoConnect=$ac phase=$phase');
if (ac && phase == VpnPhase.off) {
logLine('AutoConnect', 'triggering connect');
ref.read(connectionProvider.notifier).toggle(); ref.read(connectionProvider.notifier).toggle();
} else {
logLine('AutoConnect', 'NOT connecting (autoConnect=$ac phase=$phase)');
} }
}, fireImmediately: true); }, fireImmediately: true);
} }
@@ -162,6 +172,10 @@ class _RootFlowState extends ConsumerState<RootFlow> {
if (_forceOnboarding) { if (_forceOnboarding) {
return OnboardingScreen(t: t, onDone: () { _afterOnboarding(); }); return OnboardingScreen(t: t, onDone: () { _afterOnboarding(); });
} }
if (!_homeLogged) {
_homeLogged = true;
logLine('AutoConnect', 'HomeShell shown; scheduling auto-connect check');
}
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) _maybeAutoConnect(); if (mounted) _maybeAutoConnect();
}); });
+3
View File
@@ -11,6 +11,7 @@ import 'package:launch_at_startup/launch_at_startup.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import '../bridge/log.dart';
import '../bridge/vpn_bridge_provider.dart'; import '../bridge/vpn_bridge_provider.dart';
class AppSettings { class AppSettings {
@@ -59,6 +60,7 @@ class SettingsController extends StateNotifier<AppSettings> {
smartRoute: p.getBool(_kSmartRoute) ?? true, smartRoute: p.getBool(_kSmartRoute) ?? true,
killSwitch: p.getBool(_kKillSwitch) ?? true, killSwitch: p.getBool(_kKillSwitch) ?? true,
); );
logLine('Settings', 'loaded autoConnect=${state.autoConnect} autostart=${state.autostart}');
} catch (_) { } catch (_) {
// 测试/无平台:保留默认值。 // 测试/无平台:保留默认值。
} }
@@ -76,6 +78,7 @@ class SettingsController extends StateNotifier<AppSettings> {
Future<void> setAutoConnect(bool v) async { Future<void> setAutoConnect(bool v) async {
state = state.copyWith(autoConnect: v); state = state.copyWith(autoConnect: v);
await _persist(_kAutoConnect, v); await _persist(_kAutoConnect, v);
logLine('Settings', 'setAutoConnect=$v persisted');
} }
Future<void> setSmartRoute(bool v) async { Future<void> setSmartRoute(bool v) async {
+1 -1
View File
@@ -1,7 +1,7 @@
name: pangolin_vpn name: pangolin_vpn
description: 穿山甲 · Pangolin — 极简、稳定、跨平台网络加速客户端。 description: 穿山甲 · Pangolin — 极简、稳定、跨平台网络加速客户端。
publish_to: "none" publish_to: "none"
version: 1.0.18+19 version: 1.0.19+20
environment: environment:
sdk: ^3.5.0 sdk: ^3.5.0
+1 -1
View File
@@ -3,7 +3,7 @@
; 前置: 先在 client/ 跑 `flutter build windows`(Release),产物在 ; 前置: 先在 client/ 跑 `flutter build windows`(Release),产物在
; ..\..\build\windows\x64\runner\Release ; ..\..\build\windows\x64\runner\Release
#define MyAppName "穿山甲 Pangolin" #define MyAppName "穿山甲 Pangolin"
#define MyAppVersion "1.0.18" #define MyAppVersion "1.0.19"
#define MyAppPublisher "Pangolin" #define MyAppPublisher "Pangolin"
#define MyAppExeName "pangolin_vpn.exe" #define MyAppExeName "pangolin_vpn.exe"
#define BuildDir "..\..\build\windows\x64\runner\Release" #define BuildDir "..\..\build\windows\x64\runner\Release"