mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-04-20 22:13:48 +00:00
Add region management
This adds region management: the user can manage a list of available regions and for each channel pick a region from that list to apply to messages. Region discovery from nearby repeaters will be done in a separate PR. This is a part of the work needed for #120.
This commit is contained in:
parent
0757c8e53a
commit
0e074fd806
33 changed files with 1653 additions and 68 deletions
|
|
@ -118,6 +118,7 @@ On unexpected disconnection, auto-reconnect with exponential backoff:
|
|||
| 40 | CMD_GET_CUSTOM_VAR | Get custom variables |
|
||||
| 41 | CMD_SET_CUSTOM_VAR | Set a custom variable |
|
||||
| 50 | CMD_SEND_BINARY_REQ | Send binary request |
|
||||
| 54 | CMD_SET_FLOOD_SCOPE | Set flood routing scope (v8+) |
|
||||
| 57 | CMD_SEND_ANON_REQ | Send anonymous request |
|
||||
| 58 | CMD_SET_AUTO_ADD_CONFIG | Set auto-add configuration |
|
||||
| 59 | CMD_GET_AUTO_ADD_CONFIG | Get auto-add configuration |
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'dart:convert';
|
|||
import 'dart:math' as math;
|
||||
|
||||
import 'package:crypto/crypto.dart' as crypto;
|
||||
import 'package:meshcore_open/storage/region_store.dart';
|
||||
import 'package:pointycastle/export.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
|
||||
|
|
@ -34,6 +35,7 @@ import 'meshcore_connector_tcp.dart';
|
|||
import '../storage/channel_message_store.dart';
|
||||
import '../storage/channel_order_store.dart';
|
||||
import '../storage/channel_settings_store.dart';
|
||||
import '../storage/channel_region_store.dart';
|
||||
import '../storage/channel_store.dart';
|
||||
import '../storage/contact_discovery_store.dart';
|
||||
import '../storage/contact_settings_store.dart';
|
||||
|
|
@ -276,6 +278,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||
final MessageStore _messageStore = MessageStore();
|
||||
final ChannelOrderStore _channelOrderStore = ChannelOrderStore();
|
||||
final ChannelSettingsStore _channelSettingsStore = ChannelSettingsStore();
|
||||
final ChannelRegionStore _channelRegionStore = ChannelRegionStore();
|
||||
final ContactSettingsStore _contactSettingsStore = ContactSettingsStore();
|
||||
final ContactStore _contactStore = ContactStore();
|
||||
final ContactDiscoveryStore _discoveryContactStore = ContactDiscoveryStore();
|
||||
|
|
@ -283,6 +286,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||
final UnreadStore _unreadStore = UnreadStore();
|
||||
List<Channel> _cachedChannels = [];
|
||||
final Map<int, bool> _channelSmazEnabled = {};
|
||||
final Map<int, Region> _channelRegions = {};
|
||||
bool _lastSentWasCliCommand =
|
||||
false; // Track if last sent message was a CLI command
|
||||
final Map<String, bool> _contactSmazEnabled = {};
|
||||
|
|
@ -603,6 +607,14 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||
return _contactSmazEnabled[contactKeyHex] ?? false;
|
||||
}
|
||||
|
||||
bool hasChannelRegion(int channelIndex) {
|
||||
return _channelRegions[channelIndex] != '';
|
||||
}
|
||||
|
||||
Region getChannelRegion(int channelIndex) {
|
||||
return _channelRegions[channelIndex] ?? '';
|
||||
}
|
||||
|
||||
void ensureContactSmazSettingLoaded(String contactKeyHex) {
|
||||
_ensureContactSmazSettingLoaded(contactKeyHex);
|
||||
}
|
||||
|
|
@ -692,6 +704,14 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> setChannelRegion(int channelIndex, String region) async {
|
||||
_channelRegions[channelIndex] = await _channelRegionStore.saveRegion(
|
||||
channelIndex,
|
||||
region,
|
||||
);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> _loadChannelOrder() async {
|
||||
_channelOrder = await _channelOrderStore.loadChannelOrder();
|
||||
_applyChannelOrder();
|
||||
|
|
@ -840,9 +860,11 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||
|
||||
Future<void> loadChannelSettings({int? maxChannels}) async {
|
||||
_channelSmazEnabled.clear();
|
||||
_channelRegions.clear();
|
||||
final channelCount = maxChannels ?? _maxChannels;
|
||||
for (int i = 0; i < channelCount; i++) {
|
||||
_channelSmazEnabled[i] = await _channelSettingsStore.loadSmazEnabled(i);
|
||||
_channelRegions[i] = await _channelRegionStore.loadRegion(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2973,12 +2995,19 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||
// Send the reaction to the device (don't add as a visible message)
|
||||
final reactionQueueId = _nextReactionSendQueueId();
|
||||
_pendingChannelSentQueue.add(reactionQueueId);
|
||||
await _waitForRadioQuiet(lastInboundRxTime: _lastChannelMsgRxTime);
|
||||
await sendFrame(
|
||||
buildSendChannelTextMsgFrame(channel.index, text),
|
||||
channelSendQueueId: reactionQueueId,
|
||||
expectsGenericAck: true,
|
||||
);
|
||||
try {
|
||||
await sendFrame(
|
||||
buildSetFloodScopeFrame(getChannelRegion(channel.index)),
|
||||
);
|
||||
await _waitForRadioQuiet(lastInboundRxTime: _lastChannelMsgRxTime);
|
||||
await sendFrame(
|
||||
buildSendChannelTextMsgFrame(channel.index, text),
|
||||
channelSendQueueId: reactionQueueId,
|
||||
expectsGenericAck: true,
|
||||
);
|
||||
} finally {
|
||||
await sendFrame(buildSetFloodScopeFrame(''));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -3001,12 +3030,17 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||
(isChannelSmazEnabled(channel.index) && !isStructuredPayload)
|
||||
? Smaz.encodeIfSmaller(text)
|
||||
: text;
|
||||
await _waitForRadioQuiet(lastInboundRxTime: _lastChannelMsgRxTime);
|
||||
await sendFrame(
|
||||
buildSendChannelTextMsgFrame(channel.index, outboundText),
|
||||
channelSendQueueId: message.messageId,
|
||||
expectsGenericAck: true,
|
||||
);
|
||||
try {
|
||||
await sendFrame(buildSetFloodScopeFrame(getChannelRegion(channel.index)));
|
||||
await _waitForRadioQuiet(lastInboundRxTime: _lastChannelMsgRxTime);
|
||||
await sendFrame(
|
||||
buildSendChannelTextMsgFrame(channel.index, outboundText),
|
||||
channelSendQueueId: message.messageId,
|
||||
expectsGenericAck: true,
|
||||
);
|
||||
} finally {
|
||||
await sendFrame(buildSetFloodScopeFrame(''));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> removeContact(Contact contact) async {
|
||||
|
|
@ -3680,6 +3714,7 @@ class MeshCoreConnector extends ChangeNotifier {
|
|||
_messageStore.setPublicKeyHex = selfPublicKeyHex;
|
||||
_channelOrderStore.setPublicKeyHex = selfPublicKeyHex;
|
||||
_channelSettingsStore.setPublicKeyHex = selfPublicKeyHex;
|
||||
_channelRegionStore.setPublicKeyHex = selfPublicKeyHex;
|
||||
_contactSettingsStore.setPublicKeyHex = selfPublicKeyHex;
|
||||
_contactStore.setPublicKeyHex = selfPublicKeyHex;
|
||||
_channelStore.setPublicKeyHex = selfPublicKeyHex;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:crypto/crypto.dart' as crypto;
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
// Buffer Reader - sequential binary data reader with pointer tracking
|
||||
|
|
@ -211,6 +212,7 @@ const int cmdSendAnonReq = 57;
|
|||
const int cmdSetAutoAddConfig = 58;
|
||||
const int cmdGetAutoAddConfig = 59;
|
||||
const int cmdSetPathHashMode = 61;
|
||||
const int cmdSetFloodScope = 54;
|
||||
|
||||
// Text message types
|
||||
const int txtTypePlain = 0;
|
||||
|
|
@ -955,3 +957,18 @@ Uint8List buildSendTelemetryReq(Uint8List? pubKey) {
|
|||
}
|
||||
return writer.toBytes();
|
||||
}
|
||||
|
||||
//Build CMD_SET_FLOOD_SCOPE
|
||||
// Format: [cmd][scope]
|
||||
Uint8List buildSetFloodScopeFrame(String region) {
|
||||
if (region == '') {
|
||||
// reset scope
|
||||
return Uint8List.fromList([cmdSetFloodScope, 0]);
|
||||
}
|
||||
|
||||
final name = region.startsWith('#') ? region : '#$region';
|
||||
final hash = crypto.sha256.convert(utf8.encode(name)).bytes;
|
||||
final scope = Uint8List.fromList(hash.sublist(0, 16));
|
||||
|
||||
return Uint8List.fromList([cmdSetFloodScope, 0, ...scope]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2097,5 +2097,33 @@
|
|||
"description": "Repeater setting subtitle: describes the clock sync after login behavior"
|
||||
},
|
||||
"repeater_clockSyncAfterLogin": "Uhrzeit-Synchronisation nach dem Anmelden",
|
||||
"repeater_clockSyncAfterLoginSubtitle": "Automatisch \"Uhrzeit-Synchronisierung\" nach erfolgreicher Anmeldung senden."
|
||||
}
|
||||
"repeater_clockSyncAfterLoginSubtitle": "Automatisch \"Uhrzeit-Synchronisierung\" nach erfolgreicher Anmeldung senden.",
|
||||
"@settings_deleteRegionConfirm": {
|
||||
"placeholders": {
|
||||
"region": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"@channels_regionSetTo": {
|
||||
"placeholders": {
|
||||
"region": {
|
||||
"type": "String",
|
||||
"example": "de-mitte"
|
||||
}
|
||||
}
|
||||
},
|
||||
"settings_regionSettings": "Regionen",
|
||||
"settings_regionSettingsSubtitle": "Gespeicherte Regionen verwalten",
|
||||
"settings_regionManagement_screenTitle": "Regions-Verwaltung",
|
||||
"settings_regionNameHint": "Regions-Namen eingeben",
|
||||
"settings_regionAddRegion": "Region hinzufügen",
|
||||
"settings_regionDeleted": "Region entfernt",
|
||||
"settings_regionName": "Regions-Name",
|
||||
"settings_deleteRegion": "Region entfernen",
|
||||
"settings_deleteRegionConfirm": "Region \"{region}\" aus der Liste entfernen?",
|
||||
"channels_regionNotSet": "Region: keine",
|
||||
"channels_regionSetTo": "Region: {region}",
|
||||
"channels_regionSelect_Title": "Region auswählen",
|
||||
"channels_clearRegion": "Region zurücksetzen"
|
||||
}
|
||||
|
|
@ -127,7 +127,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
|
||||
"scanner_stop": "Stop",
|
||||
"scanner_scan": "Scan",
|
||||
"scanner_bluetoothOff": "Bluetooth is off",
|
||||
|
|
@ -149,6 +148,22 @@
|
|||
"settings_radioSettings": "Radio Settings",
|
||||
"settings_radioSettingsSubtitle": "Frequency, power, spreading factor",
|
||||
"settings_radioSettingsUpdated": "Radio settings updated",
|
||||
"settings_regionSettings": "Regions",
|
||||
"settings_regionSettingsSubtitle": "Manage stored regions",
|
||||
"settings_regionManagement_screenTitle": "Region Management",
|
||||
"settings_regionNameHint": "Enter region name",
|
||||
"settings_regionAddRegion": "Add region",
|
||||
"settings_regionName": "Region Name",
|
||||
"settings_regionDeleted": "Region deleted",
|
||||
"settings_deleteRegion": "Delete Region",
|
||||
"settings_deleteRegionConfirm": "Remove \"{region}\" from region list?",
|
||||
"@settings_deleteRegionConfirm": {
|
||||
"placeholders": {
|
||||
"region": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"settings_location": "Location",
|
||||
"settings_locationSubtitle": "GPS coordinates",
|
||||
"settings_locationUpdated": "Location and GPS settings updated",
|
||||
|
|
@ -606,6 +621,18 @@
|
|||
"channels_scanQrCodeComingSoon": "Coming soon",
|
||||
"channels_enterHashtag": "Enter hashtag",
|
||||
"channels_hashtagHint": "e.g. #team",
|
||||
"channels_regionSetTo": "Region: {region}",
|
||||
"@channels_regionSetTo": {
|
||||
"placeholders": {
|
||||
"region": {
|
||||
"type": "String",
|
||||
"example": "de-mitte"
|
||||
}
|
||||
}
|
||||
},
|
||||
"channels_regionNotSet": "Region: none",
|
||||
"channels_regionSelect_Title": "Select a region",
|
||||
"channels_clearRegion": "Clear region",
|
||||
"chat_noMessages": "No messages yet",
|
||||
"chat_sendMessage": "Send message",
|
||||
"chat_sendMessageTo": "Send message to {name}",
|
||||
|
|
@ -2065,7 +2092,6 @@
|
|||
"radioStats_stripWaiting": "Fetching radio stats…",
|
||||
"radioStats_settingsTile": "Radio stats",
|
||||
"radioStats_settingsSubtitle": "Noise floor, RSSI, SNR, and airtime",
|
||||
|
||||
"translation_title": "Translation",
|
||||
"translation_enableTitle": "Enable translation",
|
||||
"translation_enableSubtitle": "Translate incoming messages and allow pre-send translation.",
|
||||
|
|
@ -2119,4 +2145,4 @@
|
|||
},
|
||||
"translation_translationOptions": "Translation options",
|
||||
"translation_systemLanguage": "System language"
|
||||
}
|
||||
}
|
||||
|
|
@ -724,6 +724,60 @@ abstract class AppLocalizations {
|
|||
/// **'Radio settings updated'**
|
||||
String get settings_radioSettingsUpdated;
|
||||
|
||||
/// No description provided for @settings_regionSettings.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Regions'**
|
||||
String get settings_regionSettings;
|
||||
|
||||
/// No description provided for @settings_regionSettingsSubtitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Manage stored regions'**
|
||||
String get settings_regionSettingsSubtitle;
|
||||
|
||||
/// No description provided for @settings_regionManagement_screenTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Region Management'**
|
||||
String get settings_regionManagement_screenTitle;
|
||||
|
||||
/// No description provided for @settings_regionNameHint.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Enter region name'**
|
||||
String get settings_regionNameHint;
|
||||
|
||||
/// No description provided for @settings_regionAddRegion.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Add region'**
|
||||
String get settings_regionAddRegion;
|
||||
|
||||
/// No description provided for @settings_regionName.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Region Name'**
|
||||
String get settings_regionName;
|
||||
|
||||
/// No description provided for @settings_regionDeleted.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Region deleted'**
|
||||
String get settings_regionDeleted;
|
||||
|
||||
/// No description provided for @settings_deleteRegion.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Delete Region'**
|
||||
String get settings_deleteRegion;
|
||||
|
||||
/// No description provided for @settings_deleteRegionConfirm.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Remove \"{region}\" from region list?'**
|
||||
String settings_deleteRegionConfirm(String region);
|
||||
|
||||
/// No description provided for @settings_location.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
@ -2290,6 +2344,30 @@ abstract class AppLocalizations {
|
|||
/// **'e.g. #team'**
|
||||
String get channels_hashtagHint;
|
||||
|
||||
/// No description provided for @channels_regionSetTo.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Region: {region}'**
|
||||
String channels_regionSetTo(String region);
|
||||
|
||||
/// No description provided for @channels_regionNotSet.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Region: none'**
|
||||
String get channels_regionNotSet;
|
||||
|
||||
/// No description provided for @channels_regionSelect_Title.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Select a region'**
|
||||
String get channels_regionSelect_Title;
|
||||
|
||||
/// No description provided for @channels_clearRegion.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Clear region'**
|
||||
String get channels_clearRegion;
|
||||
|
||||
/// No description provided for @chat_noMessages.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
|
|||
|
|
@ -336,6 +336,35 @@ class AppLocalizationsBg extends AppLocalizations {
|
|||
String get settings_radioSettingsUpdated =>
|
||||
'Радио настройките са актуализирани';
|
||||
|
||||
@override
|
||||
String get settings_regionSettings => 'Regions';
|
||||
|
||||
@override
|
||||
String get settings_regionSettingsSubtitle => 'Manage stored regions';
|
||||
|
||||
@override
|
||||
String get settings_regionManagement_screenTitle => 'Region Management';
|
||||
|
||||
@override
|
||||
String get settings_regionNameHint => 'Enter region name';
|
||||
|
||||
@override
|
||||
String get settings_regionAddRegion => 'Add region';
|
||||
|
||||
@override
|
||||
String get settings_regionName => 'Region Name';
|
||||
|
||||
@override
|
||||
String get settings_regionDeleted => 'Region deleted';
|
||||
|
||||
@override
|
||||
String get settings_deleteRegion => 'Delete Region';
|
||||
|
||||
@override
|
||||
String settings_deleteRegionConfirm(String region) {
|
||||
return 'Remove \"$region\" from region list?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get settings_location => 'Местоположение';
|
||||
|
||||
|
|
@ -1236,6 +1265,20 @@ class AppLocalizationsBg extends AppLocalizations {
|
|||
@override
|
||||
String get channels_hashtagHint => 'напр. #отбор';
|
||||
|
||||
@override
|
||||
String channels_regionSetTo(String region) {
|
||||
return 'Region: $region';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channels_regionNotSet => 'Region: none';
|
||||
|
||||
@override
|
||||
String get channels_regionSelect_Title => 'Select a region';
|
||||
|
||||
@override
|
||||
String get channels_clearRegion => 'Clear region';
|
||||
|
||||
@override
|
||||
String get chat_noMessages => 'Няма съобщения.';
|
||||
|
||||
|
|
|
|||
|
|
@ -338,6 +338,36 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
@override
|
||||
String get settings_radioSettingsUpdated => 'Funkparameter aktualisiert';
|
||||
|
||||
@override
|
||||
String get settings_regionSettings => 'Regionen';
|
||||
|
||||
@override
|
||||
String get settings_regionSettingsSubtitle =>
|
||||
'Gespeicherte Regionen verwalten';
|
||||
|
||||
@override
|
||||
String get settings_regionManagement_screenTitle => 'Regions-Verwaltung';
|
||||
|
||||
@override
|
||||
String get settings_regionNameHint => 'Regions-Namen eingeben';
|
||||
|
||||
@override
|
||||
String get settings_regionAddRegion => 'Region hinzufügen';
|
||||
|
||||
@override
|
||||
String get settings_regionName => 'Regions-Name';
|
||||
|
||||
@override
|
||||
String get settings_regionDeleted => 'Region entfernt';
|
||||
|
||||
@override
|
||||
String get settings_deleteRegion => 'Region entfernen';
|
||||
|
||||
@override
|
||||
String settings_deleteRegionConfirm(String region) {
|
||||
return 'Region \"$region\" aus der Liste entfernen?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get settings_location => 'Ort';
|
||||
|
||||
|
|
@ -1235,6 +1265,20 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
@override
|
||||
String get channels_hashtagHint => 'z.B. #team';
|
||||
|
||||
@override
|
||||
String channels_regionSetTo(String region) {
|
||||
return 'Region: $region';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channels_regionNotSet => 'Region: keine';
|
||||
|
||||
@override
|
||||
String get channels_regionSelect_Title => 'Region auswählen';
|
||||
|
||||
@override
|
||||
String get channels_clearRegion => 'Region zurücksetzen';
|
||||
|
||||
@override
|
||||
String get chat_noMessages => 'Noch keine Nachrichten.';
|
||||
|
||||
|
|
|
|||
|
|
@ -332,6 +332,35 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
@override
|
||||
String get settings_radioSettingsUpdated => 'Radio settings updated';
|
||||
|
||||
@override
|
||||
String get settings_regionSettings => 'Regions';
|
||||
|
||||
@override
|
||||
String get settings_regionSettingsSubtitle => 'Manage stored regions';
|
||||
|
||||
@override
|
||||
String get settings_regionManagement_screenTitle => 'Region Management';
|
||||
|
||||
@override
|
||||
String get settings_regionNameHint => 'Enter region name';
|
||||
|
||||
@override
|
||||
String get settings_regionAddRegion => 'Add region';
|
||||
|
||||
@override
|
||||
String get settings_regionName => 'Region Name';
|
||||
|
||||
@override
|
||||
String get settings_regionDeleted => 'Region deleted';
|
||||
|
||||
@override
|
||||
String get settings_deleteRegion => 'Delete Region';
|
||||
|
||||
@override
|
||||
String settings_deleteRegionConfirm(String region) {
|
||||
return 'Remove \"$region\" from region list?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get settings_location => 'Location';
|
||||
|
||||
|
|
@ -1210,6 +1239,20 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
@override
|
||||
String get channels_hashtagHint => 'e.g. #team';
|
||||
|
||||
@override
|
||||
String channels_regionSetTo(String region) {
|
||||
return 'Region: $region';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channels_regionNotSet => 'Region: none';
|
||||
|
||||
@override
|
||||
String get channels_regionSelect_Title => 'Select a region';
|
||||
|
||||
@override
|
||||
String get channels_clearRegion => 'Clear region';
|
||||
|
||||
@override
|
||||
String get chat_noMessages => 'No messages yet';
|
||||
|
||||
|
|
|
|||
|
|
@ -336,6 +336,35 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||
@override
|
||||
String get settings_radioSettingsUpdated => 'Ajustes de radio actualizados';
|
||||
|
||||
@override
|
||||
String get settings_regionSettings => 'Regions';
|
||||
|
||||
@override
|
||||
String get settings_regionSettingsSubtitle => 'Manage stored regions';
|
||||
|
||||
@override
|
||||
String get settings_regionManagement_screenTitle => 'Region Management';
|
||||
|
||||
@override
|
||||
String get settings_regionNameHint => 'Enter region name';
|
||||
|
||||
@override
|
||||
String get settings_regionAddRegion => 'Add region';
|
||||
|
||||
@override
|
||||
String get settings_regionName => 'Region Name';
|
||||
|
||||
@override
|
||||
String get settings_regionDeleted => 'Region deleted';
|
||||
|
||||
@override
|
||||
String get settings_deleteRegion => 'Delete Region';
|
||||
|
||||
@override
|
||||
String settings_deleteRegionConfirm(String region) {
|
||||
return 'Remove \"$region\" from region list?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get settings_location => 'Ubicación';
|
||||
|
||||
|
|
@ -1235,6 +1264,20 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||
@override
|
||||
String get channels_hashtagHint => 'ej. #equipo';
|
||||
|
||||
@override
|
||||
String channels_regionSetTo(String region) {
|
||||
return 'Region: $region';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channels_regionNotSet => 'Region: none';
|
||||
|
||||
@override
|
||||
String get channels_regionSelect_Title => 'Select a region';
|
||||
|
||||
@override
|
||||
String get channels_clearRegion => 'Clear region';
|
||||
|
||||
@override
|
||||
String get chat_noMessages => 'Aún no hay mensajes';
|
||||
|
||||
|
|
|
|||
|
|
@ -338,6 +338,35 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||
@override
|
||||
String get settings_radioSettingsUpdated => 'Paramètres radio mis à jour';
|
||||
|
||||
@override
|
||||
String get settings_regionSettings => 'Regions';
|
||||
|
||||
@override
|
||||
String get settings_regionSettingsSubtitle => 'Manage stored regions';
|
||||
|
||||
@override
|
||||
String get settings_regionManagement_screenTitle => 'Region Management';
|
||||
|
||||
@override
|
||||
String get settings_regionNameHint => 'Enter region name';
|
||||
|
||||
@override
|
||||
String get settings_regionAddRegion => 'Add region';
|
||||
|
||||
@override
|
||||
String get settings_regionName => 'Region Name';
|
||||
|
||||
@override
|
||||
String get settings_regionDeleted => 'Region deleted';
|
||||
|
||||
@override
|
||||
String get settings_deleteRegion => 'Delete Region';
|
||||
|
||||
@override
|
||||
String settings_deleteRegionConfirm(String region) {
|
||||
return 'Remove \"$region\" from region list?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get settings_location => 'Emplacement';
|
||||
|
||||
|
|
@ -1240,6 +1269,20 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||
@override
|
||||
String get channels_hashtagHint => 'ex. #equipe';
|
||||
|
||||
@override
|
||||
String channels_regionSetTo(String region) {
|
||||
return 'Region: $region';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channels_regionNotSet => 'Region: none';
|
||||
|
||||
@override
|
||||
String get channels_regionSelect_Title => 'Select a region';
|
||||
|
||||
@override
|
||||
String get channels_clearRegion => 'Clear region';
|
||||
|
||||
@override
|
||||
String get chat_noMessages => 'Aucun message pour le moment.';
|
||||
|
||||
|
|
|
|||
|
|
@ -335,6 +335,35 @@ class AppLocalizationsHu extends AppLocalizations {
|
|||
@override
|
||||
String get settings_radioSettingsUpdated => 'A rádió beállítások frissítve';
|
||||
|
||||
@override
|
||||
String get settings_regionSettings => 'Regions';
|
||||
|
||||
@override
|
||||
String get settings_regionSettingsSubtitle => 'Manage stored regions';
|
||||
|
||||
@override
|
||||
String get settings_regionManagement_screenTitle => 'Region Management';
|
||||
|
||||
@override
|
||||
String get settings_regionNameHint => 'Enter region name';
|
||||
|
||||
@override
|
||||
String get settings_regionAddRegion => 'Add region';
|
||||
|
||||
@override
|
||||
String get settings_regionName => 'Region Name';
|
||||
|
||||
@override
|
||||
String get settings_regionDeleted => 'Region deleted';
|
||||
|
||||
@override
|
||||
String get settings_deleteRegion => 'Delete Region';
|
||||
|
||||
@override
|
||||
String settings_deleteRegionConfirm(String region) {
|
||||
return 'Remove \"$region\" from region list?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get settings_location => 'Helyszín';
|
||||
|
||||
|
|
@ -1243,6 +1272,20 @@ class AppLocalizationsHu extends AppLocalizations {
|
|||
@override
|
||||
String get channels_hashtagHint => 'pl. #csapat';
|
||||
|
||||
@override
|
||||
String channels_regionSetTo(String region) {
|
||||
return 'Region: $region';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channels_regionNotSet => 'Region: none';
|
||||
|
||||
@override
|
||||
String get channels_regionSelect_Title => 'Select a region';
|
||||
|
||||
@override
|
||||
String get channels_clearRegion => 'Clear region';
|
||||
|
||||
@override
|
||||
String get chat_noMessages => 'Még nincs üzenet.';
|
||||
|
||||
|
|
|
|||
|
|
@ -338,6 +338,35 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||
@override
|
||||
String get settings_radioSettingsUpdated => 'Impostazioni radio aggiornate';
|
||||
|
||||
@override
|
||||
String get settings_regionSettings => 'Regions';
|
||||
|
||||
@override
|
||||
String get settings_regionSettingsSubtitle => 'Manage stored regions';
|
||||
|
||||
@override
|
||||
String get settings_regionManagement_screenTitle => 'Region Management';
|
||||
|
||||
@override
|
||||
String get settings_regionNameHint => 'Enter region name';
|
||||
|
||||
@override
|
||||
String get settings_regionAddRegion => 'Add region';
|
||||
|
||||
@override
|
||||
String get settings_regionName => 'Region Name';
|
||||
|
||||
@override
|
||||
String get settings_regionDeleted => 'Region deleted';
|
||||
|
||||
@override
|
||||
String get settings_deleteRegion => 'Delete Region';
|
||||
|
||||
@override
|
||||
String settings_deleteRegionConfirm(String region) {
|
||||
return 'Remove \"$region\" from region list?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get settings_location => 'Posizione';
|
||||
|
||||
|
|
@ -1236,6 +1265,20 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||
@override
|
||||
String get channels_hashtagHint => 'es. #team';
|
||||
|
||||
@override
|
||||
String channels_regionSetTo(String region) {
|
||||
return 'Region: $region';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channels_regionNotSet => 'Region: none';
|
||||
|
||||
@override
|
||||
String get channels_regionSelect_Title => 'Select a region';
|
||||
|
||||
@override
|
||||
String get channels_clearRegion => 'Clear region';
|
||||
|
||||
@override
|
||||
String get chat_noMessages => 'Nessun messaggio ancora';
|
||||
|
||||
|
|
|
|||
|
|
@ -323,6 +323,35 @@ class AppLocalizationsJa extends AppLocalizations {
|
|||
@override
|
||||
String get settings_radioSettingsUpdated => 'ラジオの設定が更新されました';
|
||||
|
||||
@override
|
||||
String get settings_regionSettings => 'Regions';
|
||||
|
||||
@override
|
||||
String get settings_regionSettingsSubtitle => 'Manage stored regions';
|
||||
|
||||
@override
|
||||
String get settings_regionManagement_screenTitle => 'Region Management';
|
||||
|
||||
@override
|
||||
String get settings_regionNameHint => 'Enter region name';
|
||||
|
||||
@override
|
||||
String get settings_regionAddRegion => 'Add region';
|
||||
|
||||
@override
|
||||
String get settings_regionName => 'Region Name';
|
||||
|
||||
@override
|
||||
String get settings_regionDeleted => 'Region deleted';
|
||||
|
||||
@override
|
||||
String get settings_deleteRegion => 'Delete Region';
|
||||
|
||||
@override
|
||||
String settings_deleteRegionConfirm(String region) {
|
||||
return 'Remove \"$region\" from region list?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get settings_location => '場所';
|
||||
|
||||
|
|
@ -1176,6 +1205,20 @@ class AppLocalizationsJa extends AppLocalizations {
|
|||
@override
|
||||
String get channels_hashtagHint => '例:#チーム';
|
||||
|
||||
@override
|
||||
String channels_regionSetTo(String region) {
|
||||
return 'Region: $region';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channels_regionNotSet => 'Region: none';
|
||||
|
||||
@override
|
||||
String get channels_regionSelect_Title => 'Select a region';
|
||||
|
||||
@override
|
||||
String get channels_clearRegion => 'Clear region';
|
||||
|
||||
@override
|
||||
String get chat_noMessages => 'まだメッセージは届いていません';
|
||||
|
||||
|
|
|
|||
|
|
@ -322,6 +322,35 @@ class AppLocalizationsKo extends AppLocalizations {
|
|||
@override
|
||||
String get settings_radioSettingsUpdated => '라디오 설정이 업데이트되었습니다.';
|
||||
|
||||
@override
|
||||
String get settings_regionSettings => 'Regions';
|
||||
|
||||
@override
|
||||
String get settings_regionSettingsSubtitle => 'Manage stored regions';
|
||||
|
||||
@override
|
||||
String get settings_regionManagement_screenTitle => 'Region Management';
|
||||
|
||||
@override
|
||||
String get settings_regionNameHint => 'Enter region name';
|
||||
|
||||
@override
|
||||
String get settings_regionAddRegion => 'Add region';
|
||||
|
||||
@override
|
||||
String get settings_regionName => 'Region Name';
|
||||
|
||||
@override
|
||||
String get settings_regionDeleted => 'Region deleted';
|
||||
|
||||
@override
|
||||
String get settings_deleteRegion => 'Delete Region';
|
||||
|
||||
@override
|
||||
String settings_deleteRegionConfirm(String region) {
|
||||
return 'Remove \"$region\" from region list?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get settings_location => '위치';
|
||||
|
||||
|
|
@ -1171,6 +1200,20 @@ class AppLocalizationsKo extends AppLocalizations {
|
|||
@override
|
||||
String get channels_hashtagHint => '예: #팀';
|
||||
|
||||
@override
|
||||
String channels_regionSetTo(String region) {
|
||||
return 'Region: $region';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channels_regionNotSet => 'Region: none';
|
||||
|
||||
@override
|
||||
String get channels_regionSelect_Title => 'Select a region';
|
||||
|
||||
@override
|
||||
String get channels_clearRegion => 'Clear region';
|
||||
|
||||
@override
|
||||
String get chat_noMessages => '아직 메시지가 없습니다.';
|
||||
|
||||
|
|
|
|||
|
|
@ -334,6 +334,35 @@ class AppLocalizationsNl extends AppLocalizations {
|
|||
@override
|
||||
String get settings_radioSettingsUpdated => 'Radio instellingen bijgewerkt';
|
||||
|
||||
@override
|
||||
String get settings_regionSettings => 'Regions';
|
||||
|
||||
@override
|
||||
String get settings_regionSettingsSubtitle => 'Manage stored regions';
|
||||
|
||||
@override
|
||||
String get settings_regionManagement_screenTitle => 'Region Management';
|
||||
|
||||
@override
|
||||
String get settings_regionNameHint => 'Enter region name';
|
||||
|
||||
@override
|
||||
String get settings_regionAddRegion => 'Add region';
|
||||
|
||||
@override
|
||||
String get settings_regionName => 'Region Name';
|
||||
|
||||
@override
|
||||
String get settings_regionDeleted => 'Region deleted';
|
||||
|
||||
@override
|
||||
String get settings_deleteRegion => 'Delete Region';
|
||||
|
||||
@override
|
||||
String settings_deleteRegionConfirm(String region) {
|
||||
return 'Remove \"$region\" from region list?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get settings_location => 'Locatie';
|
||||
|
||||
|
|
@ -1224,6 +1253,20 @@ class AppLocalizationsNl extends AppLocalizations {
|
|||
@override
|
||||
String get channels_hashtagHint => 'bijv. #team';
|
||||
|
||||
@override
|
||||
String channels_regionSetTo(String region) {
|
||||
return 'Region: $region';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channels_regionNotSet => 'Region: none';
|
||||
|
||||
@override
|
||||
String get channels_regionSelect_Title => 'Select a region';
|
||||
|
||||
@override
|
||||
String get channels_clearRegion => 'Clear region';
|
||||
|
||||
@override
|
||||
String get chat_noMessages => 'Nog geen berichten.';
|
||||
|
||||
|
|
|
|||
|
|
@ -340,6 +340,35 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||
String get settings_radioSettingsUpdated =>
|
||||
'Ustawienia radia zostały zaktualizowane';
|
||||
|
||||
@override
|
||||
String get settings_regionSettings => 'Regions';
|
||||
|
||||
@override
|
||||
String get settings_regionSettingsSubtitle => 'Manage stored regions';
|
||||
|
||||
@override
|
||||
String get settings_regionManagement_screenTitle => 'Region Management';
|
||||
|
||||
@override
|
||||
String get settings_regionNameHint => 'Enter region name';
|
||||
|
||||
@override
|
||||
String get settings_regionAddRegion => 'Add region';
|
||||
|
||||
@override
|
||||
String get settings_regionName => 'Region Name';
|
||||
|
||||
@override
|
||||
String get settings_regionDeleted => 'Region deleted';
|
||||
|
||||
@override
|
||||
String get settings_deleteRegion => 'Delete Region';
|
||||
|
||||
@override
|
||||
String settings_deleteRegionConfirm(String region) {
|
||||
return 'Remove \"$region\" from region list?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get settings_location => 'Lokalizacja';
|
||||
|
||||
|
|
@ -1244,6 +1273,20 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||
@override
|
||||
String get channels_hashtagHint => 'np. #zespół';
|
||||
|
||||
@override
|
||||
String channels_regionSetTo(String region) {
|
||||
return 'Region: $region';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channels_regionNotSet => 'Region: none';
|
||||
|
||||
@override
|
||||
String get channels_regionSelect_Title => 'Select a region';
|
||||
|
||||
@override
|
||||
String get channels_clearRegion => 'Clear region';
|
||||
|
||||
@override
|
||||
String get chat_noMessages => 'Brak jeszcze wiadomości';
|
||||
|
||||
|
|
|
|||
|
|
@ -338,6 +338,35 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||
String get settings_radioSettingsUpdated =>
|
||||
'Configurações de rádio atualizadas';
|
||||
|
||||
@override
|
||||
String get settings_regionSettings => 'Regions';
|
||||
|
||||
@override
|
||||
String get settings_regionSettingsSubtitle => 'Manage stored regions';
|
||||
|
||||
@override
|
||||
String get settings_regionManagement_screenTitle => 'Region Management';
|
||||
|
||||
@override
|
||||
String get settings_regionNameHint => 'Enter region name';
|
||||
|
||||
@override
|
||||
String get settings_regionAddRegion => 'Add region';
|
||||
|
||||
@override
|
||||
String get settings_regionName => 'Region Name';
|
||||
|
||||
@override
|
||||
String get settings_regionDeleted => 'Region deleted';
|
||||
|
||||
@override
|
||||
String get settings_deleteRegion => 'Delete Region';
|
||||
|
||||
@override
|
||||
String settings_deleteRegionConfirm(String region) {
|
||||
return 'Remove \"$region\" from region list?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get settings_location => 'Localização';
|
||||
|
||||
|
|
@ -1235,6 +1264,20 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||
@override
|
||||
String get channels_hashtagHint => 'ex. #equipe';
|
||||
|
||||
@override
|
||||
String channels_regionSetTo(String region) {
|
||||
return 'Region: $region';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channels_regionNotSet => 'Region: none';
|
||||
|
||||
@override
|
||||
String get channels_regionSelect_Title => 'Select a region';
|
||||
|
||||
@override
|
||||
String get channels_clearRegion => 'Clear region';
|
||||
|
||||
@override
|
||||
String get chat_noMessages => 'Ainda não existem mensagens.';
|
||||
|
||||
|
|
|
|||
|
|
@ -337,6 +337,35 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||
@override
|
||||
String get settings_radioSettingsUpdated => 'Настройки радио обновлены';
|
||||
|
||||
@override
|
||||
String get settings_regionSettings => 'Regions';
|
||||
|
||||
@override
|
||||
String get settings_regionSettingsSubtitle => 'Manage stored regions';
|
||||
|
||||
@override
|
||||
String get settings_regionManagement_screenTitle => 'Region Management';
|
||||
|
||||
@override
|
||||
String get settings_regionNameHint => 'Enter region name';
|
||||
|
||||
@override
|
||||
String get settings_regionAddRegion => 'Add region';
|
||||
|
||||
@override
|
||||
String get settings_regionName => 'Region Name';
|
||||
|
||||
@override
|
||||
String get settings_regionDeleted => 'Region deleted';
|
||||
|
||||
@override
|
||||
String get settings_deleteRegion => 'Delete Region';
|
||||
|
||||
@override
|
||||
String settings_deleteRegionConfirm(String region) {
|
||||
return 'Remove \"$region\" from region list?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get settings_location => 'Позиция';
|
||||
|
||||
|
|
@ -1235,6 +1264,20 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||
@override
|
||||
String get channels_hashtagHint => 'например, #команда';
|
||||
|
||||
@override
|
||||
String channels_regionSetTo(String region) {
|
||||
return 'Region: $region';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channels_regionNotSet => 'Region: none';
|
||||
|
||||
@override
|
||||
String get channels_regionSelect_Title => 'Select a region';
|
||||
|
||||
@override
|
||||
String get channels_clearRegion => 'Clear region';
|
||||
|
||||
@override
|
||||
String get chat_noMessages => 'Сообщений пока нет';
|
||||
|
||||
|
|
|
|||
|
|
@ -336,6 +336,35 @@ class AppLocalizationsSk extends AppLocalizations {
|
|||
@override
|
||||
String get settings_radioSettingsUpdated => 'Nastavenia rádia aktualizované';
|
||||
|
||||
@override
|
||||
String get settings_regionSettings => 'Regions';
|
||||
|
||||
@override
|
||||
String get settings_regionSettingsSubtitle => 'Manage stored regions';
|
||||
|
||||
@override
|
||||
String get settings_regionManagement_screenTitle => 'Region Management';
|
||||
|
||||
@override
|
||||
String get settings_regionNameHint => 'Enter region name';
|
||||
|
||||
@override
|
||||
String get settings_regionAddRegion => 'Add region';
|
||||
|
||||
@override
|
||||
String get settings_regionName => 'Region Name';
|
||||
|
||||
@override
|
||||
String get settings_regionDeleted => 'Region deleted';
|
||||
|
||||
@override
|
||||
String get settings_deleteRegion => 'Delete Region';
|
||||
|
||||
@override
|
||||
String settings_deleteRegionConfirm(String region) {
|
||||
return 'Remove \"$region\" from region list?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get settings_location => 'Lokalita';
|
||||
|
||||
|
|
@ -1223,6 +1252,20 @@ class AppLocalizationsSk extends AppLocalizations {
|
|||
@override
|
||||
String get channels_hashtagHint => 'napr. #tím';
|
||||
|
||||
@override
|
||||
String channels_regionSetTo(String region) {
|
||||
return 'Region: $region';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channels_regionNotSet => 'Region: none';
|
||||
|
||||
@override
|
||||
String get channels_regionSelect_Title => 'Select a region';
|
||||
|
||||
@override
|
||||
String get channels_clearRegion => 'Clear region';
|
||||
|
||||
@override
|
||||
String get chat_noMessages => 'Zatiaľ žiadne správy.';
|
||||
|
||||
|
|
|
|||
|
|
@ -334,6 +334,35 @@ class AppLocalizationsSl extends AppLocalizations {
|
|||
@override
|
||||
String get settings_radioSettingsUpdated => 'Radio nastavitve posodobljene';
|
||||
|
||||
@override
|
||||
String get settings_regionSettings => 'Regions';
|
||||
|
||||
@override
|
||||
String get settings_regionSettingsSubtitle => 'Manage stored regions';
|
||||
|
||||
@override
|
||||
String get settings_regionManagement_screenTitle => 'Region Management';
|
||||
|
||||
@override
|
||||
String get settings_regionNameHint => 'Enter region name';
|
||||
|
||||
@override
|
||||
String get settings_regionAddRegion => 'Add region';
|
||||
|
||||
@override
|
||||
String get settings_regionName => 'Region Name';
|
||||
|
||||
@override
|
||||
String get settings_regionDeleted => 'Region deleted';
|
||||
|
||||
@override
|
||||
String get settings_deleteRegion => 'Delete Region';
|
||||
|
||||
@override
|
||||
String settings_deleteRegionConfirm(String region) {
|
||||
return 'Remove \"$region\" from region list?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get settings_location => 'Lokacija';
|
||||
|
||||
|
|
@ -1221,6 +1250,20 @@ class AppLocalizationsSl extends AppLocalizations {
|
|||
@override
|
||||
String get channels_hashtagHint => 'npr. #ekipa';
|
||||
|
||||
@override
|
||||
String channels_regionSetTo(String region) {
|
||||
return 'Region: $region';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channels_regionNotSet => 'Region: none';
|
||||
|
||||
@override
|
||||
String get channels_regionSelect_Title => 'Select a region';
|
||||
|
||||
@override
|
||||
String get channels_clearRegion => 'Clear region';
|
||||
|
||||
@override
|
||||
String get chat_noMessages => 'Še ni sporočil.';
|
||||
|
||||
|
|
|
|||
|
|
@ -334,6 +334,35 @@ class AppLocalizationsSv extends AppLocalizations {
|
|||
String get settings_radioSettingsUpdated =>
|
||||
'Radioinställningarna har uppdaterats';
|
||||
|
||||
@override
|
||||
String get settings_regionSettings => 'Regions';
|
||||
|
||||
@override
|
||||
String get settings_regionSettingsSubtitle => 'Manage stored regions';
|
||||
|
||||
@override
|
||||
String get settings_regionManagement_screenTitle => 'Region Management';
|
||||
|
||||
@override
|
||||
String get settings_regionNameHint => 'Enter region name';
|
||||
|
||||
@override
|
||||
String get settings_regionAddRegion => 'Add region';
|
||||
|
||||
@override
|
||||
String get settings_regionName => 'Region Name';
|
||||
|
||||
@override
|
||||
String get settings_regionDeleted => 'Region deleted';
|
||||
|
||||
@override
|
||||
String get settings_deleteRegion => 'Delete Region';
|
||||
|
||||
@override
|
||||
String settings_deleteRegionConfirm(String region) {
|
||||
return 'Remove \"$region\" from region list?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get settings_location => 'Plats';
|
||||
|
||||
|
|
@ -1214,6 +1243,20 @@ class AppLocalizationsSv extends AppLocalizations {
|
|||
@override
|
||||
String get channels_hashtagHint => 't.ex. #team';
|
||||
|
||||
@override
|
||||
String channels_regionSetTo(String region) {
|
||||
return 'Region: $region';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channels_regionNotSet => 'Region: none';
|
||||
|
||||
@override
|
||||
String get channels_regionSelect_Title => 'Select a region';
|
||||
|
||||
@override
|
||||
String get channels_clearRegion => 'Clear region';
|
||||
|
||||
@override
|
||||
String get chat_noMessages => 'Inga meddelanden ännu';
|
||||
|
||||
|
|
|
|||
|
|
@ -336,6 +336,35 @@ class AppLocalizationsUk extends AppLocalizations {
|
|||
@override
|
||||
String get settings_radioSettingsUpdated => 'Налаштування радіо оновлено';
|
||||
|
||||
@override
|
||||
String get settings_regionSettings => 'Regions';
|
||||
|
||||
@override
|
||||
String get settings_regionSettingsSubtitle => 'Manage stored regions';
|
||||
|
||||
@override
|
||||
String get settings_regionManagement_screenTitle => 'Region Management';
|
||||
|
||||
@override
|
||||
String get settings_regionNameHint => 'Enter region name';
|
||||
|
||||
@override
|
||||
String get settings_regionAddRegion => 'Add region';
|
||||
|
||||
@override
|
||||
String get settings_regionName => 'Region Name';
|
||||
|
||||
@override
|
||||
String get settings_regionDeleted => 'Region deleted';
|
||||
|
||||
@override
|
||||
String get settings_deleteRegion => 'Delete Region';
|
||||
|
||||
@override
|
||||
String settings_deleteRegionConfirm(String region) {
|
||||
return 'Remove \"$region\" from region list?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get settings_location => 'Розташування';
|
||||
|
||||
|
|
@ -1227,6 +1256,20 @@ class AppLocalizationsUk extends AppLocalizations {
|
|||
@override
|
||||
String get channels_hashtagHint => 'напр. #команда';
|
||||
|
||||
@override
|
||||
String channels_regionSetTo(String region) {
|
||||
return 'Region: $region';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channels_regionNotSet => 'Region: none';
|
||||
|
||||
@override
|
||||
String get channels_regionSelect_Title => 'Select a region';
|
||||
|
||||
@override
|
||||
String get channels_clearRegion => 'Clear region';
|
||||
|
||||
@override
|
||||
String get chat_noMessages => 'Поки немає повідомлень.';
|
||||
|
||||
|
|
|
|||
|
|
@ -320,6 +320,35 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||
@override
|
||||
String get settings_radioSettingsUpdated => '无线电设置已更新';
|
||||
|
||||
@override
|
||||
String get settings_regionSettings => 'Regions';
|
||||
|
||||
@override
|
||||
String get settings_regionSettingsSubtitle => 'Manage stored regions';
|
||||
|
||||
@override
|
||||
String get settings_regionManagement_screenTitle => 'Region Management';
|
||||
|
||||
@override
|
||||
String get settings_regionNameHint => 'Enter region name';
|
||||
|
||||
@override
|
||||
String get settings_regionAddRegion => 'Add region';
|
||||
|
||||
@override
|
||||
String get settings_regionName => 'Region Name';
|
||||
|
||||
@override
|
||||
String get settings_regionDeleted => 'Region deleted';
|
||||
|
||||
@override
|
||||
String get settings_deleteRegion => 'Delete Region';
|
||||
|
||||
@override
|
||||
String settings_deleteRegionConfirm(String region) {
|
||||
return 'Remove \"$region\" from region list?';
|
||||
}
|
||||
|
||||
@override
|
||||
String get settings_location => '位置';
|
||||
|
||||
|
|
@ -1158,6 +1187,20 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||
@override
|
||||
String get channels_hashtagHint => '例如:#团队';
|
||||
|
||||
@override
|
||||
String channels_regionSetTo(String region) {
|
||||
return 'Region: $region';
|
||||
}
|
||||
|
||||
@override
|
||||
String get channels_regionNotSet => 'Region: none';
|
||||
|
||||
@override
|
||||
String get channels_regionSelect_Title => 'Select a region';
|
||||
|
||||
@override
|
||||
String get channels_clearRegion => 'Clear region';
|
||||
|
||||
@override
|
||||
String get chat_noMessages => '暂无消息';
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@ class Channel {
|
|||
|
||||
bool get isPublicChannel => pskHex == publicChannelPsk;
|
||||
|
||||
bool get isHashtagChannel => name.startsWith('#');
|
||||
|
||||
bool get isPrivateChannel => !isPublicChannel && !isHashtagChannel;
|
||||
|
||||
static Channel? fromFrame(Uint8List frame) {
|
||||
// CHANNEL_INFO format:
|
||||
// [0] = RESP_CODE_CHANNEL_INFO (18)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:meshcore_open/screens/region_management_screen.dart';
|
||||
import 'package:meshcore_open/storage/region_store.dart';
|
||||
import 'package:meshcore_open/widgets/adaptive_app_bar_title.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../connector/meshcore_connector.dart';
|
||||
|
|
@ -50,6 +53,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||
ChannelMessage? _replyingToMessage;
|
||||
final Map<String, GlobalKey> _messageKeys = {};
|
||||
bool _isLoadingOlder = false;
|
||||
Region region = '';
|
||||
|
||||
MeshCoreConnector? _connector;
|
||||
DateTime? _lastChannelSendAt;
|
||||
|
|
@ -60,12 +64,17 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||
super.initState();
|
||||
_textFieldFocusNode.addListener(_onTextFieldFocusChange);
|
||||
_scrollController.onScrollNearTop = _loadOlderMessages;
|
||||
region = context.read<MeshCoreConnector>().getChannelRegion(
|
||||
widget.channel.index,
|
||||
);
|
||||
|
||||
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted) return;
|
||||
final connector = context.read<MeshCoreConnector>();
|
||||
final settings = context.read<AppSettingsService>().settings;
|
||||
final idx = widget.channel.index;
|
||||
final unread = connector.getUnreadCountForChannelIndex(idx);
|
||||
|
||||
ChannelMessage? anchor;
|
||||
if (settings.jumpToOldestUnread && unread > 0) {
|
||||
anchor = _findOldestUnreadChannelAnchor(
|
||||
|
|
@ -166,47 +175,81 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final connector = context.watch<MeshCoreConnector>();
|
||||
|
||||
// Determine icon and colors based on channel type
|
||||
IconData icon = Icons.lock;
|
||||
Color iconColor = Colors.blue;
|
||||
Color bgColor = Colors.blue.withValues(alpha: 0.2);
|
||||
|
||||
// TODO(clauwn): add community handling
|
||||
final isCommunityChannel = false;
|
||||
final isCommunityPublic = false;
|
||||
|
||||
if (isCommunityChannel) {
|
||||
iconColor = Colors.purple;
|
||||
bgColor = Colors.purple.withValues(alpha: 0.2);
|
||||
icon = isCommunityPublic ? Icons.groups : Icons.tag;
|
||||
} else if (widget.channel.isPublicChannel) {
|
||||
icon = Icons.public;
|
||||
iconColor = Colors.green;
|
||||
bgColor = Colors.green.withValues(alpha: 0.2);
|
||||
} else if (widget.channel.isHashtagChannel) {
|
||||
icon = Icons.tag;
|
||||
}
|
||||
|
||||
final regionHeader = region != ''
|
||||
? context.l10n.channels_regionSetTo(region)
|
||||
: context.l10n.channels_regionNotSet;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Row(
|
||||
children: [
|
||||
Icon(
|
||||
widget.channel.isPublicChannel ? Icons.public : Icons.tag,
|
||||
size: 20,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
widget.channel.name.isEmpty
|
||||
? context.l10n.channels_channelIndex(
|
||||
widget.channel.index,
|
||||
)
|
||||
: widget.channel.name,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
Consumer<MeshCoreConnector>(
|
||||
builder: (context, connector, _) {
|
||||
final unreadCount = connector
|
||||
.getUnreadCountForChannelIndex(widget.channel.index);
|
||||
final privacy = widget.channel.isPublicChannel
|
||||
? context.l10n.channels_public
|
||||
: context.l10n.channels_private;
|
||||
return Text(
|
||||
'$privacy • ${context.l10n.chat_unread(unreadCount)}',
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
title: GestureDetector(
|
||||
onTap: () => openRegionSelectDialog(widget.channel),
|
||||
child: Row(
|
||||
children: [
|
||||
CircleAvatar(
|
||||
backgroundColor: bgColor,
|
||||
child: Icon(icon, color: iconColor),
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Consumer<MeshCoreConnector>(
|
||||
builder: (context, connector, _) {
|
||||
return Text(
|
||||
widget.channel.name.isEmpty
|
||||
? context.l10n.channels_channelIndex(
|
||||
widget.channel.index,
|
||||
)
|
||||
: widget.channel.name,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
);
|
||||
},
|
||||
),
|
||||
Consumer<MeshCoreConnector>(
|
||||
builder: (context, connector, _) {
|
||||
final unreadCount = connector
|
||||
.getUnreadCountForChannelIndex(
|
||||
widget.channel.index,
|
||||
);
|
||||
return Text(
|
||||
'$regionHeader • ${context.l10n.chat_unread(unreadCount)}',
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
centerTitle: false,
|
||||
titleSpacing: 0,
|
||||
actions: [
|
||||
const RadioStatsIconButton(),
|
||||
PopupMenuButton<String>(
|
||||
|
|
@ -1341,6 +1384,117 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
|||
.map((b) => b.toRadixString(16).padLeft(2, '0').toUpperCase())
|
||||
.join(',');
|
||||
}
|
||||
|
||||
void openRegionSelectDialog(Channel channel) async {
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) => _RegionSelectDialog(channel: channel),
|
||||
);
|
||||
if (context.mounted) {
|
||||
await _connector?.loadChannelSettings();
|
||||
setState(() {
|
||||
region = _connector?.getChannelRegion(channel.index) ?? '';
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _RegionSelectDialog extends StatefulWidget {
|
||||
final Channel channel;
|
||||
|
||||
const _RegionSelectDialog({required this.channel});
|
||||
|
||||
@override
|
||||
_RegionSelectDialogState createState() => _RegionSelectDialogState();
|
||||
}
|
||||
|
||||
class _RegionSelectDialogState extends State<_RegionSelectDialog> {
|
||||
final RegionStore regionStore = RegionStore();
|
||||
|
||||
List<Region> regions = [];
|
||||
int selectedIndex = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
loadRegions();
|
||||
}
|
||||
|
||||
void loadRegions() {
|
||||
setState(() {
|
||||
regions = regionStore.loadRegions();
|
||||
Region channelRegion = context.read<MeshCoreConnector>().getChannelRegion(
|
||||
widget.channel.index,
|
||||
);
|
||||
selectedIndex = regions.indexOf(channelRegion);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Dialog(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
title: AdaptiveAppBarTitle(
|
||||
context.l10n.channels_regionSelect_Title,
|
||||
),
|
||||
centerTitle: true,
|
||||
actions: [
|
||||
IconButton(
|
||||
tooltip: context.l10n.channels_clearRegion,
|
||||
icon: const Icon(Icons.backspace_outlined),
|
||||
onPressed: () {
|
||||
context.read<MeshCoreConnector>().setChannelRegion(
|
||||
widget.channel.index,
|
||||
'',
|
||||
);
|
||||
if (context.mounted) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
tooltip: context.l10n.settings_regionSettingsSubtitle,
|
||||
icon: const Icon(Icons.settings),
|
||||
onPressed: () async {
|
||||
await pushRegionManagementScreen(context);
|
||||
loadRegions();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 15),
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: regions.length,
|
||||
itemBuilder: (context, index) => ListTile(
|
||||
title: Text(regions[index]),
|
||||
tileColor: selectedIndex == index
|
||||
? Colors.blue.withValues(alpha: 0.2)
|
||||
: null,
|
||||
onTap: () {
|
||||
context.read<MeshCoreConnector>().setChannelRegion(
|
||||
widget.channel.index,
|
||||
regions[index],
|
||||
);
|
||||
if (context.mounted) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SwipeReplyBubble extends StatefulWidget {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:meshcore_open/storage/channel_message_store.dart';
|
||||
import 'package:meshcore_open/utils/platform_info.dart';
|
||||
import 'package:meshcore_open/widgets/app_bar.dart';
|
||||
|
|
@ -380,10 +380,15 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||
isCommunityChannel && _isCommunityPublicChannel(channel, community);
|
||||
|
||||
// Determine icon and colors based on channel type
|
||||
IconData icon;
|
||||
Color iconColor;
|
||||
Color bgColor;
|
||||
String subtitle;
|
||||
IconData icon = Icons.lock;
|
||||
Color iconColor = Colors.blue;
|
||||
Color bgColor = Colors.blue.withValues(alpha: 0.2);
|
||||
String region = connector.hasChannelRegion(channel.index)
|
||||
? context.l10n.channels_regionSetTo(
|
||||
connector.getChannelRegion(channel.index),
|
||||
)
|
||||
: context.l10n.channels_regionNotSet;
|
||||
String subtitle = region;
|
||||
|
||||
if (isCommunityChannel) {
|
||||
// Community channel styling
|
||||
|
|
@ -402,17 +407,8 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
|||
icon = Icons.public;
|
||||
iconColor = Colors.green;
|
||||
bgColor = Colors.green.withValues(alpha: 0.2);
|
||||
subtitle = context.l10n.channels_publicChannel;
|
||||
} else if (channel.name.startsWith('#')) {
|
||||
} else if (channel.isHashtagChannel) {
|
||||
icon = Icons.tag;
|
||||
iconColor = Colors.blue;
|
||||
bgColor = Colors.blue.withValues(alpha: 0.2);
|
||||
subtitle = context.l10n.channels_hashtagChannel;
|
||||
} else {
|
||||
icon = Icons.lock;
|
||||
iconColor = Colors.blue;
|
||||
bgColor = Colors.blue.withValues(alpha: 0.2);
|
||||
subtitle = context.l10n.channels_privateChannel;
|
||||
}
|
||||
|
||||
return Card(
|
||||
|
|
|
|||
160
lib/screens/region_management_screen.dart
Normal file
160
lib/screens/region_management_screen.dart
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:meshcore_open/connector/meshcore_connector.dart';
|
||||
import 'package:meshcore_open/l10n/l10n.dart';
|
||||
import 'package:meshcore_open/storage/region_store.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
Future<void> pushRegionManagementScreen(BuildContext context) {
|
||||
return Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute<void>(
|
||||
builder: (context) => const RegionManagementScreen(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class RegionManagementScreen extends StatefulWidget {
|
||||
const RegionManagementScreen({super.key});
|
||||
|
||||
@override
|
||||
State<RegionManagementScreen> createState() => _RegionManagementScreenState();
|
||||
}
|
||||
|
||||
class _RegionManagementScreenState extends State<RegionManagementScreen> {
|
||||
final RegionStore _regionStore = RegionStore();
|
||||
List<Region> _regions = [];
|
||||
|
||||
String region = '';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final connector = context.read<MeshCoreConnector>();
|
||||
_regionStore.setPublicKeyHex = connector.selfPublicKeyHex;
|
||||
_loadRegions();
|
||||
}
|
||||
|
||||
void _loadRegions() {
|
||||
context.read<MeshCoreConnector>().loadChannelSettings();
|
||||
|
||||
final regions = _regionStore.loadRegions();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_regions = regions;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(l10n.settings_regionManagement_screenTitle),
|
||||
centerTitle: true,
|
||||
actions: [
|
||||
IconButton(
|
||||
tooltip: l10n.settings_regionAddRegion,
|
||||
icon: const Icon(Icons.add),
|
||||
onPressed: () => _showAddRegionDialog(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: ListView.builder(
|
||||
padding: const EdgeInsets.only(left: 16, right: 16, top: 8, bottom: 88),
|
||||
itemCount: _regions.length,
|
||||
itemBuilder: (context, index) {
|
||||
final region = _regions[index];
|
||||
return _buildRegionTile(context, region);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showAddRegionDialog(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
final controller = TextEditingController(text: region);
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text(l10n.settings_regionName),
|
||||
content: TextField(
|
||||
controller: controller,
|
||||
autofocus: true,
|
||||
textInputAction: TextInputAction.send,
|
||||
onSubmitted: (_) => _handleAddRegion(controller.text, context),
|
||||
decoration: InputDecoration(
|
||||
hintText: l10n.settings_regionNameHint,
|
||||
border: const OutlineInputBorder(),
|
||||
),
|
||||
inputFormatters: <TextInputFormatter>[
|
||||
FilteringTextInputFormatter.allow(RegExp("[a-z0-9-]")),
|
||||
],
|
||||
maxLength: 30,
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(l10n.common_cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => _handleAddRegion(controller.text, context),
|
||||
child: Text(l10n.common_add),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleAddRegion(Region region, BuildContext context) {
|
||||
Navigator.pop(context);
|
||||
_regionStore.addRegion(region);
|
||||
_loadRegions();
|
||||
}
|
||||
|
||||
Widget _buildRegionTile(BuildContext context, Region region) {
|
||||
return Card(
|
||||
key: ValueKey(region),
|
||||
child: ListTile(
|
||||
dense: false,
|
||||
title: Text(region),
|
||||
trailing: IconButton(
|
||||
icon: Icon(Icons.delete_outline),
|
||||
onPressed: () => _confirmDelete(context, region),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _confirmDelete(BuildContext context, Region region) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (dialogContext) => AlertDialog(
|
||||
title: Text(context.l10n.settings_deleteRegion),
|
||||
content: Text(context.l10n.settings_deleteRegionConfirm(region)),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(dialogContext),
|
||||
child: Text(context.l10n.common_cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(dialogContext);
|
||||
await _regionStore.removeRegion(region);
|
||||
_loadRegions();
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.settings_regionDeleted)),
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
context.l10n.common_delete,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ import 'app_settings_screen.dart';
|
|||
import 'app_debug_log_screen.dart';
|
||||
import 'ble_debug_log_screen.dart';
|
||||
import '../widgets/radio_stats_entry.dart';
|
||||
import 'region_management_screen.dart';
|
||||
|
||||
/// Convert device coding-rate value (1-4 on some firmware, 5-8 on others)
|
||||
/// to the UI enum range (always 5-8).
|
||||
|
|
@ -287,6 +288,14 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||
onTap: () => _showRadioSettings(context, connector),
|
||||
),
|
||||
const Divider(height: 1),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.landscape),
|
||||
title: Text(l10n.settings_regionSettings),
|
||||
subtitle: Text(l10n.settings_regionSettingsSubtitle),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () => pushRegionManagementScreen(context),
|
||||
),
|
||||
const Divider(height: 1),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.sensors_outlined),
|
||||
title: Text(l10n.radioStats_settingsTile),
|
||||
|
|
|
|||
|
|
@ -176,6 +176,8 @@ class BleDebugLogService extends ChangeNotifier {
|
|||
return 'CMD_SET_CUSTOM_VAR';
|
||||
case cmdSendTracePath:
|
||||
return 'CMD_SEND_TRACE_PATH';
|
||||
case cmdSetFloodScope:
|
||||
return 'CMD_SET_FLOOD_SCOPE';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
39
lib/storage/channel_region_store.dart
Normal file
39
lib/storage/channel_region_store.dart
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import '../utils/app_logger.dart';
|
||||
import 'prefs_manager.dart';
|
||||
|
||||
class ChannelRegionStore {
|
||||
static const String _keyPrefix = 'channel_region_';
|
||||
|
||||
String publicKeyHex = '';
|
||||
set setPublicKeyHex(String value) =>
|
||||
publicKeyHex = value.length >= 10 ? value.substring(0, 10) : '';
|
||||
|
||||
String get keyFor => '$_keyPrefix$publicKeyHex';
|
||||
|
||||
Future<String> loadRegion(int channelIndex) async {
|
||||
if (publicKeyHex.isEmpty) {
|
||||
appLogger.warn(
|
||||
'Public key hex is not set. Cannot load channel settings.',
|
||||
);
|
||||
return '';
|
||||
}
|
||||
final prefs = PrefsManager.instance;
|
||||
final key = '$keyFor$channelIndex';
|
||||
String? region = prefs.getString(key);
|
||||
return region ?? '';
|
||||
}
|
||||
|
||||
Future<String> saveRegion(int channelIndex, String region) async {
|
||||
if (publicKeyHex.isEmpty) {
|
||||
appLogger.warn(
|
||||
'Public key hex is not set. Cannot save channel settings.',
|
||||
);
|
||||
return '';
|
||||
}
|
||||
|
||||
final prefs = PrefsManager.instance;
|
||||
final key = '$keyFor$channelIndex';
|
||||
await prefs.setString(key, region);
|
||||
return region;
|
||||
}
|
||||
}
|
||||
53
lib/storage/region_store.dart
Normal file
53
lib/storage/region_store.dart
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import 'package:meshcore_open/storage/channel_region_store.dart';
|
||||
import 'package:meshcore_open/storage/channel_store.dart';
|
||||
|
||||
import 'prefs_manager.dart';
|
||||
|
||||
typedef Region = String;
|
||||
|
||||
class RegionStore {
|
||||
static const String key = 'regions';
|
||||
String publicKeyHex = '';
|
||||
set setPublicKeyHex(String value) =>
|
||||
publicKeyHex = value.length >= 10 ? value.substring(0, 10) : '';
|
||||
|
||||
List<Region> loadRegions() {
|
||||
final prefs = PrefsManager.instance;
|
||||
List<Region>? region = prefs.getStringList(key);
|
||||
return region ?? [];
|
||||
}
|
||||
|
||||
void saveRegions(List<Region> regions) {
|
||||
final prefs = PrefsManager.instance;
|
||||
|
||||
var distinctRegions = [
|
||||
...{...regions},
|
||||
];
|
||||
|
||||
distinctRegions.sort();
|
||||
prefs.setStringList(key, distinctRegions);
|
||||
}
|
||||
|
||||
void addRegion(Region region) {
|
||||
final regions = loadRegions();
|
||||
regions.add(region);
|
||||
saveRegions(regions);
|
||||
}
|
||||
|
||||
Future<void> removeRegion(Region region) async {
|
||||
final regions = loadRegions();
|
||||
final channelStore = ChannelStore();
|
||||
final channelRegionStore = ChannelRegionStore();
|
||||
channelStore.setPublicKeyHex = publicKeyHex;
|
||||
channelRegionStore.setPublicKeyHex = publicKeyHex;
|
||||
|
||||
for (var channel in await channelStore.loadChannels()) {
|
||||
var channelRegion = await channelRegionStore.loadRegion(channel.index);
|
||||
if (channelRegion == region) {
|
||||
channelRegionStore.saveRegion(channel.index, '');
|
||||
}
|
||||
}
|
||||
regions.remove(region);
|
||||
saveRegions(regions);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,18 @@
|
|||
{
|
||||
"bg": [
|
||||
"settings_regionSettings",
|
||||
"settings_regionSettingsSubtitle",
|
||||
"settings_regionManagement_screenTitle",
|
||||
"settings_regionNameHint",
|
||||
"settings_regionAddRegion",
|
||||
"settings_regionName",
|
||||
"settings_regionDeleted",
|
||||
"settings_deleteRegion",
|
||||
"settings_deleteRegionConfirm",
|
||||
"channels_regionSetTo",
|
||||
"channels_regionNotSet",
|
||||
"channels_regionSelect_Title",
|
||||
"channels_clearRegion",
|
||||
"chat_sendMessage"
|
||||
],
|
||||
|
||||
|
|
@ -8,62 +21,257 @@
|
|||
],
|
||||
|
||||
"es": [
|
||||
"settings_regionSettings",
|
||||
"settings_regionSettingsSubtitle",
|
||||
"settings_regionManagement_screenTitle",
|
||||
"settings_regionNameHint",
|
||||
"settings_regionAddRegion",
|
||||
"settings_regionName",
|
||||
"settings_regionDeleted",
|
||||
"settings_deleteRegion",
|
||||
"settings_deleteRegionConfirm",
|
||||
"channels_regionSetTo",
|
||||
"channels_regionNotSet",
|
||||
"channels_regionSelect_Title",
|
||||
"channels_clearRegion",
|
||||
"chat_sendMessage"
|
||||
],
|
||||
|
||||
"fr": [
|
||||
"settings_regionSettings",
|
||||
"settings_regionSettingsSubtitle",
|
||||
"settings_regionManagement_screenTitle",
|
||||
"settings_regionNameHint",
|
||||
"settings_regionAddRegion",
|
||||
"settings_regionName",
|
||||
"settings_regionDeleted",
|
||||
"settings_deleteRegion",
|
||||
"settings_deleteRegionConfirm",
|
||||
"channels_regionSetTo",
|
||||
"channels_regionNotSet",
|
||||
"channels_regionSelect_Title",
|
||||
"channels_clearRegion",
|
||||
"chat_sendMessage"
|
||||
],
|
||||
|
||||
"hu": [
|
||||
"settings_regionSettings",
|
||||
"settings_regionSettingsSubtitle",
|
||||
"settings_regionManagement_screenTitle",
|
||||
"settings_regionNameHint",
|
||||
"settings_regionAddRegion",
|
||||
"settings_regionName",
|
||||
"settings_regionDeleted",
|
||||
"settings_deleteRegion",
|
||||
"settings_deleteRegionConfirm",
|
||||
"channels_regionSetTo",
|
||||
"channels_regionNotSet",
|
||||
"channels_regionSelect_Title",
|
||||
"channels_clearRegion",
|
||||
"chat_sendMessage"
|
||||
],
|
||||
|
||||
"it": [
|
||||
"settings_regionSettings",
|
||||
"settings_regionSettingsSubtitle",
|
||||
"settings_regionManagement_screenTitle",
|
||||
"settings_regionNameHint",
|
||||
"settings_regionAddRegion",
|
||||
"settings_regionName",
|
||||
"settings_regionDeleted",
|
||||
"settings_deleteRegion",
|
||||
"settings_deleteRegionConfirm",
|
||||
"channels_regionSetTo",
|
||||
"channels_regionNotSet",
|
||||
"channels_regionSelect_Title",
|
||||
"channels_clearRegion",
|
||||
"chat_sendMessage"
|
||||
],
|
||||
|
||||
"ja": [
|
||||
"settings_regionSettings",
|
||||
"settings_regionSettingsSubtitle",
|
||||
"settings_regionManagement_screenTitle",
|
||||
"settings_regionNameHint",
|
||||
"settings_regionAddRegion",
|
||||
"settings_regionName",
|
||||
"settings_regionDeleted",
|
||||
"settings_deleteRegion",
|
||||
"settings_deleteRegionConfirm",
|
||||
"channels_regionSetTo",
|
||||
"channels_regionNotSet",
|
||||
"channels_regionSelect_Title",
|
||||
"channels_clearRegion",
|
||||
"chat_sendMessage"
|
||||
],
|
||||
|
||||
"ko": [
|
||||
"settings_regionSettings",
|
||||
"settings_regionSettingsSubtitle",
|
||||
"settings_regionManagement_screenTitle",
|
||||
"settings_regionNameHint",
|
||||
"settings_regionAddRegion",
|
||||
"settings_regionName",
|
||||
"settings_regionDeleted",
|
||||
"settings_deleteRegion",
|
||||
"settings_deleteRegionConfirm",
|
||||
"channels_regionSetTo",
|
||||
"channels_regionNotSet",
|
||||
"channels_regionSelect_Title",
|
||||
"channels_clearRegion",
|
||||
"chat_sendMessage"
|
||||
],
|
||||
|
||||
"nl": [
|
||||
"settings_regionSettings",
|
||||
"settings_regionSettingsSubtitle",
|
||||
"settings_regionManagement_screenTitle",
|
||||
"settings_regionNameHint",
|
||||
"settings_regionAddRegion",
|
||||
"settings_regionName",
|
||||
"settings_regionDeleted",
|
||||
"settings_deleteRegion",
|
||||
"settings_deleteRegionConfirm",
|
||||
"channels_regionSetTo",
|
||||
"channels_regionNotSet",
|
||||
"channels_regionSelect_Title",
|
||||
"channels_clearRegion",
|
||||
"chat_sendMessage"
|
||||
],
|
||||
|
||||
"pl": [
|
||||
"settings_regionSettings",
|
||||
"settings_regionSettingsSubtitle",
|
||||
"settings_regionManagement_screenTitle",
|
||||
"settings_regionNameHint",
|
||||
"settings_regionAddRegion",
|
||||
"settings_regionName",
|
||||
"settings_regionDeleted",
|
||||
"settings_deleteRegion",
|
||||
"settings_deleteRegionConfirm",
|
||||
"channels_regionSetTo",
|
||||
"channels_regionNotSet",
|
||||
"channels_regionSelect_Title",
|
||||
"channels_clearRegion",
|
||||
"chat_sendMessage"
|
||||
],
|
||||
|
||||
"pt": [
|
||||
"settings_regionSettings",
|
||||
"settings_regionSettingsSubtitle",
|
||||
"settings_regionManagement_screenTitle",
|
||||
"settings_regionNameHint",
|
||||
"settings_regionAddRegion",
|
||||
"settings_regionName",
|
||||
"settings_regionDeleted",
|
||||
"settings_deleteRegion",
|
||||
"settings_deleteRegionConfirm",
|
||||
"channels_regionSetTo",
|
||||
"channels_regionNotSet",
|
||||
"channels_regionSelect_Title",
|
||||
"channels_clearRegion",
|
||||
"chat_sendMessage"
|
||||
],
|
||||
|
||||
"ru": [
|
||||
"settings_regionSettings",
|
||||
"settings_regionSettingsSubtitle",
|
||||
"settings_regionManagement_screenTitle",
|
||||
"settings_regionNameHint",
|
||||
"settings_regionAddRegion",
|
||||
"settings_regionName",
|
||||
"settings_regionDeleted",
|
||||
"settings_deleteRegion",
|
||||
"settings_deleteRegionConfirm",
|
||||
"channels_regionSetTo",
|
||||
"channels_regionNotSet",
|
||||
"channels_regionSelect_Title",
|
||||
"channels_clearRegion",
|
||||
"chat_sendMessage"
|
||||
],
|
||||
|
||||
"sk": [
|
||||
"settings_regionSettings",
|
||||
"settings_regionSettingsSubtitle",
|
||||
"settings_regionManagement_screenTitle",
|
||||
"settings_regionNameHint",
|
||||
"settings_regionAddRegion",
|
||||
"settings_regionName",
|
||||
"settings_regionDeleted",
|
||||
"settings_deleteRegion",
|
||||
"settings_deleteRegionConfirm",
|
||||
"channels_regionSetTo",
|
||||
"channels_regionNotSet",
|
||||
"channels_regionSelect_Title",
|
||||
"channels_clearRegion",
|
||||
"chat_sendMessage"
|
||||
],
|
||||
|
||||
"sl": [
|
||||
"settings_regionSettings",
|
||||
"settings_regionSettingsSubtitle",
|
||||
"settings_regionManagement_screenTitle",
|
||||
"settings_regionNameHint",
|
||||
"settings_regionAddRegion",
|
||||
"settings_regionName",
|
||||
"settings_regionDeleted",
|
||||
"settings_deleteRegion",
|
||||
"settings_deleteRegionConfirm",
|
||||
"channels_regionSetTo",
|
||||
"channels_regionNotSet",
|
||||
"channels_regionSelect_Title",
|
||||
"channels_clearRegion",
|
||||
"chat_sendMessage"
|
||||
],
|
||||
|
||||
"sv": [
|
||||
"settings_regionSettings",
|
||||
"settings_regionSettingsSubtitle",
|
||||
"settings_regionManagement_screenTitle",
|
||||
"settings_regionNameHint",
|
||||
"settings_regionAddRegion",
|
||||
"settings_regionName",
|
||||
"settings_regionDeleted",
|
||||
"settings_deleteRegion",
|
||||
"settings_deleteRegionConfirm",
|
||||
"channels_regionSetTo",
|
||||
"channels_regionNotSet",
|
||||
"channels_regionSelect_Title",
|
||||
"channels_clearRegion",
|
||||
"chat_sendMessage"
|
||||
],
|
||||
|
||||
"uk": [
|
||||
"settings_regionSettings",
|
||||
"settings_regionSettingsSubtitle",
|
||||
"settings_regionManagement_screenTitle",
|
||||
"settings_regionNameHint",
|
||||
"settings_regionAddRegion",
|
||||
"settings_regionName",
|
||||
"settings_regionDeleted",
|
||||
"settings_deleteRegion",
|
||||
"settings_deleteRegionConfirm",
|
||||
"channels_regionSetTo",
|
||||
"channels_regionNotSet",
|
||||
"channels_regionSelect_Title",
|
||||
"channels_clearRegion",
|
||||
"chat_sendMessage"
|
||||
],
|
||||
|
||||
"zh": [
|
||||
"settings_regionSettings",
|
||||
"settings_regionSettingsSubtitle",
|
||||
"settings_regionManagement_screenTitle",
|
||||
"settings_regionNameHint",
|
||||
"settings_regionAddRegion",
|
||||
"settings_regionName",
|
||||
"settings_regionDeleted",
|
||||
"settings_deleteRegion",
|
||||
"settings_deleteRegionConfirm",
|
||||
"channels_regionSetTo",
|
||||
"channels_regionNotSet",
|
||||
"channels_regionSelect_Title",
|
||||
"channels_clearRegion",
|
||||
"chat_sendMessage"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue