From bdd7fc0cdd32c449cefb62536855e26887a8a4b9 Mon Sep 17 00:00:00 2001 From: "Enot (ded) Skelly" Date: Wed, 8 Apr 2026 14:56:34 -0700 Subject: [PATCH 1/5] remove unused macos path_provider_foundation added in #299 but appears not needed, flutter removes when building --- macos/Flutter/GeneratedPluginRegistrant.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 2428a77..ffc8c59 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -9,7 +9,6 @@ import flutter_blue_plus_darwin import flutter_local_notifications import mobile_scanner import package_info_plus -import path_provider_foundation import share_plus import shared_preferences_foundation import sqflite_darwin @@ -20,7 +19,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) - PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) From 7dc162d9683be55e2be8abf3cac57847c40d6f78 Mon Sep 17 00:00:00 2001 From: "Enot (ded) Skelly" Date: Fri, 10 Apr 2026 14:14:31 -0700 Subject: [PATCH 2/5] temp translations fix --- lib/l10n/app_en.arb | 7 +- lib/l10n/app_localizations.dart | 22 ++- lib/l10n/app_localizations_bg.dart | 9 ++ lib/l10n/app_localizations_de.dart | 9 ++ lib/l10n/app_localizations_en.dart | 13 +- lib/l10n/app_localizations_es.dart | 9 ++ lib/l10n/app_localizations_fr.dart | 9 ++ lib/l10n/app_localizations_hu.dart | 9 ++ lib/l10n/app_localizations_it.dart | 9 ++ lib/l10n/app_localizations_ja.dart | 9 ++ lib/l10n/app_localizations_ko.dart | 9 ++ lib/l10n/app_localizations_nl.dart | 9 ++ lib/l10n/app_localizations_pl.dart | 9 ++ lib/l10n/app_localizations_pt.dart | 9 ++ lib/l10n/app_localizations_ru.dart | 9 ++ lib/l10n/app_localizations_sk.dart | 9 ++ lib/l10n/app_localizations_sl.dart | 9 ++ lib/l10n/app_localizations_sv.dart | 9 ++ lib/l10n/app_localizations_uk.dart | 9 ++ lib/l10n/app_localizations_zh.dart | 9 ++ lib/screens/contacts_screen.dart | 17 ++- lib/screens/map_screen.dart | 12 +- lib/screens/repeater_hub_screen.dart | 196 +++++++++++++------------ lib/widgets/repeater_login_dialog.dart | 17 ++- lib/widgets/room_login_dialog.dart | 15 +- untranslated.json | 85 ++++++++--- 26 files changed, 403 insertions(+), 134 deletions(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index ffdf21d..f104480 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1038,8 +1038,8 @@ "login_enterPassword": "Enter password", "login_savePassword": "Save password", "login_savePasswordSubtitle": "Password will be stored securely on this device", - "login_repeaterDescription": "Enter the repeater password to access settings and status.", - "login_roomDescription": "Enter the room password to access settings and status.", + "login_repeaterDescription": "Enter the repeater password for guest or admin access.", + "login_roomDescription": "Enter the room password for guest or admin access.", "login_routing": "Routing", "login_routingMode": "Routing mode", "login_autoUseSavedPath": "Auto (use saved path)", @@ -1105,7 +1105,10 @@ "path_setPath": "Set Path", "repeater_management": "Repeater Management", "room_management": "Room Server Management", + "repeater_guest": "Repeater Information", + "room_guest": "Room Server Information", "repeater_managementTools": "Management Tools", + "repeater_guestTools": "Guest Tools", "repeater_status": "Status", "repeater_statusSubtitle": "View repeater status, stats, and neighbors", "repeater_telemetry": "Telemetry", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index bb390d5..3b89d69 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -3438,13 +3438,13 @@ abstract class AppLocalizations { /// No description provided for @login_repeaterDescription. /// /// In en, this message translates to: - /// **'Enter the repeater password to access settings and status.'** + /// **'Enter the repeater password for guest or admin access.'** String get login_repeaterDescription; /// No description provided for @login_roomDescription. /// /// In en, this message translates to: - /// **'Enter the room password to access settings and status.'** + /// **'Enter the room password for guest or admin access.'** String get login_roomDescription; /// No description provided for @login_routing. @@ -3609,12 +3609,30 @@ abstract class AppLocalizations { /// **'Room Server Management'** String get room_management; + /// No description provided for @repeater_guest. + /// + /// In en, this message translates to: + /// **'Repeater Information'** + String get repeater_guest; + + /// No description provided for @room_guest. + /// + /// In en, this message translates to: + /// **'Room Server Information'** + String get room_guest; + /// No description provided for @repeater_managementTools. /// /// In en, this message translates to: /// **'Management Tools'** String get repeater_managementTools; + /// No description provided for @repeater_guestTools. + /// + /// In en, this message translates to: + /// **'Guest Tools'** + String get repeater_guestTools; + /// No description provided for @repeater_status. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_bg.dart b/lib/l10n/app_localizations_bg.dart index bec54df..ec12a78 100644 --- a/lib/l10n/app_localizations_bg.dart +++ b/lib/l10n/app_localizations_bg.dart @@ -2019,9 +2019,18 @@ class AppLocalizationsBg extends AppLocalizations { @override String get room_management => 'Управление на сървъра за стая'; + @override + String get repeater_guest => 'Repeater Information'; + + @override + String get room_guest => 'Room Server Information'; + @override String get repeater_managementTools => 'Инструменти за управление'; + @override + String get repeater_guestTools => 'Guest Tools'; + @override String get repeater_status => 'Статус'; diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 078c9e9..4d3aa4b 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -2017,9 +2017,18 @@ class AppLocalizationsDe extends AppLocalizations { @override String get room_management => 'Raum-Server-Verwaltung'; + @override + String get repeater_guest => 'Repeater Information'; + + @override + String get room_guest => 'Room Server Information'; + @override String get repeater_managementTools => 'Verwaltungs-Tools'; + @override + String get repeater_guestTools => 'Guest Tools'; + @override String get repeater_status => 'Status'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index d7a79bd..73a9308 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -1871,11 +1871,11 @@ class AppLocalizationsEn extends AppLocalizations { @override String get login_repeaterDescription => - 'Enter the repeater password to access settings and status.'; + 'Enter the repeater password for guest or admin access.'; @override String get login_roomDescription => - 'Enter the room password to access settings and status.'; + 'Enter the room password for guest or admin access.'; @override String get login_routing => 'Routing'; @@ -1979,9 +1979,18 @@ class AppLocalizationsEn extends AppLocalizations { @override String get room_management => 'Room Server Management'; + @override + String get repeater_guest => 'Repeater Information'; + + @override + String get room_guest => 'Room Server Information'; + @override String get repeater_managementTools => 'Management Tools'; + @override + String get repeater_guestTools => 'Guest Tools'; + @override String get repeater_status => 'Status'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index 9a56c6d..0dfde3f 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -2015,9 +2015,18 @@ class AppLocalizationsEs extends AppLocalizations { @override String get room_management => 'Administración del Servidor de Habitación'; + @override + String get repeater_guest => 'Repeater Information'; + + @override + String get room_guest => 'Room Server Information'; + @override String get repeater_managementTools => 'Herramientas de Gestión'; + @override + String get repeater_guestTools => 'Guest Tools'; + @override String get repeater_status => 'Estado'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index 4ce4a75..6b57723 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -2026,9 +2026,18 @@ class AppLocalizationsFr extends AppLocalizations { @override String get room_management => 'Administrattion Room Server'; + @override + String get repeater_guest => 'Repeater Information'; + + @override + String get room_guest => 'Room Server Information'; + @override String get repeater_managementTools => 'Outils de Gestion'; + @override + String get repeater_guestTools => 'Guest Tools'; + @override String get repeater_status => 'État'; diff --git a/lib/l10n/app_localizations_hu.dart b/lib/l10n/app_localizations_hu.dart index 5d305ee..052a3c7 100644 --- a/lib/l10n/app_localizations_hu.dart +++ b/lib/l10n/app_localizations_hu.dart @@ -2030,9 +2030,18 @@ class AppLocalizationsHu extends AppLocalizations { @override String get room_management => 'Szoba-szerver kezelés'; + @override + String get repeater_guest => 'Repeater Information'; + + @override + String get room_guest => 'Room Server Information'; + @override String get repeater_managementTools => 'Menedzsmentes eszközök'; + @override + String get repeater_guestTools => 'Guest Tools'; + @override String get repeater_status => 'Állapot'; diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index 98cbfcb..6f0d5ea 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -2016,9 +2016,18 @@ class AppLocalizationsIt extends AppLocalizations { @override String get room_management => 'Gestione del Server di Camera'; + @override + String get repeater_guest => 'Repeater Information'; + + @override + String get room_guest => 'Room Server Information'; + @override String get repeater_managementTools => 'Strumenti di Gestione'; + @override + String get repeater_guestTools => 'Guest Tools'; + @override String get repeater_status => 'Stato'; diff --git a/lib/l10n/app_localizations_ja.dart b/lib/l10n/app_localizations_ja.dart index fc59852..9ffc8ab 100644 --- a/lib/l10n/app_localizations_ja.dart +++ b/lib/l10n/app_localizations_ja.dart @@ -1932,9 +1932,18 @@ class AppLocalizationsJa extends AppLocalizations { @override String get room_management => 'ルームサーバーの管理'; + @override + String get repeater_guest => 'Repeater Information'; + + @override + String get room_guest => 'Room Server Information'; + @override String get repeater_managementTools => '管理ツール'; + @override + String get repeater_guestTools => 'Guest Tools'; + @override String get repeater_status => 'ステータス'; diff --git a/lib/l10n/app_localizations_ko.dart b/lib/l10n/app_localizations_ko.dart index b0d849b..9142657 100644 --- a/lib/l10n/app_localizations_ko.dart +++ b/lib/l10n/app_localizations_ko.dart @@ -1929,9 +1929,18 @@ class AppLocalizationsKo extends AppLocalizations { @override String get room_management => '방 서버 관리'; + @override + String get repeater_guest => 'Repeater Information'; + + @override + String get room_guest => 'Room Server Information'; + @override String get repeater_managementTools => '관리 도구'; + @override + String get repeater_guestTools => 'Guest Tools'; + @override String get repeater_status => '상태'; diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index 6fcad22..b191919 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -2003,9 +2003,18 @@ class AppLocalizationsNl extends AppLocalizations { @override String get room_management => 'Beheer Server Kamer'; + @override + String get repeater_guest => 'Repeater Information'; + + @override + String get room_guest => 'Room Server Information'; + @override String get repeater_managementTools => 'Beheerfuncties'; + @override + String get repeater_guestTools => 'Guest Tools'; + @override String get repeater_status => 'Status'; diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 8492702..ce9549d 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -2031,9 +2031,18 @@ class AppLocalizationsPl extends AppLocalizations { @override String get room_management => 'Zarządzanie Serwerem Pokoju'; + @override + String get repeater_guest => 'Repeater Information'; + + @override + String get room_guest => 'Room Server Information'; + @override String get repeater_managementTools => 'Narzędzia Zarządzania'; + @override + String get repeater_guestTools => 'Guest Tools'; + @override String get repeater_status => 'Status'; diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart index ce7c9e8..7679d27 100644 --- a/lib/l10n/app_localizations_pt.dart +++ b/lib/l10n/app_localizations_pt.dart @@ -2015,9 +2015,18 @@ class AppLocalizationsPt extends AppLocalizations { @override String get room_management => 'Gerenciamento de Servidor de Sala'; + @override + String get repeater_guest => 'Repeater Information'; + + @override + String get room_guest => 'Room Server Information'; + @override String get repeater_managementTools => 'Ferramentas de Gerenciamento'; + @override + String get repeater_guestTools => 'Guest Tools'; + @override String get repeater_status => 'Status'; diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index 4557885..493eeb4 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -2019,9 +2019,18 @@ class AppLocalizationsRu extends AppLocalizations { @override String get room_management => 'Управление сервером комнат'; + @override + String get repeater_guest => 'Repeater Information'; + + @override + String get room_guest => 'Room Server Information'; + @override String get repeater_managementTools => 'Инструменты управления'; + @override + String get repeater_guestTools => 'Guest Tools'; + @override String get repeater_status => 'Статус'; diff --git a/lib/l10n/app_localizations_sk.dart b/lib/l10n/app_localizations_sk.dart index b59d6d8..e8104fa 100644 --- a/lib/l10n/app_localizations_sk.dart +++ b/lib/l10n/app_localizations_sk.dart @@ -2004,9 +2004,18 @@ class AppLocalizationsSk extends AppLocalizations { @override String get room_management => 'Správa servera miestnosti'; + @override + String get repeater_guest => 'Repeater Information'; + + @override + String get room_guest => 'Room Server Information'; + @override String get repeater_managementTools => 'Nástroje na správu'; + @override + String get repeater_guestTools => 'Guest Tools'; + @override String get repeater_status => 'Status'; diff --git a/lib/l10n/app_localizations_sl.dart b/lib/l10n/app_localizations_sl.dart index e5bf031..cfef170 100644 --- a/lib/l10n/app_localizations_sl.dart +++ b/lib/l10n/app_localizations_sl.dart @@ -2001,9 +2001,18 @@ class AppLocalizationsSl extends AppLocalizations { @override String get room_management => 'Upravljanje stremlišča'; + @override + String get repeater_guest => 'Repeater Information'; + + @override + String get room_guest => 'Room Server Information'; + @override String get repeater_managementTools => 'Upravne orodje'; + @override + String get repeater_guestTools => 'Guest Tools'; + @override String get repeater_status => 'Status'; diff --git a/lib/l10n/app_localizations_sv.dart b/lib/l10n/app_localizations_sv.dart index 731b846..d3ca0bf 100644 --- a/lib/l10n/app_localizations_sv.dart +++ b/lib/l10n/app_localizations_sv.dart @@ -1990,9 +1990,18 @@ class AppLocalizationsSv extends AppLocalizations { @override String get room_management => 'Rumserverhantering'; + @override + String get repeater_guest => 'Repeater Information'; + + @override + String get room_guest => 'Room Server Information'; + @override String get repeater_managementTools => 'Administrationsverktyg'; + @override + String get repeater_guestTools => 'Guest Tools'; + @override String get repeater_status => 'Status'; diff --git a/lib/l10n/app_localizations_uk.dart b/lib/l10n/app_localizations_uk.dart index 1b5a800..fe568e7 100644 --- a/lib/l10n/app_localizations_uk.dart +++ b/lib/l10n/app_localizations_uk.dart @@ -2014,9 +2014,18 @@ class AppLocalizationsUk extends AppLocalizations { @override String get room_management => 'Адміністрування сервера кімнати'; + @override + String get repeater_guest => 'Repeater Information'; + + @override + String get room_guest => 'Room Server Information'; + @override String get repeater_managementTools => 'Інструменти керування'; + @override + String get repeater_guestTools => 'Guest Tools'; + @override String get repeater_status => 'Статус'; diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index acadc58..e496cd0 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -1890,9 +1890,18 @@ class AppLocalizationsZh extends AppLocalizations { @override String get room_management => '房间服务器管理'; + @override + String get repeater_guest => 'Repeater Information'; + + @override + String get room_guest => 'Room Server Information'; + @override String get repeater_managementTools => '管理工具'; + @override + String get repeater_guestTools => 'Guest Tools'; + @override String get repeater_status => '状态'; diff --git a/lib/screens/contacts_screen.dart b/lib/screens/contacts_screen.dart index 46e2be6..50c47dc 100644 --- a/lib/screens/contacts_screen.dart +++ b/lib/screens/contacts_screen.dart @@ -963,13 +963,16 @@ class _ContactsScreenState extends State context: context, builder: (context) => RepeaterLoginDialog( repeater: repeater, - onLogin: (password) { + onLogin: (password, isAdmin) { // Navigate to repeater hub screen after successful login Navigator.push( context, MaterialPageRoute( - builder: (context) => - RepeaterHubScreen(repeater: repeater, password: password), + builder: (context) => RepeaterHubScreen( + repeater: repeater, + password: password, + isAdmin: isAdmin, + ), ), ); }, @@ -986,14 +989,18 @@ class _ContactsScreenState extends State context: context, builder: (context) => RoomLoginDialog( room: room, - onLogin: (password) { + onLogin: (password, isAdmin) { context.read().markContactRead(room.publicKeyHex); Navigator.push( context, MaterialPageRoute( builder: (context) => destination == RoomLoginDestination.management - ? RepeaterHubScreen(repeater: room, password: password) + ? RepeaterHubScreen( + repeater: room, + password: password, + isAdmin: isAdmin, + ) : ChatScreen(contact: room), ), ); diff --git a/lib/screens/map_screen.dart b/lib/screens/map_screen.dart index f2d09f3..de801ee 100644 --- a/lib/screens/map_screen.dart +++ b/lib/screens/map_screen.dart @@ -1366,13 +1366,16 @@ class _MapScreenState extends State { context: context, builder: (context) => RepeaterLoginDialog( repeater: repeater, - onLogin: (password) { + onLogin: (password, isAdmin) { // Navigate to repeater hub screen after successful login Navigator.push( context, MaterialPageRoute( - builder: (context) => - RepeaterHubScreen(repeater: repeater, password: password), + builder: (context) => RepeaterHubScreen( + repeater: repeater, + password: password, + isAdmin: isAdmin, + ), ), ); }, @@ -1385,7 +1388,8 @@ class _MapScreenState extends State { context: context, builder: (context) => RoomLoginDialog( room: room, - onLogin: (password) { + // onLogin(password, isAdmin) isAdmin not used for room caht screen + onLogin: (password, _) { // Navigate to chat screen after successful login context.read().markContactRead(room.publicKeyHex); Navigator.push( diff --git a/lib/screens/repeater_hub_screen.dart b/lib/screens/repeater_hub_screen.dart index 8a14253..0dc141c 100644 --- a/lib/screens/repeater_hub_screen.dart +++ b/lib/screens/repeater_hub_screen.dart @@ -13,11 +13,13 @@ import 'neighbors_screen.dart'; class RepeaterHubScreen extends StatelessWidget { final Contact repeater; final String password; + final bool isAdmin; const RepeaterHubScreen({ super.key, required this.repeater, required this.password, + required this.isAdmin, }); @override @@ -33,11 +35,18 @@ class RepeaterHubScreen extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - Text( - repeater.type == advTypeRepeater - ? l10n.repeater_management - : l10n.room_management, - ), + if (isAdmin) + Text( + repeater.type == advTypeRepeater + ? l10n.repeater_management + : l10n.room_management, + ), + if (!isAdmin) + Text( + repeater.type == advTypeRepeater + ? l10n.repeater_guest + : l10n.room_guest, + ), Text( repeater.name, style: const TextStyle( @@ -113,64 +122,67 @@ class RepeaterHubScreen extends StatelessWidget { ), ), const SizedBox(height: 24), - Card( - child: Padding( - padding: const EdgeInsets.fromLTRB(16, 16, 16, 12), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - const Icon(Icons.battery_full), - const SizedBox(width: 10), - Expanded( - child: Text( - l10n.appSettings_batteryChemistry, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, + if (isAdmin) + Card( + child: Padding( + padding: const EdgeInsets.fromLTRB(16, 16, 16, 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Icon(Icons.battery_full), + const SizedBox(width: 10), + Expanded( + child: Text( + l10n.appSettings_batteryChemistry, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + ), ), ), - ), - ], - ), - const SizedBox(height: 12), - DropdownButtonFormField( - initialValue: chemistry, - isExpanded: true, - decoration: const InputDecoration( - border: UnderlineInputBorder(), - isDense: true, + ], ), - onChanged: (value) { - if (value == null) return; - settingsService.setBatteryChemistryForRepeater( - repeater.publicKeyHex, - value, - ); - }, - items: [ - DropdownMenuItem( - value: 'nmc', - child: Text(l10n.appSettings_batteryNmc), + const SizedBox(height: 12), + DropdownButtonFormField( + initialValue: chemistry, + isExpanded: true, + decoration: const InputDecoration( + border: UnderlineInputBorder(), + isDense: true, ), - DropdownMenuItem( - value: 'lifepo4', - child: Text(l10n.appSettings_batteryLifepo4), - ), - DropdownMenuItem( - value: 'lipo', - child: Text(l10n.appSettings_batteryLipo), - ), - ], - ), - ], + onChanged: (value) { + if (value == null) return; + settingsService.setBatteryChemistryForRepeater( + repeater.publicKeyHex, + value, + ); + }, + items: [ + DropdownMenuItem( + value: 'nmc', + child: Text(l10n.appSettings_batteryNmc), + ), + DropdownMenuItem( + value: 'lifepo4', + child: Text(l10n.appSettings_batteryLifepo4), + ), + DropdownMenuItem( + value: 'lipo', + child: Text(l10n.appSettings_batteryLipo), + ), + ], + ), + ], + ), ), ), - ), const SizedBox(height: 24), Text( - l10n.repeater_managementTools, + isAdmin + ? l10n.repeater_managementTools + : l10n.repeater_guestTools, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 16), @@ -210,26 +222,27 @@ class RepeaterHubScreen extends StatelessWidget { ); }, ), - const SizedBox(height: 12), + if (isAdmin) const SizedBox(height: 12), // CLI button - _buildManagementCard( - context, - icon: Icons.terminal, - title: l10n.repeater_cli, - subtitle: l10n.repeater_cliSubtitle, - color: Colors.green, - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => RepeaterCliScreen( - repeater: repeater, - password: password, + if (isAdmin) + _buildManagementCard( + context, + icon: Icons.terminal, + title: l10n.repeater_cli, + subtitle: l10n.repeater_cliSubtitle, + color: Colors.green, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => RepeaterCliScreen( + repeater: repeater, + password: password, + ), ), - ), - ); - }, - ), + ); + }, + ), const SizedBox(height: 12), // Neighbors button _buildManagementCard( @@ -248,26 +261,27 @@ class RepeaterHubScreen extends StatelessWidget { ); }, ), - const SizedBox(height: 12), + if (isAdmin) const SizedBox(height: 12), // Settings button - _buildManagementCard( - context, - icon: Icons.settings, - title: l10n.repeater_settings, - subtitle: l10n.repeater_settingsSubtitle, - color: Colors.deepOrange, - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => RepeaterSettingsScreen( - repeater: repeater, - password: password, + if (isAdmin) + _buildManagementCard( + context, + icon: Icons.settings, + title: l10n.repeater_settings, + subtitle: l10n.repeater_settingsSubtitle, + color: Colors.deepOrange, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => RepeaterSettingsScreen( + repeater: repeater, + password: password, + ), ), - ), - ); - }, - ), + ); + }, + ), ], ), ), diff --git a/lib/widgets/repeater_login_dialog.dart b/lib/widgets/repeater_login_dialog.dart index 48bb6ac..f4db904 100644 --- a/lib/widgets/repeater_login_dialog.dart +++ b/lib/widgets/repeater_login_dialog.dart @@ -14,7 +14,7 @@ import 'path_management_dialog.dart'; class RepeaterLoginDialog extends StatefulWidget { final Contact repeater; - final Function(String password) onLogin; + final Function(String password, bool isAdmin) onLogin; const RepeaterLoginDialog({ super.key, @@ -119,6 +119,7 @@ class _RepeaterLoginDialogState extends State { : '${selection.hopCount} hops'; appLogger.info('Login routing: $selectionLabel', tag: 'RepeaterLogin'); bool? loginResult; + bool isAdmin = false; for (int attempt = 0; attempt < _maxAttempts; attempt++) { if (!mounted) return; setState(() { @@ -131,7 +132,7 @@ class _RepeaterLoginDialogState extends State { ); await _connector.sendFrame(loginFrame); - loginResult = await _awaitLoginResponse(timeout); + (loginResult, isAdmin) = await _awaitLoginResponse(timeout); if (loginResult == true) { appLogger.info( 'Login succeeded for ${repeater.name}', @@ -189,7 +190,7 @@ class _RepeaterLoginDialogState extends State { if (mounted) { Navigator.pop(context, password); - Future.microtask(() => widget.onLogin(password)); + Future.microtask(() => widget.onLogin(password, isAdmin)); } } catch (e) { final repeater = _resolveRepeater(_connector); @@ -206,17 +207,21 @@ class _RepeaterLoginDialogState extends State { } } - Future _awaitLoginResponse(Duration timeout) async { + // _awaitLoginResponse returns a record of bool, for success and if the client is an admin + Future<(bool?, bool)> _awaitLoginResponse(Duration timeout) async { final completer = Completer(); Timer? timer; StreamSubscription? subscription; final targetPrefix = widget.repeater.publicKey.sublist(0, 6); - + bool isAdmin = false; subscription = _connector.receivedFrames.listen((frame) { if (frame.isEmpty) return; final code = frame[0]; if (code != pushCodeLoginSuccess && code != pushCodeLoginFail) return; if (frame.length < 8) return; + // NOTE: a bug in the repeater firmware only ever sends 1 or 0 back, not the + // expected client permissions + isAdmin = (frame[1] == 1); final prefix = frame.sublist(2, 8); if (!listEquals(prefix, targetPrefix)) return; @@ -235,7 +240,7 @@ class _RepeaterLoginDialogState extends State { final result = await completer.future; timer.cancel(); await subscription.cancel(); - return result; + return (result, isAdmin); } @override diff --git a/lib/widgets/room_login_dialog.dart b/lib/widgets/room_login_dialog.dart index 3a923fe..2206227 100644 --- a/lib/widgets/room_login_dialog.dart +++ b/lib/widgets/room_login_dialog.dart @@ -14,7 +14,7 @@ import 'path_management_dialog.dart'; class RoomLoginDialog extends StatefulWidget { final Contact room; - final Function(String password) onLogin; + final Function(String password, bool isAdmin) onLogin; const RoomLoginDialog({super.key, required this.room, required this.onLogin}); @@ -114,6 +114,7 @@ class _RoomLoginDialogState extends State { : '${selection.hopCount} hops'; appLogger.info('Login routing: $selectionLabel', tag: 'RoomLogin'); bool? loginResult; + bool isAdmin = false; for (int attempt = 0; attempt < _maxAttempts; attempt++) { if (!mounted) return; setState(() { @@ -126,7 +127,7 @@ class _RoomLoginDialogState extends State { ); await _connector.sendFrame(loginFrame); - loginResult = await _awaitLoginResponse(timeout); + (loginResult, isAdmin) = await _awaitLoginResponse(timeout); if (loginResult == true) { appLogger.info('Login succeeded for ${room.name}', tag: 'RoomLogin'); break; @@ -166,7 +167,7 @@ class _RoomLoginDialogState extends State { if (mounted) { Navigator.pop(context, password); - Future.microtask(() => widget.onLogin(password)); + Future.microtask(() => widget.onLogin(password, isAdmin)); } } catch (e) { final room = _resolveRepeater(_connector); @@ -185,16 +186,20 @@ class _RoomLoginDialogState extends State { } } - Future _awaitLoginResponse(Duration timeout) async { + Future<(bool?, bool)> _awaitLoginResponse(Duration timeout) async { final completer = Completer(); Timer? timer; StreamSubscription? subscription; final targetPrefix = widget.room.publicKey.sublist(0, 6); + bool isAdmin = false; subscription = _connector.receivedFrames.listen((frame) { if (frame.isEmpty) return; final code = frame[0]; if (code != pushCodeLoginSuccess && code != pushCodeLoginFail) return; + // NOTE: a bug in the repeater firmware only ever sends 1 or 0 back, not the + // expected client permissions + isAdmin = (frame[1] == 1); if (frame.length < 8) return; final prefix = frame.sublist(2, 8); if (!listEquals(prefix, targetPrefix)) return; @@ -214,7 +219,7 @@ class _RoomLoginDialogState extends State { final result = await completer.future; timer.cancel(); await subscription.cancel(); - return result; + return (result, isAdmin); } @override diff --git a/untranslated.json b/untranslated.json index 1ebd9bc..2b4bbbc 100644 --- a/untranslated.json +++ b/untranslated.json @@ -1,69 +1,120 @@ { "bg": [ - "chat_sendMessage" + "chat_sendMessage", + "repeater_guest", + "room_guest", + "repeater_guestTools" ], "de": [ - "chat_sendMessage" + "chat_sendMessage", + "repeater_guest", + "room_guest", + "repeater_guestTools" ], "es": [ - "chat_sendMessage" + "chat_sendMessage", + "repeater_guest", + "room_guest", + "repeater_guestTools" ], "fr": [ - "chat_sendMessage" + "chat_sendMessage", + "repeater_guest", + "room_guest", + "repeater_guestTools" ], "hu": [ - "chat_sendMessage" + "chat_sendMessage", + "repeater_guest", + "room_guest", + "repeater_guestTools" ], "it": [ - "chat_sendMessage" + "chat_sendMessage", + "repeater_guest", + "room_guest", + "repeater_guestTools" ], "ja": [ - "chat_sendMessage" + "chat_sendMessage", + "repeater_guest", + "room_guest", + "repeater_guestTools" ], "ko": [ - "chat_sendMessage" + "chat_sendMessage", + "repeater_guest", + "room_guest", + "repeater_guestTools" ], "nl": [ - "chat_sendMessage" + "chat_sendMessage", + "repeater_guest", + "room_guest", + "repeater_guestTools" ], "pl": [ - "chat_sendMessage" + "chat_sendMessage", + "repeater_guest", + "room_guest", + "repeater_guestTools" ], "pt": [ - "chat_sendMessage" + "chat_sendMessage", + "repeater_guest", + "room_guest", + "repeater_guestTools" ], "ru": [ - "chat_sendMessage" + "chat_sendMessage", + "repeater_guest", + "room_guest", + "repeater_guestTools" ], "sk": [ - "chat_sendMessage" + "chat_sendMessage", + "repeater_guest", + "room_guest", + "repeater_guestTools" ], "sl": [ - "chat_sendMessage" + "chat_sendMessage", + "repeater_guest", + "room_guest", + "repeater_guestTools" ], "sv": [ - "chat_sendMessage" + "chat_sendMessage", + "repeater_guest", + "room_guest", + "repeater_guestTools" ], "uk": [ - "chat_sendMessage" + "chat_sendMessage", + "repeater_guest", + "room_guest", + "repeater_guestTools" ], "zh": [ - "chat_sendMessage" + "chat_sendMessage", + "repeater_guest", + "room_guest", + "repeater_guestTools" ] } From add4731d05341a484a9b7b1df9e4d1488ab4c111 Mon Sep 17 00:00:00 2001 From: "Enot (ded) Skelly" Date: Fri, 10 Apr 2026 15:11:44 -0700 Subject: [PATCH 3/5] fix: settings dialog lists switched to using RadioListTile instead of ListTile to be more accessible --- lib/screens/app_settings_screen.dart | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/screens/app_settings_screen.dart b/lib/screens/app_settings_screen.dart index cd7fb67..9195602 100644 --- a/lib/screens/app_settings_screen.dart +++ b/lib/screens/app_settings_screen.dart @@ -1065,25 +1065,25 @@ class AppSettingsScreen extends StatelessWidget { children: [ Text(context.l10n.appSettings_showNodesDiscoveredWithin), const SizedBox(height: 16), - ListTile( + RadioListTile( title: Text(context.l10n.appSettings_allTime), - leading: Radio(value: 0), + value: 0, ), - ListTile( + RadioListTile( title: Text(context.l10n.appSettings_lastHour), - leading: Radio(value: 1), + value: 1, ), - ListTile( + RadioListTile( title: Text(context.l10n.appSettings_last6Hours), - leading: Radio(value: 6), + value: 6, ), - ListTile( + RadioListTile( title: Text(context.l10n.appSettings_last24Hours), - leading: Radio(value: 24), + value: 24, ), - ListTile( + RadioListTile( title: Text(context.l10n.appSettings_lastWeek), - leading: Radio(value: 168), + value: 168, ), ], ), @@ -1117,13 +1117,13 @@ class AppSettingsScreen extends StatelessWidget { child: Column( mainAxisSize: MainAxisSize.min, children: [ - ListTile( + RadioListTile( title: Text(context.l10n.appSettings_unitsMetric), - leading: const Radio(value: UnitSystem.metric), + value: UnitSystem.metric, ), - ListTile( + RadioListTile( title: Text(context.l10n.appSettings_unitsImperial), - leading: const Radio(value: UnitSystem.imperial), + value: UnitSystem.imperial, ), ], ), From aa2d0f19274985f5c970f58e3ab6e537a5c69b82 Mon Sep 17 00:00:00 2001 From: "Enot (ded) Skelly" Date: Tue, 14 Apr 2026 11:59:18 -0700 Subject: [PATCH 4/5] clear toast on tap this adds a generator showDismissibleSnackBar which by default allows tapping to clear snack bar toasts. all SnackBar properties are still available and the all callers should now use showDismissibleSnackBar() instead of calling ScaffoldMessenger.of(context).showSnackBar(SnackBar()) --- lib/helpers/link_handler.dart | 19 +- lib/helpers/snack_bar_builder.dart | 56 ++++++ lib/screens/app_debug_log_screen.dart | 6 +- lib/screens/app_settings_screen.dart | 110 ++++++----- lib/screens/ble_debug_log_screen.dart | 8 +- lib/screens/channel_chat_screen.dart | 30 +-- lib/screens/channels_screen.dart | 187 ++++++++----------- lib/screens/chat_screen.dart | 75 ++++---- lib/screens/community_qr_scanner_screen.dart | 30 ++- lib/screens/contacts_screen.dart | 105 +++++------ lib/screens/discovery_screen.dart | 6 +- lib/screens/map_cache_screen.dart | 20 +- lib/screens/map_screen.dart | 16 +- lib/screens/neighbors_screen.dart | 28 ++- lib/screens/repeater_cli_screen.dart | 6 +- lib/screens/repeater_settings_screen.dart | 58 +++--- lib/screens/repeater_status_screen.dart | 21 +-- lib/screens/scanner_screen.dart | 10 +- lib/screens/settings_screen.dart | 90 +++++---- lib/screens/tcp_screen.dart | 7 +- lib/screens/telemetry_screen.dart | 28 ++- lib/screens/usb_screen.dart | 10 +- lib/widgets/path_management_dialog.dart | 59 +++--- lib/widgets/path_selection_dialog.dart | 25 ++- lib/widgets/room_login_dialog.dart | 10 +- 25 files changed, 526 insertions(+), 494 deletions(-) create mode 100644 lib/helpers/snack_bar_builder.dart diff --git a/lib/helpers/link_handler.dart b/lib/helpers/link_handler.dart index b931ca1..c2eae29 100644 --- a/lib/helpers/link_handler.dart +++ b/lib/helpers/link_handler.dart @@ -3,6 +3,7 @@ import 'package:flutter_linkify/flutter_linkify.dart'; import 'package:url_launcher/url_launcher.dart'; import '../l10n/l10n.dart'; import '../utils/platform_info.dart'; +import '../helpers/snack_bar_builder.dart'; class LinkHandler { static TextStyle defaultLinkStyle(BuildContext context, TextStyle base) { @@ -93,21 +94,19 @@ class LinkHandler { final uri = Uri.parse(url); if (!await launchUrl(uri, mode: LaunchMode.externalApplication)) { if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.chat_couldNotOpenLink(url)), - backgroundColor: Colors.red, - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.chat_couldNotOpenLink(url)), + backgroundColor: Colors.red, ); } } } catch (e) { if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.chat_invalidLink), - backgroundColor: Colors.red, - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.chat_invalidLink), + backgroundColor: Colors.red, ); } } diff --git a/lib/helpers/snack_bar_builder.dart b/lib/helpers/snack_bar_builder.dart new file mode 100644 index 0000000..d7409b6 --- /dev/null +++ b/lib/helpers/snack_bar_builder.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; + +// showDismissibleSnackBar shows a [SnackBar] with tap to dismiss +// all other properties are default and optional +void showDismissibleSnackBar( + BuildContext context, { + Key? key, + required Widget content, + Color? backgroundColor, + double? elevation, + EdgeInsetsGeometry? margin, + EdgeInsetsGeometry? padding, + double? width, + ShapeBorder? shape, + HitTestBehavior? hitTestBehavior, + SnackBarBehavior? behavior, + SnackBarAction? action, + double? actionOverflowThreshold, + bool? showCloseIcon, + Color? closeIconColor, + Duration? duration, + bool? persist, + Animation? animation, + void Function()? onVisible, + DismissDirection? dismissDirection, + Clip? clipBehavior, +}) { + final messenger = ScaffoldMessenger.of(context); + messenger.showSnackBar( + SnackBar( + key: key, + content: GestureDetector( + onTap: () => messenger.hideCurrentSnackBar(), + child: content, + ), + backgroundColor: backgroundColor, + elevation: elevation, + margin: margin, + padding: padding, + width: width, + shape: shape, + hitTestBehavior: hitTestBehavior, + behavior: behavior, + action: action, + actionOverflowThreshold: actionOverflowThreshold, + showCloseIcon: showCloseIcon, + closeIconColor: closeIconColor, + duration: duration ?? const Duration(seconds: 4), + persist: persist, + animation: animation, + onVisible: onVisible, + dismissDirection: dismissDirection ?? DismissDirection.down, + clipBehavior: clipBehavior ?? Clip.hardEdge, + ), + ); +} diff --git a/lib/screens/app_debug_log_screen.dart b/lib/screens/app_debug_log_screen.dart index 4877038..ca6a6bf 100644 --- a/lib/screens/app_debug_log_screen.dart +++ b/lib/screens/app_debug_log_screen.dart @@ -5,6 +5,7 @@ import 'package:provider/provider.dart'; import '../l10n/l10n.dart'; import '../services/app_debug_log_service.dart'; import '../widgets/adaptive_app_bar_title.dart'; +import '../helpers/snack_bar_builder.dart'; class AppDebugLogScreen extends StatelessWidget { const AppDebugLogScreen({super.key}); @@ -34,8 +35,9 @@ class AppDebugLogScreen extends StatelessWidget { .join('\n'); await Clipboard.setData(ClipboardData(text: text)); if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(context.l10n.debugLog_copied)), + showDismissibleSnackBar( + context, + content: Text(context.l10n.debugLog_copied), ); } : null, diff --git a/lib/screens/app_settings_screen.dart b/lib/screens/app_settings_screen.dart index cd7fb67..f4cab7f 100644 --- a/lib/screens/app_settings_screen.dart +++ b/lib/screens/app_settings_screen.dart @@ -10,6 +10,7 @@ import '../services/app_settings_service.dart'; import '../services/notification_service.dart'; import '../services/translation_service.dart'; import '../widgets/adaptive_app_bar_title.dart'; +import '../helpers/snack_bar_builder.dart'; import 'map_cache_screen.dart'; class AppSettingsScreen extends StatelessWidget { @@ -151,13 +152,12 @@ class AppSettingsScreen extends StatelessWidget { .requestPermissions(); if (!granted) { if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context.l10n.appSettings_notificationPermissionDenied, - ), - duration: const Duration(seconds: 2), + showDismissibleSnackBar( + context, + content: Text( + context.l10n.appSettings_notificationPermissionDenied, ), + duration: const Duration(seconds: 2), ); } return; @@ -166,15 +166,14 @@ class AppSettingsScreen extends StatelessWidget { await settingsService.setNotificationsEnabled(value); if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - value - ? context.l10n.appSettings_notificationsEnabled - : context.l10n.appSettings_notificationsDisabled, - ), - duration: const Duration(seconds: 2), + showDismissibleSnackBar( + context, + content: Text( + value + ? context.l10n.appSettings_notificationsEnabled + : context.l10n.appSettings_notificationsDisabled, ), + duration: const Duration(seconds: 2), ); } }, @@ -301,15 +300,14 @@ class AppSettingsScreen extends StatelessWidget { value: settingsService.settings.clearPathOnMaxRetry, onChanged: (value) { settingsService.setClearPathOnMaxRetry(value); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - value - ? context.l10n.appSettings_pathsWillBeCleared - : context.l10n.appSettings_pathsWillNotBeCleared, - ), - duration: const Duration(seconds: 2), + showDismissibleSnackBar( + context, + content: Text( + value + ? context.l10n.appSettings_pathsWillBeCleared + : context.l10n.appSettings_pathsWillNotBeCleared, ), + duration: const Duration(seconds: 2), ); }, ), @@ -329,15 +327,14 @@ class AppSettingsScreen extends StatelessWidget { value: settingsService.settings.autoRouteRotationEnabled, onChanged: (value) { settingsService.setAutoRouteRotationEnabled(value); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - value - ? context.l10n.appSettings_autoRouteRotationEnabled - : context.l10n.appSettings_autoRouteRotationDisabled, - ), - duration: const Duration(seconds: 2), + showDismissibleSnackBar( + context, + content: Text( + value + ? context.l10n.appSettings_autoRouteRotationEnabled + : context.l10n.appSettings_autoRouteRotationDisabled, ), + duration: const Duration(seconds: 2), ); }, ), @@ -1164,8 +1161,9 @@ class AppSettingsScreen extends StatelessWidget { String? id, }) async { if (sourceUrl.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(context.l10n.translation_enterUrlFirst)), + showDismissibleSnackBar( + context, + content: Text(context.l10n.translation_enterUrlFirst), ); return; } @@ -1176,22 +1174,23 @@ class AppSettingsScreen extends StatelessWidget { id: id, ); if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(context.l10n.translation_modelDownloaded)), + showDismissibleSnackBar( + context, + content: Text(context.l10n.translation_modelDownloaded), ); await settingsService.setTranslationEnabled(true); } on TranslationDownloadCancelled { if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(context.l10n.translation_downloadStopped)), + showDismissibleSnackBar( + context, + content: Text(context.l10n.translation_downloadStopped), ); } catch (error) { if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context.l10n.translation_downloadFailed(error.toString()), - ), + showDismissibleSnackBar( + context, + content: Text( + context.l10n.translation_downloadFailed(error.toString()), ), ); } @@ -1236,16 +1235,16 @@ class AppSettingsScreen extends StatelessWidget { try { await translationService.removeModel(model); if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - // TODO: l10n - content: Text('Deleted ${translationModelFriendlyName(model)}.'), - ), + showDismissibleSnackBar( + context, + // TODO: l10n + content: Text('Deleted ${translationModelFriendlyName(model)}.'), ); } catch (error) { if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Delete failed: $error')), + showDismissibleSnackBar( + context, + content: Text('Delete failed: $error'), ); // TODO: l10n } } @@ -1279,15 +1278,14 @@ class AppSettingsScreen extends StatelessWidget { onChanged: (value) async { await settingsService.setAppDebugLogEnabled(value); if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - value - ? context.l10n.appSettings_appDebugLoggingEnabled - : context.l10n.appSettings_appDebugLoggingDisabled, - ), - duration: const Duration(seconds: 2), + showDismissibleSnackBar( + context, + content: Text( + value + ? context.l10n.appSettings_appDebugLoggingEnabled + : context.l10n.appSettings_appDebugLoggingDisabled, ), + duration: const Duration(seconds: 2), ); }, ), diff --git a/lib/screens/ble_debug_log_screen.dart b/lib/screens/ble_debug_log_screen.dart index 1009bc4..6d18697 100644 --- a/lib/screens/ble_debug_log_screen.dart +++ b/lib/screens/ble_debug_log_screen.dart @@ -5,6 +5,7 @@ import '../l10n/l10n.dart'; import '../services/ble_debug_log_service.dart'; import '../connector/meshcore_protocol.dart'; import '../widgets/adaptive_app_bar_title.dart'; +import '../helpers/snack_bar_builder.dart'; enum _BleLogView { frames, rawLogRx } @@ -52,10 +53,9 @@ class _BleDebugLogScreenState extends State { .join('\n'); await Clipboard.setData(ClipboardData(text: text)); if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.debugLog_bleCopied), - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.debugLog_bleCopied), ); } : null, diff --git a/lib/screens/channel_chat_screen.dart b/lib/screens/channel_chat_screen.dart index 64da058..e5b5f67 100644 --- a/lib/screens/channel_chat_screen.dart +++ b/lib/screens/channel_chat_screen.dart @@ -14,6 +14,7 @@ import '../connector/meshcore_protocol.dart'; import '../helpers/gif_helper.dart'; import '../helpers/reaction_helper.dart'; import '../helpers/utf8_length_limiter.dart'; +import '../helpers/snack_bar_builder.dart'; import '../l10n/l10n.dart'; import '../models/channel.dart'; import '../models/channel_message.dart'; @@ -144,11 +145,10 @@ class _ChannelChatScreenState extends State { Future _scrollToMessage(String messageId) async { final key = _messageKeys[messageId]; if (key == null) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.chat_originalMessageNotFound), - duration: const Duration(seconds: 2), - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.chat_originalMessageNotFound), + duration: const Duration(seconds: 2), ); return; } @@ -1151,9 +1151,10 @@ class _ChannelChatScreenState extends State { final now = DateTime.now(); if (_lastChannelSendAt != null && now.difference(_lastChannelSendAt!) < const Duration(seconds: 1)) { - ScaffoldMessenger.of( + showDismissibleSnackBar( context, - ).showSnackBar(SnackBar(content: Text(context.l10n.chat_sendCooldown))); + content: Text(context.l10n.chat_sendCooldown), + ); return; } _lastChannelSendAt = now; @@ -1195,8 +1196,9 @@ class _ChannelChatScreenState extends State { final maxBytes = maxChannelMessageBytes(connector.selfName); if (utf8.encode(messageText).length > maxBytes) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(context.l10n.chat_messageTooLong(maxBytes))), + showDismissibleSnackBar( + context, + content: Text(context.l10n.chat_messageTooLong(maxBytes)), ); return; } @@ -1323,17 +1325,19 @@ class _ChannelChatScreenState extends State { void _copyMessageText(String text) { Clipboard.setData(ClipboardData(text: text)); - ScaffoldMessenger.of( + showDismissibleSnackBar( context, - ).showSnackBar(SnackBar(content: Text(context.l10n.chat_messageCopied))); + content: Text(context.l10n.chat_messageCopied), + ); } Future _deleteMessage(ChannelMessage message) async { await context.read().deleteChannelMessage(message); if (!mounted) return; - ScaffoldMessenger.of( + showDismissibleSnackBar( context, - ).showSnackBar(SnackBar(content: Text(context.l10n.chat_messageDeleted))); + content: Text(context.l10n.chat_messageDeleted), + ); } String _formatPathPrefixes(Uint8List pathBytes) { diff --git a/lib/screens/channels_screen.dart b/lib/screens/channels_screen.dart index 51d2453..44c7a69 100644 --- a/lib/screens/channels_screen.dart +++ b/lib/screens/channels_screen.dart @@ -24,6 +24,7 @@ import '../widgets/empty_state.dart'; import '../widgets/qr_code_display.dart'; import '../widgets/quick_switch_bar.dart'; import '../widgets/unread_badge.dart'; +import '../helpers/snack_bar_builder.dart'; import 'channel_chat_screen.dart'; import 'community_qr_scanner_screen.dart'; import 'contacts_screen.dart'; @@ -809,15 +810,12 @@ class _ChannelsScreenState extends State onPressed: () async { final name = nameController.text.trim(); if (name.isEmpty) { - ScaffoldMessenger.of( - dialogContext, - ).showSnackBar( - SnackBar( - content: Text( - dialogContext - .l10n - .channels_enterChannelName, - ), + showDismissibleSnackBar( + context, + content: Text( + dialogContext + .l10n + .channels_enterChannelName, ), ); return; @@ -837,13 +835,10 @@ class _ChannelsScreenState extends State nextIndex, ); if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context.l10n.channels_channelAdded( - name, - ), - ), + showDismissibleSnackBar( + context, + content: Text( + context.l10n.channels_channelAdded(name), ), ); } @@ -897,15 +892,12 @@ class _ChannelsScreenState extends State final name = nameController.text.trim(); final pskHex = pskController.text.trim(); if (name.isEmpty) { - ScaffoldMessenger.of( - dialogContext, - ).showSnackBar( - SnackBar( - content: Text( - dialogContext - .l10n - .channels_enterChannelName, - ), + showDismissibleSnackBar( + context, + content: Text( + dialogContext + .l10n + .channels_enterChannelName, ), ); return; @@ -914,15 +906,12 @@ class _ChannelsScreenState extends State try { psk = Channel.parsePskHex(pskHex); } on FormatException { - ScaffoldMessenger.of( - dialogContext, - ).showSnackBar( - SnackBar( - content: Text( - dialogContext - .l10n - .channels_pskMustBe32Hex, - ), + showDismissibleSnackBar( + context, + content: Text( + dialogContext + .l10n + .channels_pskMustBe32Hex, ), ); return; @@ -930,13 +919,10 @@ class _ChannelsScreenState extends State Navigator.pop(dialogContext); connector.setChannel(nextIndex, name, psk); if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context.l10n.channels_channelAdded( - name, - ), - ), + showDismissibleSnackBar( + context, + content: Text( + context.l10n.channels_channelAdded(name), ), ); } @@ -967,11 +953,10 @@ class _ChannelsScreenState extends State Navigator.pop(dialogContext); connector.setChannel(nextIndex, 'Public', psk); if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context.l10n.channels_publicChannelAdded, - ), + showDismissibleSnackBar( + context, + content: Text( + context.l10n.channels_publicChannelAdded, ), ); } @@ -1097,15 +1082,12 @@ class _ChannelsScreenState extends State onPressed: () async { var hashtag = hashtagController.text.trim(); if (hashtag.isEmpty) { - ScaffoldMessenger.of( - dialogContext, - ).showSnackBar( - SnackBar( - content: Text( - dialogContext - .l10n - .channels_enterChannelName, - ), + showDismissibleSnackBar( + context, + content: Text( + dialogContext + .l10n + .channels_enterChannelName, ), ); return; @@ -1125,15 +1107,12 @@ class _ChannelsScreenState extends State } else { // Community hashtag - HMAC derivation from community secret if (selectedCommunity == null) { - ScaffoldMessenger.of( + showDismissibleSnackBar( dialogContext, - ).showSnackBar( - SnackBar( - content: Text( - dialogContext - .l10n - .community_selectCommunity, - ), + content: Text( + dialogContext + .l10n + .community_selectCommunity, ), ); return; @@ -1159,12 +1138,11 @@ class _ChannelsScreenState extends State psk, ); if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context.l10n.channels_channelAdded( - channelName, - ), + showDismissibleSnackBar( + context, + content: Text( + context.l10n.channels_channelAdded( + channelName, ), ), ); @@ -1259,13 +1237,10 @@ class _ChannelsScreenState extends State onPressed: () async { final name = nameController.text.trim(); if (name.isEmpty) { - ScaffoldMessenger.of( - dialogContext, - ).showSnackBar( - SnackBar( - content: Text( - dialogContext.l10n.community_enterName, - ), + showDismissibleSnackBar( + context, + content: Text( + dialogContext.l10n.community_enterName, ), ); return; @@ -1301,11 +1276,10 @@ class _ChannelsScreenState extends State _loadCommunities(); if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context.l10n.community_created(name), - ), + showDismissibleSnackBar( + context, + content: Text( + context.l10n.community_created(name), ), ); @@ -1494,10 +1468,9 @@ class _ChannelsScreenState extends State try { psk = Channel.parsePskHex(pskHex); } on FormatException { - ScaffoldMessenger.of(dialogContext).showSnackBar( - SnackBar( - content: Text(dialogContext.l10n.channels_pskMustBe32Hex), - ), + showDismissibleSnackBar( + dialogContext, + content: Text(dialogContext.l10n.channels_pskMustBe32Hex), ); return; } @@ -1510,16 +1483,16 @@ class _ChannelsScreenState extends State smazEnabled, ); if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.channels_channelUpdated(name)), - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.channels_channelUpdated(name)), ); } catch (e, st) { debugPrint(st.toString()); if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Failed to update channel: $e')), + showDismissibleSnackBar( + context, + content: Text('Failed to update channel: $e'), ); } }, @@ -1559,21 +1532,19 @@ class _ChannelsScreenState extends State if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context.l10n.channels_channelDeleted(channel.name), - ), + showDismissibleSnackBar( + context, + content: Text( + context.l10n.channels_channelDeleted(channel.name), ), ); } catch (e, st) { if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context.l10n.channels_channelDeleteFailed(channel.name), - ), + showDismissibleSnackBar( + context, + content: Text( + context.l10n.channels_channelDeleteFailed(channel.name), ), ); @@ -1594,8 +1565,9 @@ class _ChannelsScreenState extends State void _addPublicChannel(BuildContext context, MeshCoreConnector connector) { final psk = Channel.parsePskHex(Channel.publicChannelPsk); connector.setChannel(0, 'Public', psk); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(context.l10n.channels_publicChannelAdded)), + showDismissibleSnackBar( + context, + content: Text(context.l10n.channels_publicChannelAdded), ); } @@ -1810,12 +1782,9 @@ class _ChannelsScreenState extends State _loadCommunities(); if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context.l10n.community_deleted(community.name), - ), - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.community_deleted(community.name)), ); } }, diff --git a/lib/screens/chat_screen.dart b/lib/screens/chat_screen.dart index a4ebc76..2aee61c 100644 --- a/lib/screens/chat_screen.dart +++ b/lib/screens/chat_screen.dart @@ -43,6 +43,7 @@ import '../widgets/radio_stats_entry.dart'; import '../widgets/translated_message_content.dart'; import '../utils/app_logger.dart'; import '../l10n/l10n.dart'; +import '../helpers/snack_bar_builder.dart'; import 'telemetry_screen.dart'; class ChatScreen extends StatefulWidget { @@ -633,9 +634,10 @@ class _ChatScreenState extends State { final now = DateTime.now(); if (_lastTextSendAt != null && now.difference(_lastTextSendAt!) < const Duration(seconds: 1)) { - ScaffoldMessenger.of( + showDismissibleSnackBar( context, - ).showSnackBar(SnackBar(content: Text(context.l10n.chat_sendCooldown))); + content: Text(context.l10n.chat_sendCooldown), + ); return; } _lastTextSendAt = now; @@ -671,8 +673,9 @@ class _ChatScreenState extends State { } final maxBytes = maxContactMessageBytes(); if (utf8.encode(outgoingText).length > maxBytes) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(context.l10n.chat_messageTooLong(maxBytes))), + showDismissibleSnackBar( + context, + content: Text(context.l10n.chat_messageTooLong(maxBytes)), ); return; } @@ -860,15 +863,12 @@ class _ChatScreenState extends State { _showFullPathDialog(context, path.pathBytes), onTap: () async { if (path.pathBytes.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context - .l10n - .chat_pathDetailsNotAvailable, - ), - duration: const Duration(seconds: 2), + showDismissibleSnackBar( + context, + content: Text( + context.l10n.chat_pathDetailsNotAvailable, ), + duration: const Duration(seconds: 2), ); return; } @@ -952,11 +952,10 @@ class _ChatScreenState extends State { _resolveContact(connector), ); if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.chat_pathCleared), - duration: const Duration(seconds: 2), - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.chat_pathCleared), + duration: const Duration(seconds: 2), ); Navigator.pop(context); }, @@ -982,11 +981,10 @@ class _ChatScreenState extends State { pathLen: -1, ); if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.chat_floodModeEnabled), - duration: const Duration(seconds: 2), - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.chat_floodModeEnabled), + duration: const Duration(seconds: 2), ); Navigator.pop(context); }, @@ -1020,11 +1018,10 @@ class _ChatScreenState extends State { void _showFullPathDialog(BuildContext context, List pathBytes) { if (pathBytes.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.chat_pathDetailsNotAvailable), - duration: const Duration(seconds: 2), - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.chat_pathDetailsNotAvailable), + duration: const Duration(seconds: 2), ); return; } @@ -1137,11 +1134,10 @@ class _ChatScreenState extends State { : (verified ? context.l10n.chat_pathDeviceConfirmed : context.l10n.chat_pathDeviceNotConfirmed); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.chat_pathSetHops(hopCount, status)), - duration: const Duration(seconds: 3), - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.chat_pathSetHops(hopCount, status)), + duration: const Duration(seconds: 3), ); } @@ -1490,26 +1486,29 @@ class _ChatScreenState extends State { void _copyMessageText(String text) { Clipboard.setData(ClipboardData(text: text)); - ScaffoldMessenger.of( + showDismissibleSnackBar( context, - ).showSnackBar(SnackBar(content: Text(context.l10n.chat_messageCopied))); + content: Text(context.l10n.chat_messageCopied), + ); } Future _deleteMessage(Message message) async { await context.read().deleteMessage(message); if (!mounted) return; - ScaffoldMessenger.of( + showDismissibleSnackBar( context, - ).showSnackBar(SnackBar(content: Text(context.l10n.chat_messageDeleted))); + content: Text(context.l10n.chat_messageDeleted), + ); } void _retryMessage(Message message) { final connector = Provider.of(context, listen: false); // Retry using the contact's current path override setting connector.sendMessage(_resolveContact(connector), message.text); - ScaffoldMessenger.of( + showDismissibleSnackBar( context, - ).showSnackBar(SnackBar(content: Text(context.l10n.chat_retryingMessage))); + content: Text(context.l10n.chat_retryingMessage), + ); } void _showEmojiPicker(Message message, Contact senderContact) { diff --git a/lib/screens/community_qr_scanner_screen.dart b/lib/screens/community_qr_scanner_screen.dart index 6852dfa..6b71715 100644 --- a/lib/screens/community_qr_scanner_screen.dart +++ b/lib/screens/community_qr_scanner_screen.dart @@ -8,6 +8,7 @@ import '../models/community.dart'; import '../storage/community_store.dart'; import '../widgets/adaptive_app_bar_title.dart'; import '../widgets/qr_scanner_widget.dart'; +import '../helpers/snack_bar_builder.dart'; /// Screen for scanning community QR codes to join communities. /// @@ -76,11 +77,10 @@ class _CommunityQrScannerScreenState extends State { } } catch (e) { if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.community_invalidQrCode), - backgroundColor: Colors.red, - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.community_invalidQrCode), + backgroundColor: Colors.red, ); } } finally { @@ -93,12 +93,11 @@ class _CommunityQrScannerScreenState extends State { } void _showInvalidQrError(BuildContext context) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.community_invalidQrCode), - backgroundColor: Colors.orange, - duration: const Duration(seconds: 2), - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.community_invalidQrCode), + backgroundColor: Colors.orange, + duration: const Duration(seconds: 2), ); } @@ -229,11 +228,10 @@ class _CommunityQrScannerScreenState extends State { } if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.community_joined(community.name)), - backgroundColor: Colors.green, - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.community_joined(community.name)), + backgroundColor: Colors.green, ); // Return to previous screen diff --git a/lib/screens/contacts_screen.dart b/lib/screens/contacts_screen.dart index 46e2be6..5a6f359 100644 --- a/lib/screens/contacts_screen.dart +++ b/lib/screens/contacts_screen.dart @@ -27,6 +27,7 @@ import '../widgets/quick_switch_bar.dart'; import '../widgets/repeater_login_dialog.dart'; import '../widgets/room_login_dialog.dart'; import '../widgets/unread_badge.dart'; +import '../helpers/snack_bar_builder.dart'; import 'channels_screen.dart'; import 'chat_screen.dart'; import 'discovery_screen.dart'; @@ -150,9 +151,10 @@ class _ContactsScreenState extends State } void _showGroupsUnavailableMessage(BuildContext context) { - ScaffoldMessenger.of( + showDismissibleSnackBar( context, - ).showSnackBar(SnackBar(content: Text(context.l10n.common_loading))); + content: Text(context.l10n.common_loading), + ); } void _setupFrameListener() { @@ -169,10 +171,9 @@ class _ContactsScreenState extends State // Validate packet has expected minimum size (98+ bytes per protocol) if (advertPacket.length < 98) { if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.contacts_invalidAdvertFormat), - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.contacts_invalidAdvertFormat), ); } _pendingOperations.remove(ContactOperationType.export); @@ -187,24 +188,23 @@ class _ContactsScreenState extends State if (!mounted) return; if (_pendingOperations.contains(ContactOperationType.import)) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(context.l10n.contacts_contactImported)), + showDismissibleSnackBar( + context, + content: Text(context.l10n.contacts_contactImported), ); } if (_pendingOperations.contains(ContactOperationType.zeroHopShare)) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.contacts_zeroHopContactAdvertSent), - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.contacts_zeroHopContactAdvertSent), ); } if (_pendingOperations.contains(ContactOperationType.export)) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.contacts_contactAdvertCopied), - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.contacts_contactAdvertCopied), ); } @@ -216,25 +216,22 @@ class _ContactsScreenState extends State if (!mounted) return; if (_pendingOperations.contains(ContactOperationType.import)) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.contacts_contactImportFailed), - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.contacts_contactImportFailed), ); } if (_pendingOperations.contains(ContactOperationType.zeroHopShare)) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.contacts_zeroHopContactAdvertFailed), - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.contacts_zeroHopContactAdvertFailed), ); } if (_pendingOperations.contains(ContactOperationType.export)) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.contacts_contactAdvertCopyFailed), - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.contacts_contactAdvertCopyFailed), ); } @@ -271,8 +268,9 @@ class _ContactsScreenState extends State final clipboardData = await Clipboard.getData('text/plain'); if (clipboardData == null || clipboardData.text == null) { if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(context.l10n.contacts_clipboardEmpty)), + showDismissibleSnackBar( + context, + content: Text(context.l10n.contacts_clipboardEmpty), ); } return; @@ -280,8 +278,9 @@ class _ContactsScreenState extends State final text = clipboardData.text!.trim(); if (!text.startsWith('meshcore://')) { if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(context.l10n.contacts_invalidAdvertFormat)), + showDismissibleSnackBar( + context, + content: Text(context.l10n.contacts_invalidAdvertFormat), ); } return; @@ -294,8 +293,9 @@ class _ContactsScreenState extends State connector.importContact(importContactFrame); } catch (e) { if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(context.l10n.contacts_invalidAdvertFormat)), + showDismissibleSnackBar( + context, + content: Text(context.l10n.contacts_invalidAdvertFormat), ); } } @@ -330,10 +330,9 @@ class _ContactsScreenState extends State ), onTap: () => { connector.sendSelfAdvert(flood: false), - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.settings_advertisementSent), - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.settings_advertisementSent), ), }, ), @@ -347,10 +346,9 @@ class _ContactsScreenState extends State ), onTap: () => { connector.sendSelfAdvert(flood: true), - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.settings_advertisementSent), - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.settings_advertisementSent), ), }, ), @@ -1146,19 +1144,17 @@ class _ContactsScreenState extends State onPressed: () async { final name = nameController.text.trim(); if (name.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.contacts_groupNameRequired), - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.contacts_groupNameRequired), ); return; } if (name.toLowerCase() == contactsAllGroupsValue.toLowerCase()) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.contacts_groupNameReserved), - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.contacts_groupNameReserved), ); return; } @@ -1167,11 +1163,10 @@ class _ContactsScreenState extends State return g.name.toLowerCase() == name.toLowerCase(); }); if (exists) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context.l10n.contacts_groupAlreadyExists(name), - ), + showDismissibleSnackBar( + context, + content: Text( + context.l10n.contacts_groupAlreadyExists(name), ), ); return; diff --git a/lib/screens/discovery_screen.dart b/lib/screens/discovery_screen.dart index 3f9d965..f9f0e07 100644 --- a/lib/screens/discovery_screen.dart +++ b/lib/screens/discovery_screen.dart @@ -12,6 +12,7 @@ import '../utils/contact_search.dart'; import '../utils/platform_info.dart'; import '../widgets/app_bar.dart'; import '../widgets/list_filter_widget.dart'; +import '../helpers/snack_bar_builder.dart'; enum DiscoverySortOption { lastSeen, name, type } @@ -234,8 +235,9 @@ class _DiscoveryScreenState extends State { final hexString = pubKeyToHex(contact.rawPacket!); Clipboard.setData(ClipboardData(text: "meshcore://$hexString")); if (!mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(context.l10n.contacts_contactAdvertCopied)), + showDismissibleSnackBar( + context, + content: Text(context.l10n.contacts_contactAdvertCopied), ); break; case 'delete_contact': diff --git a/lib/screens/map_cache_screen.dart b/lib/screens/map_cache_screen.dart index 1391660..1eb59a8 100644 --- a/lib/screens/map_cache_screen.dart +++ b/lib/screens/map_cache_screen.dart @@ -8,6 +8,7 @@ import '../l10n/l10n.dart'; import '../services/app_settings_service.dart'; import '../services/map_tile_cache_service.dart'; import '../widgets/adaptive_app_bar_title.dart'; +import '../helpers/snack_bar_builder.dart'; class MapCacheScreen extends StatefulWidget { const MapCacheScreen({super.key}); @@ -112,15 +113,17 @@ class _MapCacheScreenState extends State { Future _startDownload() async { final bounds = _selectedBounds; if (bounds == null) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(context.l10n.mapCache_selectAreaFirst)), + showDismissibleSnackBar( + context, + content: Text(context.l10n.mapCache_selectAreaFirst), ); return; } if (_estimatedTiles == 0) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(context.l10n.mapCache_noTilesToDownload)), + showDismissibleSnackBar( + context, + content: Text(context.l10n.mapCache_noTilesToDownload), ); return; } @@ -182,9 +185,7 @@ class _MapCacheScreenState extends State { result.failed, ) : context.l10n.mapCache_cachedTiles(result.downloaded); - ScaffoldMessenger.of( - context, - ).showSnackBar(SnackBar(content: Text(message))); + showDismissibleSnackBar(context, content: Text(message)); } Future _clearCache() async { @@ -210,8 +211,9 @@ class _MapCacheScreenState extends State { final cacheService = context.read(); await cacheService.clearCache(); if (!mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(context.l10n.mapCache_offlineCacheCleared)), + showDismissibleSnackBar( + context, + content: Text(context.l10n.mapCache_offlineCacheCleared), ); } diff --git a/lib/screens/map_screen.dart b/lib/screens/map_screen.dart index f2d09f3..daf49d9 100644 --- a/lib/screens/map_screen.dart +++ b/lib/screens/map_screen.dart @@ -29,6 +29,7 @@ import 'chat_screen.dart'; import 'contacts_screen.dart'; import '../widgets/repeater_login_dialog.dart'; import '../widgets/room_login_dialog.dart'; +import '../helpers/snack_bar_builder.dart'; import 'repeater_hub_screen.dart'; import 'settings_screen.dart'; import 'line_of_sight_map_screen.dart'; @@ -1659,7 +1660,10 @@ class _MapScreenState extends State { ); await connector.refreshDeviceInfo(); if (!mounted) return; - messenger.showSnackBar(SnackBar(content: Text(successMsg))); + showDismissibleSnackBar( + messenger.context, + content: Text(successMsg), + ); }, ), ListTile( @@ -1681,8 +1685,9 @@ class _MapScreenState extends State { required String flags, }) async { if (!connector.isConnected) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(context.l10n.map_connectToShareMarkers)), + showDismissibleSnackBar( + context, + content: Text(context.l10n.map_connectToShareMarkers), ); return; } @@ -2271,8 +2276,9 @@ class _MapScreenState extends State { _points.clear(); _polylines.clear(); }); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(l10n.map_pathTraceCancelled)), + showDismissibleSnackBar( + context, + content: Text(l10n.map_pathTraceCancelled), ); }, tooltip: l10n.common_cancel, diff --git a/lib/screens/neighbors_screen.dart b/lib/screens/neighbors_screen.dart index 7286eb0..77559d4 100644 --- a/lib/screens/neighbors_screen.dart +++ b/lib/screens/neighbors_screen.dart @@ -11,6 +11,7 @@ import '../connector/meshcore_protocol.dart'; import '../services/repeater_command_service.dart'; import '../widgets/path_management_dialog.dart'; import '../widgets/snr_indicator.dart'; +import '../helpers/snack_bar_builder.dart'; class NeighborsScreen extends StatefulWidget { final Contact repeater; @@ -163,11 +164,10 @@ class _NeighborsScreenState extends State { _neighborCount = neighborCount; }); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.neighbors_receivedData), - backgroundColor: Colors.green, - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.neighbors_receivedData), + backgroundColor: Colors.green, ); _statusTimeout?.cancel(); if (!mounted) return; @@ -224,11 +224,10 @@ class _NeighborsScreenState extends State { _isLoading = false; _isLoaded = false; }); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.neighbors_requestTimedOut), - backgroundColor: Colors.red, - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.neighbors_requestTimedOut), + backgroundColor: Colors.red, ); _recordStatusResult(false); }); @@ -239,11 +238,10 @@ class _NeighborsScreenState extends State { _isLoaded = false; }); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.neighbors_errorLoading(e.toString())), - backgroundColor: Colors.red, - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.neighbors_errorLoading(e.toString())), + backgroundColor: Colors.red, ); } } diff --git a/lib/screens/repeater_cli_screen.dart b/lib/screens/repeater_cli_screen.dart index 5f76828..5e9a462 100644 --- a/lib/screens/repeater_cli_screen.dart +++ b/lib/screens/repeater_cli_screen.dart @@ -9,6 +9,7 @@ import '../connector/meshcore_protocol.dart'; import '../widgets/debug_frame_viewer.dart'; import '../services/repeater_command_service.dart'; import '../widgets/path_management_dialog.dart'; +import '../helpers/snack_bar_builder.dart'; class RepeaterCliScreen extends StatefulWidget { final Contact repeater; @@ -336,8 +337,9 @@ class _RepeaterCliScreenState extends State { if (_commandController.text.trim().isNotEmpty) { _sendCommand(showDebug: true); } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(l10n.repeater_enterCommandFirst)), + showDismissibleSnackBar( + context, + content: Text(l10n.repeater_enterCommandFirst), ); } }, diff --git a/lib/screens/repeater_settings_screen.dart b/lib/screens/repeater_settings_screen.dart index d0236bb..6d0b4e6 100644 --- a/lib/screens/repeater_settings_screen.dart +++ b/lib/screens/repeater_settings_screen.dart @@ -10,6 +10,7 @@ import '../services/app_debug_log_service.dart'; import '../services/repeater_command_service.dart'; import '../services/storage_service.dart'; import '../widgets/path_management_dialog.dart'; +import '../helpers/snack_bar_builder.dart'; class RepeaterSettingsScreen extends StatefulWidget { final Contact repeater; @@ -468,18 +469,16 @@ class _RepeaterSettingsScreenState extends State { if (mounted) { if (successCount > 0) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(l10n.repeater_refreshed(label)), - backgroundColor: Colors.green, - ), + showDismissibleSnackBar( + context, + content: Text(l10n.repeater_refreshed(label)), + backgroundColor: Colors.green, ); } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(l10n.repeater_errorRefreshing(label)), - backgroundColor: Colors.red, - ), + showDismissibleSnackBar( + context, + content: Text(l10n.repeater_errorRefreshing(label)), + backgroundColor: Colors.red, ); } @@ -666,11 +665,10 @@ class _RepeaterSettingsScreenState extends State { }); if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.repeater_settingsSaved), - backgroundColor: Colors.green, - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.repeater_settingsSaved), + backgroundColor: Colors.green, ); } } catch (e) { @@ -679,13 +677,12 @@ class _RepeaterSettingsScreenState extends State { }); if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context.l10n.repeater_errorSavingSettings(e.toString()), - ), - backgroundColor: Colors.red, + showDismissibleSnackBar( + context, + content: Text( + context.l10n.repeater_errorSavingSettings(e.toString()), ), + backgroundColor: Colors.red, ); } } @@ -1429,9 +1426,10 @@ class _RepeaterSettingsScreenState extends State { if (command == 'erase') { if (mounted) { - ScaffoldMessenger.of( + showDismissibleSnackBar( context, - ).showSnackBar(SnackBar(content: Text(l10n.repeater_eraseSerialOnly))); + content: Text(l10n.repeater_eraseSerialOnly), + ); } return; } @@ -1453,17 +1451,17 @@ class _RepeaterSettingsScreenState extends State { await connector.sendFrame(frame); if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(l10n.repeater_commandSent(command))), + showDismissibleSnackBar( + context, + content: Text(l10n.repeater_commandSent(command)), ); } } catch (e) { if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(l10n.repeater_errorSendingCommand(e.toString())), - backgroundColor: Colors.red, - ), + showDismissibleSnackBar( + context, + content: Text(l10n.repeater_errorSendingCommand(e.toString())), + backgroundColor: Colors.red, ); } } diff --git a/lib/screens/repeater_status_screen.dart b/lib/screens/repeater_status_screen.dart index f938419..720c32a 100644 --- a/lib/screens/repeater_status_screen.dart +++ b/lib/screens/repeater_status_screen.dart @@ -12,6 +12,7 @@ import '../services/app_settings_service.dart'; import '../services/repeater_command_service.dart'; import '../utils/battery_utils.dart'; import '../widgets/path_management_dialog.dart'; +import '../helpers/snack_bar_builder.dart'; class RepeaterStatusScreen extends StatefulWidget { final Contact repeater; @@ -309,11 +310,10 @@ class _RepeaterStatusScreenState extends State { setState(() { _isLoading = false; }); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.repeater_statusRequestTimeout), - backgroundColor: Colors.red, - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.repeater_statusRequestTimeout), + backgroundColor: Colors.red, ); _recordStatusResult(false); }); @@ -323,13 +323,10 @@ class _RepeaterStatusScreenState extends State { _isLoading = false; }); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context.l10n.repeater_errorLoadingStatus(e.toString()), - ), - backgroundColor: Colors.red, - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.repeater_errorLoadingStatus(e.toString())), + backgroundColor: Colors.red, ); } _recordStatusResult(false); diff --git a/lib/screens/scanner_screen.dart b/lib/screens/scanner_screen.dart index 17f26ea..a503ec0 100644 --- a/lib/screens/scanner_screen.dart +++ b/lib/screens/scanner_screen.dart @@ -10,6 +10,7 @@ import '../services/linux_ble_error_classifier.dart'; import '../utils/app_logger.dart'; import '../widgets/adaptive_app_bar_title.dart'; import '../widgets/device_tile.dart'; +import '../helpers/snack_bar_builder.dart'; import 'contacts_screen.dart'; import 'tcp_screen.dart'; import 'usb_screen.dart'; @@ -317,11 +318,10 @@ class _ScannerScreenState extends State { return; } if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.scanner_connectionFailed(e.toString())), - backgroundColor: Colors.red, - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.scanner_connectionFailed(e.toString())), + backgroundColor: Colors.red, ); } } diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index e9b73f8..47b9b9c 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -11,6 +11,7 @@ import '../l10n/l10n.dart'; import '../models/radio_settings.dart'; import '../services/app_debug_log_service.dart'; import '../widgets/app_bar.dart'; +import '../helpers/snack_bar_builder.dart'; import 'app_settings_screen.dart'; import 'app_debug_log_screen.dart'; import 'ble_debug_log_screen.dart'; @@ -513,8 +514,9 @@ class _SettingsScreenState extends State { await connector.setNodeName(controller.text); await connector.refreshDeviceInfo(); if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(l10n.settings_nodeNameUpdated)), + showDismissibleSnackBar( + context, + content: Text(l10n.settings_nodeNameUpdated), ); }, child: Text(l10n.common_save), @@ -628,10 +630,9 @@ class _SettingsScreenState extends State { final interval = int.tryParse(intervalText); if (interval == null || interval < 60 || interval >= 86400) { if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(l10n.settings_locationIntervalInvalid), - ), + showDismissibleSnackBar( + context, + content: Text(l10n.settings_locationIntervalInvalid), ); return; } @@ -639,8 +640,9 @@ class _SettingsScreenState extends State { await connector.setCustomVar("gps_interval:$interval"); await connector.refreshDeviceInfo(); if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(l10n.settings_locationUpdated)), + showDismissibleSnackBar( + context, + content: Text(l10n.settings_locationUpdated), ); } @@ -660,15 +662,17 @@ class _SettingsScreenState extends State { : currentLon; if (lat == null || lon == null) { if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(l10n.settings_locationBothRequired)), + showDismissibleSnackBar( + context, + content: Text(l10n.settings_locationBothRequired), ); return; } if (lat < -90 || lat > 90 || lon < -180 || lon > 180) { if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(l10n.settings_locationInvalid)), + showDismissibleSnackBar( + context, + content: Text(l10n.settings_locationInvalid), ); return; } @@ -676,8 +680,9 @@ class _SettingsScreenState extends State { await connector.setNodeLocation(lat: lat, lon: lon); await connector.refreshDeviceInfo(); if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(l10n.settings_locationUpdated)), + showDismissibleSnackBar( + context, + content: Text(l10n.settings_locationUpdated), ); }, child: Text(l10n.common_save), @@ -691,9 +696,10 @@ class _SettingsScreenState extends State { void _syncTime(BuildContext context, MeshCoreConnector connector) { final l10n = context.l10n; connector.syncTime(); - ScaffoldMessenger.of( + showDismissibleSnackBar( context, - ).showSnackBar(SnackBar(content: Text(l10n.settings_timeSynchronized))); + content: Text(l10n.settings_timeSynchronized), + ); } void _confirmReboot(BuildContext context, MeshCoreConnector connector) { @@ -758,23 +764,27 @@ class _SettingsScreenState extends State { if (!mounted) return; switch (result) { case gpxExportSuccess: - ScaffoldMessenger.of( + showDismissibleSnackBar( context, - ).showSnackBar(SnackBar(content: Text(l10n.settings_gpxExportSuccess))); + content: Text(l10n.settings_gpxExportSuccess), + ); case gpxExportNoContacts: - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(l10n.settings_gpxExportNoContacts)), + showDismissibleSnackBar( + context, + content: Text(l10n.settings_gpxExportNoContacts), ); break; case gpxExportNotAvailable: - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(l10n.settings_gpxExportNotAvailable)), + showDismissibleSnackBar( + context, + content: Text(l10n.settings_gpxExportNotAvailable), ); break; case gpxExportFailed: - ScaffoldMessenger.of( + showDismissibleSnackBar( context, - ).showSnackBar(SnackBar(content: Text(l10n.settings_gpxExportError))); + content: Text(l10n.settings_gpxExportError), + ); break; } } @@ -1077,8 +1087,9 @@ void _privacySettings(BuildContext context, MeshCoreConnector connector) { ); await connector.refreshDeviceInfo(); if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(l10n.settings_telemetryModeUpdated)), + showDismissibleSnackBar( + context, + content: Text(l10n.settings_telemetryModeUpdated), ); }, child: Text(l10n.common_save), @@ -1410,18 +1421,18 @@ class _RadioSettingsDialogState extends State<_RadioSettingsDialog> { final txPower = int.tryParse(_txPowerController.text); if (freqMHz == null || freqMHz < 300 || freqMHz > 2500) { - ScaffoldMessenger.of( + showDismissibleSnackBar( context, - ).showSnackBar(SnackBar(content: Text(l10n.settings_frequencyInvalid))); + content: Text(l10n.settings_frequencyInvalid), + ); return; } final maxTxPower = widget.connector.maxTxPower ?? 22; if (txPower == null || txPower < 0 || txPower > maxTxPower) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('${l10n.settings_txPowerInvalid} (0-$maxTxPower dBm)'), - ), + showDismissibleSnackBar( + context, + content: Text('${l10n.settings_txPowerInvalid} (0-$maxTxPower dBm)'), ); return; } @@ -1441,8 +1452,9 @@ class _RadioSettingsDialogState extends State<_RadioSettingsDialog> { if (knownRepeat) { const validRepeatFreqsKHz = {433000, 869000, 918000}; if (_clientRepeat && !validRepeatFreqsKHz.contains(freqHz)) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(l10n.settings_clientRepeatFreqWarning)), + showDismissibleSnackBar( + context, + content: Text(l10n.settings_clientRepeatFreqWarning), ); return; } @@ -1472,14 +1484,16 @@ class _RadioSettingsDialogState extends State<_RadioSettingsDialog> { if (!mounted) return; _logRadioSettingsState('Radio settings saved successfully'); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(l10n.settings_radioSettingsUpdated)), + showDismissibleSnackBar( + context, + content: Text(l10n.settings_radioSettingsUpdated), ); } catch (e) { _appLog.warn('Radio settings save failed: $e', tag: 'RadioSettings'); if (!mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(l10n.settings_error(e.toString()))), + showDismissibleSnackBar( + context, + content: Text(l10n.settings_error(e.toString())), ); } Navigator.pop(context); diff --git a/lib/screens/tcp_screen.dart b/lib/screens/tcp_screen.dart index 11ab80a..3bd1b0b 100644 --- a/lib/screens/tcp_screen.dart +++ b/lib/screens/tcp_screen.dart @@ -8,6 +8,7 @@ import '../l10n/l10n.dart'; import '../services/app_settings_service.dart'; import '../utils/platform_info.dart'; import '../widgets/adaptive_app_bar_title.dart'; +import '../helpers/snack_bar_builder.dart'; import 'contacts_screen.dart'; import 'usb_screen.dart'; @@ -270,8 +271,10 @@ class _TcpScreenState extends State { void _showError(String message) { if (!mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(message), backgroundColor: Colors.red), + showDismissibleSnackBar( + context, + content: Text(message), + backgroundColor: Colors.red, ); } diff --git a/lib/screens/telemetry_screen.dart b/lib/screens/telemetry_screen.dart index 66911dc..47593a3 100644 --- a/lib/screens/telemetry_screen.dart +++ b/lib/screens/telemetry_screen.dart @@ -14,6 +14,7 @@ import '../utils/app_logger.dart'; import '../widgets/path_management_dialog.dart'; import '../helpers/cayenne_lpp.dart'; import '../utils/battery_utils.dart'; +import '../helpers/snack_bar_builder.dart'; class TelemetryScreen extends StatefulWidget { final Contact contact; @@ -86,11 +87,10 @@ class _TelemetryScreenState extends State { _isLoading = false; _isLoaded = false; }); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.telemetry_requestTimeout), - backgroundColor: Colors.red, - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.telemetry_requestTimeout), + backgroundColor: Colors.red, ); _recordTelemetryResult(false); }); @@ -137,11 +137,10 @@ class _TelemetryScreenState extends State { _parsedTelemetry = parsedTelemetry; }); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.telemetry_receivedData), - backgroundColor: Colors.green, - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.telemetry_receivedData), + backgroundColor: Colors.green, ); _statusTimeout?.cancel(); if (!mounted) return; @@ -182,11 +181,10 @@ class _TelemetryScreenState extends State { _isLoaded = false; }); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.telemetry_errorLoading(e.toString())), - backgroundColor: Colors.red, - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.telemetry_errorLoading(e.toString())), + backgroundColor: Colors.red, ); } } diff --git a/lib/screens/usb_screen.dart b/lib/screens/usb_screen.dart index 2f2713a..6b8fe9d 100644 --- a/lib/screens/usb_screen.dart +++ b/lib/screens/usb_screen.dart @@ -10,6 +10,7 @@ import '../utils/app_logger.dart'; import '../utils/platform_info.dart'; import '../utils/usb_port_labels.dart'; import '../widgets/adaptive_app_bar_title.dart'; +import '../helpers/snack_bar_builder.dart'; import 'contacts_screen.dart'; import 'scanner_screen.dart'; import 'tcp_screen.dart'; @@ -383,11 +384,10 @@ class _UsbScreenState extends State { void _showError(Object error) { if (!mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(_friendlyErrorMessage(error)), - backgroundColor: Colors.red, - ), + showDismissibleSnackBar( + context, + content: Text(_friendlyErrorMessage(error)), + backgroundColor: Colors.red, ); } diff --git a/lib/widgets/path_management_dialog.dart b/lib/widgets/path_management_dialog.dart index 4e91a69..094805a 100644 --- a/lib/widgets/path_management_dialog.dart +++ b/lib/widgets/path_management_dialog.dart @@ -11,6 +11,7 @@ import '../l10n/l10n.dart'; import '../models/contact.dart'; import '../helpers/path_helper.dart'; import '../services/path_history_service.dart'; +import '../helpers/snack_bar_builder.dart'; import 'path_selection_dialog.dart'; class PathManagementDialog { @@ -65,11 +66,10 @@ class _PathManagementDialogState extends State<_PathManagementDialog> { void _showFullPathDialog(BuildContext context, List pathBytes) { final l10n = context.l10n; if (pathBytes.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(l10n.chat_pathDetailsNotAvailable), - duration: const Duration(seconds: 2), - ), + showDismissibleSnackBar( + context, + content: Text(l10n.chat_pathDetailsNotAvailable), + duration: const Duration(seconds: 2), ); return; } @@ -159,11 +159,10 @@ class _PathManagementDialogState extends State<_PathManagementDialog> { ); if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(l10n.chat_hopsCount(result.length)), - duration: const Duration(seconds: 2), - ), + showDismissibleSnackBar( + context, + content: Text(l10n.chat_hopsCount(result.length)), + duration: const Duration(seconds: 2), ); } } @@ -337,13 +336,12 @@ class _PathManagementDialogState extends State<_PathManagementDialog> { _showFullPathDialog(context, path.pathBytes), onTap: () async { if (path.pathBytes.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - l10n.chat_pathDetailsNotAvailable, - ), - duration: const Duration(seconds: 2), + showDismissibleSnackBar( + context, + content: Text( + l10n.chat_pathDetailsNotAvailable, ), + duration: const Duration(seconds: 2), ); return; } @@ -361,13 +359,12 @@ class _PathManagementDialogState extends State<_PathManagementDialog> { if (!context.mounted) return; Navigator.pop(context); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - l10n.path_usingHopsPath(path.hopCount), - ), - duration: const Duration(seconds: 2), + showDismissibleSnackBar( + context, + content: Text( + l10n.path_usingHopsPath(path.hopCount), ), + duration: const Duration(seconds: 2), ); }, ), @@ -459,11 +456,10 @@ class _PathManagementDialogState extends State<_PathManagementDialog> { onTap: () async { await connector.clearContactPath(currentContact); if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(l10n.chat_pathCleared), - duration: const Duration(seconds: 2), - ), + showDismissibleSnackBar( + context, + content: Text(l10n.chat_pathCleared), + duration: const Duration(seconds: 2), ); Navigator.pop(context); }, @@ -489,11 +485,10 @@ class _PathManagementDialogState extends State<_PathManagementDialog> { pathLen: -1, ); if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(l10n.chat_floodModeEnabled), - duration: const Duration(seconds: 2), - ), + showDismissibleSnackBar( + context, + content: Text(l10n.chat_floodModeEnabled), + duration: const Duration(seconds: 2), ); Navigator.pop(context); }, diff --git a/lib/widgets/path_selection_dialog.dart b/lib/widgets/path_selection_dialog.dart index b1733fc..7a890ec 100644 --- a/lib/widgets/path_selection_dialog.dart +++ b/lib/widgets/path_selection_dialog.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:meshcore_open/connector/meshcore_protocol.dart'; import '../l10n/l10n.dart'; import '../models/contact.dart'; +import '../helpers/snack_bar_builder.dart'; class PathSelectionDialog extends StatefulWidget { final List availableContacts; @@ -138,26 +139,22 @@ class _PathSelectionDialogState extends State { // Show error for invalid prefixes if (invalidPrefixes.isNotEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - l10n.path_invalidHexPrefixes(invalidPrefixes.join(", ")), - ), - duration: const Duration(seconds: 3), - backgroundColor: Colors.red, - ), + showDismissibleSnackBar( + context, + content: Text(l10n.path_invalidHexPrefixes(invalidPrefixes.join(", "))), + duration: const Duration(seconds: 3), + backgroundColor: Colors.red, ); return; } // Check max path length (64 hops) if (pathBytesList.length > 64) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(l10n.path_tooLong), - duration: const Duration(seconds: 3), - backgroundColor: Colors.red, - ), + showDismissibleSnackBar( + context, + content: Text(l10n.path_tooLong), + duration: const Duration(seconds: 3), + backgroundColor: Colors.red, ); return; } diff --git a/lib/widgets/room_login_dialog.dart b/lib/widgets/room_login_dialog.dart index 3a923fe..d4028a3 100644 --- a/lib/widgets/room_login_dialog.dart +++ b/lib/widgets/room_login_dialog.dart @@ -10,6 +10,7 @@ import '../services/storage_service.dart'; import '../connector/meshcore_connector.dart'; import '../connector/meshcore_protocol.dart'; import '../utils/app_logger.dart'; +import '../helpers/snack_bar_builder.dart'; import 'path_management_dialog.dart'; class RoomLoginDialog extends StatefulWidget { @@ -175,11 +176,10 @@ class _RoomLoginDialogState extends State { setState(() { _isLoggingIn = false; }); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.login_failed(e.toString())), - backgroundColor: Colors.red, - ), + showDismissibleSnackBar( + context, + content: Text(context.l10n.login_failed(e.toString())), + backgroundColor: Colors.red, ); } } From 37ec8f2f056844b8d177a5824cf36f20eb92585c Mon Sep 17 00:00:00 2001 From: zjs81 Date: Tue, 14 Apr 2026 21:38:12 -0700 Subject: [PATCH 5/5] Add localization for chat and repeater features in multiple languages - Added translations for "Send message", "Guest information", and "Guest tools" in Bulgarian, German, Spanish, French, Hungarian, Italian, Japanese, Korean, Dutch, Polish, Portuguese, Russian, Slovak, Slovenian, Swedish, Ukrainian, and Chinese. - Updated the "Clock synchronization after login" feature subtitle in all affected languages. - Removed untranslated keys from the untranslated.json file as they have now been localized. --- lib/l10n/app_bg.arb | 6 +- lib/l10n/app_de.arb | 6 +- lib/l10n/app_es.arb | 6 +- lib/l10n/app_fr.arb | 6 +- lib/l10n/app_hu.arb | 6 +- lib/l10n/app_it.arb | 6 +- lib/l10n/app_ja.arb | 6 +- lib/l10n/app_ko.arb | 6 +- lib/l10n/app_localizations_bg.dart | 8 +- lib/l10n/app_localizations_de.dart | 8 +- lib/l10n/app_localizations_es.dart | 8 +- lib/l10n/app_localizations_fr.dart | 8 +- lib/l10n/app_localizations_hu.dart | 8 +- lib/l10n/app_localizations_it.dart | 8 +- lib/l10n/app_localizations_ja.dart | 8 +- lib/l10n/app_localizations_ko.dart | 8 +- lib/l10n/app_localizations_nl.dart | 8 +- lib/l10n/app_localizations_pl.dart | 8 +- lib/l10n/app_localizations_pt.dart | 8 +- lib/l10n/app_localizations_ru.dart | 8 +- lib/l10n/app_localizations_sk.dart | 8 +- lib/l10n/app_localizations_sl.dart | 8 +- lib/l10n/app_localizations_sv.dart | 8 +- lib/l10n/app_localizations_uk.dart | 8 +- lib/l10n/app_localizations_zh.dart | 8 +- lib/l10n/app_nl.arb | 6 +- lib/l10n/app_pl.arb | 6 +- lib/l10n/app_pt.arb | 6 +- lib/l10n/app_ru.arb | 6 +- lib/l10n/app_sk.arb | 6 +- lib/l10n/app_sl.arb | 6 +- lib/l10n/app_sv.arb | 6 +- lib/l10n/app_uk.arb | 6 +- lib/l10n/app_zh.arb | 6 +- untranslated.json | 121 +---------------------------- 35 files changed, 154 insertions(+), 205 deletions(-) diff --git a/lib/l10n/app_bg.arb b/lib/l10n/app_bg.arb index 718bfd9..7ac5417 100644 --- a/lib/l10n/app_bg.arb +++ b/lib/l10n/app_bg.arb @@ -2069,5 +2069,9 @@ "description": "Repeater setting subtitle: describes the clock sync after login behavior" }, "repeater_clockSyncAfterLoginSubtitle": "Автоматично изпращайте съобщение \"синхронизиране на часовника\" след успешно влизане.", - "repeater_clockSyncAfterLogin": "Синхронизиране на часовника след влизане" + "repeater_clockSyncAfterLogin": "Синхронизиране на часовника след влизане", + "chat_sendMessage": "Изпратете съобщение", + "room_guest": "Информация за сървъра на стаята", + "repeater_guest": "Информация за ретранслаторите", + "repeater_guestTools": "Инструменти за гости" } diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 54683d2..4695505 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -2097,5 +2097,9 @@ "description": "Repeater setting subtitle: describes the clock sync after login behavior" }, "repeater_clockSyncAfterLogin": "Uhrzeit-Synchronisation nach dem Anmelden", - "repeater_clockSyncAfterLoginSubtitle": "Automatisch \"Uhrzeit-Synchronisierung\" nach erfolgreicher Anmeldung senden." + "repeater_clockSyncAfterLoginSubtitle": "Automatisch \"Uhrzeit-Synchronisierung\" nach erfolgreicher Anmeldung senden.", + "repeater_guest": "Informationen zu Repeatern", + "repeater_guestTools": "Gastwerkzeuge", + "chat_sendMessage": "Nachricht senden", + "room_guest": "Informationen zum Room Server" } diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 6f95d81..ac9527b 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -2097,5 +2097,9 @@ "description": "Repeater setting subtitle: describes the clock sync after login behavior" }, "repeater_clockSyncAfterLoginSubtitle": "Enviar automáticamente la función de \"sincronización de reloj\" después de un inicio de sesión exitoso.", - "repeater_clockSyncAfterLogin": "Sincronización del reloj después de iniciar sesión" + "repeater_clockSyncAfterLogin": "Sincronización del reloj después de iniciar sesión", + "repeater_guest": "Información sobre repetidores", + "chat_sendMessage": "Enviar mensaje", + "repeater_guestTools": "Herramientas para invitados", + "room_guest": "Información del servidor" } diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 4b0497b..a942aa2 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -2069,5 +2069,9 @@ "description": "Repeater setting subtitle: describes the clock sync after login behavior" }, "repeater_clockSyncAfterLoginSubtitle": "Envoyer automatiquement une notification \"synchronisation de l'heure\" après une connexion réussie.", - "repeater_clockSyncAfterLogin": "Synchronisation de l'horloge après la connexion" + "repeater_clockSyncAfterLogin": "Synchronisation de l'horloge après la connexion", + "repeater_guestTools": "Outils pour les invités", + "chat_sendMessage": "Envoyer un message", + "room_guest": "Informations sur le serveur", + "repeater_guest": "Informations sur les répéteurs" } diff --git a/lib/l10n/app_hu.arb b/lib/l10n/app_hu.arb index 3553b18..6f43463 100644 --- a/lib/l10n/app_hu.arb +++ b/lib/l10n/app_hu.arb @@ -2107,5 +2107,9 @@ "description": "Repeater setting subtitle: describes the clock sync after login behavior" }, "repeater_clockSyncAfterLoginSubtitle": "Automatikusan küldje el a \"óra szinkronizálás\" üzenetet a sikeres bejelentkezés után.", - "repeater_clockSyncAfterLogin": "Óra szinkronizálás bejelentkezés után" + "repeater_clockSyncAfterLogin": "Óra szinkronizálás bejelentkezés után", + "repeater_guestTools": "Vendégek számára elérhető eszközök", + "room_guest": "Szoba szerver információk", + "chat_sendMessage": "Üzenet küldése", + "repeater_guest": "Adatok a repeaterről" } diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb index d0e195e..387c8cf 100644 --- a/lib/l10n/app_it.arb +++ b/lib/l10n/app_it.arb @@ -2069,5 +2069,9 @@ "description": "Repeater setting subtitle: describes the clock sync after login behavior" }, "repeater_clockSyncAfterLoginSubtitle": "Invia automaticamente il comando \"sincronizzazione dell'orologio\" dopo un login riuscito.", - "repeater_clockSyncAfterLogin": "Sincronizzazione dell'orologio dopo il login" + "repeater_clockSyncAfterLogin": "Sincronizzazione dell'orologio dopo il login", + "repeater_guest": "Informazioni sul ripetitore", + "repeater_guestTools": "Strumenti per gli ospiti", + "chat_sendMessage": "Invia messaggio", + "room_guest": "Informazioni sul server" } diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb index 6f85116..b63f146 100644 --- a/lib/l10n/app_ja.arb +++ b/lib/l10n/app_ja.arb @@ -2107,5 +2107,9 @@ "description": "Repeater setting subtitle: describes the clock sync after login behavior" }, "repeater_clockSyncAfterLogin": "ログイン後、時計の時刻を同期する", - "repeater_clockSyncAfterLoginSubtitle": "ログインが成功した場合、自動的に「時刻同期」を送信する。" + "repeater_clockSyncAfterLoginSubtitle": "ログインが成功した場合、自動的に「時刻同期」を送信する。", + "room_guest": "ルームサーバーに関する情報", + "chat_sendMessage": "メッセージを送信する", + "repeater_guest": "繰り返し送信に関する情報", + "repeater_guestTools": "ゲスト向けツール" } diff --git a/lib/l10n/app_ko.arb b/lib/l10n/app_ko.arb index bd73847..40721fe 100644 --- a/lib/l10n/app_ko.arb +++ b/lib/l10n/app_ko.arb @@ -2107,5 +2107,9 @@ "description": "Repeater setting subtitle: describes the clock sync after login behavior" }, "repeater_clockSyncAfterLogin": "로그인 후 시계 동기화", - "repeater_clockSyncAfterLoginSubtitle": "성공적인 로그인 후, 자동으로 \"시간 동기화\"를 전송합니다." + "repeater_clockSyncAfterLoginSubtitle": "성공적인 로그인 후, 자동으로 \"시간 동기화\"를 전송합니다.", + "repeater_guestTools": "손님용 도구", + "chat_sendMessage": "메시지를 보내기", + "repeater_guest": "반복 장비 정보", + "room_guest": "서버 정보" } diff --git a/lib/l10n/app_localizations_bg.dart b/lib/l10n/app_localizations_bg.dart index ffb1728..b3e1279 100644 --- a/lib/l10n/app_localizations_bg.dart +++ b/lib/l10n/app_localizations_bg.dart @@ -1240,7 +1240,7 @@ class AppLocalizationsBg extends AppLocalizations { String get chat_noMessages => 'Няма съобщения.'; @override - String get chat_sendMessage => 'Send message'; + String get chat_sendMessage => 'Изпратете съобщение'; @override String chat_sendMessageTo(String contactName) { @@ -2020,16 +2020,16 @@ class AppLocalizationsBg extends AppLocalizations { String get room_management => 'Управление на сървъра за стая'; @override - String get repeater_guest => 'Repeater Information'; + String get repeater_guest => 'Информация за ретранслаторите'; @override - String get room_guest => 'Room Server Information'; + String get room_guest => 'Информация за сървъра на стаята'; @override String get repeater_managementTools => 'Инструменти за управление'; @override - String get repeater_guestTools => 'Guest Tools'; + String get repeater_guestTools => 'Инструменти за гости'; @override String get repeater_status => 'Статус'; diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index f58e94a..d7c1691 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -1239,7 +1239,7 @@ class AppLocalizationsDe extends AppLocalizations { String get chat_noMessages => 'Noch keine Nachrichten.'; @override - String get chat_sendMessage => 'Send message'; + String get chat_sendMessage => 'Nachricht senden'; @override String chat_sendMessageTo(String contactName) { @@ -2018,16 +2018,16 @@ class AppLocalizationsDe extends AppLocalizations { String get room_management => 'Raum-Server-Verwaltung'; @override - String get repeater_guest => 'Repeater Information'; + String get repeater_guest => 'Informationen zu Repeatern'; @override - String get room_guest => 'Room Server Information'; + String get room_guest => 'Informationen zum Room Server'; @override String get repeater_managementTools => 'Verwaltungs-Tools'; @override - String get repeater_guestTools => 'Guest Tools'; + String get repeater_guestTools => 'Gastwerkzeuge'; @override String get repeater_status => 'Status'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index bdcf34c..a127012 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -1239,7 +1239,7 @@ class AppLocalizationsEs extends AppLocalizations { String get chat_noMessages => 'Aún no hay mensajes'; @override - String get chat_sendMessage => 'Send message'; + String get chat_sendMessage => 'Enviar mensaje'; @override String chat_sendMessageTo(String contactName) { @@ -2016,16 +2016,16 @@ class AppLocalizationsEs extends AppLocalizations { String get room_management => 'Administración del Servidor de Habitación'; @override - String get repeater_guest => 'Repeater Information'; + String get repeater_guest => 'Información sobre repetidores'; @override - String get room_guest => 'Room Server Information'; + String get room_guest => 'Información del servidor'; @override String get repeater_managementTools => 'Herramientas de Gestión'; @override - String get repeater_guestTools => 'Guest Tools'; + String get repeater_guestTools => 'Herramientas para invitados'; @override String get repeater_status => 'Estado'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index 6689efb..a006391 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -1244,7 +1244,7 @@ class AppLocalizationsFr extends AppLocalizations { String get chat_noMessages => 'Aucun message pour le moment.'; @override - String get chat_sendMessage => 'Send message'; + String get chat_sendMessage => 'Envoyer un message'; @override String chat_sendMessageTo(String contactName) { @@ -2027,16 +2027,16 @@ class AppLocalizationsFr extends AppLocalizations { String get room_management => 'Administrattion Room Server'; @override - String get repeater_guest => 'Repeater Information'; + String get repeater_guest => 'Informations sur les répéteurs'; @override - String get room_guest => 'Room Server Information'; + String get room_guest => 'Informations sur le serveur'; @override String get repeater_managementTools => 'Outils de Gestion'; @override - String get repeater_guestTools => 'Guest Tools'; + String get repeater_guestTools => 'Outils pour les invités'; @override String get repeater_status => 'État'; diff --git a/lib/l10n/app_localizations_hu.dart b/lib/l10n/app_localizations_hu.dart index a274768..1ad8558 100644 --- a/lib/l10n/app_localizations_hu.dart +++ b/lib/l10n/app_localizations_hu.dart @@ -1247,7 +1247,7 @@ class AppLocalizationsHu extends AppLocalizations { String get chat_noMessages => 'Még nincs üzenet.'; @override - String get chat_sendMessage => 'Send message'; + String get chat_sendMessage => 'Üzenet küldése'; @override String chat_sendMessageTo(String contactName) { @@ -2031,16 +2031,16 @@ class AppLocalizationsHu extends AppLocalizations { String get room_management => 'Szoba-szerver kezelés'; @override - String get repeater_guest => 'Repeater Information'; + String get repeater_guest => 'Adatok a repeaterről'; @override - String get room_guest => 'Room Server Information'; + String get room_guest => 'Szoba szerver információk'; @override String get repeater_managementTools => 'Menedzsmentes eszközök'; @override - String get repeater_guestTools => 'Guest Tools'; + String get repeater_guestTools => 'Vendégek számára elérhető eszközök'; @override String get repeater_status => 'Állapot'; diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index 2dae65c..3a55559 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -1240,7 +1240,7 @@ class AppLocalizationsIt extends AppLocalizations { String get chat_noMessages => 'Nessun messaggio ancora'; @override - String get chat_sendMessage => 'Send message'; + String get chat_sendMessage => 'Invia messaggio'; @override String chat_sendMessageTo(String contactName) { @@ -2017,16 +2017,16 @@ class AppLocalizationsIt extends AppLocalizations { String get room_management => 'Gestione del Server di Camera'; @override - String get repeater_guest => 'Repeater Information'; + String get repeater_guest => 'Informazioni sul ripetitore'; @override - String get room_guest => 'Room Server Information'; + String get room_guest => 'Informazioni sul server'; @override String get repeater_managementTools => 'Strumenti di Gestione'; @override - String get repeater_guestTools => 'Guest Tools'; + String get repeater_guestTools => 'Strumenti per gli ospiti'; @override String get repeater_status => 'Stato'; diff --git a/lib/l10n/app_localizations_ja.dart b/lib/l10n/app_localizations_ja.dart index bf47adc..afb8c29 100644 --- a/lib/l10n/app_localizations_ja.dart +++ b/lib/l10n/app_localizations_ja.dart @@ -1180,7 +1180,7 @@ class AppLocalizationsJa extends AppLocalizations { String get chat_noMessages => 'まだメッセージは届いていません'; @override - String get chat_sendMessage => 'Send message'; + String get chat_sendMessage => 'メッセージを送信する'; @override String chat_sendMessageTo(String contactName) { @@ -1933,16 +1933,16 @@ class AppLocalizationsJa extends AppLocalizations { String get room_management => 'ルームサーバーの管理'; @override - String get repeater_guest => 'Repeater Information'; + String get repeater_guest => '繰り返し送信に関する情報'; @override - String get room_guest => 'Room Server Information'; + String get room_guest => 'ルームサーバーに関する情報'; @override String get repeater_managementTools => '管理ツール'; @override - String get repeater_guestTools => 'Guest Tools'; + String get repeater_guestTools => 'ゲスト向けツール'; @override String get repeater_status => 'ステータス'; diff --git a/lib/l10n/app_localizations_ko.dart b/lib/l10n/app_localizations_ko.dart index ef66cc4..ff4bd26 100644 --- a/lib/l10n/app_localizations_ko.dart +++ b/lib/l10n/app_localizations_ko.dart @@ -1175,7 +1175,7 @@ class AppLocalizationsKo extends AppLocalizations { String get chat_noMessages => '아직 메시지가 없습니다.'; @override - String get chat_sendMessage => 'Send message'; + String get chat_sendMessage => '메시지를 보내기'; @override String chat_sendMessageTo(String contactName) { @@ -1930,16 +1930,16 @@ class AppLocalizationsKo extends AppLocalizations { String get room_management => '방 서버 관리'; @override - String get repeater_guest => 'Repeater Information'; + String get repeater_guest => '반복 장비 정보'; @override - String get room_guest => 'Room Server Information'; + String get room_guest => '서버 정보'; @override String get repeater_managementTools => '관리 도구'; @override - String get repeater_guestTools => 'Guest Tools'; + String get repeater_guestTools => '손님용 도구'; @override String get repeater_status => '상태'; diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index 0779ffd..dd770e1 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -1228,7 +1228,7 @@ class AppLocalizationsNl extends AppLocalizations { String get chat_noMessages => 'Nog geen berichten.'; @override - String get chat_sendMessage => 'Send message'; + String get chat_sendMessage => 'Verzend bericht'; @override String chat_sendMessageTo(String contactName) { @@ -2004,16 +2004,16 @@ class AppLocalizationsNl extends AppLocalizations { String get room_management => 'Beheer Server Kamer'; @override - String get repeater_guest => 'Repeater Information'; + String get repeater_guest => 'Informatie over herhalingsapparatuur'; @override - String get room_guest => 'Room Server Information'; + String get room_guest => 'Informatie over de server'; @override String get repeater_managementTools => 'Beheerfuncties'; @override - String get repeater_guestTools => 'Guest Tools'; + String get repeater_guestTools => 'Gastenfuncties'; @override String get repeater_status => 'Status'; diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index cfffdf0..357dd7e 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -1248,7 +1248,7 @@ class AppLocalizationsPl extends AppLocalizations { String get chat_noMessages => 'Brak jeszcze wiadomości'; @override - String get chat_sendMessage => 'Send message'; + String get chat_sendMessage => 'Wyślij wiadomość'; @override String chat_sendMessageTo(String contactName) { @@ -2032,16 +2032,16 @@ class AppLocalizationsPl extends AppLocalizations { String get room_management => 'Zarządzanie Serwerem Pokoju'; @override - String get repeater_guest => 'Repeater Information'; + String get repeater_guest => 'Informacje dotyczące urządzenia powtarzającego'; @override - String get room_guest => 'Room Server Information'; + String get room_guest => 'Informacje o serwerze'; @override String get repeater_managementTools => 'Narzędzia Zarządzania'; @override - String get repeater_guestTools => 'Guest Tools'; + String get repeater_guestTools => 'Narzędzia dla gości'; @override String get repeater_status => 'Status'; diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart index 2abc403..2dfcd8b 100644 --- a/lib/l10n/app_localizations_pt.dart +++ b/lib/l10n/app_localizations_pt.dart @@ -1239,7 +1239,7 @@ class AppLocalizationsPt extends AppLocalizations { String get chat_noMessages => 'Ainda não existem mensagens.'; @override - String get chat_sendMessage => 'Send message'; + String get chat_sendMessage => 'Enviar mensagem'; @override String chat_sendMessageTo(String contactName) { @@ -2016,16 +2016,16 @@ class AppLocalizationsPt extends AppLocalizations { String get room_management => 'Gerenciamento de Servidor de Sala'; @override - String get repeater_guest => 'Repeater Information'; + String get repeater_guest => 'Informações sobre repetidores'; @override - String get room_guest => 'Room Server Information'; + String get room_guest => 'Informações do Servidor'; @override String get repeater_managementTools => 'Ferramentas de Gerenciamento'; @override - String get repeater_guestTools => 'Guest Tools'; + String get repeater_guestTools => 'Ferramentas para hóspedes'; @override String get repeater_status => 'Status'; diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index 8002011..4fac42c 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -1239,7 +1239,7 @@ class AppLocalizationsRu extends AppLocalizations { String get chat_noMessages => 'Сообщений пока нет'; @override - String get chat_sendMessage => 'Send message'; + String get chat_sendMessage => 'Отправить сообщение'; @override String chat_sendMessageTo(String contactName) { @@ -2020,16 +2020,16 @@ class AppLocalizationsRu extends AppLocalizations { String get room_management => 'Управление сервером комнат'; @override - String get repeater_guest => 'Repeater Information'; + String get repeater_guest => 'Информация о ретрансляторе'; @override - String get room_guest => 'Room Server Information'; + String get room_guest => 'Информация о сервере'; @override String get repeater_managementTools => 'Инструменты управления'; @override - String get repeater_guestTools => 'Guest Tools'; + String get repeater_guestTools => 'Инструменты для гостей'; @override String get repeater_status => 'Статус'; diff --git a/lib/l10n/app_localizations_sk.dart b/lib/l10n/app_localizations_sk.dart index aceaa69..c42e024 100644 --- a/lib/l10n/app_localizations_sk.dart +++ b/lib/l10n/app_localizations_sk.dart @@ -1227,7 +1227,7 @@ class AppLocalizationsSk extends AppLocalizations { String get chat_noMessages => 'Zatiaľ žiadne správy.'; @override - String get chat_sendMessage => 'Send message'; + String get chat_sendMessage => 'Odoslať správu'; @override String chat_sendMessageTo(String contactName) { @@ -2005,16 +2005,16 @@ class AppLocalizationsSk extends AppLocalizations { String get room_management => 'Správa servera miestnosti'; @override - String get repeater_guest => 'Repeater Information'; + String get repeater_guest => 'Informácie o opakovači'; @override - String get room_guest => 'Room Server Information'; + String get room_guest => 'Informácie o serveri'; @override String get repeater_managementTools => 'Nástroje na správu'; @override - String get repeater_guestTools => 'Guest Tools'; + String get repeater_guestTools => 'Nástroje pre hostí'; @override String get repeater_status => 'Status'; diff --git a/lib/l10n/app_localizations_sl.dart b/lib/l10n/app_localizations_sl.dart index 7f1d320..2d89aa4 100644 --- a/lib/l10n/app_localizations_sl.dart +++ b/lib/l10n/app_localizations_sl.dart @@ -1225,7 +1225,7 @@ class AppLocalizationsSl extends AppLocalizations { String get chat_noMessages => 'Še ni sporočil.'; @override - String get chat_sendMessage => 'Send message'; + String get chat_sendMessage => 'Pošlji sporočilo'; @override String chat_sendMessageTo(String contactName) { @@ -2002,16 +2002,16 @@ class AppLocalizationsSl extends AppLocalizations { String get room_management => 'Upravljanje stremlišča'; @override - String get repeater_guest => 'Repeater Information'; + String get repeater_guest => 'Informacije o ponovljalniku'; @override - String get room_guest => 'Room Server Information'; + String get room_guest => 'Informacije o strežniku'; @override String get repeater_managementTools => 'Upravne orodje'; @override - String get repeater_guestTools => 'Guest Tools'; + String get repeater_guestTools => 'Naložila za goste'; @override String get repeater_status => 'Status'; diff --git a/lib/l10n/app_localizations_sv.dart b/lib/l10n/app_localizations_sv.dart index efa4d60..38e0893 100644 --- a/lib/l10n/app_localizations_sv.dart +++ b/lib/l10n/app_localizations_sv.dart @@ -1218,7 +1218,7 @@ class AppLocalizationsSv extends AppLocalizations { String get chat_noMessages => 'Inga meddelanden ännu'; @override - String get chat_sendMessage => 'Send message'; + String get chat_sendMessage => 'Skicka meddelande'; @override String chat_sendMessageTo(String contactName) { @@ -1991,16 +1991,16 @@ class AppLocalizationsSv extends AppLocalizations { String get room_management => 'Rumserverhantering'; @override - String get repeater_guest => 'Repeater Information'; + String get repeater_guest => 'Information om repetorer'; @override - String get room_guest => 'Room Server Information'; + String get room_guest => 'Information om servern'; @override String get repeater_managementTools => 'Administrationsverktyg'; @override - String get repeater_guestTools => 'Guest Tools'; + String get repeater_guestTools => 'Gästverktyg'; @override String get repeater_status => 'Status'; diff --git a/lib/l10n/app_localizations_uk.dart b/lib/l10n/app_localizations_uk.dart index 6134c64..8ed4b9f 100644 --- a/lib/l10n/app_localizations_uk.dart +++ b/lib/l10n/app_localizations_uk.dart @@ -1231,7 +1231,7 @@ class AppLocalizationsUk extends AppLocalizations { String get chat_noMessages => 'Поки немає повідомлень.'; @override - String get chat_sendMessage => 'Send message'; + String get chat_sendMessage => 'Надіслати повідомлення'; @override String chat_sendMessageTo(String contactName) { @@ -2015,16 +2015,16 @@ class AppLocalizationsUk extends AppLocalizations { String get room_management => 'Адміністрування сервера кімнати'; @override - String get repeater_guest => 'Repeater Information'; + String get repeater_guest => 'Інформація про ретранслятор'; @override - String get room_guest => 'Room Server Information'; + String get room_guest => 'Інформація про сервер кімнати'; @override String get repeater_managementTools => 'Інструменти керування'; @override - String get repeater_guestTools => 'Guest Tools'; + String get repeater_guestTools => 'Інструменти для гостей'; @override String get repeater_status => 'Статус'; diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index 56f235c..4f38c64 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -1162,7 +1162,7 @@ class AppLocalizationsZh extends AppLocalizations { String get chat_noMessages => '暂无消息'; @override - String get chat_sendMessage => 'Send message'; + String get chat_sendMessage => '发送消息'; @override String chat_sendMessageTo(String contactName) { @@ -1891,16 +1891,16 @@ class AppLocalizationsZh extends AppLocalizations { String get room_management => '房间服务器管理'; @override - String get repeater_guest => 'Repeater Information'; + String get repeater_guest => '重复器信息'; @override - String get room_guest => 'Room Server Information'; + String get room_guest => '服务器信息'; @override String get repeater_managementTools => '管理工具'; @override - String get repeater_guestTools => 'Guest Tools'; + String get repeater_guestTools => '访客工具'; @override String get repeater_status => '状态'; diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb index abfd5e7..96bdb84 100644 --- a/lib/l10n/app_nl.arb +++ b/lib/l10n/app_nl.arb @@ -2069,5 +2069,9 @@ "description": "Repeater setting subtitle: describes the clock sync after login behavior" }, "repeater_clockSyncAfterLoginSubtitle": "Automatisch een \"klok synchroniseren\" bericht versturen na een succesvolle inlog.", - "repeater_clockSyncAfterLogin": "Na het inloggen, klok synchroniseren" + "repeater_clockSyncAfterLogin": "Na het inloggen, klok synchroniseren", + "repeater_guestTools": "Gastenfuncties", + "room_guest": "Informatie over de server", + "chat_sendMessage": "Verzend bericht", + "repeater_guest": "Informatie over herhalingsapparatuur" } diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index e626708..b62a78a 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -2107,5 +2107,9 @@ "description": "Repeater setting subtitle: describes the clock sync after login behavior" }, "repeater_clockSyncAfterLogin": "Synchronizacja zegara po zalogowaniu", - "repeater_clockSyncAfterLoginSubtitle": "Automatycznie wysyłaj powiadomienie \"synchronizacja zegara\" po pomyślnym zalogowaniu." + "repeater_clockSyncAfterLoginSubtitle": "Automatycznie wysyłaj powiadomienie \"synchronizacja zegara\" po pomyślnym zalogowaniu.", + "chat_sendMessage": "Wyślij wiadomość", + "repeater_guestTools": "Narzędzia dla gości", + "repeater_guest": "Informacje dotyczące urządzenia powtarzającego", + "room_guest": "Informacje o serwerze" } diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index bacc1ca..bf3e893 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -2069,5 +2069,9 @@ "description": "Repeater setting subtitle: describes the clock sync after login behavior" }, "repeater_clockSyncAfterLoginSubtitle": "Enviar automaticamente a sincronização do \"relógio\" após um login bem-sucedido.", - "repeater_clockSyncAfterLogin": "Sincronização do relógio após o login" + "repeater_clockSyncAfterLogin": "Sincronização do relógio após o login", + "room_guest": "Informações do Servidor", + "chat_sendMessage": "Enviar mensagem", + "repeater_guest": "Informações sobre repetidores", + "repeater_guestTools": "Ferramentas para hóspedes" } diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb index e4dad42..a83d139 100644 --- a/lib/l10n/app_ru.arb +++ b/lib/l10n/app_ru.arb @@ -1309,5 +1309,9 @@ "description": "Repeater setting subtitle: describes the clock sync after login behavior" }, "repeater_clockSyncAfterLogin": "Синхронизация часов после входа в систему", - "repeater_clockSyncAfterLoginSubtitle": "Автоматически отправлять сообщение \"синхронизация времени\" после успешной авторизации." + "repeater_clockSyncAfterLoginSubtitle": "Автоматически отправлять сообщение \"синхронизация времени\" после успешной авторизации.", + "chat_sendMessage": "Отправить сообщение", + "repeater_guest": "Информация о ретрансляторе", + "room_guest": "Информация о сервере", + "repeater_guestTools": "Инструменты для гостей" } diff --git a/lib/l10n/app_sk.arb b/lib/l10n/app_sk.arb index 937bacb..e4466c3 100644 --- a/lib/l10n/app_sk.arb +++ b/lib/l10n/app_sk.arb @@ -2069,5 +2069,9 @@ "description": "Repeater setting subtitle: describes the clock sync after login behavior" }, "repeater_clockSyncAfterLogin": "Synchronizácia hodiniek po prihlávení", - "repeater_clockSyncAfterLoginSubtitle": "Automaticky posielajte notifikáciu \"synchronizácia času\" po úspešnom prihládení." + "repeater_clockSyncAfterLoginSubtitle": "Automaticky posielajte notifikáciu \"synchronizácia času\" po úspešnom prihládení.", + "chat_sendMessage": "Odoslať správu", + "repeater_guest": "Informácie o opakovači", + "room_guest": "Informácie o serveri", + "repeater_guestTools": "Nástroje pre hostí" } diff --git a/lib/l10n/app_sl.arb b/lib/l10n/app_sl.arb index 58d0f9e..f6a317e 100644 --- a/lib/l10n/app_sl.arb +++ b/lib/l10n/app_sl.arb @@ -2069,5 +2069,9 @@ "description": "Repeater setting subtitle: describes the clock sync after login behavior" }, "repeater_clockSyncAfterLoginSubtitle": "Samodejno po uspešnem vstopu pošljite obvestilo o sinhronizaciji časa.", - "repeater_clockSyncAfterLogin": "Sinhronizacija ure po prijavi" + "repeater_clockSyncAfterLogin": "Sinhronizacija ure po prijavi", + "repeater_guest": "Informacije o ponovljalniku", + "chat_sendMessage": "Pošlji sporočilo", + "room_guest": "Informacije o strežniku", + "repeater_guestTools": "Naložila za goste" } diff --git a/lib/l10n/app_sv.arb b/lib/l10n/app_sv.arb index 59b27a4..eab348c 100644 --- a/lib/l10n/app_sv.arb +++ b/lib/l10n/app_sv.arb @@ -2069,5 +2069,9 @@ "description": "Repeater setting subtitle: describes the clock sync after login behavior" }, "repeater_clockSyncAfterLoginSubtitle": "Automatiskt skicka \"klocksynkronisering\" efter en lyckad inloggning.", - "repeater_clockSyncAfterLogin": "Synkronisera klockan efter inloggning" + "repeater_clockSyncAfterLogin": "Synkronisera klockan efter inloggning", + "repeater_guest": "Information om repetorer", + "chat_sendMessage": "Skicka meddelande", + "repeater_guestTools": "Gästverktyg", + "room_guest": "Information om servern" } diff --git a/lib/l10n/app_uk.arb b/lib/l10n/app_uk.arb index c19f3bd..a005b36 100644 --- a/lib/l10n/app_uk.arb +++ b/lib/l10n/app_uk.arb @@ -2069,5 +2069,9 @@ "description": "Repeater setting subtitle: describes the clock sync after login behavior" }, "repeater_clockSyncAfterLoginSubtitle": "Автоматично надсилати повідомлення \"синхронізація годин\" після успішного входу.", - "repeater_clockSyncAfterLogin": "Синхронізація годин після входу" + "repeater_clockSyncAfterLogin": "Синхронізація годин після входу", + "repeater_guestTools": "Інструменти для гостей", + "repeater_guest": "Інформація про ретранслятор", + "room_guest": "Інформація про сервер кімнати", + "chat_sendMessage": "Надіслати повідомлення" } diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 3fbfc39..9dc2325 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -2074,5 +2074,9 @@ "description": "Repeater setting subtitle: describes the clock sync after login behavior" }, "repeater_clockSyncAfterLogin": "登录后,自动同步时钟", - "repeater_clockSyncAfterLoginSubtitle": "在成功登录后,自动发送“时钟同步”指令。" + "repeater_clockSyncAfterLoginSubtitle": "在成功登录后,自动发送“时钟同步”指令。", + "repeater_guestTools": "访客工具", + "repeater_guest": "重复器信息", + "chat_sendMessage": "发送消息", + "room_guest": "服务器信息" } diff --git a/untranslated.json b/untranslated.json index 2b4bbbc..9e26dfe 100644 --- a/untranslated.json +++ b/untranslated.json @@ -1,120 +1 @@ -{ - "bg": [ - "chat_sendMessage", - "repeater_guest", - "room_guest", - "repeater_guestTools" - ], - - "de": [ - "chat_sendMessage", - "repeater_guest", - "room_guest", - "repeater_guestTools" - ], - - "es": [ - "chat_sendMessage", - "repeater_guest", - "room_guest", - "repeater_guestTools" - ], - - "fr": [ - "chat_sendMessage", - "repeater_guest", - "room_guest", - "repeater_guestTools" - ], - - "hu": [ - "chat_sendMessage", - "repeater_guest", - "room_guest", - "repeater_guestTools" - ], - - "it": [ - "chat_sendMessage", - "repeater_guest", - "room_guest", - "repeater_guestTools" - ], - - "ja": [ - "chat_sendMessage", - "repeater_guest", - "room_guest", - "repeater_guestTools" - ], - - "ko": [ - "chat_sendMessage", - "repeater_guest", - "room_guest", - "repeater_guestTools" - ], - - "nl": [ - "chat_sendMessage", - "repeater_guest", - "room_guest", - "repeater_guestTools" - ], - - "pl": [ - "chat_sendMessage", - "repeater_guest", - "room_guest", - "repeater_guestTools" - ], - - "pt": [ - "chat_sendMessage", - "repeater_guest", - "room_guest", - "repeater_guestTools" - ], - - "ru": [ - "chat_sendMessage", - "repeater_guest", - "room_guest", - "repeater_guestTools" - ], - - "sk": [ - "chat_sendMessage", - "repeater_guest", - "room_guest", - "repeater_guestTools" - ], - - "sl": [ - "chat_sendMessage", - "repeater_guest", - "room_guest", - "repeater_guestTools" - ], - - "sv": [ - "chat_sendMessage", - "repeater_guest", - "room_guest", - "repeater_guestTools" - ], - - "uk": [ - "chat_sendMessage", - "repeater_guest", - "room_guest", - "repeater_guestTools" - ], - - "zh": [ - "chat_sendMessage", - "repeater_guest", - "room_guest", - "repeater_guestTools" - ] -} +{} \ No newline at end of file