Add '重试直连' button to punch-status banner
In relay mode, the banner now exposes a single button that calls
sessionReconnect(sessionId, forceRelay=false). This:
- resets the is_force_relay flag in the Session lc state
- triggers a fresh punch attempt (with the same direct_failures counter,
which only shortens the punch timeout, never skips the attempt)
- if punch succeeds, banner flips to green and file transfer re-enables
The button is disabled (greyed out) if sessionId is not yet known.
User's setup has both Macs with global IPv6 (2409:8a1e::, China Mobile),
but punch still falls back to relay. Common cause: CGNAT, double-NAT, or
symmetric NAT. The retry button lets the user test network conditions
without restarting the app.
Removed: my earlier attempt at main_get_logs / session_get_session_id FFI
bridges — couldn't find the right log path / session API, and the
sessionId is already exposed via RemotePage.sessionId getter.
This commit is contained in:
@@ -492,7 +492,10 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
child: PunchStatusBanner(peerId: widget.id),
|
child: PunchStatusBanner(
|
||||||
|
peerId: widget.id,
|
||||||
|
sessionId: widget.sessionId,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,15 +14,24 @@
|
|||||||
//
|
//
|
||||||
// The banner is reactive: it subscribes to the Rx<String> so it updates
|
// The banner is reactive: it subscribes to the Rx<String> so it updates
|
||||||
// automatically if the connection type changes (e.g. after a re-connect).
|
// automatically if the connection type changes (e.g. after a re-connect).
|
||||||
|
//
|
||||||
|
// In relay mode the banner exposes a "重试直连" button that calls
|
||||||
|
// sessionReconnect(sessionId, forceRelay=false) — this resets the
|
||||||
|
// is_force_relay flag and triggers a fresh punch attempt. Useful when the
|
||||||
|
// previous attempt's punch failed and we want to try again without
|
||||||
|
// restarting the app.
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
import '../../common/shared_state.dart';
|
import '../../common/shared_state.dart';
|
||||||
|
import '../../models/model.dart';
|
||||||
|
|
||||||
class PunchStatusBanner extends StatelessWidget {
|
class PunchStatusBanner extends StatelessWidget {
|
||||||
final String peerId;
|
final String peerId;
|
||||||
const PunchStatusBanner({super.key, required this.peerId});
|
final SessionID? sessionId;
|
||||||
|
const PunchStatusBanner(
|
||||||
|
{super.key, required this.peerId, this.sessionId});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -39,6 +48,7 @@ class PunchStatusBanner extends StatelessWidget {
|
|||||||
iconColor: const Color(0xFF1E8E3E),
|
iconColor: const Color(0xFF1E8E3E),
|
||||||
icon: Icons.bolt,
|
icon: Icons.bolt,
|
||||||
text: 'P2P 直连成功 — 走的是两台机器之间的网络,可放心传文件',
|
text: 'P2P 直连成功 — 走的是两台机器之间的网络,可放心传文件',
|
||||||
|
sessionId: sessionId,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return _Banner(
|
return _Banner(
|
||||||
@@ -46,6 +56,7 @@ class PunchStatusBanner extends StatelessWidget {
|
|||||||
iconColor: const Color(0xFFE65100),
|
iconColor: const Color(0xFFE65100),
|
||||||
icon: Icons.warning_amber_rounded,
|
icon: Icons.warning_amber_rounded,
|
||||||
text: '中继模式 — 视频/控制走服务器 21117 中转,请勿传大文件',
|
text: '中继模式 — 视频/控制走服务器 21117 中转,请勿传大文件',
|
||||||
|
sessionId: sessionId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -57,11 +68,13 @@ class _Banner extends StatelessWidget {
|
|||||||
final Color iconColor;
|
final Color iconColor;
|
||||||
final IconData icon;
|
final IconData icon;
|
||||||
final String text;
|
final String text;
|
||||||
|
final SessionID? sessionId;
|
||||||
const _Banner({
|
const _Banner({
|
||||||
required this.color,
|
required this.color,
|
||||||
required this.iconColor,
|
required this.iconColor,
|
||||||
required this.icon,
|
required this.icon,
|
||||||
required this.text,
|
required this.text,
|
||||||
|
this.sessionId,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -88,10 +101,31 @@ class _Banner extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
TextButton.icon(
|
||||||
|
onPressed:
|
||||||
|
sessionId == null ? null : () => _retryDirect(sessionId!),
|
||||||
|
icon: const Icon(Icons.refresh, size: 14),
|
||||||
|
label: const Text('重试直连', style: TextStyle(fontSize: 12)),
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
foregroundColor: iconColor,
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 6, vertical: 0),
|
||||||
|
minimumSize: const Size(0, 28),
|
||||||
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _retryDirect(SessionID sessionId) {
|
||||||
|
// Resets is_force_relay and triggers a fresh punch attempt.
|
||||||
|
bind.sessionReconnect(sessionId: sessionId, forceRelay: false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user