@openrails/contentkit-ui 0.99.2 → 0.100.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"moderation.js","names":[],"sources":["../../../src/components/social/moderation.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useEffect, useId, useMemo, useState } from \"react\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { Decision, HeldKind } from \"../../client/content/types.js\";\nimport { toContentKitError, type ContentKitError } from \"../../client/errors.js\";\nimport type { AdminComment, HeldItem, PublicUser } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useAdminLocation, useFlowClient, useStaffAccess, type AdminLocation, type FlowClientOptions, type StaffAccess } from \"../../react/admin.js\";\nimport { useModerationQueue } from \"../../react/comments.js\";\nimport { useErrorReporter, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { keyOf, useContentScope, usePaged } from \"../../react/use-resource.js\";\nimport { Badge } from \"#ckui/ui/badge\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Input } from \"#ckui/ui/input\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"#ckui/ui/tabs\";\nimport { AdminRoot, NoAccess, Pager, Rows, pageOf, useGuarded, type AdminFlowProps, type AdminRouteProps } from \"../admin/shell.js\";\nimport { CommentBanDialog, CommentBans } from \"./bans.js\";\nimport { BannedWords, IPBans } from \"./rules.js\";\nimport { contentError, ErrorLine, RelativeTime, UserAvatar, UserName } from \"./parts.js\";\n\nexport type ModerationSection = \"comments\" | \"queue\" | \"bans\" | \"words\" | \"ips\";\n\nexport interface CommentModerationProps extends AdminFlowProps, AdminRouteProps {\n /** Content kinds offered as a filter of the comment list: codes, or codes with labels. */\n contentKinds?: readonly (string | { value: string; label: string })[];\n /** A link to a commented item. */\n itemHref?: (item: { kind: string; id: string }) => string | undefined;\n userHref?: (user: PublicUser) => string | undefined;\n /** The sections, in order; the first is at basePath, the others at basePath/{section}. Default all. */\n sections?: readonly ModerationSection[];\n pageSize?: number;\n}\n\nconst ALL_SECTIONS: readonly ModerationSection[] = [\"comments\", \"queue\", \"bans\", \"words\", \"ips\"];\n\n/** Which moderation sections the caller may use: every comment, the review queue, the site's bans, banned words and IP bans. */\nexport function useCommentModerationAccess(o: FlowClientOptions = {}): StaffAccess<ModerationSection> {\n return useStaffAccess(ALL_SECTIONS, o);\n}\n\n/**\n * Staff: every comment (deleted, held and rejected ones with their text) to\n * delete, restore or ban its author, by content kind or author; the review\n * queue of held comments and content to approve or reject; the site's\n * comment bans; and the rules every comment and answer meets (banned words,\n * IP bans). Sections, filters and pages live in the URL under basePath;\n * the caller sees only the sections its permissions allow.\n */\nexport function CommentModeration({ client: own, baseUrl, auth, guard, appearance, className, language, messages, sections = ALL_SECTIONS, ...p }: CommentModerationProps) {\n const client = useFlowClient({ client: own, baseUrl, auth });\n return (\n <AdminRoot flow=\"comment-moderation\" appearance={appearance} className={className} language={language} messages={messages} guard={guard}>\n <Moderation {...p} client={client} sections={sections} />\n </AdminRoot>\n );\n}\n\ntype Rest = Omit<CommentModerationProps, \"client\" | \"baseUrl\" | \"auth\" | \"guard\" | \"appearance\" | \"className\" | \"language\" | \"messages\" | \"sections\" | \"basePath\" | \"navigation\"> & {\n client: ContentKitClient;\n};\n\nfunction Moderation({ sections, basePath, navigation, ...p }: Rest & { sections: readonly ModerationSection[]; basePath?: string; navigation?: CommentModerationProps[\"navigation\"] }) {\n const { t } = useMessages();\n const loc = useAdminLocation(basePath, navigation);\n const access = useCommentModerationAccess({ client: p.client });\n const allowed = sections.filter((s) => access.allowed[s]);\n const label: Record<ModerationSection, string> = {\n comments: t(\"moderation.all\"),\n queue: t(\"moderation.queue\"),\n bans: t(\"moderation.bans\"),\n words: t(\"moderationRules.words\"),\n ips: t(\"moderationRules.ips\"),\n };\n const title = <h2 className=\"text-base font-semibold\">{t(\"moderation.title\")}</h2>;\n if (access.loading)\n return (\n <>\n {title}\n <Rows />\n </>\n );\n if (!allowed.length)\n return (\n <>\n {title}\n <NoAccess>{t(\"moderation.noAccess\")}</NoAccess>\n </>\n );\n const viewOf = (s: ModerationSection) => (s === sections[0] ? \"\" : s);\n const asked = (loc.view || sections[0]) as ModerationSection;\n const section = allowed.includes(asked) ? asked : allowed[0]!;\n const view = viewOf(section);\n return (\n <>\n {title}\n <Tabs value={section} onValueChange={(v) => loc.go(viewOf(v as ModerationSection))}>\n {allowed.length > 1 && (\n <TabsList className=\"flex-wrap group-data-horizontal/tabs:h-auto\">\n {allowed.map((s) => (\n <TabsTrigger key={s} value={s} className=\"h-7.5\">\n {label[s]}\n </TabsTrigger>\n ))}\n </TabsList>\n )}\n <TabsContent value={section} className=\"pt-3\">\n {section === \"comments\" && <AllComments {...p} loc={loc} view={view} />}\n {section === \"queue\" && <Queue {...p} loc={loc} view={view} />}\n {section === \"bans\" && (\n <CommentBans\n scope=\"global\"\n pageSize={p.pageSize}\n page={pageOf(loc.query)}\n onPageChange={(n) => loc.go(view, { page: n > 1 ? n : undefined })}\n userHref={p.userHref}\n client={p.client}\n onError={p.onError}\n />\n )}\n {section === \"words\" && <BannedWords client={p.client} onError={p.onError} />}\n {section === \"ips\" && <IPBans client={p.client} onError={p.onError} />}\n </TabsContent>\n </Tabs>\n </>\n );\n}\n\ntype SectionProps = Rest & { loc: AdminLocation; view: string };\n\nfunction status(c: AdminComment): \"deleted\" | \"held\" | \"rejected\" | \"published\" {\n if (c.deleted) return \"deleted\";\n return c.moderation === \"held\" ? \"held\" : c.moderation === \"rejected\" ? \"rejected\" : \"published\";\n}\n\nfunction AllComments({ loc, view, client, ...p }: SectionProps) {\n const m = useMessages();\n const { t } = m;\n const ids = useId();\n const kinds = useMemo(() => (p.contentKinds ?? []).map((k) => (typeof k === \"string\" ? { value: k, label: k } : k)), [p.contentKinds]);\n const kind = loc.query.get(\"kind\") ?? \"\";\n const author = loc.query.get(\"author\") ?? \"\";\n const page = pageOf(loc.query);\n const size = p.pageSize ?? 25;\n const filter = (next: { kind?: string; author?: string }) => loc.go(view, { kind, author, ...next });\n const [authorInput, setAuthorInput] = useState(author);\n useEffect(() => setAuthorInput(author), [author]);\n const { store, scope } = useContentScope(client);\n const list = usePaged(store, keyOf(\"admin-comments-page\", scope, kind, author, size), { type: \"admin-comments\" }, page, size, (q, signal) =>\n client.comments.adminList({ ...q, contentKind: kind || undefined, author: author || undefined }, signal),\n );\n const report = useErrorReporter(p.onError);\n const guarded = useGuarded();\n const [error, setError] = useState<ContentKitError | null>(null);\n const [banning, setBanning] = useState<{ id: string; name: string } | null>(null);\n const act = (op: \"comment.delete\" | \"comment.restore\", fn: () => Promise<unknown>) => {\n setError(null);\n guarded(fn).catch((e) => {\n const err = toContentKitError(e);\n setError(err);\n report(err, op);\n });\n };\n const byName = author ? (list.items.find((c) => c.user_id === author)?.author?.username ?? author) : \"\";\n return (\n <div className=\"grid gap-3\">\n <div className=\"flex flex-wrap items-end gap-2\">\n {kinds.length > 0 && (\n <Select value={kind} onValueChange={(v) => filter({ kind: String(v ?? \"\") })}>\n <SelectTrigger size=\"sm\" aria-label={t(\"moderation.kind\")}>\n <SelectValue>{(v: string) => (v ? (kinds.find((k) => k.value === v)?.label ?? v) : t(\"moderation.allKinds\"))}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"\">{t(\"moderation.allKinds\")}</SelectItem>\n {kinds.map((k) => (\n <SelectItem key={k.value} value={k.value}>\n {k.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n <form\n className=\"flex min-w-56 flex-1 items-end gap-2\"\n onSubmit={(e) => {\n e.preventDefault();\n filter({ author: authorInput.trim() });\n }}\n >\n <div className=\"grid flex-1 gap-1\">\n <Label htmlFor={`${ids}-author`} className=\"sr-only\">\n {t(\"moderation.author\")}\n </Label>\n <Input id={`${ids}-author`} className=\"h-8\" value={authorInput} placeholder={t(\"moderation.authorPlaceholder\")} onChange={(e) => setAuthorInput(e.target.value)} />\n </div>\n <Button type=\"submit\" size=\"sm\" variant=\"outline\">\n {t(\"moderation.filter\")}\n </Button>\n </form>\n </div>\n {author && (\n <div className=\"flex flex-wrap items-center gap-2\" data-ckui=\"author-filter\">\n <span>{t(\"moderation.byAuthor\", { name: byName })}</span>\n <Button size=\"sm\" variant=\"ghost\" onClick={() => filter({ author: \"\" })}>\n {t(\"moderation.clearAuthor\")}\n </Button>\n </div>\n )}\n <ErrorLine error={error} />\n <Listing loading={list.loading} error={list.error} empty={t(\"moderation.empty\")} count={list.items.length} reload={list.reload}>\n {list.items.map((c) => {\n const name = c.author?.username ?? c.anon_name ?? c.user_id ?? t(\"comments.anonymous\");\n const s = status(c);\n const href = p.itemHref?.({ kind: c.content_kind, id: c.content_id });\n return (\n <li key={c.id} className=\"grid gap-2 p-3\" data-ckui=\"admin-comment\" data-status={s}>\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1\">\n <UserAvatar user={c.author} name={name} size=\"sm\" />\n <UserName user={c.author} name={name} href={p.userHref} />\n <RelativeTime at={c.created_at} className=\"text-xs text-muted-foreground\" />\n <Badge variant={s === \"published\" ? \"outline\" : s === \"held\" ? \"secondary\" : \"destructive\"}>{t(`moderation.${s}`)}</Badge>\n {c.reply_to_id && <Badge variant=\"outline\">{t(\"moderation.reply\")}</Badge>}\n <span className=\"text-xs text-muted-foreground\">\n {t(\"moderation.on\")}{\" \"}\n {href ? (\n <a className=\"hover:underline\" href={href}>\n {c.content_kind}/{c.content_id}\n </a>\n ) : (\n `${c.content_kind}/${c.content_id}`\n )}\n </span>\n </div>\n <p className={cn(\"line-clamp-4 break-words whitespace-pre-wrap\", c.deleted && \"text-muted-foreground line-through\")}>{c.body}</p>\n {c.moderation_reason && <p className=\"text-xs text-muted-foreground\">{t(\"moderation.heldBecause\", { reason: c.moderation_reason })}</p>}\n <div className=\"flex flex-wrap gap-2\">\n {c.deleted ? (\n <Button size=\"sm\" variant=\"outline\" onClick={() => act(\"comment.restore\", () => client.comments.restore(c.id))}>\n {t(\"moderation.restore\")}\n </Button>\n ) : (\n <Button size=\"sm\" variant=\"destructive\" onClick={() => act(\"comment.delete\", () => client.comments.delete(c.id))}>\n {t(\"moderation.delete\")}\n </Button>\n )}\n {c.user_id && c.user_id !== author && (\n <Button size=\"sm\" variant=\"ghost\" onClick={() => filter({ author: c.user_id! })}>\n {t(\"moderation.theirComments\")}\n </Button>\n )}\n {c.user_id && (\n <Button size=\"sm\" variant=\"ghost\" onClick={() => setBanning({ id: c.user_id!, name })}>\n {t(\"comments.ban\")}\n </Button>\n )}\n </div>\n </li>\n );\n })}\n </Listing>\n <Pager page={page} hasNext={list.hasNext} disabled={list.loading} onPage={(n) => loc.go(view, { kind, author, page: n > 1 ? n : undefined })} />\n {banning && <CommentBanDialog open onOpenChange={(o) => !o && setBanning(null)} user={banning} scopes={[\"global\"]} client={client} onError={p.onError} />}\n </div>\n );\n}\n\nfunction Queue({ loc, view, ...p }: SectionProps) {\n const m = useMessages();\n const { t } = m;\n const kind: HeldKind = loc.query.get(\"kind\") === \"content\" ? \"content\" : \"comment\";\n const queue = useModerationQueue({ kind, pageSize: p.pageSize, client: p.client });\n return (\n <div className=\"grid gap-3\">\n <div className=\"flex gap-2\" role=\"group\" aria-label={t(\"moderation.queue\")}>\n {([\"comment\", \"content\"] as const).map((k) => (\n <Button key={k} size=\"sm\" variant={kind === k ? \"default\" : \"outline\"} aria-pressed={kind === k} onClick={() => loc.go(view, { kind: k === \"comment\" ? undefined : k })}>\n {t(k === \"content\" ? \"moderation.queueContent\" : \"moderation.queueComments\")}\n </Button>\n ))}\n </div>\n <Listing loading={queue.loading} error={queue.error} empty={t(\"moderation.queueEmpty\")} count={queue.items.length} reload={queue.reload}>\n {queue.items.map((it) => (\n <HeldRow key={it.id} item={it} itemHref={p.itemHref} userHref={p.userHref} onResolve={queue.resolve} reload={queue.reload} onError={p.onError} />\n ))}\n </Listing>\n {queue.hasMore && (\n <Button variant=\"outline\" className=\"justify-self-center\" disabled={queue.loadingMore} onClick={() => void queue.loadMore()}>\n {t(\"moderation.loadMore\")}\n </Button>\n )}\n </div>\n );\n}\n\nfunction HeldRow(p: {\n item: HeldItem;\n itemHref?: Rest[\"itemHref\"];\n userHref?: Rest[\"userHref\"];\n onResolve: (item: HeldItem, d: Decision, reason?: string) => Promise<void>;\n reload: () => void;\n onError?: ContentKitErrorHandler;\n}) {\n const m = useMessages();\n const { t } = m;\n const report = useErrorReporter(p.onError);\n const guarded = useGuarded();\n const { item: it } = p;\n const [rejecting, setRejecting] = useState(false);\n const [reason, setReason] = useState(\"\");\n const [pending, setPending] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const name = it.author?.username ?? it.anon_name ?? it.author_id ?? t(\"comments.anonymous\");\n const href = p.itemHref?.({ kind: it.ref.content_kind, id: it.ref.content_id });\n const resolve = async (d: Decision) => {\n setPending(true);\n setError(null);\n try {\n await guarded(() => p.onResolve(it, d, d === \"reject\" ? reason.trim() || undefined : undefined));\n } catch (e) {\n const err = toContentKitError(e);\n report(err, \"moderation.resolve\");\n // A stale revision: the text changed since it was listed.\n if (err.code === \"not_found\" || err.code === \"conflict\") {\n setError(t(\"moderation.stale\"));\n p.reload();\n } else setError(contentError(m, err));\n } finally {\n setPending(false);\n }\n };\n return (\n <li className=\"grid gap-2 p-3\" data-ckui=\"held-item\">\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1\">\n <UserAvatar user={it.author} name={name} size=\"sm\" />\n <UserName user={it.author} name={name} href={p.userHref} />\n <RelativeTime at={it.held_at} className=\"text-xs text-muted-foreground\" />\n {href && (\n <a className=\"text-xs text-muted-foreground hover:underline\" href={href}>\n {t(\"moderation.on\")} {it.ref.content_kind}/{it.ref.content_id}\n </a>\n )}\n </div>\n {it.title && <p className=\"font-medium\">{it.title}</p>}\n <p className=\"line-clamp-6 break-words whitespace-pre-wrap\">{it.body}</p>\n {(it.reason || it.error) && <p className=\"text-xs text-muted-foreground\">{t(\"moderation.heldBecause\", { reason: it.reason || it.error || \"\" })}</p>}\n {rejecting && (\n <Input value={reason} maxLength={500} placeholder={t(\"moderation.rejectReason\")} aria-label={t(\"moderation.rejectReason\")} onChange={(e) => setReason(e.target.value)} autoFocus />\n )}\n <div className=\"flex flex-wrap gap-2\">\n <Button size=\"sm\" disabled={pending} onClick={() => void resolve(\"approve\")}>\n {t(\"moderation.approve\")}\n </Button>\n <Button size=\"sm\" variant=\"destructive\" disabled={pending} onClick={() => (rejecting ? void resolve(\"reject\") : setRejecting(true))}>\n {t(\"moderation.reject\")}\n </Button>\n {rejecting && (\n <Button size=\"sm\" variant=\"ghost\" onClick={() => setRejecting(false)}>\n {t(\"common.cancel\")}\n </Button>\n )}\n </div>\n {error && <ErrorLine>{error}</ErrorLine>}\n </li>\n );\n}\n\nfunction Listing(p: { loading: boolean; error?: ContentKitError; empty: string; count: number; reload: () => void; children: React.ReactNode }) {\n const m = useMessages();\n const { t } = m;\n if (p.loading && !p.count) {\n return (\n <div className=\"grid gap-3\" aria-busy>\n <Skeleton className=\"h-16 w-full\" />\n <Skeleton className=\"h-16 w-full\" />\n </div>\n );\n }\n if (p.error && !p.count) {\n return (\n <div className=\"grid justify-items-start gap-2\">\n <ErrorLine>{p.error.code === \"forbidden\" || p.error.code === \"unauthorized\" ? contentError(m, p.error) : t(\"moderation.loadFailed\")}</ErrorLine>\n <Button size=\"sm\" variant=\"outline\" onClick={p.reload}>\n {t(\"common.retry\")}\n </Button>\n </div>\n );\n }\n if (!p.count) return <p className=\"text-muted-foreground\">{p.empty}</p>;\n return <ul className=\"grid divide-y divide-border rounded-lg border border-border\">{p.children}</ul>;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"moderation.js","names":[],"sources":["../../../src/components/social/moderation.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useEffect, useId, useMemo, useState } from \"react\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { Decision, HeldKind } from \"../../client/content/types.js\";\nimport { toContentKitError, type ContentKitError } from \"../../client/errors.js\";\nimport type { AdminComment, HeldItem, PublicUser } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useAdminLocation, useFlowClient, useStaffAccess, type AdminLocation, type FlowClientOptions, type StaffAccess } from \"../../react/admin.js\";\nimport { useModerationQueue } from \"../../react/comments.js\";\nimport { useErrorReporter, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { keyOf, useContentScope, usePaged } from \"../../react/use-resource.js\";\nimport { Badge } from \"#ckui/ui/badge\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Input } from \"#ckui/ui/input\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"#ckui/ui/tabs\";\nimport { AdminRoot, NoAccess, Pager, Rows, pageOf, useGuarded, type AdminFlowProps, type AdminRouteProps } from \"../admin/shell.js\";\nimport { CommentBanDialog, CommentBans } from \"./bans.js\";\nimport { BannedWords, IPBans } from \"./rules.js\";\nimport { contentError, ErrorLine, RelativeTime, UserAvatar, UserName } from \"./parts.js\";\n\nexport type ModerationSection = \"comments\" | \"queue\" | \"bans\" | \"words\" | \"ips\";\n\nexport interface CommentModerationProps extends AdminFlowProps, AdminRouteProps {\n /** Content kinds offered as a filter of the comment list: codes, or codes with labels. */\n contentKinds?: readonly (string | { value: string; label: string })[];\n /** A link to a commented item. */\n itemHref?: (item: { kind: string; id: string }) => string | undefined;\n userHref?: (user: PublicUser) => string | undefined;\n /** The sections, in order; the first is at basePath, the others at basePath/{section}. Default all. */\n sections?: readonly ModerationSection[];\n pageSize?: number;\n}\n\nconst ALL_SECTIONS: readonly ModerationSection[] = [\"comments\", \"queue\", \"bans\", \"words\", \"ips\"];\n\n/** Which moderation sections the caller may use: every comment, the review queue, the site's bans, banned words and IP bans. */\nexport function useCommentModerationAccess(o: FlowClientOptions = {}): StaffAccess<ModerationSection> {\n return useStaffAccess(ALL_SECTIONS, o);\n}\n\n/**\n * Staff: every comment (deleted, held and rejected ones with their text) to\n * delete, restore or ban its author, by content kind or author; the review\n * queue of held comments and content to approve or reject; the site's\n * comment bans; and the rules every comment and answer meets (banned words,\n * IP bans). Sections, filters and pages live in the URL under basePath;\n * the caller sees only the sections its permissions allow.\n */\nexport function CommentModeration({ client: own, baseUrl, auth, guard, appearance, className, language, messages, sections = ALL_SECTIONS, ...p }: CommentModerationProps) {\n const client = useFlowClient({ client: own, baseUrl, auth });\n return (\n <AdminRoot flow=\"comment-moderation\" appearance={appearance} className={className} language={language} messages={messages} guard={guard}>\n <Moderation {...p} client={client} sections={sections} />\n </AdminRoot>\n );\n}\n\ntype Rest = Omit<CommentModerationProps, \"client\" | \"baseUrl\" | \"auth\" | \"guard\" | \"appearance\" | \"className\" | \"language\" | \"messages\" | \"sections\" | \"basePath\" | \"navigation\"> & {\n client: ContentKitClient;\n};\n\nfunction Moderation({ sections, basePath, navigation, ...p }: Rest & { sections: readonly ModerationSection[]; basePath?: string; navigation?: CommentModerationProps[\"navigation\"] }) {\n const { t } = useMessages();\n const loc = useAdminLocation(basePath, navigation);\n const access = useCommentModerationAccess({ client: p.client });\n const allowed = sections.filter((s) => access.allowed[s]);\n const label: Record<ModerationSection, string> = {\n comments: t(\"moderation.all\"),\n queue: t(\"moderation.queue\"),\n bans: t(\"moderation.bans\"),\n words: t(\"moderationRules.words\"),\n ips: t(\"moderationRules.ips\"),\n };\n const title = <h2 className=\"text-base font-semibold\">{t(\"moderation.title\")}</h2>;\n if (access.loading)\n return (\n <>\n {title}\n <Rows />\n </>\n );\n if (!allowed.length)\n return (\n <>\n {title}\n <NoAccess>{t(\"moderation.noAccess\")}</NoAccess>\n </>\n );\n const viewOf = (s: ModerationSection) => (s === sections[0] ? \"\" : s);\n const asked = (loc.view || sections[0]) as ModerationSection;\n const section = allowed.includes(asked) ? asked : allowed[0]!;\n const view = viewOf(section);\n return (\n <>\n {title}\n <Tabs value={section} onValueChange={(v) => loc.go(viewOf(v as ModerationSection))}>\n {allowed.length > 1 && (\n <TabsList className=\"flex-wrap group-data-horizontal/tabs:h-auto\">\n {allowed.map((s) => (\n <TabsTrigger key={s} value={s} className=\"h-7.5\">\n {label[s]}\n </TabsTrigger>\n ))}\n </TabsList>\n )}\n <TabsContent value={section} className=\"pt-3\">\n {section === \"comments\" && <AllComments {...p} loc={loc} view={view} />}\n {section === \"queue\" && <Queue {...p} loc={loc} view={view} />}\n {section === \"bans\" && (\n <CommentBans\n scope=\"global\"\n pageSize={p.pageSize}\n page={pageOf(loc.query)}\n onPageChange={(n) => loc.go(view, { page: n > 1 ? n : undefined })}\n userHref={p.userHref}\n client={p.client}\n onError={p.onError}\n />\n )}\n {section === \"words\" && <BannedWords client={p.client} onError={p.onError} />}\n {section === \"ips\" && <IPBans client={p.client} onError={p.onError} />}\n </TabsContent>\n </Tabs>\n </>\n );\n}\n\ntype SectionProps = Rest & { loc: AdminLocation; view: string };\n\nfunction status(c: AdminComment): \"deleted\" | \"held\" | \"rejected\" | \"published\" {\n if (c.deleted) return \"deleted\";\n return c.moderation === \"held\" ? \"held\" : c.moderation === \"rejected\" ? \"rejected\" : \"published\";\n}\n\nfunction AllComments({ loc, view, client, ...p }: SectionProps) {\n const m = useMessages();\n const { t } = m;\n const ids = useId();\n const kinds = useMemo(() => (p.contentKinds ?? []).map((k) => (typeof k === \"string\" ? { value: k, label: k } : k)), [p.contentKinds]);\n const kind = loc.query.get(\"kind\") ?? \"\";\n const author = loc.query.get(\"author\") ?? \"\";\n const page = pageOf(loc.query);\n const size = p.pageSize ?? 25;\n const filter = (next: { kind?: string; author?: string }) => loc.go(view, { kind, author, ...next });\n const [authorInput, setAuthorInput] = useState(author);\n useEffect(() => setAuthorInput(author), [author]);\n const { store, scope } = useContentScope(client);\n const list = usePaged(store, keyOf(\"admin-comments-page\", scope, kind, author, size), { type: \"admin-comments\" }, page, size, (q, signal) =>\n client.comments.adminList({ ...q, contentKind: kind || undefined, author: author || undefined }, signal),\n );\n const report = useErrorReporter(p.onError);\n const guarded = useGuarded();\n const [error, setError] = useState<ContentKitError | null>(null);\n const [banning, setBanning] = useState<{ id: string; name: string } | null>(null);\n const act = (op: \"comment.delete\" | \"comment.restore\", fn: () => Promise<unknown>) => {\n setError(null);\n guarded(fn).catch((e) => {\n const err = toContentKitError(e);\n setError(err);\n report(err, op);\n });\n };\n const byName = author ? (list.items.find((c) => c.user_id === author)?.author?.username ?? author) : \"\";\n return (\n <div className=\"grid gap-3\">\n <div className=\"flex flex-wrap items-end gap-2\">\n {kinds.length > 0 && (\n <Select value={kind} onValueChange={(v) => filter({ kind: String(v ?? \"\") })}>\n <SelectTrigger size=\"sm\" aria-label={t(\"moderation.kind\")}>\n <SelectValue>{(v: string) => (v ? (kinds.find((k) => k.value === v)?.label ?? v) : t(\"moderation.allKinds\"))}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"\">{t(\"moderation.allKinds\")}</SelectItem>\n {kinds.map((k) => (\n <SelectItem key={k.value} value={k.value}>\n {k.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n <form\n className=\"flex min-w-56 flex-1 items-end gap-2\"\n onSubmit={(e) => {\n e.preventDefault();\n filter({ author: authorInput.trim() });\n }}\n >\n <div className=\"grid flex-1 gap-1\">\n <Label htmlFor={`${ids}-author`} className=\"sr-only\">\n {t(\"moderation.author\")}\n </Label>\n <Input id={`${ids}-author`} className=\"h-8\" value={authorInput} placeholder={t(\"moderation.authorPlaceholder\")} onChange={(e) => setAuthorInput(e.target.value)} />\n </div>\n <Button type=\"submit\" size=\"sm\" variant=\"outline\">\n {t(\"moderation.filter\")}\n </Button>\n </form>\n </div>\n {author && (\n <div className=\"flex flex-wrap items-center gap-2\" data-ckui=\"author-filter\">\n <span>{t(\"moderation.byAuthor\", { name: byName })}</span>\n <Button size=\"sm\" variant=\"ghost\" onClick={() => filter({ author: \"\" })}>\n {t(\"moderation.clearAuthor\")}\n </Button>\n </div>\n )}\n <ErrorLine error={error} />\n <Listing loading={list.loading} error={list.error} empty={t(\"moderation.empty\")} count={list.items.length} reload={list.reload}>\n {list.items.map((c) => {\n const name = c.author?.username ?? c.anon_name ?? c.user_id ?? t(\"comments.anonymous\");\n const s = status(c);\n const href = p.itemHref?.({ kind: c.content_kind, id: c.content_id });\n return (\n <li key={c.id} className=\"grid gap-2 p-3\" data-ckui=\"admin-comment\" data-status={s}>\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1\">\n <UserAvatar user={c.author} name={name} size=\"sm\" />\n <UserName user={c.author} name={name} href={p.userHref} />\n <RelativeTime at={c.created_at} className=\"text-xs text-muted-foreground\" />\n <Badge variant={s === \"published\" ? \"outline\" : s === \"held\" ? \"secondary\" : \"destructive\"}>{t(`moderation.${s}`)}</Badge>\n {c.reply_to_id && <Badge variant=\"outline\">{t(\"moderation.reply\")}</Badge>}\n <span className=\"text-xs text-muted-foreground\">\n {t(\"moderation.on\")}{\" \"}\n {href ? (\n <a className=\"hover:underline\" href={href}>\n {c.content_kind}/{c.content_id}\n </a>\n ) : (\n `${c.content_kind}/${c.content_id}`\n )}\n </span>\n </div>\n <p className={cn(\"line-clamp-4 break-words whitespace-pre-wrap\", c.deleted && \"text-muted-foreground line-through\")}>{c.body}</p>\n {c.moderation_reason && <p className=\"text-xs text-muted-foreground\">{t(\"moderation.heldBecause\", { reason: c.moderation_reason })}</p>}\n <div className=\"flex flex-wrap gap-2\">\n {c.deleted ? (\n <Button size=\"sm\" variant=\"outline\" onClick={() => act(\"comment.restore\", () => client.comments.restore(c.id))}>\n {t(\"moderation.restore\")}\n </Button>\n ) : (\n <Button size=\"sm\" variant=\"destructive\" onClick={() => act(\"comment.delete\", () => client.comments.delete(c.id))}>\n {t(\"moderation.delete\")}\n </Button>\n )}\n {c.user_id && c.user_id !== author && (\n <Button size=\"sm\" variant=\"ghost\" onClick={() => filter({ author: c.user_id! })}>\n {t(\"moderation.theirComments\")}\n </Button>\n )}\n {c.user_id && (\n <Button size=\"sm\" variant=\"ghost\" onClick={() => setBanning({ id: c.user_id!, name })}>\n {t(\"comments.ban\")}\n </Button>\n )}\n </div>\n </li>\n );\n })}\n </Listing>\n <Pager page={page} hasNext={list.hasNext} disabled={list.loading} onPage={(n) => loc.go(view, { kind, author, page: n > 1 ? n : undefined })} />\n {banning && <CommentBanDialog open onOpenChange={(o) => !o && setBanning(null)} user={banning} scopes={[\"global\"]} client={client} onError={p.onError} />}\n </div>\n );\n}\n\nfunction Queue({ loc, view, ...p }: SectionProps) {\n const m = useMessages();\n const { t } = m;\n const kind: HeldKind = loc.query.get(\"kind\") === \"content\" ? \"content\" : \"comment\";\n const queue = useModerationQueue({ kind, pageSize: p.pageSize, client: p.client });\n return (\n <div className=\"grid gap-3\">\n <div className=\"flex gap-2\" role=\"group\" aria-label={t(\"moderation.queue\")}>\n {([\"comment\", \"content\"] as const).map((k) => (\n <Button key={k} size=\"sm\" variant={kind === k ? \"default\" : \"outline\"} aria-pressed={kind === k} onClick={() => loc.go(view, { kind: k === \"comment\" ? undefined : k })}>\n {t(k === \"content\" ? \"moderation.queueContent\" : \"moderation.queueComments\")}\n </Button>\n ))}\n </div>\n <Listing loading={queue.loading || queue.loadingMore} error={queue.error} empty={t(\"moderation.queueEmpty\")} count={queue.items.length} reload={queue.items.length && queue.hasMore ? () => void queue.loadMore() : queue.reload}>\n {queue.items.map((it) => (\n <HeldRow key={it.id} item={it} itemHref={p.itemHref} userHref={p.userHref} onResolve={queue.resolve} reload={queue.reload} onError={p.onError} />\n ))}\n </Listing>\n {queue.hasMore && !queue.error && (\n <Button variant=\"outline\" className=\"justify-self-center\" disabled={queue.loadingMore} onClick={() => void queue.loadMore()}>\n {t(\"moderation.loadMore\")}\n </Button>\n )}\n </div>\n );\n}\n\nfunction HeldRow(p: {\n item: HeldItem;\n itemHref?: Rest[\"itemHref\"];\n userHref?: Rest[\"userHref\"];\n onResolve: (item: HeldItem, d: Decision, reason?: string) => Promise<void>;\n reload: () => void;\n onError?: ContentKitErrorHandler;\n}) {\n const m = useMessages();\n const { t } = m;\n const report = useErrorReporter(p.onError);\n const guarded = useGuarded();\n const { item: it } = p;\n const [rejecting, setRejecting] = useState(false);\n const [reason, setReason] = useState(\"\");\n const [pending, setPending] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const name = it.author?.username ?? it.anon_name ?? it.author_id ?? t(\"comments.anonymous\");\n const href = p.itemHref?.({ kind: it.ref.content_kind, id: it.ref.content_id });\n const resolve = async (d: Decision) => {\n setPending(true);\n setError(null);\n try {\n await guarded(() => p.onResolve(it, d, d === \"reject\" ? reason.trim() || undefined : undefined));\n } catch (e) {\n const err = toContentKitError(e);\n report(err, \"moderation.resolve\");\n // A stale revision: the text changed since it was listed.\n if (err.code === \"not_found\" || err.code === \"conflict\") {\n setError(t(\"moderation.stale\"));\n p.reload();\n } else setError(contentError(m, err));\n } finally {\n setPending(false);\n }\n };\n return (\n <li className=\"grid gap-2 p-3\" data-ckui=\"held-item\">\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1\">\n <UserAvatar user={it.author} name={name} size=\"sm\" />\n <UserName user={it.author} name={name} href={p.userHref} />\n <RelativeTime at={it.held_at} className=\"text-xs text-muted-foreground\" />\n {href && (\n <a className=\"text-xs text-muted-foreground hover:underline\" href={href}>\n {t(\"moderation.on\")} {it.ref.content_kind}/{it.ref.content_id}\n </a>\n )}\n </div>\n {it.title && <p className=\"font-medium\">{it.title}</p>}\n <p className=\"line-clamp-6 break-words whitespace-pre-wrap\">{it.body}</p>\n {(it.reason || it.error) && <p className=\"text-xs text-muted-foreground\">{t(\"moderation.heldBecause\", { reason: it.reason || it.error || \"\" })}</p>}\n {rejecting && (\n <Input value={reason} maxLength={500} placeholder={t(\"moderation.rejectReason\")} aria-label={t(\"moderation.rejectReason\")} onChange={(e) => setReason(e.target.value)} autoFocus />\n )}\n <div className=\"flex flex-wrap gap-2\">\n <Button size=\"sm\" disabled={pending} onClick={() => void resolve(\"approve\")}>\n {t(\"moderation.approve\")}\n </Button>\n <Button size=\"sm\" variant=\"destructive\" disabled={pending} onClick={() => (rejecting ? void resolve(\"reject\") : setRejecting(true))}>\n {t(\"moderation.reject\")}\n </Button>\n {rejecting && (\n <Button size=\"sm\" variant=\"ghost\" onClick={() => setRejecting(false)}>\n {t(\"common.cancel\")}\n </Button>\n )}\n </div>\n {error && <ErrorLine>{error}</ErrorLine>}\n </li>\n );\n}\n\nfunction Listing(p: { loading: boolean; error?: ContentKitError; empty: string; count: number; reload: () => void; children: React.ReactNode }) {\n const m = useMessages();\n const { t } = m;\n if (p.loading && !p.count) {\n return (\n <div className=\"grid gap-3\" aria-busy>\n <Skeleton className=\"h-16 w-full\" />\n <Skeleton className=\"h-16 w-full\" />\n </div>\n );\n }\n if (p.error && !p.count) {\n return (\n <div className=\"grid justify-items-start gap-2\">\n <ErrorLine>{p.error.code === \"forbidden\" || p.error.code === \"unauthorized\" ? contentError(m, p.error) : t(\"moderation.loadFailed\")}</ErrorLine>\n <Button size=\"sm\" variant=\"outline\" onClick={p.reload}>\n {t(\"common.retry\")}\n </Button>\n </div>\n );\n }\n if (!p.count) return <p className=\"text-muted-foreground\">{p.empty}</p>;\n return <>\n <ul className=\"grid divide-y divide-border rounded-lg border border-border\">{p.children}</ul>\n {p.error && <div className=\"grid justify-items-center gap-2\">\n <ErrorLine error={p.error} />\n <Button size=\"sm\" variant=\"outline\" disabled={p.loading} onClick={p.reload}>\n {t(\"common.retry\")}\n </Button>\n </div>}\n </>;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAoCA,IAAM,IAA6C;CAAC;CAAY;CAAS;CAAQ;CAAS;AAAK;AAG/F,SAAgB,EAA2B,IAAuB,CAAC,GAAmC;CACpG,OAAO,EAAe,GAAc,CAAC;AACvC;AAUA,SAAgB,EAAkB,EAAE,QAAQ,GAAK,YAAS,SAAM,UAAO,eAAY,cAAW,aAAU,aAAU,cAAW,GAAc,GAAG,KAA6B;CACzK,IAAM,IAAS,EAAc;EAAE,QAAQ;EAAK;EAAS;CAAK,CAAC;CAC3D,OACE,kBAAC,GAAD;EAAW,MAAK;EAAiC;EAAuB;EAAqB;EAAoB;EAAiB;EAChI,UAAA,kBAAC,GAAD;GAAY,GAAI;GAAW;GAAkB;EAAW,CAAA;CAC/C,CAAA;AAEf;AAMA,SAAS,EAAW,EAAE,aAAU,aAAU,eAAY,GAAG,KAA8H;CACrL,IAAM,EAAE,SAAM,EAAY,GACpB,IAAM,EAAiB,GAAU,CAAU,GAC3C,IAAS,EAA2B,EAAE,QAAQ,EAAE,OAAO,CAAC,GACxD,IAAU,EAAS,QAAQ,MAAM,EAAO,QAAQ,EAAE,GAClD,IAA2C;EAC/C,UAAU,EAAE,gBAAgB;EAC5B,OAAO,EAAE,kBAAkB;EAC3B,MAAM,EAAE,iBAAiB;EACzB,OAAO,EAAE,uBAAuB;EAChC,KAAK,EAAE,qBAAqB;CAC9B,GACM,IAAQ,kBAAC,MAAD;EAAI,WAAU;EAA2B,UAAA,EAAE,kBAAkB;CAAM,CAAA;CACjF,IAAI,EAAO,SACT,OACE,kBAAA,GAAA,EAAA,UAAA,CACG,GACD,kBAAC,GAAD,CAAO,CAAA,CACP,EAAA,CAAA;CAEN,IAAI,CAAC,EAAQ,QACX,OACE,kBAAA,GAAA,EAAA,UAAA,CACG,GACD,kBAAC,GAAD,EAAA,UAAW,EAAE,qBAAqB,EAAY,CAAA,CAC9C,EAAA,CAAA;CAEN,IAAM,KAAU,MAA0B,MAAM,EAAS,KAAK,KAAK,GAC7D,IAAS,EAAI,QAAQ,EAAS,IAC9B,IAAU,EAAQ,SAAS,CAAK,IAAI,IAAQ,EAAQ,IACpD,IAAO,EAAO,CAAO;CAC3B,OACE,kBAAA,GAAA,EAAA,UAAA,CACG,GACD,kBAAC,GAAD;EAAM,OAAO;EAAS,gBAAgB,MAAM,EAAI,GAAG,EAAO,CAAsB,CAAC;EAAjF,UAAA,CACG,EAAQ,SAAS,KAChB,kBAAC,GAAD;GAAU,WAAU;GACjB,UAAA,EAAQ,KAAK,MACZ,kBAAC,GAAD;IAAqB,OAAO;IAAG,WAAU;IACtC,UAAA,EAAM;GACI,GAFK,CAEL,CACd;EACO,CAAA,GAEZ,kBAAC,GAAD;GAAa,OAAO;GAAS,WAAU;GAAvC,UAAA;IACG,MAAY,cAAc,kBAAC,GAAD;KAAa,GAAI;KAAQ;KAAW;IAAO,CAAA;IACrE,MAAY,WAAW,kBAAC,GAAD;KAAO,GAAI;KAAQ;KAAW;IAAO,CAAA;IAC5D,MAAY,UACX,kBAAC,GAAD;KACE,OAAM;KACN,UAAU,EAAE;KACZ,MAAM,EAAO,EAAI,KAAK;KACtB,eAAe,MAAM,EAAI,GAAG,GAAM,EAAE,MAAM,IAAI,IAAI,IAAI,KAAA,EAAU,CAAC;KACjE,UAAU,EAAE;KACZ,QAAQ,EAAE;KACV,SAAS,EAAE;IACZ,CAAA;IAEF,MAAY,WAAW,kBAAC,GAAD;KAAa,QAAQ,EAAE;KAAQ,SAAS,EAAE;IAAU,CAAA;IAC3E,MAAY,SAAS,kBAAC,GAAD;KAAQ,QAAQ,EAAE;KAAQ,SAAS,EAAE;IAAU,CAAA;GAC1D;EACT,CAAA,CAAA;CACN,CAAA,CAAA,EAAA,CAAA;AAEN;AAIA,SAAS,EAAO,GAAgE;CAE9E,OADI,EAAE,UAAgB,YACf,EAAE,eAAe,SAAS,SAAS,EAAE,eAAe,aAAa,aAAa;AACvF;AAEA,SAAS,EAAY,EAAE,QAAK,SAAM,WAAQ,GAAG,KAAmB;CAE9D,IAAM,EAAE,SADE,EACI,GACR,IAAM,GAAM,GACZ,IAAQ,UAAe,EAAE,gBAAgB,CAAC,EAAA,CAAG,KAAK,MAAO,OAAO,KAAM,WAAW;EAAE,OAAO;EAAG,OAAO;CAAE,IAAI,CAAE,GAAG,CAAC,EAAE,YAAY,CAAC,GAC/H,IAAO,EAAI,MAAM,IAAI,MAAM,KAAK,IAChC,IAAS,EAAI,MAAM,IAAI,QAAQ,KAAK,IACpC,IAAO,EAAO,EAAI,KAAK,GACvB,IAAO,EAAE,YAAY,IACrB,KAAU,MAA6C,EAAI,GAAG,GAAM;EAAE;EAAM;EAAQ,GAAG;CAAK,CAAC,GAC7F,CAAC,GAAa,KAAkB,EAAS,CAAM;CACrD,QAAgB,EAAe,CAAM,GAAG,CAAC,CAAM,CAAC;CAChD,IAAM,EAAE,UAAO,aAAU,EAAgB,CAAM,GACzC,IAAO,EAAS,GAAO,EAAM,uBAAuB,GAAO,GAAM,GAAQ,CAAI,GAAG,EAAE,MAAM,iBAAiB,GAAG,GAAM,IAAO,GAAG,MAChI,EAAO,SAAS,UAAU;EAAE,GAAG;EAAG,aAAa,KAAQ,KAAA;EAAW,QAAQ,KAAU,KAAA;CAAU,GAAG,CAAM,CACzG,GACM,IAAS,EAAiB,EAAE,OAAO,GACnC,IAAU,EAAW,GACrB,CAAC,GAAO,KAAY,EAAiC,IAAI,GACzD,CAAC,GAAS,KAAc,EAA8C,IAAI,GAC1E,KAAO,GAA0C,MAA+B;EAEpF,AADA,EAAS,IAAI,GACb,EAAQ,CAAE,CAAC,CAAC,OAAO,MAAM;GACvB,IAAM,IAAM,EAAkB,CAAC;GAE/B,AADA,EAAS,CAAG,GACZ,EAAO,GAAK,CAAE;EAChB,CAAC;CACH,GACM,KAAS,IAAU,EAAK,MAAM,MAAM,MAAM,EAAE,YAAY,CAAM,CAAC,EAAE,QAAQ,YAAY,IAAU;CACrG,OACE,kBAAC,OAAD;EAAK,WAAU;EAAf,UAAA;GACE,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACG,EAAM,SAAS,KACd,kBAAC,GAAD;KAAQ,OAAO;KAAM,gBAAgB,MAAM,EAAO,EAAE,MAAM,OAAO,KAAK,EAAE,EAAE,CAAC;KAA3E,UAAA,CACE,kBAAC,GAAD;MAAe,MAAK;MAAK,cAAY,EAAE,iBAAiB;MACtD,UAAA,kBAAC,GAAD,EAAA,WAAe,MAAe,IAAK,EAAM,MAAM,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,SAAS,IAAK,EAAE,qBAAqB,EAAgB,CAAA;KAC7G,CAAA,GACf,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;MAAY,OAAM;MAAI,UAAA,EAAE,qBAAqB;KAAc,CAAA,GAC1D,EAAM,KAAK,MACV,kBAAC,GAAD;MAA0B,OAAO,EAAE;MAChC,UAAA,EAAE;KACO,GAFK,EAAE,KAEP,CACb,CACY,EAAA,CAAA,CACT;IAEV,CAAA,GAAA,kBAAC,QAAD;KACE,WAAU;KACV,WAAW,MAAM;MAEf,AADA,EAAE,eAAe,GACjB,EAAO,EAAE,QAAQ,EAAY,KAAK,EAAE,CAAC;KACvC;KALF,UAAA,CAOE,kBAAC,OAAD;MAAK,WAAU;MAAf,UAAA,CACE,kBAAC,GAAD;OAAO,SAAS,GAAG,EAAI;OAAU,WAAU;OACxC,UAAA,EAAE,mBAAmB;MACjB,CAAA,GACP,kBAAC,GAAD;OAAO,IAAI,GAAG,EAAI;OAAU,WAAU;OAAM,OAAO;OAAa,aAAa,EAAE,8BAA8B;OAAG,WAAW,MAAM,EAAe,EAAE,OAAO,KAAK;MAAI,CAAA,CAC/J;KACL,CAAA,GAAA,kBAAC,GAAD;MAAQ,MAAK;MAAS,MAAK;MAAK,SAAQ;MACrC,UAAA,EAAE,mBAAmB;KAChB,CAAA,CACJ;IACH,CAAA,CAAA;;GACJ,KACC,kBAAC,OAAD;IAAK,WAAU;IAAoC,aAAU;IAA7D,UAAA,CACE,kBAAC,QAAD,EAAA,UAAO,EAAE,uBAAuB,EAAE,MAAM,GAAO,CAAC,EAAQ,CAAA,GACxD,kBAAC,GAAD;KAAQ,MAAK;KAAK,SAAQ;KAAQ,eAAe,EAAO,EAAE,QAAQ,GAAG,CAAC;KACnE,UAAA,EAAE,wBAAwB;IACrB,CAAA,CACL;;GAEP,kBAAC,GAAD,EAAkB,SAAQ,CAAA;GAC1B,kBAAC,GAAD;IAAS,SAAS,EAAK;IAAS,OAAO,EAAK;IAAO,OAAO,EAAE,kBAAkB;IAAG,OAAO,EAAK,MAAM;IAAQ,QAAQ,EAAK;IACrH,UAAA,EAAK,MAAM,KAAK,MAAM;KACrB,IAAM,IAAO,EAAE,QAAQ,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,oBAAoB,GAC/E,IAAI,EAAO,CAAC,GACZ,IAAO,EAAE,WAAW;MAAE,MAAM,EAAE;MAAc,IAAI,EAAE;KAAW,CAAC;KACpE,OACE,kBAAC,MAAD;MAAe,WAAU;MAAiB,aAAU;MAAgB,eAAa;MAAjF,UAAA;OACE,kBAAC,OAAD;QAAK,WAAU;QAAf,UAAA;SACE,kBAAC,GAAD;UAAY,MAAM,EAAE;UAAc;UAAM,MAAK;SAAM,CAAA;SACnD,kBAAC,GAAD;UAAU,MAAM,EAAE;UAAc;UAAM,MAAM,EAAE;SAAW,CAAA;SACzD,kBAAC,GAAD;UAAc,IAAI,EAAE;UAAY,WAAU;SAAiC,CAAA;SAC3E,kBAAC,GAAD;UAAO,SAAS,MAAM,cAAc,YAAY,MAAM,SAAS,cAAc;UAAgB,UAAA,EAAE,cAAc,GAAG;SAAS,CAAA;SACxH,EAAE,eAAe,kBAAC,GAAD;UAAO,SAAQ;UAAW,UAAA,EAAE,kBAAkB;SAAS,CAAA;SACzE,kBAAC,QAAD;UAAM,WAAU;UAAhB,UAAA;WACG,EAAE,eAAe;WAAG;WACpB,IACC,kBAAC,KAAD;YAAG,WAAU;YAAwB;YAArC,UAAA;aACG,EAAE;aAAa;aAAE,EAAE;YACnB;WAEH,CAAA,IAAA,GAAG,EAAE,aAAa,GAAG,EAAE;UAErB;;QACH;;OACL,kBAAC,KAAD;QAAG,WAAW,EAAG,gDAAgD,EAAE,WAAW,oCAAoC;QAAI,UAAA,EAAE;OAAQ,CAAA;OAC/H,EAAE,qBAAqB,kBAAC,KAAD;QAAG,WAAU;QAAiC,UAAA,EAAE,0BAA0B,EAAE,QAAQ,EAAE,kBAAkB,CAAC;OAAK,CAAA;OACtI,kBAAC,OAAD;QAAK,WAAU;QAAf,UAAA;SACG,EAAE,UACD,kBAAC,GAAD;UAAQ,MAAK;UAAK,SAAQ;UAAU,eAAe,EAAI,yBAAyB,EAAO,SAAS,QAAQ,EAAE,EAAE,CAAC;UAC1G,UAAA,EAAE,oBAAoB;SACjB,CAAA,IAER,kBAAC,GAAD;UAAQ,MAAK;UAAK,SAAQ;UAAc,eAAe,EAAI,wBAAwB,EAAO,SAAS,OAAO,EAAE,EAAE,CAAC;UAC5G,UAAA,EAAE,mBAAmB;SAChB,CAAA;SAET,EAAE,WAAW,EAAE,YAAY,KAC1B,kBAAC,GAAD;UAAQ,MAAK;UAAK,SAAQ;UAAQ,eAAe,EAAO,EAAE,QAAQ,EAAE,QAAS,CAAC;UAC3E,UAAA,EAAE,0BAA0B;SACvB,CAAA;SAET,EAAE,WACD,kBAAC,GAAD;UAAQ,MAAK;UAAK,SAAQ;UAAQ,eAAe,EAAW;WAAE,IAAI,EAAE;WAAU;UAAK,CAAC;UACjF,UAAA,EAAE,cAAc;SACX,CAAA;QAEP;;MACH;KAzCK,GAAA,EAAE,EAyCP;IAER,CAAC;GACM,CAAA;GACT,kBAAC,GAAD;IAAa;IAAM,SAAS,EAAK;IAAS,UAAU,EAAK;IAAS,SAAS,MAAM,EAAI,GAAG,GAAM;KAAE;KAAM;KAAQ,MAAM,IAAI,IAAI,IAAI,KAAA;IAAU,CAAC;GAAI,CAAA;GAC9I,KAAW,kBAAC,GAAD;IAAkB,MAAA;IAAK,eAAe,MAAM,CAAC,KAAK,EAAW,IAAI;IAAG,MAAM;IAAS,QAAQ,CAAC,QAAQ;IAAW;IAAQ,SAAS,EAAE;GAAU,CAAA;EACrJ;;AAET;AAEA,SAAS,EAAM,EAAE,QAAK,SAAM,GAAG,KAAmB;CAEhD,IAAM,EAAE,SADE,EACI,GACR,IAAiB,EAAI,MAAM,IAAI,MAAM,MAAM,YAAY,YAAY,WACnE,IAAQ,EAAmB;EAAE;EAAM,UAAU,EAAE;EAAU,QAAQ,EAAE;CAAO,CAAC;CACjF,OACE,kBAAC,OAAD;EAAK,WAAU;EAAf,UAAA;GACE,kBAAC,OAAD;IAAK,WAAU;IAAa,MAAK;IAAQ,cAAY,EAAE,kBAAkB;IACrE,UAAA,CAAC,WAAW,SAAS,CAAC,CAAW,KAAK,MACtC,kBAAC,GAAD;KAAgB,MAAK;KAAK,SAAS,MAAS,IAAI,YAAY;KAAW,gBAAc,MAAS;KAAG,eAAe,EAAI,GAAG,GAAM,EAAE,MAAM,MAAM,YAAY,KAAA,IAAY,EAAE,CAAC;KACnK,UAAA,EAAE,MAAM,YAAY,4BAA4B,0BAA0B;IACrE,GAFK,CAEL,CACT;GACE,CAAA;GACL,kBAAC,GAAD;IAAS,SAAS,EAAM,WAAW,EAAM;IAAa,OAAO,EAAM;IAAO,OAAO,EAAE,uBAAuB;IAAG,OAAO,EAAM,MAAM;IAAQ,QAAQ,EAAM,MAAM,UAAU,EAAM,gBAAgB,KAAK,EAAM,SAAS,IAAI,EAAM;IACvN,UAAA,EAAM,MAAM,KAAK,MAChB,kBAAC,GAAD;KAAqB,MAAM;KAAI,UAAU,EAAE;KAAU,UAAU,EAAE;KAAU,WAAW,EAAM;KAAS,QAAQ,EAAM;KAAQ,SAAS,EAAE;IAAU,GAAlI,EAAG,EAA+H,CACjJ;GACM,CAAA;GACR,EAAM,WAAW,CAAC,EAAM,SACvB,kBAAC,GAAD;IAAQ,SAAQ;IAAU,WAAU;IAAsB,UAAU,EAAM;IAAa,eAAe,KAAK,EAAM,SAAS;IACvH,UAAA,EAAE,qBAAqB;GAClB,CAAA;EAEP;;AAET;AAEA,SAAS,EAAQ,GAOd;CACD,IAAM,IAAI,EAAY,GAChB,EAAE,SAAM,GACR,IAAS,EAAiB,EAAE,OAAO,GACnC,IAAU,EAAW,GACrB,EAAE,MAAM,MAAO,GACf,CAAC,GAAW,KAAgB,EAAS,EAAK,GAC1C,CAAC,GAAQ,KAAa,EAAS,EAAE,GACjC,CAAC,GAAS,KAAc,EAAS,EAAK,GACtC,CAAC,GAAO,KAAY,EAAwB,IAAI,GAChD,IAAO,EAAG,QAAQ,YAAY,EAAG,aAAa,EAAG,aAAa,EAAE,oBAAoB,GACpF,IAAO,EAAE,WAAW;EAAE,MAAM,EAAG,IAAI;EAAc,IAAI,EAAG,IAAI;CAAW,CAAC,GACxE,IAAU,OAAO,MAAgB;EAErC,AADA,EAAW,EAAI,GACf,EAAS,IAAI;EACb,IAAI;GACF,MAAM,QAAc,EAAE,UAAU,GAAI,GAAG,MAAM,YAAW,EAAO,KAAK,KAAiB,KAAA,CAAS,CAAC;EACjG,SAAS,GAAG;GACV,IAAM,IAAM,EAAkB,CAAC;GAG/B,AAFA,EAAO,GAAK,oBAAoB,GAE5B,EAAI,SAAS,eAAe,EAAI,SAAS,cAC3C,EAAS,EAAE,kBAAkB,CAAC,GAC9B,EAAE,OAAO,KACJ,EAAS,EAAa,GAAG,CAAG,CAAC;EACtC,UAAU;GACR,EAAW,EAAK;EAClB;CACF;CACA,OACE,kBAAC,MAAD;EAAI,WAAU;EAAiB,aAAU;EAAzC,UAAA;GACE,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,kBAAC,GAAD;MAAY,MAAM,EAAG;MAAc;MAAM,MAAK;KAAM,CAAA;KACpD,kBAAC,GAAD;MAAU,MAAM,EAAG;MAAc;MAAM,MAAM,EAAE;KAAW,CAAA;KAC1D,kBAAC,GAAD;MAAc,IAAI,EAAG;MAAS,WAAU;KAAiC,CAAA;KACxE,KACC,kBAAC,KAAD;MAAG,WAAU;MAAsD;MAAnE,UAAA;OACG,EAAE,eAAe;OAAE;OAAE,EAAG,IAAI;OAAa;OAAE,EAAG,IAAI;MAClD;;IAEF;;GACJ,EAAG,SAAS,kBAAC,KAAD;IAAG,WAAU;IAAe,UAAA,EAAG;GAAS,CAAA;GACrD,kBAAC,KAAD;IAAG,WAAU;IAAgD,UAAA,EAAG;GAAQ,CAAA;IACtE,EAAG,UAAU,EAAG,UAAU,kBAAC,KAAD;IAAG,WAAU;IAAiC,UAAA,EAAE,0BAA0B,EAAE,QAAQ,EAAG,UAAU,EAAG,SAAS,GAAG,CAAC;GAAK,CAAA;GACjJ,KACC,kBAAC,GAAD;IAAO,OAAO;IAAQ,WAAW;IAAK,aAAa,EAAE,yBAAyB;IAAG,cAAY,EAAE,yBAAyB;IAAG,WAAW,MAAM,EAAU,EAAE,OAAO,KAAK;IAAG,WAAA;GAAW,CAAA;GAEpL,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,kBAAC,GAAD;MAAQ,MAAK;MAAK,UAAU;MAAS,eAAe,KAAK,EAAQ,SAAS;MACvE,UAAA,EAAE,oBAAoB;KACjB,CAAA;KACR,kBAAC,GAAD;MAAQ,MAAK;MAAK,SAAQ;MAAc,UAAU;MAAS,eAAgB,IAAY,KAAK,EAAQ,QAAQ,IAAI,EAAa,EAAI;MAC9H,UAAA,EAAE,mBAAmB;KAChB,CAAA;KACP,KACC,kBAAC,GAAD;MAAQ,MAAK;MAAK,SAAQ;MAAQ,eAAe,EAAa,EAAK;MAChE,UAAA,EAAE,eAAe;KACZ,CAAA;IAEP;;GACJ,KAAS,kBAAC,GAAD,EAAA,UAAY,EAAiB,CAAA;EACrC;;AAER;AAEA,SAAS,EAAQ,GAA+H;CAC9I,IAAM,IAAI,EAAY,GAChB,EAAE,SAAM;CAoBd,OAnBI,EAAE,WAAW,CAAC,EAAE,QAEhB,kBAAC,OAAD;EAAK,WAAU;EAAa,aAAA;EAA5B,UAAA,CACE,kBAAC,GAAD,EAAU,WAAU,cAAe,CAAA,GACnC,kBAAC,GAAD,EAAU,WAAU,cAAe,CAAA,CAChC;MAGL,EAAE,SAAS,CAAC,EAAE,QAEd,kBAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACE,kBAAC,GAAD,EAAA,UAAY,EAAE,MAAM,SAAS,eAAe,EAAE,MAAM,SAAS,iBAAiB,EAAa,GAAG,EAAE,KAAK,IAAI,EAAE,uBAAuB,EAAa,CAAA,GAC/I,kBAAC,GAAD;GAAQ,MAAK;GAAK,SAAQ;GAAU,SAAS,EAAE;GAC5C,UAAA,EAAE,cAAc;EACX,CAAA,CACL;MAGJ,EAAE,QACA,kBAAA,GAAA,EAAA,UAAA,CACL,kBAAC,MAAD;EAAI,WAAU;EAA+D,UAAA,EAAE;CAAa,CAAA,GAC3F,EAAE,SAAS,kBAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACV,kBAAC,GAAD,EAAW,OAAO,EAAE,MAAQ,CAAA,GAC5B,kBAAC,GAAD;GAAQ,MAAK;GAAK,SAAQ;GAAU,UAAU,EAAE;GAAS,SAAS,EAAE;GACjE,UAAA,EAAE,cAAc;EACX,CAAA,CACL;CACL,CAAA,CAAA,EAAA,CAAA,IATmB,kBAAC,KAAD;EAAG,WAAU;EAAyB,UAAA,EAAE;CAAS,CAAA;AAUxE"}
@@ -12,8 +12,9 @@ export interface ReadWindow {
12
12
  export interface MediaReadOptions extends Omit<ReadOptions, "offset" | "limit"> {
13
13
  /**
14
14
  * Read in windows of `chunk` files and merge them: the files in
15
- * [start, end) get URLs, and windows once read stay loaded (and refreshed)
16
- * until the item or options change. Without it, one read of the server's
15
+ * [start, end) get URLs. Only the requested windows stay active and refresh;
16
+ * a prior read supplies placeholder metadata while the new range loads.
17
+ * Without it, one read of the server's
17
18
  * default range (or offset and limit).
18
19
  */
19
20
  window?: ReadWindow;
@@ -1,27 +1,29 @@
1
1
  import { itemRef as e } from "../client/media/ref.js";
2
2
  import { useOptionalContentKitClient as t, useReadScope as n } from "./context.js";
3
- import { chunkOffsets as r, mergeReads as i, processing as a } from "../client/media/windows.js";
4
- import { readKey as o, storeFor as s } from "./store.js";
5
- import { useCallback as c, useEffect as l, useMemo as u, useRef as d, useState as f, useSyncExternalStore as ee } from "react";
3
+ import { chunkOffsets as r, mergeReads as i, processing as ee } from "../client/media/windows.js";
4
+ import { readKey as a, storeFor as o } from "./store.js";
5
+ import { useCallback as s, useEffect as c, useMemo as l, useRef as u, useState as d, useSyncExternalStore as te } from "react";
6
6
  //#region src/react/read.ts
7
- var te = 2500, p = [];
7
+ var f = 2500, p = [];
8
8
  function m(m, h = {}) {
9
- let { read: g, client: _, prefix: v, offset: y, limit: b, download: x, editor: S, window: C, refresh: w = !0 } = h, T = h.poll ?? (S ? te : !1), E = t(_), D = n(), O = E ? s(E) : null, k = C?.chunk ?? 50, A = D + o(m, {
9
+ let { read: g, client: _, prefix: v, offset: y, limit: b, download: x, editor: S, window: C, refresh: w = !0 } = h, T = h.poll ?? (S ? f : !1), E = t(_), D = n(), O = E ? o(E) : null, k = C?.chunk ?? 50, A = D + a(m, {
10
10
  prefix: v,
11
11
  download: x,
12
12
  editor: S
13
- }) + (C ? `#${k}` : `#${y ?? 0}:${b ?? 0}`), [j, M] = f({ base: A }), N = j.base === A ? j.n : void 0, P = C ? r(C.start, C.end, N ?? C.start + 1, k) : [], [F, I] = f({
13
+ }) + (C ? `#${k}` : `#${y ?? 0}:${b ?? 0}`), [j, M] = d({
14
14
  base: A,
15
- offsets: []
16
- }), L = F.base === A ? F.offsets : [];
17
- C && (F.base !== A || P.some((e) => !L.includes(e))) && I({
15
+ store: O,
16
+ read: null
17
+ }), N = j.base === A && j.store === O;
18
+ N || M({
18
19
  base: A,
19
- offsets: [.../* @__PURE__ */ new Set([...L, ...P])].sort((e, t) => e - t)
20
+ store: O,
21
+ read: null
20
22
  });
21
- let R = C ? L.length ? L : P.length ? P : [0] : [y ?? 0], z = u(() => R.map((e) => ({
23
+ let P = N ? j.n : void 0, F = C ? r(C.start, C.end, P ?? C.start + 1, k) : [], I = C ? F.length ? F : [0] : [y ?? 0], L = l(() => I.map((e) => ({
22
24
  offset: e,
23
25
  limit: C ? k : b
24
- })), [R.join(","), C ? k : b]), B = u(() => z.map((e) => D + o(m, {
26
+ })), [I.join(","), C ? k : b]), R = l(() => L.map((e) => D + a(m, {
25
27
  prefix: v,
26
28
  download: x,
27
29
  editor: S,
@@ -29,20 +31,20 @@ function m(m, h = {}) {
29
31
  limit: e.limit
30
32
  })), [
31
33
  D,
32
- z,
34
+ L,
33
35
  m.kind,
34
36
  m.id,
35
37
  m.version,
36
38
  v,
37
39
  x,
38
40
  S
39
- ]), [V, H] = f(null), [U, W] = f(null), G = V?.base === A && V.given === g ? V : null, K = !!O && (g === void 0 || !!G), ne = c((t) => {
40
- if (!O || !K) return () => {};
41
- let n = B.map((n, r) => O.subscribe(n, e(m), {
41
+ ]), [z, B] = d(null), [V, H] = d(null), U = z?.base === A && z.given === g ? z : null, W = !!O && (g === void 0 || !!U), G = s((t) => {
42
+ if (!O || !W) return () => {};
43
+ let n = R.map((n, r) => O.subscribe(n, e(m), {
42
44
  prefix: v,
43
45
  download: x,
44
46
  editor: S,
45
- ...z[r]
47
+ ...L[r]
46
48
  }, t, {
47
49
  poll: T,
48
50
  refresh: w
@@ -50,91 +52,96 @@ function m(m, h = {}) {
50
52
  return () => n.forEach((e) => e());
51
53
  }, [
52
54
  O,
53
- K,
54
- B,
55
+ W,
56
+ R,
55
57
  T,
56
58
  w
57
- ]), q = d(p), J = c(() => {
58
- if (!O || !K) return p;
59
- let e = B.map((e) => O.snapshot(e)), t = q.current;
60
- return t.length === e.length && t.every((t, n) => t === e[n]) ? t : q.current = e;
59
+ ]), K = u(p), q = s(() => {
60
+ if (!O || !W) return p;
61
+ let e = R.map((e) => O.snapshot(e)), t = K.current;
62
+ return t.length === e.length && t.every((t, n) => t === e[n]) ? t : K.current = e;
61
63
  }, [
62
64
  O,
63
- K,
64
- B
65
- ]), Y = ee(ne, J, J);
66
- l(() => {
67
- if (O && K) for (let e of B) O.load(e, G ? "reload" : "initial");
65
+ W,
66
+ R
67
+ ]), J = te(G, q, q);
68
+ c(() => {
69
+ if (O && W) for (let e of R) O.load(e, U ? "reload" : "initial");
68
70
  }, [
69
71
  O,
70
- K,
71
- B,
72
- G
72
+ W,
73
+ R,
74
+ U
73
75
  ]);
74
- let X = u(() => i(Y.map((e) => e.read)), [Y]);
75
- l(() => {
76
- C && X && X.total !== N && M({
76
+ let Y = l(() => i(J.map((e) => e.read)), [J]), X = J.some((e) => !!e.error);
77
+ c(() => {
78
+ let e = X ? null : Y;
79
+ C && (X || Y || J.length && J.every((e) => e.loaded)) && M((t) => t.base === A && t.store === O && t.read === e ? t : {
77
80
  base: A,
78
- n: X.total
81
+ store: O,
82
+ read: e,
83
+ n: Y?.total ?? (t.base === A && t.store === O ? t.n : void 0)
79
84
  });
80
85
  }, [
81
- C,
86
+ !!C,
87
+ Y,
82
88
  X,
83
- N,
84
- A
89
+ J,
90
+ A,
91
+ O
85
92
  ]);
86
- let re = c(() => {
87
- if (g !== void 0) H({
93
+ let ne = s(() => {
94
+ if (g !== void 0) B({
88
95
  base: A,
89
96
  given: g
90
97
  });
91
- else if (O) for (let e of B) O.load(e, "reload");
92
- W(null);
98
+ else if (O) for (let e of R) O.load(e, "reload");
99
+ H(null);
93
100
  }, [
94
101
  O,
95
- B,
102
+ R,
96
103
  A,
97
104
  g
98
- ]), ie = c(() => {
99
- if (g !== void 0 && !G) H({
105
+ ]), re = s(() => {
106
+ if (g !== void 0 && !U) B({
100
107
  base: A,
101
108
  given: g
102
109
  });
103
- else if (O) for (let e of B) O.load(e, "refresh");
110
+ else if (O) for (let e of R) O.load(e, "refresh");
104
111
  }, [
105
112
  O,
106
- B,
113
+ R,
107
114
  A,
108
115
  g,
109
- G
110
- ]), ae = c((e) => {
111
- if (O && K) for (let t of B) O.set(t, e);
112
- else W({
116
+ U
117
+ ]), ie = s((e) => {
118
+ if (O && W) for (let t of R) O.set(t, e);
119
+ else H({
113
120
  base: A,
114
121
  given: g,
115
122
  read: e
116
123
  });
117
124
  }, [
118
125
  O,
119
- K,
120
- B,
126
+ W,
127
+ R,
121
128
  A,
122
129
  g
123
130
  ]), Z, Q = !1, $;
124
- if (U && U.base === A && U.given === g) Z = U.read;
125
- else if (!Y.length) Z = g ?? null;
131
+ if (V && V.base === A && V.given === g) Z = V.read;
132
+ else if (!J.length) Z = g ?? null;
126
133
  else {
127
- let e = Y.every((e) => e.loaded);
128
- Z = X ?? (e ? null : g ?? null), Q = Y.some((e) => e.loading || !e.loaded), $ = Y.find((e) => e.error)?.error;
134
+ let e = J.every((e) => e.loaded);
135
+ Z = Y ?? (e ? null : g ?? (C && N && !X ? j.read : null)), Q = J.some((e) => e.loading || !e.loaded), $ = J.find((e) => e.error)?.error;
129
136
  }
130
137
  return {
131
138
  read: Z,
132
139
  loading: Q,
133
140
  error: $,
134
- processing: a(Z),
135
- reload: re,
136
- refresh: ie,
137
- set: ae
141
+ processing: ee(Z),
142
+ reload: ne,
143
+ refresh: re,
144
+ set: ie
138
145
  };
139
146
  }
140
147
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"read.js","names":[],"sources":["../../src/react/read.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport type { ReadResult, RefBody } from \"../client/generated/wire.js\";\nimport type { ReadOptions } from \"../client/media/api.js\";\nimport { chunkOffsets, mergeReads, processing as isProcessing, READ_CHUNK } from \"../client/media/windows.js\";\nimport { itemRef } from \"../client/media/ref.js\";\nimport { useOptionalContentKitClient, useReadScope } from \"./context.js\";\nimport { readKey, storeFor, type ReadEntry } from \"./store.js\";\n\n/** Files [start, end) get URLs. */\nexport interface ReadWindow {\n start: number;\n end: number;\n /** Files per read. Default 50. */\n chunk?: number;\n}\n\nexport interface MediaReadOptions extends Omit<ReadOptions, \"offset\" | \"limit\"> {\n /**\n * Read in windows of `chunk` files and merge them: the files in\n * [start, end) get URLs, and windows once read stay loaded (and refreshed)\n * until the item or options change. Without it, one read of the server's\n * default range (or offset and limit).\n */\n window?: ReadWindow;\n offset?: number;\n limit?: number;\n /** Poll at this interval (ms) while uploads process. Default 2500 for editor reads, else off. */\n poll?: number | false;\n /** Read again shortly before the URLs expire. Default true. */\n refresh?: boolean;\n /** A read the host already has: shown until reload() or set(). */\n read?: ReadResult | null;\n /** Overrides the provider's client. */\n client?: ContentKitClient | null;\n}\n\nexport interface UseMediaRead {\n /** The read (windows merged); null until it loads or when it failed. */\n read: ReadResult | null;\n loading: boolean;\n error?: ContentKitError;\n /** Uploads are still processing (editor reads). */\n processing: boolean;\n /** Reads again now, restarting a read in flight. */\n reload: () => void;\n /** Reads again unless a read is in flight: a player's grant refresh. */\n refresh: () => void;\n /** Replaces the read without refetching, for every hook showing it. */\n set: (r: ReadResult) => void;\n}\n\nconst POLL = 2500;\nconst NONE: readonly ReadEntry[] = [];\n\n/**\n * A read of the item, shared with every hook reading the same item and\n * options: refreshed before its URLs expire, retried after a read rate limit,\n * polled while uploads process, reloaded after a commit through the client.\n * A window splits it into merged reads. Without a client (none passed, no\n * provider) it only shows a supplied read.\n */\nexport function useMediaRead(ref: RefBody, o: MediaReadOptions = {}): UseMediaRead {\n const { read: given, client: own, prefix, offset, limit, download, editor, window, refresh: refreshing = true } = o;\n const poll = o.poll ?? (editor ? POLL : false);\n const client = useOptionalContentKitClient(own);\n const scope = useReadScope();\n const store = client ? storeFor(client) : null;\n const chunk = window?.chunk ?? READ_CHUNK;\n const base = scope + readKey(ref, { prefix, download, editor }) + (window ? `#${chunk}` : `#${offset ?? 0}:${limit ?? 0}`);\n\n // Windows once wanted stay loaded until the item or options change.\n const [total, setTotal] = useState<{ base: string; n?: number }>({ base });\n const known = total.base === base ? total.n : undefined;\n // Until the first read states the total, only the window's first chunk.\n const wanted = window ? chunkOffsets(window.start, window.end, known ?? window.start + 1, chunk) : [];\n const [kept, setKept] = useState<{ base: string; offsets: number[] }>({ base, offsets: [] });\n const prior = kept.base === base ? kept.offsets : [];\n if (window && (kept.base !== base || wanted.some((w) => !prior.includes(w)))) {\n setKept({ base, offsets: [...new Set([...prior, ...wanted])].sort((a, b) => a - b) });\n }\n const offsets = window ? (prior.length ? prior : wanted.length ? wanted : [0]) : [offset ?? 0];\n const windows = useMemo(\n () => offsets.map((off) => ({ offset: off, limit: window ? chunk : limit })),\n // offsets is derived from the stable kept list\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [offsets.join(\",\"), window ? chunk : limit],\n );\n const keys = useMemo(() => windows.map((w) => scope + readKey(ref, { prefix, download, editor, offset: w.offset, limit: w.limit })), [scope, windows, ref.kind, ref.id, ref.version, prefix, download, editor]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // A supplied read is shown until reload() asks the server or set() replaces it.\n const [requested, setRequested] = useState<{ base: string; given?: ReadResult | null } | null>(null);\n const [local, setLocal] = useState<{ base: string; given?: ReadResult | null; read: ReadResult } | null>(null);\n const asked = requested?.base === base && requested.given === given ? requested : null;\n const fetching = !!store && (given === undefined || !!asked);\n\n const subscribe = useCallback(\n (l: () => void) => {\n if (!store || !fetching) return () => {};\n const offs = keys.map((key, i) => store.subscribe(key, itemRef(ref), { prefix, download, editor, ...windows[i] }, l, { poll, refresh: refreshing }, scope));\n return () => offs.forEach((off) => off());\n },\n // ref and options are in keys.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [store, fetching, keys, poll, refreshing],\n );\n const last = useRef<readonly ReadEntry[]>(NONE);\n const snapshot = useCallback(() => {\n if (!store || !fetching) return NONE;\n const next = keys.map((k) => store.snapshot(k));\n const prev = last.current;\n if (prev.length === next.length && prev.every((e, i) => e === next[i])) return prev;\n return (last.current = next);\n }, [store, fetching, keys]);\n const entries = useSyncExternalStore(subscribe, snapshot, snapshot);\n useEffect(() => {\n if (store && fetching) for (const k of keys) store.load(k, asked ? \"reload\" : \"initial\");\n }, [store, fetching, keys, asked]);\n\n const merged = useMemo(() => mergeReads(entries.map((e) => e.read)), [entries]);\n useEffect(() => {\n if (window && merged && merged.total !== known) setTotal({ base, n: merged.total });\n }, [window, merged, known, base]);\n\n const reload = useCallback(() => {\n if (given !== undefined) setRequested({ base, given });\n else if (store) for (const k of keys) store.load(k, \"reload\");\n setLocal(null);\n }, [store, keys, base, given]);\n const refresh = useCallback(() => {\n if (given !== undefined && !asked) setRequested({ base, given });\n else if (store) for (const k of keys) store.load(k, \"refresh\");\n }, [store, keys, base, given, asked]);\n const set = useCallback(\n (read: ReadResult) => {\n if (store && fetching) for (const k of keys) store.set(k, read);\n else setLocal({ base, given, read });\n },\n [store, fetching, keys, base, given],\n );\n\n let read: ReadResult | null;\n let loading = false;\n let error: ContentKitError | undefined;\n if (local && local.base === base && local.given === given) read = local.read;\n else if (!entries.length) read = given ?? null;\n else {\n const loaded = entries.every((e) => e.loaded);\n read = merged ?? (loaded ? null : (given ?? null));\n loading = entries.some((e) => e.loading || !e.loaded);\n error = entries.find((e) => e.error)?.error;\n }\n return { read, loading, error, processing: isProcessing(read), reload, refresh, set };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"read.js","names":[],"sources":["../../src/react/read.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport type { ReadResult, RefBody } from \"../client/generated/wire.js\";\nimport type { ReadOptions } from \"../client/media/api.js\";\nimport { chunkOffsets, mergeReads, processing as isProcessing, READ_CHUNK } from \"../client/media/windows.js\";\nimport { itemRef } from \"../client/media/ref.js\";\nimport { useOptionalContentKitClient, useReadScope } from \"./context.js\";\nimport { readKey, storeFor, type ReadEntry } from \"./store.js\";\n\n/** Files [start, end) get URLs. */\nexport interface ReadWindow {\n start: number;\n end: number;\n /** Files per read. Default 50. */\n chunk?: number;\n}\n\nexport interface MediaReadOptions extends Omit<ReadOptions, \"offset\" | \"limit\"> {\n /**\n * Read in windows of `chunk` files and merge them: the files in\n * [start, end) get URLs. Only the requested windows stay active and refresh;\n * a prior read supplies placeholder metadata while the new range loads.\n * Without it, one read of the server's\n * default range (or offset and limit).\n */\n window?: ReadWindow;\n offset?: number;\n limit?: number;\n /** Poll at this interval (ms) while uploads process. Default 2500 for editor reads, else off. */\n poll?: number | false;\n /** Read again shortly before the URLs expire. Default true. */\n refresh?: boolean;\n /** A read the host already has: shown until reload() or set(). */\n read?: ReadResult | null;\n /** Overrides the provider's client. */\n client?: ContentKitClient | null;\n}\n\nexport interface UseMediaRead {\n /** The read (windows merged); null until it loads or when it failed. */\n read: ReadResult | null;\n loading: boolean;\n error?: ContentKitError;\n /** Uploads are still processing (editor reads). */\n processing: boolean;\n /** Reads again now, restarting a read in flight. */\n reload: () => void;\n /** Reads again unless a read is in flight: a player's grant refresh. */\n refresh: () => void;\n /** Replaces the read without refetching, for every hook showing it. */\n set: (r: ReadResult) => void;\n}\n\nconst POLL = 2500;\nconst NONE: readonly ReadEntry[] = [];\n\n/**\n * A read of the item, shared with every hook reading the same item and\n * options: refreshed before its URLs expire, retried after a read rate limit,\n * polled while uploads process, reloaded after a commit through the client.\n * A window splits it into merged reads. Without a client (none passed, no\n * provider) it only shows a supplied read.\n */\nexport function useMediaRead(ref: RefBody, o: MediaReadOptions = {}): UseMediaRead {\n const { read: given, client: own, prefix, offset, limit, download, editor, window, refresh: refreshing = true } = o;\n const poll = o.poll ?? (editor ? POLL : false);\n const client = useOptionalContentKitClient(own);\n const scope = useReadScope();\n const store = client ? storeFor(client) : null;\n const chunk = window?.chunk ?? READ_CHUNK;\n const base = scope + readKey(ref, { prefix, download, editor }) + (window ? `#${chunk}` : `#${offset ?? 0}:${limit ?? 0}`);\n\n // Keep one placeholder, never subscriptions for the reader's visit history.\n const [previous, setPrevious] = useState<{ base: string; store: typeof store; read: ReadResult | null; n?: number }>({ base, store, read: null });\n const same = previous.base === base && previous.store === store;\n if (!same) setPrevious({ base, store, read: null });\n const known = same ? previous.n : undefined;\n // Until the first read states the total, only the window's first chunk.\n const wanted = window ? chunkOffsets(window.start, window.end, known ?? window.start + 1, chunk) : [];\n const offsets = window ? (wanted.length ? wanted : [0]) : [offset ?? 0];\n const windows = useMemo(\n () => offsets.map((off) => ({ offset: off, limit: window ? chunk : limit })),\n // Window identity depends on its values, not the caller's array identity.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [offsets.join(\",\"), window ? chunk : limit],\n );\n const keys = useMemo(() => windows.map((w) => scope + readKey(ref, { prefix, download, editor, offset: w.offset, limit: w.limit })), [scope, windows, ref.kind, ref.id, ref.version, prefix, download, editor]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // A supplied read is shown until reload() asks the server or set() replaces it.\n const [requested, setRequested] = useState<{ base: string; given?: ReadResult | null } | null>(null);\n const [local, setLocal] = useState<{ base: string; given?: ReadResult | null; read: ReadResult } | null>(null);\n const asked = requested?.base === base && requested.given === given ? requested : null;\n const fetching = !!store && (given === undefined || !!asked);\n\n const subscribe = useCallback(\n (l: () => void) => {\n if (!store || !fetching) return () => {};\n const offs = keys.map((key, i) => store.subscribe(key, itemRef(ref), { prefix, download, editor, ...windows[i] }, l, { poll, refresh: refreshing }, scope));\n return () => offs.forEach((off) => off());\n },\n // ref and options are in keys.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [store, fetching, keys, poll, refreshing],\n );\n const last = useRef<readonly ReadEntry[]>(NONE);\n const snapshot = useCallback(() => {\n if (!store || !fetching) return NONE;\n const next = keys.map((k) => store.snapshot(k));\n const prev = last.current;\n if (prev.length === next.length && prev.every((e, i) => e === next[i])) return prev;\n return (last.current = next);\n }, [store, fetching, keys]);\n const entries = useSyncExternalStore(subscribe, snapshot, snapshot);\n useEffect(() => {\n if (store && fetching) for (const k of keys) store.load(k, asked ? \"reload\" : \"initial\");\n }, [store, fetching, keys, asked]);\n\n const merged = useMemo(() => mergeReads(entries.map((e) => e.read)), [entries]);\n const failed = entries.some((e) => !!e.error);\n useEffect(() => {\n const read = failed ? null : merged;\n if (window && (failed || merged || (entries.length && entries.every((e) => e.loaded)))) {\n setPrevious((prev) => prev.base === base && prev.store === store && prev.read === read ? prev : {\n base, store, read, n: merged?.total ?? (prev.base === base && prev.store === store ? prev.n : undefined),\n });\n }\n }, [!!window, merged, failed, entries, base, store]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const reload = useCallback(() => {\n if (given !== undefined) setRequested({ base, given });\n else if (store) for (const k of keys) store.load(k, \"reload\");\n setLocal(null);\n }, [store, keys, base, given]);\n const refresh = useCallback(() => {\n if (given !== undefined && !asked) setRequested({ base, given });\n else if (store) for (const k of keys) store.load(k, \"refresh\");\n }, [store, keys, base, given, asked]);\n const set = useCallback(\n (read: ReadResult) => {\n if (store && fetching) for (const k of keys) store.set(k, read);\n else setLocal({ base, given, read });\n },\n [store, fetching, keys, base, given],\n );\n\n let read: ReadResult | null;\n let loading = false;\n let error: ContentKitError | undefined;\n if (local && local.base === base && local.given === given) read = local.read;\n else if (!entries.length) read = given ?? null;\n else {\n const loaded = entries.every((e) => e.loaded);\n read = merged ?? (loaded ? null : (given ?? (window && same && !failed ? previous.read : null)));\n loading = entries.some((e) => e.loading || !e.loaded);\n error = entries.find((e) => e.error)?.error;\n }\n return { read, loading, error, processing: isProcessing(read), reload, refresh, set };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openrails/contentkit-ui",
3
- "version": "0.99.2",
3
+ "version": "0.100.0",
4
4
  "description": "ContentKit in the browser: client, React hooks and styled components for media, uploads and content URLs.",
5
5
  "type": "module",
6
6
  "license": "MIT",