feat(client): 断开连接前弹二次确认框(避免误触瞬断)
连接键在 on 态点击不再直接断开:先弹危险操作确认框(告知连接已加密, 确认才 toggle 断开);off/connecting 态行为不变直接 toggle。 新增 l10n disconnectTitle/Body/Action(en/zh/es/ja/ko/ru 六语)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FEVUXAbFT6bF1Qw27RHWoD
This commit is contained in:
@@ -343,6 +343,9 @@ abstract class AppText {
|
||||
String get capOn;
|
||||
String get connectNow; // 连接键 off 态圆内文字
|
||||
String get secure; // 连接键圆内「已加密」
|
||||
String get disconnectTitle; // 断开确认弹窗标题
|
||||
String get disconnectBody; // 断开确认弹窗正文
|
||||
String get disconnectAction; // 断开确认弹窗「断开」按钮
|
||||
|
||||
// ── 底部 / 侧栏导航 ──
|
||||
String get tabConnect;
|
||||
|
||||
@@ -27,6 +27,12 @@ class StringsEn extends AppText {
|
||||
@override
|
||||
String get secure => 'SECURE';
|
||||
@override
|
||||
String get disconnectTitle => 'Disconnect?';
|
||||
@override
|
||||
String get disconnectBody => 'Your connection is encrypted. Are you sure you want to disconnect?';
|
||||
@override
|
||||
String get disconnectAction => 'Disconnect';
|
||||
@override
|
||||
String get tabConnect => 'Connect';
|
||||
@override
|
||||
String get tabServers => 'Servers';
|
||||
|
||||
@@ -27,6 +27,12 @@ class StringsEs extends AppText {
|
||||
@override
|
||||
String get secure => 'SEGURO';
|
||||
@override
|
||||
String get disconnectTitle => '¿Desconectar?';
|
||||
@override
|
||||
String get disconnectBody => 'Tu conexión está cifrada. ¿Seguro que quieres desconectar?';
|
||||
@override
|
||||
String get disconnectAction => 'Desconectar';
|
||||
@override
|
||||
String get tabConnect => 'Conectar';
|
||||
@override
|
||||
String get tabServers => 'Servidores';
|
||||
|
||||
@@ -27,6 +27,12 @@ class StringsJa extends AppText {
|
||||
@override
|
||||
String get secure => 'セキュア';
|
||||
@override
|
||||
String get disconnectTitle => '切断しますか?';
|
||||
@override
|
||||
String get disconnectBody => '接続は暗号化されています。切断してもよろしいですか?';
|
||||
@override
|
||||
String get disconnectAction => '切断';
|
||||
@override
|
||||
String get tabConnect => '接続';
|
||||
@override
|
||||
String get tabServers => 'サーバー';
|
||||
|
||||
@@ -27,6 +27,12 @@ class StringsKo extends AppText {
|
||||
@override
|
||||
String get secure => '보안';
|
||||
@override
|
||||
String get disconnectTitle => '연결 해제하시겠습니까?';
|
||||
@override
|
||||
String get disconnectBody => '연결이 암호화되어 있습니다. 정말 연결을 해제하시겠습니까?';
|
||||
@override
|
||||
String get disconnectAction => '연결 해제';
|
||||
@override
|
||||
String get tabConnect => '연결';
|
||||
@override
|
||||
String get tabServers => '서버';
|
||||
|
||||
@@ -27,6 +27,12 @@ class StringsRu extends AppText {
|
||||
@override
|
||||
String get secure => 'ЗАЩИЩЕНО';
|
||||
@override
|
||||
String get disconnectTitle => 'Отключиться?';
|
||||
@override
|
||||
String get disconnectBody => 'Ваше соединение зашифровано. Действительно отключиться?';
|
||||
@override
|
||||
String get disconnectAction => 'Отключить';
|
||||
@override
|
||||
String get tabConnect => 'Подключение';
|
||||
@override
|
||||
String get tabServers => 'Серверы';
|
||||
|
||||
@@ -27,6 +27,12 @@ class StringsZh extends AppText {
|
||||
@override
|
||||
String get secure => '已加密';
|
||||
@override
|
||||
String get disconnectTitle => '断开连接?';
|
||||
@override
|
||||
String get disconnectBody => '当前连接已加密。确定要断开吗?';
|
||||
@override
|
||||
String get disconnectAction => '断开';
|
||||
@override
|
||||
String get tabConnect => '连接';
|
||||
@override
|
||||
String get tabServers => '节点';
|
||||
|
||||
@@ -21,6 +21,47 @@ class ConnectPage extends ConsumerWidget {
|
||||
final bool isWide;
|
||||
final VoidCallback onOpenNodes;
|
||||
|
||||
// 连接键点击:off/connecting 直接 toggle;on 态(已连接)先弹二次确认再断开,
|
||||
// 避免误触瞬断加密连接。
|
||||
Future<void> _handleToggle(BuildContext context, WidgetRef ref, VpnPhase phase, AppText t) async {
|
||||
final notifier = ref.read(connectionProvider.notifier);
|
||||
if (phase != VpnPhase.on) {
|
||||
notifier.toggle();
|
||||
return;
|
||||
}
|
||||
final c = context.pangolin;
|
||||
final ok = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
backgroundColor: c.surface,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(PangolinRadius.xl)),
|
||||
title: Row(children: [
|
||||
Container(
|
||||
width: 34,
|
||||
height: 34,
|
||||
decoration: BoxDecoration(color: c.dangerSubtle, shape: BoxShape.circle),
|
||||
child: Icon(PangolinIcons.alertTriangle, size: 18, color: c.danger)),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(t.disconnectTitle,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: PangolinText.body.copyWith(color: c.fg1, fontWeight: FontWeight.w700))),
|
||||
]),
|
||||
content: Text(t.disconnectBody, style: PangolinText.sm.copyWith(color: c.fg2, height: 1.5)),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(ctx, false),
|
||||
child: Text(t.devCancel, style: PangolinText.sm.copyWith(color: c.fg2, fontWeight: FontWeight.w600))),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(ctx, true),
|
||||
child: Text(t.disconnectAction,
|
||||
style: PangolinText.sm.copyWith(color: c.danger, fontWeight: FontWeight.w700))),
|
||||
],
|
||||
),
|
||||
);
|
||||
if (ok == true) notifier.toggle();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final c = context.pangolin;
|
||||
@@ -53,7 +94,7 @@ class ConnectPage extends ConsumerWidget {
|
||||
elapsed: conn.elapsed,
|
||||
offLabel: t.connectNow,
|
||||
secureLabel: t.secure,
|
||||
onTap: () => ref.read(connectionProvider.notifier).toggle(),
|
||||
onTap: () => _handleToggle(context, ref, conn.phase, t),
|
||||
);
|
||||
|
||||
final captionWidget = Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
@@ -94,7 +135,7 @@ class ConnectPage extends ConsumerWidget {
|
||||
offLabel: t.connectNow,
|
||||
secureLabel: t.secure,
|
||||
size: 176,
|
||||
onTap: () => ref.read(connectionProvider.notifier).toggle(),
|
||||
onTap: () => _handleToggle(context, ref, conn.phase, t),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(caption,
|
||||
|
||||
Reference in New Issue
Block a user