mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-07-02 23:10:55 +10:00
Merge remote-tracking branch 'upstream/main'
This commit is contained in:
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 |
@@ -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": "Контактът не е успешно импортиран.",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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 => 'Уведомления';
|
||||||
|
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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 => 'Уведомления';
|
||||||
|
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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 => 'Сповіщення';
|
||||||
|
|
||||||
|
|||||||
@@ -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 => '通知';
|
||||||
|
|
||||||
|
|||||||
@@ -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.",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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.",
|
||||||
|
|||||||
@@ -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": "Копировать рекламу в буфер обмена",
|
||||||
|
|||||||
@@ -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.",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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.",
|
||||||
|
|||||||
@@ -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": "Рекламу скопійовано до буфера обміну.",
|
||||||
|
|||||||
@@ -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": "接收消息和广告的通知",
|
||||||
|
|||||||
@@ -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>?,
|
||||||
|
|||||||
@@ -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);
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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],
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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],
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user