From 09f4109b01a02679262e00ea049e1857e428ad35 Mon Sep 17 00:00:00 2001 From: Eduard Gert Date: Wed, 27 May 2026 18:01:06 +0200 Subject: [PATCH] update peers ui --- client/ui/frontend/src/layouts/Navigation.tsx | 4 +- .../src/modules/exit-nodes/ExitNodes.tsx | 40 +++------ .../src/modules/networks/NetworkFilters.tsx | 87 +++++++++++++++---- .../src/modules/networks/Networks.tsx | 55 ++++-------- .../src/modules/peers/PeerFilters.tsx | 87 +++++++++++++++---- .../ui/frontend/src/modules/peers/Peers.tsx | 47 +++------- .../frontend/src/modules/peers/PeersList.tsx | 2 +- client/ui/i18n/locales/de/common.json | 4 +- client/ui/i18n/locales/en/common.json | 4 +- client/ui/i18n/locales/hu/common.json | 4 +- 10 files changed, 191 insertions(+), 143 deletions(-) diff --git a/client/ui/frontend/src/layouts/Navigation.tsx b/client/ui/frontend/src/layouts/Navigation.tsx index d3760adb5..6a73e0bc1 100644 --- a/client/ui/frontend/src/layouts/Navigation.tsx +++ b/client/ui/frontend/src/layouts/Navigation.tsx @@ -36,7 +36,7 @@ export const Navigation = () => { ]; return ( -
+
{tabs.map((tab) => { const isActive = tab.value === section; const isDisabled = !isConnected && !isActive; @@ -49,7 +49,7 @@ export const Navigation = () => { disabled={isDisabled} className={cn( "group relative flex flex-1 items-center justify-center", - "gap-2.5 px-5 py-3", + "gap-2.5 px-5 py-3.5", "outline-none transition-all", isActive ? "text-netbird" : "text-nb-gray-400 hover:text-nb-gray-300", isDisabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer", diff --git a/client/ui/frontend/src/modules/exit-nodes/ExitNodes.tsx b/client/ui/frontend/src/modules/exit-nodes/ExitNodes.tsx index 7ae62602e..364f95eee 100644 --- a/client/ui/frontend/src/modules/exit-nodes/ExitNodes.tsx +++ b/client/ui/frontend/src/modules/exit-nodes/ExitNodes.tsx @@ -7,12 +7,6 @@ import { SearchInput } from "@/components/SearchInput"; import { EmptyState } from "@/components/EmptyState"; import { NoResults } from "@/components/NoResults"; import { useStatus } from "@/modules/daemon-status/StatusContext"; -import { - formatShortcut, - useKeyboardShortcut, -} from "@/lib/useKeyboardShortcut"; - -const SEARCH_SHORTCUT = { key: "k", cmd: true } as const; import { useNetworks } from "@/modules/networks/NetworksContext"; import { ExitNodesList } from "./ExitNodesList"; @@ -28,11 +22,6 @@ export const ExitNodes = () => { searchRef.current?.focus(); }, []); - useKeyboardShortcut(SEARCH_SHORTCUT, () => { - searchRef.current?.focus(); - searchRef.current?.select(); - }); - const filtered = useMemo(() => { const q = search.trim().toLowerCase(); const matches = exitNodes.filter((r) => { @@ -64,28 +53,23 @@ export const ExitNodes = () => { } return ( -
-
- setSearch(e.target.value)} - shortcut={formatShortcut(SEARCH_SHORTCUT)} - /> +
+
+
+ setSearch(e.target.value)} + /> +
- + {filtered.length === 0 ? ( ) : ( - + )} { const { t, i18n } = useTranslation(); + const [open, setOpen] = useState(false); const filters: { value: NetworkFilter; label: string }[] = [ { value: "all", label: t("networks.filter.all") }, { value: "active", label: t("networks.filter.active") }, { value: "overlapping", label: t("networks.filter.overlapping") }, ]; + const active = filters.find((f) => f.value === value) ?? filters[0]; + + const handleSelect = (v: NetworkFilter) => { + onChange(v); + setOpen(false); + }; return ( - onChange(v as NetworkFilter)} - disabled={disabled} - className={"w-full"} - > - {filters.map((f) => ( - - {f.label} - ({counts[f.value]}) - - ))} - + + + + + {active.label}{" "} + + ({counts[active.value]}) + + + + + + {filters.map((f) => { + const checked = f.value === value; + return ( + handleSelect(f.value)} + className={"gap-2"} + > + + {f.label}{" "} + + ({counts[f.value]}) + + + + {checked && ( + + )} + + + ); + })} + + ); }; diff --git a/client/ui/frontend/src/modules/networks/Networks.tsx b/client/ui/frontend/src/modules/networks/Networks.tsx index be5576cc9..58311c7c1 100644 --- a/client/ui/frontend/src/modules/networks/Networks.tsx +++ b/client/ui/frontend/src/modules/networks/Networks.tsx @@ -7,12 +7,6 @@ import { SearchInput } from "@/components/SearchInput"; import { EmptyState } from "@/components/EmptyState"; import { NoResults } from "@/components/NoResults"; import { useStatus } from "@/modules/daemon-status/StatusContext"; -import { - formatShortcut, - useKeyboardShortcut, -} from "@/lib/useKeyboardShortcut"; - -const SEARCH_SHORTCUT = { key: "k", cmd: true } as const; import { NetworkFilter, NetworkFilters } from "./NetworkFilters"; import { NetworksList } from "./NetworksList"; import { useNetworks } from "./NetworksContext"; @@ -50,15 +44,7 @@ export const Networks = () => { searchRef.current?.focus(); }, []); - useKeyboardShortcut(SEARCH_SHORTCUT, () => { - searchRef.current?.focus(); - searchRef.current?.select(); - }); - - const overlapGroups = useMemo( - () => buildOverlapMap(networkRoutes), - [networkRoutes], - ); + const overlapGroups = useMemo(() => buildOverlapMap(networkRoutes), [networkRoutes]); const overlapById = useMemo(() => { const map = new Map(); @@ -83,9 +69,7 @@ export const Networks = () => { if (filter === "active" && !r.selected) return false; if (filter === "overlapping" && !overlapById.has(r.id)) return false; if (q) { - const haystack = [r.id, r.range, ...r.domains] - .join(" ") - .toLowerCase(); + const haystack = [r.id, r.range, ...r.domains].join(" ").toLowerCase(); if (!haystack.includes(q)) return false; } return true; @@ -115,33 +99,24 @@ export const Networks = () => { } return ( -
-
- setSearch(e.target.value)} - shortcut={formatShortcut(SEARCH_SHORTCUT)} - /> - +
+
+
+ setSearch(e.target.value)} + /> +
+
- + {filtered.length === 0 ? ( ) : ( - + )} { const { t, i18n } = useTranslation(); + const [open, setOpen] = useState(false); const filters: { value: StatusFilter; label: string }[] = [ { value: "all", label: t("peers.filter.all") }, { value: "online", label: t("peers.filter.online") }, { value: "offline", label: t("peers.filter.offline") }, ]; + const active = filters.find((f) => f.value === value) ?? filters[0]; + + const handleSelect = (v: StatusFilter) => { + onChange(v); + setOpen(false); + }; return ( - onChange(v as StatusFilter)} - disabled={disabled} - className={"w-full"} - > - {filters.map((f) => ( - - {f.label} - ({counts[f.value]}) - - ))} - + + + + + {active.label}{" "} + + ({counts[active.value]}) + + + + + + {filters.map((f) => { + const checked = f.value === value; + return ( + handleSelect(f.value)} + className={"gap-2"} + > + + {f.label}{" "} + + ({counts[f.value]}) + + + + {checked && ( + + )} + + + ); + })} + + ); }; diff --git a/client/ui/frontend/src/modules/peers/Peers.tsx b/client/ui/frontend/src/modules/peers/Peers.tsx index 602cde4f9..84c1c8583 100644 --- a/client/ui/frontend/src/modules/peers/Peers.tsx +++ b/client/ui/frontend/src/modules/peers/Peers.tsx @@ -7,17 +7,11 @@ import { SearchInput } from "@/components/SearchInput"; import { EmptyState } from "@/components/EmptyState"; import { NoResults } from "@/components/NoResults"; import { useStatus } from "@/modules/daemon-status/StatusContext"; -import { - formatShortcut, - useKeyboardShortcut, -} from "@/lib/useKeyboardShortcut"; import { PeerFilters, StatusFilter } from "./PeerFilters"; import { PeersList } from "./PeersList"; const isOnline = (connStatus: string) => connStatus === "Connected"; -const SEARCH_SHORTCUT = { key: "k", cmd: true } as const; - export const Peers = () => { const { t } = useTranslation(); const { status } = useStatus(); @@ -32,11 +26,6 @@ export const Peers = () => { searchRef.current?.focus(); }, []); - useKeyboardShortcut(SEARCH_SHORTCUT, () => { - searchRef.current?.focus(); - searchRef.current?.select(); - }); - const isConnected = status?.status === "Connected"; const peers = status?.peers ?? []; @@ -88,31 +77,21 @@ export const Peers = () => { } return ( -
-
- setSearch(e.target.value)} - shortcut={formatShortcut(SEARCH_SHORTCUT)} - /> - +
+
+
+ setSearch(e.target.value)} + /> +
+
- + - {filtered.length === 0 ? ( - - ) : ( - - )} + {filtered.length === 0 ? : } { key={peer.pubKey} onClick={() => setSelected(peer)} className={cn( - "group flex items-start gap-2.5 px-7 py-3 min-w-0", + "group flex items-start gap-2.5 px-7 py-3 min-w-0 first:mt-2", "hover:bg-nb-gray-900/40 transition-colors", "wails-no-draggable cursor-pointer", )} diff --git a/client/ui/i18n/locales/de/common.json b/client/ui/i18n/locales/de/common.json index c5d622e02..dec785537 100644 --- a/client/ui/i18n/locales/de/common.json +++ b/client/ui/i18n/locales/de/common.json @@ -310,7 +310,7 @@ "sessionAboutToExpire.extendFailedTitle": "Sitzungsverlängerung fehlgeschlagen", "sessionAboutToExpire.logoutFailedTitle": "Abmeldung fehlgeschlagen", - "peers.search.placeholder": "Nach Peer-Name, DNS oder IP-Adresse suchen", + "peers.search.placeholder": "Nach Name oder IP suchen", "peers.filter.all": "Alle", "peers.filter.online": "Online", "peers.filter.offline": "Offline", @@ -335,7 +335,7 @@ "peers.details.p2p": "P2P", "peers.details.rosenpass": "Rosenpass aktiviert", - "networks.search.placeholder": "Nach Netzwerk, Bereich oder Domain suchen", + "networks.search.placeholder": "Nach Netzwerk oder Domain suchen", "networks.filter.all": "Alle", "networks.filter.active": "Aktiv", "networks.filter.overlapping": "Überlappend", diff --git a/client/ui/i18n/locales/en/common.json b/client/ui/i18n/locales/en/common.json index f0f7e82f5..a00e94d2c 100644 --- a/client/ui/i18n/locales/en/common.json +++ b/client/ui/i18n/locales/en/common.json @@ -326,7 +326,7 @@ "sessionAboutToExpire.expired": "Session expired", "sessionAboutToExpire.extendFailedTitle": "Extend Session Failed", - "peers.search.placeholder": "Search by peer name, DNS or IP address", + "peers.search.placeholder": "Search by name or IP", "peers.filter.all": "All", "peers.filter.online": "Online", "peers.filter.offline": "Offline", @@ -351,7 +351,7 @@ "peers.details.p2p": "P2P", "peers.details.rosenpass": "Rosenpass enabled", - "networks.search.placeholder": "Search by network, range or domain", + "networks.search.placeholder": "Search by network or domain", "networks.filter.all": "All", "networks.filter.active": "Active", "networks.filter.overlapping": "Overlapping", diff --git a/client/ui/i18n/locales/hu/common.json b/client/ui/i18n/locales/hu/common.json index 292bbaff1..01a368899 100644 --- a/client/ui/i18n/locales/hu/common.json +++ b/client/ui/i18n/locales/hu/common.json @@ -310,7 +310,7 @@ "sessionAboutToExpire.extendFailedTitle": "A munkamenet meghosszabbítása sikertelen", "sessionAboutToExpire.logoutFailedTitle": "Kijelentkezés sikertelen", - "peers.search.placeholder": "Keresés társ neve, DNS vagy IP-cím alapján", + "peers.search.placeholder": "Keresés név vagy IP alapján", "peers.filter.all": "Összes", "peers.filter.online": "Online", "peers.filter.offline": "Offline", @@ -335,7 +335,7 @@ "peers.details.p2p": "P2P", "peers.details.rosenpass": "Rosenpass engedélyezve", - "networks.search.placeholder": "Keresés hálózat, tartomány vagy domain alapján", + "networks.search.placeholder": "Keresés hálózat vagy domain alapján", "networks.filter.all": "Összes", "networks.filter.active": "Aktív", "networks.filter.overlapping": "Átfedő",