From ac8d417c120befd37e7e3ef8d8fb88eb966d7395 Mon Sep 17 00:00:00 2001 From: Eduard Gert Date: Thu, 28 May 2026 14:43:28 +0200 Subject: [PATCH] update exit node tab --- .../src/modules/exit-nodes/ExitNodes.tsx | 33 ++++++++++++++----- .../src/modules/exit-nodes/ExitNodesList.tsx | 26 ++++++++------- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/client/ui/frontend/src/modules/exit-nodes/ExitNodes.tsx b/client/ui/frontend/src/modules/exit-nodes/ExitNodes.tsx index 364f95eee..bcbe9e1ee 100644 --- a/client/ui/frontend/src/modules/exit-nodes/ExitNodes.tsx +++ b/client/ui/frontend/src/modules/exit-nodes/ExitNodes.tsx @@ -22,17 +22,32 @@ export const ExitNodes = () => { searchRef.current?.focus(); }, []); + // Initial order: active-first, then by id. After that, positions are sticky + // — toggling a row doesn't move it. Mirrors the networks-list behavior so + // the optimistic radio flip paints in place instead of the row jumping to + // the top. + const orderRef = useRef([]); + const ordered = useMemo(() => { + const byId = new Map(exitNodes.map((n) => [n.id, n])); + const kept = orderRef.current.filter((id) => byId.has(id)); + const known = new Set(kept); + const fresh = exitNodes + .filter((n) => !known.has(n.id)) + .sort((a, b) => { + if (a.selected !== b.selected) return a.selected ? -1 : 1; + return a.id.localeCompare(b.id); + }) + .map((n) => n.id); + const next = [...kept, ...fresh]; + orderRef.current = next; + return next.map((id) => byId.get(id)!); + }, [exitNodes]); + const filtered = useMemo(() => { const q = search.trim().toLowerCase(); - const matches = exitNodes.filter((r) => { - if (!q) return true; - return r.id.toLowerCase().includes(q); - }); - return matches.sort((a, b) => { - if (a.selected !== b.selected) return a.selected ? -1 : 1; - return a.id.localeCompare(b.id); - }); - }, [exitNodes, search]); + if (!q) return ordered; + return ordered.filter((r) => r.id.toLowerCase().includes(q)); + }, [ordered, search]); if (isConnected && exitNodes.length === 0) { return ( diff --git a/client/ui/frontend/src/modules/exit-nodes/ExitNodesList.tsx b/client/ui/frontend/src/modules/exit-nodes/ExitNodesList.tsx index 4d813dfe1..bc67600eb 100644 --- a/client/ui/frontend/src/modules/exit-nodes/ExitNodesList.tsx +++ b/client/ui/frontend/src/modules/exit-nodes/ExitNodesList.tsx @@ -30,7 +30,7 @@ export const ExitNodesList = ({ data, onToggle }: Props) => { onValueChange={handleChange} className={"flex flex-col"} > - + {data.map((n) => ( ))} @@ -41,17 +41,26 @@ export const ExitNodesList = ({ data, onToggle }: Props) => { type RowProps = { value: string; label: string; + first?: boolean; }; -const Row = ({ value, label }: RowProps) => ( +const Row = ({ value, label, first }: RowProps) => ( + + {label} + ( className={"h-2 w-2 rounded-full bg-white"} /> - - {label} - );