From db935a745433367dffb46228c621a81ba8982376 Mon Sep 17 00:00:00 2001 From: Zach Date: Fri, 13 Mar 2026 10:58:52 -0700 Subject: [PATCH] refactor(tcp): promote MeshCoreTcpConnector, fix translations, harden UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace thin MeshCoreTcpManager facade with a proper MeshCoreTcpConnector that owns TcpTransportService and the frame subscription, mirroring MeshCoreUsbManager. The connector no longer holds a raw TcpTransportService or a _tcpFrameSubscription field. - Remove hardcoded default host IP from TcpScreen (keep port 5000 hint). - Disable connect button during scanning state, not just connecting state. - Fix tcpPortLabel mistranslated as nautical "port/harbor" in de, it, pt, nl, sv, sk, sl, zh; fix corrupted Slovak tcpPortHint ("5 000" → "5000"). - Remove unused tcpStatus_connecting string from all 15 locale arb files and all generated app_localizations_*.dart files. - Add extendedPadding to TCP screen FABs to match USB screen. - Add Key to connect button; update tests to use byKey and assert onPressed == null when button is disabled during scanning. --- lib/connector/meshcore_connector.dart | 39 +++++++++----------- lib/connector/meshcore_connector_tcp.dart | 44 ++++++++++++++++++++--- lib/l10n/app_bg.arb | 1 - lib/l10n/app_de.arb | 3 +- lib/l10n/app_en.arb | 1 - lib/l10n/app_es.arb | 1 - lib/l10n/app_fr.arb | 1 - lib/l10n/app_it.arb | 3 +- lib/l10n/app_localizations.dart | 6 ---- lib/l10n/app_localizations_bg.dart | 3 -- lib/l10n/app_localizations_de.dart | 6 +--- lib/l10n/app_localizations_en.dart | 3 -- lib/l10n/app_localizations_es.dart | 3 -- lib/l10n/app_localizations_fr.dart | 3 -- lib/l10n/app_localizations_it.dart | 5 +-- lib/l10n/app_localizations_nl.dart | 5 +-- lib/l10n/app_localizations_pl.dart | 3 -- lib/l10n/app_localizations_pt.dart | 6 +--- lib/l10n/app_localizations_ru.dart | 3 -- lib/l10n/app_localizations_sk.dart | 7 ++-- lib/l10n/app_localizations_sl.dart | 5 +-- lib/l10n/app_localizations_sv.dart | 5 +-- lib/l10n/app_localizations_uk.dart | 3 -- lib/l10n/app_localizations_zh.dart | 5 +-- lib/l10n/app_nl.arb | 3 +- lib/l10n/app_pl.arb | 1 - lib/l10n/app_pt.arb | 3 +- lib/l10n/app_ru.arb | 1 - lib/l10n/app_sk.arb | 5 ++- lib/l10n/app_sl.arb | 3 +- lib/l10n/app_sv.arb | 3 +- lib/l10n/app_uk.arb | 1 - lib/l10n/app_zh.arb | 3 +- lib/screens/tcp_screen.dart | 10 ++++-- test/screens/tcp_flow_test.dart | 17 +++++---- 35 files changed, 91 insertions(+), 123 deletions(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index b800cfa3..d974b7b8 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -116,8 +116,7 @@ class MeshCoreConnector extends ChangeNotifier { bool _manualDisconnect = false; final MeshCoreUsbManager _usbManager = MeshCoreUsbManager(); StreamSubscription? _usbFrameSubscription; - final MeshCoreTcpManager _tcpManager = MeshCoreTcpManager(); - StreamSubscription? _tcpFrameSubscription; + final MeshCoreTcpConnector _tcpConnector = MeshCoreTcpConnector(); MeshCoreTransportType _activeTransport = MeshCoreTransportType.bluetooth; final List _scanResults = []; @@ -257,7 +256,7 @@ class MeshCoreConnector extends ChangeNotifier { bool get isUsbTransportConnected => _state == MeshCoreConnectionState.connected && _activeTransport == MeshCoreTransportType.usb; - String? get activeTcpEndpoint => _tcpManager.activeEndpoint; + String? get activeTcpEndpoint => _tcpConnector.activeEndpoint; bool get isTcpTransportConnected => _state == MeshCoreConnectionState.connected && _activeTransport == MeshCoreTransportType.tcp; @@ -666,7 +665,7 @@ class MeshCoreConnector extends ChangeNotifier { _appDebugLogService = appDebugLogService; _backgroundService = backgroundService; _usbManager.setDebugLogService(_appDebugLogService); - _tcpManager.setDebugLogService(_appDebugLogService); + _tcpConnector.setDebugLogService(_appDebugLogService); // Initialize notification service _notificationService.initialize(); @@ -1002,22 +1001,21 @@ class MeshCoreConnector extends ChangeNotifier { await disconnect(manual: false); return; } - if (_tcpManager.isConnected) { - await _tcpManager.disconnect(); + if (_tcpConnector.isConnected) { + await _tcpConnector.disconnect(); } } - await _tcpFrameSubscription?.cancel(); - _tcpFrameSubscription = null; - await _tcpManager.connect(host: host, port: port); + await _tcpConnector.cancelFrameSubscription(); + await _tcpConnector.connect(host: host, port: port); final isTcpConnectCancelled = _activeTransport != MeshCoreTransportType.tcp || _state != MeshCoreConnectionState.connecting || - !_tcpManager.isConnected; + !_tcpConnector.isConnected; if (isTcpConnectCancelled) { await handleTcpConnectAbort( message: - 'connectTcp aborted before handshake: state=$_state transport=$_activeTransport connected=${_tcpManager.isConnected}', + 'connectTcp aborted before handshake: state=$_state transport=$_activeTransport connected=${_tcpConnector.isConnected}', ); return; } @@ -1027,16 +1025,16 @@ class MeshCoreConnector extends ChangeNotifier { final isTcpConnectCancelledAfterDelay = _activeTransport != MeshCoreTransportType.tcp || _state != MeshCoreConnectionState.connecting || - !_tcpManager.isConnected; + !_tcpConnector.isConnected; if (isTcpConnectCancelledAfterDelay) { await handleTcpConnectAbort( message: - 'connectTcp aborted after connect delay: state=$_state transport=$_activeTransport connected=${_tcpManager.isConnected}', + 'connectTcp aborted after connect delay: state=$_state transport=$_activeTransport connected=${_tcpConnector.isConnected}', ); return; } - _tcpFrameSubscription = _tcpManager.frameStream.listen( - _handleFrame, + _tcpConnector.listenFrames( + onFrame: _handleFrame, onError: (error, stackTrace) { _appDebugLogService?.error('TCP transport error: $error', tag: 'TCP'); unawaited(disconnect(manual: false)); @@ -1073,7 +1071,7 @@ class MeshCoreConnector extends ChangeNotifier { manualDisconnect: _manualDisconnect, state: _state, activeTransport: _activeTransport, - tcpManagerConnected: _tcpManager.isConnected, + tcpManagerConnected: _tcpConnector.isConnected, ); if (tcpConnectCancelledBeforeHandshake) { _appDebugLogService?.info( @@ -1445,9 +1443,7 @@ class MeshCoreConnector extends ChangeNotifier { await _usbFrameSubscription?.cancel(); _usbFrameSubscription = null; await _usbManager.disconnect(); - await _tcpFrameSubscription?.cancel(); - _tcpFrameSubscription = null; - await _tcpManager.disconnect(); + await _tcpConnector.disconnect(); await _notifySubscription?.cancel(); _notifySubscription = null; @@ -1530,7 +1526,7 @@ class MeshCoreConnector extends ChangeNotifier { if (_activeTransport == MeshCoreTransportType.usb) { await _usbManager.write(data); } else if (_activeTransport == MeshCoreTransportType.tcp) { - await _tcpManager.write(data); + await _tcpConnector.write(data); } else { if (_rxCharacteristic == null) { throw Exception("MeshCore RX characteristic not available"); @@ -4484,14 +4480,13 @@ class MeshCoreConnector extends ChangeNotifier { _scanSubscription?.cancel(); _connectionSubscription?.cancel(); _usbFrameSubscription?.cancel(); - _tcpFrameSubscription?.cancel(); _notifySubscription?.cancel(); _notifyListenersTimer?.cancel(); _reconnectTimer?.cancel(); _batteryPollTimer?.cancel(); _receivedFramesController.close(); _usbManager.dispose(); - _tcpManager.dispose(); + _tcpConnector.dispose(); // Flush pending unread writes before disposal _unreadStore.flush(); diff --git a/lib/connector/meshcore_connector_tcp.dart b/lib/connector/meshcore_connector_tcp.dart index 92b98d77..7c93d9f5 100644 --- a/lib/connector/meshcore_connector_tcp.dart +++ b/lib/connector/meshcore_connector_tcp.dart @@ -1,34 +1,70 @@ +import 'dart:async'; import 'dart:typed_data'; import '../services/app_debug_log_service.dart'; import '../services/tcp_transport_service.dart'; -class MeshCoreTcpManager { +/// Manages TCP transport for MeshCore devices. +/// +/// Owns the [TcpTransportService] and TCP-specific connection state. +/// The main [MeshCoreConnector] delegates all TCP operations here. +class MeshCoreTcpConnector { final TcpTransportService _service = TcpTransportService(); AppDebugLogService? _debugLog; + StreamSubscription? _frameSubscription; + // --- Getters --- String? get activeEndpoint => _service.activeEndpoint; bool get isConnected => _service.isConnected; - Stream get frameStream => _service.frameStream; + // --- Configuration --- void setDebugLogService(AppDebugLogService? service) { _debugLog = service; _service.setDebugLogService(service); } + // --- Connection lifecycle --- Future connect({required String host, required int port}) async { - _debugLog?.info('TcpManager.connect endpoint=$host:$port', tag: 'TCP'); + _debugLog?.info('TcpConnector.connect endpoint=$host:$port', tag: 'TCP'); + await _frameSubscription?.cancel(); + _frameSubscription = null; await _service.connect(host: host, port: port); + _debugLog?.info( + 'TcpConnector.connect done, endpoint=${_service.activeEndpoint}', + tag: 'TCP', + ); + } + + StreamSubscription listenFrames({ + required void Function(Uint8List) onFrame, + required void Function(Object, StackTrace?) onError, + required void Function() onDone, + }) { + _frameSubscription = _service.frameStream.listen( + onFrame, + onError: onError, + onDone: onDone, + ); + return _frameSubscription!; + } + + Future cancelFrameSubscription() async { + await _frameSubscription?.cancel(); + _frameSubscription = null; } Future disconnect() async { - _debugLog?.info('TcpManager.disconnect', tag: 'TCP'); + if (!_service.isConnected && _frameSubscription == null) return; + _debugLog?.info('TcpConnector.disconnect', tag: 'TCP'); + await _frameSubscription?.cancel(); + _frameSubscription = null; await _service.disconnect(); } Future write(Uint8List data) => _service.write(data); void dispose() { + _frameSubscription?.cancel(); _service.dispose(); } } diff --git a/lib/l10n/app_bg.arb b/lib/l10n/app_bg.arb index fa84e50d..c5acba9f 100644 --- a/lib/l10n/app_bg.arb +++ b/lib/l10n/app_bg.arb @@ -1881,7 +1881,6 @@ "tcpPortLabel": "Пристанище", "tcpPortHint": "5000", "tcpStatus_notConnected": "Въведете крайната точка и свържете се.", - "tcpStatus_connecting": "Свързване към TCP крайния пункт...", "tcpStatus_connectingTo": "Свързване към {endpoint}...", "tcpErrorHostRequired": "Необходим е IP адрес.", "tcpErrorPortInvalid": "Портът трябва да бъде между 1 и 65535.", diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index eae33e5d..2eed9223 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -1906,10 +1906,9 @@ "connectionChoiceTcpLabel": "TCP", "tcpHostHint": "192.168.40.10", "tcpScreenTitle": "Verbinden über TCP", - "tcpPortLabel": "Hafen", + "tcpPortLabel": "Port", "tcpPortHint": "5000", "tcpStatus_notConnected": "Geben Sie den Endpunkt ein und verbinden Sie sich.", - "tcpStatus_connecting": "Verbindung zum TCP-Endpunkt hergestellt...", "tcpStatus_connectingTo": "Verbindung zu {endpoint}...", "tcpErrorHostRequired": "Eine IP-Adresse ist erforderlich.", "tcpErrorPortInvalid": "Die Portnummer muss zwischen 1 und 65535 liegen.", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 0acf9a59..58569e4f 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -56,7 +56,6 @@ "tcpPortLabel": "Port", "tcpPortHint": "5000", "tcpStatus_notConnected": "Enter endpoint and connect", - "tcpStatus_connecting": "Connecting to TCP endpoint...", "tcpStatus_connectingTo": "Connecting to {endpoint}...", "@tcpStatus_connectingTo": { "placeholders": { diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 974e2c3c..25e03457 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -1909,7 +1909,6 @@ "tcpPortLabel": "Puerto", "tcpPortHint": "5000", "tcpStatus_notConnected": "Ingrese la dirección final y conecte.", - "tcpStatus_connecting": "Conectándose al punto final TCP...", "tcpStatus_connectingTo": "Conectándose a {endpoint}...", "tcpErrorHostRequired": "Se requiere la dirección IP.", "tcpErrorPortInvalid": "El puerto debe estar entre 1 y 65535.", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 3c36a961..5d586f4a 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -1881,7 +1881,6 @@ "tcpPortLabel": "Port", "tcpPortHint": "5000", "tcpStatus_notConnected": "Entrez l'adresse de destination et connectez-vous.", - "tcpStatus_connecting": "Connexion au point de terminaison TCP...", "tcpStatus_connectingTo": "Connexion à {endpoint}...", "tcpErrorHostRequired": "Une adresse IP est obligatoire.", "tcpErrorPortInvalid": "La taille du port doit être comprise entre 1 et 65535.", diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb index 8a1c29d7..4de9e9d1 100644 --- a/lib/l10n/app_it.arb +++ b/lib/l10n/app_it.arb @@ -1878,10 +1878,9 @@ "tcpHostHint": "192.168.40.10", "connectionChoiceTcpLabel": "TCP", "tcpScreenTitle": "Stabilire una connessione tramite TCP", - "tcpPortLabel": "Porto", + "tcpPortLabel": "Porta", "tcpPortHint": "5000", "tcpStatus_notConnected": "Inserisci l'endpoint e connettiti.", - "tcpStatus_connecting": "Connessione al punto finale TCP...", "tcpStatus_connectingTo": "Connessione a {endpoint}...", "tcpErrorHostRequired": "È necessario fornire un indirizzo IP.", "tcpErrorPortInvalid": "La dimensione della porta deve essere compresa tra 1 e 65535.", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 36be9552..3690bf2c 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -376,12 +376,6 @@ abstract class AppLocalizations { /// **'Enter endpoint and connect'** String get tcpStatus_notConnected; - /// No description provided for @tcpStatus_connecting. - /// - /// In en, this message translates to: - /// **'Connecting to TCP endpoint...'** - String get tcpStatus_connecting; - /// No description provided for @tcpStatus_connectingTo. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_bg.dart b/lib/l10n/app_localizations_bg.dart index fd61be07..785f3732 100644 --- a/lib/l10n/app_localizations_bg.dart +++ b/lib/l10n/app_localizations_bg.dart @@ -138,9 +138,6 @@ class AppLocalizationsBg extends AppLocalizations { @override String get tcpStatus_notConnected => 'Въведете крайната точка и свържете се.'; - @override - String get tcpStatus_connecting => 'Свързване към TCP крайния пункт...'; - @override String tcpStatus_connectingTo(String endpoint) { return 'Свързване към $endpoint...'; diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 04002374..708e0abd 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -130,7 +130,7 @@ class AppLocalizationsDe extends AppLocalizations { String get tcpHostHint => '192.168.40.10'; @override - String get tcpPortLabel => 'Hafen'; + String get tcpPortLabel => 'Port'; @override String get tcpPortHint => '5000'; @@ -139,10 +139,6 @@ class AppLocalizationsDe extends AppLocalizations { String get tcpStatus_notConnected => 'Geben Sie den Endpunkt ein und verbinden Sie sich.'; - @override - String get tcpStatus_connecting => - 'Verbindung zum TCP-Endpunkt hergestellt...'; - @override String tcpStatus_connectingTo(String endpoint) { return 'Verbindung zu $endpoint...'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index d01dc366..4938dd4b 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -138,9 +138,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get tcpStatus_notConnected => 'Enter endpoint and connect'; - @override - String get tcpStatus_connecting => 'Connecting to TCP endpoint...'; - @override String tcpStatus_connectingTo(String endpoint) { return 'Connecting to $endpoint...'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index 7e5f8064..2d4e2fb2 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -138,9 +138,6 @@ class AppLocalizationsEs extends AppLocalizations { @override String get tcpStatus_notConnected => 'Ingrese la dirección final y conecte.'; - @override - String get tcpStatus_connecting => 'Conectándose al punto final TCP...'; - @override String tcpStatus_connectingTo(String endpoint) { return 'Conectándose a $endpoint...'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index c7a225ba..28bbab3b 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -139,9 +139,6 @@ class AppLocalizationsFr extends AppLocalizations { String get tcpStatus_notConnected => 'Entrez l\'adresse de destination et connectez-vous.'; - @override - String get tcpStatus_connecting => 'Connexion au point de terminaison TCP...'; - @override String tcpStatus_connectingTo(String endpoint) { return 'Connexion à $endpoint...'; diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index 121c2f85..b510bc1e 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -130,7 +130,7 @@ class AppLocalizationsIt extends AppLocalizations { String get tcpHostHint => '192.168.40.10'; @override - String get tcpPortLabel => 'Porto'; + String get tcpPortLabel => 'Porta'; @override String get tcpPortHint => '5000'; @@ -138,9 +138,6 @@ class AppLocalizationsIt extends AppLocalizations { @override String get tcpStatus_notConnected => 'Inserisci l\'endpoint e connettiti.'; - @override - String get tcpStatus_connecting => 'Connessione al punto finale TCP...'; - @override String tcpStatus_connectingTo(String endpoint) { return 'Connessione a $endpoint...'; diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index 48fe3790..7c054dde 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -130,7 +130,7 @@ class AppLocalizationsNl extends AppLocalizations { String get tcpHostHint => '192.168.40.10'; @override - String get tcpPortLabel => 'Haven'; + String get tcpPortLabel => 'Poort'; @override String get tcpPortHint => '5000'; @@ -138,9 +138,6 @@ class AppLocalizationsNl extends AppLocalizations { @override String get tcpStatus_notConnected => 'Voer het eindpunt in en verbind'; - @override - String get tcpStatus_connecting => 'Verbinding maken met TCP-eindpunt...'; - @override String tcpStatus_connectingTo(String endpoint) { return 'Verbinding maken met $endpoint...'; diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 97681be7..dec65831 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -138,9 +138,6 @@ class AppLocalizationsPl extends AppLocalizations { @override String get tcpStatus_notConnected => 'Wprowadź adres URL i połącz'; - @override - String get tcpStatus_connecting => 'Połączenie z punktem TCP...'; - @override String tcpStatus_connectingTo(String endpoint) { return 'Połączenie z $endpoint...'; diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart index eb2fc7f6..4d8d20e4 100644 --- a/lib/l10n/app_localizations_pt.dart +++ b/lib/l10n/app_localizations_pt.dart @@ -130,7 +130,7 @@ class AppLocalizationsPt extends AppLocalizations { String get tcpHostHint => '192.168.40.10'; @override - String get tcpPortLabel => 'Porto'; + String get tcpPortLabel => 'Porta'; @override String get tcpPortHint => '5000'; @@ -138,10 +138,6 @@ class AppLocalizationsPt extends AppLocalizations { @override String get tcpStatus_notConnected => 'Insira o endereço final e conecte-se.'; - @override - String get tcpStatus_connecting => - 'Conectando ao ponto de extremidade TCP...'; - @override String tcpStatus_connectingTo(String endpoint) { return 'Conectando a $endpoint...'; diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index 1a28e8d5..60aa486e 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -138,9 +138,6 @@ class AppLocalizationsRu extends AppLocalizations { @override String get tcpStatus_notConnected => 'Введите адрес и подключитесь.'; - @override - String get tcpStatus_connecting => 'Установление соединения с TCP-портом...'; - @override String tcpStatus_connectingTo(String endpoint) { return 'Подключение к $endpoint...'; diff --git a/lib/l10n/app_localizations_sk.dart b/lib/l10n/app_localizations_sk.dart index eb53c401..4e11719d 100644 --- a/lib/l10n/app_localizations_sk.dart +++ b/lib/l10n/app_localizations_sk.dart @@ -130,17 +130,14 @@ class AppLocalizationsSk extends AppLocalizations { String get tcpHostHint => '192.168.40.10'; @override - String get tcpPortLabel => 'Pri항'; + String get tcpPortLabel => 'Port'; @override - String get tcpPortHint => '5 000'; + String get tcpPortHint => '5000'; @override String get tcpStatus_notConnected => 'Zadajte cieľovú adresu a pripojte sa.'; - @override - String get tcpStatus_connecting => 'Pripojenie k TCP endpointu...'; - @override String tcpStatus_connectingTo(String endpoint) { return 'Pripojenie k $endpoint...'; diff --git a/lib/l10n/app_localizations_sl.dart b/lib/l10n/app_localizations_sl.dart index be872489..f967db4e 100644 --- a/lib/l10n/app_localizations_sl.dart +++ b/lib/l10n/app_localizations_sl.dart @@ -130,7 +130,7 @@ class AppLocalizationsSl extends AppLocalizations { String get tcpHostHint => '192.168.40.10'; @override - String get tcpPortLabel => 'Pril'; + String get tcpPortLabel => 'Vrata'; @override String get tcpPortHint => '5000'; @@ -138,9 +138,6 @@ class AppLocalizationsSl extends AppLocalizations { @override String get tcpStatus_notConnected => 'Vnesite končni naslov in se povežite'; - @override - String get tcpStatus_connecting => 'Povezava z TCP koncem...'; - @override String tcpStatus_connectingTo(String endpoint) { return 'Povezava z $endpoint...'; diff --git a/lib/l10n/app_localizations_sv.dart b/lib/l10n/app_localizations_sv.dart index 55ac5c6b..200bdbe4 100644 --- a/lib/l10n/app_localizations_sv.dart +++ b/lib/l10n/app_localizations_sv.dart @@ -130,7 +130,7 @@ class AppLocalizationsSv extends AppLocalizations { String get tcpHostHint => '192.168.40.10'; @override - String get tcpPortLabel => 'Hamn'; + String get tcpPortLabel => 'Port'; @override String get tcpPortHint => '5000'; @@ -138,9 +138,6 @@ class AppLocalizationsSv extends AppLocalizations { @override String get tcpStatus_notConnected => 'Ange slutpunkt och anslut'; - @override - String get tcpStatus_connecting => 'Anslutning till TCP-slutpunkt...'; - @override String tcpStatus_connectingTo(String endpoint) { return 'Anslutning till $endpoint...'; diff --git a/lib/l10n/app_localizations_uk.dart b/lib/l10n/app_localizations_uk.dart index 8466adf1..8dfe1231 100644 --- a/lib/l10n/app_localizations_uk.dart +++ b/lib/l10n/app_localizations_uk.dart @@ -138,9 +138,6 @@ class AppLocalizationsUk extends AppLocalizations { @override String get tcpStatus_notConnected => 'Введіть кінцеву точку та підключіться'; - @override - String get tcpStatus_connecting => 'Підключення до TCP-кінцевої точки...'; - @override String tcpStatus_connectingTo(String endpoint) { return 'Підключення до $endpoint...'; diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index 85026c29..ecd6813e 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -130,7 +130,7 @@ class AppLocalizationsZh extends AppLocalizations { String get tcpHostHint => '192.168.40.10'; @override - String get tcpPortLabel => '港'; + String get tcpPortLabel => '端口'; @override String get tcpPortHint => '5000'; @@ -138,9 +138,6 @@ class AppLocalizationsZh extends AppLocalizations { @override String get tcpStatus_notConnected => '输入目标地址,然后连接'; - @override - String get tcpStatus_connecting => '连接到 TCP 终点...'; - @override String tcpStatus_connectingTo(String endpoint) { return '连接到 $endpoint...'; diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb index 51c045cc..d38fb4ce 100644 --- a/lib/l10n/app_nl.arb +++ b/lib/l10n/app_nl.arb @@ -1878,10 +1878,9 @@ "tcpHostLabel": "IP-adres", "tcpHostHint": "192.168.40.10", "connectionChoiceTcpLabel": "TCP", - "tcpPortLabel": "Haven", + "tcpPortLabel": "Poort", "tcpPortHint": "5000", "tcpStatus_notConnected": "Voer het eindpunt in en verbind", - "tcpStatus_connecting": "Verbinding maken met TCP-eindpunt...", "tcpStatus_connectingTo": "Verbinding maken met {endpoint}...", "tcpErrorHostRequired": "Een IP-adres is vereist.", "tcpErrorPortInvalid": "De poortwaarde moet tussen 1 en 65535 liggen.", diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index e711a991..9dc3b33a 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -1881,7 +1881,6 @@ "tcpPortLabel": "Port", "tcpPortHint": "5000", "tcpStatus_notConnected": "Wprowadź adres URL i połącz", - "tcpStatus_connecting": "Połączenie z punktem TCP...", "tcpStatus_connectingTo": "Połączenie z {endpoint}...", "tcpErrorHostRequired": "Wymagana jest adresa IP.", "tcpErrorPortInvalid": "Numer portu musi mieścić się w zakresie od 1 do 65535.", diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index 6c1ad9e7..cded31f8 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -1878,10 +1878,9 @@ "connectionChoiceTcpLabel": "TCP", "tcpScreenTitle": "Estabelecer conexão via TCP", "tcpHostHint": "192.168.40.10", - "tcpPortLabel": "Porto", + "tcpPortLabel": "Porta", "tcpPortHint": "5000", "tcpStatus_notConnected": "Insira o endereço final e conecte-se.", - "tcpStatus_connecting": "Conectando ao ponto de extremidade TCP...", "tcpStatus_connectingTo": "Conectando a {endpoint}...", "tcpErrorHostRequired": "É necessário fornecer um endereço IP.", "tcpErrorPortInvalid": "O valor do porto deve estar entre 1 e 65535.", diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb index d90c387b..43e1b9a8 100644 --- a/lib/l10n/app_ru.arb +++ b/lib/l10n/app_ru.arb @@ -1121,7 +1121,6 @@ "tcpPortLabel": "Порт", "tcpPortHint": "5000", "tcpStatus_notConnected": "Введите адрес и подключитесь.", - "tcpStatus_connecting": "Установление соединения с TCP-портом...", "tcpStatus_connectingTo": "Подключение к {endpoint}...", "tcpErrorHostRequired": "Необходимо указать IP-адрес.", "tcpErrorPortInvalid": "Порт должен находиться в диапазоне от 1 до 65535.", diff --git a/lib/l10n/app_sk.arb b/lib/l10n/app_sk.arb index ae558433..f03d276e 100644 --- a/lib/l10n/app_sk.arb +++ b/lib/l10n/app_sk.arb @@ -1878,10 +1878,9 @@ "tcpHostLabel": "IP adresa", "tcpScreenTitle": "Spojte sa pomocou protokolu TCP", "connectionChoiceTcpLabel": "TCP", - "tcpPortLabel": "Pri항", - "tcpPortHint": "5 000", + "tcpPortLabel": "Port", + "tcpPortHint": "5000", "tcpStatus_notConnected": "Zadajte cieľovú adresu a pripojte sa.", - "tcpStatus_connecting": "Pripojenie k TCP endpointu...", "tcpStatus_connectingTo": "Pripojenie k {endpoint}...", "tcpErrorHostRequired": "Je potrebné zadať IP adresu.", "tcpErrorPortInvalid": "Číslo portu musí byť medzi 1 a 65535.", diff --git a/lib/l10n/app_sl.arb b/lib/l10n/app_sl.arb index 6ad24499..4a4b5cb1 100644 --- a/lib/l10n/app_sl.arb +++ b/lib/l10n/app_sl.arb @@ -1878,10 +1878,9 @@ "tcpHostLabel": "IP naslov", "tcpHostHint": "192.168.40.10", "tcpScreenTitle": "Komunicirajte preko protokola TCP", - "tcpPortLabel": "Pril", + "tcpPortLabel": "Vrata", "tcpPortHint": "5000", "tcpStatus_notConnected": "Vnesite končni naslov in se povežite", - "tcpStatus_connecting": "Povezava z TCP koncem...", "tcpStatus_connectingTo": "Povezava z {endpoint}...", "tcpErrorHostRequired": "Potrebna je IP-naslov.", "tcpErrorPortInvalid": "Port mora biti med 1 in 65535.", diff --git a/lib/l10n/app_sv.arb b/lib/l10n/app_sv.arb index b70049f5..6a33e117 100644 --- a/lib/l10n/app_sv.arb +++ b/lib/l10n/app_sv.arb @@ -1878,10 +1878,9 @@ "tcpHostLabel": "IP-adress", "tcpScreenTitle": "Anslut via TCP", "connectionChoiceTcpLabel": "TCP", - "tcpPortLabel": "Hamn", + "tcpPortLabel": "Port", "tcpPortHint": "5000", "tcpStatus_notConnected": "Ange slutpunkt och anslut", - "tcpStatus_connecting": "Anslutning till TCP-slutpunkt...", "tcpStatus_connectingTo": "Anslutning till {endpoint}...", "tcpErrorHostRequired": "IP-adress krävs.", "tcpErrorPortInvalid": "Porten måste vara mellan 1 och 65535.", diff --git a/lib/l10n/app_uk.arb b/lib/l10n/app_uk.arb index 9dcf88f9..c179ca35 100644 --- a/lib/l10n/app_uk.arb +++ b/lib/l10n/app_uk.arb @@ -1881,7 +1881,6 @@ "tcpPortLabel": "Порт", "tcpPortHint": "5000", "tcpStatus_notConnected": "Введіть кінцеву точку та підключіться", - "tcpStatus_connecting": "Підключення до TCP-кінцевої точки...", "tcpStatus_connectingTo": "Підключення до {endpoint}...", "tcpErrorHostRequired": "Необхідно вказати IP-адресу.", "tcpErrorPortInvalid": "Порт повинен бути в межах від 1 до 65535.", diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 199f85c9..cac4b797 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -1883,10 +1883,9 @@ "tcpHostHint": "192.168.40.10", "tcpScreenTitle": "通过 TCP 连接", "connectionChoiceTcpLabel": "TCP", - "tcpPortLabel": "港", + "tcpPortLabel": "端口", "tcpPortHint": "5000", "tcpStatus_notConnected": "输入目标地址,然后连接", - "tcpStatus_connecting": "连接到 TCP 终点...", "tcpStatus_connectingTo": "连接到 {endpoint}...", "tcpErrorHostRequired": "需要提供IP地址。", "tcpErrorPortInvalid": "端口号必须在 1 到 65535 之间。", diff --git a/lib/screens/tcp_screen.dart b/lib/screens/tcp_screen.dart index 55bec20a..cf873825 100644 --- a/lib/screens/tcp_screen.dart +++ b/lib/screens/tcp_screen.dart @@ -27,7 +27,7 @@ class _TcpScreenState extends State { @override void initState() { super.initState(); - _hostController = TextEditingController(text: '192.168.40.10'); + _hostController = TextEditingController(); _portController = TextEditingController(text: '5000'); _connector = context.read(); @@ -81,6 +81,9 @@ class _TcpScreenState extends State { final isConnecting = connector.state == MeshCoreConnectionState.connecting && connector.activeTransport == MeshCoreTransportType.tcp; + final isButtonDisabled = + isConnecting || + connector.state == MeshCoreConnectionState.scanning; return Column( children: [ _buildStatusBar(context, connector), @@ -112,7 +115,8 @@ class _TcpScreenState extends State { ), const SizedBox(height: 16), FilledButton.icon( - onPressed: isConnecting ? null : _connectTcp, + key: const Key('tcp_connect_button'), + onPressed: isButtonDisabled ? null : _connectTcp, icon: isConnecting ? const SizedBox( width: 18, @@ -153,6 +157,7 @@ class _TcpScreenState extends State { ); }, heroTag: 'tcp_usb_action', + extendedPadding: const EdgeInsets.symmetric(horizontal: 12), icon: const Icon(Icons.usb), label: Text(context.l10n.connectionChoiceUsbLabel), ), @@ -162,6 +167,7 @@ class _TcpScreenState extends State { Navigator.of(context).maybePop(); }, heroTag: 'tcp_ble_action', + extendedPadding: const EdgeInsets.symmetric(horizontal: 12), icon: const Icon(Icons.bluetooth), label: Text(context.l10n.connectionChoiceBluetoothLabel), ), diff --git a/test/screens/tcp_flow_test.dart b/test/screens/tcp_flow_test.dart index 5c240f4b..725388ab 100644 --- a/test/screens/tcp_flow_test.dart +++ b/test/screens/tcp_flow_test.dart @@ -93,7 +93,7 @@ void main() { final l10n = AppLocalizations.of(context); await tester.enterText(find.byType(TextField).first, ''); - await tester.tap(find.widgetWithText(FilledButton, 'Connect')); + await tester.tap(find.byKey(const Key('tcp_connect_button'))); await tester.pumpAndSettle(); expect(find.text(l10n.tcpErrorHostRequired), findsOneWidget); @@ -101,7 +101,7 @@ void main() { await tester.enterText(find.byType(TextField).first, '192.168.1.50'); await tester.enterText(find.byType(TextField).at(1), '99999'); - await tester.tap(find.widgetWithText(FilledButton, 'Connect')); + await tester.tap(find.byKey(const Key('tcp_connect_button'))); await tester.pumpAndSettle(); expect(connector.connectTcpCalls, 0); @@ -135,7 +135,7 @@ void main() { await tester.pump(const Duration(milliseconds: 60)); }); - testWidgets('TcpScreen allows connect while connector is scanning', ( + testWidgets('TcpScreen disables connect button while connector is scanning', ( tester, ) async { final connector = _FakeMeshCoreConnector() @@ -150,12 +150,11 @@ void main() { ); await tester.pumpAndSettle(); - await tester.tap(find.widgetWithText(FilledButton, 'Connect')); - await tester.pumpAndSettle(); - - expect(connector.connectTcpCalls, 1); - expect(connector.lastHost, '192.168.40.10'); - expect(connector.lastPort, 5000); + final button = tester.widget( + find.byKey(const Key('tcp_connect_button')), + ); + expect(button.onPressed, isNull); + expect(connector.connectTcpCalls, 0); }); testWidgets('TcpScreen narrow width long status text does not overflow', (