import { useEffect, useMemo, useState } from 'react'; import { api } from '../api'; import type { Contact } from '../types'; import { formatRouteLabel, formatRoutingOverrideInput, getDirectContactRoute, hasRoutingOverride, } from '../utils/pathUtils'; import { Button } from './ui/button'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from './ui/dialog'; import { Input } from './ui/input'; import { Label } from './ui/label'; interface ContactRoutingOverrideModalProps { open: boolean; onClose: () => void; contact: Contact; onSaved: (message: string) => void; 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, contact, onSaved, onError, }: ContactRoutingOverrideModalProps) { const [route, setRoute] = useState(''); const [saving, setSaving] = useState(false); const [error, setError] = useState(null); useEffect(() => { if (!open) { return; } setRoute(formatRoutingOverrideInput(contact)); setError(null); }, [contact, open]); const forcedRouteSummary = useMemo(() => summarizeForcedRoute(contact), [contact]); const saveRoute = async (value: string) => { setSaving(true); setError(null); try { await api.setContactRoutingOverride(contact.public_key, value); onSaved(value.trim() === '' ? 'Routing override cleared' : 'Routing override updated'); onClose(); } catch (err) { const message = err instanceof Error ? err.message : 'Failed to update routing override'; setError(message); onError(message); } finally { setSaving(false); } }; return ( !isOpen && onClose()}> Routing Override Set a forced route for this contact. Leave the field blank to clear the override and fall back to the learned route or flood until a new path is heard.
{ event.preventDefault(); void saveRoute(route); }} >
{contact.name || contact.public_key.slice(0, 12)}
Current learned route: {summarizeLearnedRoute(contact)}
{forcedRouteSummary && (
Current forced route: {forcedRouteSummary}
)}
setRoute(event.target.value)} placeholder='Examples: "ae,f1" or "ae92,f13e"' autoFocus disabled={saving} />

Use comma-separated 1, 2, or 3 byte hop IDs for an explicit path.

Note: direct messages that do not see an ACK retry up to 3 times. The final retry is sent as flood, even when forced routing is configured.

{error && (
{error}
)}
); }