mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-06-22 02:14:28 +10: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:
@@ -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]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user