Merge remote-tracking branch 'upstream/main'

This commit is contained in:
Ben Allfree
2026-02-23 14:56:00 -08:00
44 changed files with 615 additions and 205 deletions
+1
View File
@@ -78,6 +78,7 @@ MeshCore Open is a cross-platform mobile application for communicating with Mesh
-**Android**: Full support (API 21+) -**Android**: Full support (API 21+)
-**iOS**: Full support (iOS 12+) -**iOS**: Full support (iOS 12+)
- 🚧 **Desktop**: Limited support (macOS/Linux/Windows) - 🚧 **Desktop**: Limited support (macOS/Linux/Windows)
- 🚧 **Web**: Under construction (Chrome)
### Dependencies ### Dependencies
+3 -3
View File
@@ -19,13 +19,13 @@ android {
ndkVersion = flutter.ndkVersion ndkVersion = flutter.ndkVersion
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_11 sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_17
isCoreLibraryDesugaringEnabled = true isCoreLibraryDesugaringEnabled = true
} }
kotlinOptions { kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString() jvmTarget = JavaVersion.VERSION_17.toString()
} }
defaultConfig { defaultConfig {
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M268-240 42-466l57-56 170 170 56 56-57 56Zm226 0L268-466l56-57 170 170 368-368 56 57-424 424Zm0-226-57-56 198-198 57 56-198 198Z"/></svg>

After

Width:  |  Height:  |  Size: 253 B

+2
View File
@@ -1554,6 +1554,8 @@
"contacts_clipboardEmpty": "Клипборда е празна.", "contacts_clipboardEmpty": "Клипборда е празна.",
"contacts_invalidAdvertFormat": "Невалидни данни за контакт", "contacts_invalidAdvertFormat": "Невалидни данни за контакт",
"appSettings_languageRu": "Руски", "appSettings_languageRu": "Руски",
"appSettings_enableMessageTracing": "Разрешаване на проследяване на съобщения",
"appSettings_enableMessageTracingSubtitle": "Показване на подробни метаданни за маршрутизация и синхронизация за съобщения",
"contacts_contactImported": "Контактът е импортиран.", "contacts_contactImported": "Контактът е импортиран.",
"contacts_zeroHopAdvert": "Реклама без скок", "contacts_zeroHopAdvert": "Реклама без скок",
"contacts_contactImportFailed": "Контактът не е успешно импортиран.", "contacts_contactImportFailed": "Контактът не е успешно импортиран.",
+2
View File
@@ -1554,6 +1554,8 @@
"contacts_invalidAdvertFormat": "Ungültige Kontaktdaten", "contacts_invalidAdvertFormat": "Ungültige Kontaktdaten",
"contacts_clipboardEmpty": "Die Zwischenablage ist leer.", "contacts_clipboardEmpty": "Die Zwischenablage ist leer.",
"appSettings_languageUk": "Ukrainisch", "appSettings_languageUk": "Ukrainisch",
"appSettings_enableMessageTracing": "Nachrichtenverfolgung aktivieren",
"appSettings_enableMessageTracingSubtitle": "Detaillierte Routing- und Timing-Metadaten für Nachrichten anzeigen",
"contacts_contactImported": "Kontakt wurde importiert.", "contacts_contactImported": "Kontakt wurde importiert.",
"contacts_contactImportFailed": "Kontakt konnte nicht importiert werden", "contacts_contactImportFailed": "Kontakt konnte nicht importiert werden",
"contacts_zeroHopAdvert": "Zero-Hop-Ankündigung", "contacts_zeroHopAdvert": "Zero-Hop-Ankündigung",
+2
View File
@@ -183,6 +183,8 @@
"appSettings_languageBg": "Български", "appSettings_languageBg": "Български",
"appSettings_languageRu": "Русский", "appSettings_languageRu": "Русский",
"appSettings_languageUk": "Українська", "appSettings_languageUk": "Українська",
"appSettings_enableMessageTracing": "Enable Message Tracing",
"appSettings_enableMessageTracingSubtitle": "Show detailed routing and timing metadata for messages",
"appSettings_notifications": "Notifications", "appSettings_notifications": "Notifications",
"appSettings_enableNotifications": "Enable Notifications", "appSettings_enableNotifications": "Enable Notifications",
"appSettings_enableNotificationsSubtitle": "Receive notifications for messages and adverts", "appSettings_enableNotificationsSubtitle": "Receive notifications for messages and adverts",
+2
View File
@@ -1553,6 +1553,8 @@
"appSettings_languageUk": "Ucraniano", "appSettings_languageUk": "Ucraniano",
"contacts_clipboardEmpty": "El portapapeles está vacío.", "contacts_clipboardEmpty": "El portapapeles está vacío.",
"appSettings_languageRu": "Ruso", "appSettings_languageRu": "Ruso",
"appSettings_enableMessageTracing": "Habilitar seguimiento de mensajes",
"appSettings_enableMessageTracingSubtitle": "Mostrar metadatos detallados de enrutamiento y tiempo para los mensajes",
"contacts_invalidAdvertFormat": "Datos de contacto no válidos", "contacts_invalidAdvertFormat": "Datos de contacto no válidos",
"contacts_floodAdvert": "Anuncio de inundación", "contacts_floodAdvert": "Anuncio de inundación",
"contacts_contactImported": "El contacto ha sido importado.", "contacts_contactImported": "El contacto ha sido importado.",
+5 -3
View File
@@ -262,7 +262,7 @@
} }
}, },
"contacts_manageRepeater": "Gérer le répéteur", "contacts_manageRepeater": "Gérer le répéteur",
"contacts_roomLogin": "Connexion Salle", "contacts_roomLogin": "Connexion Room Server",
"contacts_openChat": "Ouverture du Chat", "contacts_openChat": "Ouverture du Chat",
"contacts_editGroup": "Modifier le groupe", "contacts_editGroup": "Modifier le groupe",
"contacts_deleteGroup": "Supprimer le groupe", "contacts_deleteGroup": "Supprimer le groupe",
@@ -798,7 +798,7 @@
"dialog_disconnect": "Déconnecter", "dialog_disconnect": "Déconnecter",
"dialog_disconnectConfirm": "Êtes-vous sûr de vouloir vous déconnecter de cet appareil ?", "dialog_disconnectConfirm": "Êtes-vous sûr de vouloir vous déconnecter de cet appareil ?",
"login_repeaterLogin": "Connexion au répéteur", "login_repeaterLogin": "Connexion au répéteur",
"login_roomLogin": "Connexion Salle", "login_roomLogin": "Connexion Room Server",
"login_password": "Mot de passe", "login_password": "Mot de passe",
"login_enterPassword": "Entrez votre mot de passe", "login_enterPassword": "Entrez votre mot de passe",
"login_savePassword": "Sauvegarder le mot de passe", "login_savePassword": "Sauvegarder le mot de passe",
@@ -1393,7 +1393,7 @@
"settings_locationIntervalSec": "Intervalle de mise-à-jour du GPS (Secondes)", "settings_locationIntervalSec": "Intervalle de mise-à-jour du GPS (Secondes)",
"settings_locationIntervalInvalid": "L'intervalle doit être compris entre 60 et 86400 secondes.", "settings_locationIntervalInvalid": "L'intervalle doit être compris entre 60 et 86400 secondes.",
"contacts_manageRoom": "Gérer le Room Server", "contacts_manageRoom": "Gérer le Room Server",
"room_management": "Administración del Servidor de Habitación", "room_management": "Administrattion Room Server",
"@community_joinConfirmation": { "@community_joinConfirmation": {
"placeholders": { "placeholders": {
"name": { "name": {
@@ -1553,6 +1553,8 @@
"contacts_invalidAdvertFormat": "Données de contact non valides", "contacts_invalidAdvertFormat": "Données de contact non valides",
"appSettings_languageUk": "Ukrainien", "appSettings_languageUk": "Ukrainien",
"appSettings_languageRu": "Russe", "appSettings_languageRu": "Russe",
"appSettings_enableMessageTracing": "Activer le traçage des messages",
"appSettings_enableMessageTracingSubtitle": "Afficher les métadonnées détaillées de routage et de synchronisation des messages",
"contacts_clipboardEmpty": "Le presse-papiers est vide.", "contacts_clipboardEmpty": "Le presse-papiers est vide.",
"contacts_contactImported": "Le contact a été importé.", "contacts_contactImported": "Le contact a été importé.",
"contacts_floodAdvert": "Annonce à tout le réseau", "contacts_floodAdvert": "Annonce à tout le réseau",
+2
View File
@@ -1553,6 +1553,8 @@
"appSettings_languageRu": "Russo", "appSettings_languageRu": "Russo",
"contacts_invalidAdvertFormat": "Dati di contatto non validi", "contacts_invalidAdvertFormat": "Dati di contatto non validi",
"appSettings_languageUk": "Ucraino", "appSettings_languageUk": "Ucraino",
"appSettings_enableMessageTracing": "Abilita tracciamento messaggi",
"appSettings_enableMessageTracingSubtitle": "Mostra metadati dettagliati su instradamento e tempi per i messaggi",
"contacts_zeroHopAdvert": "Annuncio Zero Hop", "contacts_zeroHopAdvert": "Annuncio Zero Hop",
"contacts_floodAdvert": "Annuncio alluvionale", "contacts_floodAdvert": "Annuncio alluvionale",
"contacts_copyAdvertToClipboard": "Copia Annuncio negli Appunti", "contacts_copyAdvertToClipboard": "Copia Annuncio negli Appunti",
+12
View File
@@ -970,6 +970,18 @@ abstract class AppLocalizations {
/// **'Українська'** /// **'Українська'**
String get appSettings_languageUk; String get appSettings_languageUk;
/// No description provided for @appSettings_enableMessageTracing.
///
/// In en, this message translates to:
/// **'Enable Message Tracing'**
String get appSettings_enableMessageTracing;
/// No description provided for @appSettings_enableMessageTracingSubtitle.
///
/// In en, this message translates to:
/// **'Show detailed routing and timing metadata for messages'**
String get appSettings_enableMessageTracingSubtitle;
/// No description provided for @appSettings_notifications. /// No description provided for @appSettings_notifications.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
+8
View File
@@ -466,6 +466,14 @@ class AppLocalizationsBg extends AppLocalizations {
@override @override
String get appSettings_languageUk => 'Украински'; String get appSettings_languageUk => 'Украински';
@override
String get appSettings_enableMessageTracing =>
'Разрешаване на проследяване на съобщения';
@override
String get appSettings_enableMessageTracingSubtitle =>
'Показване на подробни метаданни за маршрутизация и синхронизация за съобщения';
@override @override
String get appSettings_notifications => 'Уведомления'; String get appSettings_notifications => 'Уведомления';
+8
View File
@@ -460,6 +460,14 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get appSettings_languageUk => 'Ukrainisch'; String get appSettings_languageUk => 'Ukrainisch';
@override
String get appSettings_enableMessageTracing =>
'Nachrichtenverfolgung aktivieren';
@override
String get appSettings_enableMessageTracingSubtitle =>
'Detaillierte Routing- und Timing-Metadaten für Nachrichten anzeigen';
@override @override
String get appSettings_notifications => 'Benachrichtigungen'; String get appSettings_notifications => 'Benachrichtigungen';
+7
View File
@@ -458,6 +458,13 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get appSettings_languageUk => 'Українська'; String get appSettings_languageUk => 'Українська';
@override
String get appSettings_enableMessageTracing => 'Enable Message Tracing';
@override
String get appSettings_enableMessageTracingSubtitle =>
'Show detailed routing and timing metadata for messages';
@override @override
String get appSettings_notifications => 'Notifications'; String get appSettings_notifications => 'Notifications';
+8
View File
@@ -463,6 +463,14 @@ class AppLocalizationsEs extends AppLocalizations {
@override @override
String get appSettings_languageUk => 'Ucraniano'; String get appSettings_languageUk => 'Ucraniano';
@override
String get appSettings_enableMessageTracing =>
'Habilitar seguimiento de mensajes';
@override
String get appSettings_enableMessageTracingSubtitle =>
'Mostrar metadatos detallados de enrutamiento y tiempo para los mensajes';
@override @override
String get appSettings_notifications => 'Notificaciones'; String get appSettings_notifications => 'Notificaciones';
+11 -3
View File
@@ -464,6 +464,14 @@ class AppLocalizationsFr extends AppLocalizations {
@override @override
String get appSettings_languageUk => 'Ukrainien'; String get appSettings_languageUk => 'Ukrainien';
@override
String get appSettings_enableMessageTracing =>
'Activer le traçage des messages';
@override
String get appSettings_enableMessageTracingSubtitle =>
'Afficher les métadonnées détaillées de routage et de synchronisation des messages';
@override @override
String get appSettings_notifications => 'Notifications'; String get appSettings_notifications => 'Notifications';
@@ -696,7 +704,7 @@ class AppLocalizationsFr extends AppLocalizations {
String get contacts_manageRoom => 'Gérer le Room Server'; String get contacts_manageRoom => 'Gérer le Room Server';
@override @override
String get contacts_roomLogin => 'Connexion Salle'; String get contacts_roomLogin => 'Connexion Room Server';
@override @override
String get contacts_openChat => 'Ouverture du Chat'; String get contacts_openChat => 'Ouverture du Chat';
@@ -1559,7 +1567,7 @@ class AppLocalizationsFr extends AppLocalizations {
String get login_repeaterLogin => 'Connexion au répéteur'; String get login_repeaterLogin => 'Connexion au répéteur';
@override @override
String get login_roomLogin => 'Connexion Salle'; String get login_roomLogin => 'Connexion Room Server';
@override @override
String get login_password => 'Mot de passe'; String get login_password => 'Mot de passe';
@@ -1684,7 +1692,7 @@ class AppLocalizationsFr extends AppLocalizations {
String get repeater_management => 'Gestion des répéteurs'; String get repeater_management => 'Gestion des répéteurs';
@override @override
String get room_management => 'Administración del Servidor de Habitación'; String get room_management => 'Administrattion Room Server';
@override @override
String get repeater_managementTools => 'Outils de Gestion'; String get repeater_managementTools => 'Outils de Gestion';
+8
View File
@@ -462,6 +462,14 @@ class AppLocalizationsIt extends AppLocalizations {
@override @override
String get appSettings_languageUk => 'Ucraino'; String get appSettings_languageUk => 'Ucraino';
@override
String get appSettings_enableMessageTracing =>
'Abilita tracciamento messaggi';
@override
String get appSettings_enableMessageTracingSubtitle =>
'Mostra metadati dettagliati su instradamento e tempi per i messaggi';
@override @override
String get appSettings_notifications => 'Notifiche'; String get appSettings_notifications => 'Notifiche';
+7
View File
@@ -460,6 +460,13 @@ class AppLocalizationsNl extends AppLocalizations {
@override @override
String get appSettings_languageUk => 'Oekraïens'; String get appSettings_languageUk => 'Oekraïens';
@override
String get appSettings_enableMessageTracing => 'Berichttracking inschakelen';
@override
String get appSettings_enableMessageTracingSubtitle =>
'Gedetailleerde routerings- en timing-metadata voor berichten weergeven';
@override @override
String get appSettings_notifications => 'Notificaties'; String get appSettings_notifications => 'Notificaties';
+7
View File
@@ -464,6 +464,13 @@ class AppLocalizationsPl extends AppLocalizations {
@override @override
String get appSettings_languageUk => 'Ukraińska'; String get appSettings_languageUk => 'Ukraińska';
@override
String get appSettings_enableMessageTracing => 'Włącz śledzenie wiadomości';
@override
String get appSettings_enableMessageTracingSubtitle =>
'Pokaż szczegółowe metadane trasowania i czasu dla wiadomości';
@override @override
String get appSettings_notifications => 'Powiadomienia'; String get appSettings_notifications => 'Powiadomienia';
+8
View File
@@ -464,6 +464,14 @@ class AppLocalizationsPt extends AppLocalizations {
@override @override
String get appSettings_languageUk => 'Ucraniano'; String get appSettings_languageUk => 'Ucraniano';
@override
String get appSettings_enableMessageTracing =>
'Ativar rastreamento de mensagens';
@override
String get appSettings_enableMessageTracingSubtitle =>
'Mostrar metadados detalhados de roteamento e tempo para as mensagens';
@override @override
String get appSettings_notifications => 'Notificações'; String get appSettings_notifications => 'Notificações';
+8
View File
@@ -462,6 +462,14 @@ class AppLocalizationsRu extends AppLocalizations {
@override @override
String get appSettings_languageUk => 'Українська'; String get appSettings_languageUk => 'Українська';
@override
String get appSettings_enableMessageTracing =>
'Включить трассировку сообщений';
@override
String get appSettings_enableMessageTracingSubtitle =>
'Показывать подробные метаданные о маршрутизации и времени для сообщений';
@override @override
String get appSettings_notifications => 'Уведомления'; String get appSettings_notifications => 'Уведомления';
+7
View File
@@ -460,6 +460,13 @@ class AppLocalizationsSk extends AppLocalizations {
@override @override
String get appSettings_languageUk => 'Ukrajinská'; String get appSettings_languageUk => 'Ukrajinská';
@override
String get appSettings_enableMessageTracing => 'Povoliť sledovanie správ';
@override
String get appSettings_enableMessageTracingSubtitle =>
'Zobraziť podrobné metadáta o smerovaní a časovaní správ';
@override @override
String get appSettings_notifications => 'Upozornenia'; String get appSettings_notifications => 'Upozornenia';
+7
View File
@@ -459,6 +459,13 @@ class AppLocalizationsSl extends AppLocalizations {
@override @override
String get appSettings_languageUk => 'Ukrajinsko'; String get appSettings_languageUk => 'Ukrajinsko';
@override
String get appSettings_enableMessageTracing => 'Omogoči sledenje sporočilom';
@override
String get appSettings_enableMessageTracingSubtitle =>
'Prikaži podrobne metapodatke o usmerjanju in časovnem usklajevanju sporočil';
@override @override
String get appSettings_notifications => 'Obvestila'; String get appSettings_notifications => 'Obvestila';
+7
View File
@@ -457,6 +457,13 @@ class AppLocalizationsSv extends AppLocalizations {
@override @override
String get appSettings_languageUk => 'Ukrainska'; String get appSettings_languageUk => 'Ukrainska';
@override
String get appSettings_enableMessageTracing => 'Aktivera meddelandespårning';
@override
String get appSettings_enableMessageTracingSubtitle =>
'Visa detaljerade metadata om dirigering och tidsinställningar för meddelanden';
@override @override
String get appSettings_notifications => 'Meddelanden'; String get appSettings_notifications => 'Meddelanden';
+8
View File
@@ -462,6 +462,14 @@ class AppLocalizationsUk extends AppLocalizations {
@override @override
String get appSettings_languageUk => 'Українська'; String get appSettings_languageUk => 'Українська';
@override
String get appSettings_enableMessageTracing =>
'Увімкнути відстеження повідомлень';
@override
String get appSettings_enableMessageTracingSubtitle =>
'Показувати детальні метадані про маршрутизацію та час для повідомлень';
@override @override
String get appSettings_notifications => 'Сповіщення'; String get appSettings_notifications => 'Сповіщення';
+6
View File
@@ -446,6 +446,12 @@ class AppLocalizationsZh extends AppLocalizations {
@override @override
String get appSettings_languageUk => '乌克兰'; String get appSettings_languageUk => '乌克兰';
@override
String get appSettings_enableMessageTracing => '启用消息追踪';
@override
String get appSettings_enableMessageTracingSubtitle => '显示消息的详细路由和时间元数据';
@override @override
String get appSettings_notifications => '通知'; String get appSettings_notifications => '通知';
+2
View File
@@ -1557,6 +1557,8 @@
"contacts_floodAdvert": "Overstromingsadvertentie", "contacts_floodAdvert": "Overstromingsadvertentie",
"contacts_copyAdvertToClipboard": "Advert naar klembord kopiëren", "contacts_copyAdvertToClipboard": "Advert naar klembord kopiëren",
"appSettings_languageRu": "Russisch", "appSettings_languageRu": "Russisch",
"appSettings_enableMessageTracing": "Berichttracking inschakelen",
"appSettings_enableMessageTracingSubtitle": "Gedetailleerde routerings- en timing-metadata voor berichten weergeven",
"contacts_clipboardEmpty": "Knipbord is leeg.", "contacts_clipboardEmpty": "Knipbord is leeg.",
"contacts_addContactFromClipboard": "Contact uit klembord toevoegen", "contacts_addContactFromClipboard": "Contact uit klembord toevoegen",
"contacts_contactImported": "Contact is geïmporteerd.", "contacts_contactImported": "Contact is geïmporteerd.",
+2
View File
@@ -1552,6 +1552,8 @@
"contacts_chatTraceRoute": "Śledź trasę promienia", "contacts_chatTraceRoute": "Śledź trasę promienia",
"appSettings_languageRu": "Rosyjski", "appSettings_languageRu": "Rosyjski",
"appSettings_languageUk": "Ukraińska", "appSettings_languageUk": "Ukraińska",
"appSettings_enableMessageTracing": "Włącz śledzenie wiadomości",
"appSettings_enableMessageTracingSubtitle": "Pokaż szczegółowe metadane trasowania i czasu dla wiadomości",
"contacts_contactImportFailed": "Kontakt nie został zaimportowany.", "contacts_contactImportFailed": "Kontakt nie został zaimportowany.",
"contacts_zeroHopAdvert": "Reklama Zero Hop", "contacts_zeroHopAdvert": "Reklama Zero Hop",
"contacts_floodAdvert": "Reklama powodziowa", "contacts_floodAdvert": "Reklama powodziowa",
+2
View File
@@ -1558,6 +1558,8 @@
"contacts_copyAdvertToClipboard": "Copiar Anúncio para Área de Transferência", "contacts_copyAdvertToClipboard": "Copiar Anúncio para Área de Transferência",
"contacts_addContactFromClipboard": "Adicionar Contato da Área de Transferência", "contacts_addContactFromClipboard": "Adicionar Contato da Área de Transferência",
"appSettings_languageRu": "Russo", "appSettings_languageRu": "Russo",
"appSettings_enableMessageTracing": "Ativar rastreamento de mensagens",
"appSettings_enableMessageTracingSubtitle": "Mostrar metadados detalhados de roteamento e tempo para as mensagens",
"contacts_ShareContact": "Copiar contato para Área de Transferência", "contacts_ShareContact": "Copiar contato para Área de Transferência",
"contacts_contactImportFailed": "Contato falhou ao ser importado.", "contacts_contactImportFailed": "Contato falhou ao ser importado.",
"contacts_zeroHopContactAdvertSent": "Enviou contato por anúncio.", "contacts_zeroHopContactAdvertSent": "Enviou contato por anúncio.",
+2
View File
@@ -796,6 +796,8 @@
"contacts_invalidAdvertFormat": "Недействительные контактные данные", "contacts_invalidAdvertFormat": "Недействительные контактные данные",
"contacts_zeroHopAdvert": "Реклама Zero Hop", "contacts_zeroHopAdvert": "Реклама Zero Hop",
"appSettings_languageUk": "Українська", "appSettings_languageUk": "Українська",
"appSettings_enableMessageTracing": "Включить трассировку сообщений",
"appSettings_enableMessageTracingSubtitle": "Показывать подробные метаданные о маршрутизации и времени для сообщений",
"contacts_floodAdvert": "Рекламный поток", "contacts_floodAdvert": "Рекламный поток",
"contacts_clipboardEmpty": "Буфер обмена пуст.", "contacts_clipboardEmpty": "Буфер обмена пуст.",
"contacts_copyAdvertToClipboard": "Копировать рекламу в буфер обмена", "contacts_copyAdvertToClipboard": "Копировать рекламу в буфер обмена",
+2
View File
@@ -1558,6 +1558,8 @@
"contacts_copyAdvertToClipboard": "Kopírovať reklamu do schránky", "contacts_copyAdvertToClipboard": "Kopírovať reklamu do schránky",
"contacts_invalidAdvertFormat": "Neplatné kontaktné údaje", "contacts_invalidAdvertFormat": "Neplatné kontaktné údaje",
"appSettings_languageRu": "Ruština", "appSettings_languageRu": "Ruština",
"appSettings_enableMessageTracing": "Povoliť sledovanie správ",
"appSettings_enableMessageTracingSubtitle": "Zobraziť podrobné metadáta o smerovaní a časovaní správ",
"contacts_addContactFromClipboard": "Pridať kontakt z schránky", "contacts_addContactFromClipboard": "Pridať kontakt z schránky",
"contacts_contactImported": "Kontakt bol importovaný.", "contacts_contactImported": "Kontakt bol importovaný.",
"contacts_zeroHopContactAdvertSent": "Poslal kontakt cez inzerát.", "contacts_zeroHopContactAdvertSent": "Poslal kontakt cez inzerát.",
+2
View File
@@ -1552,6 +1552,8 @@
"contacts_pathTraceTo": "Trace route to {name}", "contacts_pathTraceTo": "Trace route to {name}",
"appSettings_languageRu": "Ruščina", "appSettings_languageRu": "Ruščina",
"appSettings_languageUk": "Ukrajinsko", "appSettings_languageUk": "Ukrajinsko",
"appSettings_enableMessageTracing": "Omogoči sledenje sporočilom",
"appSettings_enableMessageTracingSubtitle": "Prikaži podrobne metapodatke o usmerjanju in časovnem usklajevanju sporočil",
"contacts_contactImported": "Kontakt je bil uvožen.", "contacts_contactImported": "Kontakt je bil uvožen.",
"contacts_contactImportFailed": "Kontakt ni bil uspešno uvožen.", "contacts_contactImportFailed": "Kontakt ni bil uspešno uvožen.",
"contacts_zeroHopAdvert": "Reklama brez posrednikov", "contacts_zeroHopAdvert": "Reklama brez posrednikov",
+2
View File
@@ -1558,6 +1558,8 @@
"contacts_copyAdvertToClipboard": "Kopiera annons till urklipp", "contacts_copyAdvertToClipboard": "Kopiera annons till urklipp",
"contacts_invalidAdvertFormat": "Ogiltiga kontaktuppgifter", "contacts_invalidAdvertFormat": "Ogiltiga kontaktuppgifter",
"appSettings_languageUk": "Ukrainska", "appSettings_languageUk": "Ukrainska",
"appSettings_enableMessageTracing": "Aktivera meddelandespårning",
"appSettings_enableMessageTracingSubtitle": "Visa detaljerade metadata om dirigering och tidsinställningar för meddelanden",
"contacts_addContactFromClipboard": "Lägg till kontakt från urklipp", "contacts_addContactFromClipboard": "Lägg till kontakt från urklipp",
"contacts_contactImported": "Kontakt har importerats.", "contacts_contactImported": "Kontakt har importerats.",
"contacts_zeroHopContactAdvertSent": "Skickat kontakt via annons.", "contacts_zeroHopContactAdvertSent": "Skickat kontakt via annons.",
+2
View File
@@ -1559,6 +1559,8 @@
"contacts_copyAdvertToClipboard": "Копіювати оголошення в буфер обміну", "contacts_copyAdvertToClipboard": "Копіювати оголошення в буфер обміну",
"contacts_clipboardEmpty": "Буфер обміну порожній", "contacts_clipboardEmpty": "Буфер обміну порожній",
"appSettings_languageRu": "Російська", "appSettings_languageRu": "Російська",
"appSettings_enableMessageTracing": "Увімкнути відстеження повідомлень",
"appSettings_enableMessageTracingSubtitle": "Показувати детальні метадані про маршрутизацію та час для повідомлень",
"contacts_ShareContact": "Копіювати контакт у буфер обміну", "contacts_ShareContact": "Копіювати контакт у буфер обміну",
"contacts_zeroHopContactAdvertFailed": "Не вдалося надіслати контакт.", "contacts_zeroHopContactAdvertFailed": "Не вдалося надіслати контакт.",
"contacts_contactAdvertCopied": "Рекламу скопійовано до буфера обміну.", "contacts_contactAdvertCopied": "Рекламу скопійовано до буфера обміну.",
+2
View File
@@ -176,6 +176,8 @@
"appSettings_languageBg": "保加利亚", "appSettings_languageBg": "保加利亚",
"appSettings_languageRu": "俄语", "appSettings_languageRu": "俄语",
"appSettings_languageUk": "乌克兰", "appSettings_languageUk": "乌克兰",
"appSettings_enableMessageTracing": "启用消息追踪",
"appSettings_enableMessageTracingSubtitle": "显示消息的详细路由和时间元数据",
"appSettings_notifications": "通知", "appSettings_notifications": "通知",
"appSettings_enableNotifications": "启用通知", "appSettings_enableNotifications": "启用通知",
"appSettings_enableNotificationsSubtitle": "接收消息和广告的通知", "appSettings_enableNotificationsSubtitle": "接收消息和广告的通知",
+6
View File
@@ -22,6 +22,7 @@ class AppSettings {
final bool mapKeyPrefixEnabled; final bool mapKeyPrefixEnabled;
final String mapKeyPrefix; final String mapKeyPrefix;
final bool mapShowMarkers; final bool mapShowMarkers;
final bool enableMessageTracing;
final Map<String, double>? mapCacheBounds; final Map<String, double>? mapCacheBounds;
final int mapCacheMinZoom; final int mapCacheMinZoom;
final int mapCacheMaxZoom; final int mapCacheMaxZoom;
@@ -47,6 +48,7 @@ class AppSettings {
this.mapKeyPrefixEnabled = false, this.mapKeyPrefixEnabled = false,
this.mapKeyPrefix = '', this.mapKeyPrefix = '',
this.mapShowMarkers = true, this.mapShowMarkers = true,
this.enableMessageTracing = false,
this.mapCacheBounds, this.mapCacheBounds,
this.mapCacheMinZoom = 10, this.mapCacheMinZoom = 10,
this.mapCacheMaxZoom = 15, this.mapCacheMaxZoom = 15,
@@ -76,6 +78,7 @@ class AppSettings {
'map_key_prefix_enabled': mapKeyPrefixEnabled, 'map_key_prefix_enabled': mapKeyPrefixEnabled,
'map_key_prefix': mapKeyPrefix, 'map_key_prefix': mapKeyPrefix,
'map_show_markers': mapShowMarkers, 'map_show_markers': mapShowMarkers,
'enable_message_tracing': enableMessageTracing,
'map_cache_bounds': mapCacheBounds, 'map_cache_bounds': mapCacheBounds,
'map_cache_min_zoom': mapCacheMinZoom, 'map_cache_min_zoom': mapCacheMinZoom,
'map_cache_max_zoom': mapCacheMaxZoom, 'map_cache_max_zoom': mapCacheMaxZoom,
@@ -112,6 +115,7 @@ class AppSettings {
mapKeyPrefixEnabled: json['map_key_prefix_enabled'] as bool? ?? false, mapKeyPrefixEnabled: json['map_key_prefix_enabled'] as bool? ?? false,
mapKeyPrefix: json['map_key_prefix'] as String? ?? '', mapKeyPrefix: json['map_key_prefix'] as String? ?? '',
mapShowMarkers: json['map_show_markers'] as bool? ?? true, mapShowMarkers: json['map_show_markers'] as bool? ?? true,
enableMessageTracing: json['enable_message_tracing'] as bool? ?? false,
mapCacheBounds: (json['map_cache_bounds'] as Map?)?.map( mapCacheBounds: (json['map_cache_bounds'] as Map?)?.map(
(key, value) => MapEntry(key.toString(), (value as num).toDouble()), (key, value) => MapEntry(key.toString(), (value as num).toDouble()),
), ),
@@ -155,6 +159,7 @@ class AppSettings {
bool? mapKeyPrefixEnabled, bool? mapKeyPrefixEnabled,
String? mapKeyPrefix, String? mapKeyPrefix,
bool? mapShowMarkers, bool? mapShowMarkers,
bool? enableMessageTracing,
Object? mapCacheBounds = _unset, Object? mapCacheBounds = _unset,
int? mapCacheMinZoom, int? mapCacheMinZoom,
int? mapCacheMaxZoom, int? mapCacheMaxZoom,
@@ -180,6 +185,7 @@ class AppSettings {
mapKeyPrefixEnabled: mapKeyPrefixEnabled ?? this.mapKeyPrefixEnabled, mapKeyPrefixEnabled: mapKeyPrefixEnabled ?? this.mapKeyPrefixEnabled,
mapKeyPrefix: mapKeyPrefix ?? this.mapKeyPrefix, mapKeyPrefix: mapKeyPrefix ?? this.mapKeyPrefix,
mapShowMarkers: mapShowMarkers ?? this.mapShowMarkers, mapShowMarkers: mapShowMarkers ?? this.mapShowMarkers,
enableMessageTracing: enableMessageTracing ?? this.enableMessageTracing,
mapCacheBounds: mapCacheBounds == _unset mapCacheBounds: mapCacheBounds == _unset
? this.mapCacheBounds ? this.mapCacheBounds
: mapCacheBounds as Map<String, double>?, : mapCacheBounds as Map<String, double>?,
+12
View File
@@ -82,6 +82,18 @@ class AppSettingsScreen extends StatelessWidget {
trailing: const Icon(Icons.chevron_right), trailing: const Icon(Icons.chevron_right),
onTap: () => _showLanguageDialog(context, settingsService), onTap: () => _showLanguageDialog(context, settingsService),
), ),
const Divider(height: 1),
SwitchListTile(
secondary: const Icon(Icons.location_searching),
title: Text(context.l10n.appSettings_enableMessageTracing),
subtitle: Text(
context.l10n.appSettings_enableMessageTracingSubtitle,
),
value: settingsService.settings.enableMessageTracing,
onChanged: (value) {
settingsService.setEnableMessageTracing(value);
},
),
], ],
), ),
); );
+176 -87
View File
@@ -17,11 +17,13 @@ import '../helpers/utf8_length_limiter.dart';
import '../l10n/l10n.dart'; import '../l10n/l10n.dart';
import '../models/channel.dart'; import '../models/channel.dart';
import '../models/channel_message.dart'; import '../models/channel_message.dart';
import '../services/app_settings_service.dart';
import '../utils/emoji_utils.dart'; import '../utils/emoji_utils.dart';
import '../widgets/emoji_picker.dart'; import '../widgets/emoji_picker.dart';
import '../widgets/gif_message.dart'; import '../widgets/gif_message.dart';
import '../widgets/jump_to_bottom_button.dart'; import '../widgets/jump_to_bottom_button.dart';
import '../widgets/gif_picker.dart'; import '../widgets/gif_picker.dart';
import '../widgets/message_status_icon.dart';
import 'channel_message_path_screen.dart'; import 'channel_message_path_screen.dart';
import 'map_screen.dart'; import 'map_screen.dart';
@@ -263,6 +265,8 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
} }
Widget _buildMessageBubble(ChannelMessage message) { Widget _buildMessageBubble(ChannelMessage message) {
final settingsService = context.watch<AppSettingsService>();
final enableTracing = settingsService.settings.enableMessageTracing;
final isOutgoing = message.isOutgoing; final isOutgoing = message.isOutgoing;
final gifId = _parseGifId(message.text); final gifId = _parseGifId(message.text);
final poi = _parsePoiMessage(message.text); final poi = _parsePoiMessage(message.text);
@@ -334,110 +338,189 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
const SizedBox(height: 8), const SizedBox(height: 8),
], ],
if (poi != null) if (poi != null)
_buildPoiMessage(context, poi, isOutgoing) _buildPoiMessage(
context,
poi,
isOutgoing,
trailing: (!enableTracing && isOutgoing)
? Padding(
padding: const EdgeInsets.only(bottom: 2),
child: MessageStatusIcon(
isAcked:
message.status ==
ChannelMessageStatus.sent &&
displayPath.isNotEmpty,
isFailed:
message.status ==
ChannelMessageStatus.failed,
),
)
: null,
)
else if (gifId != null) else if (gifId != null)
ClipRRect( Stack(
borderRadius: BorderRadius.circular(8), children: [
child: GifMessage( ClipRRect(
url: borderRadius: BorderRadius.circular(8),
'https://media.giphy.com/media/$gifId/giphy.gif', child: GifMessage(
backgroundColor: Colors.transparent, url:
fallbackTextColor: isOutgoing 'https://media.giphy.com/media/$gifId/giphy.gif',
? Theme.of(context) backgroundColor: Colors.transparent,
.colorScheme fallbackTextColor: isOutgoing
.onPrimaryContainer ? Theme.of(context)
.withValues(alpha: 0.7) .colorScheme
: Theme.of(context).colorScheme.onSurface .onPrimaryContainer
.withValues(alpha: 0.6), .withValues(alpha: 0.7)
), : Theme.of(context).colorScheme.onSurface
.withValues(alpha: 0.6),
),
),
if (!enableTracing && isOutgoing)
Positioned(
top: 0,
right: 0,
child: Container(
padding: const EdgeInsets.all(3),
decoration: BoxDecoration(
color: isOutgoing
? Theme.of(
context,
).colorScheme.primaryContainer
: Theme.of(
context,
).colorScheme.surfaceContainerHighest,
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(10),
topRight: Radius.circular(8),
),
),
child: MessageStatusIcon(
isAcked:
message.status ==
ChannelMessageStatus.sent &&
displayPath.isNotEmpty,
isFailed:
message.status ==
ChannelMessageStatus.failed,
),
),
),
],
) )
else else
Linkify( Row(
text: message.text,
style: const TextStyle(fontSize: 14),
linkStyle: const TextStyle(
fontSize: 14,
color: Colors.green,
decoration: TextDecoration.underline,
),
options: const LinkifyOptions(
humanize: false,
defaultToHttps: false,
),
linkifiers: const [UrlLinkifier()],
onOpen: (link) =>
LinkHandler.handleLinkTap(context, link.url),
),
if (displayPath.isNotEmpty) ...[
const SizedBox(height: 4),
Padding(
padding: gifId != null
? const EdgeInsets.symmetric(horizontal: 8)
: EdgeInsets.zero,
child: Text(
'via ${_formatPathPrefixes(displayPath)}',
style: TextStyle(
fontSize: 11,
color: Colors.grey[600],
),
),
),
],
const SizedBox(height: 4),
Padding(
padding: gifId != null
? const EdgeInsets.only(
left: 8,
right: 8,
bottom: 4,
)
: EdgeInsets.zero,
child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
Text( Flexible(
_formatTime(message.timestamp), child: Linkify(
text: message.text,
style: const TextStyle(fontSize: 14),
linkStyle: const TextStyle(
fontSize: 14,
color: Colors.green,
decoration: TextDecoration.underline,
),
options: const LinkifyOptions(
humanize: false,
defaultToHttps: false,
),
linkifiers: const [UrlLinkifier()],
onOpen: (link) => LinkHandler.handleLinkTap(
context,
link.url,
),
),
),
if (!enableTracing && isOutgoing) ...[
const SizedBox(width: 4),
Padding(
padding: const EdgeInsets.only(bottom: 2),
child: MessageStatusIcon(
isAcked:
message.status ==
ChannelMessageStatus.sent &&
displayPath.isNotEmpty,
isFailed:
message.status ==
ChannelMessageStatus.failed,
),
),
],
],
),
if (enableTracing) ...[
if (displayPath.isNotEmpty) ...[
const SizedBox(height: 4),
Padding(
padding: gifId != null
? const EdgeInsets.symmetric(horizontal: 8)
: EdgeInsets.zero,
child: Text(
'via ${_formatPathPrefixes(displayPath)}',
style: TextStyle( style: TextStyle(
fontSize: 11, fontSize: 11,
color: Colors.grey[600], color: Colors.grey[600],
), ),
), ),
if (message.repeatCount > 0) ...[ ),
const SizedBox(width: 6), ],
Icon( const SizedBox(height: 4),
Icons.repeat, Padding(
size: 12, padding: gifId != null
color: Colors.grey[600], ? const EdgeInsets.only(
), left: 8,
const SizedBox(width: 2), right: 8,
bottom: 4,
)
: EdgeInsets.zero,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text( Text(
'${message.repeatCount}', _formatTime(message.timestamp),
style: TextStyle( style: TextStyle(
fontSize: 11, fontSize: 11,
color: Colors.grey[600], color: Colors.grey[600],
), ),
), ),
if (message.repeatCount > 0) ...[
const SizedBox(width: 6),
Icon(
Icons.repeat,
size: 12,
color: Colors.grey[600],
),
const SizedBox(width: 2),
Text(
'${message.repeatCount}',
style: TextStyle(
fontSize: 11,
color: Colors.grey[600],
),
),
],
if (isOutgoing) ...[
const SizedBox(width: 4),
Icon(
message.status == ChannelMessageStatus.sent
? Icons.check
: message.status ==
ChannelMessageStatus.pending
? Icons.schedule
: Icons.error_outline,
size: 14,
color:
message.status ==
ChannelMessageStatus.failed
? Colors.red
: Colors.grey[600],
),
],
], ],
if (isOutgoing) ...[ ),
const SizedBox(width: 4),
Icon(
message.status == ChannelMessageStatus.sent
? Icons.check
: message.status ==
ChannelMessageStatus.pending
? Icons.schedule
: Icons.error_outline,
size: 14,
color:
message.status ==
ChannelMessageStatus.failed
? Colors.red
: Colors.grey[600],
),
],
],
), ),
), ],
], ],
), ),
), ),
@@ -650,7 +733,12 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
return _PoiInfo(lat: lat, lon: lon, label: label); return _PoiInfo(lat: lat, lon: lon, label: label);
} }
Widget _buildPoiMessage(BuildContext context, _PoiInfo poi, bool isOutgoing) { Widget _buildPoiMessage(
BuildContext context,
_PoiInfo poi,
bool isOutgoing, {
Widget? trailing,
}) {
final colorScheme = Theme.of(context).colorScheme; final colorScheme = Theme.of(context).colorScheme;
final textColor = isOutgoing final textColor = isOutgoing
? colorScheme.onPrimaryContainer ? colorScheme.onPrimaryContainer
@@ -696,6 +784,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
], ],
), ),
), ),
if (trailing != null) ...[const SizedBox(width: 4), trailing],
], ],
); );
} }
+20 -2
View File
@@ -3,6 +3,7 @@ import 'dart:math';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:meshcore_open/storage/channel_message_store.dart';
import 'package:meshcore_open/widgets/app_bar.dart'; import 'package:meshcore_open/widgets/app_bar.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
@@ -105,6 +106,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final connector = context.watch<MeshCoreConnector>(); final connector = context.watch<MeshCoreConnector>();
final channelMessageStore = ChannelMessageStore();
// Auto-navigate back to scanner if disconnected // Auto-navigate back to scanner if disconnected
if (!checkConnectionAndNavigate(connector)) { if (!checkConnectionAndNavigate(connector)) {
@@ -304,6 +306,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
return _buildChannelTile( return _buildChannelTile(
context, context,
connector, connector,
channelMessageStore,
channel, channel,
showDragHandle: true, showDragHandle: true,
dragIndex: index, dragIndex: index,
@@ -323,6 +326,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
return _buildChannelTile( return _buildChannelTile(
context, context,
connector, connector,
channelMessageStore,
channel, channel,
); );
}, },
@@ -352,6 +356,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
Widget _buildChannelTile( Widget _buildChannelTile(
BuildContext context, BuildContext context,
MeshCoreConnector connector, MeshCoreConnector connector,
ChannelMessageStore channelMessageStore,
Channel channel, { Channel channel, {
bool showDragHandle = false, bool showDragHandle = false,
int? dragIndex, int? dragIndex,
@@ -468,7 +473,12 @@ class _ChannelsScreenState extends State<ChannelsScreen>
); );
} }
}, },
onLongPress: () => _showChannelActions(context, connector, channel), onLongPress: () => _showChannelActions(
context,
connector,
channelMessageStore,
channel,
),
), ),
); );
} }
@@ -476,6 +486,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
void _showChannelActions( void _showChannelActions(
BuildContext context, BuildContext context,
MeshCoreConnector connector, MeshCoreConnector connector,
ChannelMessageStore channelMessageStore,
Channel channel, Channel channel,
) { ) {
final settingsService = context.read<AppSettingsService>(); final settingsService = context.read<AppSettingsService>();
@@ -528,7 +539,12 @@ class _ChannelsScreenState extends State<ChannelsScreen>
Navigator.pop(context); Navigator.pop(context);
await Future.delayed(const Duration(milliseconds: 100)); await Future.delayed(const Duration(milliseconds: 100));
if (context.mounted) { if (context.mounted) {
_confirmDeleteChannel(context, connector, channel); _confirmDeleteChannel(
context,
connector,
channelMessageStore,
channel,
);
} }
}, },
), ),
@@ -1474,6 +1490,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
void _confirmDeleteChannel( void _confirmDeleteChannel(
BuildContext context, BuildContext context,
MeshCoreConnector connector, MeshCoreConnector connector,
ChannelMessageStore channelMessageStore,
Channel channel, Channel channel,
) { ) {
showDialog( showDialog(
@@ -1492,6 +1509,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
onPressed: () { onPressed: () {
Navigator.pop(dialogContext); Navigator.pop(dialogContext);
connector.deleteChannel(channel.index); connector.deleteChannel(channel.index);
channelMessageStore.clearChannelMessages(channel.index);
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text( content: Text(
+164 -85
View File
@@ -13,6 +13,7 @@ import 'package:latlong2/latlong.dart';
import '../connector/meshcore_connector.dart'; import '../connector/meshcore_connector.dart';
import '../connector/meshcore_protocol.dart'; import '../connector/meshcore_protocol.dart';
import '../helpers/reaction_helper.dart'; import '../helpers/reaction_helper.dart';
import '../widgets/message_status_icon.dart';
import '../helpers/chat_scroll_controller.dart'; import '../helpers/chat_scroll_controller.dart';
import '../helpers/link_handler.dart'; import '../helpers/link_handler.dart';
import '../helpers/utf8_length_limiter.dart'; import '../helpers/utf8_length_limiter.dart';
@@ -20,6 +21,7 @@ import '../models/channel_message.dart';
import '../models/contact.dart'; import '../models/contact.dart';
import '../models/message.dart'; import '../models/message.dart';
import '../models/path_history.dart'; import '../models/path_history.dart';
import '../services/app_settings_service.dart';
import '../services/path_history_service.dart'; import '../services/path_history_service.dart';
import '../widgets/elements_ui.dart'; import '../widgets/elements_ui.dart';
import 'channel_message_path_screen.dart'; import 'channel_message_path_screen.dart';
@@ -1172,6 +1174,8 @@ class _MessageBubble extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final settingsService = context.watch<AppSettingsService>();
final enableTracing = settingsService.settings.enableMessageTracing;
final isOutgoing = message.isOutgoing; final isOutgoing = message.isOutgoing;
final colorScheme = Theme.of(context).colorScheme; final colorScheme = Theme.of(context).colorScheme;
final gifId = _parseGifId(message.text); final gifId = _parseGifId(message.text);
@@ -1249,102 +1253,175 @@ class _MessageBubble extends StatelessWidget {
if (gifId == null) const SizedBox(height: 4), if (gifId == null) const SizedBox(height: 4),
], ],
if (poi != null) if (poi != null)
_buildPoiMessage(context, poi, textColor, metaColor) _buildPoiMessage(
context,
poi,
textColor,
metaColor,
trailing: (!enableTracing && isOutgoing)
? Padding(
padding: const EdgeInsets.only(bottom: 2),
child: MessageStatusIcon(
isAcked:
message.status ==
MessageStatus.delivered &&
message.pathBytes.isNotEmpty,
isFailed:
message.status ==
MessageStatus.failed,
),
)
: null,
)
else if (gifId != null) else if (gifId != null)
ClipRRect( Stack(
borderRadius: BorderRadius.circular(12), children: [
child: GifMessage( ClipRRect(
url: borderRadius: BorderRadius.circular(12),
'https://media.giphy.com/media/$gifId/giphy.gif', child: GifMessage(
backgroundColor: Colors.transparent, url:
fallbackTextColor: textColor.withValues( 'https://media.giphy.com/media/$gifId/giphy.gif',
alpha: 0.7, backgroundColor: Colors.transparent,
fallbackTextColor: textColor.withValues(
alpha: 0.7,
),
),
), ),
), if (!enableTracing && isOutgoing)
Positioned(
top: 0,
right: 0,
child: Container(
padding: const EdgeInsets.all(3),
decoration: BoxDecoration(
color: bubbleColor,
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(10),
topRight: Radius.circular(12),
),
),
child: MessageStatusIcon(
isAcked:
message.status ==
MessageStatus.delivered &&
message.pathBytes.isNotEmpty,
isFailed:
message.status ==
MessageStatus.failed,
),
),
),
],
) )
else else
Linkify( Row(
text: messageText, mainAxisSize: MainAxisSize.min,
style: TextStyle(color: textColor), crossAxisAlignment: CrossAxisAlignment.end,
linkStyle: const TextStyle(
color: Colors.green,
decoration: TextDecoration.underline,
),
options: const LinkifyOptions(
humanize: false,
defaultToHttps: false,
),
linkifiers: const [UrlLinkifier()],
onOpen: (link) =>
LinkHandler.handleLinkTap(context, link.url),
),
if (isOutgoing && message.retryCount > 0) ...[
const SizedBox(height: 4),
Padding(
padding: gifId != null
? const EdgeInsets.symmetric(horizontal: 8)
: EdgeInsets.zero,
child: Text(
context.l10n.chat_retryCount(
message.retryCount,
4,
),
style: TextStyle(
fontSize: 10,
color: metaColor,
fontWeight: FontWeight.w500,
),
),
),
],
const SizedBox(height: 4),
Padding(
padding: gifId != null
? const EdgeInsets.only(
left: 8,
right: 8,
bottom: 4,
)
: EdgeInsets.zero,
child: Wrap(
spacing: 4,
crossAxisAlignment: WrapCrossAlignment.center,
children: [ children: [
Text( Flexible(
_formatTime(message.timestamp), child: Linkify(
style: TextStyle( text: messageText,
fontSize: 10, style: TextStyle(color: textColor),
color: metaColor, linkStyle: const TextStyle(
color: Colors.green,
decoration: TextDecoration.underline,
),
options: const LinkifyOptions(
humanize: false,
defaultToHttps: false,
),
linkifiers: const [UrlLinkifier()],
onOpen: (link) => LinkHandler.handleLinkTap(
context,
link.url,
),
), ),
), ),
if (isOutgoing) ...[ if (!enableTracing && isOutgoing) ...[
const SizedBox(width: 4), const SizedBox(width: 4),
_buildStatusIcon(metaColor), Padding(
], padding: const EdgeInsets.only(bottom: 2),
if (message.tripTimeMs != null && child: MessageStatusIcon(
message.status == isAcked:
MessageStatus.delivered) ...[ message.status ==
const SizedBox(width: 4), MessageStatus.delivered &&
Icon( message.pathBytes.isNotEmpty,
Icons.speed, isFailed:
size: 10, message.status == MessageStatus.failed,
color: isOutgoing
? metaColor
: Colors.green[700],
),
Text(
'${(message.tripTimeMs! / 1000).toStringAsFixed(1)}s',
style: TextStyle(
fontSize: 9,
color: isOutgoing
? metaColor
: Colors.green[700],
), ),
), ),
], ],
], ],
), ),
), if (enableTracing) ...[
if (isOutgoing && message.retryCount > 0) ...[
const SizedBox(height: 4),
Padding(
padding: gifId != null
? const EdgeInsets.symmetric(horizontal: 8)
: EdgeInsets.zero,
child: Text(
context.l10n.chat_retryCount(
message.retryCount,
4,
),
style: TextStyle(
fontSize: 10,
color: metaColor,
fontWeight: FontWeight.w500,
),
),
),
],
const SizedBox(height: 4),
Padding(
padding: gifId != null
? const EdgeInsets.only(
left: 8,
right: 8,
bottom: 4,
)
: EdgeInsets.zero,
child: Wrap(
spacing: 4,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Text(
_formatTime(message.timestamp),
style: TextStyle(
fontSize: 10,
color: metaColor,
),
),
if (isOutgoing) ...[
const SizedBox(width: 4),
_buildStatusIcon(metaColor),
],
if (message.tripTimeMs != null &&
message.status ==
MessageStatus.delivered) ...[
const SizedBox(width: 4),
Icon(
Icons.speed,
size: 10,
color: isOutgoing
? metaColor
: Colors.green[700],
),
Text(
'${(message.tripTimeMs! / 1000).toStringAsFixed(1)}s',
style: TextStyle(
fontSize: 9,
color: isOutgoing
? metaColor
: Colors.green[700],
),
),
],
],
),
),
],
], ],
), ),
), ),
@@ -1387,8 +1464,9 @@ class _MessageBubble extends StatelessWidget {
BuildContext context, BuildContext context,
_PoiInfo poi, _PoiInfo poi,
Color textColor, Color textColor,
Color metaColor, Color metaColor, {
) { Widget? trailing,
}) {
return Row( return Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
@@ -1425,6 +1503,7 @@ class _MessageBubble extends StatelessWidget {
], ],
), ),
), ),
if (trailing != null) ...[const SizedBox(width: 4), trailing],
], ],
); );
} }
+4
View File
@@ -80,6 +80,10 @@ class AppSettingsService extends ChangeNotifier {
await updateSettings(_settings.copyWith(mapShowMarkers: value)); await updateSettings(_settings.copyWith(mapShowMarkers: value));
} }
Future<void> setEnableMessageTracing(bool value) async {
await updateSettings(_settings.copyWith(enableMessageTracing: value));
}
Future<void> setMapCacheBounds(Map<String, double>? value) async { Future<void> setMapCacheBounds(Map<String, double>? value) async {
await updateSettings(_settings.copyWith(mapCacheBounds: value)); await updateSettings(_settings.copyWith(mapCacheBounds: value));
} }
+36
View File
@@ -0,0 +1,36 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
class MessageStatusIcon extends StatelessWidget {
final bool isAcked;
final bool isFailed;
final double size;
const MessageStatusIcon({
super.key,
required this.isAcked,
this.isFailed = false,
this.size = 14,
});
@override
Widget build(BuildContext context) {
if (isFailed) {
return Icon(Icons.cancel, size: size, color: Colors.red);
}
final Color color;
if (isAcked) {
color = Colors.green;
} else {
color = Colors.grey;
}
return SvgPicture.asset(
'assets/icons/done_all.svg',
width: size,
height: size,
colorFilter: ColorFilter.mode(color, BlendMode.srcIn),
);
}
}
+12 -5
View File
@@ -5,10 +5,13 @@ PODS:
- flutter_local_notifications (0.0.1): - flutter_local_notifications (0.0.1):
- FlutterMacOS - FlutterMacOS
- FlutterMacOS (1.0.0) - FlutterMacOS (1.0.0)
- mobile_scanner (6.0.2): - mobile_scanner (7.0.0):
- Flutter
- FlutterMacOS - FlutterMacOS
- package_info_plus (0.0.1): - package_info_plus (0.0.1):
- FlutterMacOS - FlutterMacOS
- share_plus (0.0.1):
- FlutterMacOS
- shared_preferences_foundation (0.0.1): - shared_preferences_foundation (0.0.1):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
@@ -24,8 +27,9 @@ DEPENDENCIES:
- flutter_blue_plus_darwin (from `Flutter/ephemeral/.symlinks/plugins/flutter_blue_plus_darwin/darwin`) - flutter_blue_plus_darwin (from `Flutter/ephemeral/.symlinks/plugins/flutter_blue_plus_darwin/darwin`)
- flutter_local_notifications (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos`) - flutter_local_notifications (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos`)
- FlutterMacOS (from `Flutter/ephemeral`) - FlutterMacOS (from `Flutter/ephemeral`)
- mobile_scanner (from `Flutter/ephemeral/.symlinks/plugins/mobile_scanner/macos`) - mobile_scanner (from `Flutter/ephemeral/.symlinks/plugins/mobile_scanner/darwin`)
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
- share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`)
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite_darwin (from `Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin`) - sqflite_darwin (from `Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin`)
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
@@ -39,9 +43,11 @@ EXTERNAL SOURCES:
FlutterMacOS: FlutterMacOS:
:path: Flutter/ephemeral :path: Flutter/ephemeral
mobile_scanner: mobile_scanner:
:path: Flutter/ephemeral/.symlinks/plugins/mobile_scanner/macos :path: Flutter/ephemeral/.symlinks/plugins/mobile_scanner/darwin
package_info_plus: package_info_plus:
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
share_plus:
:path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos
shared_preferences_foundation: shared_preferences_foundation:
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
sqflite_darwin: sqflite_darwin:
@@ -53,10 +59,11 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
flutter_blue_plus_darwin: 20a08bfeaa0f7804d524858d3d8744bcc1b6dbc3 flutter_blue_plus_darwin: 20a08bfeaa0f7804d524858d3d8744bcc1b6dbc3
flutter_local_notifications: 13862b132e32eb858dea558a86d45d08daeacfe7 flutter_local_notifications: 4bf37a31afde695b56091b4ae3e4d9c7a7e6cda0
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1 FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
mobile_scanner: 0e365ed56cad24f28c0fd858ca04edefb40dfac3 mobile_scanner: 9157936403f5a0644ca3779a38ff8404c5434a93
package_info_plus: f0052d280d17aa382b932f399edf32507174e870 package_info_plus: f0052d280d17aa382b932f399edf32507174e870
share_plus: 510bf0af1a42cd602274b4629920c9649c52f4cc
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
url_launcher_macos: f87a979182d112f911de6820aefddaf56ee9fbfd url_launcher_macos: f87a979182d112f911de6820aefddaf56ee9fbfd
+2 -2
View File
@@ -513,10 +513,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.17.0" version: "1.18.0"
mgrs_dart: mgrs_dart:
dependency: transitive dependency: transitive
description: description:
+4 -1
View File
@@ -50,7 +50,7 @@ dependencies:
cached_network_image: ^3.4.1 cached_network_image: ^3.4.1
flutter_cache_manager: ^3.4.1 flutter_cache_manager: ^3.4.1
flutter_foreground_task: ^9.2.0 flutter_foreground_task: ^9.2.0
wakelock_plus: ^1.2.8 wakelock_plus: ^1.4.0
characters: ^1.4.0 characters: ^1.4.0
package_info_plus: ^9.0.0 package_info_plus: ^9.0.0
mobile_scanner: ^7.1.4 # QR/barcode scanning mobile_scanner: ^7.1.4 # QR/barcode scanning
@@ -60,6 +60,8 @@ dependencies:
gpx: ^2.3.0 gpx: ^2.3.0
path_provider: ^2.1.5 path_provider: ^2.1.5
share_plus: ^12.0.1 share_plus: ^12.0.1
web: ^1.1.1
flutter_svg: ^2.0.10+1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
@@ -87,6 +89,7 @@ flutter:
assets: assets:
- assets/images/ - assets/images/
- assets/icons/
flutter_launcher_icons: flutter_launcher_icons:
android: true android: true