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:
Janez T
2025-10-15 10:30:21 +02:00
parent 8af96c3fec
commit e831612a1a
15 changed files with 3261 additions and 3055 deletions

View 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;
}

View 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(':');
}
}