admin: live and query log pages show client names
This commit is contained in:
@@ -56,23 +56,6 @@ const styles = stylex.create({
|
||||
dash: {
|
||||
color: colors.textMuted,
|
||||
},
|
||||
/**
|
||||
* A learned name is runtime state, not something the operator typed, so it
|
||||
* reads muted and carries an outlined "learned" tag. The tag is real text —
|
||||
* a screen reader announces it — because colour alone is not an affordance.
|
||||
*/
|
||||
learnedTag: {
|
||||
marginLeft: "0.5rem",
|
||||
borderWidth: 1,
|
||||
borderStyle: "solid",
|
||||
borderColor: colors.border,
|
||||
borderRadius: "0.25rem",
|
||||
paddingInline: "0.375rem",
|
||||
paddingBlock: "0.125rem",
|
||||
fontSize: "0.75rem",
|
||||
lineHeight: "1rem",
|
||||
color: colors.textMuted,
|
||||
},
|
||||
badge: {
|
||||
marginLeft: "0.5rem",
|
||||
borderRadius: "0.25rem",
|
||||
@@ -153,9 +136,9 @@ export default function ClientsPage() {
|
||||
{client.name !== "" ? (
|
||||
client.name
|
||||
) : client.learned_name !== "" ? (
|
||||
<span {...stylex.props(styles.dash)}>
|
||||
<span {...stylex.props(shared.learnedName)}>
|
||||
{client.learned_name}
|
||||
<span {...stylex.props(styles.learnedTag)}>learned</span>
|
||||
<span {...stylex.props(shared.learnedTag)}>learned</span>
|
||||
</span>
|
||||
) : (
|
||||
<span {...stylex.props(styles.dash)}>—</span>
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
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 {...stylex.props(shared.learnedTag)}>learned</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user