mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-06-14 22:55:12 +10:00
feat: Enhance companion connection features and UI updates
- Added functionality to load and restore the last companion's scope on app startup. - Implemented caching mechanisms for contacts, channels, and messages related to the current companion. - Updated UI to reflect connection status, including disabling message input when disconnected. - Introduced new dialog prompts to inform users when they need to connect to a companion for accessing features. - Refactored navigation logic to improve user experience when disconnected, directing users to the scanner screen. - Added localization strings for new companion connection prompts in multiple languages.
This commit is contained in:
@@ -40,6 +40,7 @@ import '../storage/contact_discovery_store.dart';
|
||||
import '../storage/contact_settings_store.dart';
|
||||
import '../storage/contact_store.dart';
|
||||
import '../storage/message_store.dart';
|
||||
import '../storage/prefs_manager.dart';
|
||||
import '../storage/unread_store.dart';
|
||||
import '../utils/app_logger.dart';
|
||||
import '../utils/battery_utils.dart';
|
||||
@@ -124,6 +125,8 @@ class MeshCoreRadioStateSnapshot {
|
||||
class MeshCoreConnector extends ChangeNotifier {
|
||||
// Message windowing to limit memory usage
|
||||
static const int _messageWindowSize = 200;
|
||||
static const String _lastCompanionPublicKeyPref =
|
||||
'last_companion_public_key_hex';
|
||||
|
||||
MeshCoreConnectionState _state = MeshCoreConnectionState.disconnected;
|
||||
BluetoothDevice? _device;
|
||||
@@ -478,6 +481,9 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
}
|
||||
|
||||
List<Message> getMessages(Contact contact) {
|
||||
if (!_loadedConversationKeys.contains(contact.publicKeyHex)) {
|
||||
unawaited(_loadMessagesForContact(contact.publicKeyHex));
|
||||
}
|
||||
return _conversations[contact.publicKeyHex] ?? [];
|
||||
}
|
||||
|
||||
@@ -682,8 +688,14 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
}
|
||||
|
||||
Future<void> loadCachedChannels() async {
|
||||
_cachedChannels = await _channelStore.loadChannels();
|
||||
_recalculateCachedChannelsUnreadTotal();
|
||||
final loaded = await _channelStore.loadChannels();
|
||||
_cachedChannels = loaded;
|
||||
if (_channels.isEmpty && loaded.isNotEmpty) {
|
||||
_channels
|
||||
..clear()
|
||||
..addAll(loaded);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
void setActiveContact(String? contactKeyHex) {
|
||||
@@ -947,6 +959,69 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadAllCachedDataForCurrentCompanion() async {
|
||||
await loadContactCache();
|
||||
await _loadDiscoveredContactCache();
|
||||
await loadChannelSettings();
|
||||
await loadCachedChannels();
|
||||
await loadAllChannelMessages();
|
||||
await loadUnreadState();
|
||||
}
|
||||
|
||||
Future<void> restoreLastCompanionScope() async {
|
||||
final prefs = PrefsManager.instance;
|
||||
final lastCompanionPublicKeyHex = prefs.getString(
|
||||
_lastCompanionPublicKeyPref,
|
||||
);
|
||||
if (lastCompanionPublicKeyHex == null ||
|
||||
lastCompanionPublicKeyHex.trim().isEmpty) {
|
||||
return;
|
||||
}
|
||||
_setScopedStorePublicKey(lastCompanionPublicKeyHex);
|
||||
}
|
||||
|
||||
Future<void> loadDiscoveredContactCache() => _loadDiscoveredContactCache();
|
||||
|
||||
void _setScopedStorePublicKey(String publicKeyHex) {
|
||||
_channelMessageStore.setPublicKeyHex = publicKeyHex;
|
||||
_messageStore.setPublicKeyHex = publicKeyHex;
|
||||
_channelOrderStore.setPublicKeyHex = publicKeyHex;
|
||||
_channelSettingsStore.setPublicKeyHex = publicKeyHex;
|
||||
_contactSettingsStore.setPublicKeyHex = publicKeyHex;
|
||||
_contactStore.setPublicKeyHex = publicKeyHex;
|
||||
_channelStore.setPublicKeyHex = publicKeyHex;
|
||||
_unreadStore.setPublicKeyHex = publicKeyHex;
|
||||
}
|
||||
|
||||
void _clearCachedCompanionData() {
|
||||
_contacts.clear();
|
||||
_discoveredContacts.clear();
|
||||
_conversations.clear();
|
||||
_loadedConversationKeys.clear();
|
||||
_channelMessages.clear();
|
||||
_cachedChannels.clear();
|
||||
_knownContactKeys.clear();
|
||||
_contactUnreadCount.clear();
|
||||
_unreadStateLoaded = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> _persistLastCompanionScope() async {
|
||||
final keyHex = selfPublicKeyHex;
|
||||
if (keyHex.isEmpty) return;
|
||||
final prefs = PrefsManager.instance;
|
||||
await prefs.setString(_lastCompanionPublicKeyPref, keyHex);
|
||||
}
|
||||
|
||||
Future<void> _reloadOfflineCachesForLastCompanion() async {
|
||||
if (_state != MeshCoreConnectionState.disconnected) {
|
||||
return;
|
||||
}
|
||||
await restoreLastCompanionScope();
|
||||
await loadContactCache();
|
||||
await _loadDiscoveredContactCache();
|
||||
}
|
||||
|
||||
Future<void> _loadDiscoveredContactCache() async {
|
||||
final cached = await _discoveryContactStore.loadContacts();
|
||||
_discoveredContacts
|
||||
@@ -1484,6 +1559,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
_cancelReconnectTimer();
|
||||
_manualDisconnect = false;
|
||||
_resetConnectionHandshakeState();
|
||||
_clearCachedCompanionData();
|
||||
_activeTransport = MeshCoreTransportType.usb;
|
||||
_setState(MeshCoreConnectionState.connecting);
|
||||
|
||||
@@ -1565,6 +1641,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
_cancelReconnectTimer();
|
||||
_manualDisconnect = false;
|
||||
_resetConnectionHandshakeState();
|
||||
_clearCachedCompanionData();
|
||||
_activeTransport = MeshCoreTransportType.tcp;
|
||||
_setState(MeshCoreConnectionState.connecting);
|
||||
|
||||
@@ -1699,6 +1776,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
_activeTransport = MeshCoreTransportType.bluetooth;
|
||||
|
||||
await stopScan();
|
||||
_clearCachedCompanionData();
|
||||
_setState(MeshCoreConnectionState.connecting);
|
||||
_device = device;
|
||||
_deviceId = device.remoteId.toString();
|
||||
@@ -2450,6 +2528,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
'Disconnect complete transport=$transportLabel manual=$manual',
|
||||
tag: 'Connection',
|
||||
);
|
||||
unawaited(_reloadOfflineCachesForLastCompanion());
|
||||
if (!manual && transportAtDisconnect == MeshCoreTransportType.bluetooth) {
|
||||
_scheduleReconnect();
|
||||
}
|
||||
@@ -3834,26 +3913,12 @@ class MeshCoreConnector extends ChangeNotifier {
|
||||
return;
|
||||
}
|
||||
|
||||
//set all the stores' public key so they can load the correct data
|
||||
_channelMessageStore.setPublicKeyHex = selfPublicKeyHex;
|
||||
_messageStore.setPublicKeyHex = selfPublicKeyHex;
|
||||
_channelOrderStore.setPublicKeyHex = selfPublicKeyHex;
|
||||
_channelSettingsStore.setPublicKeyHex = selfPublicKeyHex;
|
||||
_contactSettingsStore.setPublicKeyHex = selfPublicKeyHex;
|
||||
_contactStore.setPublicKeyHex = selfPublicKeyHex;
|
||||
_channelStore.setPublicKeyHex = selfPublicKeyHex;
|
||||
_unreadStore.setPublicKeyHex = selfPublicKeyHex;
|
||||
// Set scoped stores to this companion and remember it for next launch.
|
||||
_setScopedStorePublicKey(selfPublicKeyHex);
|
||||
unawaited(_persistLastCompanionScope());
|
||||
|
||||
// Now that we have self info, we can load all the persisted data for this node
|
||||
_loadChannelOrder();
|
||||
loadContactCache();
|
||||
loadChannelSettings();
|
||||
loadCachedChannels();
|
||||
|
||||
// Load persisted channel messages
|
||||
loadAllChannelMessages();
|
||||
loadUnreadState();
|
||||
_loadDiscoveredContactCache();
|
||||
// Now that we have self info, we can load all the persisted data for this node.
|
||||
unawaited(loadAllCachedDataForCurrentCompanion());
|
||||
|
||||
_awaitingSelfInfo = false;
|
||||
_selfInfoRetryTimer?.cancel();
|
||||
|
||||
+2
-1
@@ -2311,5 +2311,6 @@
|
||||
"settings_companionDebugLogSubtitle": "Команди, отговори и сурови данни за протоколите BLE/TCP/USB",
|
||||
"chat_newMessages": "Нови съобщения",
|
||||
"settings_companionDebugLog": "Лог за отстраняване на грешки (за съпътстваща програма)",
|
||||
"repeater_chanUtil": "Използване на канала"
|
||||
"repeater_chanUtil": "Използване на канала",
|
||||
"contact_connectCompanion": "Свържете се с придружител, за да получите достъп до функциите на ретранслатора и сървъра за стаи."
|
||||
}
|
||||
|
||||
+2
-1
@@ -2339,5 +2339,6 @@
|
||||
"chat_newMessages": "Neue Nachrichten",
|
||||
"settings_companionDebugLog": "Debug-Protokoll für die Begleitsoftware",
|
||||
"settings_companionDebugLogSubtitle": "BLE/TCP/USB-Befehle, Antworten und Rohdaten",
|
||||
"repeater_chanUtil": "Nutzung des Kanals"
|
||||
"repeater_chanUtil": "Nutzung des Kanals",
|
||||
"contact_connectCompanion": "Verbinden Sie sich mit einem Companion, um auf die Funktionen des Repeaters und des Raumservers zuzugreifen."
|
||||
}
|
||||
|
||||
+2
-1
@@ -2366,5 +2366,6 @@
|
||||
"contact_typeRepeater": "Repeater",
|
||||
"contact_typeRoom": "Room",
|
||||
"contact_typeSensor": "Sensor",
|
||||
"contact_typeUnknown": "Unknown"
|
||||
"contact_typeUnknown": "Unknown",
|
||||
"contact_connectCompanion": "Connect to a companion to access repeater and room server features."
|
||||
}
|
||||
|
||||
+2
-1
@@ -2339,5 +2339,6 @@
|
||||
"chat_newMessages": "Nuevos mensajes",
|
||||
"settings_companionDebugLogSubtitle": "Comandos, respuestas y datos brutos para protocolos BLE/TCP/USB",
|
||||
"chat_markAsUnread": "Marcar como no leído",
|
||||
"repeater_chanUtil": "Utilización del canal"
|
||||
"repeater_chanUtil": "Utilización del canal",
|
||||
"contact_connectCompanion": "Conéctate a un compañero para acceder a las funciones de repetidor y servidor de sala."
|
||||
}
|
||||
|
||||
+2
-1
@@ -2318,5 +2318,6 @@
|
||||
"chat_markAsUnread": "Signaler comme non lu",
|
||||
"chat_newMessages": "Nouveaux messages",
|
||||
"settings_companionDebugLogSubtitle": "Commandes, réponses et données brutes pour les protocoles BLE/TCP/USB",
|
||||
"repeater_chanUtil": "Utilisation du canal"
|
||||
"repeater_chanUtil": "Utilisation du canal",
|
||||
"contact_connectCompanion": "Connectez-vous à un compagnon pour accéder aux fonctionnalités de répéteur et de serveur de salle."
|
||||
}
|
||||
|
||||
+2
-1
@@ -2349,5 +2349,6 @@
|
||||
"chat_newMessages": "Új üzenetek",
|
||||
"settings_companionDebugLog": "Párhuzamos hibakeresési napló",
|
||||
"settings_companionDebugLogSubtitle": "BLE/TCP/USB parancsok, válaszok és alapvető adatok",
|
||||
"repeater_chanUtil": "Csatorna-használat"
|
||||
"repeater_chanUtil": "Csatorna-használat",
|
||||
"contact_connectCompanion": "Csatlakozzon egy kísérőhöz a ismétlő és szobaszerver funkciók eléréséhez."
|
||||
}
|
||||
|
||||
+2
-1
@@ -2311,5 +2311,6 @@
|
||||
"settings_companionDebugLog": "Registro di debug per il supporto",
|
||||
"chat_newMessages": "Nuovi messaggi",
|
||||
"chat_markAsUnread": "Segna come non letto",
|
||||
"repeater_chanUtil": "Utilizzo del canale"
|
||||
"repeater_chanUtil": "Utilizzo del canale",
|
||||
"contact_connectCompanion": "Connettiti a un dispositivo companion per accedere alle funzionalità di ripetitore e server stanza."
|
||||
}
|
||||
|
||||
+2
-1
@@ -2349,5 +2349,6 @@
|
||||
"settings_companionDebugLog": "同伴デバッグログ",
|
||||
"chat_newMessages": "新しいメッセージ",
|
||||
"chat_markAsUnread": "未読としてマークする",
|
||||
"repeater_chanUtil": "チャンネルの利用状況"
|
||||
"repeater_chanUtil": "チャンネルの利用状況",
|
||||
"contact_connectCompanion": "コネクトしてリピーターとルームサーバー機能にアクセス"
|
||||
}
|
||||
|
||||
+2
-1
@@ -2349,5 +2349,6 @@
|
||||
"chat_newMessages": "새로운 메시지",
|
||||
"settings_companionDebugLogSubtitle": "BLE/TCP/USB 명령어, 응답 및 원시 데이터",
|
||||
"chat_markAsUnread": "미리 읽지 않음으로 표시",
|
||||
"repeater_chanUtil": "채널 활용도"
|
||||
"repeater_chanUtil": "채널 활용도",
|
||||
"contact_connectCompanion": "리피터 및 룸 서버 기능에 액세스하려면 컴패니언에 연결하세요."
|
||||
}
|
||||
|
||||
@@ -7335,6 +7335,12 @@ abstract class AppLocalizations {
|
||||
/// In en, this message translates to:
|
||||
/// **'Unknown'**
|
||||
String get contact_typeUnknown;
|
||||
|
||||
/// No description provided for @contact_connectCompanion.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Connect to a companion to access repeater and room server features.'**
|
||||
String get contact_connectCompanion;
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate
|
||||
|
||||
@@ -4288,4 +4288,8 @@ class AppLocalizationsBg extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get contact_typeUnknown => 'Unknown';
|
||||
|
||||
@override
|
||||
String get contact_connectCompanion =>
|
||||
'Свържете се с придружител, за да получите достъп до функциите на ретранслатора и сървъра за стаи.';
|
||||
}
|
||||
|
||||
@@ -4305,4 +4305,8 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get contact_typeUnknown => 'Unknown';
|
||||
|
||||
@override
|
||||
String get contact_connectCompanion =>
|
||||
'Verbinden Sie sich mit einem Companion, um auf die Funktionen des Repeaters und des Raumservers zuzugreifen.';
|
||||
}
|
||||
|
||||
@@ -4210,4 +4210,8 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get contact_typeUnknown => 'Unknown';
|
||||
|
||||
@override
|
||||
String get contact_connectCompanion =>
|
||||
'Connect to a companion to access repeater and room server features.';
|
||||
}
|
||||
|
||||
@@ -4292,4 +4292,8 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get contact_typeUnknown => 'Unknown';
|
||||
|
||||
@override
|
||||
String get contact_connectCompanion =>
|
||||
'Conéctate a un compañero para acceder a las funciones de repetidor y servidor de sala.';
|
||||
}
|
||||
|
||||
@@ -4321,4 +4321,8 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get contact_typeUnknown => 'Unknown';
|
||||
|
||||
@override
|
||||
String get contact_connectCompanion =>
|
||||
'Connectez-vous à un compagnon pour accéder aux fonctionnalités de répéteur et de serveur de salle.';
|
||||
}
|
||||
|
||||
@@ -4309,4 +4309,8 @@ class AppLocalizationsHu extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get contact_typeUnknown => 'Unknown';
|
||||
|
||||
@override
|
||||
String get contact_connectCompanion =>
|
||||
'Csatlakozzon egy kísérőhöz a ismétlő és szobaszerver funkciók eléréséhez.';
|
||||
}
|
||||
|
||||
@@ -4297,4 +4297,8 @@ class AppLocalizationsIt extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get contact_typeUnknown => 'Unknown';
|
||||
|
||||
@override
|
||||
String get contact_connectCompanion =>
|
||||
'Connettiti a un dispositivo companion per accedere alle funzionalità di ripetitore e server stanza.';
|
||||
}
|
||||
|
||||
@@ -4063,4 +4063,7 @@ class AppLocalizationsJa extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get contact_typeUnknown => 'Unknown';
|
||||
|
||||
@override
|
||||
String get contact_connectCompanion => 'コネクトしてリピーターとルームサーバー機能にアクセス';
|
||||
}
|
||||
|
||||
@@ -4064,4 +4064,7 @@ class AppLocalizationsKo extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get contact_typeUnknown => 'Unknown';
|
||||
|
||||
@override
|
||||
String get contact_connectCompanion => '리피터 및 룸 서버 기능에 액세스하려면 컴패니언에 연결하세요.';
|
||||
}
|
||||
|
||||
@@ -4273,4 +4273,8 @@ class AppLocalizationsNl extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get contact_typeUnknown => 'Unknown';
|
||||
|
||||
@override
|
||||
String get contact_connectCompanion =>
|
||||
'Maak verbinding met een companion om repeater- en kamerserverfuncties te gebruiken.';
|
||||
}
|
||||
|
||||
@@ -4309,4 +4309,8 @@ class AppLocalizationsPl extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get contact_typeUnknown => 'Unknown';
|
||||
|
||||
@override
|
||||
String get contact_connectCompanion =>
|
||||
'Połącz się z towarzyszem, aby uzyskać dostęp do funkcji powtarzacza i serwera pokoi.';
|
||||
}
|
||||
|
||||
@@ -4285,4 +4285,8 @@ class AppLocalizationsPt extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get contact_typeUnknown => 'Unknown';
|
||||
|
||||
@override
|
||||
String get contact_connectCompanion =>
|
||||
'Conecte-se a um dispositivo companion para acessar as funcionalidades de repetidor e servidor de salas.';
|
||||
}
|
||||
|
||||
@@ -4303,4 +4303,8 @@ class AppLocalizationsRu extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get contact_typeUnknown => 'Неизвестно';
|
||||
|
||||
@override
|
||||
String get contact_connectCompanion =>
|
||||
'Подключитесь к компаньону, чтобы получить доступ к функциям ретранслятора и сервера комнат.';
|
||||
}
|
||||
|
||||
@@ -4269,4 +4269,8 @@ class AppLocalizationsSk extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get contact_typeUnknown => 'Unknown';
|
||||
|
||||
@override
|
||||
String get contact_connectCompanion =>
|
||||
'Pripojte sa k sprievodcovi a získajte prístup k funkciám opakovača a serveru miestností.';
|
||||
}
|
||||
|
||||
@@ -4267,4 +4267,8 @@ class AppLocalizationsSl extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get contact_typeUnknown => 'Unknown';
|
||||
|
||||
@override
|
||||
String get contact_connectCompanion =>
|
||||
'Povežite se s spremljevalnikom za dostop do funkcij ponavljalnika in strežnika sob.';
|
||||
}
|
||||
|
||||
@@ -4241,4 +4241,8 @@ class AppLocalizationsSv extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get contact_typeUnknown => 'Unknown';
|
||||
|
||||
@override
|
||||
String get contact_connectCompanion =>
|
||||
'Anslut till en sällskapstjänst för att komma åt upprepning och rumsserverfunktioner.';
|
||||
}
|
||||
|
||||
@@ -4304,4 +4304,8 @@ class AppLocalizationsUk extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get contact_typeUnknown => 'Невідомо';
|
||||
|
||||
@override
|
||||
String get contact_connectCompanion =>
|
||||
'Підключіться до супутнього пристрою, щоб отримати доступ до функцій ретранслятора та сервера кімнат.';
|
||||
}
|
||||
|
||||
@@ -3938,4 +3938,7 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get contact_typeUnknown => 'Unknown';
|
||||
|
||||
@override
|
||||
String get contact_connectCompanion => '连接伴机以访问中继器和房间服务器功能。';
|
||||
}
|
||||
|
||||
+2
-1
@@ -2311,5 +2311,6 @@
|
||||
"chat_newMessages": "Nieuwe berichten",
|
||||
"chat_markAsUnread": "Markeer als ongelezen",
|
||||
"settings_companionDebugLogSubtitle": "BLE/TCP/USB commando's, antwoorden en ruwe data",
|
||||
"repeater_chanUtil": "Gebruik van het kanaal"
|
||||
"repeater_chanUtil": "Gebruik van het kanaal",
|
||||
"contact_connectCompanion": "Maak verbinding met een companion om repeater- en kamerserverfuncties te gebruiken."
|
||||
}
|
||||
|
||||
+2
-1
@@ -2349,5 +2349,6 @@
|
||||
"settings_companionDebugLogSubtitle": "Polecenia, odpowiedzi i surowe dane związane z protokołami BLE/TCP/USB",
|
||||
"chat_markAsUnread": "Oznacz jako nieprzeczytane",
|
||||
"settings_companionDebugLog": "Log debugowania (dla pomocy w rozwiązywaniu problemów)",
|
||||
"repeater_chanUtil": "Wykorzystanie kanału"
|
||||
"repeater_chanUtil": "Wykorzystanie kanału",
|
||||
"contact_connectCompanion": "Połącz się z towarzyszem, aby uzyskać dostęp do funkcji powtarzacza i serwera pokoi."
|
||||
}
|
||||
|
||||
+2
-1
@@ -2311,5 +2311,6 @@
|
||||
"settings_companionDebugLogSubtitle": "Comandos, respostas e dados brutos para protocolos BLE/TCP/USB",
|
||||
"chat_markAsUnread": "Marcar como não lido",
|
||||
"chat_newMessages": "Novas mensagens",
|
||||
"repeater_chanUtil": "Utilização do canal"
|
||||
"repeater_chanUtil": "Utilização do canal",
|
||||
"contact_connectCompanion": "Conecte-se a um dispositivo companion para acessar as funcionalidades de repetidor e servidor de salas."
|
||||
}
|
||||
|
||||
+2
-1
@@ -1614,5 +1614,6 @@
|
||||
"repeater_cliHelpStatsCore": "(Только для серийного оборудования) Отображает основные статистические данные прошивки.",
|
||||
"settings_companionDebugLogSubtitle": "Команды, ответы и необработанные данные, используемые для протоколов BLE, TCP и USB.",
|
||||
"repeater_chanUtil": "Использование канала",
|
||||
"settings_companionDebugLog": "Журнал отладки (для сопутствующего приложения)"
|
||||
"settings_companionDebugLog": "Журнал отладки (для сопутствующего приложения)",
|
||||
"contact_connectCompanion": "Подключитесь к компаньону, чтобы получить доступ к функциям ретранслятора и сервера комнат."
|
||||
}
|
||||
|
||||
+2
-1
@@ -2311,5 +2311,6 @@
|
||||
"settings_companionDebugLogSubtitle": "Príkazy, odpovede a surové dáta pre protokoly BLE/TCP/USB",
|
||||
"settings_companionDebugLog": "Logovanie pre ladenie (sprievodný log)",
|
||||
"chat_newMessages": "Nové správy",
|
||||
"repeater_chanUtil": "Využitie kanálu"
|
||||
"repeater_chanUtil": "Využitie kanálu",
|
||||
"contact_connectCompanion": "Pripojte sa k sprievodcovi a získajte prístup k funkciám opakovača a serveru miestností."
|
||||
}
|
||||
|
||||
+2
-1
@@ -2311,5 +2311,6 @@
|
||||
"chat_markAsUnread": "Označiti kot neneobdelano",
|
||||
"chat_newMessages": "Nove novice",
|
||||
"settings_companionDebugLogSubtitle": "Navodila, odgovori in surova podatka za BLE/TCP/USB.",
|
||||
"repeater_chanUtil": "Uporaba kanala"
|
||||
"repeater_chanUtil": "Uporaba kanala",
|
||||
"contact_connectCompanion": "Povežite se s spremljevalnikom za dostop do funkcij ponavljalnika in strežnika sob."
|
||||
}
|
||||
|
||||
+2
-1
@@ -2311,5 +2311,6 @@
|
||||
"settings_companionDebugLog": "Följande felsökningslogg",
|
||||
"chat_newMessages": "Nya meddelanden",
|
||||
"settings_companionDebugLogSubtitle": "BLE/TCP/USB-kommandon, svar och rådata",
|
||||
"repeater_chanUtil": "Användning av kanal"
|
||||
"repeater_chanUtil": "Användning av kanal",
|
||||
"contact_connectCompanion": "Anslut till en sällskapstjänst för att komma åt upprepning och rumsserverfunktioner."
|
||||
}
|
||||
|
||||
+2
-1
@@ -2291,5 +2291,6 @@
|
||||
"settings_companionDebugLogSubtitle": "Команди, відповіді та необроблена інформація для протоколів BLE/TCP/USB",
|
||||
"chat_newMessages": "Нові повідомлення",
|
||||
"chat_markAsUnread": "Позначити як непрочитане",
|
||||
"repeater_chanUtil": "Використання каналу"
|
||||
"repeater_chanUtil": "Використання каналу",
|
||||
"contact_connectCompanion": "Підключіться до супутнього пристрою, щоб отримати доступ до функцій ретранслятора та сервера кімнат."
|
||||
}
|
||||
|
||||
+2
-1
@@ -2316,5 +2316,6 @@
|
||||
"settings_companionDebugLog": "调试日志",
|
||||
"chat_newMessages": "新的消息",
|
||||
"settings_companionDebugLogSubtitle": "BLE/TCP/USB 协议、响应和原始数据",
|
||||
"repeater_chanUtil": "频道利用率"
|
||||
"repeater_chanUtil": "频道利用率",
|
||||
"contact_connectCompanion": "连接伴机以访问中继器和房间服务器功能。"
|
||||
}
|
||||
|
||||
+4
-9
@@ -5,10 +5,10 @@ import 'l10n/app_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'screens/chrome_required_screen.dart';
|
||||
import 'screens/contacts_screen.dart';
|
||||
import 'utils/platform_info.dart';
|
||||
|
||||
import 'connector/meshcore_connector.dart';
|
||||
import 'screens/scanner_screen.dart';
|
||||
import 'services/storage_service.dart';
|
||||
import 'services/message_retry_service.dart';
|
||||
import 'services/path_history_service.dart';
|
||||
@@ -81,13 +81,8 @@ void main() async {
|
||||
timeoutPredictionService: timeoutPredictionService,
|
||||
);
|
||||
|
||||
await connector.loadContactCache();
|
||||
await connector.loadChannelSettings();
|
||||
await connector.loadCachedChannels();
|
||||
|
||||
// Load persisted channel messages
|
||||
await connector.loadAllChannelMessages();
|
||||
await connector.loadUnreadState();
|
||||
await connector.restoreLastCompanionScope();
|
||||
await connector.loadAllCachedDataForCurrentCompanion();
|
||||
|
||||
runApp(
|
||||
MeshCoreApp(
|
||||
@@ -218,7 +213,7 @@ class MeshCoreApp extends StatelessWidget {
|
||||
},
|
||||
home: (PlatformInfo.isWeb && !PlatformInfo.isChrome)
|
||||
? const ChromeRequiredScreen()
|
||||
: const ScannerScreen(),
|
||||
: const ContactsScreen(),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -1123,6 +1123,9 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
||||
|
||||
Widget _buildMessageComposer() {
|
||||
final connector = context.watch<MeshCoreConnector>();
|
||||
if (!connector.isConnected) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
final maxBytes = maxChannelMessageBytes(connector.selfName);
|
||||
final settings = context.watch<AppSettingsService>().settings;
|
||||
return Column(
|
||||
@@ -1274,6 +1277,9 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
||||
}
|
||||
|
||||
Future<void> _sendMessage() async {
|
||||
final connector = context.read<MeshCoreConnector>();
|
||||
if (!connector.isConnected) return;
|
||||
|
||||
final text = _textController.text.trim();
|
||||
if (text.isEmpty) return;
|
||||
|
||||
@@ -1288,7 +1294,6 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
||||
}
|
||||
_lastChannelSendAt = now;
|
||||
|
||||
final connector = context.read<MeshCoreConnector>();
|
||||
final settings = context.read<AppSettingsService>().settings;
|
||||
final translationService = context.read<TranslationService>();
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ import '../models/channel.dart';
|
||||
import '../models/community.dart';
|
||||
import '../storage/community_store.dart';
|
||||
import '../utils/dialog_utils.dart';
|
||||
import '../utils/disconnect_navigation_mixin.dart';
|
||||
import '../utils/route_transitions.dart';
|
||||
import '../widgets/list_filter_widget.dart';
|
||||
import '../widgets/empty_state.dart';
|
||||
@@ -29,6 +28,7 @@ import 'channel_chat_screen.dart';
|
||||
import 'community_qr_scanner_screen.dart';
|
||||
import 'contacts_screen.dart';
|
||||
import 'map_screen.dart';
|
||||
import 'scanner_screen.dart';
|
||||
import 'settings_screen.dart';
|
||||
|
||||
class ChannelsScreen extends StatefulWidget {
|
||||
@@ -40,8 +40,7 @@ class ChannelsScreen extends StatefulWidget {
|
||||
State<ChannelsScreen> createState() => _ChannelsScreenState();
|
||||
}
|
||||
|
||||
class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
with DisconnectNavigationMixin {
|
||||
class _ChannelsScreenState extends State<ChannelsScreen> {
|
||||
final TextEditingController _searchController = TextEditingController();
|
||||
final CommunityStore _communityStore = CommunityStore();
|
||||
final CommunityPskIndex _communityIndex = CommunityPskIndex();
|
||||
@@ -89,11 +88,6 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
final channelMessageStore = ChannelMessageStore();
|
||||
channelMessageStore.setPublicKeyHex = connector.selfPublicKeyHex;
|
||||
|
||||
// Auto-navigate back to scanner if disconnected
|
||||
if (!checkConnectionAndNavigate(connector)) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
final allowBack = !connector.isConnected;
|
||||
|
||||
return PopScope(
|
||||
@@ -106,6 +100,7 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
actions: [
|
||||
PopupMenuButton(
|
||||
itemBuilder: (context) => [
|
||||
if (connector.isConnected)
|
||||
PopupMenuItem(
|
||||
child: Row(
|
||||
children: [
|
||||
@@ -115,6 +110,22 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
],
|
||||
),
|
||||
onTap: () => _disconnect(context),
|
||||
)
|
||||
else
|
||||
PopupMenuItem(
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.bluetooth_searching),
|
||||
const SizedBox(width: 8),
|
||||
Text(context.l10n.common_connect),
|
||||
],
|
||||
),
|
||||
onTap: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const ScannerScreen(),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_communities.isNotEmpty)
|
||||
PopupMenuItem(
|
||||
|
||||
@@ -414,7 +414,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
],
|
||||
),
|
||||
),
|
||||
_buildInputBar(connector),
|
||||
if (connector.isConnected) _buildInputBar(connector),
|
||||
],
|
||||
);
|
||||
},
|
||||
@@ -693,6 +693,9 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
}
|
||||
|
||||
Future<void> _sendMessage(MeshCoreConnector connector) async {
|
||||
if (!connector.isConnected) {
|
||||
return;
|
||||
}
|
||||
final text = _textController.text.trim();
|
||||
if (text.isEmpty) return;
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ import '../services/ui_view_state_service.dart';
|
||||
import '../utils/contact_search.dart';
|
||||
import '../storage/contact_group_store.dart';
|
||||
import '../utils/dialog_utils.dart';
|
||||
import '../utils/disconnect_navigation_mixin.dart';
|
||||
import '../utils/emoji_utils.dart';
|
||||
import '../utils/route_transitions.dart';
|
||||
import '../widgets/list_filter_widget.dart';
|
||||
@@ -34,6 +33,7 @@ import 'chat_screen.dart';
|
||||
import 'discovery_screen.dart';
|
||||
import 'map_screen.dart';
|
||||
import 'repeater_hub_screen.dart';
|
||||
import 'scanner_screen.dart';
|
||||
import 'settings_screen.dart';
|
||||
|
||||
enum RoomLoginDestination { chat, management }
|
||||
@@ -49,8 +49,7 @@ class ContactsScreen extends StatefulWidget {
|
||||
State<ContactsScreen> createState() => _ContactsScreenState();
|
||||
}
|
||||
|
||||
class _ContactsScreenState extends State<ContactsScreen>
|
||||
with DisconnectNavigationMixin {
|
||||
class _ContactsScreenState extends State<ContactsScreen> {
|
||||
final TextEditingController _searchController = TextEditingController();
|
||||
final ContactGroupStore _groupStore = ContactGroupStore();
|
||||
MeshCoreConnector? _scopeSyncConnector;
|
||||
@@ -306,11 +305,6 @@ class _ContactsScreenState extends State<ContactsScreen>
|
||||
Widget build(BuildContext context) {
|
||||
final connector = context.watch<MeshCoreConnector>();
|
||||
|
||||
// Auto-navigate back to scanner if disconnected
|
||||
if (!checkConnectionAndNavigate(connector)) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
final allowBack = !connector.isConnected;
|
||||
return PopScope(
|
||||
canPop: allowBack,
|
||||
@@ -378,6 +372,7 @@ class _ContactsScreenState extends State<ContactsScreen>
|
||||
),
|
||||
PopupMenuButton(
|
||||
itemBuilder: (context) => [
|
||||
if (connector.isConnected)
|
||||
PopupMenuItem(
|
||||
child: Row(
|
||||
children: [
|
||||
@@ -387,6 +382,22 @@ class _ContactsScreenState extends State<ContactsScreen>
|
||||
],
|
||||
),
|
||||
onTap: () => _disconnect(context, connector),
|
||||
)
|
||||
else
|
||||
PopupMenuItem(
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.bluetooth_searching),
|
||||
const SizedBox(width: 8),
|
||||
Text(context.l10n.common_connect),
|
||||
],
|
||||
),
|
||||
onTap: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const ScannerScreen(),
|
||||
),
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
child: Row(
|
||||
@@ -967,6 +978,11 @@ class _ContactsScreenState extends State<ContactsScreen>
|
||||
}
|
||||
|
||||
void _showRepeaterLogin(BuildContext context, Contact repeater) {
|
||||
final connector = context.read<MeshCoreConnector>();
|
||||
if (!connector.isConnected) {
|
||||
_showCompanionRequiredDialog(context);
|
||||
return;
|
||||
}
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => RepeaterLoginDialog(
|
||||
@@ -993,6 +1009,11 @@ class _ContactsScreenState extends State<ContactsScreen>
|
||||
Contact room,
|
||||
RoomLoginDestination destination,
|
||||
) {
|
||||
final connector = context.read<MeshCoreConnector>();
|
||||
if (!connector.isConnected) {
|
||||
_showCompanionRequiredDialog(context);
|
||||
return;
|
||||
}
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => RoomLoginDialog(
|
||||
@@ -1021,6 +1042,22 @@ class _ContactsScreenState extends State<ContactsScreen>
|
||||
);
|
||||
}
|
||||
|
||||
void _showCompanionRequiredDialog(BuildContext context) {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (dialogContext) => AlertDialog(
|
||||
title: Text(context.l10n.scanner_notConnected),
|
||||
content: Text(context.l10n.contact_connectCompanion),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(dialogContext),
|
||||
child: Text(context.l10n.common_ok),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _confirmDeleteGroup(BuildContext context, ContactGroup group) {
|
||||
if (!_hasGroupStoreScope(context.read<MeshCoreConnector>())) {
|
||||
_showGroupsUnavailableMessage(context);
|
||||
|
||||
@@ -31,6 +31,7 @@ import '../widgets/repeater_login_dialog.dart';
|
||||
import '../widgets/room_login_dialog.dart';
|
||||
import '../helpers/snack_bar_builder.dart';
|
||||
import 'repeater_hub_screen.dart';
|
||||
import 'scanner_screen.dart';
|
||||
import 'settings_screen.dart';
|
||||
import 'line_of_sight_map_screen.dart';
|
||||
|
||||
@@ -466,6 +467,7 @@ class _MapScreenState extends State<MapScreen> {
|
||||
),
|
||||
PopupMenuButton(
|
||||
itemBuilder: (context) => [
|
||||
if (connector.isConnected)
|
||||
PopupMenuItem(
|
||||
child: Row(
|
||||
children: [
|
||||
@@ -475,6 +477,22 @@ class _MapScreenState extends State<MapScreen> {
|
||||
],
|
||||
),
|
||||
onTap: () => _disconnect(context, connector),
|
||||
)
|
||||
else
|
||||
PopupMenuItem(
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.bluetooth_searching),
|
||||
const SizedBox(width: 8),
|
||||
Text(context.l10n.common_connect),
|
||||
],
|
||||
),
|
||||
onTap: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const ScannerScreen(),
|
||||
),
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
child: Row(
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../connector/meshcore_connector.dart';
|
||||
|
||||
/// Mixin that automatically navigates back to scanner when disconnected.
|
||||
/// Use in State classes for screens that require active connection.
|
||||
mixin DisconnectNavigationMixin<T extends StatefulWidget> on State<T> {
|
||||
/// Call this in your Widget build method to enable auto-navigation.
|
||||
/// Returns true if still connected, false if navigation was triggered.
|
||||
bool checkConnectionAndNavigate(MeshCoreConnector connector) {
|
||||
if (!connector.isConnected) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted) {
|
||||
Navigator.popUntil(context, (route) => route.isFirst);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user