From 17a9db0f0e5d8935279337551249d365d1f94264 Mon Sep 17 00:00:00 2001 From: Winston Lowe Date: Wed, 18 Feb 2026 09:28:25 -0800 Subject: [PATCH] Refactor ranking calculation for direct repeaters and update path handling in channel message screens --- lib/connector/meshcore_connector.dart | 3 +- lib/screens/channel_chat_screen.dart | 3 +- lib/screens/channel_message_path_screen.dart | 38 +++++++++++++++----- lib/screens/map_screen.dart | 6 ++-- lib/screens/path_trace_map.dart | 8 +++-- 5 files changed, 41 insertions(+), 17 deletions(-) diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index a46305ea..d04ca706 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -59,13 +59,12 @@ class DirectRepeater { return -1; // Stale repeaters get lowest rank } // Higher SNR gets higher rank and recency within maxAgeMinutes breaks ties. - final snrOffset = snr + 31.75; final ageMs = DateTime.now().millisecondsSinceEpoch - lastUpdated.millisecondsSinceEpoch; final maxAgeMs = maxAgeMinutes * 60 * 1000; final recencyScore = (maxAgeMs - ageMs).clamp(0, maxAgeMs); - return (snrOffset * 1000).round() + recencyScore; + return (snr * snr).round() + recencyScore; } bool isStale() { diff --git a/lib/screens/channel_chat_screen.dart b/lib/screens/channel_chat_screen.dart index c82356d2..021ad7d8 100644 --- a/lib/screens/channel_chat_screen.dart +++ b/lib/screens/channel_chat_screen.dart @@ -901,7 +901,8 @@ class _ChannelChatScreenState extends State { Navigator.push( context, MaterialPageRoute( - builder: (context) => ChannelMessagePathScreen(message: message), + builder: (context) => + ChannelMessagePathScreen(message: message, channelMessage: true), ), ); } diff --git a/lib/screens/channel_message_path_screen.dart b/lib/screens/channel_message_path_screen.dart index 1b0544ca..d4723dfd 100644 --- a/lib/screens/channel_message_path_screen.dart +++ b/lib/screens/channel_message_path_screen.dart @@ -17,18 +17,27 @@ import '../models/contact.dart'; class ChannelMessagePathScreen extends StatelessWidget { final ChannelMessage message; - - const ChannelMessagePathScreen({super.key, required this.message}); + final bool channelMessage; + const ChannelMessagePathScreen({ + super.key, + required this.message, + this.channelMessage = false, + }); @override Widget build(BuildContext context) { return Consumer( builder: (context, connector, _) { final l10n = context.l10n; - final primaryPath = _selectPrimaryPath( + final primaryPathTmp = _selectPrimaryPath( message.pathBytes, message.pathVariants, ); + + final primaryPath = !channelMessage && !message.isOutgoing + ? Uint8List.fromList(primaryPathTmp.reversed.toList()) + : primaryPathTmp; + final hops = _buildPathHops(primaryPath, connector.contacts, l10n); final hasHopDetails = primaryPath.isNotEmpty; final observedLabel = _formatObservedHops( @@ -37,7 +46,6 @@ class ChannelMessagePathScreen extends StatelessWidget { l10n, ); final extraPaths = _otherPaths(primaryPath, message.pathVariants); - return Scaffold( appBar: AppBar( title: Text(l10n.channelPath_title), @@ -50,9 +58,9 @@ class ChannelMessagePathScreen extends StatelessWidget { MaterialPageRoute( builder: (context) => PathTraceMapScreen( title: context.l10n.contacts_repeaterPathTrace, - path: Uint8List.fromList(primaryPath), + path: primaryPath, flipPathRound: true, - reversePathRound: true, + reversePathRound: !message.isOutgoing, ), ), ), @@ -62,7 +70,7 @@ class ChannelMessagePathScreen extends StatelessWidget { tooltip: l10n.channelPath_viewMap, onPressed: hasHopDetails ? () { - _openPathMap(context); + _openPathMap(context, channelMessage: channelMessage); } : null, ), @@ -248,13 +256,18 @@ class ChannelMessagePathScreen extends StatelessWidget { ); } - void _openPathMap(BuildContext context, {Uint8List? initialPath}) { + void _openPathMap( + BuildContext context, { + Uint8List? initialPath, + bool channelMessage = false, + }) { Navigator.push( context, MaterialPageRoute( builder: (context) => ChannelMessagePathMapScreen( message: message, initialPath: initialPath, + channelMessage: channelMessage, ), ), ); @@ -264,11 +277,13 @@ class ChannelMessagePathScreen extends StatelessWidget { class ChannelMessagePathMapScreen extends StatefulWidget { final ChannelMessage message; final Uint8List? initialPath; + final bool channelMessage; const ChannelMessagePathMapScreen({ super.key, required this.message, this.initialPath, + this.channelMessage = false, }); @override @@ -323,11 +338,16 @@ class _ChannelMessagePathMapScreenState primaryPath, widget.message.pathVariants, ); - final selectedPath = _resolveSelectedPath( + final selectedPathTmp = _resolveSelectedPath( _selectedPath, observedPaths, primaryPath, ); + + final selectedPath = !widget.channelMessage && widget.message.isOutgoing + ? Uint8List.fromList(selectedPathTmp.reversed.toList()) + : selectedPathTmp; + final selectedIndex = _indexForPath(selectedPath, observedPaths); final hops = _buildPathHops( selectedPath, diff --git a/lib/screens/map_screen.dart b/lib/screens/map_screen.dart index f65bd990..1fad04b6 100644 --- a/lib/screens/map_screen.dart +++ b/lib/screens/map_screen.dart @@ -105,7 +105,7 @@ class _MapScreenState extends State { double _zoomFromStdDev(double latStdDev, double lonStdDev) { final maxSpread = max(latStdDev, lonStdDev); if (maxSpread <= 0) return 13.0; - // Approzimate: each zoom level halves the visible area + // Approximate: each zoom level halves the visible area // ~0.01 degrees spread -> zoom 13, ~0.1 -> zoom 10, ~1.0 -> zoom 7 final zoom = 10.0 - log(maxSpread * 10 + 1) / ln10 * 3; return zoom.clamp(4.0, 15.0); @@ -825,7 +825,7 @@ class _MapScreenState extends State { color: _getNodeColor(contact.type), ), const SizedBox(width: 8), - Expanded(child: Text(contact.name)), + Expanded(child: SelectableText(contact.name)), ], ), content: Column( @@ -996,7 +996,7 @@ class _MapScreenState extends State { ), ), const SizedBox(height: 2), - Text(value, style: const TextStyle(fontSize: 14)), + SelectableText(value, style: const TextStyle(fontSize: 14)), ], ), ); diff --git a/lib/screens/path_trace_map.dart b/lib/screens/path_trace_map.dart index 01262991..7da1f9c8 100644 --- a/lib/screens/path_trace_map.dart +++ b/lib/screens/path_trace_map.dart @@ -46,6 +46,7 @@ class PathTraceData { class PathTraceMapScreen extends StatefulWidget { final String title; final Uint8List path; + final int? repeaterId; final bool flipPathRound; final bool reversePathRound; @@ -53,6 +54,7 @@ class PathTraceMapScreen extends StatefulWidget { super.key, required this.title, required this.path, + this.repeaterId, this.flipPathRound = false, this.reversePathRound = false, }); @@ -97,7 +99,7 @@ class _PathTraceMapScreenState extends State { super.dispose(); } - Uint8List addReturnpath(Uint8List pathBytes) { + Uint8List addReturnPath(Uint8List pathBytes) { Uint8List? traceBytes; final len = (pathBytes.length + pathBytes.length - 1); traceBytes = Uint8List(len); @@ -125,11 +127,13 @@ class _PathTraceMapScreenState extends State { : widget.path; if (widget.flipPathRound) { - path = addReturnpath(pathTmp); + path = addReturnPath(pathTmp); } else { path = pathTmp; } + print('Initiating path trace with path: ${_formatPathPrefixes(path)}'); + final connector = Provider.of(context, listen: false); final frame = buildTraceReq( DateTime.now().millisecondsSinceEpoch ~/ 1000,