// 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 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, ), ), ), ], ), ), ), ); } }