diff --git a/lib/helpers/path_hop_resolver.dart b/lib/helpers/path_hop_resolver.dart index ed06207a..b17f7b56 100644 --- a/lib/helpers/path_hop_resolver.dart +++ b/lib/helpers/path_hop_resolver.dart @@ -21,10 +21,10 @@ class PathHopResolver { if (contact.type != advTypeRepeater && contact.type != advTypeRoom) { continue; } - final prefix = PathHelper.formatHopHex(contact.publicKey.sublist(0, width)); - candidatesByPrefix - .putIfAbsent(prefix, () => []) - .add(contact); + final prefix = PathHelper.formatHopHex( + contact.publicKey.sublist(0, width), + ); + candidatesByPrefix.putIfAbsent(prefix, () => []).add(contact); } for (final candidates in candidatesByPrefix.values) { candidates.sort((a, b) => b.lastSeen.compareTo(a.lastSeen)); @@ -39,7 +39,8 @@ class PathHopResolver { var previousPosition = endpoint; for (final index in indexes) { - final candidates = candidatesByPrefix[PathHelper.formatHopHex(hops[index])]; + final candidates = + candidatesByPrefix[PathHelper.formatHopHex(hops[index])]; if (candidates == null || candidates.isEmpty) continue; var bestIndex = 0; diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 2412b3b8..c44e3d01 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -2591,7 +2591,7 @@ class AppLocalizationsDe extends AppLocalizations { @override String get repeater_pathHashModeHelper => - 'Bytes, die zur Kodierung der ID dieses Repeaters in Flood-Pfad-/Schleifen-Erkennung-Tags verwendet werden. 0 = 1 Byte (256 IDs, bis zu 64 Hops), 1 = 2 Bytes (65.000 IDs, bis zu 32 Hops), 2 = 3 Bytes (16 Millionen IDs, bis zu 21 Hops). Firmware vor v1.14 verwendet immer 1-Byte-Pfade; ab v1.14 können 2- oder 3-Byte-Pfade eingestellt werden.'; + 'Bytes, die zur Kodierung der ID dieses Repeaters in Flood-Pfad-/Schleifen-Erkennung-Tags verwendet werden. 0 = 1 Byte (256 IDs, bis zu 64 Hops), 1 = 2 Bytes (65.000 IDs, bis zu 32 Hops), 2 = 3 Bytes (16 Millionen IDs, bis zu 21 Hops). Firmware-Versionen 1.13 und älter verwenden mehrstellige Pfade – ab Version 1.14+ wird nur ein Pfad erstellt, sobald das Netzwerk aktiv ist.'; @override String get repeater_txDelay => 'Flood-TX-Verzögerung'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index d7b171bd..0882003a 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -2591,7 +2591,7 @@ class AppLocalizationsEs extends AppLocalizations { @override String get repeater_pathHashModeHelper => - 'Bytes utilizados para codificar el ID de este repetidor en las etiquetas de ruta flood/detección de bucles. 0=1 byte (256 IDs, hasta 64 saltos), 1=2 bytes (65.000 IDs, hasta 32 saltos), 2=3 bytes (16 millones de IDs, hasta 21 saltos). El firmware anterior a v1.14 siempre usaba rutas de 1 byte; v1.14 y posteriores pueden configurarse para rutas de 2 o 3 bytes.'; + 'Bytes utilizados para codificar el ID de este repetidor en las etiquetas de ruta/detección de bucles. 0=1 byte (256 IDs, hasta 64 saltos), 1=2 bytes (65.000 IDs, hasta 32 saltos), 2=3 bytes (16 millones de IDs, hasta 21 saltos). Las versiones 1.13 y anteriores de firmware eliminan rutas de múltiples bytes; solo se detectan una vez que la red está activa en la versión 1.14 o posterior.'; @override String get repeater_txDelay => 'Retraso TX por inundación'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index ce91155a..a5ae5046 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -2602,7 +2602,7 @@ class AppLocalizationsFr extends AppLocalizations { @override String get repeater_pathHashModeHelper => - 'Octets utilisés pour encoder l\'ID de ce répéteur dans les balises de chemin flood/détection de boucle. 0=1 octet (256 ID, jusqu\'à 64 sauts), 1=2 octets (65 000 ID, jusqu\'à 32 sauts), 2=3 octets (16 millions d\'ID, jusqu\'à 21 sauts). Le firmware antérieur à v1.14 utilisait toujours des chemins d\'1 octet; v1.14 et les versions ultérieures peuvent être configurées pour des chemins de 2 ou 3 octets.'; + 'Octets utilisés pour encoder l\'ID de ce routeur dans les balises de détection de flux/boucles. 0 = 1 octet (256 ID, jusqu\'à 64 sauts), 1 = 2 octets (65 000 ID, jusqu\'à 32 sauts), 2 = 3 octets (16 millions d\'ID, jusqu\'à 21 sauts). Les versions 1.13 et antérieures utilisent des chemins multi-octets ; à partir de la version 1.14, cela n\'est plus nécessaire.'; @override String get repeater_txDelay => 'Délai de transmission en flood'; diff --git a/lib/screens/channel_chat_screen.dart b/lib/screens/channel_chat_screen.dart index 3e271adb..25fb265c 100644 --- a/lib/screens/channel_chat_screen.dart +++ b/lib/screens/channel_chat_screen.dart @@ -515,6 +515,11 @@ class _ChannelChatScreenState extends State { final displayPathHashWidth = message.pathHashWidth ?? context.read().pathHashByteWidth; + final displayHopCount = _displayHopCount( + displayPath, + displayPathHashWidth, + message.pathLength, + ); // Bubble colors — outgoing uses MeshPalette.me / meBorder / meInk. final bubbleColor = isOutgoing @@ -665,9 +670,7 @@ class _ChannelChatScreenState extends State { children: [ RouteChip( isDirect: (message.pathLength ?? -1) >= 0, - hops: (message.pathLength ?? -1) >= 0 - ? message.pathLength - : null, + hops: displayHopCount, ), const SizedBox(width: 4), Text( @@ -1564,6 +1567,16 @@ class _ChannelChatScreenState extends State { ).map(PathHelper.formatHopHex).join(','); } + int? _displayHopCount( + Uint8List pathBytes, + int pathHashByteWidth, + int? fallbackPathLength, + ) { + if ((fallbackPathLength ?? -1) < 0) return null; + if (pathBytes.isEmpty) return fallbackPathLength; + return PathHelper.splitPathBytes(pathBytes, pathHashByteWidth).length; + } + /// Deterministic name-to-hue mapping consistent with [AvatarCircle]. Color _colorForName(String name) { const hues = [ diff --git a/lib/screens/channel_message_path_screen.dart b/lib/screens/channel_message_path_screen.dart index 634ca388..98a9c874 100644 --- a/lib/screens/channel_message_path_screen.dart +++ b/lib/screens/channel_message_path_screen.dart @@ -156,11 +156,11 @@ class ChannelMessagePathScreen extends StatelessWidget { }) { final l10n = context.l10n; final scheme = Theme.of(context).colorScheme; - final routeChip = message.pathLength == null + final routeChip = effectiveHopCount == null ? null - : message.pathLength! < 0 + : effectiveHopCount < 0 ? const RouteChip(isDirect: false) - : RouteChip(isDirect: true, hops: message.pathLength); + : RouteChip(isDirect: true, hops: effectiveHopCount); return MeshCard( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), @@ -1887,9 +1887,7 @@ Uint8List _orientPathBytes( }) { if (!reverse || pathBytes.isEmpty) return pathBytes; final hops = PathHelper.splitPathBytes(pathBytes, hashByteWidth); - return Uint8List.fromList([ - for (final hop in hops.reversed) ...hop, - ]); + return Uint8List.fromList([for (final hop in hops.reversed) ...hop]); } int _hopCountFromBytes(int byteCount, int hashByteWidth) { diff --git a/lib/screens/map_screen.dart b/lib/screens/map_screen.dart index 74ba1e35..9be1a584 100644 --- a/lib/screens/map_screen.dart +++ b/lib/screens/map_screen.dart @@ -1321,7 +1321,9 @@ class _MapScreenState extends State { (contact.type == advTypeRepeater || contact.type == advTypeRoom) && contact.publicKey.length >= overlapHopWidth && overlapPrefixes.contains( - PathHelper.formatHopHex(contact.publicKey.sublist(0, overlapHopWidth)), + PathHelper.formatHopHex( + contact.publicKey.sublist(0, overlapHopWidth), + ), ); void addNode(Contact contact, {bool dot = false}) {