update exit node tab

This commit is contained in:
Eduard Gert
2026-05-28 14:43:28 +02:00
parent dccc0ebe4b
commit ac8d417c12
2 changed files with 38 additions and 21 deletions

View File

@@ -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<string[]>([]);
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 (

View File

@@ -30,7 +30,7 @@ export const ExitNodesList = ({ data, onToggle }: Props) => {
onValueChange={handleChange}
className={"flex flex-col"}
>
<Row value={NONE_VALUE} label={t("exitNodes.none")} />
<Row value={NONE_VALUE} label={t("exitNodes.none")} first />
{data.map((n) => (
<Row key={n.id} value={n.id} label={n.id} />
))}
@@ -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) => (
<RadioGroup.Item
value={value}
className={cn(
"group flex items-center gap-3 px-7 py-3 min-w-0 text-left outline-none",
"cursor-pointer wails-no-draggable",
"hover:bg-nb-gray-900/40",
"group flex items-center gap-2.5 pl-6 pr-8 py-3 min-w-0 w-full",
first && "mt-2",
"hover:bg-nb-gray-900/40 transition-colors",
"wails-no-draggable cursor-pointer outline-none text-left",
)}
>
<span
className={
"min-w-0 flex-1 text-[0.81rem] font-medium text-nb-gray-100 truncate"
}
>
{label}
</span>
<span
className={cn(
"h-4 w-4 shrink-0 rounded-full border",
@@ -64,12 +73,5 @@ const Row = ({ value, label }: RowProps) => (
className={"h-2 w-2 rounded-full bg-white"}
/>
</span>
<span
className={
"text-[0.81rem] font-medium text-nb-gray-100 truncate min-w-0 flex-1"
}
>
{label}
</span>
</RadioGroup.Item>
);