/** * A player's picture, or the letter that stands in for it. * * Two pages draw the same account: the profile at /profile/<...> and the * record at /matches/<...>. The drawing is one function so that a face is one * thing wherever it appears — the same fallback, the same read of the same * blob, and the same classes for the same shape. * * The picture is the network's, not ours: avatars.ts reads it out of the * player's own repository, and an account with none keeps the monogram * rather than growing a hole where a picture would have gone. */ import { el } from "./dom"; import { avatarUrl, monogram } from "./profile"; /** Who is being drawn. The same pair every address here resolves to. */ export interface Face { did: string; handle: string | null; } /** * The box, monogram already in it, and the portrait swapped in if one loads. * * Swapped on `load` rather than on the URL arriving: a broken blob would * otherwise replace a readable letter with a broken-image icon. */ export function playerFace(actor: Face): HTMLElement { const box = el("span", { className: "profile-face" }, [ el("span", { className: "profile-monogram", textContent: monogram(actor), }), ]); void avatarUrl(actor.did) .then((src) => { if (!src) return; const image = el("img", { className: "profile-portrait", src, // Decorative: the handle is beside it. alt: "", decoding: "async", }); image.addEventListener("load", () => box.replaceChildren(image)); }) .catch((error: unknown) => { console.warn("profile: the picture could not be found", error); }); return box; }