import { useQueryClient } from "@tanstack/react-query";
import { useEffect, useRef, useState } from "react";
import { getHovercard } from "~/fns/hovercards";
import { isReservedName } from "~/lib/reserved-names";
const SHOW_DELAY_MS = 350;
const HIDE_DELAY_MS = 200;
const KINDS = ["issue", "user", "team", "commit"] as const;
/** Cards describe immutable refs (a commit) or slow-moving ones (a user), so a page visit shares one fetch. */
const CARD_STALE_MS = 5 * 60 * 1000;
type Kind = (typeof KINDS)[number];
type Card = NonNullable<Awaited<ReturnType<typeof getHovercard>>>;
export type HovercardProfile = NonNullable<Card["profile"]>;
export type HovercardState = { card: Card; top: number; left: number } | null;
export const hovercardQueryKey = (kind: Kind, ref: string) => ["hovercard", kind, ref] as const;
function isKind(value: string | undefined): value is Kind {
return KINDS.some((kind) => kind === value);
}
/** A link straight to a profile page, e.g. author names, which gets a user card without opting in. */
const PROFILE_PATH = /^\/([A-Za-z0-9](?:[A-Za-z0-9-]{0,38}))$/;
type Target = { link: HTMLAnchorElement; kind: Kind; ref: string };
function hoverTarget(event: Event): Target | null {
if (!(event.target instanceof Element)) return null;
const tagged = event.target.closest<HTMLAnchorElement>("a[data-hovercard]");
if (tagged) {
const kind = tagged.dataset.hovercard;
const ref = tagged.dataset.ref;
return isKind(kind) && ref ? { link: tagged, kind, ref } : null;
}
const link = event.target.closest<HTMLAnchorElement>("a[href]");
if (!link || link.closest("header, nav, [data-no-hovercard]") || link.origin !== window.location.origin) return null;
const name = PROFILE_PATH.exec(link.pathname)?.[1];
return name && !isReservedName(name) ? { link, kind: "user", ref: name } : null;
}