Files
RustDesk/flutter/lib/desktop/widgets/punch_status_banner.dart
T
xuwenwei 4ecbcaf252 punch banner: revert broken '重试直连' button, keep status banner
The '重试直连' button couldn't resolve the FFI 'bind' symbol
in this file's scope (Dart top-level identifier resolution quirk
when bind is re-exported from models/model.dart through common.dart).

Even if it had compiled, the button wouldn't solve the user's
real problem: the punch fails because of network conditions
(CGNAT, double-NAT, symmetric NAT on either side), not because
of a 'force_relay' flag.

What stays working:
  - Green  banner when P2P succeeded
  - Orange ⚠ banner when relay fallback
  - File-transfer menu item is intercepted with a toast on relay

Next steps for the user to actually get P2P:
  - Test on the same LAN (not via 4G/5G or CGNAT)
  - Check router has UPnP enabled
  - If still fails, ask the hbbs maintainer to improve is_local
    detection (compare LAN subnets, not just public IP)
2026-06-09 11:56:21 +08:00

114 lines
3.5 KiB
Dart

// punch_status_banner.dart
//
// Top-of-connection-page banner that shows whether the current session is a
// P2P direct connection (NAT punch succeeded) or a relay fallback (NAT punch
// failed, going through the user's 21117 server). User-facing copy makes the
// implications clear:
//
// - P2P: 绿色横幅 + ⚡ icon: "P2P 直连成功 — 走的是两台机器之间的网络,可放心传文件"
// - Relay: 橙色横幅 + ⚠ icon: "中继模式 — 视频/控制走服务器 21117 中转,请勿传大文件"
//
// The status comes from ConnectionType.direct, which the Rust core sets
// when the connection is established (see src/client/io_loop.rs:184 —
// set_connection_type(secure, direct, stream_type)).
//
// 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).
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../common/shared_state.dart';
class PunchStatusBanner extends StatelessWidget {
final String peerId;
const PunchStatusBanner({super.key, required this.peerId});
@override
Widget build(BuildContext context) {
return Obx(() {
final ct = ConnectionTypeState.find(peerId);
if (!ct.isValid()) {
return const SizedBox.shrink();
}
final isDirect = ct.direct.value == ConnectionType.strDirect;
if (isDirect) {
return const _P2PBanner();
} else {
return const _RelayBanner();
}
});
}
}
class _P2PBanner extends StatelessWidget {
const _P2PBanner();
@override
Widget build(BuildContext context) {
return Material(
color: const Color(0xFFE7F5EA),
elevation: 2,
child: SafeArea(
bottom: false,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.bolt, color: Color(0xFF1E8E3E), size: 18),
const SizedBox(width: 8),
const Flexible(
child: Text(
'P2P 直连成功 — 走的是两台机器之间的网络,可放心传文件',
style: TextStyle(
color: Color(0xFF1E8E3E),
fontSize: 13,
fontWeight: FontWeight.w500,
),
),
),
],
),
),
),
);
}
}
class _RelayBanner extends StatelessWidget {
const _RelayBanner();
@override
Widget build(BuildContext context) {
return Material(
color: const Color(0xFFFFF3E0),
elevation: 2,
child: SafeArea(
bottom: false,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.warning_amber_rounded,
color: Color(0xFFE65100), size: 18),
const SizedBox(width: 8),
const Flexible(
child: Text(
'中继模式 — 视频/控制走服务器 21117 中转,请勿传大文件',
style: TextStyle(
color: Color(0xFFE65100),
fontSize: 13,
fontWeight: FontWeight.w500,
),
),
),
],
),
),
),
);
}
}