54 lines
2.0 KiB
TypeScript
54 lines
2.0 KiB
TypeScript
/**
|
|
* The client column of the query tables reads as a name wherever one is known,
|
|
* with the same precedence the Clients page applies: a hand-typed `name` wins,
|
|
* the reverse-DNS `learned_name` stands in muted behind it, and an address with
|
|
* neither — including one the loaded list has never seen — stays bare.
|
|
*
|
|
* The muted colour is the whole of the affordance here. The Clients page pairs
|
|
* it with an outlined "learned" tag, and keeps it: one mention per client is
|
|
* information. Repeating that tag down every row of a query table is noise, so
|
|
* the tables carry the name alone.
|
|
*/
|
|
|
|
import { useMemo } from "react";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import * as stylex from "@stylexjs/stylex";
|
|
import { clientsQuery } from "@/lib/queries";
|
|
import type { Client } from "@/lib/types";
|
|
import { styles as shared } from "@/ui/styles";
|
|
|
|
export type ClientNames = ReadonlyMap<string, Pick<Client, "name" | "learned_name">>;
|
|
|
|
/**
|
|
* The live stream names clients the loaded list has never seen. Polling folds
|
|
* them in on the next tick, which keeps the lookup a single cached query
|
|
* instead of a fetch fired per unknown address.
|
|
*/
|
|
const CLIENTS_POLL_MS = 30_000;
|
|
|
|
export function useClientNames(): ClientNames {
|
|
const { data } = useQuery({ ...clientsQuery(), refetchInterval: CLIENTS_POLL_MS });
|
|
return useMemo(
|
|
() =>
|
|
new Map(
|
|
(data ?? []).map((client) => [client.ip, { name: client.name, learned_name: client.learned_name }]),
|
|
),
|
|
[data],
|
|
);
|
|
}
|
|
|
|
export function ClientName({ ip, names }: { ip: string; names: ClientNames }) {
|
|
const client = names.get(ip);
|
|
if (client === undefined || (client.name === "" && client.learned_name === "")) {
|
|
return <span {...stylex.props(shared.mono)}>{ip}</span>;
|
|
}
|
|
// The name replaces the address on screen, so the address stays reachable
|
|
// as the tooltip rather than disappearing from the row entirely.
|
|
if (client.name !== "") return <span title={ip}>{client.name}</span>;
|
|
return (
|
|
<span title={ip} {...stylex.props(shared.learnedName)}>
|
|
{client.learned_name}
|
|
</span>
|
|
);
|
|
}
|