fix(tcp): guard connect cancellation race and align USB screen actions

- add connectTcp cancellation guards after socket connect and connect delay so handshake does not proceed when transport/state changed
- ignore late TCP connect errors after manual cancel or transport switch to avoid spurious second disconnect paths
- keep TCP action hidden only on web and show Bluetooth action on USB screen across platforms for navigation consistency
This commit is contained in:
just-stuff-tm 2026-03-10 19:27:39 -04:00
parent 929c1c3d28
commit 1913a5aa11
2 changed files with 39 additions and 4 deletions

View file

@ -995,9 +995,37 @@ class MeshCoreConnector extends ChangeNotifier {
await _tcpFrameSubscription?.cancel();
_tcpFrameSubscription = null;
await _tcpManager.connect(host: host, port: port);
final isTcpConnectCancelled =
_activeTransport != MeshCoreTransportType.tcp ||
_state != MeshCoreConnectionState.connecting ||
!_tcpManager.isConnected;
if (isTcpConnectCancelled) {
_appDebugLogService?.warn(
'connectTcp aborted before handshake: state=$_state transport=$_activeTransport connected=${_tcpManager.isConnected}',
tag: 'TCP',
);
if (_tcpManager.isConnected) {
await _tcpManager.disconnect();
}
return;
}
notifyListeners();
await Future<void>.delayed(const Duration(milliseconds: 200));
final isTcpConnectCancelledAfterDelay =
_activeTransport != MeshCoreTransportType.tcp ||
_state != MeshCoreConnectionState.connecting ||
!_tcpManager.isConnected;
if (isTcpConnectCancelledAfterDelay) {
_appDebugLogService?.warn(
'connectTcp aborted after connect delay: state=$_state transport=$_activeTransport connected=${_tcpManager.isConnected}',
tag: 'TCP',
);
if (_tcpManager.isConnected) {
await _tcpManager.disconnect();
}
return;
}
_tcpFrameSubscription = _tcpManager.frameStream.listen(
_handleFrame,
onError: (error, stackTrace) {
@ -1031,6 +1059,16 @@ class MeshCoreConnector extends ChangeNotifier {
await syncTime();
} catch (error) {
_appDebugLogService?.error('TCP connection error: $error', tag: 'TCP');
final tcpConnectNoLongerActive =
_activeTransport != MeshCoreTransportType.tcp ||
_state != MeshCoreConnectionState.connecting;
if (tcpConnectNoLongerActive) {
_appDebugLogService?.info(
'Ignoring late TCP connect error after cancellation/switch: state=$_state transport=$_activeTransport',
tag: 'TCP',
);
return;
}
await disconnect(manual: false);
rethrow;
}

View file

@ -108,10 +108,7 @@ class _UsbScreenState extends State<UsbScreen> {
bottomNavigationBar: Consumer<MeshCoreConnector>(
builder: (context, connector, child) {
final isLoading = _isLoadingPorts;
final showBle =
PlatformInfo.isWeb ||
PlatformInfo.isAndroid ||
PlatformInfo.isIOS;
final showBle = true;
final showTcp = !PlatformInfo.isWeb;
return SafeArea(