refactor(tcp): promote MeshCoreTcpConnector, fix translations, harden UI

- 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.
chore/offband-rebrand
Zach 4 months ago
parent 1ad5db27ca
commit db935a7454

@ -116,8 +116,7 @@ class MeshCoreConnector extends ChangeNotifier {
bool _manualDisconnect = false; bool _manualDisconnect = false;
final MeshCoreUsbManager _usbManager = MeshCoreUsbManager(); final MeshCoreUsbManager _usbManager = MeshCoreUsbManager();
StreamSubscription<Uint8List>? _usbFrameSubscription; StreamSubscription<Uint8List>? _usbFrameSubscription;
final MeshCoreTcpManager _tcpManager = MeshCoreTcpManager(); final MeshCoreTcpConnector _tcpConnector = MeshCoreTcpConnector();
StreamSubscription<Uint8List>? _tcpFrameSubscription;
MeshCoreTransportType _activeTransport = MeshCoreTransportType.bluetooth; MeshCoreTransportType _activeTransport = MeshCoreTransportType.bluetooth;
final List<ScanResult> _scanResults = []; final List<ScanResult> _scanResults = [];
@ -257,7 +256,7 @@ class MeshCoreConnector extends ChangeNotifier {
bool get isUsbTransportConnected => bool get isUsbTransportConnected =>
_state == MeshCoreConnectionState.connected && _state == MeshCoreConnectionState.connected &&
_activeTransport == MeshCoreTransportType.usb; _activeTransport == MeshCoreTransportType.usb;
String? get activeTcpEndpoint => _tcpManager.activeEndpoint; String? get activeTcpEndpoint => _tcpConnector.activeEndpoint;
bool get isTcpTransportConnected => bool get isTcpTransportConnected =>
_state == MeshCoreConnectionState.connected && _state == MeshCoreConnectionState.connected &&
_activeTransport == MeshCoreTransportType.tcp; _activeTransport == MeshCoreTransportType.tcp;
@ -666,7 +665,7 @@ class MeshCoreConnector extends ChangeNotifier {
_appDebugLogService = appDebugLogService; _appDebugLogService = appDebugLogService;
_backgroundService = backgroundService; _backgroundService = backgroundService;
_usbManager.setDebugLogService(_appDebugLogService); _usbManager.setDebugLogService(_appDebugLogService);
_tcpManager.setDebugLogService(_appDebugLogService); _tcpConnector.setDebugLogService(_appDebugLogService);
// Initialize notification service // Initialize notification service
_notificationService.initialize(); _notificationService.initialize();
@ -1002,22 +1001,21 @@ class MeshCoreConnector extends ChangeNotifier {
await disconnect(manual: false); await disconnect(manual: false);
return; return;
} }
if (_tcpManager.isConnected) { if (_tcpConnector.isConnected) {
await _tcpManager.disconnect(); await _tcpConnector.disconnect();
} }
} }
await _tcpFrameSubscription?.cancel(); await _tcpConnector.cancelFrameSubscription();
_tcpFrameSubscription = null; await _tcpConnector.connect(host: host, port: port);
await _tcpManager.connect(host: host, port: port);
final isTcpConnectCancelled = final isTcpConnectCancelled =
_activeTransport != MeshCoreTransportType.tcp || _activeTransport != MeshCoreTransportType.tcp ||
_state != MeshCoreConnectionState.connecting || _state != MeshCoreConnectionState.connecting ||
!_tcpManager.isConnected; !_tcpConnector.isConnected;
if (isTcpConnectCancelled) { if (isTcpConnectCancelled) {
await handleTcpConnectAbort( await handleTcpConnectAbort(
message: message:
'connectTcp aborted before handshake: state=$_state transport=$_activeTransport connected=${_tcpManager.isConnected}', 'connectTcp aborted before handshake: state=$_state transport=$_activeTransport connected=${_tcpConnector.isConnected}',
); );
return; return;
} }
@ -1027,16 +1025,16 @@ class MeshCoreConnector extends ChangeNotifier {
final isTcpConnectCancelledAfterDelay = final isTcpConnectCancelledAfterDelay =
_activeTransport != MeshCoreTransportType.tcp || _activeTransport != MeshCoreTransportType.tcp ||
_state != MeshCoreConnectionState.connecting || _state != MeshCoreConnectionState.connecting ||
!_tcpManager.isConnected; !_tcpConnector.isConnected;
if (isTcpConnectCancelledAfterDelay) { if (isTcpConnectCancelledAfterDelay) {
await handleTcpConnectAbort( await handleTcpConnectAbort(
message: 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; return;
} }
_tcpFrameSubscription = _tcpManager.frameStream.listen( _tcpConnector.listenFrames(
_handleFrame, onFrame: _handleFrame,
onError: (error, stackTrace) { onError: (error, stackTrace) {
_appDebugLogService?.error('TCP transport error: $error', tag: 'TCP'); _appDebugLogService?.error('TCP transport error: $error', tag: 'TCP');
unawaited(disconnect(manual: false)); unawaited(disconnect(manual: false));
@ -1073,7 +1071,7 @@ class MeshCoreConnector extends ChangeNotifier {
manualDisconnect: _manualDisconnect, manualDisconnect: _manualDisconnect,
state: _state, state: _state,
activeTransport: _activeTransport, activeTransport: _activeTransport,
tcpManagerConnected: _tcpManager.isConnected, tcpManagerConnected: _tcpConnector.isConnected,
); );
if (tcpConnectCancelledBeforeHandshake) { if (tcpConnectCancelledBeforeHandshake) {
_appDebugLogService?.info( _appDebugLogService?.info(
@ -1445,9 +1443,7 @@ class MeshCoreConnector extends ChangeNotifier {
await _usbFrameSubscription?.cancel(); await _usbFrameSubscription?.cancel();
_usbFrameSubscription = null; _usbFrameSubscription = null;
await _usbManager.disconnect(); await _usbManager.disconnect();
await _tcpFrameSubscription?.cancel(); await _tcpConnector.disconnect();
_tcpFrameSubscription = null;
await _tcpManager.disconnect();
await _notifySubscription?.cancel(); await _notifySubscription?.cancel();
_notifySubscription = null; _notifySubscription = null;
@ -1530,7 +1526,7 @@ class MeshCoreConnector extends ChangeNotifier {
if (_activeTransport == MeshCoreTransportType.usb) { if (_activeTransport == MeshCoreTransportType.usb) {
await _usbManager.write(data); await _usbManager.write(data);
} else if (_activeTransport == MeshCoreTransportType.tcp) { } else if (_activeTransport == MeshCoreTransportType.tcp) {
await _tcpManager.write(data); await _tcpConnector.write(data);
} else { } else {
if (_rxCharacteristic == null) { if (_rxCharacteristic == null) {
throw Exception("MeshCore RX characteristic not available"); throw Exception("MeshCore RX characteristic not available");
@ -4484,14 +4480,13 @@ class MeshCoreConnector extends ChangeNotifier {
_scanSubscription?.cancel(); _scanSubscription?.cancel();
_connectionSubscription?.cancel(); _connectionSubscription?.cancel();
_usbFrameSubscription?.cancel(); _usbFrameSubscription?.cancel();
_tcpFrameSubscription?.cancel();
_notifySubscription?.cancel(); _notifySubscription?.cancel();
_notifyListenersTimer?.cancel(); _notifyListenersTimer?.cancel();
_reconnectTimer?.cancel(); _reconnectTimer?.cancel();
_batteryPollTimer?.cancel(); _batteryPollTimer?.cancel();
_receivedFramesController.close(); _receivedFramesController.close();
_usbManager.dispose(); _usbManager.dispose();
_tcpManager.dispose(); _tcpConnector.dispose();
// Flush pending unread writes before disposal // Flush pending unread writes before disposal
_unreadStore.flush(); _unreadStore.flush();

@ -1,34 +1,70 @@
import 'dart:async';
import 'dart:typed_data'; import 'dart:typed_data';
import '../services/app_debug_log_service.dart'; import '../services/app_debug_log_service.dart';
import '../services/tcp_transport_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(); final TcpTransportService _service = TcpTransportService();
AppDebugLogService? _debugLog; AppDebugLogService? _debugLog;
StreamSubscription<Uint8List>? _frameSubscription;
// --- Getters ---
String? get activeEndpoint => _service.activeEndpoint; String? get activeEndpoint => _service.activeEndpoint;
bool get isConnected => _service.isConnected; bool get isConnected => _service.isConnected;
Stream<Uint8List> get frameStream => _service.frameStream;
// --- Configuration ---
void setDebugLogService(AppDebugLogService? service) { void setDebugLogService(AppDebugLogService? service) {
_debugLog = service; _debugLog = service;
_service.setDebugLogService(service); _service.setDebugLogService(service);
} }
// --- Connection lifecycle ---
Future<void> connect({required String host, required int port}) async { Future<void> 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); await _service.connect(host: host, port: port);
_debugLog?.info(
'TcpConnector.connect done, endpoint=${_service.activeEndpoint}',
tag: 'TCP',
);
}
StreamSubscription<Uint8List> 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<void> cancelFrameSubscription() async {
await _frameSubscription?.cancel();
_frameSubscription = null;
} }
Future<void> disconnect() async { Future<void> 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(); await _service.disconnect();
} }
Future<void> write(Uint8List data) => _service.write(data); Future<void> write(Uint8List data) => _service.write(data);
void dispose() { void dispose() {
_frameSubscription?.cancel();
_service.dispose(); _service.dispose();
} }
} }

@ -1881,7 +1881,6 @@
"tcpPortLabel": "Пристанище", "tcpPortLabel": "Пристанище",
"tcpPortHint": "5000", "tcpPortHint": "5000",
"tcpStatus_notConnected": "Въведете крайната точка и свържете се.", "tcpStatus_notConnected": "Въведете крайната точка и свържете се.",
"tcpStatus_connecting": "Свързване към TCP крайния пункт...",
"tcpStatus_connectingTo": "Свързване към {endpoint}...", "tcpStatus_connectingTo": "Свързване към {endpoint}...",
"tcpErrorHostRequired": "Необходим е IP адрес.", "tcpErrorHostRequired": "Необходим е IP адрес.",
"tcpErrorPortInvalid": "Портът трябва да бъде между 1 и 65535.", "tcpErrorPortInvalid": "Портът трябва да бъде между 1 и 65535.",

@ -1906,10 +1906,9 @@
"connectionChoiceTcpLabel": "TCP", "connectionChoiceTcpLabel": "TCP",
"tcpHostHint": "192.168.40.10", "tcpHostHint": "192.168.40.10",
"tcpScreenTitle": "Verbinden über TCP", "tcpScreenTitle": "Verbinden über TCP",
"tcpPortLabel": "Hafen", "tcpPortLabel": "Port",
"tcpPortHint": "5000", "tcpPortHint": "5000",
"tcpStatus_notConnected": "Geben Sie den Endpunkt ein und verbinden Sie sich.", "tcpStatus_notConnected": "Geben Sie den Endpunkt ein und verbinden Sie sich.",
"tcpStatus_connecting": "Verbindung zum TCP-Endpunkt hergestellt...",
"tcpStatus_connectingTo": "Verbindung zu {endpoint}...", "tcpStatus_connectingTo": "Verbindung zu {endpoint}...",
"tcpErrorHostRequired": "Eine IP-Adresse ist erforderlich.", "tcpErrorHostRequired": "Eine IP-Adresse ist erforderlich.",
"tcpErrorPortInvalid": "Die Portnummer muss zwischen 1 und 65535 liegen.", "tcpErrorPortInvalid": "Die Portnummer muss zwischen 1 und 65535 liegen.",

@ -56,7 +56,6 @@
"tcpPortLabel": "Port", "tcpPortLabel": "Port",
"tcpPortHint": "5000", "tcpPortHint": "5000",
"tcpStatus_notConnected": "Enter endpoint and connect", "tcpStatus_notConnected": "Enter endpoint and connect",
"tcpStatus_connecting": "Connecting to TCP endpoint...",
"tcpStatus_connectingTo": "Connecting to {endpoint}...", "tcpStatus_connectingTo": "Connecting to {endpoint}...",
"@tcpStatus_connectingTo": { "@tcpStatus_connectingTo": {
"placeholders": { "placeholders": {

@ -1909,7 +1909,6 @@
"tcpPortLabel": "Puerto", "tcpPortLabel": "Puerto",
"tcpPortHint": "5000", "tcpPortHint": "5000",
"tcpStatus_notConnected": "Ingrese la dirección final y conecte.", "tcpStatus_notConnected": "Ingrese la dirección final y conecte.",
"tcpStatus_connecting": "Conectándose al punto final TCP...",
"tcpStatus_connectingTo": "Conectándose a {endpoint}...", "tcpStatus_connectingTo": "Conectándose a {endpoint}...",
"tcpErrorHostRequired": "Se requiere la dirección IP.", "tcpErrorHostRequired": "Se requiere la dirección IP.",
"tcpErrorPortInvalid": "El puerto debe estar entre 1 y 65535.", "tcpErrorPortInvalid": "El puerto debe estar entre 1 y 65535.",

@ -1881,7 +1881,6 @@
"tcpPortLabel": "Port", "tcpPortLabel": "Port",
"tcpPortHint": "5000", "tcpPortHint": "5000",
"tcpStatus_notConnected": "Entrez l'adresse de destination et connectez-vous.", "tcpStatus_notConnected": "Entrez l'adresse de destination et connectez-vous.",
"tcpStatus_connecting": "Connexion au point de terminaison TCP...",
"tcpStatus_connectingTo": "Connexion à {endpoint}...", "tcpStatus_connectingTo": "Connexion à {endpoint}...",
"tcpErrorHostRequired": "Une adresse IP est obligatoire.", "tcpErrorHostRequired": "Une adresse IP est obligatoire.",
"tcpErrorPortInvalid": "La taille du port doit être comprise entre 1 et 65535.", "tcpErrorPortInvalid": "La taille du port doit être comprise entre 1 et 65535.",

@ -1878,10 +1878,9 @@
"tcpHostHint": "192.168.40.10", "tcpHostHint": "192.168.40.10",
"connectionChoiceTcpLabel": "TCP", "connectionChoiceTcpLabel": "TCP",
"tcpScreenTitle": "Stabilire una connessione tramite TCP", "tcpScreenTitle": "Stabilire una connessione tramite TCP",
"tcpPortLabel": "Porto", "tcpPortLabel": "Porta",
"tcpPortHint": "5000", "tcpPortHint": "5000",
"tcpStatus_notConnected": "Inserisci l'endpoint e connettiti.", "tcpStatus_notConnected": "Inserisci l'endpoint e connettiti.",
"tcpStatus_connecting": "Connessione al punto finale TCP...",
"tcpStatus_connectingTo": "Connessione a {endpoint}...", "tcpStatus_connectingTo": "Connessione a {endpoint}...",
"tcpErrorHostRequired": "È necessario fornire un indirizzo IP.", "tcpErrorHostRequired": "È necessario fornire un indirizzo IP.",
"tcpErrorPortInvalid": "La dimensione della porta deve essere compresa tra 1 e 65535.", "tcpErrorPortInvalid": "La dimensione della porta deve essere compresa tra 1 e 65535.",

@ -376,12 +376,6 @@ abstract class AppLocalizations {
/// **'Enter endpoint and connect'** /// **'Enter endpoint and connect'**
String get tcpStatus_notConnected; 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. /// No description provided for @tcpStatus_connectingTo.
/// ///
/// In en, this message translates to: /// In en, this message translates to:

@ -138,9 +138,6 @@ class AppLocalizationsBg extends AppLocalizations {
@override @override
String get tcpStatus_notConnected => 'Въведете крайната точка и свържете се.'; String get tcpStatus_notConnected => 'Въведете крайната точка и свържете се.';
@override
String get tcpStatus_connecting => 'Свързване към TCP крайния пункт...';
@override @override
String tcpStatus_connectingTo(String endpoint) { String tcpStatus_connectingTo(String endpoint) {
return 'Свързване към $endpoint...'; return 'Свързване към $endpoint...';

@ -130,7 +130,7 @@ class AppLocalizationsDe extends AppLocalizations {
String get tcpHostHint => '192.168.40.10'; String get tcpHostHint => '192.168.40.10';
@override @override
String get tcpPortLabel => 'Hafen'; String get tcpPortLabel => 'Port';
@override @override
String get tcpPortHint => '5000'; String get tcpPortHint => '5000';
@ -139,10 +139,6 @@ class AppLocalizationsDe extends AppLocalizations {
String get tcpStatus_notConnected => String get tcpStatus_notConnected =>
'Geben Sie den Endpunkt ein und verbinden Sie sich.'; 'Geben Sie den Endpunkt ein und verbinden Sie sich.';
@override
String get tcpStatus_connecting =>
'Verbindung zum TCP-Endpunkt hergestellt...';
@override @override
String tcpStatus_connectingTo(String endpoint) { String tcpStatus_connectingTo(String endpoint) {
return 'Verbindung zu $endpoint...'; return 'Verbindung zu $endpoint...';

@ -138,9 +138,6 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get tcpStatus_notConnected => 'Enter endpoint and connect'; String get tcpStatus_notConnected => 'Enter endpoint and connect';
@override
String get tcpStatus_connecting => 'Connecting to TCP endpoint...';
@override @override
String tcpStatus_connectingTo(String endpoint) { String tcpStatus_connectingTo(String endpoint) {
return 'Connecting to $endpoint...'; return 'Connecting to $endpoint...';

@ -138,9 +138,6 @@ class AppLocalizationsEs extends AppLocalizations {
@override @override
String get tcpStatus_notConnected => 'Ingrese la dirección final y conecte.'; String get tcpStatus_notConnected => 'Ingrese la dirección final y conecte.';
@override
String get tcpStatus_connecting => 'Conectándose al punto final TCP...';
@override @override
String tcpStatus_connectingTo(String endpoint) { String tcpStatus_connectingTo(String endpoint) {
return 'Conectándose a $endpoint...'; return 'Conectándose a $endpoint...';

@ -139,9 +139,6 @@ class AppLocalizationsFr extends AppLocalizations {
String get tcpStatus_notConnected => String get tcpStatus_notConnected =>
'Entrez l\'adresse de destination et connectez-vous.'; 'Entrez l\'adresse de destination et connectez-vous.';
@override
String get tcpStatus_connecting => 'Connexion au point de terminaison TCP...';
@override @override
String tcpStatus_connectingTo(String endpoint) { String tcpStatus_connectingTo(String endpoint) {
return 'Connexion à $endpoint...'; return 'Connexion à $endpoint...';

@ -130,7 +130,7 @@ class AppLocalizationsIt extends AppLocalizations {
String get tcpHostHint => '192.168.40.10'; String get tcpHostHint => '192.168.40.10';
@override @override
String get tcpPortLabel => 'Porto'; String get tcpPortLabel => 'Porta';
@override @override
String get tcpPortHint => '5000'; String get tcpPortHint => '5000';
@ -138,9 +138,6 @@ class AppLocalizationsIt extends AppLocalizations {
@override @override
String get tcpStatus_notConnected => 'Inserisci l\'endpoint e connettiti.'; String get tcpStatus_notConnected => 'Inserisci l\'endpoint e connettiti.';
@override
String get tcpStatus_connecting => 'Connessione al punto finale TCP...';
@override @override
String tcpStatus_connectingTo(String endpoint) { String tcpStatus_connectingTo(String endpoint) {
return 'Connessione a $endpoint...'; return 'Connessione a $endpoint...';

@ -130,7 +130,7 @@ class AppLocalizationsNl extends AppLocalizations {
String get tcpHostHint => '192.168.40.10'; String get tcpHostHint => '192.168.40.10';
@override @override
String get tcpPortLabel => 'Haven'; String get tcpPortLabel => 'Poort';
@override @override
String get tcpPortHint => '5000'; String get tcpPortHint => '5000';
@ -138,9 +138,6 @@ class AppLocalizationsNl extends AppLocalizations {
@override @override
String get tcpStatus_notConnected => 'Voer het eindpunt in en verbind'; String get tcpStatus_notConnected => 'Voer het eindpunt in en verbind';
@override
String get tcpStatus_connecting => 'Verbinding maken met TCP-eindpunt...';
@override @override
String tcpStatus_connectingTo(String endpoint) { String tcpStatus_connectingTo(String endpoint) {
return 'Verbinding maken met $endpoint...'; return 'Verbinding maken met $endpoint...';

@ -138,9 +138,6 @@ class AppLocalizationsPl extends AppLocalizations {
@override @override
String get tcpStatus_notConnected => 'Wprowadź adres URL i połącz'; String get tcpStatus_notConnected => 'Wprowadź adres URL i połącz';
@override
String get tcpStatus_connecting => 'Połączenie z punktem TCP...';
@override @override
String tcpStatus_connectingTo(String endpoint) { String tcpStatus_connectingTo(String endpoint) {
return 'Połączenie z $endpoint...'; return 'Połączenie z $endpoint...';

@ -130,7 +130,7 @@ class AppLocalizationsPt extends AppLocalizations {
String get tcpHostHint => '192.168.40.10'; String get tcpHostHint => '192.168.40.10';
@override @override
String get tcpPortLabel => 'Porto'; String get tcpPortLabel => 'Porta';
@override @override
String get tcpPortHint => '5000'; String get tcpPortHint => '5000';
@ -138,10 +138,6 @@ class AppLocalizationsPt extends AppLocalizations {
@override @override
String get tcpStatus_notConnected => 'Insira o endereço final e conecte-se.'; String get tcpStatus_notConnected => 'Insira o endereço final e conecte-se.';
@override
String get tcpStatus_connecting =>
'Conectando ao ponto de extremidade TCP...';
@override @override
String tcpStatus_connectingTo(String endpoint) { String tcpStatus_connectingTo(String endpoint) {
return 'Conectando a $endpoint...'; return 'Conectando a $endpoint...';

@ -138,9 +138,6 @@ class AppLocalizationsRu extends AppLocalizations {
@override @override
String get tcpStatus_notConnected => 'Введите адрес и подключитесь.'; String get tcpStatus_notConnected => 'Введите адрес и подключитесь.';
@override
String get tcpStatus_connecting => 'Установление соединения с TCP-портом...';
@override @override
String tcpStatus_connectingTo(String endpoint) { String tcpStatus_connectingTo(String endpoint) {
return 'Подключение к $endpoint...'; return 'Подключение к $endpoint...';

@ -130,17 +130,14 @@ class AppLocalizationsSk extends AppLocalizations {
String get tcpHostHint => '192.168.40.10'; String get tcpHostHint => '192.168.40.10';
@override @override
String get tcpPortLabel => 'Pri항'; String get tcpPortLabel => 'Port';
@override @override
String get tcpPortHint => '5 000'; String get tcpPortHint => '5000';
@override @override
String get tcpStatus_notConnected => 'Zadajte cieľovú adresu a pripojte sa.'; String get tcpStatus_notConnected => 'Zadajte cieľovú adresu a pripojte sa.';
@override
String get tcpStatus_connecting => 'Pripojenie k TCP endpointu...';
@override @override
String tcpStatus_connectingTo(String endpoint) { String tcpStatus_connectingTo(String endpoint) {
return 'Pripojenie k $endpoint...'; return 'Pripojenie k $endpoint...';

@ -130,7 +130,7 @@ class AppLocalizationsSl extends AppLocalizations {
String get tcpHostHint => '192.168.40.10'; String get tcpHostHint => '192.168.40.10';
@override @override
String get tcpPortLabel => 'Pril'; String get tcpPortLabel => 'Vrata';
@override @override
String get tcpPortHint => '5000'; String get tcpPortHint => '5000';
@ -138,9 +138,6 @@ class AppLocalizationsSl extends AppLocalizations {
@override @override
String get tcpStatus_notConnected => 'Vnesite končni naslov in se povežite'; String get tcpStatus_notConnected => 'Vnesite končni naslov in se povežite';
@override
String get tcpStatus_connecting => 'Povezava z TCP koncem...';
@override @override
String tcpStatus_connectingTo(String endpoint) { String tcpStatus_connectingTo(String endpoint) {
return 'Povezava z $endpoint...'; return 'Povezava z $endpoint...';

@ -130,7 +130,7 @@ class AppLocalizationsSv extends AppLocalizations {
String get tcpHostHint => '192.168.40.10'; String get tcpHostHint => '192.168.40.10';
@override @override
String get tcpPortLabel => 'Hamn'; String get tcpPortLabel => 'Port';
@override @override
String get tcpPortHint => '5000'; String get tcpPortHint => '5000';
@ -138,9 +138,6 @@ class AppLocalizationsSv extends AppLocalizations {
@override @override
String get tcpStatus_notConnected => 'Ange slutpunkt och anslut'; String get tcpStatus_notConnected => 'Ange slutpunkt och anslut';
@override
String get tcpStatus_connecting => 'Anslutning till TCP-slutpunkt...';
@override @override
String tcpStatus_connectingTo(String endpoint) { String tcpStatus_connectingTo(String endpoint) {
return 'Anslutning till $endpoint...'; return 'Anslutning till $endpoint...';

@ -138,9 +138,6 @@ class AppLocalizationsUk extends AppLocalizations {
@override @override
String get tcpStatus_notConnected => 'Введіть кінцеву точку та підключіться'; String get tcpStatus_notConnected => 'Введіть кінцеву точку та підключіться';
@override
String get tcpStatus_connecting => 'Підключення до TCP-кінцевої точки...';
@override @override
String tcpStatus_connectingTo(String endpoint) { String tcpStatus_connectingTo(String endpoint) {
return 'Підключення до $endpoint...'; return 'Підключення до $endpoint...';

@ -130,7 +130,7 @@ class AppLocalizationsZh extends AppLocalizations {
String get tcpHostHint => '192.168.40.10'; String get tcpHostHint => '192.168.40.10';
@override @override
String get tcpPortLabel => ''; String get tcpPortLabel => '端口';
@override @override
String get tcpPortHint => '5000'; String get tcpPortHint => '5000';
@ -138,9 +138,6 @@ class AppLocalizationsZh extends AppLocalizations {
@override @override
String get tcpStatus_notConnected => '输入目标地址,然后连接'; String get tcpStatus_notConnected => '输入目标地址,然后连接';
@override
String get tcpStatus_connecting => '连接到 TCP 终点...';
@override @override
String tcpStatus_connectingTo(String endpoint) { String tcpStatus_connectingTo(String endpoint) {
return '连接到 $endpoint...'; return '连接到 $endpoint...';

@ -1878,10 +1878,9 @@
"tcpHostLabel": "IP-adres", "tcpHostLabel": "IP-adres",
"tcpHostHint": "192.168.40.10", "tcpHostHint": "192.168.40.10",
"connectionChoiceTcpLabel": "TCP", "connectionChoiceTcpLabel": "TCP",
"tcpPortLabel": "Haven", "tcpPortLabel": "Poort",
"tcpPortHint": "5000", "tcpPortHint": "5000",
"tcpStatus_notConnected": "Voer het eindpunt in en verbind", "tcpStatus_notConnected": "Voer het eindpunt in en verbind",
"tcpStatus_connecting": "Verbinding maken met TCP-eindpunt...",
"tcpStatus_connectingTo": "Verbinding maken met {endpoint}...", "tcpStatus_connectingTo": "Verbinding maken met {endpoint}...",
"tcpErrorHostRequired": "Een IP-adres is vereist.", "tcpErrorHostRequired": "Een IP-adres is vereist.",
"tcpErrorPortInvalid": "De poortwaarde moet tussen 1 en 65535 liggen.", "tcpErrorPortInvalid": "De poortwaarde moet tussen 1 en 65535 liggen.",

@ -1881,7 +1881,6 @@
"tcpPortLabel": "Port", "tcpPortLabel": "Port",
"tcpPortHint": "5000", "tcpPortHint": "5000",
"tcpStatus_notConnected": "Wprowadź adres URL i połącz", "tcpStatus_notConnected": "Wprowadź adres URL i połącz",
"tcpStatus_connecting": "Połączenie z punktem TCP...",
"tcpStatus_connectingTo": "Połączenie z {endpoint}...", "tcpStatus_connectingTo": "Połączenie z {endpoint}...",
"tcpErrorHostRequired": "Wymagana jest adresa IP.", "tcpErrorHostRequired": "Wymagana jest adresa IP.",
"tcpErrorPortInvalid": "Numer portu musi mieścić się w zakresie od 1 do 65535.", "tcpErrorPortInvalid": "Numer portu musi mieścić się w zakresie od 1 do 65535.",

@ -1878,10 +1878,9 @@
"connectionChoiceTcpLabel": "TCP", "connectionChoiceTcpLabel": "TCP",
"tcpScreenTitle": "Estabelecer conexão via TCP", "tcpScreenTitle": "Estabelecer conexão via TCP",
"tcpHostHint": "192.168.40.10", "tcpHostHint": "192.168.40.10",
"tcpPortLabel": "Porto", "tcpPortLabel": "Porta",
"tcpPortHint": "5000", "tcpPortHint": "5000",
"tcpStatus_notConnected": "Insira o endereço final e conecte-se.", "tcpStatus_notConnected": "Insira o endereço final e conecte-se.",
"tcpStatus_connecting": "Conectando ao ponto de extremidade TCP...",
"tcpStatus_connectingTo": "Conectando a {endpoint}...", "tcpStatus_connectingTo": "Conectando a {endpoint}...",
"tcpErrorHostRequired": "É necessário fornecer um endereço IP.", "tcpErrorHostRequired": "É necessário fornecer um endereço IP.",
"tcpErrorPortInvalid": "O valor do porto deve estar entre 1 e 65535.", "tcpErrorPortInvalid": "O valor do porto deve estar entre 1 e 65535.",

@ -1121,7 +1121,6 @@
"tcpPortLabel": "Порт", "tcpPortLabel": "Порт",
"tcpPortHint": "5000", "tcpPortHint": "5000",
"tcpStatus_notConnected": "Введите адрес и подключитесь.", "tcpStatus_notConnected": "Введите адрес и подключитесь.",
"tcpStatus_connecting": "Установление соединения с TCP-портом...",
"tcpStatus_connectingTo": "Подключение к {endpoint}...", "tcpStatus_connectingTo": "Подключение к {endpoint}...",
"tcpErrorHostRequired": "Необходимо указать IP-адрес.", "tcpErrorHostRequired": "Необходимо указать IP-адрес.",
"tcpErrorPortInvalid": "Порт должен находиться в диапазоне от 1 до 65535.", "tcpErrorPortInvalid": "Порт должен находиться в диапазоне от 1 до 65535.",

@ -1878,10 +1878,9 @@
"tcpHostLabel": "IP adresa", "tcpHostLabel": "IP adresa",
"tcpScreenTitle": "Spojte sa pomocou protokolu TCP", "tcpScreenTitle": "Spojte sa pomocou protokolu TCP",
"connectionChoiceTcpLabel": "TCP", "connectionChoiceTcpLabel": "TCP",
"tcpPortLabel": "Pri항", "tcpPortLabel": "Port",
"tcpPortHint": "5 000", "tcpPortHint": "5000",
"tcpStatus_notConnected": "Zadajte cieľovú adresu a pripojte sa.", "tcpStatus_notConnected": "Zadajte cieľovú adresu a pripojte sa.",
"tcpStatus_connecting": "Pripojenie k TCP endpointu...",
"tcpStatus_connectingTo": "Pripojenie k {endpoint}...", "tcpStatus_connectingTo": "Pripojenie k {endpoint}...",
"tcpErrorHostRequired": "Je potrebné zadať IP adresu.", "tcpErrorHostRequired": "Je potrebné zadať IP adresu.",
"tcpErrorPortInvalid": "Číslo portu musí byť medzi 1 a 65535.", "tcpErrorPortInvalid": "Číslo portu musí byť medzi 1 a 65535.",

@ -1878,10 +1878,9 @@
"tcpHostLabel": "IP naslov", "tcpHostLabel": "IP naslov",
"tcpHostHint": "192.168.40.10", "tcpHostHint": "192.168.40.10",
"tcpScreenTitle": "Komunicirajte preko protokola TCP", "tcpScreenTitle": "Komunicirajte preko protokola TCP",
"tcpPortLabel": "Pril", "tcpPortLabel": "Vrata",
"tcpPortHint": "5000", "tcpPortHint": "5000",
"tcpStatus_notConnected": "Vnesite končni naslov in se povežite", "tcpStatus_notConnected": "Vnesite končni naslov in se povežite",
"tcpStatus_connecting": "Povezava z TCP koncem...",
"tcpStatus_connectingTo": "Povezava z {endpoint}...", "tcpStatus_connectingTo": "Povezava z {endpoint}...",
"tcpErrorHostRequired": "Potrebna je IP-naslov.", "tcpErrorHostRequired": "Potrebna je IP-naslov.",
"tcpErrorPortInvalid": "Port mora biti med 1 in 65535.", "tcpErrorPortInvalid": "Port mora biti med 1 in 65535.",

@ -1878,10 +1878,9 @@
"tcpHostLabel": "IP-adress", "tcpHostLabel": "IP-adress",
"tcpScreenTitle": "Anslut via TCP", "tcpScreenTitle": "Anslut via TCP",
"connectionChoiceTcpLabel": "TCP", "connectionChoiceTcpLabel": "TCP",
"tcpPortLabel": "Hamn", "tcpPortLabel": "Port",
"tcpPortHint": "5000", "tcpPortHint": "5000",
"tcpStatus_notConnected": "Ange slutpunkt och anslut", "tcpStatus_notConnected": "Ange slutpunkt och anslut",
"tcpStatus_connecting": "Anslutning till TCP-slutpunkt...",
"tcpStatus_connectingTo": "Anslutning till {endpoint}...", "tcpStatus_connectingTo": "Anslutning till {endpoint}...",
"tcpErrorHostRequired": "IP-adress krävs.", "tcpErrorHostRequired": "IP-adress krävs.",
"tcpErrorPortInvalid": "Porten måste vara mellan 1 och 65535.", "tcpErrorPortInvalid": "Porten måste vara mellan 1 och 65535.",

@ -1881,7 +1881,6 @@
"tcpPortLabel": "Порт", "tcpPortLabel": "Порт",
"tcpPortHint": "5000", "tcpPortHint": "5000",
"tcpStatus_notConnected": "Введіть кінцеву точку та підключіться", "tcpStatus_notConnected": "Введіть кінцеву точку та підключіться",
"tcpStatus_connecting": "Підключення до TCP-кінцевої точки...",
"tcpStatus_connectingTo": "Підключення до {endpoint}...", "tcpStatus_connectingTo": "Підключення до {endpoint}...",
"tcpErrorHostRequired": "Необхідно вказати IP-адресу.", "tcpErrorHostRequired": "Необхідно вказати IP-адресу.",
"tcpErrorPortInvalid": "Порт повинен бути в межах від 1 до 65535.", "tcpErrorPortInvalid": "Порт повинен бути в межах від 1 до 65535.",

@ -1883,10 +1883,9 @@
"tcpHostHint": "192.168.40.10", "tcpHostHint": "192.168.40.10",
"tcpScreenTitle": "通过 TCP 连接", "tcpScreenTitle": "通过 TCP 连接",
"connectionChoiceTcpLabel": "TCP", "connectionChoiceTcpLabel": "TCP",
"tcpPortLabel": "", "tcpPortLabel": "端口",
"tcpPortHint": "5000", "tcpPortHint": "5000",
"tcpStatus_notConnected": "输入目标地址,然后连接", "tcpStatus_notConnected": "输入目标地址,然后连接",
"tcpStatus_connecting": "连接到 TCP 终点...",
"tcpStatus_connectingTo": "连接到 {endpoint}...", "tcpStatus_connectingTo": "连接到 {endpoint}...",
"tcpErrorHostRequired": "需要提供IP地址。", "tcpErrorHostRequired": "需要提供IP地址。",
"tcpErrorPortInvalid": "端口号必须在 1 到 65535 之间。", "tcpErrorPortInvalid": "端口号必须在 1 到 65535 之间。",

@ -27,7 +27,7 @@ class _TcpScreenState extends State<TcpScreen> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_hostController = TextEditingController(text: '192.168.40.10'); _hostController = TextEditingController();
_portController = TextEditingController(text: '5000'); _portController = TextEditingController(text: '5000');
_connector = context.read<MeshCoreConnector>(); _connector = context.read<MeshCoreConnector>();
@ -81,6 +81,9 @@ class _TcpScreenState extends State<TcpScreen> {
final isConnecting = final isConnecting =
connector.state == MeshCoreConnectionState.connecting && connector.state == MeshCoreConnectionState.connecting &&
connector.activeTransport == MeshCoreTransportType.tcp; connector.activeTransport == MeshCoreTransportType.tcp;
final isButtonDisabled =
isConnecting ||
connector.state == MeshCoreConnectionState.scanning;
return Column( return Column(
children: [ children: [
_buildStatusBar(context, connector), _buildStatusBar(context, connector),
@ -112,7 +115,8 @@ class _TcpScreenState extends State<TcpScreen> {
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
FilledButton.icon( FilledButton.icon(
onPressed: isConnecting ? null : _connectTcp, key: const Key('tcp_connect_button'),
onPressed: isButtonDisabled ? null : _connectTcp,
icon: isConnecting icon: isConnecting
? const SizedBox( ? const SizedBox(
width: 18, width: 18,
@ -153,6 +157,7 @@ class _TcpScreenState extends State<TcpScreen> {
); );
}, },
heroTag: 'tcp_usb_action', heroTag: 'tcp_usb_action',
extendedPadding: const EdgeInsets.symmetric(horizontal: 12),
icon: const Icon(Icons.usb), icon: const Icon(Icons.usb),
label: Text(context.l10n.connectionChoiceUsbLabel), label: Text(context.l10n.connectionChoiceUsbLabel),
), ),
@ -162,6 +167,7 @@ class _TcpScreenState extends State<TcpScreen> {
Navigator.of(context).maybePop(); Navigator.of(context).maybePop();
}, },
heroTag: 'tcp_ble_action', heroTag: 'tcp_ble_action',
extendedPadding: const EdgeInsets.symmetric(horizontal: 12),
icon: const Icon(Icons.bluetooth), icon: const Icon(Icons.bluetooth),
label: Text(context.l10n.connectionChoiceBluetoothLabel), label: Text(context.l10n.connectionChoiceBluetoothLabel),
), ),

@ -93,7 +93,7 @@ void main() {
final l10n = AppLocalizations.of(context); final l10n = AppLocalizations.of(context);
await tester.enterText(find.byType(TextField).first, ''); 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(); await tester.pumpAndSettle();
expect(find.text(l10n.tcpErrorHostRequired), findsOneWidget); 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).first, '192.168.1.50');
await tester.enterText(find.byType(TextField).at(1), '99999'); 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(); await tester.pumpAndSettle();
expect(connector.connectTcpCalls, 0); expect(connector.connectTcpCalls, 0);
@ -135,7 +135,7 @@ void main() {
await tester.pump(const Duration(milliseconds: 60)); 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, tester,
) async { ) async {
final connector = _FakeMeshCoreConnector() final connector = _FakeMeshCoreConnector()
@ -150,12 +150,11 @@ void main() {
); );
await tester.pumpAndSettle(); await tester.pumpAndSettle();
await tester.tap(find.widgetWithText(FilledButton, 'Connect')); final button = tester.widget<ButtonStyleButton>(
await tester.pumpAndSettle(); find.byKey(const Key('tcp_connect_button')),
);
expect(connector.connectTcpCalls, 1); expect(button.onPressed, isNull);
expect(connector.lastHost, '192.168.40.10'); expect(connector.connectTcpCalls, 0);
expect(connector.lastPort, 5000);
}); });
testWidgets('TcpScreen narrow width long status text does not overflow', ( testWidgets('TcpScreen narrow width long status text does not overflow', (

Loading…
Cancel
Save

Powered by TurnKey Linux.