mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-04-20 22:13:48 +00:00
Merge pull request #42 from wel97459/dev-neighbours
Added Neighbors to the repeater hub and a screen to display the Neighbors
This commit is contained in:
commit
f790604d23
33 changed files with 1436 additions and 9 deletions
|
|
@ -3,3 +3,4 @@ template-arb-file: app_en.arb
|
|||
output-localization-file: app_localizations.dart
|
||||
output-class: AppLocalizations
|
||||
nullable-getter: false
|
||||
untranslated-messages-file: untranslated.json
|
||||
|
|
|
|||
|
|
@ -1337,6 +1337,20 @@
|
|||
"listFilter_roomServers": "Сървъри на стая",
|
||||
"listFilter_unreadOnly": "Само непрочетените",
|
||||
"listFilter_newGroup": "Нова група",
|
||||
"@neighbors_errorLoading": {
|
||||
"placeholders": {
|
||||
"error": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repeater_neighboursSubtitle": "Преглед на съседни възли с нулев скок.",
|
||||
"repeater_neighbours": "Съседи",
|
||||
"neighbors_receivedData": "Получени данни за съседи",
|
||||
"neighbors_requestTimedOut": "Съседите поискат изтичане на време.",
|
||||
"neighbors_errorLoading": "Грешка при зареждане на съседи: {error}",
|
||||
"neighbors_repeatersNeighbours": "Повторители Съседи",
|
||||
"neighbors_noData": "Няма налични данни за съседи.",
|
||||
"channels_createPrivateChannel": "Създай Частен Канал",
|
||||
"channels_joinPrivateChannel": "Присъедини се към Частен Канал",
|
||||
"channels_createPrivateChannelDesc": "Защитено с таен ключ.",
|
||||
|
|
@ -1349,6 +1363,22 @@
|
|||
"channels_scanQrCodeComingSoon": "Ще излезе скоро",
|
||||
"channels_enterHashtag": "Въведете хаштаг",
|
||||
"channels_hashtagHint": "напр. #отбор",
|
||||
"@neighbors_unknownContact": {
|
||||
"placeholders": {
|
||||
"pubkey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@neighbors_heardAgo": {
|
||||
"placeholders": {
|
||||
"time": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"neighbors_heardAgo": "Слушано преди {time}.",
|
||||
"neighbors_unknownContact": "Неизвестна {pubkey}",
|
||||
"settings_locationIntervalSec": "Интервал за GPS (Секунди)",
|
||||
"settings_locationGPSEnable": "Активиране на GPS",
|
||||
"settings_locationGPSEnableSubtitle": "Активирайте автоматичното актуализиране на местоположението чрез GPS.",
|
||||
|
|
|
|||
|
|
@ -1337,6 +1337,20 @@
|
|||
"listFilter_roomServers": "Raumserver",
|
||||
"listFilter_unreadOnly": "Nur nicht gelesen",
|
||||
"listFilter_newGroup": "Neue Gruppe",
|
||||
"@neighbors_errorLoading": {
|
||||
"placeholders": {
|
||||
"error": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repeater_neighbours": "Nachbarn",
|
||||
"repeater_neighboursSubtitle": "Anzahl der Hop-Nachbarn anzeigen.",
|
||||
"neighbors_receivedData": "Empfangene Nachbarendaten",
|
||||
"neighbors_requestTimedOut": "Nachbarn melden zeitweise Ausfall.",
|
||||
"neighbors_errorLoading": "Fehler beim Laden der Nachbarn: {error}",
|
||||
"neighbors_repeatersNeighbours": "Wiederholer Nachbarn",
|
||||
"neighbors_noData": "Keine Nachbardaten verfügbar.",
|
||||
"channels_joinPrivateChannel": "Treten Sie einem privaten Kanal bei",
|
||||
"channels_joinPrivateChannelDesc": "Manuelle Eingabe eines geheimen Schlüssels.",
|
||||
"channels_createPrivateChannel": "Erstelle einen privaten Kanal",
|
||||
|
|
@ -1348,5 +1362,27 @@
|
|||
"channels_scanQrCode": "Scannen Sie einen QR-Code",
|
||||
"channels_scanQrCodeComingSoon": "Bald verfügbar",
|
||||
"channels_enterHashtag": "Gib Hashtag ein",
|
||||
"channels_hashtagHint": "z.B. #team"
|
||||
"channels_hashtagHint": "z.B. #team",
|
||||
"@neighbors_unknownContact": {
|
||||
"placeholders": {
|
||||
"pubkey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@neighbors_heardAgo": {
|
||||
"placeholders": {
|
||||
"time": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"neighbors_heardAgo": "Hörte: {time} vor her.",
|
||||
"neighbors_unknownContact": "Unbekannte {pubkey}",
|
||||
"settings_locationGPSEnable": "GPS aktivieren",
|
||||
"settings_locationGPSEnableSubtitle": "Aktiviert GPS zur automatischen Aktualisierung des Standorts.",
|
||||
"settings_locationIntervalSec": "Intervall für GPS (Sekunden)",
|
||||
"settings_locationIntervalInvalid": "Das Intervall muss mindestens 60 Sekunden und weniger als 86400 Sekunden betragen.",
|
||||
"contacts_manageRoom": "Raum-Server verwalten",
|
||||
"room_management": "Raum-Server-Verwaltung"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -773,6 +773,8 @@
|
|||
"repeater_telemetrySubtitle": "View telemetry of sensors and system stats",
|
||||
"repeater_cli": "CLI",
|
||||
"repeater_cliSubtitle": "Send commands to the repeater",
|
||||
"repeater_neighbours": "Neighbors",
|
||||
"repeater_neighboursSubtitle": "View zero hop neighbors.",
|
||||
"repeater_settings": "Settings",
|
||||
"repeater_settingsSubtitle": "Configure repeater parameters",
|
||||
|
||||
|
|
@ -1075,6 +1077,29 @@
|
|||
"fahrenheit": {"type": "String"}
|
||||
}
|
||||
},
|
||||
|
||||
"neighbors_receivedData": "Received Neighbours Data",
|
||||
"neighbors_requestTimedOut": "Neighbours request timed out.",
|
||||
"neighbors_errorLoading": "Error loading neighbors: {error}",
|
||||
"@neighbors_errorLoading": {
|
||||
"placeholders": {
|
||||
"error": {"type": "String"}
|
||||
}
|
||||
},
|
||||
"neighbors_repeatersNeighbours": "Repeaters Neighbours",
|
||||
"neighbors_noData": "No neighbours data available.",
|
||||
"neighbors_unknownContact": "Unknown {pubkey}",
|
||||
"@neighbors_unknownContact": {
|
||||
"placeholders": {
|
||||
"pubkey": {"type": "String"}
|
||||
}
|
||||
},
|
||||
"neighbors_heardAgo": "Heard: {time} ago",
|
||||
"@neighbors_heardAgo": {
|
||||
"placeholders": {
|
||||
"time": {"type": "String"}
|
||||
}
|
||||
},
|
||||
"channelPath_title": "Packet Path",
|
||||
"channelPath_viewMap": "View map",
|
||||
"channelPath_otherObservedPaths": "Other Observed Paths",
|
||||
|
|
|
|||
|
|
@ -1337,6 +1337,20 @@
|
|||
"listFilter_roomServers": "Servidores de la sala",
|
||||
"listFilter_unreadOnly": "Solo sin leer",
|
||||
"listFilter_newGroup": "Nuevo grupo",
|
||||
"@neighbors_errorLoading": {
|
||||
"placeholders": {
|
||||
"error": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repeater_neighbours": "Vecinos",
|
||||
"repeater_neighboursSubtitle": "Ver vecinos de salto cero.",
|
||||
"neighbors_receivedData": "Recibidas Datos de Vecinos",
|
||||
"neighbors_requestTimedOut": "Los vecinos solicitan que se desconecte.",
|
||||
"neighbors_errorLoading": "Error al cargar vecinos: {error}",
|
||||
"neighbors_repeatersNeighbours": "Repetidores Vecinos",
|
||||
"neighbors_noData": "No hay datos de vecinos disponibles.",
|
||||
"channels_joinPrivateChannel": "Únete a un Canal Privado",
|
||||
"channels_createPrivateChannel": "Crear un Canal Privado",
|
||||
"channels_createPrivateChannelDesc": "Cifrado con una clave secreta.",
|
||||
|
|
@ -1349,6 +1363,22 @@
|
|||
"channels_scanQrCodeComingSoon": "Próximamente",
|
||||
"channels_enterHashtag": "Introducir hashtag",
|
||||
"channels_hashtagHint": "ej. #equipo",
|
||||
"@neighbors_unknownContact": {
|
||||
"placeholders": {
|
||||
"pubkey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@neighbors_heardAgo": {
|
||||
"placeholders": {
|
||||
"time": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"neighbors_unknownContact": "Clave pública desconocida {pubkey}",
|
||||
"neighbors_heardAgo": "Escuchado: {time} hace atrás",
|
||||
"settings_locationGPSEnable": "Habilitar GPS",
|
||||
"settings_locationGPSEnableSubtitle": "Habilita la actualización automática de la ubicación mediante GPS.",
|
||||
"settings_locationIntervalSec": "Intervalo para GPS (Segundos)",
|
||||
|
|
|
|||
|
|
@ -1337,6 +1337,20 @@
|
|||
"listFilter_roomServers": "Serveurs de pièce",
|
||||
"listFilter_unreadOnly": "Messages non lus seulement",
|
||||
"listFilter_newGroup": "Nouvelle groupe",
|
||||
"@neighbors_errorLoading": {
|
||||
"placeholders": {
|
||||
"error": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repeater_neighbours": "Voisins",
|
||||
"repeater_neighboursSubtitle": "Afficher les voisins de saut nuls.",
|
||||
"neighbors_receivedData": "Données des voisins reçues",
|
||||
"neighbors_requestTimedOut": "Les voisins demandent un délai.",
|
||||
"neighbors_errorLoading": "Erreur lors du chargement des voisins : {error}",
|
||||
"neighbors_repeatersNeighbours": "Répéteurs Voisins",
|
||||
"neighbors_noData": "Aucune donnée concernant les voisins disponible.",
|
||||
"channels_createPrivateChannelDesc": "Sécurisé avec une clé secrète.",
|
||||
"channels_joinPrivateChannel": "Rejoindre un Canal Privé",
|
||||
"channels_createPrivateChannel": "Créer un Canal Privé",
|
||||
|
|
@ -1349,6 +1363,22 @@
|
|||
"channels_scanQrCodeComingSoon": "Bientôt disponible",
|
||||
"channels_enterHashtag": "Entrez le hashtag",
|
||||
"channels_hashtagHint": "ex. #équipe",
|
||||
"@neighbors_unknownContact": {
|
||||
"placeholders": {
|
||||
"pubkey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@neighbors_heardAgo": {
|
||||
"placeholders": {
|
||||
"time": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"neighbors_unknownContact": "Clé publique inconnue {pubkey}",
|
||||
"neighbors_heardAgo": "Écouté : {time} auparavant",
|
||||
"settings_locationGPSEnable": "Habilita GPS",
|
||||
"settings_locationGPSEnableSubtitle": "Habilita la actualización automática de la ubicación mediante GPS.",
|
||||
"settings_locationIntervalSec": "Intervalo pour GPS (Segundos)",
|
||||
|
|
|
|||
|
|
@ -1337,6 +1337,20 @@
|
|||
"listFilter_roomServers": "Server della stanza",
|
||||
"listFilter_unreadOnly": "Solo non letto",
|
||||
"listFilter_newGroup": "Nuovo gruppo",
|
||||
"@neighbors_errorLoading": {
|
||||
"placeholders": {
|
||||
"error": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repeater_neighbours": "Vicini",
|
||||
"repeater_neighboursSubtitle": "Visualizza vicini di salto pari a zero.",
|
||||
"neighbors_receivedData": "Ricevute dati vicini",
|
||||
"neighbors_requestTimedOut": "I vicini richiedono un timeout.",
|
||||
"neighbors_errorLoading": "Errore nel caricamento dei vicini: {error}",
|
||||
"neighbors_repeatersNeighbours": "Ripetitori Vicini",
|
||||
"neighbors_noData": "Nessun dato sugli vicini disponibile.",
|
||||
"channels_createPrivateChannel": "Crea un Canale Privato",
|
||||
"channels_createPrivateChannelDesc": "Protetta con una chiave segreta.",
|
||||
"channels_joinPrivateChannel": "Unisciti a un Canale Privato",
|
||||
|
|
@ -1349,6 +1363,22 @@
|
|||
"channels_scanQrCodeComingSoon": "Arriverà presto",
|
||||
"channels_enterHashtag": "Inserisci hashtag",
|
||||
"channels_hashtagHint": "es. #team",
|
||||
"@neighbors_unknownContact": {
|
||||
"placeholders": {
|
||||
"pubkey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@neighbors_heardAgo": {
|
||||
"placeholders": {
|
||||
"time": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"neighbors_heardAgo": "Sentito: {time} fa",
|
||||
"neighbors_unknownContact": "Chiave pubblica sconosciuta {pubkey}",
|
||||
"settings_locationGPSEnable": "Abilita GPS",
|
||||
"settings_locationGPSEnableSubtitle": "Abilita l'aggiornamento automatico della posizione tramite GPS.",
|
||||
"settings_locationIntervalSec": "Intervallo GPS (Secondi)",
|
||||
|
|
|
|||
|
|
@ -2945,6 +2945,18 @@ abstract class AppLocalizations {
|
|||
/// **'Send commands to the repeater'**
|
||||
String get repeater_cliSubtitle;
|
||||
|
||||
/// No description provided for @repeater_neighbours.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Neighbors'**
|
||||
String get repeater_neighbours;
|
||||
|
||||
/// No description provided for @repeater_neighboursSubtitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'View zero hop neighbors.'**
|
||||
String get repeater_neighboursSubtitle;
|
||||
|
||||
/// No description provided for @repeater_settings.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
@ -4084,6 +4096,48 @@ abstract class AppLocalizations {
|
|||
/// **'{celsius}°C / {fahrenheit}°F'**
|
||||
String telemetry_temperatureValue(String celsius, String fahrenheit);
|
||||
|
||||
/// No description provided for @neighbors_receivedData.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Received Neighbours Data'**
|
||||
String get neighbors_receivedData;
|
||||
|
||||
/// No description provided for @neighbors_requestTimedOut.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Neighbours request timed out.'**
|
||||
String get neighbors_requestTimedOut;
|
||||
|
||||
/// No description provided for @neighbors_errorLoading.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Error loading neighbors: {error}'**
|
||||
String neighbors_errorLoading(String error);
|
||||
|
||||
/// No description provided for @neighbors_repeatersNeighbours.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Repeaters Neighbours'**
|
||||
String get neighbors_repeatersNeighbours;
|
||||
|
||||
/// No description provided for @neighbors_noData.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No neighbours data available.'**
|
||||
String get neighbors_noData;
|
||||
|
||||
/// No description provided for @neighbors_unknownContact.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Unknown {pubkey}'**
|
||||
String neighbors_unknownContact(String pubkey);
|
||||
|
||||
/// No description provided for @neighbors_heardAgo.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Heard: {time} ago'**
|
||||
String neighbors_heardAgo(String time);
|
||||
|
||||
/// No description provided for @channelPath_title.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
|
|||
|
|
@ -1630,6 +1630,13 @@ class AppLocalizationsBg extends AppLocalizations {
|
|||
@override
|
||||
String get repeater_cliSubtitle => 'Изпрати команди към ретранслатора';
|
||||
|
||||
@override
|
||||
String get repeater_neighbours => 'Съседи';
|
||||
|
||||
@override
|
||||
String get repeater_neighboursSubtitle =>
|
||||
'Преглед на съседни възли с нулев скок.';
|
||||
|
||||
@override
|
||||
String get repeater_settings => 'Настройки';
|
||||
|
||||
|
|
@ -2315,6 +2322,33 @@ class AppLocalizationsBg extends AppLocalizations {
|
|||
return '$celsius°C / $fahrenheit°F';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_receivedData => 'Получени данни за съседи';
|
||||
|
||||
@override
|
||||
String get neighbors_requestTimedOut => 'Съседите поискат изтичане на време.';
|
||||
|
||||
@override
|
||||
String neighbors_errorLoading(String error) {
|
||||
return 'Грешка при зареждане на съседи: $error';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_repeatersNeighbours => 'Повторители Съседи';
|
||||
|
||||
@override
|
||||
String get neighbors_noData => 'Няма налични данни за съседи.';
|
||||
|
||||
@override
|
||||
String neighbors_unknownContact(String pubkey) {
|
||||
return 'Неизвестна $pubkey';
|
||||
}
|
||||
|
||||
@override
|
||||
String neighbors_heardAgo(String time) {
|
||||
return 'Слушано преди $time.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channelPath_title => 'Пътеки пъзел';
|
||||
|
||||
|
|
|
|||
|
|
@ -201,18 +201,18 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
String get settings_locationInvalid => 'Ungültige Breiten- oder Längengrade.';
|
||||
|
||||
@override
|
||||
String get settings_locationGPSEnable => 'GPS Enable';
|
||||
String get settings_locationGPSEnable => 'GPS aktivieren';
|
||||
|
||||
@override
|
||||
String get settings_locationGPSEnableSubtitle =>
|
||||
'Enables GPS to automatically update location.';
|
||||
'Aktiviert GPS zur automatischen Aktualisierung des Standorts.';
|
||||
|
||||
@override
|
||||
String get settings_locationIntervalSec => 'Interval for GPS (Seconds)';
|
||||
String get settings_locationIntervalSec => 'Intervall für GPS (Sekunden)';
|
||||
|
||||
@override
|
||||
String get settings_locationIntervalInvalid =>
|
||||
'Interval must be at least 60 seconds, and less than 86400 seconds.';
|
||||
'Das Intervall muss mindestens 60 Sekunden und weniger als 86400 Sekunden betragen.';
|
||||
|
||||
@override
|
||||
String get settings_latitude => 'Breitengrad';
|
||||
|
|
@ -662,7 +662,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
String get contacts_manageRepeater => 'Wiederholungen verwalten';
|
||||
|
||||
@override
|
||||
String get contacts_manageRoom => 'Manage Room Server';
|
||||
String get contacts_manageRoom => 'Raum-Server verwalten';
|
||||
|
||||
@override
|
||||
String get contacts_roomLogin => 'Raum-Login';
|
||||
|
|
@ -1604,7 +1604,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
String get repeater_management => 'Repeater-Verwaltung';
|
||||
|
||||
@override
|
||||
String get room_management => 'Room Server Management';
|
||||
String get room_management => 'Raum-Server-Verwaltung';
|
||||
|
||||
@override
|
||||
String get repeater_managementTools => 'Verwaltungs-Tools';
|
||||
|
|
@ -1629,6 +1629,12 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
@override
|
||||
String get repeater_cliSubtitle => 'Sende Befehle an den Repeater';
|
||||
|
||||
@override
|
||||
String get repeater_neighbours => 'Nachbarn';
|
||||
|
||||
@override
|
||||
String get repeater_neighboursSubtitle => 'Anzahl der Hop-Nachbarn anzeigen.';
|
||||
|
||||
@override
|
||||
String get repeater_settings => 'Einstellungen';
|
||||
|
||||
|
|
@ -2318,6 +2324,33 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
return '$celsius°C / $fahrenheit°F';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_receivedData => 'Empfangene Nachbarendaten';
|
||||
|
||||
@override
|
||||
String get neighbors_requestTimedOut => 'Nachbarn melden zeitweise Ausfall.';
|
||||
|
||||
@override
|
||||
String neighbors_errorLoading(String error) {
|
||||
return 'Fehler beim Laden der Nachbarn: $error';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_repeatersNeighbours => 'Wiederholer Nachbarn';
|
||||
|
||||
@override
|
||||
String get neighbors_noData => 'Keine Nachbardaten verfügbar.';
|
||||
|
||||
@override
|
||||
String neighbors_unknownContact(String pubkey) {
|
||||
return 'Unbekannte $pubkey';
|
||||
}
|
||||
|
||||
@override
|
||||
String neighbors_heardAgo(String time) {
|
||||
return 'Hörte: $time vor her.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channelPath_title => 'Paketpfad';
|
||||
|
||||
|
|
|
|||
|
|
@ -1604,6 +1604,12 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
@override
|
||||
String get repeater_cliSubtitle => 'Send commands to the repeater';
|
||||
|
||||
@override
|
||||
String get repeater_neighbours => 'Neighbors';
|
||||
|
||||
@override
|
||||
String get repeater_neighboursSubtitle => 'View zero hop neighbors.';
|
||||
|
||||
@override
|
||||
String get repeater_settings => 'Settings';
|
||||
|
||||
|
|
@ -2277,6 +2283,33 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
return '$celsius°C / $fahrenheit°F';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_receivedData => 'Received Neighbours Data';
|
||||
|
||||
@override
|
||||
String get neighbors_requestTimedOut => 'Neighbours request timed out.';
|
||||
|
||||
@override
|
||||
String neighbors_errorLoading(String error) {
|
||||
return 'Error loading neighbors: $error';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_repeatersNeighbours => 'Repeaters Neighbours';
|
||||
|
||||
@override
|
||||
String get neighbors_noData => 'No neighbours data available.';
|
||||
|
||||
@override
|
||||
String neighbors_unknownContact(String pubkey) {
|
||||
return 'Unknown $pubkey';
|
||||
}
|
||||
|
||||
@override
|
||||
String neighbors_heardAgo(String time) {
|
||||
return 'Heard: $time ago';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channelPath_title => 'Packet Path';
|
||||
|
||||
|
|
|
|||
|
|
@ -1628,6 +1628,12 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||
@override
|
||||
String get repeater_cliSubtitle => 'Enviar comandos al repetidor';
|
||||
|
||||
@override
|
||||
String get repeater_neighbours => 'Vecinos';
|
||||
|
||||
@override
|
||||
String get repeater_neighboursSubtitle => 'Ver vecinos de salto cero.';
|
||||
|
||||
@override
|
||||
String get repeater_settings => 'Configuración';
|
||||
|
||||
|
|
@ -2312,6 +2318,34 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||
return '$celsius°C / $fahrenheit°F';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_receivedData => 'Recibidas Datos de Vecinos';
|
||||
|
||||
@override
|
||||
String get neighbors_requestTimedOut =>
|
||||
'Los vecinos solicitan que se desconecte.';
|
||||
|
||||
@override
|
||||
String neighbors_errorLoading(String error) {
|
||||
return 'Error al cargar vecinos: $error';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_repeatersNeighbours => 'Repetidores Vecinos';
|
||||
|
||||
@override
|
||||
String get neighbors_noData => 'No hay datos de vecinos disponibles.';
|
||||
|
||||
@override
|
||||
String neighbors_unknownContact(String pubkey) {
|
||||
return 'Clave pública desconocida $pubkey';
|
||||
}
|
||||
|
||||
@override
|
||||
String neighbors_heardAgo(String time) {
|
||||
return 'Escuchado: $time hace atrás';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channelPath_title => 'Ruta del Paquete';
|
||||
|
||||
|
|
|
|||
|
|
@ -1634,6 +1634,13 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||
@override
|
||||
String get repeater_cliSubtitle => 'Envoyer des commandes au répétiteur';
|
||||
|
||||
@override
|
||||
String get repeater_neighbours => 'Voisins';
|
||||
|
||||
@override
|
||||
String get repeater_neighboursSubtitle =>
|
||||
'Afficher les voisins de saut nuls.';
|
||||
|
||||
@override
|
||||
String get repeater_settings => 'Paramètres';
|
||||
|
||||
|
|
@ -2326,6 +2333,34 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||
return '$celsius°C / $fahrenheit°F';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_receivedData => 'Données des voisins reçues';
|
||||
|
||||
@override
|
||||
String get neighbors_requestTimedOut => 'Les voisins demandent un délai.';
|
||||
|
||||
@override
|
||||
String neighbors_errorLoading(String error) {
|
||||
return 'Erreur lors du chargement des voisins : $error';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_repeatersNeighbours => 'Répéteurs Voisins';
|
||||
|
||||
@override
|
||||
String get neighbors_noData =>
|
||||
'Aucune donnée concernant les voisins disponible.';
|
||||
|
||||
@override
|
||||
String neighbors_unknownContact(String pubkey) {
|
||||
return 'Clé publique inconnue $pubkey';
|
||||
}
|
||||
|
||||
@override
|
||||
String neighbors_heardAgo(String time) {
|
||||
return 'Écouté : $time auparavant';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channelPath_title => 'Chemin de paquet';
|
||||
|
||||
|
|
|
|||
|
|
@ -1626,6 +1626,13 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||
@override
|
||||
String get repeater_cliSubtitle => 'Invia comandi al ripetitore';
|
||||
|
||||
@override
|
||||
String get repeater_neighbours => 'Vicini';
|
||||
|
||||
@override
|
||||
String get repeater_neighboursSubtitle =>
|
||||
'Visualizza vicini di salto pari a zero.';
|
||||
|
||||
@override
|
||||
String get repeater_settings => 'Impostazioni';
|
||||
|
||||
|
|
@ -2312,6 +2319,33 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||
return '$celsius°C / $fahrenheit°F';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_receivedData => 'Ricevute dati vicini';
|
||||
|
||||
@override
|
||||
String get neighbors_requestTimedOut => 'I vicini richiedono un timeout.';
|
||||
|
||||
@override
|
||||
String neighbors_errorLoading(String error) {
|
||||
return 'Errore nel caricamento dei vicini: $error';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_repeatersNeighbours => 'Ripetitori Vicini';
|
||||
|
||||
@override
|
||||
String get neighbors_noData => 'Nessun dato sugli vicini disponibile.';
|
||||
|
||||
@override
|
||||
String neighbors_unknownContact(String pubkey) {
|
||||
return 'Chiave pubblica sconosciuta $pubkey';
|
||||
}
|
||||
|
||||
@override
|
||||
String neighbors_heardAgo(String time) {
|
||||
return 'Sentito: $time fa';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channelPath_title => 'Percorso Pacchetto';
|
||||
|
||||
|
|
|
|||
|
|
@ -1621,6 +1621,12 @@ class AppLocalizationsNl extends AppLocalizations {
|
|||
@override
|
||||
String get repeater_cliSubtitle => 'Verzend commando\'s naar de repeater';
|
||||
|
||||
@override
|
||||
String get repeater_neighbours => 'Buren';
|
||||
|
||||
@override
|
||||
String get repeater_neighboursSubtitle => 'Bekijk nul hops buren.';
|
||||
|
||||
@override
|
||||
String get repeater_settings => 'Instellingen';
|
||||
|
||||
|
|
@ -2302,6 +2308,34 @@ class AppLocalizationsNl extends AppLocalizations {
|
|||
return '$celsius°C / $fahrenheit°F';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_receivedData => 'Ontvangen Buurdata';
|
||||
|
||||
@override
|
||||
String get neighbors_requestTimedOut =>
|
||||
'Buren vragen om tijdelijk uitgeschakeld.';
|
||||
|
||||
@override
|
||||
String neighbors_errorLoading(String error) {
|
||||
return 'Fout bij het laden van buren: $error';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_repeatersNeighbours => 'Herhalingen Buren';
|
||||
|
||||
@override
|
||||
String get neighbors_noData => 'Geen gegevens van buren beschikbaar.';
|
||||
|
||||
@override
|
||||
String neighbors_unknownContact(String pubkey) {
|
||||
return 'Onbekende $pubkey';
|
||||
}
|
||||
|
||||
@override
|
||||
String neighbors_heardAgo(String time) {
|
||||
return 'Horen: $time geleden';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channelPath_title => 'Pakketpad';
|
||||
|
||||
|
|
|
|||
|
|
@ -1630,6 +1630,13 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||
@override
|
||||
String get repeater_cliSubtitle => 'Wyślij polecenia do powielacza';
|
||||
|
||||
@override
|
||||
String get repeater_neighbours => 'Sąsiedzi';
|
||||
|
||||
@override
|
||||
String get repeater_neighboursSubtitle =>
|
||||
'Wyświetl sąsiedztwo zerowych hopów.';
|
||||
|
||||
@override
|
||||
String get repeater_settings => 'Ustawienia';
|
||||
|
||||
|
|
@ -2310,6 +2317,34 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||
return '$celsius°C / $fahrenheit°F';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_receivedData => 'Otrzymano dane sąsiedztwa';
|
||||
|
||||
@override
|
||||
String get neighbors_requestTimedOut =>
|
||||
'Sąsiedzi proszą o wyłączenie timingu.';
|
||||
|
||||
@override
|
||||
String neighbors_errorLoading(String error) {
|
||||
return 'Błąd podczas ładowania sąsiadów: $error';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_repeatersNeighbours => 'Powtarzacze Sąsiedzi';
|
||||
|
||||
@override
|
||||
String get neighbors_noData => 'Brak danych dotyczących sąsiadów.';
|
||||
|
||||
@override
|
||||
String neighbors_unknownContact(String pubkey) {
|
||||
return 'Nieznana $pubkey';
|
||||
}
|
||||
|
||||
@override
|
||||
String neighbors_heardAgo(String time) {
|
||||
return 'Usłyszano: $time temu';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channelPath_title => 'Ścieżka pakietu';
|
||||
|
||||
|
|
|
|||
|
|
@ -1628,6 +1628,13 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||
@override
|
||||
String get repeater_cliSubtitle => 'Enviar comandos ao repetidor';
|
||||
|
||||
@override
|
||||
String get repeater_neighbours => 'Vizinhos';
|
||||
|
||||
@override
|
||||
String get repeater_neighboursSubtitle =>
|
||||
'Visualizar vizinhos de salto zero.';
|
||||
|
||||
@override
|
||||
String get repeater_settings => 'Configurações';
|
||||
|
||||
|
|
@ -2312,6 +2319,34 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||
return '$celsius°C / $fahrenheit°F';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_receivedData => 'Dados dos Vizinhos Recebidos';
|
||||
|
||||
@override
|
||||
String get neighbors_requestTimedOut =>
|
||||
'Vizinhos solicitam tempo limite esgotado.';
|
||||
|
||||
@override
|
||||
String neighbors_errorLoading(String error) {
|
||||
return 'Erro ao carregar vizinhos: $error';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_repeatersNeighbours => 'Repetidores Vizinhos';
|
||||
|
||||
@override
|
||||
String get neighbors_noData => 'Não estão disponíveis dados de vizinhos.';
|
||||
|
||||
@override
|
||||
String neighbors_unknownContact(String pubkey) {
|
||||
return '$pubkey Desconhecido';
|
||||
}
|
||||
|
||||
@override
|
||||
String neighbors_heardAgo(String time) {
|
||||
return 'Ouvido: $time atrás';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channelPath_title => 'Rótulo de Caminho de Pacote';
|
||||
|
||||
|
|
|
|||
|
|
@ -1623,6 +1623,12 @@ class AppLocalizationsSk extends AppLocalizations {
|
|||
@override
|
||||
String get repeater_cliSubtitle => 'Pošlite príkazy opakovaču';
|
||||
|
||||
@override
|
||||
String get repeater_neighbours => 'Súsezný';
|
||||
|
||||
@override
|
||||
String get repeater_neighboursSubtitle => 'Zobraziť susedné body bez skokov.';
|
||||
|
||||
@override
|
||||
String get repeater_settings => 'Nastavenia';
|
||||
|
||||
|
|
@ -2300,6 +2306,34 @@ class AppLocalizationsSk extends AppLocalizations {
|
|||
return '$celsius°C / $fahrenheit°F';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_receivedData => 'Obdielo dáta suseda';
|
||||
|
||||
@override
|
||||
String get neighbors_requestTimedOut => 'Súďia žiadajú o časové ukončenie.';
|
||||
|
||||
@override
|
||||
String neighbors_errorLoading(String error) {
|
||||
return 'Chyba pri načítaní susedov: $error';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_repeatersNeighbours => 'Opakovadlá Súsezná';
|
||||
|
||||
@override
|
||||
String get neighbors_noData =>
|
||||
'Nie je dostupná žiadna informácia o susedoch.';
|
||||
|
||||
@override
|
||||
String neighbors_unknownContact(String pubkey) {
|
||||
return 'Neznáma $pubkey';
|
||||
}
|
||||
|
||||
@override
|
||||
String neighbors_heardAgo(String time) {
|
||||
return 'Počuli sme to: $time dozadu';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channelPath_title => 'Cesta balíka';
|
||||
|
||||
|
|
|
|||
|
|
@ -1624,6 +1624,12 @@ class AppLocalizationsSl extends AppLocalizations {
|
|||
String get repeater_cliSubtitle =>
|
||||
'Pošlji ukazne povelje na ponovitveno enoto.';
|
||||
|
||||
@override
|
||||
String get repeater_neighbours => 'Sosedi';
|
||||
|
||||
@override
|
||||
String get repeater_neighboursSubtitle => 'Pogledati nič sosednjih hopjev.';
|
||||
|
||||
@override
|
||||
String get repeater_settings => 'Nastavitve';
|
||||
|
||||
|
|
@ -2305,6 +2311,34 @@ class AppLocalizationsSl extends AppLocalizations {
|
|||
return '$celsius°C / $fahrenheit°F';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_receivedData => 'Prejeto podatke o sosedih';
|
||||
|
||||
@override
|
||||
String get neighbors_requestTimedOut =>
|
||||
'Sosedi zahtevajo izklop po dogovoru.';
|
||||
|
||||
@override
|
||||
String neighbors_errorLoading(String error) {
|
||||
return 'Napaka pri obnašanju sosedov: $error';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_repeatersNeighbours => 'Ponovitve Sosedi';
|
||||
|
||||
@override
|
||||
String get neighbors_noData => 'Niso na voljo podatki o sosedih.';
|
||||
|
||||
@override
|
||||
String neighbors_unknownContact(String pubkey) {
|
||||
return 'Nepoznano $pubkey';
|
||||
}
|
||||
|
||||
@override
|
||||
String neighbors_heardAgo(String time) {
|
||||
return 'Udeleženec je prejel sporočilo $time nazaj.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channelPath_title => 'Pot do paketa';
|
||||
|
||||
|
|
|
|||
|
|
@ -1612,6 +1612,12 @@ class AppLocalizationsSv extends AppLocalizations {
|
|||
@override
|
||||
String get repeater_cliSubtitle => 'Skicka kommandon till repetitorn';
|
||||
|
||||
@override
|
||||
String get repeater_neighbours => 'Grannar';
|
||||
|
||||
@override
|
||||
String get repeater_neighboursSubtitle => 'Visa noll hoppgrannar.';
|
||||
|
||||
@override
|
||||
String get repeater_settings => 'Inställningar';
|
||||
|
||||
|
|
@ -2289,6 +2295,33 @@ class AppLocalizationsSv extends AppLocalizations {
|
|||
return '$celsius°C / $fahrenheit°F';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_receivedData => 'Mottagna grannars data';
|
||||
|
||||
@override
|
||||
String get neighbors_requestTimedOut => 'Grannar begär tidsinställd utskick.';
|
||||
|
||||
@override
|
||||
String neighbors_errorLoading(String error) {
|
||||
return 'Fel vid inläsning av grannar: $error';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_repeatersNeighbours => 'Upprepar grannar';
|
||||
|
||||
@override
|
||||
String get neighbors_noData => 'Inga grannuppgifter finns tillgängliga.';
|
||||
|
||||
@override
|
||||
String neighbors_unknownContact(String pubkey) {
|
||||
return 'Okänd $pubkey';
|
||||
}
|
||||
|
||||
@override
|
||||
String neighbors_heardAgo(String time) {
|
||||
return 'Hördes: $time sedan';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channelPath_title => 'Paketväg';
|
||||
|
||||
|
|
|
|||
|
|
@ -1552,6 +1552,12 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||
@override
|
||||
String get repeater_cliSubtitle => '发送命令到重复器';
|
||||
|
||||
@override
|
||||
String get repeater_neighbours => '邻居';
|
||||
|
||||
@override
|
||||
String get repeater_neighboursSubtitle => '查看零跳邻居。';
|
||||
|
||||
@override
|
||||
String get repeater_settings => '设置';
|
||||
|
||||
|
|
@ -2182,6 +2188,33 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||
return '$celsius°C / $fahrenheit°F';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_receivedData => '收到邻居数据';
|
||||
|
||||
@override
|
||||
String get neighbors_requestTimedOut => '邻居请求超时处理。';
|
||||
|
||||
@override
|
||||
String neighbors_errorLoading(String error) {
|
||||
return '加载邻居时出错:$error';
|
||||
}
|
||||
|
||||
@override
|
||||
String get neighbors_repeatersNeighbours => '重复器邻居';
|
||||
|
||||
@override
|
||||
String get neighbors_noData => '没有可用的邻居数据。';
|
||||
|
||||
@override
|
||||
String neighbors_unknownContact(String pubkey) {
|
||||
return '未知$pubkey';
|
||||
}
|
||||
|
||||
@override
|
||||
String neighbors_heardAgo(String time) {
|
||||
return '听到的时间:$time前';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channelPath_title => '数据包路径';
|
||||
|
||||
|
|
|
|||
|
|
@ -1337,6 +1337,20 @@
|
|||
"listFilter_roomServers": "Roomservers",
|
||||
"listFilter_unreadOnly": "Alleen ongelezen",
|
||||
"listFilter_newGroup": "Nieuwe groep",
|
||||
"@neighbors_errorLoading": {
|
||||
"placeholders": {
|
||||
"error": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repeater_neighbours": "Buren",
|
||||
"repeater_neighboursSubtitle": "Bekijk nul hops buren.",
|
||||
"neighbors_receivedData": "Ontvangen Buurdata",
|
||||
"neighbors_requestTimedOut": "Buren vragen om tijdelijk uitgeschakeld.",
|
||||
"neighbors_errorLoading": "Fout bij het laden van buren: {error}",
|
||||
"neighbors_repeatersNeighbours": "Herhalingen Buren",
|
||||
"neighbors_noData": "Geen gegevens van buren beschikbaar.",
|
||||
"channels_createPrivateChannelDesc": "Beveiligd met een geheime sleutel.",
|
||||
"channels_createPrivateChannel": "Maak een Privé Kanaal",
|
||||
"channels_joinPrivateChannel": "Sluit een Privé Kanaal aan",
|
||||
|
|
@ -1349,6 +1363,22 @@
|
|||
"channels_scanQrCodeComingSoon": "Komt later",
|
||||
"channels_enterHashtag": "Voer hashtag in",
|
||||
"channels_hashtagHint": "bijv. #team",
|
||||
"@neighbors_unknownContact": {
|
||||
"placeholders": {
|
||||
"pubkey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@neighbors_heardAgo": {
|
||||
"placeholders": {
|
||||
"time": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"neighbors_unknownContact": "Onbekende {pubkey}",
|
||||
"neighbors_heardAgo": "Horen: {time} geleden",
|
||||
"settings_locationGPSEnable": "GPS inschakelen",
|
||||
"settings_locationGPSEnableSubtitle": "Activeer automatisch locatieupdates via GPS.",
|
||||
"settings_locationIntervalSec": "Interval voor GPS (Seconden)",
|
||||
|
|
|
|||
|
|
@ -1337,6 +1337,20 @@
|
|||
"listFilter_roomServers": "Serwery pokoju",
|
||||
"listFilter_unreadOnly": "Tylko nieprzeczytane",
|
||||
"listFilter_newGroup": "Nowa grupa",
|
||||
"@neighbors_errorLoading": {
|
||||
"placeholders": {
|
||||
"error": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repeater_neighbours": "Sąsiedzi",
|
||||
"repeater_neighboursSubtitle": "Wyświetl sąsiedztwo zerowych hopów.",
|
||||
"neighbors_receivedData": "Otrzymano dane sąsiedztwa",
|
||||
"neighbors_requestTimedOut": "Sąsiedzi proszą o wyłączenie timingu.",
|
||||
"neighbors_errorLoading": "Błąd podczas ładowania sąsiadów: {error}",
|
||||
"neighbors_repeatersNeighbours": "Powtarzacze Sąsiedzi",
|
||||
"neighbors_noData": "Brak danych dotyczących sąsiadów.",
|
||||
"channels_joinPrivateChannelDesc": "Ręcznie wprowadź klucz tajny.",
|
||||
"channels_createPrivateChannel": "Utwórz Prywatny Kanał",
|
||||
"channels_createPrivateChannelDesc": "Zabezpieczone kluczem szyfrowym.",
|
||||
|
|
@ -1349,6 +1363,22 @@
|
|||
"channels_scanQrCodeComingSoon": "Wkrótce",
|
||||
"channels_enterHashtag": "Wprowadź hashtag",
|
||||
"channels_hashtagHint": "np. #zespół",
|
||||
"@neighbors_unknownContact": {
|
||||
"placeholders": {
|
||||
"pubkey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@neighbors_heardAgo": {
|
||||
"placeholders": {
|
||||
"time": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"neighbors_heardAgo": "Usłyszano: {time} temu",
|
||||
"neighbors_unknownContact": "Nieznana {pubkey}",
|
||||
"settings_locationGPSEnable": "Włącz GPS",
|
||||
"settings_locationGPSEnableSubtitle": "Włącza automatyczne aktualizowanie pozycji za pomocą GPS.",
|
||||
"settings_locationIntervalSec": "Interwał dla GPS (Sekundy)",
|
||||
|
|
|
|||
|
|
@ -1337,6 +1337,20 @@
|
|||
"listFilter_roomServers": "Servidores de sala",
|
||||
"listFilter_unreadOnly": "Apenas não lido",
|
||||
"listFilter_newGroup": "Novo grupo",
|
||||
"@neighbors_errorLoading": {
|
||||
"placeholders": {
|
||||
"error": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repeater_neighbours": "Vizinhos",
|
||||
"neighbors_receivedData": "Dados dos Vizinhos Recebidos",
|
||||
"repeater_neighboursSubtitle": "Visualizar vizinhos de salto zero.",
|
||||
"neighbors_requestTimedOut": "Vizinhos solicitam tempo limite esgotado.",
|
||||
"neighbors_errorLoading": "Erro ao carregar vizinhos: {error}",
|
||||
"neighbors_repeatersNeighbours": "Repetidores Vizinhos",
|
||||
"neighbors_noData": "Não estão disponíveis dados de vizinhos.",
|
||||
"channels_createPrivateChannelDesc": "Protegido com uma chave secreta.",
|
||||
"channels_joinPrivateChannelDesc": "Inserir uma chave secreta manualmente.",
|
||||
"channels_createPrivateChannel": "Criar um Canal Privado",
|
||||
|
|
@ -1349,6 +1363,22 @@
|
|||
"channels_scanQrCodeComingSoon": "Em breve",
|
||||
"channels_enterHashtag": "Insira hashtag",
|
||||
"channels_hashtagHint": "ex. #equipe",
|
||||
"@neighbors_unknownContact": {
|
||||
"placeholders": {
|
||||
"pubkey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@neighbors_heardAgo": {
|
||||
"placeholders": {
|
||||
"time": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"neighbors_heardAgo": "Ouvido: {time} atrás",
|
||||
"neighbors_unknownContact": "{pubkey} Desconhecido",
|
||||
"settings_locationGPSEnable": "Ativar GPS",
|
||||
"settings_locationGPSEnableSubtitle": "Habilita a atualização automática da localização via GPS.",
|
||||
"settings_locationIntervalInvalid": "O intervalo deve ser de pelo menos 60 segundos e inferior a 86400 segundos.",
|
||||
|
|
|
|||
|
|
@ -1337,6 +1337,20 @@
|
|||
"listFilter_roomServers": "Servéry miestnosti",
|
||||
"listFilter_unreadOnly": "Nezaregistrované len",
|
||||
"listFilter_newGroup": "Nová skupina",
|
||||
"@neighbors_errorLoading": {
|
||||
"placeholders": {
|
||||
"error": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repeater_neighboursSubtitle": "Zobraziť susedné body bez skokov.",
|
||||
"neighbors_requestTimedOut": "Súďia žiadajú o časové ukončenie.",
|
||||
"neighbors_receivedData": "Obdielo dáta suseda",
|
||||
"repeater_neighbours": "Súsezný",
|
||||
"neighbors_errorLoading": "Chyba pri načítaní susedov: {error}",
|
||||
"neighbors_repeatersNeighbours": "Opakovadlá Súsezná",
|
||||
"neighbors_noData": "Nie je dostupná žiadna informácia o susedoch.",
|
||||
"channels_createPrivateChannel": "Vytvorte súkromný kanál",
|
||||
"channels_joinPrivateChannel": "Pripojiť sa k súkromnému kanálu",
|
||||
"channels_joinPrivateChannelDesc": "Ručne zadajte tajný kľúč.",
|
||||
|
|
@ -1349,6 +1363,22 @@
|
|||
"channels_scanQrCodeComingSoon": "Čoskoro",
|
||||
"channels_enterHashtag": "Zadajte hashtag",
|
||||
"channels_hashtagHint": "napr. #tím",
|
||||
"@neighbors_unknownContact": {
|
||||
"placeholders": {
|
||||
"pubkey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@neighbors_heardAgo": {
|
||||
"placeholders": {
|
||||
"time": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"neighbors_heardAgo": "Počuli sme to: {time} dozadu",
|
||||
"neighbors_unknownContact": "Neznáma {pubkey}",
|
||||
"settings_locationGPSEnable": "Aktivovať GPS",
|
||||
"settings_locationGPSEnableSubtitle": "Povolí automatické aktualizovanie polohy pomocou GPS.",
|
||||
"settings_locationIntervalSec": "Interval pre GPS (Sekundy)",
|
||||
|
|
|
|||
|
|
@ -1337,6 +1337,20 @@
|
|||
"listFilter_roomServers": "Smeti za prostore",
|
||||
"listFilter_unreadOnly": "Nezbrani samo",
|
||||
"listFilter_newGroup": "Nova skupina",
|
||||
"@neighbors_errorLoading": {
|
||||
"placeholders": {
|
||||
"error": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repeater_neighboursSubtitle": "Pogledati nič sosednjih hopjev.",
|
||||
"repeater_neighbours": "Sosedi",
|
||||
"neighbors_receivedData": "Prejeto podatke o sosedih",
|
||||
"neighbors_requestTimedOut": "Sosedi zahtevajo izklop po dogovoru.",
|
||||
"neighbors_errorLoading": "Napaka pri obnašanju sosedov: {error}",
|
||||
"neighbors_repeatersNeighbours": "Ponovitve Sosedi",
|
||||
"neighbors_noData": "Niso na voljo podatki o sosedih.",
|
||||
"channels_joinPrivateChannel": "Pridružite se zasebni skupini",
|
||||
"channels_createPrivateChannelDesc": "Varno zaklenjeno s skrivnim ključem.",
|
||||
"channels_joinPrivateChannelDesc": "Ročno vnesite zaporni ključ.",
|
||||
|
|
@ -1349,6 +1363,22 @@
|
|||
"channels_scanQrCodeComingSoon": "Prihajajoča",
|
||||
"channels_enterHashtag": "Vnesite hashtag",
|
||||
"channels_hashtagHint": "npr. #ekipa",
|
||||
"@neighbors_unknownContact": {
|
||||
"placeholders": {
|
||||
"pubkey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@neighbors_heardAgo": {
|
||||
"placeholders": {
|
||||
"time": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"neighbors_unknownContact": "Nepoznano {pubkey}",
|
||||
"neighbors_heardAgo": "Udeleženec je prejel sporočilo {time} nazaj.",
|
||||
"settings_locationGPSEnable": "Omogoči GPS",
|
||||
"settings_locationGPSEnableSubtitle": "Omogoči samodejno posodabljanje lokacije z GPS-jem.",
|
||||
"settings_locationIntervalSec": "Interval za GPS (Sekunde)",
|
||||
|
|
|
|||
|
|
@ -1337,6 +1337,20 @@
|
|||
"listFilter_roomServers": "Rumservrar",
|
||||
"listFilter_unreadOnly": "Endast oinlästa",
|
||||
"listFilter_newGroup": "Ny grupp",
|
||||
"@neighbors_errorLoading": {
|
||||
"placeholders": {
|
||||
"error": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repeater_neighbours": "Grannar",
|
||||
"repeater_neighboursSubtitle": "Visa noll hoppgrannar.",
|
||||
"neighbors_receivedData": "Mottagna grannars data",
|
||||
"neighbors_requestTimedOut": "Grannar begär tidsinställd utskick.",
|
||||
"neighbors_errorLoading": "Fel vid inläsning av grannar: {error}",
|
||||
"neighbors_repeatersNeighbours": "Upprepar grannar",
|
||||
"neighbors_noData": "Inga grannuppgifter finns tillgängliga.",
|
||||
"channels_createPrivateChannel": "Skapa en privat kanal",
|
||||
"channels_joinPrivateChannel": "Gå med i en Privat Kanal",
|
||||
"channels_joinPrivateChannelDesc": "Ange en hemlig nyckel manuellt.",
|
||||
|
|
@ -1349,6 +1363,22 @@
|
|||
"channels_scanQrCodeComingSoon": "Kommer snart",
|
||||
"channels_enterHashtag": "Ange hashtag",
|
||||
"channels_hashtagHint": "t.ex. #team",
|
||||
"@neighbors_unknownContact": {
|
||||
"placeholders": {
|
||||
"pubkey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@neighbors_heardAgo": {
|
||||
"placeholders": {
|
||||
"time": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"neighbors_heardAgo": "Hördes: {time} sedan",
|
||||
"neighbors_unknownContact": "Okänd {pubkey}",
|
||||
"settings_locationGPSEnable": "Aktivera GPS",
|
||||
"settings_locationGPSEnableSubtitle": "Aktivera automatiska uppdateringar av platsen med hjälp av GPS.",
|
||||
"settings_locationIntervalSec": "Interval för GPS (Sekunder)",
|
||||
|
|
|
|||
|
|
@ -1337,6 +1337,20 @@
|
|||
"listFilter_roomServers": "房间服务器",
|
||||
"listFilter_unreadOnly": "未读消息",
|
||||
"listFilter_newGroup": "新组",
|
||||
"@neighbors_errorLoading": {
|
||||
"placeholders": {
|
||||
"error": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repeater_neighboursSubtitle": "查看零跳邻居。",
|
||||
"repeater_neighbours": "邻居",
|
||||
"neighbors_receivedData": "收到邻居数据",
|
||||
"neighbors_requestTimedOut": "邻居请求超时处理。",
|
||||
"neighbors_errorLoading": "加载邻居时出错:{error}",
|
||||
"neighbors_repeatersNeighbours": "重复器邻居",
|
||||
"neighbors_noData": "没有可用的邻居数据。",
|
||||
"channels_joinPrivateChannel": "加入私密频道",
|
||||
"channels_createPrivateChannelDesc": "使用密钥保护。",
|
||||
"channels_joinPrivateChannelDesc": "手动输入密钥。",
|
||||
|
|
@ -1349,6 +1363,22 @@
|
|||
"channels_scanQrCodeComingSoon": "即将到来",
|
||||
"channels_enterHashtag": "输入标签",
|
||||
"channels_hashtagHint": "例如 #团队",
|
||||
"@neighbors_unknownContact": {
|
||||
"placeholders": {
|
||||
"pubkey": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@neighbors_heardAgo": {
|
||||
"placeholders": {
|
||||
"time": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"neighbors_heardAgo": "听到的时间:{time}前",
|
||||
"neighbors_unknownContact": "未知{pubkey}",
|
||||
"settings_locationGPSEnable": "启用GPS",
|
||||
"settings_locationGPSEnableSubtitle": "启用GPS自动更新位置。",
|
||||
"settings_locationIntervalSec": "GPS 间隔(秒)",
|
||||
|
|
|
|||
456
lib/screens/neighbours_screen.dart
Normal file
456
lib/screens/neighbours_screen.dart
Normal file
|
|
@ -0,0 +1,456 @@
|
|||
import 'dart:async';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../l10n/l10n.dart';
|
||||
import '../models/contact.dart';
|
||||
import '../models/path_selection.dart';
|
||||
import '../connector/meshcore_connector.dart';
|
||||
import '../connector/meshcore_protocol.dart';
|
||||
import '../services/repeater_command_service.dart';
|
||||
import '../widgets/path_management_dialog.dart';
|
||||
import '../widgets/snr_indicator.dart';
|
||||
|
||||
class NeighboursScreen extends StatefulWidget {
|
||||
final Contact repeater;
|
||||
final String password;
|
||||
|
||||
const NeighboursScreen({
|
||||
super.key,
|
||||
required this.repeater,
|
||||
required this.password,
|
||||
});
|
||||
|
||||
@override
|
||||
State<NeighboursScreen> createState() => _NeighboursScreenState();
|
||||
}
|
||||
|
||||
class _NeighboursScreenState extends State<NeighboursScreen> {
|
||||
static const int _reqNeighboursKeyLen = 4;
|
||||
static const int _statusPayloadOffset = 8;
|
||||
static const int _statusStatsSize = 52;
|
||||
static const int _statusResponseBytes =
|
||||
_statusPayloadOffset + _statusStatsSize;
|
||||
Uint8List _tagData = Uint8List(4);
|
||||
int _neighbourCount = 0;
|
||||
|
||||
bool _isLoading = false;
|
||||
bool _isLoaded = false;
|
||||
bool _hasData = false;
|
||||
Timer? _statusTimeout;
|
||||
StreamSubscription<Uint8List>? _frameSubscription;
|
||||
RepeaterCommandService? _commandService;
|
||||
PathSelection? _pendingStatusSelection;
|
||||
List<Map<String, dynamic>>? _parsedNeighbours;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final connector = Provider.of<MeshCoreConnector>(context, listen: false);
|
||||
_commandService = RepeaterCommandService(connector);
|
||||
_setupMessageListener();
|
||||
_loadNeighbours();
|
||||
_hasData = false;
|
||||
}
|
||||
|
||||
void _setupMessageListener() {
|
||||
final connector = Provider.of<MeshCoreConnector>(context, listen: false);
|
||||
|
||||
// Listen for incoming text messages from the repeater
|
||||
_frameSubscription = connector.receivedFrames.listen((frame) {
|
||||
if (frame.isEmpty) return;
|
||||
|
||||
if (frame[0] == respCodeSent) {
|
||||
_tagData = frame.sublist(2, 6);
|
||||
//_timeEstment = frame.buffer.asByteData().getUint32(6, Endian.little);
|
||||
}
|
||||
|
||||
// Check if it's a binary response
|
||||
if (frame[0] == pushCodeBinaryResponse &&
|
||||
listEquals(frame.sublist(2, 6), _tagData)) {
|
||||
_handleNeighboursResponse(connector, frame.sublist(6));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
String fmtDuration(double seconds) {
|
||||
if (seconds < 60) {
|
||||
return '${seconds.toStringAsFixed(1)}s';
|
||||
}
|
||||
|
||||
final int m = (seconds ~/ 60).toInt();
|
||||
final double s = seconds - (60 * m);
|
||||
|
||||
if (m < 60) {
|
||||
return '${m}m ${s.toStringAsFixed(0)}s';
|
||||
}
|
||||
|
||||
final int h = m ~/ 60;
|
||||
final int m2 = m % 60;
|
||||
|
||||
return '${h}h ${m2}m';
|
||||
}
|
||||
|
||||
static List<Map<String, dynamic>> parseNeighboursData(
|
||||
BufferReader buffer,
|
||||
int resultsCount,
|
||||
) {
|
||||
final Map<int, Map<String, dynamic>> neighbours = {};
|
||||
for (var i = 0; i < resultsCount; i++) {
|
||||
final neighbourData = neighbours.putIfAbsent(
|
||||
i,
|
||||
() => {
|
||||
'contact': null,
|
||||
'publicKey': <Uint8List>{},
|
||||
'lastHeard': <int>{},
|
||||
'snr': <double>{},
|
||||
},
|
||||
);
|
||||
neighbourData['publicKey'] = buffer.readBytes(_reqNeighboursKeyLen);
|
||||
neighbourData['lastHeard'] = buffer.readUInt32LE();
|
||||
neighbourData['snr'] = buffer.readInt8() / 4.0;
|
||||
}
|
||||
|
||||
return neighbours.values.toList();
|
||||
}
|
||||
|
||||
void _handleNeighboursResponse(MeshCoreConnector connector, Uint8List frame) {
|
||||
final buffer = BufferReader(frame);
|
||||
final neighbourCount = buffer.readUInt16LE();
|
||||
final parsedNeighbours = parseNeighboursData(buffer, buffer.readUInt16LE());
|
||||
connector.contacts.where((c) => c.type == advTypeRepeater).forEach((
|
||||
repeater,
|
||||
) {
|
||||
for (var neighbourData in parsedNeighbours) {
|
||||
final publicKey = neighbourData['publicKey'];
|
||||
if (listEquals(
|
||||
repeater.publicKey.sublist(0, _reqNeighboursKeyLen),
|
||||
publicKey,
|
||||
)) {
|
||||
neighbourData['contact'] = repeater;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setState(() {
|
||||
_parsedNeighbours = parsedNeighbours;
|
||||
_neighbourCount = neighbourCount;
|
||||
});
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.neighbors_receivedData),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
_statusTimeout?.cancel();
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
_isLoaded = true;
|
||||
_hasData = true;
|
||||
});
|
||||
}
|
||||
|
||||
Contact _resolveRepeater(MeshCoreConnector connector) {
|
||||
return connector.contacts.firstWhere(
|
||||
(c) => c.publicKeyHex == widget.repeater.publicKeyHex,
|
||||
orElse: () => widget.repeater,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _loadNeighbours() async {
|
||||
if (_commandService == null) return;
|
||||
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
_isLoaded = false;
|
||||
});
|
||||
try {
|
||||
final connector = Provider.of<MeshCoreConnector>(context, listen: false);
|
||||
final repeater = _resolveRepeater(connector);
|
||||
final selection = await connector.preparePathForContactSend(repeater);
|
||||
_pendingStatusSelection = selection;
|
||||
|
||||
//[version][number of requested neighbours][offset_16bit][order by][len of public key]
|
||||
final frame = buildSendBinaryReq(
|
||||
repeater.publicKey,
|
||||
payload: Uint8List.fromList([
|
||||
reqTypeGetNeighbours,
|
||||
0x00,
|
||||
0x0F,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
_reqNeighboursKeyLen,
|
||||
]),
|
||||
);
|
||||
await connector.sendFrame(frame);
|
||||
|
||||
final pathLengthValue = selection.useFlood ? -1 : selection.hopCount;
|
||||
final messageBytes = frame.length >= _statusResponseBytes
|
||||
? frame.length
|
||||
: _statusResponseBytes;
|
||||
final timeoutMs = connector.calculateTimeout(
|
||||
pathLength: pathLengthValue,
|
||||
messageBytes: messageBytes,
|
||||
);
|
||||
_statusTimeout?.cancel();
|
||||
_statusTimeout = Timer(Duration(milliseconds: timeoutMs), () {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
_isLoaded = false;
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.neighbors_requestTimedOut),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
_recordStatusResult(false);
|
||||
});
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
_isLoaded = false;
|
||||
});
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.neighbors_errorLoading(e.toString())),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _recordStatusResult(bool success) {
|
||||
final selection = _pendingStatusSelection;
|
||||
if (selection == null) return;
|
||||
final connector = Provider.of<MeshCoreConnector>(context, listen: false);
|
||||
final repeater = _resolveRepeater(connector);
|
||||
connector.recordRepeaterPathResult(repeater, selection, success, null);
|
||||
_pendingStatusSelection = null;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_frameSubscription?.cancel();
|
||||
_commandService?.dispose();
|
||||
_statusTimeout?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
final connector = context.watch<MeshCoreConnector>();
|
||||
final repeater = _resolveRepeater(connector);
|
||||
final isFloodMode = repeater.pathOverride == -1;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
l10n.neighbors_repeatersNeighbours,
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
Text(
|
||||
repeater.name,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
centerTitle: false,
|
||||
actions: [
|
||||
PopupMenuButton<String>(
|
||||
icon: Icon(isFloodMode ? Icons.waves : Icons.route),
|
||||
tooltip: l10n.repeater_routingMode,
|
||||
onSelected: (mode) async {
|
||||
if (mode == 'flood') {
|
||||
await connector.setPathOverride(repeater, pathLen: -1);
|
||||
} else {
|
||||
await connector.setPathOverride(repeater, pathLen: null);
|
||||
}
|
||||
},
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
value: 'auto',
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.auto_mode,
|
||||
size: 20,
|
||||
color: !isFloodMode
|
||||
? Theme.of(context).primaryColor
|
||||
: null,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
l10n.repeater_autoUseSavedPath,
|
||||
style: TextStyle(
|
||||
fontWeight: !isFloodMode
|
||||
? FontWeight.bold
|
||||
: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'flood',
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.waves,
|
||||
size: 20,
|
||||
color: isFloodMode
|
||||
? Theme.of(context).primaryColor
|
||||
: null,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
l10n.repeater_forceFloodMode,
|
||||
style: TextStyle(
|
||||
fontWeight: isFloodMode
|
||||
? FontWeight.bold
|
||||
: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.timeline),
|
||||
tooltip: l10n.repeater_pathManagement,
|
||||
onPressed: () =>
|
||||
PathManagementDialog.show(context, contact: repeater),
|
||||
),
|
||||
IconButton(
|
||||
icon: _isLoading
|
||||
? const SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
)
|
||||
: const Icon(Icons.refresh),
|
||||
onPressed: _isLoading ? null : _loadNeighbours,
|
||||
tooltip: l10n.repeater_refresh,
|
||||
),
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
top: false,
|
||||
child: RefreshIndicator(
|
||||
onRefresh: _loadNeighbours,
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
if (!_isLoaded &&
|
||||
!_hasData &&
|
||||
(_parsedNeighbours == null || _parsedNeighbours!.isEmpty))
|
||||
Center(
|
||||
child: Text(
|
||||
l10n.neighbors_noData,
|
||||
style: TextStyle(fontSize: 16, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
if (_isLoaded ||
|
||||
_hasData &&
|
||||
!(_parsedNeighbours == null ||
|
||||
_parsedNeighbours!.isEmpty))
|
||||
_buildNeighboursInfoCard(
|
||||
"${l10n.repeater_neighbours} - $_neighbourCount",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildNeighboursInfoCard(String title) {
|
||||
final connector = Provider.of<MeshCoreConnector>(context, listen: false);
|
||||
return Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.info_outline,
|
||||
color: Theme.of(context).textTheme.headlineSmall?.color,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Divider(),
|
||||
for (final entry in _parsedNeighbours!.asMap().entries)
|
||||
_buildInfoRow(
|
||||
entry.value['contact'] != null
|
||||
? entry.value['contact'].name
|
||||
: context.l10n.neighbors_unknownContact(
|
||||
"<${pubKeyToHex(entry.value['publicKey'])}>",
|
||||
),
|
||||
context.l10n.neighbors_heardAgo(
|
||||
fmtDuration(entry.value['lastHeard'] + 0.0),
|
||||
),
|
||||
entry.value['snr'],
|
||||
connector.currentSf!,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInfoRow(
|
||||
String label,
|
||||
String value,
|
||||
double snr,
|
||||
int spreadingFactor,
|
||||
) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 3),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Text(
|
||||
label,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
subtitle: Text(value),
|
||||
trailing: SNRIcon(
|
||||
snr: snr,
|
||||
snrLevels: getSNRfromSF(spreadingFactor),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import 'repeater_status_screen.dart';
|
|||
import 'repeater_cli_screen.dart';
|
||||
import 'repeater_settings_screen.dart';
|
||||
import 'telemetry_screen.dart';
|
||||
import 'neighbours_screen.dart';
|
||||
|
||||
class RepeaterHubScreen extends StatelessWidget {
|
||||
final Contact repeater;
|
||||
|
|
@ -169,13 +170,33 @@ class RepeaterHubScreen extends StatelessWidget {
|
|||
},
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
// Neighbors button
|
||||
_buildManagementCard(
|
||||
context,
|
||||
icon: Icons.group,
|
||||
title: l10n.repeater_neighbours,
|
||||
subtitle: l10n.repeater_neighboursSubtitle,
|
||||
color: Colors.orange,
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => NeighboursScreen(
|
||||
repeater: repeater,
|
||||
password: password,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
// Settings button
|
||||
_buildManagementCard(
|
||||
context,
|
||||
icon: Icons.settings,
|
||||
title: l10n.repeater_settings,
|
||||
subtitle: l10n.repeater_settingsSubtitle,
|
||||
color: Colors.orange,
|
||||
color: Colors.deepOrange,
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
|
|
|
|||
62
lib/widgets/snr_indicator.dart
Normal file
62
lib/widgets/snr_indicator.dart
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
List<double> getSNRfromSF(int spreadingFactor) {
|
||||
switch (spreadingFactor) {
|
||||
case 7:
|
||||
return [4.0, -2.0, -4.0, -6.0];
|
||||
case 8:
|
||||
return [4.0, -4.0, -6.0, -8.0];
|
||||
case 9:
|
||||
return [4.0, -6.0, -8.0, -10.0];
|
||||
case 10:
|
||||
return [4.0, -8.0, -10.0, -13.0];
|
||||
case 11:
|
||||
return [4.0, -10.0, -12.5, -15.0];
|
||||
case 12:
|
||||
return [4.0, -12.5, -15.0, -18.0];
|
||||
default:
|
||||
return []; // Or throw Exception('Invalid SF: $spreadingFactor');
|
||||
}
|
||||
}
|
||||
|
||||
class SNRIcon extends StatelessWidget {
|
||||
final double snr;
|
||||
final List<double> snrLevels;
|
||||
|
||||
const SNRIcon({
|
||||
super.key,
|
||||
required this.snr,
|
||||
this.snrLevels = const [4.0, -2.0, -4.0, -6.0],
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
IconData icon;
|
||||
Color color;
|
||||
|
||||
if (snr >= snrLevels[0]) {
|
||||
icon = Icons.signal_cellular_alt;
|
||||
color = Colors.green;
|
||||
} else if (snr >= snrLevels[1]) {
|
||||
icon = Icons.signal_cellular_alt;
|
||||
color = Colors.lightGreen;
|
||||
} else if (snr >= snrLevels[2]) {
|
||||
icon = Icons.signal_cellular_alt;
|
||||
color = Colors.yellow;
|
||||
} else if (snr >= snrLevels[3]) {
|
||||
icon = Icons.signal_cellular_alt_2_bar;
|
||||
color = Colors.orange;
|
||||
} else {
|
||||
icon = Icons.signal_cellular_alt_1_bar;
|
||||
color = Colors.red;
|
||||
}
|
||||
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(icon, color: color),
|
||||
Text('$snr dB', style: TextStyle(fontSize: 10, color: color)),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -891,4 +891,4 @@ def translate_locale(
|
|||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
raise SystemExit(main())
|
||||
1
untranslated.json
Normal file
1
untranslated.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
Loading…
Add table
Add a link
Reference in a new issue