mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-12 08:50:30 +00:00
Add CompassSarList widget and refactor DetailedCompassDialog
- Introduced CompassSarList widget to display filtered SAR markers with distance and bearing information. - Refactored DetailedCompassDialog to integrate CompassSarList and CompassContactList for better organization. - Removed the previous filter dialog implementation and replaced it with a more modular CompassFilters widget. - Simplified the handling of zoom and scale updates in the compass view. - Cleaned up unused code related to previous implementations of contact and SAR marker lists.
This commit is contained in:
@@ -2,7 +2,6 @@ import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../models/device_info.dart';
|
||||
import '../models/contact.dart';
|
||||
import '../models/message.dart';
|
||||
@@ -10,6 +9,8 @@ import '../models/room_login_state.dart';
|
||||
import '../services/meshcore_ble_service.dart';
|
||||
import '../services/cayenne_lpp_parser.dart';
|
||||
import '../utils/sar_message_parser.dart';
|
||||
import 'helpers/room_login_manager.dart';
|
||||
import 'helpers/message_delivery_tracker.dart';
|
||||
|
||||
/// Connection Provider - manages MeshCore BLE connection
|
||||
class ConnectionProvider with ChangeNotifier {
|
||||
@@ -46,13 +47,12 @@ class ConnectionProvider with ChangeNotifier {
|
||||
// Message sync state
|
||||
bool _noMoreMessages = false;
|
||||
|
||||
// Room login state tracking
|
||||
final Map<String, RoomLoginState> _roomLoginStates = {};
|
||||
Map<String, RoomLoginState> get roomLoginStates => Map.unmodifiable(_roomLoginStates);
|
||||
// Helper instances
|
||||
final RoomLoginManager _roomLoginManager = RoomLoginManager();
|
||||
final MessageDeliveryTracker _messageDeliveryTracker = MessageDeliveryTracker();
|
||||
|
||||
// Track sent message IDs by ACK tag for delivery confirmation
|
||||
final Map<int, String> _ackTagToMessageId = {};
|
||||
final List<String> _pendingSentMessageIds = []; // Queue of pending message IDs
|
||||
// Expose room login states
|
||||
Map<String, RoomLoginState> get roomLoginStates => _roomLoginManager.roomLoginStates;
|
||||
|
||||
// Callbacks for other providers
|
||||
Function(Contact)? onContactReceived;
|
||||
@@ -149,15 +149,12 @@ class ConnectionProvider with ChangeNotifier {
|
||||
print(' Public key prefix: ${publicKeyPrefix.map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}');
|
||||
print(' Permissions: $permissions, Admin: $isAdmin, Tag: $tag');
|
||||
|
||||
// Update room login state
|
||||
final prefixHex = publicKeyPrefix.map((b) => b.toRadixString(16).padLeft(2, '0')).join(':');
|
||||
final hasPassword = await _hasPasswordForRoom(publicKeyPrefix);
|
||||
_roomLoginStates[prefixHex] = RoomLoginState.loggedIn(
|
||||
// Update room login state via helper
|
||||
await _roomLoginManager.handleLoginSuccess(
|
||||
publicKeyPrefix: publicKeyPrefix,
|
||||
permissions: permissions,
|
||||
isAdmin: isAdmin,
|
||||
tag: tag,
|
||||
hasPassword: hasPassword,
|
||||
);
|
||||
notifyListeners();
|
||||
|
||||
@@ -168,11 +165,9 @@ class ConnectionProvider with ChangeNotifier {
|
||||
print('📥 [Provider] Login failed to room');
|
||||
print(' Public key prefix: ${publicKeyPrefix.map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}');
|
||||
|
||||
// Update room login state to logged out
|
||||
final prefixHex = publicKeyPrefix.map((b) => b.toRadixString(16).padLeft(2, '0')).join(':');
|
||||
_roomLoginStates[prefixHex] = RoomLoginState.loggedOut(
|
||||
// Update room login state to logged out via helper
|
||||
_roomLoginManager.handleLoginFail(
|
||||
publicKeyPrefix: publicKeyPrefix,
|
||||
hasPassword: false, // Password was incorrect
|
||||
);
|
||||
notifyListeners();
|
||||
|
||||
@@ -913,6 +908,31 @@ class ConnectionProvider with ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
/// Reset routing path for a contact
|
||||
///
|
||||
/// Clears the learned path to a contact, forcing the next message to use
|
||||
/// flood routing to discover a new route. Useful when:
|
||||
/// - A mobile repeater has moved and the path is broken
|
||||
/// - You want to find a better/shorter route
|
||||
/// - Direct messages are timing out due to path issues
|
||||
///
|
||||
/// After calling this, the device will automatically fall back to flood mode
|
||||
/// for the next message to this contact, and learn a new path from the response.
|
||||
Future<void> resetPath(Uint8List contactPublicKey) async {
|
||||
if (!_bleService.isConnected) {
|
||||
_error = 'Not connected to device';
|
||||
notifyListeners();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await _bleService.resetPath(contactPublicKey);
|
||||
} catch (e) {
|
||||
_error = 'Failed to reset path: $e';
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear error message
|
||||
void clearError() {
|
||||
_error = null;
|
||||
|
||||
71
lib/providers/helpers/message_delivery_tracker.dart
Normal file
71
lib/providers/helpers/message_delivery_tracker.dart
Normal file
@@ -0,0 +1,71 @@
|
||||
/// Message delivery tracking helper
|
||||
///
|
||||
/// Manages message delivery tracking for sent messages, including:
|
||||
/// - ACK tag to message ID mapping
|
||||
/// - Pending sent message IDs queue
|
||||
/// - Message sent/delivered coordination
|
||||
class MessageDeliveryTracker {
|
||||
/// Map of ACK tag to message ID for delivery confirmation
|
||||
final Map<int, String> _ackTagToMessageId = {};
|
||||
|
||||
/// Queue of pending message IDs (FIFO)
|
||||
/// Messages must be sent sequentially for proper matching
|
||||
final List<String> _pendingSentMessageIds = [];
|
||||
|
||||
/// Track a pending message ID
|
||||
///
|
||||
/// Add message ID to pending queue. When SENT response arrives,
|
||||
/// it will be matched with this message ID (FIFO order).
|
||||
void trackPendingMessage(String messageId) {
|
||||
_pendingSentMessageIds.add(messageId);
|
||||
}
|
||||
|
||||
/// Get message ID for ACK tag and remove it from tracking
|
||||
///
|
||||
/// Called when SENT response arrives. Returns the message ID
|
||||
/// that corresponds to this ACK tag (FIFO order).
|
||||
///
|
||||
/// Returns null if no pending messages.
|
||||
String? popPendingMessageId() {
|
||||
if (_pendingSentMessageIds.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return _pendingSentMessageIds.removeAt(0);
|
||||
}
|
||||
|
||||
/// Store ACK tag to message ID mapping
|
||||
///
|
||||
/// Call this after receiving SENT response with expectedAckTag.
|
||||
/// Later, when SEND_CONFIRMED arrives with matching ackCode,
|
||||
/// you can look up the original message ID.
|
||||
void mapAckTagToMessageId(int ackTag, String messageId) {
|
||||
_ackTagToMessageId[ackTag] = messageId;
|
||||
}
|
||||
|
||||
/// Get message ID for ACK code
|
||||
///
|
||||
/// Called when SEND_CONFIRMED arrives. Returns the message ID
|
||||
/// that corresponds to this ACK code.
|
||||
///
|
||||
/// Returns null if ACK tag not found.
|
||||
String? getMessageIdForAck(int ackCode) {
|
||||
return _ackTagToMessageId[ackCode];
|
||||
}
|
||||
|
||||
/// Remove ACK tag mapping after delivery confirmed
|
||||
void removeAckTag(int ackCode) {
|
||||
_ackTagToMessageId.remove(ackCode);
|
||||
}
|
||||
|
||||
/// Clear all tracking state
|
||||
void clearTracking() {
|
||||
_ackTagToMessageId.clear();
|
||||
_pendingSentMessageIds.clear();
|
||||
}
|
||||
|
||||
/// Get count of pending messages
|
||||
int get pendingCount => _pendingSentMessageIds.length;
|
||||
|
||||
/// Get count of tracked ACK tags
|
||||
int get ackTagCount => _ackTagToMessageId.length;
|
||||
}
|
||||
85
lib/providers/helpers/room_login_manager.dart
Normal file
85
lib/providers/helpers/room_login_manager.dart
Normal file
@@ -0,0 +1,85 @@
|
||||
import 'dart:typed_data';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../../models/room_login_state.dart';
|
||||
|
||||
/// Room login state management helper
|
||||
///
|
||||
/// Manages login state tracking for room contacts, including:
|
||||
/// - Room login state per contact (Map<String, RoomLoginState>)
|
||||
/// - Password checking logic
|
||||
/// - Login success/fail state updates
|
||||
class RoomLoginManager {
|
||||
/// Map of room public key prefix (hex string) to login state
|
||||
final Map<String, RoomLoginState> _roomLoginStates = {};
|
||||
|
||||
/// Get all room login states (unmodifiable view)
|
||||
Map<String, RoomLoginState> get roomLoginStates => Map.unmodifiable(_roomLoginStates);
|
||||
|
||||
/// Get login state for a room by public key prefix
|
||||
RoomLoginState? getRoomLoginState(Uint8List publicKeyPrefix) {
|
||||
final prefixHex = _publicKeyPrefixToHex(publicKeyPrefix);
|
||||
return _roomLoginStates[prefixHex];
|
||||
}
|
||||
|
||||
/// Check if logged into a specific room
|
||||
bool isLoggedIntoRoom(Uint8List publicKeyPrefix) {
|
||||
final state = getRoomLoginState(publicKeyPrefix);
|
||||
return state?.isLoggedIn ?? false;
|
||||
}
|
||||
|
||||
/// Update room login state after successful login
|
||||
Future<void> handleLoginSuccess({
|
||||
required Uint8List publicKeyPrefix,
|
||||
required int permissions,
|
||||
required bool isAdmin,
|
||||
required int tag,
|
||||
}) async {
|
||||
final prefixHex = _publicKeyPrefixToHex(publicKeyPrefix);
|
||||
final hasPassword = await _hasPasswordForRoom(publicKeyPrefix);
|
||||
|
||||
_roomLoginStates[prefixHex] = RoomLoginState.loggedIn(
|
||||
publicKeyPrefix: publicKeyPrefix,
|
||||
permissions: permissions,
|
||||
isAdmin: isAdmin,
|
||||
tag: tag,
|
||||
hasPassword: hasPassword,
|
||||
);
|
||||
}
|
||||
|
||||
/// Update room login state after failed login
|
||||
void handleLoginFail({
|
||||
required Uint8List publicKeyPrefix,
|
||||
}) {
|
||||
final prefixHex = _publicKeyPrefixToHex(publicKeyPrefix);
|
||||
|
||||
_roomLoginStates[prefixHex] = RoomLoginState.loggedOut(
|
||||
publicKeyPrefix: publicKeyPrefix,
|
||||
hasPassword: false, // Password was incorrect
|
||||
);
|
||||
}
|
||||
|
||||
/// Clear all room login states (call on disconnect)
|
||||
void clearRoomLoginStates() {
|
||||
_roomLoginStates.clear();
|
||||
}
|
||||
|
||||
/// Check if a password exists for a room (by public key prefix)
|
||||
Future<bool> _hasPasswordForRoom(Uint8List publicKeyPrefix) async {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
// Convert prefix to hex string for storage key
|
||||
final prefixHex = _publicKeyPrefixToHex(publicKeyPrefix);
|
||||
final roomKey = 'room_password_$prefixHex';
|
||||
return prefs.getString(roomKey) != null;
|
||||
} catch (e) {
|
||||
debugPrint('Error checking password for room: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert public key prefix to hex string (colon-separated)
|
||||
String _publicKeyPrefixToHex(Uint8List publicKeyPrefix) {
|
||||
return publicKeyPrefix.map((b) => b.toRadixString(16).padLeft(2, '0')).join(':');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user