Refactor contact filtering and improve localization strings; enhance path trace handling

This commit is contained in:
Winston Lowe
2026-03-24 17:45:54 -07:00
parent 411cd3f8d2
commit bde9a029c1
4 changed files with 59 additions and 35 deletions
+4 -1
View File
@@ -323,8 +323,11 @@ class MeshCoreConnector extends ChangeNotifier {
List<Contact> get allContacts => List.unmodifiable([ List<Contact> get allContacts => List.unmodifiable([
..._contacts, ..._contacts,
..._discoveredContacts.where((c) => !c.isActive), ..._discoveredContacts.where(
(c) => !c.isActive && c.publicKeyHex != selfPublicKeyHex,
),
]); ]);
List<Contact> get discoveredContacts { List<Contact> get discoveredContacts {
return List.unmodifiable(_discoveredContacts); return List.unmodifiable(_discoveredContacts);
} }
+7 -12
View File
@@ -1240,9 +1240,7 @@ class _ContactsScreenState extends State<ContactsScreen>
if (isRepeater) ...[ if (isRepeater) ...[
ListTile( ListTile(
leading: const Icon(Icons.radar, color: Colors.green), leading: const Icon(Icons.radar, color: Colors.green),
title: contact.pathBytesForDisplay.isNotEmpty title: Text(context.l10n.contacts_ping),
? Text(context.l10n.contacts_pathTrace)
: Text(context.l10n.contacts_ping),
onTap: () { onTap: () {
final hw = context final hw = context
.read<MeshCoreConnector>() .read<MeshCoreConnector>()
@@ -1251,11 +1249,8 @@ class _ContactsScreenState extends State<ContactsScreen>
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => PathTraceMapScreen( builder: (context) => PathTraceMapScreen(
title: contact.pathBytesForDisplay.isNotEmpty title: context.l10n.contacts_repeaterPing,
? context.l10n.contacts_repeaterPathTrace path: Uint8List.fromList([contact.publicKey.first]),
: context.l10n.contacts_repeaterPing,
path: contact.pathBytesForDisplay,
flipPathAround: true,
targetContact: contact, targetContact: contact,
pathHashByteWidth: hw, pathHashByteWidth: hw,
), ),
@@ -1274,9 +1269,7 @@ class _ContactsScreenState extends State<ContactsScreen>
] else if (isRoom) ...[ ] else if (isRoom) ...[
ListTile( ListTile(
leading: const Icon(Icons.radar, color: Colors.green), leading: const Icon(Icons.radar, color: Colors.green),
title: contact.pathLength > 0 title: Text(context.l10n.contacts_pathTrace),
? Text(context.l10n.contacts_pathTrace)
: Text(context.l10n.contacts_ping),
onTap: () { onTap: () {
final hw = context final hw = context
.read<MeshCoreConnector>() .read<MeshCoreConnector>()
@@ -1288,7 +1281,9 @@ class _ContactsScreenState extends State<ContactsScreen>
title: contact.pathBytesForDisplay.isNotEmpty title: contact.pathBytesForDisplay.isNotEmpty
? context.l10n.contacts_roomPathTrace ? context.l10n.contacts_roomPathTrace
: context.l10n.contacts_roomPing, : context.l10n.contacts_roomPing,
path: contact.pathBytesForDisplay, path: contact.pathBytesForDisplay.isNotEmpty
? contact.pathBytesForDisplay
: Uint8List.fromList([contact.publicKey.first]),
flipPathAround: contact.pathBytesForDisplay.isNotEmpty, flipPathAround: contact.pathBytesForDisplay.isNotEmpty,
targetContact: contact, targetContact: contact,
pathHashByteWidth: hw, pathHashByteWidth: hw,
+32 -12
View File
@@ -64,6 +64,7 @@ class _MapScreenState extends State<MapScreen> {
bool _hasInitializedMap = false; bool _hasInitializedMap = false;
bool _removedMarkersLoaded = false; bool _removedMarkersLoaded = false;
final List<int> _pathTrace = []; final List<int> _pathTrace = [];
final List<Contact> _pathTraceContacts = [];
final List<LatLng> _points = []; final List<LatLng> _points = [];
final List<Polyline> _polylines = []; final List<Polyline> _polylines = [];
bool _legendExpanded = false; bool _legendExpanded = false;
@@ -488,11 +489,10 @@ class _MapScreenState extends State<MapScreen> {
), ),
), ),
), ),
if (!_isBuildingPathTrace) ..._buildGuessedMarker(
..._buildGuessedMarker( guessedLocations,
guessedLocations, showLabels: _showNodeLabels,
showLabels: _showNodeLabels, ),
),
..._buildMarkers( ..._buildMarkers(
contactsWithLocation, contactsWithLocation,
settings, settings,
@@ -788,17 +788,26 @@ class _MapScreenState extends State<MapScreen> {
final markers = <Marker>[]; final markers = <Marker>[];
for (final guess in guessed) { for (final guess in guessed) {
if (guess.contact.type == advTypeChat && _isBuildingPathTrace) {
continue;
}
final color = _getNodeColor(guess.contact.type); final color = _getNodeColor(guess.contact.type);
final marker = Marker( final marker = Marker(
point: guess.position, point: guess.position,
width: 35, width: 35,
height: 35, height: 35,
child: GestureDetector( child: GestureDetector(
onTap: () => _showNodeInfo( onLongPress: () => _isBuildingPathTrace
context, ? _showNodeInfo(context, guess.contact)
guess.contact, : null,
guessedPosition: guess.position, onTap: () => _isBuildingPathTrace
), ? _addToPath(context, guess.contact, position: guess.position)
: _showNodeInfo(
context,
guess.contact,
guessedPosition: guess.position,
),
child: Container( child: Container(
padding: const EdgeInsets.all(4), padding: const EdgeInsets.all(4),
decoration: BoxDecoration( decoration: BoxDecoration(
@@ -2121,12 +2130,18 @@ class _MapScreenState extends State<MapScreen> {
} }
} }
void _addToPath(BuildContext context, Contact contact) { void _addToPath(BuildContext context, Contact contact, {LatLng? position}) {
setState(() { setState(() {
_pathTrace.add( _pathTrace.add(
contact.publicKey[0], contact.publicKey[0],
); // Add first 16 bytes of public key to path trace ); // Add first 16 bytes of public key to path trace
_points.add(LatLng(contact.latitude!, contact.longitude!)); _pathTraceContacts.add(
contact.copyWith(
latitude: position?.latitude ?? contact.latitude,
longitude: position?.longitude ?? contact.longitude,
),
); // Add contact to path trace contacts
_points.add(position ?? LatLng(contact.latitude!, contact.longitude!));
}); });
} }
@@ -2134,6 +2149,7 @@ class _MapScreenState extends State<MapScreen> {
setState(() { setState(() {
_isBuildingPathTrace = true; _isBuildingPathTrace = true;
_pathTrace.clear(); _pathTrace.clear();
_pathTraceContacts.clear();
_points.clear(); _points.clear();
_polylines.clear(); _polylines.clear();
_points.add(position); _points.add(position);
@@ -2143,6 +2159,9 @@ class _MapScreenState extends State<MapScreen> {
void _removePath() { void _removePath() {
setState(() { setState(() {
_pathTrace.removeLast(); // Remove last node from path trace _pathTrace.removeLast(); // Remove last node from path trace
_pathTraceContacts.remove(
_pathTrace.last,
); // Remove last contact from path trace
_points.removeLast(); // Remove last point from points list _points.removeLast(); // Remove last point from points list
_polylines.clear(); // Clear polylines _polylines.clear(); // Clear polylines
}); });
@@ -2201,6 +2220,7 @@ class _MapScreenState extends State<MapScreen> {
title: l10n.contacts_pathTrace, title: l10n.contacts_pathTrace,
path: Uint8List.fromList(_pathTrace), path: Uint8List.fromList(_pathTrace),
pathHashByteWidth: hashW, pathHashByteWidth: hashW,
pathContacts: _pathTraceContacts,
), ),
), ),
); );
+16 -10
View File
@@ -56,6 +56,7 @@ class PathTraceMapScreen extends StatefulWidget {
final bool reversePathAround; final bool reversePathAround;
final Contact? targetContact; final Contact? targetContact;
final int pathHashByteWidth; final int pathHashByteWidth;
final List<Contact>? pathContacts;
const PathTraceMapScreen({ const PathTraceMapScreen({
super.key, super.key,
@@ -66,6 +67,7 @@ class PathTraceMapScreen extends StatefulWidget {
this.reversePathAround = false, this.reversePathAround = false,
this.targetContact, this.targetContact,
this.pathHashByteWidth = pathHashSize, this.pathHashByteWidth = pathHashSize,
this.pathContacts,
}); });
@override @override
@@ -266,17 +268,21 @@ class _PathTraceMapScreenState extends State<PathTraceMapScreen> {
.toList(); .toList();
Map<int, Contact> pathContacts = {}; Map<int, Contact> pathContacts = {};
final contacts = connector.allContacts; if (widget.pathContacts != null) {
contacts.where((c) => c.type != advTypeChat).forEach((repeater) { pathContacts = {for (var c in widget.pathContacts!) c.publicKey[0]: c};
for (var repeaterData in pathData) { } else {
if (listEquals( final contacts = connector.allContacts;
repeater.publicKey.sublist(0, 1), contacts.where((c) => c.type != advTypeChat).forEach((repeater) {
Uint8List.fromList([repeaterData]), for (var repeaterData in pathData) {
)) { if (listEquals(
pathContacts[repeaterData] = repeater; repeater.publicKey.sublist(0, 1),
Uint8List.fromList([repeaterData]),
)) {
pathContacts[repeaterData] = repeater;
}
} }
} });
}); }
// For hops with no GPS contact, infer position from other contacts // For hops with no GPS contact, infer position from other contacts
// with known GPS that share the same last-hop byte. // with known GPS that share the same last-hop byte.