import { useState } from "react"; import { useMutation, useQueryClient, useSuspenseQuery } from "@tanstack/react-query"; import { formatTime } from "@/lib/format"; import InlineError from "@/lib/InlineError"; import { blocklistCreateMutation, blocklistDeleteMutation, blocklistUpdateMutation, blocklistsQuery, blocklistsUpdateNowMutation, } from "@/lib/queries"; import type { Blocklist, BlocklistInput } from "@/lib/types"; import BlocklistForm from "./BlocklistForm"; import { useRefreshStatus } from "./refreshStore"; import SourceStatusSection from "./SourceStatusSection"; import { dangerLinkButtonClass, focusRing, linkButtonClass, primaryButtonClass, tableWrapClass, tdClass, thClass, } from "@/ui/classes"; export default function BlocklistsPage() { const queryClient = useQueryClient(); const { data: blocklists } = useSuspenseQuery(blocklistsQuery()); const [editing, setEditing] = useState(null); const create = useMutation(blocklistCreateMutation(queryClient)); const save = useMutation(blocklistUpdateMutation(queryClient)); const toggle = useMutation(blocklistUpdateMutation(queryClient)); const remove = useMutation(blocklistDeleteMutation(queryClient)); const updateNow = useMutation(blocklistsUpdateNowMutation(queryClient)); const sources = useRefreshStatus(); const namesById = new Map(blocklists.map((b) => [b.id, b.name])); async function submitForm(input: BlocklistInput) { if (editing === null) { await create.mutateAsync(input); } else { await save.mutateAsync({ id: editing.id, input: { ...input, is_suggested: editing.is_suggested } }); setEditing(null); } } function toggleEnabled(b: Blocklist) { toggle.mutate({ id: b.id, input: { url: b.url, name: b.name, enabled: !b.enabled, is_suggested: b.is_suggested }, }); } function deleteBlocklist(b: Blocklist) { if (window.confirm(`Delete blocklist "${b.name}"? Its domains stop being blocked.`)) { remove.mutate(b.id); } } const formError = editing === null ? create.error : save.error; const tableError = remove.error ?? toggle.error; return (

Blocklists

{updateNow.isSuccess && !updateNow.isPending && (

Update completed; source status refreshed below.

)} {blocklists.length === 0 ? (

No blocklist sources yet. Add one below.

) : (
{blocklists.map((b) => ( ))}
Name URL Enabled Domains Wildcards Skipped regex Last updated Actions
{b.name} {b.is_suggested && ( Suggested )} {b.url} toggleEnabled(b)} className={focusRing} /> {b.domain_count} {b.wildcard_count} {b.skipped_regex_count} {b.last_updated === null ? "never" : formatTime(b.last_updated)}
)} setEditing(null)} />
); }