Show learned path in routing override. Closes #195.

This commit is contained in:
Jack Kingsman
2026-04-16 11:59:35 -07:00
parent b1cd6e1aa9
commit 0e9bd59b44
3 changed files with 41 additions and 44 deletions

View File

@@ -3,10 +3,9 @@ import { useMemo, useState } from 'react';
import type { Contact, PathDiscoveryResponse, PathDiscoveryRoute } from '../types';
import {
findContactsByPrefix,
formatForcedRouteSummary,
formatLearnedRouteSummary,
formatRouteLabel,
getDirectContactRoute,
getEffectiveContactRoute,
hasRoutingOverride,
parsePathHops,
} from '../utils/pathUtils';
import { Button } from './ui/button';
@@ -99,30 +98,9 @@ export function ContactPathDiscoveryModal({
const [error, setError] = useState<string | null>(null);
const [result, setResult] = useState<PathDiscoveryResponse | null>(null);
const effectiveRoute = useMemo(() => getEffectiveContactRoute(contact), [contact]);
const directRoute = useMemo(() => getDirectContactRoute(contact), [contact]);
const hasForcedRoute = hasRoutingOverride(contact);
const learnedRouteSummary = useMemo(() => {
if (!directRoute) {
return 'Flood';
}
const hops = parsePathHops(directRoute.path, directRoute.path_len);
return hops.length > 0
? `${formatRouteLabel(directRoute.path_len, true)} (${hops.join(' -> ')})`
: formatRouteLabel(directRoute.path_len, true);
}, [directRoute]);
const forcedRouteSummary = useMemo(() => {
if (!hasForcedRoute) {
return null;
}
if (effectiveRoute.pathLen === -1) {
return 'Flood';
}
const hops = parsePathHops(effectiveRoute.path, effectiveRoute.pathLen);
return hops.length > 0
? `${formatRouteLabel(effectiveRoute.pathLen, true)} (${hops.join(' -> ')})`
: formatRouteLabel(effectiveRoute.pathLen, true);
}, [effectiveRoute, hasForcedRoute]);
const learnedRouteSummary = useMemo(() => formatLearnedRouteSummary(contact), [contact]);
const forcedRouteSummary = useMemo(() => formatForcedRouteSummary(contact), [contact]);
const hasForcedRoute = forcedRouteSummary !== null;
const forwardChain = result
? renderRouteNodes(

View File

@@ -3,10 +3,9 @@ import { useEffect, useMemo, useState } from 'react';
import { api } from '../api';
import type { Contact } from '../types';
import {
formatRouteLabel,
formatForcedRouteSummary,
formatLearnedRouteSummary,
formatRoutingOverrideInput,
getDirectContactRoute,
hasRoutingOverride,
} from '../utils/pathUtils';
import { Button } from './ui/button';
import {
@@ -28,18 +27,6 @@ interface ContactRoutingOverrideModalProps {
onError: (message: string) => void;
}
function summarizeLearnedRoute(contact: Contact): string {
return formatRouteLabel(getDirectContactRoute(contact)?.path_len ?? -1, true);
}
function summarizeForcedRoute(contact: Contact): string | null {
if (!hasRoutingOverride(contact)) {
return null;
}
const routeOverrideLen = contact.route_override_len;
return routeOverrideLen == null ? null : formatRouteLabel(routeOverrideLen, true);
}
export function ContactRoutingOverrideModal({
open,
onClose,
@@ -59,7 +46,8 @@ export function ContactRoutingOverrideModal({
setError(null);
}, [contact, open]);
const forcedRouteSummary = useMemo(() => summarizeForcedRoute(contact), [contact]);
const learnedRouteSummary = useMemo(() => formatLearnedRouteSummary(contact), [contact]);
const forcedRouteSummary = useMemo(() => formatForcedRouteSummary(contact), [contact]);
const saveRoute = async (value: string) => {
setSaving(true);
@@ -98,7 +86,7 @@ export function ContactRoutingOverrideModal({
<div className="rounded-md border border-border bg-muted/20 p-3 text-sm">
<div className="font-medium">{contact.name || contact.public_key.slice(0, 12)}</div>
<div className="mt-1 text-muted-foreground">
Current learned route: {summarizeLearnedRoute(contact)}
Current learned route: {learnedRouteSummary}
</div>
{forcedRouteSummary && (
<div className="mt-1 text-destructive">

View File

@@ -209,6 +209,37 @@ export function formatRouteLabel(pathLen: number, capitalize: boolean = false):
return capitalize ? label.charAt(0).toUpperCase() + label.slice(1) : label;
}
/**
* Format the learned direct route for display in route-editing dialogs,
* e.g. "2 hops (AE -> F1)", "Direct", or "Flood".
*/
export function formatLearnedRouteSummary(contact: Contact): string {
const directRoute = getDirectContactRoute(contact);
if (!directRoute) {
return formatRouteLabel(-1, true);
}
const hops = parsePathHops(directRoute.path, directRoute.path_len);
const label = formatRouteLabel(directRoute.path_len, true);
return hops.length > 0 ? `${label} (${hops.join(' -> ')})` : label;
}
/**
* Format the forced (override) route for display in route-editing dialogs,
* matching the learned-route format. Returns null when no override is set.
*/
export function formatForcedRouteSummary(contact: Contact): string | null {
if (!hasRoutingOverride(contact)) {
return null;
}
const effectiveRoute = getEffectiveContactRoute(contact);
if (effectiveRoute.pathLen === -1) {
return formatRouteLabel(-1, true);
}
const hops = parsePathHops(effectiveRoute.path, effectiveRoute.pathLen);
const label = formatRouteLabel(effectiveRoute.pathLen, true);
return hops.length > 0 ? `${label} (${hops.join(' -> ')})` : label;
}
export function formatRoutingOverrideInput(contact: Contact): string {
const routeOverride = getRouteOverride(contact);
if (!routeOverride) {