import { useState } from "react"; import { useMutation, useQuery, useQueryClient, keepPreviousData } from "@tanstack/react-query"; import { useNavigate } from "react-router"; import { Plus, Search, ShieldCheck, Trash2 } from "lucide-react"; import { api, qs } from "../lib/api"; import type { DupHash, Paged, WhitelistRow } from "../lib/types"; import { int, relative, shortHash } from "../lib/format"; import { Badge, Button, Card, Checkbox, CopyButton, Empty, ErrorBox, Input, Loading, PageHeader, Pager, Segmented, Table, Td, Textarea, Th, useFeedback, } from "../components/ui"; interface HashUsage { hash: string; whitelisted: boolean; total: number; items: { addonId: string; name: string; path: string; version: string }[]; } export default function Whitelist() { const [tab, setTab] = useState<"list" | "candidates">("list"); const [inspect, setInspect] = useState(null); return (
} /> {tab === "list" ? : } {inspect && setInspect(null)} />}
); } function List({ onInspect }: { onInspect: (h: string) => void }) { const qc = useQueryClient(); const { toast, confirm } = useFeedback(); const [q, setQ] = useState(""); const [draft, setDraft] = useState(""); const [offset, setOffset] = useState(0); const [text, setText] = useState(""); const [note, setNote] = useState(""); const limit = 50; const list = useQuery({ queryKey: ["whitelist", q, offset], queryFn: () => api>("/whitelist" + qs({ q, offset, limit })), placeholderData: keepPreviousData, }); const add = useMutation({ mutationFn: () => api<{ added: number; skipped: { hash: string; reason: string }[] }>("/whitelist", { method: "POST", body: { text, note } }), onSuccess: (r) => { const reasons = r.skipped.length ? ` · ${r.skipped.length} skipped (${[...new Set(r.skipped.map((s) => s.reason))].join(", ")})` : ""; toast(`${r.added} hash${r.added === 1 ? "" : "es"} added${reasons}`, r.added ? "good" : "neutral"); if (r.added) { setText(""); setNote(""); } qc.invalidateQueries({ queryKey: ["whitelist"] }); qc.invalidateQueries({ queryKey: ["dups"] }); }, onError: (e) => toast((e as Error).message, "critical"), }); const remove = useMutation({ mutationFn: (id: number) => api(`/whitelist/${id}`, { method: "DELETE" }), onSuccess: () => { toast("Removed from whitelist"); qc.invalidateQueries({ queryKey: ["whitelist"] }); }, onError: (e) => toast((e as Error).message, "critical"), }); const count = text.split(/[\s,;]+/).filter(Boolean).length; return (