import { Check, Loader2, Search, X } from "lucide-react"; import { useEffect, useState } from "react"; import { api, errorMessage } from "../api"; import type { EmojiRow } from "../types"; import { StaticLottie } from "./StaticLottie"; function isAnimated(mime: string): boolean { const m = mime.toLowerCase(); return m.includes("tgsticker") || m.includes("lottie") || m.includes("json"); } function EmojiThumb({ row }: { row: EmojiRow }) { const [failed, setFailed] = useState(!isAnimated(row.MimeType)); useEffect(() => { setFailed(!isAnimated(row.MimeType)); }, [row.DocumentID, row.MimeType]); if (failed) { return
{row.Alt || "🙂"}
; } return ( api.emojiAnimation(row.DocumentID)} onError={() => setFailed(true)} /> ); } // EmojiPicker searches every custom-emoji document already on this // deployment -- unlike the Emoji admin page's per-pack browsing, this is a // flat document search with no system/non-system distinction, so a bundled // icon from a default pack (e.g. Topics' ✅) is just as findable as a // hand-uploaded one. Lets the caller select a document id by clicking it // instead of typing one from memory; used wherever a raw document id field // otherwise has nothing else in the panel to pick from (e.g. a // bot-verification icon). export function EmojiPicker({ label, value, onChange }: { label: string; value: string; onChange: (documentID: string) => void; }) { const [query, setQuery] = useState(""); const [rows, setRows] = useState([]); const [busy, setBusy] = useState(false); const [error, setError] = useState(""); const selected = rows.find((row) => row.DocumentID === value) ?? null; async function search() { setBusy(true); setError(""); const params = new URLSearchParams({ limit: "24" }); if (query.trim()) params.set("q", query.trim()); try { const result = await api.emoji(params); setRows(result.rows ?? []); } catch (err) { setError(errorMessage(err)); } finally { setBusy(false); } } useEffect(() => { void search(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return (
{label} {value ? ( ) : null}
{value ? (
{selected?.Alt || "—"} {value}
{selected?.SetTitle || "-"}
) : null}
setQuery(event.target.value)} onKeyDown={(event) => { if (event.key === "Enter") { event.preventDefault(); void search(); } }} placeholder={"Search document ID or emoji"} />
{error &&
{error}
}
{rows.map((row) => ( ))} {rows.length === 0 && !busy ?
{"No results"}
: null}
); }