@openrails/contentkit-ui 0.85.1 → 0.86.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.
- package/README.md +7 -4
- package/dist/client/beacons.d.ts +13 -4
- package/dist/client/beacons.js +14 -7
- package/dist/client/beacons.js.map +1 -1
- package/dist/client/client.d.ts +3 -0
- package/dist/client/client.js +29 -27
- package/dist/client/client.js.map +1 -1
- package/dist/client/codes.d.ts +6 -1
- package/dist/client/codes.js +4 -1
- package/dist/client/codes.js.map +1 -1
- package/dist/client/generated/error-codes.js +1 -1
- package/dist/client/generated/error-codes.js.map +1 -1
- package/dist/client/generated/routes.d.ts +14 -1
- package/dist/client/generated/routes.js +17 -1
- package/dist/client/generated/routes.js.map +1 -1
- package/dist/client/generated/wire.d.ts +29 -0
- package/dist/client/generated/wire.js.map +1 -1
- package/dist/client/index.d.ts +2 -1
- package/dist/client/media/queue.d.ts +4 -2
- package/dist/client/media/queue.js +18 -12
- package/dist/client/media/queue.js.map +1 -1
- package/dist/client/media/rules.d.ts +7 -1
- package/dist/client/media/rules.js +27 -21
- package/dist/client/media/rules.js.map +1 -1
- package/dist/client/stats.d.ts +32 -0
- package/dist/client/stats.js +35 -0
- package/dist/client/stats.js.map +1 -0
- package/dist/client.js +30 -29
- package/dist/components/admin/content-manager.js +206 -206
- package/dist/components/admin/content-manager.js.map +1 -1
- package/dist/components/admin/group-manager.js +71 -70
- package/dist/components/admin/group-manager.js.map +1 -1
- package/dist/components/admin/media-item-panel.d.ts +24 -7
- package/dist/components/admin/media-item-panel.js +209 -197
- package/dist/components/admin/media-item-panel.js.map +1 -1
- package/dist/components/admin/poll-manager.js +25 -25
- package/dist/components/admin/stats-dashboard.d.ts +22 -0
- package/dist/components/admin/stats-dashboard.js +257 -0
- package/dist/components/admin/stats-dashboard.js.map +1 -0
- package/dist/components/content/card.js +42 -42
- package/dist/components/content/card.js.map +1 -1
- package/dist/components/content/editors.js +455 -427
- package/dist/components/content/editors.js.map +1 -1
- package/dist/components/content/grid.js +47 -47
- package/dist/components/content/index.d.ts +2 -1
- package/dist/components/content/listing.js +34 -34
- package/dist/components/content/listing.js.map +1 -1
- package/dist/components/content/paywall.d.ts +17 -5
- package/dist/components/content/paywall.js +115 -100
- package/dist/components/content/paywall.js.map +1 -1
- package/dist/components/content/reader-stats.d.ts +12 -0
- package/dist/components/content/reader-stats.js +47 -0
- package/dist/components/content/reader-stats.js.map +1 -0
- package/dist/components/groups/banner.js +33 -33
- package/dist/components/groups/directory.js +11 -11
- package/dist/components/groups/editor.js +293 -293
- package/dist/components/groups/editor.js.map +1 -1
- package/dist/components/groups/header.js +27 -27
- package/dist/components/groups/header.js.map +1 -1
- package/dist/components/groups/memberships.js +180 -180
- package/dist/components/groups/parts.d.ts +0 -2
- package/dist/components/groups/parts.js +9 -27
- package/dist/components/groups/parts.js.map +1 -1
- package/dist/components/media-folder-editor.js +239 -189
- package/dist/components/media-folder-editor.js.map +1 -1
- package/dist/components/notifications.js +17 -17
- package/dist/components/slot-upload.d.ts +2 -2
- package/dist/components/slot-upload.js +84 -79
- package/dist/components/slot-upload.js.map +1 -1
- package/dist/components/social/bans.js +34 -34
- package/dist/components/social/moderation.js +52 -48
- package/dist/components/social/moderation.js.map +1 -1
- package/dist/components/video-player.d.ts +2 -0
- package/dist/components/video-player.js +318 -314
- package/dist/components/video-player.js.map +1 -1
- package/dist/i18n/context.d.ts +2 -0
- package/dist/i18n/context.js +23 -5
- package/dist/i18n/context.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +12 -10
- package/dist/locales/admin/de.js +27 -0
- package/dist/locales/admin/de.js.map +1 -1
- package/dist/locales/admin/en.d.ts +27 -0
- package/dist/locales/admin/en.js +27 -0
- package/dist/locales/admin/en.js.map +1 -1
- package/dist/locales/admin/es.js +27 -0
- package/dist/locales/admin/es.js.map +1 -1
- package/dist/locales/admin/ja.js +27 -0
- package/dist/locales/admin/ja.js.map +1 -1
- package/dist/locales/admin/ko.js +27 -0
- package/dist/locales/admin/ko.js.map +1 -1
- package/dist/locales/admin/zh.js +27 -0
- package/dist/locales/admin/zh.js.map +1 -1
- package/dist/locales/content/de.js +6 -0
- package/dist/locales/content/de.js.map +1 -1
- package/dist/locales/content/en.d.ts +6 -0
- package/dist/locales/content/en.js +6 -0
- package/dist/locales/content/en.js.map +1 -1
- package/dist/locales/content/es.js +6 -0
- package/dist/locales/content/es.js.map +1 -1
- package/dist/locales/content/ja.js +6 -0
- package/dist/locales/content/ja.js.map +1 -1
- package/dist/locales/content/ko.js +6 -0
- package/dist/locales/content/ko.js.map +1 -1
- package/dist/locales/content/zh.js +6 -0
- package/dist/locales/content/zh.js.map +1 -1
- package/dist/locales/de.js +1 -0
- package/dist/locales/de.js.map +1 -1
- package/dist/locales/en.d.ts +34 -0
- package/dist/locales/en.js +1 -0
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es.js +1 -0
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/ja.js +1 -0
- package/dist/locales/ja.js.map +1 -1
- package/dist/locales/ko.js +1 -0
- package/dist/locales/ko.js.map +1 -1
- package/dist/locales/zh.js +1 -0
- package/dist/locales/zh.js.map +1 -1
- package/dist/react/admin.js +22 -23
- package/dist/react/admin.js.map +1 -1
- package/dist/react/canonical.d.ts +4 -3
- package/dist/react/canonical.js +34 -33
- package/dist/react/canonical.js.map +1 -1
- package/dist/react/contents.js +144 -144
- package/dist/react/contents.js.map +1 -1
- package/dist/react/detail.d.ts +12 -6
- package/dist/react/detail.js +31 -24
- package/dist/react/detail.js.map +1 -1
- package/dist/react/folder.d.ts +7 -2
- package/dist/react/folder.js +165 -147
- package/dist/react/folder.js.map +1 -1
- package/dist/react/groups.d.ts +0 -1
- package/dist/react/groups.js +13 -12
- package/dist/react/groups.js.map +1 -1
- package/dist/react/index.d.ts +4 -2
- package/dist/react/location.d.ts +11 -0
- package/dist/react/location.js +18 -0
- package/dist/react/location.js.map +1 -0
- package/dist/react/public.d.ts +8 -0
- package/dist/react/public.js +32 -27
- package/dist/react/public.js.map +1 -1
- package/dist/react/stats.d.ts +24 -0
- package/dist/react/stats.js +28 -0
- package/dist/react/stats.js.map +1 -0
- package/dist/react/use-resource.js +8 -7
- package/dist/react/use-resource.js.map +1 -1
- package/dist/react/versions.js +20 -20
- package/dist/react.js +26 -24
- package/dist/styles.css +1 -1
- package/dist/urls/index.d.ts +35 -10
- package/dist/urls.js +100 -70
- package/dist/urls.js.map +1 -1
- package/package.json +1 -1
package/dist/react/folder.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"folder.js","names":[],"sources":["../../src/react/folder.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { itemKey } from \"../client/media/ref.js\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport { ContentKitError, failureError, toContentKitError } from \"../client/errors.js\";\nimport type { Edit, FileInfo, Op, ReadResult, RefBody, UploadRule } from \"../client/generated/wire.js\";\nimport { stem } from \"../client/media/client.js\";\nimport type { QueueItem } from \"../client/media/queue.js\";\nimport { filesFor, isPattern, ruleDir, ruleFor, screenFiles, uniqueName, uploadRules, type Screened } from \"../client/media/rules.js\";\nimport { useContentKitClient, useErrorReporter, type ContentKitErrorHandler } from \"./context.js\";\nimport { useMediaRead, type UseMediaRead } from \"./read.js\";\nimport { useUploadQueue, type UseUploadQueue } from \"./upload.js\";\n\nexport interface MediaFolderOptions {\n /**\n * The upload paths shown and added to, e.g. [\"images/{name}\",\n * \"videos/{name}\"]: one group each. Default every pattern path of the kind\n * the server does not name. A file goes to the first path whose rule takes\n * its type.\n */\n paths?: readonly string[];\n /**\n * \"manual\" (default): commit() adds the uploaded files. \"auto\": each\n * upload commits once those before it have, in the order added (a draft).\n */\n commit?: \"manual\" | \"auto\";\n /** A file's name under its path; default its own, numbered around names taken. */\n name?: (file: File) => string;\n /** Files uploading at once. Default 2. */\n concurrency?: number;\n /** Poll interval (ms) while uploads process. Default 2500. */\n poll?: number | false;\n /** Every failure (uploads, commits, updates, processing); default the provider's. */\n onError?: ContentKitErrorHandler;\n /** Overrides the provider's client. */\n client?: ContentKitClient;\n}\n\nexport interface FolderGroup {\n /** The upload path, e.g. \"images/{name}\". */\n path: string;\n /** Its rules, once an editor read states them. */\n rule?: UploadRule;\n /** Its uploads in manifest order. */\n files: FileInfo[];\n}\n\nexport interface UseMediaFolder {\n /** The item; null until the host has one (files added meanwhile wait for it). */\n ref: RefBody | null;\n /** The options the folder was opened with. */\n options: MediaFolderOptions;\n /** The editor read of the item (every window, polled while processing). */\n read: UseMediaRead;\n groups: FolderGroup[];\n /** Every group's uploads. */\n uploads: FileInfo[];\n /** The upload queue; a file leaves it once the editor read lists it. */\n queue: UseUploadQueue;\n /**\n * Files added before the item and its editor read (the upload rules) were\n * known. They are screened and queued when the rules arrive, or refused\n * when the read fails or states no rules (the actor may not edit the item).\n */\n waiting: readonly File[];\n /** Screens files against the rules and queues the rest (once the rules are known); refusals land in refused. */\n add: (files: Iterable<File>) => void;\n /** The last screening's refusals. */\n refused: Screened[\"refused\"];\n dismiss: () => void;\n /** Moves an upload to index among its group's uploads. */\n move: (path: string, index: number) => Promise<void>;\n remove: (paths: readonly string[]) => Promise<void>;\n /** Renames an upload within its path; the extension stays unless name has one. */\n rename: (path: string, name: string) => Promise<void>;\n edit: (path: string, edit: Edit | null) => Promise<void>;\n /** Uploads file in place of the upload at path (same position and meta, no edit). */\n replace: (path: string, file: File) => Promise<void>;\n /** Commits the uploaded files in queue order. */\n commit: () => Promise<FileInfo[]>;\n /** Stops every upload, aborts its multipart upload and empties the queue (before deleting a draft). */\n discard: () => void;\n /** An update (move, remove, rename, edit, replace) is running. */\n updating: boolean;\n committing: boolean;\n /**\n * Work is in flight: files wait for the rules, uploads are queued or\n * running, an automatic commit is due, or a commit or update runs.\n * Leaving the page now interrupts it.\n */\n busy: boolean;\n /** The item's uploads plus the files on their way in (waiting, queued, uploading, uploaded, being added); failed uploads are not counted. */\n fileCount: number;\n /** The last update's or commit's failure; cleared by the next success. */\n error?: ContentKitError;\n}\n\nconst ALL = { start: 0, end: Number.MAX_SAFE_INTEGER, chunk: 200 };\n\n/** The upload's name under its group (\"images/a.png\" in \"images/{name}\" is \"a.png\"). */\nexport const nameIn = (group: { path: string }, path: string) => path.slice(ruleDir(group.path).length);\n\n/** Its smallest derived image with a URL: a thumbnail for an editor. */\nexport function thumbnailOf(read: ReadResult | null | undefined, upload: FileInfo): FileInfo | undefined {\n let best: FileInfo | undefined;\n for (const f of read?.files ?? []) {\n if (f.upload || f.from !== upload.path || !f.url || !f.type.startsWith(\"image/\")) continue;\n if (!best || (f.w ?? Infinity) < (best.w ?? Infinity)) best = f;\n }\n return best;\n}\n\nconst NO_ITEM: RefBody = { kind: \"\", id: \"\" };\nconst keyOf = (ref: RefBody | null) => (ref ? itemKey(ref) : \"\");\n\nconst noRules = new WeakMap<ReadResult, ContentKitError>();\n/** The refusal for files added to an item whose read carries no upload rules (one per read). */\nfunction notEditor(read: ReadResult): ContentKitError {\n let e = noRules.get(read);\n if (!e) noRules.set(read, (e = new ContentKitError(\"forbidden\", \"the editor read states no upload rules\", { status: 403 })));\n return e;\n}\n\n/** Queue items still on their way in: not failed, and not yet listed by the read. */\nfunction inbound(items: readonly QueueItem[], listed: ReadonlySet<string>) {\n return items.filter((i) => i.status !== \"failed\" && !(i.status === \"committed\" && listed.has(i.path)));\n}\n\n/**\n * An item's folder for its editors: the editor read, an upload queue, the\n * kind's upload rules screening files before they upload, and the updates\n * (move, remove, rename, edit, replace). Files added before the editor read\n * arrives wait for its rules; with a null ref they wait for the item too (a\n * draft created on the first drop). Uploads the worker fails after the read\n * are reported once (\"folder.process\"). Another ref gets a fresh queue.\n */\nexport function useMediaFolder(ref: RefBody | null | undefined, o: MediaFolderOptions = {}): UseMediaFolder {\n const client = useContentKitClient(o.client);\n const report = useErrorReporter(o.onError);\n const item = ref ?? null;\n const key = keyOf(item);\n const target = item ?? NO_ITEM;\n // Without an item there is nothing to read: a supplied null read never fetches.\n const read = useMediaRead(target, { editor: true, window: ALL, poll: o.poll, client, read: item ? undefined : null });\n const rules = uploadRules(read.read);\n const pathsKey = o.paths?.join(\"\\n\");\n const groupPaths = useMemo(\n () => (pathsKey !== undefined ? pathsKey.split(\"\\n\").filter(Boolean) : rules.filter((r) => isPattern(r.path) && !r.named).map((r) => r.path)),\n [pathsKey, rules],\n );\n const groups = useMemo((): FolderGroup[] => {\n const known: { path: string }[] = rules.length ? rules : groupPaths.map((path) => ({ path }));\n return groupPaths.map((path) => ({ path, rule: rules.find((r) => r.path === path), files: filesFor(read.read, { path }, known) }));\n }, [groupPaths, rules, read.read]);\n const uploads = useMemo(() => groups.flatMap((g) => g.files), [groups]);\n const listed = useMemo(() => new Set(uploads.map((f) => f.path)), [uploads]);\n\n const raw = useUploadQueue({ client, ref: target, path: `${ruleDir(groupPaths[0] ?? \"{name}\")}{name}`, concurrency: o.concurrency });\n // The ItemUploadQueue itself is stable per item; the hook's wrapper is new every render.\n const q = raw.queue;\n // A committed file stays in the queue until the read lists it, so it never drops out of view.\n const items = useMemo(() => raw.items.filter((i) => !(i.status === \"committed\" && listed.has(i.path))), [raw.items, listed]);\n const queue = { ...raw, items };\n const [refused, setRefused] = useState<Screened[\"refused\"]>([]);\n const [waiting, setWaiting] = useState<readonly File[]>([]);\n const pending = useRef<File[]>([]);\n const [updating, setUpdating] = useState(0);\n const [committing, setCommitting] = useState(false);\n const [error, setError] = useState<ContentKitError>();\n // An automatic commit that failed: no more until commit() or new files.\n const [stall, setStall] = useState<{ key: string; error: ContentKitError }>();\n const stalled = stall?.key === key ? stall.error : undefined;\n const opts = useRef(o);\n opts.current = o;\n // The rules are known once the item's editor read states them. A failed read\n // refuses what waits, and so does one without rules: the server answered a\n // viewer's read, so nothing uploads here.\n const ready = !!item && !!read.read?.uploads;\n const loadError = !item ? undefined : !read.read ? read.error : !read.read.uploads ? notEditor(read.read) : undefined;\n const live = useRef({ groups, ref: target, listed, ready, loadError });\n live.current = { groups, ref: target, listed, ready, loadError };\n\n const place = useCallback(\n (files: File[]) => {\n const { groups, listed } = live.current;\n const gs = groups.map((g) => g.rule ?? { path: g.path, types: [], max_bytes: 0 });\n const queued = inbound(q.getSnapshot().items, listed);\n const inGroup = (path: string, rule: UploadRule) => ruleFor(gs, path)?.path === rule.path;\n const s = screenFiles(files, gs, (rule) => groups.find((g) => g.path === rule.path)!.files.length + queued.filter((i) => inGroup(i.path, rule)).length);\n const taken = new Map<string, string[]>();\n const takenIn = (rule: UploadRule) => {\n let t = taken.get(rule.path);\n if (!t) {\n const g = groups.find((x) => x.path === rule.path)!;\n t = [...g.files.map((f) => nameIn(g, f.path)), ...queued.filter((i) => inGroup(i.path, rule)).map((i) => nameIn(g, i.path))];\n taken.set(rule.path, t);\n }\n return t;\n };\n const paths = new Map<File, string>();\n for (const { file, rule } of s.accepted) {\n const t = takenIn(rule);\n const name = opts.current.name?.(file) ?? uniqueName(file.name, t);\n t.push(name);\n paths.set(file, ruleDir(rule.path) + name);\n }\n if (paths.size) {\n q.add(paths.keys(), { path: (f) => paths.get(f)! });\n // New files lift a block and restart a queue discard() paused.\n q.start();\n setStall(undefined);\n }\n setRefused(s.refused);\n for (const r of s.refused) report(r.error, \"upload\", r.file.name);\n },\n [q, report],\n );\n\n // Screens what waits once the rules are known; a failed read refuses it (the read failure is reported once, as folder.load).\n const flush = useCallback(() => {\n const files = pending.current;\n const { ready, loadError } = live.current;\n if (!files.length || (!ready && !loadError)) return false;\n pending.current = [];\n setWaiting([]);\n if (ready) place(files);\n else setRefused(files.map((file) => ({ file, error: loadError! })));\n return true;\n }, [place]);\n\n const add = useCallback(\n (files: Iterable<File>) => {\n const list = [...files];\n if (!list.length) return;\n pending.current = [...pending.current, ...list];\n if (!flush()) setWaiting(pending.current);\n },\n [flush],\n );\n useEffect(() => void flush(), [flush, ready, loadError]);\n\n const update = useCallback(\n async (ops: Op[], run?: () => Promise<unknown>) => {\n setUpdating((n) => n + 1);\n try {\n if (run) await run();\n else await client.media.commit(live.current.ref, ops);\n setError(undefined);\n } catch (e) {\n const err = toContentKitError(e);\n if (err.code !== \"aborted\") setError(err);\n report(err, \"folder.update\");\n throw err;\n } finally {\n setUpdating((n) => n - 1);\n }\n },\n [client, report],\n );\n\n const commit = useCallback(async () => {\n setCommitting(true);\n try {\n const files = await q.commit(undefined, { head: opts.current.commit === \"auto\" });\n setError(undefined);\n return files;\n } catch (e) {\n const err = toContentKitError(e);\n setError(err);\n report(err, \"folder.commit\");\n throw err;\n } finally {\n setCommitting(false);\n }\n }, [q, report]);\n\n // Committed files leave the queue once the read lists them, or once a read lands without them.\n useEffect(() => {\n for (const i of raw.items) if (i.status === \"committed\" && (listed.has(i.path) || !read.loading)) q.remove(i.id);\n }, [raw.items, listed, read.loading, q]);\n\n // A draft commits the uploaded head of the queue as it finishes; a failed commit waits for commit().\n const auto = o.commit === \"auto\";\n const headReady = items.find((i) => i.status !== \"committed\")?.status === \"uploaded\";\n useEffect(() => {\n if (!auto || !headReady || committing || stalled) return;\n commit().catch((e: unknown) => setStall({ key, error: toContentKitError(e) }));\n }, [auto, headReady, committing, stalled, commit, key]);\n const manualCommit = useCallback(async () => {\n setStall(undefined);\n return commit();\n }, [commit]);\n\n // Each failed upload and queue-wide refusal is reported once; rows keep showing it.\n const seen = useRef(new Set<unknown>());\n useEffect(() => {\n for (const i of raw.items) {\n if (i.status !== \"failed\" || !i.error || seen.current.has(i.error)) continue;\n seen.current.add(i.error);\n report(i.error, \"upload\", i.file.name);\n }\n if (raw.blocked && !seen.current.has(raw.blocked)) {\n seen.current.add(raw.blocked);\n report(raw.blocked, \"upload\");\n }\n }, [raw.items, raw.blocked, report]);\n\n // Processing failures that appear after the item's first read.\n const failures = useRef<{ key: string; seen: Set<string> } | null>(null);\n useEffect(() => {\n if (!read.read) return;\n const failed = uploads.filter((f) => f.failed);\n if (failures.current?.key !== key) {\n failures.current = { key, seen: new Set(failed.map((f) => `${f.path}:${f.failed!.of}`)) };\n return;\n }\n for (const f of failed) {\n const id = `${f.path}:${f.failed!.of}`;\n if (failures.current.seen.has(id)) continue;\n failures.current.seen.add(id);\n report(failureError(f.failed!), \"folder.process\", f.path.split(\"/\").pop());\n }\n }, [read.read, uploads, report, key]);\n\n useEffect(() => void (read.error && report(read.error, \"folder.load\")), [read.error, report]);\n\n const busy =\n waiting.length > 0 ||\n committing ||\n updating > 0 ||\n (auto && headReady && !stalled) ||\n items.some((i) => i.status === \"uploading\" || (i.status === \"queued\" && !raw.blocked));\n const fileCount = uploads.length + waiting.length + inbound(items, listed).length;\n\n // Reads the latest groups, so callbacks stay stable.\n const groupOf = (path: string) => live.current.groups.find((g) => ruleFor([{ path: g.path }], path));\n return {\n ref: item,\n options: o,\n read,\n groups,\n uploads,\n queue,\n waiting,\n add,\n refused,\n dismiss: useCallback(() => setRefused([]), []),\n move: useCallback((path, index) => update([{ op: \"move\", path, index }]), [update]),\n remove: useCallback((paths) => update(paths.map((path): Op => ({ op: \"remove\", path }))), [update]),\n rename: useCallback(\n (path, name) => {\n const g = groupOf(path);\n const to = (g ? ruleDir(g.path) : path.slice(0, path.lastIndexOf(\"/\") + 1)) + name.trim();\n if (stem(to) === stem(path)) return Promise.resolve();\n if (g?.files.some((f) => stem(f.path) === stem(to))) {\n const err = new ContentKitError(\"conflict\", `${name} exists`, { status: 409 });\n setError(err);\n return Promise.reject(err);\n }\n return update([{ op: \"rename\", path, to }]);\n },\n [update],\n ),\n edit: useCallback((path, edit) => update([{ op: \"edit\", path, ...(edit ? { edit } : {}) }]), [update]),\n replace: useCallback(\n (path, file) => update([], () => client.media.put(file, { ref: live.current.ref, path: stem(path), wait: false })),\n [update, client],\n ),\n commit: manualCommit,\n discard: useCallback(() => {\n pending.current = [];\n setWaiting([]);\n for (const i of q.getSnapshot().items) q.remove(i.id);\n q.pause();\n }, [q]),\n updating: updating > 0,\n committing,\n busy,\n fileCount,\n error: error ?? stalled,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"folder.js","names":[],"sources":["../../src/react/folder.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { itemKey } from \"../client/media/ref.js\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport { ContentKitError, failureError, toContentKitError } from \"../client/errors.js\";\nimport type { Edit, FileInfo, Op, ReadResult, RefBody, UploadRule } from \"../client/generated/wire.js\";\nimport { stem } from \"../client/media/client.js\";\nimport type { QueueItem } from \"../client/media/queue.js\";\nimport { filesFor, isPattern, ruleDir, ruleFor, screenFiles, uniqueName, uploadRules, type Screened } from \"../client/media/rules.js\";\nimport { useContentKitClient, useErrorReporter, type ContentKitErrorHandler } from \"./context.js\";\nimport { useMediaRead, type UseMediaRead } from \"./read.js\";\nimport { useUploadQueue, type UseUploadQueue } from \"./upload.js\";\n\nexport interface MediaFolderOptions {\n /**\n * The upload paths shown and added to, e.g. [\"images/{name}\",\n * \"videos/{name}\"]: one group each. Default every pattern path of the kind\n * the server does not name. A file goes to the first path whose rule takes\n * its type.\n */\n paths?: readonly string[];\n /**\n * \"manual\" (default): commit() adds the uploaded files. \"auto\": each\n * upload commits once those before it have, in the order added (a draft).\n */\n commit?: \"manual\" | \"auto\";\n /** A file's name under its path; default its own, numbered around names taken. */\n name?: (file: File) => string;\n /** Files uploading at once. Default 2. */\n concurrency?: number;\n /** Poll interval (ms) while uploads process. Default 2500. */\n poll?: number | false;\n /** Every failure (uploads, commits, updates, processing); default the provider's. */\n onError?: ContentKitErrorHandler;\n /** Overrides the provider's client. */\n client?: ContentKitClient;\n}\n\nexport interface FolderGroup {\n /** The upload path, e.g. \"images/{name}\". */\n path: string;\n /** Its rules, once an editor read states them. */\n rule?: UploadRule;\n /** Its uploads in manifest order. */\n files: FileInfo[];\n}\n\nexport interface UseMediaFolder {\n /** The item; null until the host has one (files added meanwhile wait for it). */\n ref: RefBody | null;\n /** The options the folder was opened with. */\n options: MediaFolderOptions;\n /** The editor read of the item (every window, polled while processing). */\n read: UseMediaRead;\n groups: FolderGroup[];\n /** Every group's uploads. */\n uploads: FileInfo[];\n /** The upload queue; a file leaves it once the editor read lists it. */\n queue: UseUploadQueue;\n /**\n * Uploads on the server not yet added and not in the queue: an earlier\n * visit's, or another editor's. commit() adds them; remove() discards them.\n */\n pending: FileInfo[];\n /**\n * Files added before the item and its editor read (the upload rules) were\n * known. They are screened and queued when the rules arrive, or refused\n * when the read fails or states no rules (the actor may not edit the item).\n */\n waiting: readonly File[];\n /** Screens files against the rules and queues the rest (once the rules are known); refusals land in refused. */\n add: (files: Iterable<File>) => void;\n /** The last screening's refusals. */\n refused: Screened[\"refused\"];\n dismiss: () => void;\n /** Moves an upload to index among its group's uploads. */\n move: (path: string, index: number) => Promise<void>;\n remove: (paths: readonly string[]) => Promise<void>;\n /** Renames an upload within its path; the extension stays unless name has one. */\n rename: (path: string, name: string) => Promise<void>;\n edit: (path: string, edit: Edit | null) => Promise<void>;\n /** Uploads file in place of the upload at path (same position and meta, no edit). */\n replace: (path: string, file: File) => Promise<void>;\n /** Commits the uploaded files in queue order, then the pending ones. */\n commit: () => Promise<FileInfo[]>;\n /** Stops every upload, aborts its multipart upload and empties the queue (before deleting a draft). */\n discard: () => void;\n /** An update (move, remove, rename, edit, replace) is running. */\n updating: boolean;\n committing: boolean;\n /**\n * Work is in flight: files wait for the rules, uploads are queued or\n * running, an automatic commit is due, or a commit or update runs.\n * Leaving the page now interrupts it.\n */\n busy: boolean;\n /** The item's uploads plus the files on their way in (waiting, queued, uploading, uploaded, pending, being added); failed uploads are not counted. */\n fileCount: number;\n /** The last update's or commit's failure; cleared by the next success. */\n error?: ContentKitError;\n}\n\nconst ALL = { start: 0, end: Number.MAX_SAFE_INTEGER, chunk: 200 };\n\n/** The upload's name under its group (\"images/a.png\" in \"images/{name}\" is \"a.png\"). */\nexport const nameIn = (group: { path: string }, path: string) => path.slice(ruleDir(group.path).length);\n\n/** Its smallest derived image with a URL: a thumbnail for an editor. */\nexport function thumbnailOf(read: ReadResult | null | undefined, upload: FileInfo): FileInfo | undefined {\n let best: FileInfo | undefined;\n for (const f of read?.files ?? []) {\n if (f.upload || f.from !== upload.path || !f.url || !f.type.startsWith(\"image/\")) continue;\n if (!best || (f.w ?? Infinity) < (best.w ?? Infinity)) best = f;\n }\n return best;\n}\n\nconst NO_ITEM: RefBody = { kind: \"\", id: \"\" };\nconst keyOf = (ref: RefBody | null) => (ref ? itemKey(ref) : \"\");\n\nconst noRules = new WeakMap<ReadResult, ContentKitError>();\n/** The refusal for files added to an item whose read carries no upload rules (one per read). */\nfunction notEditor(read: ReadResult): ContentKitError {\n let e = noRules.get(read);\n if (!e) noRules.set(read, (e = new ContentKitError(\"forbidden\", \"the editor read states no upload rules\", { status: 403 })));\n return e;\n}\n\n/** Queue items still on their way in: not failed, and not yet listed by the read. */\nfunction inbound(items: readonly QueueItem[], listed: ReadonlySet<string>) {\n return items.filter((i) => i.status !== \"failed\" && !(i.status === \"committed\" && listed.has(i.path)));\n}\n\n/**\n * An item's folder for its editors: the editor read, an upload queue, the\n * kind's upload rules screening files before they upload, and the updates\n * (move, remove, rename, edit, replace). Files added before the editor read\n * arrives wait for its rules; with a null ref they wait for the item too (a\n * draft created on the first drop). Uploads the worker fails after the read\n * are reported once (\"folder.process\"). Another ref gets a fresh queue.\n */\nexport function useMediaFolder(ref: RefBody | null | undefined, o: MediaFolderOptions = {}): UseMediaFolder {\n const client = useContentKitClient(o.client);\n const report = useErrorReporter(o.onError);\n const item = ref ?? null;\n const key = keyOf(item);\n const target = item ?? NO_ITEM;\n // Without an item there is nothing to read: a supplied null read never fetches.\n const read = useMediaRead(target, { editor: true, window: ALL, poll: o.poll, client, read: item ? undefined : null });\n const rules = uploadRules(read.read);\n const pathsKey = o.paths?.join(\"\\n\");\n const groupPaths = useMemo(\n () => (pathsKey !== undefined ? pathsKey.split(\"\\n\").filter(Boolean) : rules.filter((r) => isPattern(r.path) && !r.named).map((r) => r.path)),\n [pathsKey, rules],\n );\n const groups = useMemo((): FolderGroup[] => {\n const known: { path: string }[] = rules.length ? rules : groupPaths.map((path) => ({ path }));\n return groupPaths.map((path) => ({ path, rule: rules.find((r) => r.path === path), files: filesFor(read.read, { path }, known) }));\n }, [groupPaths, rules, read.read]);\n const uploads = useMemo(() => groups.flatMap((g) => g.files), [groups]);\n const listed = useMemo(() => new Set(uploads.map((f) => f.path)), [uploads]);\n // Unattached uploads under the folder's paths: this session's while it adds them, or left by another.\n const unattached = useMemo(() => {\n const known: { path: string }[] = rules.length ? rules : groupPaths.map((path) => ({ path }));\n return (read.read?.files ?? []).filter((f) => f.upload && f.unattached && groupPaths.includes(ruleFor(known, f.path)?.path ?? \"\"));\n }, [read.read, rules, groupPaths]);\n\n const raw = useUploadQueue({ client, ref: target, path: `${ruleDir(groupPaths[0] ?? \"{name}\")}{name}`, concurrency: o.concurrency });\n // The ItemUploadQueue itself is stable per item; the hook's wrapper is new every render.\n const q = raw.queue;\n // A committed file stays in the queue until the read lists it, so it never drops out of view.\n const items = useMemo(() => raw.items.filter((i) => !(i.status === \"committed\" && listed.has(i.path))), [raw.items, listed]);\n const queue = { ...raw, items };\n const leftover = useMemo(() => {\n const held = new Set(raw.items.map((i) => stem(i.path)));\n return unattached.filter((f) => !held.has(stem(f.path)));\n }, [unattached, raw.items]);\n const [refused, setRefused] = useState<Screened[\"refused\"]>([]);\n const [waiting, setWaiting] = useState<readonly File[]>([]);\n const pending = useRef<File[]>([]);\n const [updating, setUpdating] = useState(0);\n const [committing, setCommitting] = useState(false);\n const [error, setError] = useState<ContentKitError>();\n // An automatic commit that failed: no more until commit() or new files.\n const [stall, setStall] = useState<{ key: string; error: ContentKitError }>();\n const stalled = stall?.key === key ? stall.error : undefined;\n const opts = useRef(o);\n opts.current = o;\n // The rules are known once the item's editor read states them. A failed read\n // refuses what waits, and so does one without rules: the server answered a\n // viewer's read, so nothing uploads here.\n const ready = !!item && !!read.read?.uploads;\n const loadError = !item ? undefined : !read.read ? read.error : !read.read.uploads ? notEditor(read.read) : undefined;\n const live = useRef({ groups, ref: target, listed, unattached, leftover, ready, loadError });\n live.current = { groups, ref: target, listed, unattached, leftover, ready, loadError };\n\n const place = useCallback(\n (files: File[]) => {\n const { groups, listed, unattached, leftover } = live.current;\n const gs = groups.map((g) => g.rule ?? { path: g.path, types: [], max_bytes: 0 });\n const queued = inbound(q.getSnapshot().items, listed);\n const inGroup = (path: string, rule: UploadRule) => ruleFor(gs, path)?.path === rule.path;\n const s = screenFiles(files, gs, (rule) => groups.find((g) => g.path === rule.path)!.files.length + leftover.filter((f) => inGroup(f.path, rule)).length + queued.filter((i) => inGroup(i.path, rule)).length);\n const taken = new Map<string, string[]>();\n const takenIn = (rule: UploadRule) => {\n let t = taken.get(rule.path);\n if (!t) {\n const g = groups.find((x) => x.path === rule.path)!;\n // Unattached uploads hold their names too: a put over one is refused.\n t = [g.files, unattached].flat().filter((f) => inGroup(f.path, rule)).map((f) => nameIn(g, f.path));\n t.push(...queued.filter((i) => inGroup(i.path, rule)).map((i) => nameIn(g, i.path)));\n taken.set(rule.path, t);\n }\n return t;\n };\n const paths = new Map<File, string>();\n for (const { file, rule } of s.accepted) {\n const t = takenIn(rule);\n const name = opts.current.name?.(file) ?? uniqueName(file.name, t);\n t.push(name);\n paths.set(file, ruleDir(rule.path) + name);\n }\n if (paths.size) {\n q.add(paths.keys(), { path: (f) => paths.get(f)! });\n // New files lift a block and restart a queue discard() paused.\n q.start();\n setStall(undefined);\n }\n setRefused(s.refused);\n for (const r of s.refused) report(r.error, \"upload\", r.file.name);\n },\n [q, report],\n );\n\n // Screens what waits once the rules are known; a failed read refuses it (the read failure is reported once, as folder.load).\n const flush = useCallback(() => {\n const files = pending.current;\n const { ready, loadError } = live.current;\n if (!files.length || (!ready && !loadError)) return false;\n pending.current = [];\n setWaiting([]);\n if (ready) place(files);\n else setRefused(files.map((file) => ({ file, error: loadError! })));\n return true;\n }, [place]);\n\n const add = useCallback(\n (files: Iterable<File>) => {\n const list = [...files];\n if (!list.length) return;\n pending.current = [...pending.current, ...list];\n if (!flush()) setWaiting(pending.current);\n },\n [flush],\n );\n useEffect(() => void flush(), [flush, ready, loadError]);\n\n const update = useCallback(\n async (ops: Op[], run?: () => Promise<unknown>) => {\n setUpdating((n) => n + 1);\n try {\n if (run) await run();\n else await client.media.commit(live.current.ref, ops);\n setError(undefined);\n } catch (e) {\n const err = toContentKitError(e);\n if (err.code !== \"aborted\") setError(err);\n report(err, \"folder.update\");\n throw err;\n } finally {\n setUpdating((n) => n - 1);\n }\n },\n [client, report],\n );\n\n const commit = useCallback(async () => {\n setCommitting(true);\n try {\n const files = await q.commit(undefined, { head: opts.current.commit === \"auto\", attach: live.current.leftover.map((f) => f.path) });\n setError(undefined);\n return files;\n } catch (e) {\n const err = toContentKitError(e);\n setError(err);\n report(err, \"folder.commit\");\n throw err;\n } finally {\n setCommitting(false);\n }\n }, [q, report]);\n\n // Committed files leave the queue once the read lists them, or once a read lands without them.\n useEffect(() => {\n for (const i of raw.items) if (i.status === \"committed\" && (listed.has(i.path) || !read.loading)) q.remove(i.id);\n }, [raw.items, listed, read.loading, q]);\n\n // A draft commits the uploaded head of the queue as it finishes, and what\n // an earlier visit left pending once a read states it; a failed commit waits for commit().\n const auto = o.commit === \"auto\";\n const headReady = items.find((i) => i.status !== \"committed\")?.status === \"uploaded\" || (leftover.length > 0 && !read.loading);\n useEffect(() => {\n if (!auto || !headReady || committing || stalled) return;\n commit().catch((e: unknown) => setStall({ key, error: toContentKitError(e) }));\n }, [auto, headReady, committing, stalled, commit, key]);\n const manualCommit = useCallback(async () => {\n setStall(undefined);\n return commit();\n }, [commit]);\n\n // Each failed upload and queue-wide refusal is reported once; rows keep showing it.\n const seen = useRef(new Set<unknown>());\n useEffect(() => {\n for (const i of raw.items) {\n if (i.status !== \"failed\" || !i.error || seen.current.has(i.error)) continue;\n seen.current.add(i.error);\n report(i.error, \"upload\", i.file.name);\n }\n if (raw.blocked && !seen.current.has(raw.blocked)) {\n seen.current.add(raw.blocked);\n report(raw.blocked, \"upload\");\n }\n }, [raw.items, raw.blocked, report]);\n\n // Processing failures that appear after the item's first read.\n const failures = useRef<{ key: string; seen: Set<string> } | null>(null);\n useEffect(() => {\n if (!read.read) return;\n const failed = uploads.filter((f) => f.failed);\n if (failures.current?.key !== key) {\n failures.current = { key, seen: new Set(failed.map((f) => `${f.path}:${f.failed!.of}`)) };\n return;\n }\n for (const f of failed) {\n const id = `${f.path}:${f.failed!.of}`;\n if (failures.current.seen.has(id)) continue;\n failures.current.seen.add(id);\n report(failureError(f.failed!), \"folder.process\", f.path.split(\"/\").pop());\n }\n }, [read.read, uploads, report, key]);\n\n useEffect(() => void (read.error && report(read.error, \"folder.load\")), [read.error, report]);\n\n const busy =\n waiting.length > 0 ||\n committing ||\n updating > 0 ||\n (auto && headReady && !stalled) ||\n items.some((i) => i.status === \"uploading\" || (i.status === \"queued\" && !raw.blocked));\n const fileCount = uploads.length + waiting.length + inbound(items, listed).length + leftover.length;\n\n // Reads the latest groups, so callbacks stay stable.\n const groupOf = (path: string) => live.current.groups.find((g) => ruleFor([{ path: g.path }], path));\n return {\n ref: item,\n options: o,\n read,\n groups,\n uploads,\n queue,\n pending: leftover,\n waiting,\n add,\n refused,\n dismiss: useCallback(() => setRefused([]), []),\n move: useCallback((path, index) => update([{ op: \"move\", path, index }]), [update]),\n remove: useCallback((paths) => update(paths.map((path): Op => ({ op: \"remove\", path }))), [update]),\n rename: useCallback(\n (path, name) => {\n const g = groupOf(path);\n const to = (g ? ruleDir(g.path) : path.slice(0, path.lastIndexOf(\"/\") + 1)) + name.trim();\n if (stem(to) === stem(path)) return Promise.resolve();\n if (g?.files.some((f) => stem(f.path) === stem(to))) {\n const err = new ContentKitError(\"conflict\", `${name} exists`, { status: 409 });\n setError(err);\n return Promise.reject(err);\n }\n return update([{ op: \"rename\", path, to }]);\n },\n [update],\n ),\n edit: useCallback((path, edit) => update([{ op: \"edit\", path, ...(edit ? { edit } : {}) }]), [update]),\n replace: useCallback(\n (path, file) => update([], () => client.media.put(file, { ref: live.current.ref, path: stem(path), wait: false })),\n [update, client],\n ),\n commit: manualCommit,\n discard: useCallback(() => {\n pending.current = [];\n setWaiting([]);\n for (const i of q.getSnapshot().items) q.remove(i.id);\n q.pause();\n }, [q]),\n updating: updating > 0,\n committing,\n busy,\n fileCount,\n error: error ?? stalled,\n };\n}\n"],"mappings":"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"}
|
package/dist/react/groups.d.ts
CHANGED
|
@@ -108,7 +108,6 @@ export interface UseContentGroups {
|
|
|
108
108
|
/** Replaces the content's memberships, its versions' included, in these group kinds (every kind its kind joins when omitted). */
|
|
109
109
|
set: (memberships: Membership[], kinds?: string[]) => Promise<ContentMemberships>;
|
|
110
110
|
}
|
|
111
|
-
/** Staff: one content's memberships, its versions' included, and their replacement. */
|
|
112
111
|
export declare function useContentGroups(contentId: string | null | undefined, o?: {
|
|
113
112
|
language?: string;
|
|
114
113
|
client?: ContentKitClient;
|
package/dist/react/groups.js
CHANGED
|
@@ -189,41 +189,42 @@ function h(i, o = {}) {
|
|
|
189
189
|
reorder: a((e) => _((t) => s.groups.reorder(t, e)), [s, _])
|
|
190
190
|
};
|
|
191
191
|
}
|
|
192
|
-
|
|
192
|
+
var g = [];
|
|
193
|
+
function _(r, o = {}) {
|
|
193
194
|
let { client: c, store: u, scope: d, language: f } = n(o.client), p = o.language ?? f, m = r ? t("group-memberships", d, r, p) : null, h = i(u, m, {
|
|
194
195
|
type: "group-memberships",
|
|
195
196
|
id: r ?? void 0
|
|
196
|
-
}, (e) => c.groups.ofContent(r, p, e)),
|
|
197
|
+
}, (e) => c.groups.ofContent(r, p, e)), _ = e(), [v, y] = l(0), b = a(async (e, t) => {
|
|
197
198
|
if (!r) throw Error("contentkit-ui: no content");
|
|
198
|
-
|
|
199
|
+
y((e) => e + 1);
|
|
199
200
|
try {
|
|
200
|
-
let n = await
|
|
201
|
+
let n = await _(() => c.groups.setOfContent(r, {
|
|
201
202
|
kinds: t,
|
|
202
203
|
memberships: e
|
|
203
204
|
}, p));
|
|
204
205
|
return m && u.set(m, n), n;
|
|
205
206
|
} finally {
|
|
206
|
-
|
|
207
|
+
y((e) => e - 1);
|
|
207
208
|
}
|
|
208
209
|
}, [
|
|
209
210
|
c,
|
|
210
211
|
r,
|
|
211
212
|
p,
|
|
212
|
-
|
|
213
|
+
_,
|
|
213
214
|
u,
|
|
214
215
|
m
|
|
215
|
-
]),
|
|
216
|
+
]), x = s(() => new Map((h.data?.groups ?? []).map((e) => [e.id, e])), [h.data]);
|
|
216
217
|
return {
|
|
217
|
-
memberships: h.data?.memberships ?? [],
|
|
218
|
-
groups:
|
|
218
|
+
memberships: s(() => h.data?.memberships ?? g, [h.data]),
|
|
219
|
+
groups: x,
|
|
219
220
|
loading: !!m && (h.loading || !h.loaded),
|
|
220
221
|
error: h.error,
|
|
221
|
-
saving:
|
|
222
|
+
saving: v > 0,
|
|
222
223
|
reload: h.reload,
|
|
223
|
-
set:
|
|
224
|
+
set: b
|
|
224
225
|
};
|
|
225
226
|
}
|
|
226
227
|
//#endregion
|
|
227
|
-
export { m as memberId,
|
|
228
|
+
export { m as memberId, _ as useContentGroups, f as useGroup, h as useGroupMembers, d as useGroups, u as useKinds, p as useMyGroups };
|
|
228
229
|
|
|
229
230
|
//# sourceMappingURL=groups.js.map
|
package/dist/react/groups.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"groups.js","names":[],"sources":["../../src/react/groups.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport type {\n ContentMemberships,\n ContentModel,\n Edge,\n EdgeInput,\n Group,\n GroupCard,\n GroupDetail,\n GroupInput,\n GroupUpdate,\n MemberRef,\n Membership,\n MergeReport,\n MyGroup,\n Name,\n Edit,\n} from \"../client/generated/wire.js\";\nimport type { GroupImageSlot, MyGroupQuery } from \"../client/groups.js\";\nimport { useGuarded } from \"./context.js\";\nimport { keyOf, useContentScope, useList, useResource, type UseList } from \"./use-resource.js\";\n\nexport interface UseKinds {\n /** null until it loads (or when it fails). */\n kinds: ContentModel | null;\n loading: boolean;\n error?: ContentKitError;\n reload: () => void;\n}\n\n/** The site's content model (GET /v1/kinds): languages, tiers, kinds, group kinds and content filters; one read per client. */\nexport function useKinds(o: { client?: ContentKitClient } = {}): UseKinds {\n const { client, store } = useContentScope(o.client);\n const r = useResource<ContentModel>(store, keyOf(\"kinds\"), { type: \"kinds\" }, (s) => client.kinds(s));\n return { kinds: r.data ?? null, loading: !r.data && (r.loading || !r.loaded), error: r.error, reload: r.reload };\n}\n\nexport type GroupSort = \"name\" | \"count\" | \"newest\";\n\nexport interface GroupFilter {\n kind?: string;\n /** Children of this group. */\n parent?: string;\n /** Groups scoped by this group (characters of a series). */\n scope?: string;\n slug?: string;\n /** Sort names starting with this letter (a-z), or with anything else (\"#\"). */\n letter?: string;\n /** Names, counts and paths in this language; default the provider's. */\n language?: string;\n /** Count contents of this kind only. */\n contentKind?: string;\n /** Default \"name\". */\n sort?: GroupSort;\n /** Default 50, at most 200. */\n pageSize?: number;\n}\n\n/** Active groups with name, path, cover and count, a cursor page at a time. */\nexport function useGroups(f: GroupFilter = {}, o: { client?: ContentKitClient } = {}): UseList<GroupCard> {\n const { client, store, scope, language: provided } = useContentScope(o.client);\n const language = f.language ?? provided;\n const size = f.pageSize ?? 50;\n return useList(\n store,\n keyOf(\"groups\", scope, f.kind, f.parent, f.scope, f.slug, f.letter, language, f.contentKind, f.sort, size),\n { type: \"groups\" },\n async (cursor, signal) => {\n const page = await client.groups.list(\n { kind: f.kind, parent: f.parent, scope: f.scope, slug: f.slug, letter: f.letter, language, content_kind: f.contentKind, sort: f.sort, cursor: cursor as string | undefined, limit: size },\n signal,\n );\n return { items: page.groups, next: page.next_cursor ?? null };\n },\n );\n}\n\nexport interface UseGroup {\n /** The group as its page shows it; null before create and while loading. A merged group reads as its survivor. */\n group: GroupDetail | null;\n /** What this hook deleted, until restored: reads no longer answer for it. */\n deleted: Group | null;\n loading: boolean;\n error?: ContentKitError;\n /** Writes in flight. */\n saving: boolean;\n reload: () => void;\n /** Creates the group; the hook then shows it. */\n create: (input: GroupInput) => Promise<Group>;\n update: (patch: GroupUpdate) => Promise<Group>;\n remove: () => Promise<Group>;\n restore: () => Promise<Group>;\n /** Replaces (\"set\"), adds or removes names and aliases; a new canonical name demotes the old one to an alias. */\n names: (op: \"set\" | \"add\" | \"remove\", names: Name[]) => Promise<Name[]>;\n edges: (op: \"add\" | \"remove\", edges: EdgeInput[]) => Promise<Edge[]>;\n /** Merges the group into another of its kind; the hook then shows the survivor. */\n merge: (into: string) => Promise<MergeReport>;\n /** Uploads an image (cropped by edit) and selects it as the cover or avatar. */\n uploadImage: (slot: GroupImageSlot, file: Blob & { name?: string }, o?: { wait?: boolean; edit?: Edit }) => Promise<Group>;\n /** Shows the images uploaded here until their renditions exist. */\n previews: Partial<Record<GroupImageSlot, string>>;\n}\n\n/** One group: its page for anyone; create, edit, names, edges, cover, merge, delete and restore for staff (root:groups:write). null creates. */\nexport function useGroup(id: string | null | undefined, o: { language?: string; initial?: GroupDetail; client?: ContentKitClient } = {}): UseGroup {\n const { client, store, scope, language: provided } = useContentScope(o.client);\n const language = o.language ?? provided;\n const [created, setCreated] = useState<string | null>(null);\n const [merged, setMerged] = useState<string | null>(null);\n const [deleted, setDeleted] = useState<Group | null>(null);\n const gid = merged ?? id ?? created;\n useEffect(() => {\n setMerged(null);\n setDeleted(null);\n }, [id]);\n const key = gid && !deleted ? keyOf(\"group\", scope, gid, language) : null;\n const r = useResource<GroupDetail>(store, key, { type: \"group\", id: gid ?? undefined }, (s) => client.groups.get(gid!, language, s), { initial: o.initial });\n const guarded = useGuarded();\n const [inflight, setInflight] = useState(0);\n const run = useCallback(\n async <T>(fn: () => Promise<T>): Promise<T> => {\n setInflight((n) => n + 1);\n try {\n return await guarded(fn);\n } finally {\n setInflight((n) => n - 1);\n }\n },\n [guarded],\n );\n const need = useCallback(() => {\n if (!gid) throw new Error(\"contentkit-ui: create the group first\");\n return gid;\n }, [gid]);\n\n const [previews, setPreviews] = useState<Partial<Record<GroupImageSlot, string>>>({});\n const blobs = useRef<string[]>([]);\n useEffect(() => {\n const urls = blobs.current;\n return () => urls.forEach((u) => URL.revokeObjectURL(u));\n }, []);\n\n const group = r.data ?? null;\n return {\n group: deleted ? null : group,\n deleted,\n loading: !!key && (r.loading || (!r.loaded && o.initial === undefined)),\n error: deleted ? undefined : r.error,\n saving: inflight > 0,\n reload: r.reload,\n create: useCallback(\n (input: GroupInput) =>\n run(async () => {\n const [g] = await client.groups.create([input]);\n setCreated(g!.id);\n return g!;\n }),\n [client, run],\n ),\n update: useCallback((patch: GroupUpdate) => run(() => client.groups.update(need(), patch)), [client, run, need]),\n remove: useCallback(\n () =>\n run(async () => {\n const g = await client.groups.remove(need());\n setDeleted(g);\n return g;\n }),\n [client, run, need],\n ),\n restore: useCallback(\n () =>\n run(async () => {\n const g = await client.groups.update(need(), { state: \"active\" });\n setDeleted(null);\n return g;\n }),\n [client, run, need],\n ),\n names: useCallback((op: \"set\" | \"add\" | \"remove\", names: Name[]) => run(() => client.groups.names(need(), op, names)), [client, run, need]),\n edges: useCallback((op: \"add\" | \"remove\", edges: EdgeInput[]) => run(() => client.groups.edges(need(), op, edges)), [client, run, need]),\n merge: useCallback(\n (into: string) =>\n run(async () => {\n const report = await client.groups.merge(need(), into);\n setMerged(report.into);\n return report;\n }),\n [client, run, need],\n ),\n uploadImage: useCallback(\n (slot: GroupImageSlot, file: Blob & { name?: string }, u: { wait?: boolean; edit?: Edit } = {}) =>\n run(async () => {\n const kind = group?.kind;\n if (!kind) throw new Error(\"contentkit-ui: load the group first\");\n const g = await client.groups.uploadImage({ kind, id: need() }, slot, file, u);\n // A cropped upload's renditions show the crop; until then the file shows uncropped.\n if (typeof URL.createObjectURL === \"function\" && !u.edit) {\n const url = URL.createObjectURL(file);\n blobs.current.push(url);\n setPreviews((p) => ({ ...p, [slot]: url }));\n }\n return g;\n }),\n [client, run, need, group?.kind],\n ),\n previews: { cover: group?.cover?.image ? previews.cover : undefined, avatar: group?.avatar?.image ? previews.avatar : undefined },\n };\n}\n\n/** Groups the signed-in person owns, edits or belongs to, a cursor page at a time; nothing signed out. */\nexport function useMyGroups(q: Omit<MyGroupQuery, \"cursor\" | \"limit\"> & { pageSize?: number } = {}, o: { client?: ContentKitClient } = {}): UseList<MyGroup> {\n const { client, store, scope, viewer, language } = useContentScope(o.client);\n const { pageSize, ...rest } = q;\n const query = { language, ...rest };\n return useList(store, viewer === null ? null : keyOf(\"groups-mine\", scope, query, pageSize), { type: \"groups-mine\" }, async (cursor, signal) => {\n const page = await client.groups.mine({ ...query, cursor: cursor as string | undefined, limit: pageSize }, signal);\n return { items: page.groups, next: page.next_cursor ?? null };\n });\n}\n\n/** A membership with a stable id: (group, content, version, role) is its identity. */\nexport type Member = Membership & { id: string };\n\nexport const memberId = (m: Membership): string => [m.group_id ?? \"\", m.content_id ?? \"\", m.version_id ?? \"\", m.role ?? \"\"].join(\"|\");\n\nexport interface UseGroupMembers extends UseList<Member> {\n saving: boolean;\n /** Adds members, or updates their role's position, window and state. */\n add: (members: Membership[]) => Promise<number>;\n remove: (members: Membership[]) => Promise<number>;\n /** Orders an ordered group: the listed members first, in list order. */\n reorder: (members: MemberRef[]) => Promise<number>;\n}\n\n/** Staff: a group's memberships in member order, with writes. */\nexport function useGroupMembers(id: string | null | undefined, o: { pageSize?: number; client?: ContentKitClient } = {}): UseGroupMembers {\n const { client, store, scope } = useContentScope(o.client);\n const size = o.pageSize ?? 50;\n const list = useList<Member>(store, id ? keyOf(\"group-members\", scope, id, size) : null, { type: \"group-members\", id: id ?? undefined }, async (cursor, signal) => {\n const page = await client.groups.members(id!, { cursor: cursor as string | undefined, limit: size }, signal);\n return { items: page.members.map((m) => ({ ...m, id: memberId(m) })), next: page.next_cursor ?? null };\n });\n const guarded = useGuarded();\n const [inflight, setInflight] = useState(0);\n const write = useCallback(\n async (fn: (gid: string) => Promise<{ count: number }>) => {\n if (!id) throw new Error(\"contentkit-ui: no group\");\n setInflight((n) => n + 1);\n try {\n return (await guarded(() => fn(id))).count;\n } finally {\n setInflight((n) => n - 1);\n }\n },\n [id, guarded],\n );\n return {\n ...list,\n saving: inflight > 0,\n add: useCallback((ms: Membership[]) => write((g) => client.groups.addMembers(g, ms)), [client, write]),\n remove: useCallback((ms: Membership[]) => write((g) => client.groups.removeMembers(g, ms)), [client, write]),\n reorder: useCallback((ms: MemberRef[]) => write((g) => client.groups.reorder(g, ms)), [client, write]),\n };\n}\n\nexport interface UseContentGroups {\n memberships: Membership[];\n /** The groups the memberships name. */\n groups: ReadonlyMap<string, GroupCard>;\n loading: boolean;\n error?: ContentKitError;\n saving: boolean;\n reload: () => void;\n /** Replaces the content's memberships, its versions' included, in these group kinds (every kind its kind joins when omitted). */\n set: (memberships: Membership[], kinds?: string[]) => Promise<ContentMemberships>;\n}\n\n/** Staff: one content's memberships, its versions' included, and their replacement. */\nexport function useContentGroups(contentId: string | null | undefined, o: { language?: string; client?: ContentKitClient } = {}): UseContentGroups {\n const { client, store, scope, language: provided } = useContentScope(o.client);\n const language = o.language ?? provided;\n const key = contentId ? keyOf(\"group-memberships\", scope, contentId, language) : null;\n const r = useResource<ContentMemberships>(store, key, { type: \"group-memberships\", id: contentId ?? undefined }, (s) => client.groups.ofContent(contentId!, language, s));\n const guarded = useGuarded();\n const [inflight, setInflight] = useState(0);\n const set = useCallback(\n async (memberships: Membership[], kinds?: string[]) => {\n if (!contentId) throw new Error(\"contentkit-ui: no content\");\n setInflight((n) => n + 1);\n try {\n const out = await guarded(() => client.groups.setOfContent(contentId, { kinds, memberships }, language));\n if (key) store.set(key, out);\n return out;\n } finally {\n setInflight((n) => n - 1);\n }\n },\n [client, contentId, language, guarded, store, key],\n );\n const groups = useMemo(() => new Map((r.data?.groups ?? []).map((g) => [g.id, g])), [r.data]);\n return {\n memberships: r.data?.memberships ?? [],\n groups,\n loading: !!key && (r.loading || !r.loaded),\n error: r.error,\n saving: inflight > 0,\n reload: r.reload,\n set,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"groups.js","names":[],"sources":["../../src/react/groups.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport type {\n ContentMemberships,\n ContentModel,\n Edge,\n EdgeInput,\n Group,\n GroupCard,\n GroupDetail,\n GroupInput,\n GroupUpdate,\n MemberRef,\n Membership,\n MergeReport,\n MyGroup,\n Name,\n Edit,\n} from \"../client/generated/wire.js\";\nimport type { GroupImageSlot, MyGroupQuery } from \"../client/groups.js\";\nimport { useGuarded } from \"./context.js\";\nimport { keyOf, useContentScope, useList, useResource, type UseList } from \"./use-resource.js\";\n\nexport interface UseKinds {\n /** null until it loads (or when it fails). */\n kinds: ContentModel | null;\n loading: boolean;\n error?: ContentKitError;\n reload: () => void;\n}\n\n/** The site's content model (GET /v1/kinds): languages, tiers, kinds, group kinds and content filters; one read per client. */\nexport function useKinds(o: { client?: ContentKitClient } = {}): UseKinds {\n const { client, store } = useContentScope(o.client);\n const r = useResource<ContentModel>(store, keyOf(\"kinds\"), { type: \"kinds\" }, (s) => client.kinds(s));\n return { kinds: r.data ?? null, loading: !r.data && (r.loading || !r.loaded), error: r.error, reload: r.reload };\n}\n\nexport type GroupSort = \"name\" | \"count\" | \"newest\";\n\nexport interface GroupFilter {\n kind?: string;\n /** Children of this group. */\n parent?: string;\n /** Groups scoped by this group (characters of a series). */\n scope?: string;\n slug?: string;\n /** Sort names starting with this letter (a-z), or with anything else (\"#\"). */\n letter?: string;\n /** Names, counts and paths in this language; default the provider's. */\n language?: string;\n /** Count contents of this kind only. */\n contentKind?: string;\n /** Default \"name\". */\n sort?: GroupSort;\n /** Default 50, at most 200. */\n pageSize?: number;\n}\n\n/** Active groups with name, path, cover and count, a cursor page at a time. */\nexport function useGroups(f: GroupFilter = {}, o: { client?: ContentKitClient } = {}): UseList<GroupCard> {\n const { client, store, scope, language: provided } = useContentScope(o.client);\n const language = f.language ?? provided;\n const size = f.pageSize ?? 50;\n return useList(\n store,\n keyOf(\"groups\", scope, f.kind, f.parent, f.scope, f.slug, f.letter, language, f.contentKind, f.sort, size),\n { type: \"groups\" },\n async (cursor, signal) => {\n const page = await client.groups.list(\n { kind: f.kind, parent: f.parent, scope: f.scope, slug: f.slug, letter: f.letter, language, content_kind: f.contentKind, sort: f.sort, cursor: cursor as string | undefined, limit: size },\n signal,\n );\n return { items: page.groups, next: page.next_cursor ?? null };\n },\n );\n}\n\nexport interface UseGroup {\n /** The group as its page shows it; null before create and while loading. A merged group reads as its survivor. */\n group: GroupDetail | null;\n /** What this hook deleted, until restored: reads no longer answer for it. */\n deleted: Group | null;\n loading: boolean;\n error?: ContentKitError;\n /** Writes in flight. */\n saving: boolean;\n reload: () => void;\n /** Creates the group; the hook then shows it. */\n create: (input: GroupInput) => Promise<Group>;\n update: (patch: GroupUpdate) => Promise<Group>;\n remove: () => Promise<Group>;\n restore: () => Promise<Group>;\n /** Replaces (\"set\"), adds or removes names and aliases; a new canonical name demotes the old one to an alias. */\n names: (op: \"set\" | \"add\" | \"remove\", names: Name[]) => Promise<Name[]>;\n edges: (op: \"add\" | \"remove\", edges: EdgeInput[]) => Promise<Edge[]>;\n /** Merges the group into another of its kind; the hook then shows the survivor. */\n merge: (into: string) => Promise<MergeReport>;\n /** Uploads an image (cropped by edit) and selects it as the cover or avatar. */\n uploadImage: (slot: GroupImageSlot, file: Blob & { name?: string }, o?: { wait?: boolean; edit?: Edit }) => Promise<Group>;\n /** Shows the images uploaded here until their renditions exist. */\n previews: Partial<Record<GroupImageSlot, string>>;\n}\n\n/** One group: its page for anyone; create, edit, names, edges, cover, merge, delete and restore for staff (root:groups:write). null creates. */\nexport function useGroup(id: string | null | undefined, o: { language?: string; initial?: GroupDetail; client?: ContentKitClient } = {}): UseGroup {\n const { client, store, scope, language: provided } = useContentScope(o.client);\n const language = o.language ?? provided;\n const [created, setCreated] = useState<string | null>(null);\n const [merged, setMerged] = useState<string | null>(null);\n const [deleted, setDeleted] = useState<Group | null>(null);\n const gid = merged ?? id ?? created;\n useEffect(() => {\n setMerged(null);\n setDeleted(null);\n }, [id]);\n const key = gid && !deleted ? keyOf(\"group\", scope, gid, language) : null;\n const r = useResource<GroupDetail>(store, key, { type: \"group\", id: gid ?? undefined }, (s) => client.groups.get(gid!, language, s), { initial: o.initial });\n const guarded = useGuarded();\n const [inflight, setInflight] = useState(0);\n const run = useCallback(\n async <T>(fn: () => Promise<T>): Promise<T> => {\n setInflight((n) => n + 1);\n try {\n return await guarded(fn);\n } finally {\n setInflight((n) => n - 1);\n }\n },\n [guarded],\n );\n const need = useCallback(() => {\n if (!gid) throw new Error(\"contentkit-ui: create the group first\");\n return gid;\n }, [gid]);\n\n const [previews, setPreviews] = useState<Partial<Record<GroupImageSlot, string>>>({});\n const blobs = useRef<string[]>([]);\n useEffect(() => {\n const urls = blobs.current;\n return () => urls.forEach((u) => URL.revokeObjectURL(u));\n }, []);\n\n const group = r.data ?? null;\n return {\n group: deleted ? null : group,\n deleted,\n loading: !!key && (r.loading || (!r.loaded && o.initial === undefined)),\n error: deleted ? undefined : r.error,\n saving: inflight > 0,\n reload: r.reload,\n create: useCallback(\n (input: GroupInput) =>\n run(async () => {\n const [g] = await client.groups.create([input]);\n setCreated(g!.id);\n return g!;\n }),\n [client, run],\n ),\n update: useCallback((patch: GroupUpdate) => run(() => client.groups.update(need(), patch)), [client, run, need]),\n remove: useCallback(\n () =>\n run(async () => {\n const g = await client.groups.remove(need());\n setDeleted(g);\n return g;\n }),\n [client, run, need],\n ),\n restore: useCallback(\n () =>\n run(async () => {\n const g = await client.groups.update(need(), { state: \"active\" });\n setDeleted(null);\n return g;\n }),\n [client, run, need],\n ),\n names: useCallback((op: \"set\" | \"add\" | \"remove\", names: Name[]) => run(() => client.groups.names(need(), op, names)), [client, run, need]),\n edges: useCallback((op: \"add\" | \"remove\", edges: EdgeInput[]) => run(() => client.groups.edges(need(), op, edges)), [client, run, need]),\n merge: useCallback(\n (into: string) =>\n run(async () => {\n const report = await client.groups.merge(need(), into);\n setMerged(report.into);\n return report;\n }),\n [client, run, need],\n ),\n uploadImage: useCallback(\n (slot: GroupImageSlot, file: Blob & { name?: string }, u: { wait?: boolean; edit?: Edit } = {}) =>\n run(async () => {\n const kind = group?.kind;\n if (!kind) throw new Error(\"contentkit-ui: load the group first\");\n const g = await client.groups.uploadImage({ kind, id: need() }, slot, file, u);\n // A cropped upload's renditions show the crop; until then the file shows uncropped.\n if (typeof URL.createObjectURL === \"function\" && !u.edit) {\n const url = URL.createObjectURL(file);\n blobs.current.push(url);\n setPreviews((p) => ({ ...p, [slot]: url }));\n }\n return g;\n }),\n [client, run, need, group?.kind],\n ),\n previews: { cover: group?.cover?.image ? previews.cover : undefined, avatar: group?.avatar?.image ? previews.avatar : undefined },\n };\n}\n\n/** Groups the signed-in person owns, edits or belongs to, a cursor page at a time; nothing signed out. */\nexport function useMyGroups(q: Omit<MyGroupQuery, \"cursor\" | \"limit\"> & { pageSize?: number } = {}, o: { client?: ContentKitClient } = {}): UseList<MyGroup> {\n const { client, store, scope, viewer, language } = useContentScope(o.client);\n const { pageSize, ...rest } = q;\n const query = { language, ...rest };\n return useList(store, viewer === null ? null : keyOf(\"groups-mine\", scope, query, pageSize), { type: \"groups-mine\" }, async (cursor, signal) => {\n const page = await client.groups.mine({ ...query, cursor: cursor as string | undefined, limit: pageSize }, signal);\n return { items: page.groups, next: page.next_cursor ?? null };\n });\n}\n\n/** A membership with a stable id: (group, content, version, role) is its identity. */\nexport type Member = Membership & { id: string };\n\nexport const memberId = (m: Membership): string => [m.group_id ?? \"\", m.content_id ?? \"\", m.version_id ?? \"\", m.role ?? \"\"].join(\"|\");\n\nexport interface UseGroupMembers extends UseList<Member> {\n saving: boolean;\n /** Adds members, or updates their role's position, window and state. */\n add: (members: Membership[]) => Promise<number>;\n remove: (members: Membership[]) => Promise<number>;\n /** Orders an ordered group: the listed members first, in list order. */\n reorder: (members: MemberRef[]) => Promise<number>;\n}\n\n/** Staff: a group's memberships in member order, with writes. */\nexport function useGroupMembers(id: string | null | undefined, o: { pageSize?: number; client?: ContentKitClient } = {}): UseGroupMembers {\n const { client, store, scope } = useContentScope(o.client);\n const size = o.pageSize ?? 50;\n const list = useList<Member>(store, id ? keyOf(\"group-members\", scope, id, size) : null, { type: \"group-members\", id: id ?? undefined }, async (cursor, signal) => {\n const page = await client.groups.members(id!, { cursor: cursor as string | undefined, limit: size }, signal);\n return { items: page.members.map((m) => ({ ...m, id: memberId(m) })), next: page.next_cursor ?? null };\n });\n const guarded = useGuarded();\n const [inflight, setInflight] = useState(0);\n const write = useCallback(\n async (fn: (gid: string) => Promise<{ count: number }>) => {\n if (!id) throw new Error(\"contentkit-ui: no group\");\n setInflight((n) => n + 1);\n try {\n return (await guarded(() => fn(id))).count;\n } finally {\n setInflight((n) => n - 1);\n }\n },\n [id, guarded],\n );\n return {\n ...list,\n saving: inflight > 0,\n add: useCallback((ms: Membership[]) => write((g) => client.groups.addMembers(g, ms)), [client, write]),\n remove: useCallback((ms: Membership[]) => write((g) => client.groups.removeMembers(g, ms)), [client, write]),\n reorder: useCallback((ms: MemberRef[]) => write((g) => client.groups.reorder(g, ms)), [client, write]),\n };\n}\n\nexport interface UseContentGroups {\n memberships: Membership[];\n /** The groups the memberships name. */\n groups: ReadonlyMap<string, GroupCard>;\n loading: boolean;\n error?: ContentKitError;\n saving: boolean;\n reload: () => void;\n /** Replaces the content's memberships, its versions' included, in these group kinds (every kind its kind joins when omitted). */\n set: (memberships: Membership[], kinds?: string[]) => Promise<ContentMemberships>;\n}\n\n/** Staff: one content's memberships, its versions' included, and their replacement. */\nconst NO_MEMBERSHIPS: Membership[] = [];\n\nexport function useContentGroups(contentId: string | null | undefined, o: { language?: string; client?: ContentKitClient } = {}): UseContentGroups {\n const { client, store, scope, language: provided } = useContentScope(o.client);\n const language = o.language ?? provided;\n const key = contentId ? keyOf(\"group-memberships\", scope, contentId, language) : null;\n const r = useResource<ContentMemberships>(store, key, { type: \"group-memberships\", id: contentId ?? undefined }, (s) => client.groups.ofContent(contentId!, language, s));\n const guarded = useGuarded();\n const [inflight, setInflight] = useState(0);\n const set = useCallback(\n async (memberships: Membership[], kinds?: string[]) => {\n if (!contentId) throw new Error(\"contentkit-ui: no content\");\n setInflight((n) => n + 1);\n try {\n const out = await guarded(() => client.groups.setOfContent(contentId, { kinds, memberships }, language));\n if (key) store.set(key, out);\n return out;\n } finally {\n setInflight((n) => n - 1);\n }\n },\n [client, contentId, language, guarded, store, key],\n );\n const groups = useMemo(() => new Map((r.data?.groups ?? []).map((g) => [g.id, g])), [r.data]);\n // Stable while loading or failed: effects keyed on it must not see a new list every render.\n const memberships = useMemo(() => r.data?.memberships ?? NO_MEMBERSHIPS, [r.data]);\n return {\n memberships,\n groups,\n loading: !!key && (r.loading || !r.loaded),\n error: r.error,\n saving: inflight > 0,\n reload: r.reload,\n set,\n };\n}\n"],"mappings":";;;;AAiCA,SAAgB,EAAS,IAAmC,CAAC,GAAa;CACxE,IAAM,EAAE,WAAQ,aAAU,EAAgB,EAAE,MAAM,GAC5C,IAAI,EAA0B,GAAO,EAAM,OAAO,GAAG,EAAE,MAAM,QAAQ,IAAI,MAAM,EAAO,MAAM,CAAC,CAAC;CACpG,OAAO;EAAE,OAAO,EAAE,QAAQ;EAAM,SAAS,CAAC,EAAE,SAAS,EAAE,WAAW,CAAC,EAAE;EAAS,OAAO,EAAE;EAAO,QAAQ,EAAE;CAAO;AACjH;AAwBA,SAAgB,EAAU,IAAiB,CAAC,GAAG,IAAmC,CAAC,GAAuB;CACxG,IAAM,EAAE,WAAQ,UAAO,UAAO,UAAU,MAAa,EAAgB,EAAE,MAAM,GACvE,IAAW,EAAE,YAAY,GACzB,IAAO,EAAE,YAAY;CAC3B,OAAO,EACL,GACA,EAAM,UAAU,GAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAU,EAAE,aAAa,EAAE,MAAM,CAAI,GACzG,EAAE,MAAM,SAAS,GACjB,OAAO,GAAQ,MAAW;EACxB,IAAM,IAAO,MAAM,EAAO,OAAO,KAC/B;GAAE,MAAM,EAAE;GAAM,QAAQ,EAAE;GAAQ,OAAO,EAAE;GAAO,MAAM,EAAE;GAAM,QAAQ,EAAE;GAAQ;GAAU,cAAc,EAAE;GAAa,MAAM,EAAE;GAAc;GAA8B,OAAO;EAAK,GACzL,CACF;EACA,OAAO;GAAE,OAAO,EAAK;GAAQ,MAAM,EAAK,eAAe;EAAK;CAC9D,CACF;AACF;AA6BA,SAAgB,EAAS,GAA+B,IAA6E,CAAC,GAAa;CACjJ,IAAM,EAAE,WAAQ,UAAO,UAAO,UAAU,MAAa,EAAgB,EAAE,MAAM,GACvE,IAAW,EAAE,YAAY,GACzB,CAAC,GAAS,KAAc,EAAwB,IAAI,GACpD,CAAC,GAAQ,KAAa,EAAwB,IAAI,GAClD,CAAC,GAAS,KAAc,EAAuB,IAAI,GACnD,IAAM,KAAU,KAAM;CAC5B,QAAgB;EAEd,AADA,EAAU,IAAI,GACd,EAAW,IAAI;CACjB,GAAG,CAAC,CAAE,CAAC;CACP,IAAM,IAAM,KAAO,CAAC,IAAU,EAAM,SAAS,GAAO,GAAK,CAAQ,IAAI,MAC/D,IAAI,EAAyB,GAAO,GAAK;EAAE,MAAM;EAAS,IAAI,KAAO,KAAA;CAAU,IAAI,MAAM,EAAO,OAAO,IAAI,GAAM,GAAU,CAAC,GAAG,EAAE,SAAS,EAAE,QAAQ,CAAC,GACrJ,IAAU,EAAW,GACrB,CAAC,GAAU,KAAe,EAAS,CAAC,GACpC,IAAM,EACV,OAAU,MAAqC;EAC7C,GAAa,MAAM,IAAI,CAAC;EACxB,IAAI;GACF,OAAO,MAAM,EAAQ,CAAE;EACzB,UAAU;GACR,GAAa,MAAM,IAAI,CAAC;EAC1B;CACF,GACA,CAAC,CAAO,CACV,GACM,IAAO,QAAkB;EAC7B,IAAI,CAAC,GAAK,MAAU,MAAM,uCAAuC;EACjE,OAAO;CACT,GAAG,CAAC,CAAG,CAAC,GAEF,CAAC,GAAU,KAAe,EAAkD,CAAC,CAAC,GAC9E,IAAQ,EAAiB,CAAC,CAAC;CACjC,QAAgB;EACd,IAAM,IAAO,EAAM;EACnB,aAAa,EAAK,SAAS,MAAM,IAAI,gBAAgB,CAAC,CAAC;CACzD,GAAG,CAAC,CAAC;CAEL,IAAM,IAAQ,EAAE,QAAQ;CACxB,OAAO;EACL,OAAO,IAAU,OAAO;EACxB;EACA,SAAS,CAAC,CAAC,MAAQ,EAAE,WAAY,CAAC,EAAE,UAAU,EAAE,YAAY,KAAA;EAC5D,OAAO,IAAU,KAAA,IAAY,EAAE;EAC/B,QAAQ,IAAW;EACnB,QAAQ,EAAE;EACV,QAAQ,GACL,MACC,EAAI,YAAY;GACd,IAAM,CAAC,KAAK,MAAM,EAAO,OAAO,OAAO,CAAC,CAAK,CAAC;GAE9C,OADA,EAAW,EAAG,EAAE,GACT;EACT,CAAC,GACH,CAAC,GAAQ,CAAG,CACd;EACA,QAAQ,GAAa,MAAuB,QAAU,EAAO,OAAO,OAAO,EAAK,GAAG,CAAK,CAAC,GAAG;GAAC;GAAQ;GAAK;EAAI,CAAC;EAC/G,QAAQ,QAEJ,EAAI,YAAY;GACd,IAAM,IAAI,MAAM,EAAO,OAAO,OAAO,EAAK,CAAC;GAE3C,OADA,EAAW,CAAC,GACL;EACT,CAAC,GACH;GAAC;GAAQ;GAAK;EAAI,CACpB;EACA,SAAS,QAEL,EAAI,YAAY;GACd,IAAM,IAAI,MAAM,EAAO,OAAO,OAAO,EAAK,GAAG,EAAE,OAAO,SAAS,CAAC;GAEhE,OADA,EAAW,IAAI,GACR;EACT,CAAC,GACH;GAAC;GAAQ;GAAK;EAAI,CACpB;EACA,OAAO,GAAa,GAA8B,MAAkB,QAAU,EAAO,OAAO,MAAM,EAAK,GAAG,GAAI,CAAK,CAAC,GAAG;GAAC;GAAQ;GAAK;EAAI,CAAC;EAC1I,OAAO,GAAa,GAAsB,MAAuB,QAAU,EAAO,OAAO,MAAM,EAAK,GAAG,GAAI,CAAK,CAAC,GAAG;GAAC;GAAQ;GAAK;EAAI,CAAC;EACvI,OAAO,GACJ,MACC,EAAI,YAAY;GACd,IAAM,IAAS,MAAM,EAAO,OAAO,MAAM,EAAK,GAAG,CAAI;GAErD,OADA,EAAU,EAAO,IAAI,GACd;EACT,CAAC,GACH;GAAC;GAAQ;GAAK;EAAI,CACpB;EACA,aAAa,GACV,GAAsB,GAAgC,IAAqC,CAAC,MAC3F,EAAI,YAAY;GACd,IAAM,IAAO,GAAO;GACpB,IAAI,CAAC,GAAM,MAAU,MAAM,qCAAqC;GAChE,IAAM,IAAI,MAAM,EAAO,OAAO,YAAY;IAAE;IAAM,IAAI,EAAK;GAAE,GAAG,GAAM,GAAM,CAAC;GAE7E,IAAI,OAAO,IAAI,mBAAoB,cAAc,CAAC,EAAE,MAAM;IACxD,IAAM,IAAM,IAAI,gBAAgB,CAAI;IAEpC,AADA,EAAM,QAAQ,KAAK,CAAG,GACtB,GAAa,OAAO;KAAE,GAAG;MAAI,IAAO;IAAI,EAAE;GAC5C;GACA,OAAO;EACT,CAAC,GACH;GAAC;GAAQ;GAAK;GAAM,GAAO;EAAI,CACjC;EACA,UAAU;GAAE,OAAO,GAAO,OAAO,QAAQ,EAAS,QAAQ,KAAA;GAAW,QAAQ,GAAO,QAAQ,QAAQ,EAAS,SAAS,KAAA;EAAU;CAClI;AACF;AAGA,SAAgB,EAAY,IAAoE,CAAC,GAAG,IAAmC,CAAC,GAAqB;CAC3J,IAAM,EAAE,WAAQ,UAAO,UAAO,WAAQ,gBAAa,EAAgB,EAAE,MAAM,GACrE,EAAE,aAAU,GAAG,MAAS,GACxB,IAAQ;EAAE;EAAU,GAAG;CAAK;CAClC,OAAO,EAAQ,GAAO,MAAW,OAAO,OAAO,EAAM,eAAe,GAAO,GAAO,CAAQ,GAAG,EAAE,MAAM,cAAc,GAAG,OAAO,GAAQ,MAAW;EAC9I,IAAM,IAAO,MAAM,EAAO,OAAO,KAAK;GAAE,GAAG;GAAe;GAA8B,OAAO;EAAS,GAAG,CAAM;EACjH,OAAO;GAAE,OAAO,EAAK;GAAQ,MAAM,EAAK,eAAe;EAAK;CAC9D,CAAC;AACH;AAKA,IAAa,KAAY,MAA0B;CAAC,EAAE,YAAY;CAAI,EAAE,cAAc;CAAI,EAAE,cAAc;CAAI,EAAE,QAAQ;AAAE,CAAC,CAAC,KAAK,GAAG;AAYpI,SAAgB,EAAgB,GAA+B,IAAsD,CAAC,GAAoB;CACxI,IAAM,EAAE,WAAQ,UAAO,aAAU,EAAgB,EAAE,MAAM,GACnD,IAAO,EAAE,YAAY,IACrB,IAAO,EAAgB,GAAO,IAAK,EAAM,iBAAiB,GAAO,GAAI,CAAI,IAAI,MAAM;EAAE,MAAM;EAAiB,IAAI,KAAM,KAAA;CAAU,GAAG,OAAO,GAAQ,MAAW;EACjK,IAAM,IAAO,MAAM,EAAO,OAAO,QAAQ,GAAK;GAAU;GAA8B,OAAO;EAAK,GAAG,CAAM;EAC3G,OAAO;GAAE,OAAO,EAAK,QAAQ,KAAK,OAAO;IAAE,GAAG;IAAG,IAAI,EAAS,CAAC;GAAE,EAAE;GAAG,MAAM,EAAK,eAAe;EAAK;CACvG,CAAC,GACK,IAAU,EAAW,GACrB,CAAC,GAAU,KAAe,EAAS,CAAC,GACpC,IAAQ,EACZ,OAAO,MAAoD;EACzD,IAAI,CAAC,GAAI,MAAU,MAAM,yBAAyB;EAClD,GAAa,MAAM,IAAI,CAAC;EACxB,IAAI;GACF,QAAQ,MAAM,QAAc,EAAG,CAAE,CAAC,EAAA,CAAG;EACvC,UAAU;GACR,GAAa,MAAM,IAAI,CAAC;EAC1B;CACF,GACA,CAAC,GAAI,CAAO,CACd;CACA,OAAO;EACL,GAAG;EACH,QAAQ,IAAW;EACnB,KAAK,GAAa,MAAqB,GAAO,MAAM,EAAO,OAAO,WAAW,GAAG,CAAE,CAAC,GAAG,CAAC,GAAQ,CAAK,CAAC;EACrG,QAAQ,GAAa,MAAqB,GAAO,MAAM,EAAO,OAAO,cAAc,GAAG,CAAE,CAAC,GAAG,CAAC,GAAQ,CAAK,CAAC;EAC3G,SAAS,GAAa,MAAoB,GAAO,MAAM,EAAO,OAAO,QAAQ,GAAG,CAAE,CAAC,GAAG,CAAC,GAAQ,CAAK,CAAC;CACvG;AACF;AAeA,IAAM,IAA+B,CAAC;AAEtC,SAAgB,EAAiB,GAAsC,IAAsD,CAAC,GAAqB;CACjJ,IAAM,EAAE,WAAQ,UAAO,UAAO,UAAU,MAAa,EAAgB,EAAE,MAAM,GACvE,IAAW,EAAE,YAAY,GACzB,IAAM,IAAY,EAAM,qBAAqB,GAAO,GAAW,CAAQ,IAAI,MAC3E,IAAI,EAAgC,GAAO,GAAK;EAAE,MAAM;EAAqB,IAAI,KAAa,KAAA;CAAU,IAAI,MAAM,EAAO,OAAO,UAAU,GAAY,GAAU,CAAC,CAAC,GAClK,IAAU,EAAW,GACrB,CAAC,GAAU,KAAe,EAAS,CAAC,GACpC,IAAM,EACV,OAAO,GAA2B,MAAqB;EACrD,IAAI,CAAC,GAAW,MAAU,MAAM,2BAA2B;EAC3D,GAAa,MAAM,IAAI,CAAC;EACxB,IAAI;GACF,IAAM,IAAM,MAAM,QAAc,EAAO,OAAO,aAAa,GAAW;IAAE;IAAO;GAAY,GAAG,CAAQ,CAAC;GAEvG,OADI,KAAK,EAAM,IAAI,GAAK,CAAG,GACpB;EACT,UAAU;GACR,GAAa,MAAM,IAAI,CAAC;EAC1B;CACF,GACA;EAAC;EAAQ;EAAW;EAAU;EAAS;EAAO;CAAG,CACnD,GACM,IAAS,QAAc,IAAI,KAAK,EAAE,MAAM,UAAU,CAAC,EAAA,CAAG,KAAK,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC;CAG5F,OAAO;EACL,aAFkB,QAAc,EAAE,MAAM,eAAe,GAAgB,CAAC,EAAE,IAAI,CAE9E;EACA;EACA,SAAS,CAAC,CAAC,MAAQ,EAAE,WAAW,CAAC,EAAE;EACnC,OAAO,EAAE;EACT,QAAQ,IAAW;EACnB,QAAQ,EAAE;EACV;CACF;AACF"}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { useContentKit, useContentKitClient, useContentURLs, useErrorReporter, u
|
|
|
3
3
|
export { useMediaRead, type MediaReadOptions, type ReadWindow, type UseMediaRead } from './read.js';
|
|
4
4
|
export { nameIn, thumbnailOf, useMediaFolder, type FolderGroup, type MediaFolderOptions, type UseMediaFolder } from './folder.js';
|
|
5
5
|
export { useEditorCrop, type EditorCropOptions, type EditorCropState, type UseEditorCrop } from './crop.js';
|
|
6
|
-
export { usePresets, usePublicImage, type PublicImageOptions, type UsePresets, type UsePublicImage } from './public.js';
|
|
6
|
+
export { useAvatar, usePresets, usePublicImage, type PublicImageOptions, type UsePresets, type UsePublicImage } from './public.js';
|
|
7
7
|
export { useCanonicalContent, type CanonicalContent, type CanonicalContentOptions } from './canonical.js';
|
|
8
8
|
export { useNearViewport, type NearViewportOptions } from './viewport.js';
|
|
9
9
|
export { useAdminLocation, useFlowClient, useStaffAccess, type AdminLocation, type AdminNavigation, type AdminQuery, type ContentKitAuth, type FlowClientOptions, type StaffAccess, type StaffArea, } from './admin.js';
|
|
@@ -12,7 +12,9 @@ export { useContentConfig, type UseContentConfig } from './config.js';
|
|
|
12
12
|
export { useFavorite, useReaction, type UseFavorite, type UseReaction } from './engagement.js';
|
|
13
13
|
export { usePoll, usePollEditor, usePolls, withVote, type PollFilter, type PollImages, type UsePoll, type UsePollEditor } from './polls.js';
|
|
14
14
|
export { useContent, useManagedContents, type ManageFilter, type UseContent } from './contents.js';
|
|
15
|
-
export {
|
|
15
|
+
export { useActivity, useMyStats, useTopContents, type UseStats } from './stats.js';
|
|
16
|
+
export { defaultVersion, findVersion, liveVersions, useContentDetail, type ContentDetailOptions, type UseContentDetail } from './detail.js';
|
|
17
|
+
export { useVersionParam } from './location.js';
|
|
16
18
|
export { memberId, useContentGroups, useGroup, useGroupMembers, useGroups, useKinds, useMyGroups, type GroupFilter, type GroupSort, type Member, type UseContentGroups, type UseGroup, type UseGroupMembers, type UseKinds, } from './groups.js';
|
|
17
19
|
export { beaconsFor, useBeacons, useContentFilters, useContents, useExposure, useHistory, useMyFavorites, useMyReactions, useRecentSearches, useRecommendations, useReleaseDates, useSearch, useSimilar, useTypeahead, useViewSignal, type ExposureOptions, type MyInteraction, type ReleaseDate, type UseContentFilters, type UseExposure, type UseHistory, type UseRail, type UseRecentSearches, type UseTypeahead, } from './discovery.js';
|
|
18
20
|
export { nextRelease, useReaderPlan, useSegmentPages, useVersionDownload, type ReaderPage, type UseReaderPlan, type UseSegmentPages, type UseVersionDownload, } from './versions.js';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Dispatched after a history write, so location readers look again. */
|
|
2
|
+
export declare const MOVED = "ckui:navigate";
|
|
3
|
+
export declare const subscribeHistory: (cb: () => void) => () => void;
|
|
4
|
+
/**
|
|
5
|
+
* The page's ?v= version code (null when it names none) and a setter that
|
|
6
|
+
* writes it, null removing it: through the provider's navigate (a replace),
|
|
7
|
+
* else the browser history. location overrides window.location.
|
|
8
|
+
*/
|
|
9
|
+
export declare function useVersionParam(o?: {
|
|
10
|
+
location?: string;
|
|
11
|
+
}): [string | null, (code: string | null) => void];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ContentKitContext as e } from "./context.js";
|
|
2
|
+
import { versionParam as t, withVersion as n } from "../urls.js";
|
|
3
|
+
import { useCallback as r, useContext as i, useSyncExternalStore as a } from "react";
|
|
4
|
+
//#region src/react/location.ts
|
|
5
|
+
var o = "ckui:navigate", s = (e) => (addEventListener("popstate", e), addEventListener(o, e), () => {
|
|
6
|
+
removeEventListener("popstate", e), removeEventListener(o, e);
|
|
7
|
+
}), c = () => location.pathname + location.search + location.hash;
|
|
8
|
+
function l(l = {}) {
|
|
9
|
+
let u = i(e)?.navigate, d = a(s, c, () => ""), f = l.location ?? d, p = r((e) => {
|
|
10
|
+
let t = n(l.location ?? c(), e);
|
|
11
|
+
u ? u(t, { replace: !0 }) : history.replaceState(history.state, "", t), dispatchEvent(new Event(o));
|
|
12
|
+
}, [u, l.location]);
|
|
13
|
+
return [t(f), p];
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { o as MOVED, s as subscribeHistory, l as useVersionParam };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=location.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"location.js","names":[],"sources":["../../src/react/location.ts"],"sourcesContent":["import { useCallback, useContext, useSyncExternalStore } from \"react\";\nimport { versionParam, withVersion } from \"../urls/index.js\";\nimport { ContentKitContext } from \"./context.js\";\n\n/** Dispatched after a history write, so location readers look again. */\nexport const MOVED = \"ckui:navigate\";\n\nexport const subscribeHistory = (cb: () => void) => {\n addEventListener(\"popstate\", cb);\n addEventListener(MOVED, cb);\n return () => {\n removeEventListener(\"popstate\", cb);\n removeEventListener(MOVED, cb);\n };\n};\n\nconst here = () => location.pathname + location.search + location.hash;\n\n/**\n * The page's ?v= version code (null when it names none) and a setter that\n * writes it, null removing it: through the provider's navigate (a replace),\n * else the browser history. location overrides window.location.\n */\nexport function useVersionParam(o: { location?: string } = {}): [string | null, (code: string | null) => void] {\n const navigate = useContext(ContentKitContext)?.navigate;\n const current = useSyncExternalStore(subscribeHistory, here, () => \"\");\n const at = o.location ?? current;\n const set = useCallback(\n (code: string | null) => {\n const to = withVersion(o.location ?? here(), code);\n if (navigate) navigate(to, { replace: true });\n else history.replaceState(history.state, \"\", to);\n dispatchEvent(new Event(MOVED));\n },\n [navigate, o.location],\n );\n return [versionParam(at), set];\n}\n"],"mappings":";;;;AAKA,IAAa,IAAQ,iBAER,KAAoB,OAC/B,iBAAiB,YAAY,CAAE,GAC/B,iBAAiB,GAAO,CAAE,SACb;CAEX,AADA,oBAAoB,YAAY,CAAE,GAClC,oBAAoB,GAAO,CAAE;AAC/B,IAGI,UAAa,SAAS,WAAW,SAAS,SAAS,SAAS;AAOlE,SAAgB,EAAgB,IAA2B,CAAC,GAAmD;CAC7G,IAAM,IAAW,EAAW,CAAiB,CAAC,EAAE,UAC1C,IAAU,EAAqB,GAAkB,SAAY,EAAE,GAC/D,IAAK,EAAE,YAAY,GACnB,IAAM,GACT,MAAwB;EACvB,IAAM,IAAK,EAAY,EAAE,YAAY,EAAK,GAAG,CAAI;EAGjD,AAFI,IAAU,EAAS,GAAI,EAAE,SAAS,GAAK,CAAC,IACvC,QAAQ,aAAa,QAAQ,OAAO,IAAI,CAAE,GAC/C,cAAc,IAAI,MAAM,CAAK,CAAC;CAChC,GACA,CAAC,GAAU,EAAE,QAAQ,CACvB;CACA,OAAO,CAAC,EAAa,CAAE,GAAG,CAAG;AAC/B"}
|
package/dist/react/public.d.ts
CHANGED
|
@@ -38,3 +38,11 @@ export interface UsePublicImage {
|
|
|
38
38
|
* commits through the same client.
|
|
39
39
|
*/
|
|
40
40
|
export declare function usePublicImage(item: RefBody, preset: string, o?: PublicImageOptions): UsePublicImage;
|
|
41
|
+
/**
|
|
42
|
+
* A person's avatar (the content model's `avatars`, a Stable preset): URLs
|
|
43
|
+
* built from their id on this site's media host, showing their image or
|
|
44
|
+
* the default. null while the model loads or when the site shows none.
|
|
45
|
+
*/
|
|
46
|
+
export declare function useAvatar(userId: string | null | undefined, o?: {
|
|
47
|
+
client?: ContentKitClient;
|
|
48
|
+
}): PublicPreset | null;
|
package/dist/react/public.js
CHANGED
|
@@ -1,38 +1,39 @@
|
|
|
1
|
-
import { defaultImage as e, isPattern as t, ruleDir as n } from "../client/media/rules.js";
|
|
2
|
-
import { useOptionalContentKitClient as
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { defaultImage as e, isPattern as t, ruleDir as n, stableImage as r } from "../client/media/rules.js";
|
|
2
|
+
import { useOptionalContentKitClient as i } from "./context.js";
|
|
3
|
+
import { useKinds as a } from "./groups.js";
|
|
4
|
+
import { useMediaRead as o } from "./read.js";
|
|
5
|
+
import { useEffect as s, useMemo as c, useState as l } from "react";
|
|
5
6
|
//#region src/react/public.ts
|
|
6
|
-
var
|
|
7
|
-
function
|
|
8
|
-
let t =
|
|
9
|
-
return
|
|
10
|
-
if (!t ||
|
|
11
|
-
let e = !0, r =
|
|
12
|
-
return r || (r = t.media.presets().then((e) => void
|
|
7
|
+
var u = /* @__PURE__ */ new WeakMap(), d = /* @__PURE__ */ new WeakMap();
|
|
8
|
+
function f(e = {}) {
|
|
9
|
+
let t = i(e.client), [, n] = l(0), r = t ? u.get(t) : void 0;
|
|
10
|
+
return s(() => {
|
|
11
|
+
if (!t || u.has(t)) return;
|
|
12
|
+
let e = !0, r = d.get(t);
|
|
13
|
+
return r || (r = t.media.presets().then((e) => void u.set(t, { presets: e }), (e) => void u.set(t, {
|
|
13
14
|
presets: [],
|
|
14
15
|
error: e
|
|
15
|
-
})).finally(() =>
|
|
16
|
+
})).finally(() => d.delete(t)), d.set(t, r)), r.then(() => e && n((e) => e + 1)), () => {
|
|
16
17
|
e = !1;
|
|
17
18
|
};
|
|
18
19
|
}, [t]), {
|
|
19
|
-
presets:
|
|
20
|
-
loaded: !!
|
|
21
|
-
error:
|
|
20
|
+
presets: r?.presets ?? [],
|
|
21
|
+
loaded: !!r || !t,
|
|
22
|
+
error: r?.error
|
|
22
23
|
};
|
|
23
24
|
}
|
|
24
|
-
function
|
|
25
|
-
let { kind: l, id:
|
|
25
|
+
function p(r, a, s = {}) {
|
|
26
|
+
let { kind: l, id: u } = r, d = i(s.client), { presets: p, loaded: m } = f({ client: d }), h = p.find((e) => e.kind === l && e.name === a), g = s.image !== void 0, _ = h ? t(h.from) ? n(h.from) : h.from : void 0, v = o(r, {
|
|
26
27
|
prefix: _,
|
|
27
|
-
read: g || !m ? null :
|
|
28
|
-
client:
|
|
28
|
+
read: g || !m ? null : s.read,
|
|
29
|
+
client: d
|
|
29
30
|
});
|
|
30
31
|
return {
|
|
31
|
-
...
|
|
32
|
-
let t = g ?
|
|
32
|
+
...c(() => {
|
|
33
|
+
let t = g ? s.image : v.read?.public?.find((e) => e.preset === a);
|
|
33
34
|
if (t && t.renditions.length) return {
|
|
34
35
|
image: {
|
|
35
|
-
preset:
|
|
36
|
+
preset: a,
|
|
36
37
|
renditions: t.renditions,
|
|
37
38
|
aspect: "aspect" in t && t.aspect || h?.aspect
|
|
38
39
|
},
|
|
@@ -42,7 +43,7 @@ function d(a, s, c = {}) {
|
|
|
42
43
|
image: null,
|
|
43
44
|
isDefault: !1
|
|
44
45
|
};
|
|
45
|
-
let n = h && e(h,
|
|
46
|
+
let n = h && e(h, u);
|
|
46
47
|
return n ? {
|
|
47
48
|
image: n,
|
|
48
49
|
isDefault: !0
|
|
@@ -52,11 +53,11 @@ function d(a, s, c = {}) {
|
|
|
52
53
|
};
|
|
53
54
|
}, [
|
|
54
55
|
g,
|
|
55
|
-
|
|
56
|
+
s.image,
|
|
56
57
|
v.read,
|
|
57
|
-
|
|
58
|
+
a,
|
|
58
59
|
h,
|
|
59
|
-
|
|
60
|
+
u
|
|
60
61
|
]),
|
|
61
62
|
rule: h,
|
|
62
63
|
loading: !m || v.loading,
|
|
@@ -64,7 +65,11 @@ function d(a, s, c = {}) {
|
|
|
64
65
|
reload: v.reload
|
|
65
66
|
};
|
|
66
67
|
}
|
|
68
|
+
function m(e, t = {}) {
|
|
69
|
+
let { kinds: n } = a(t), i = n?.avatars;
|
|
70
|
+
return c(() => i && e ? r(i, e) : null, [i, e]);
|
|
71
|
+
}
|
|
67
72
|
//#endregion
|
|
68
|
-
export {
|
|
73
|
+
export { m as useAvatar, f as usePresets, p as usePublicImage };
|
|
69
74
|
|
|
70
75
|
//# sourceMappingURL=public.js.map
|
package/dist/react/public.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public.js","names":[],"sources":["../../src/react/public.ts"],"sourcesContent":["import { useEffect, useMemo, useState } 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 { PresetRule } from \"../client/generated/wire.js\";\nimport { defaultImage, isPattern, ruleDir } from \"../client/media/rules.js\";\nimport type { PublicPreset } from \"../client/public.js\";\nimport { useOptionalContentKitClient } from \"./context.js\";\nimport { useMediaRead } from \"./read.js\";\n\nexport interface UsePresets {\n /** Every kind's public presets; empty until they load (or when the server has none). */\n presets: PresetRule[];\n loaded: boolean;\n error?: ContentKitError;\n}\n\nconst settled = new WeakMap<ContentKitClient, { presets: PresetRule[]; error?: ContentKitError }>();\nconst inflight = new WeakMap<ContentKitClient, Promise<void>>();\n\n/** The public presets (`GET /media/presets`), fetched once per client. */\nexport function usePresets(o: { client?: ContentKitClient | null } = {}): UsePresets {\n const client = useOptionalContentKitClient(o.client);\n const [, rerender] = useState(0);\n const done = client ? settled.get(client) : undefined;\n useEffect(() => {\n if (!client || settled.has(client)) return;\n let live = true;\n let load = inflight.get(client);\n if (!load) {\n load = client.media.presets().then(\n (presets) => void settled.set(client, { presets }),\n (error: ContentKitError) => void settled.set(client, { presets: [], error }),\n ).finally(() => inflight.delete(client));\n inflight.set(client, load);\n }\n void load.then(() => live && rerender((n) => n + 1));\n return () => {\n live = false;\n };\n }, [client]);\n return { presets: done?.presets ?? [], loaded: !!done || !client, error: done?.error };\n}\n\nexport interface PublicImageOptions {\n /** The host's listing of the item's image (its API's): shown without a read. */\n image?: PublicPreset | null;\n /** A read of the item the host already has. */\n read?: ReadResult | null;\n /** Overrides the provider's client. */\n client?: ContentKitClient | null;\n}\n\nexport interface UsePublicImage {\n /** The item's current renditions; else the kind's default image (when the preset has one); null while unknown or when there is neither. */\n image: PublicPreset | null;\n /** The preset's rules (aspect, widths, min_width) once the presets load. */\n rule?: PresetRule;\n /** image is the kind's default, not the item's own. */\n isDefault: boolean;\n loading: boolean;\n error?: ContentKitError;\n reload: () => void;\n}\n\n/**\n * An item's public image for a preset (\"avatar\", \"cover\", a version's\n * \"poster\"): the exact renditions from a read's `public` (or the host's\n * listing), else the kind's default image, else none. Reads refresh after\n * commits through the same client.\n */\nexport function usePublicImage(item: RefBody, preset: string, o: PublicImageOptions = {}): UsePublicImage {\n const { kind, id } = item;\n const client = useOptionalContentKitClient(o.client);\n const { presets, loaded } = usePresets({ client });\n const rule = presets.find((p) => p.kind === kind && p.name === preset);\n const listed = o.image !== undefined;\n // The read waits for the presets, so it lists only the preset's upload.\n const prefix = rule ? (isPattern(rule.from) ? ruleDir(rule.from) : rule.from) : undefined;\n const r = useMediaRead(item, { prefix, read: listed || !loaded ? null : o.read, client });\n const image = useMemo((): { image: PublicPreset | null; isDefault: boolean } => {\n const own = listed ? o.image : r.read?.public?.find((p) => p.preset === preset);\n if (own && own.renditions.length) return { image: { preset, renditions: own.renditions, aspect: (\"aspect\" in own && own.aspect) || rule?.aspect }, isDefault: false };\n if (!listed && !r.read) return { image: null, isDefault: false };\n const def = rule && defaultImage(rule, id);\n return def ? { image: def, isDefault: true } : { image: null, isDefault: false };\n }, [listed, o.image, r.read, preset, rule, id]);\n return { ...image, rule, loading: !loaded || r.loading, error: r.error, reload: r.reload };\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"public.js","names":[],"sources":["../../src/react/public.ts"],"sourcesContent":["import { useEffect, useMemo, useState } 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 { PresetRule } from \"../client/generated/wire.js\";\nimport { defaultImage, isPattern, ruleDir, stableImage } from \"../client/media/rules.js\";\nimport type { PublicPreset } from \"../client/public.js\";\nimport { useOptionalContentKitClient } from \"./context.js\";\nimport { useKinds } from \"./groups.js\";\nimport { useMediaRead } from \"./read.js\";\n\nexport interface UsePresets {\n /** Every kind's public presets; empty until they load (or when the server has none). */\n presets: PresetRule[];\n loaded: boolean;\n error?: ContentKitError;\n}\n\nconst settled = new WeakMap<ContentKitClient, { presets: PresetRule[]; error?: ContentKitError }>();\nconst inflight = new WeakMap<ContentKitClient, Promise<void>>();\n\n/** The public presets (`GET /media/presets`), fetched once per client. */\nexport function usePresets(o: { client?: ContentKitClient | null } = {}): UsePresets {\n const client = useOptionalContentKitClient(o.client);\n const [, rerender] = useState(0);\n const done = client ? settled.get(client) : undefined;\n useEffect(() => {\n if (!client || settled.has(client)) return;\n let live = true;\n let load = inflight.get(client);\n if (!load) {\n load = client.media.presets().then(\n (presets) => void settled.set(client, { presets }),\n (error: ContentKitError) => void settled.set(client, { presets: [], error }),\n ).finally(() => inflight.delete(client));\n inflight.set(client, load);\n }\n void load.then(() => live && rerender((n) => n + 1));\n return () => {\n live = false;\n };\n }, [client]);\n return { presets: done?.presets ?? [], loaded: !!done || !client, error: done?.error };\n}\n\nexport interface PublicImageOptions {\n /** The host's listing of the item's image (its API's): shown without a read. */\n image?: PublicPreset | null;\n /** A read of the item the host already has. */\n read?: ReadResult | null;\n /** Overrides the provider's client. */\n client?: ContentKitClient | null;\n}\n\nexport interface UsePublicImage {\n /** The item's current renditions; else the kind's default image (when the preset has one); null while unknown or when there is neither. */\n image: PublicPreset | null;\n /** The preset's rules (aspect, widths, min_width) once the presets load. */\n rule?: PresetRule;\n /** image is the kind's default, not the item's own. */\n isDefault: boolean;\n loading: boolean;\n error?: ContentKitError;\n reload: () => void;\n}\n\n/**\n * An item's public image for a preset (\"avatar\", \"cover\", a version's\n * \"poster\"): the exact renditions from a read's `public` (or the host's\n * listing), else the kind's default image, else none. Reads refresh after\n * commits through the same client.\n */\nexport function usePublicImage(item: RefBody, preset: string, o: PublicImageOptions = {}): UsePublicImage {\n const { kind, id } = item;\n const client = useOptionalContentKitClient(o.client);\n const { presets, loaded } = usePresets({ client });\n const rule = presets.find((p) => p.kind === kind && p.name === preset);\n const listed = o.image !== undefined;\n // The read waits for the presets, so it lists only the preset's upload.\n const prefix = rule ? (isPattern(rule.from) ? ruleDir(rule.from) : rule.from) : undefined;\n const r = useMediaRead(item, { prefix, read: listed || !loaded ? null : o.read, client });\n const image = useMemo((): { image: PublicPreset | null; isDefault: boolean } => {\n const own = listed ? o.image : r.read?.public?.find((p) => p.preset === preset);\n if (own && own.renditions.length) return { image: { preset, renditions: own.renditions, aspect: (\"aspect\" in own && own.aspect) || rule?.aspect }, isDefault: false };\n if (!listed && !r.read) return { image: null, isDefault: false };\n const def = rule && defaultImage(rule, id);\n return def ? { image: def, isDefault: true } : { image: null, isDefault: false };\n }, [listed, o.image, r.read, preset, rule, id]);\n return { ...image, rule, loading: !loaded || r.loading, error: r.error, reload: r.reload };\n}\n\n/**\n * A person's avatar (the content model's `avatars`, a Stable preset): URLs\n * built from their id on this site's media host, showing their image or\n * the default. null while the model loads or when the site shows none.\n */\nexport function useAvatar(userId: string | null | undefined, o: { client?: ContentKitClient } = {}): PublicPreset | null {\n const { kinds } = useKinds(o);\n const rule = kinds?.avatars;\n return useMemo(() => (rule && userId ? stableImage(rule, userId) : null), [rule, userId]);\n}\n"],"mappings":";;;;;;AAkBA,IAAM,oBAAU,IAAI,QAA8E,GAC5F,oBAAW,IAAI,QAAyC;AAG9D,SAAgB,EAAW,IAA0C,CAAC,GAAe;CACnF,IAAM,IAAS,EAA4B,EAAE,MAAM,GAC7C,GAAG,KAAY,EAAS,CAAC,GACzB,IAAO,IAAS,EAAQ,IAAI,CAAM,IAAI,KAAA;CAiB5C,OAhBA,QAAgB;EACd,IAAI,CAAC,KAAU,EAAQ,IAAI,CAAM,GAAG;EACpC,IAAI,IAAO,IACP,IAAO,EAAS,IAAI,CAAM;EAS9B,OARK,MACH,IAAO,EAAO,MAAM,QAAQ,CAAC,CAAC,MAC3B,MAAY,KAAK,EAAQ,IAAI,GAAQ,EAAE,WAAQ,CAAC,IAChD,MAA2B,KAAK,EAAQ,IAAI,GAAQ;GAAE,SAAS,CAAC;GAAG;EAAM,CAAC,CAC7E,CAAC,CAAC,cAAc,EAAS,OAAO,CAAM,CAAC,GACvC,EAAS,IAAI,GAAQ,CAAI,IAE3B,EAAU,WAAW,KAAQ,GAAU,MAAM,IAAI,CAAC,CAAC,SACtC;GACX,IAAO;EACT;CACF,GAAG,CAAC,CAAM,CAAC,GACJ;EAAE,SAAS,GAAM,WAAW,CAAC;EAAG,QAAQ,CAAC,CAAC,KAAQ,CAAC;EAAQ,OAAO,GAAM;CAAM;AACvF;AA6BA,SAAgB,EAAe,GAAe,GAAgB,IAAwB,CAAC,GAAmB;CACxG,IAAM,EAAE,SAAM,UAAO,GACf,IAAS,EAA4B,EAAE,MAAM,GAC7C,EAAE,YAAS,cAAW,EAAW,EAAE,UAAO,CAAC,GAC3C,IAAO,EAAQ,MAAM,MAAM,EAAE,SAAS,KAAQ,EAAE,SAAS,CAAM,GAC/D,IAAS,EAAE,UAAU,KAAA,GAErB,IAAS,IAAQ,EAAU,EAAK,IAAI,IAAI,EAAQ,EAAK,IAAI,IAAI,EAAK,OAAQ,KAAA,GAC1E,IAAI,EAAa,GAAM;EAAE;EAAQ,MAAM,KAAU,CAAC,IAAS,OAAO,EAAE;EAAM;CAAO,CAAC;CAQxF,OAAO;EAAE,GAPK,QAAkE;GAC9E,IAAM,IAAM,IAAS,EAAE,QAAQ,EAAE,MAAM,QAAQ,MAAM,MAAM,EAAE,WAAW,CAAM;GAC9E,IAAI,KAAO,EAAI,WAAW,QAAQ,OAAO;IAAE,OAAO;KAAE;KAAQ,YAAY,EAAI;KAAY,QAAS,YAAY,KAAO,EAAI,UAAW,GAAM;IAAO;IAAG,WAAW;GAAM;GACpK,IAAI,CAAC,KAAU,CAAC,EAAE,MAAM,OAAO;IAAE,OAAO;IAAM,WAAW;GAAM;GAC/D,IAAM,IAAM,KAAQ,EAAa,GAAM,CAAE;GACzC,OAAO,IAAM;IAAE,OAAO;IAAK,WAAW;GAAK,IAAI;IAAE,OAAO;IAAM,WAAW;GAAM;EACjF,GAAG;GAAC;GAAQ,EAAE;GAAO,EAAE;GAAM;GAAQ;GAAM;EAAE,CACjC;EAAO;EAAM,SAAS,CAAC,KAAU,EAAE;EAAS,OAAO,EAAE;EAAO,QAAQ,EAAE;CAAO;AAC3F;AAOA,SAAgB,EAAU,GAAmC,IAAmC,CAAC,GAAwB;CACvH,IAAM,EAAE,aAAU,EAAS,CAAC,GACtB,IAAO,GAAO;CACpB,OAAO,QAAe,KAAQ,IAAS,EAAY,GAAM,CAAM,IAAI,MAAO,CAAC,GAAM,CAAM,CAAC;AAC1F"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ContentKitClient } from '../client/client.js';
|
|
2
|
+
import { ContentKitError } from '../client/errors.js';
|
|
3
|
+
import { Activity, MyStats, TopContents } from '../client/generated/wire.js';
|
|
4
|
+
import { ActivityQuery, TopQuery } from '../client/stats.js';
|
|
5
|
+
export interface UseStats<T> {
|
|
6
|
+
data: T | null;
|
|
7
|
+
loading: boolean;
|
|
8
|
+
error?: ContentKitError;
|
|
9
|
+
/** The site runs without the signal plane (not_configured). */
|
|
10
|
+
unavailable: boolean;
|
|
11
|
+
reload: () => void;
|
|
12
|
+
}
|
|
13
|
+
/** Staff: a kind's views per UTC day, newest first; null reads nothing. */
|
|
14
|
+
export declare function useActivity(q: ActivityQuery | null, o?: {
|
|
15
|
+
client?: ContentKitClient;
|
|
16
|
+
}): UseStats<Activity>;
|
|
17
|
+
/** Staff: a kind's content ranked by views or comments over a period; null reads nothing. */
|
|
18
|
+
export declare function useTopContents(q: TopQuery | null, o?: {
|
|
19
|
+
client?: ContentKitClient;
|
|
20
|
+
}): UseStats<TopContents>;
|
|
21
|
+
/** The signed-in reader's statistics; nothing signed out. */
|
|
22
|
+
export declare function useMyStats(o?: {
|
|
23
|
+
client?: ContentKitClient;
|
|
24
|
+
}): UseStats<MyStats>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { keyOf as e, useContentScope as t, useResource as n } from "./use-resource.js";
|
|
2
|
+
//#region src/react/stats.ts
|
|
3
|
+
function r(e, r, i) {
|
|
4
|
+
let { client: a, store: o } = t(i.client), s = n(o, e, { type: "stats" }, (e) => r(a, e)), c = s.error?.code === "not_configured";
|
|
5
|
+
return {
|
|
6
|
+
data: s.data ?? null,
|
|
7
|
+
loading: !!e && (s.loading || !s.loaded),
|
|
8
|
+
error: c ? void 0 : s.error,
|
|
9
|
+
unavailable: c,
|
|
10
|
+
reload: s.reload
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function i(n, i = {}) {
|
|
14
|
+
let { scope: a } = t(i.client);
|
|
15
|
+
return r(n ? e("stats", a, "activity", n.kind, n.days) : null, (e, t) => e.stats.activity(n, t), i);
|
|
16
|
+
}
|
|
17
|
+
function a(n, i = {}) {
|
|
18
|
+
let { scope: a } = t(i.client);
|
|
19
|
+
return r(n ? e("stats", a, "top", n) : null, (e, t) => e.stats.top(n, t), i);
|
|
20
|
+
}
|
|
21
|
+
function o(n = {}) {
|
|
22
|
+
let { scope: i, viewer: a } = t(n.client);
|
|
23
|
+
return r(a === null ? null : e("stats", i, "me"), (e, t) => e.stats.mine(t), n);
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { i as useActivity, o as useMyStats, a as useTopContents };
|
|
27
|
+
|
|
28
|
+
//# sourceMappingURL=stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats.js","names":[],"sources":["../../src/react/stats.ts"],"sourcesContent":["import type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport type { Activity, MyStats, TopContents } from \"../client/generated/wire.js\";\nimport type { ActivityQuery, TopQuery } from \"../client/stats.js\";\nimport { keyOf, useContentScope, useResource } from \"./use-resource.js\";\n\nexport interface UseStats<T> {\n data: T | null;\n loading: boolean;\n error?: ContentKitError;\n /** The site runs without the signal plane (not_configured). */\n unavailable: boolean;\n reload: () => void;\n}\n\nfunction useStats<T>(key: string | null, fetch: (c: ContentKitClient, signal: AbortSignal) => Promise<T>, o: { client?: ContentKitClient }): UseStats<T> {\n const { client, store } = useContentScope(o.client);\n const r = useResource<T>(store, key, { type: \"stats\" }, (signal) => fetch(client, signal));\n const unavailable = r.error?.code === \"not_configured\";\n return { data: r.data ?? null, loading: !!key && (r.loading || !r.loaded), error: unavailable ? undefined : r.error, unavailable, reload: r.reload };\n}\n\n/** Staff: a kind's views per UTC day, newest first; null reads nothing. */\nexport function useActivity(q: ActivityQuery | null, o: { client?: ContentKitClient } = {}): UseStats<Activity> {\n const { scope } = useContentScope(o.client);\n return useStats(q ? keyOf(\"stats\", scope, \"activity\", q.kind, q.days) : null, (c, s) => c.stats.activity(q!, s), o);\n}\n\n/** Staff: a kind's content ranked by views or comments over a period; null reads nothing. */\nexport function useTopContents(q: TopQuery | null, o: { client?: ContentKitClient } = {}): UseStats<TopContents> {\n const { scope } = useContentScope(o.client);\n return useStats(q ? keyOf(\"stats\", scope, \"top\", q) : null, (c, s) => c.stats.top(q!, s), o);\n}\n\n/** The signed-in reader's statistics; nothing signed out. */\nexport function useMyStats(o: { client?: ContentKitClient } = {}): UseStats<MyStats> {\n const { scope, viewer } = useContentScope(o.client);\n return useStats(viewer === null ? null : keyOf(\"stats\", scope, \"me\"), (c, s) => c.stats.mine(s), o);\n}\n"],"mappings":";;AAeA,SAAS,EAAY,GAAoB,GAAiE,GAA+C;CACvJ,IAAM,EAAE,WAAQ,aAAU,EAAgB,EAAE,MAAM,GAC5C,IAAI,EAAe,GAAO,GAAK,EAAE,MAAM,QAAQ,IAAI,MAAW,EAAM,GAAQ,CAAM,CAAC,GACnF,IAAc,EAAE,OAAO,SAAS;CACtC,OAAO;EAAE,MAAM,EAAE,QAAQ;EAAM,SAAS,CAAC,CAAC,MAAQ,EAAE,WAAW,CAAC,EAAE;EAAS,OAAO,IAAc,KAAA,IAAY,EAAE;EAAO;EAAa,QAAQ,EAAE;CAAO;AACrJ;AAGA,SAAgB,EAAY,GAAyB,IAAmC,CAAC,GAAuB;CAC9G,IAAM,EAAE,aAAU,EAAgB,EAAE,MAAM;CAC1C,OAAO,EAAS,IAAI,EAAM,SAAS,GAAO,YAAY,EAAE,MAAM,EAAE,IAAI,IAAI,OAAO,GAAG,MAAM,EAAE,MAAM,SAAS,GAAI,CAAC,GAAG,CAAC;AACpH;AAGA,SAAgB,EAAe,GAAoB,IAAmC,CAAC,GAA0B;CAC/G,IAAM,EAAE,aAAU,EAAgB,EAAE,MAAM;CAC1C,OAAO,EAAS,IAAI,EAAM,SAAS,GAAO,OAAO,CAAC,IAAI,OAAO,GAAG,MAAM,EAAE,MAAM,IAAI,GAAI,CAAC,GAAG,CAAC;AAC7F;AAGA,SAAgB,EAAW,IAAmC,CAAC,GAAsB;CACnF,IAAM,EAAE,UAAO,cAAW,EAAgB,EAAE,MAAM;CAClD,OAAO,EAAS,MAAW,OAAO,OAAO,EAAM,SAAS,GAAO,IAAI,IAAI,GAAG,MAAM,EAAE,MAAM,KAAK,CAAC,GAAG,CAAC;AACpG"}
|
|
@@ -44,10 +44,11 @@ function f(e, t, r, a, l = {}) {
|
|
|
44
44
|
reload: y
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
|
-
|
|
47
|
+
var p = [];
|
|
48
|
+
function m(e, t, n, r) {
|
|
48
49
|
let a = f(e, t, n, (e) => r(void 0, e), { more: (e, t) => r(e, t) }), o = i(() => t ? e.loadMore(t) : Promise.resolve(), [e, t]);
|
|
49
50
|
return {
|
|
50
|
-
items: a.data?.items ??
|
|
51
|
+
items: a.data?.items ?? p,
|
|
51
52
|
loading: a.loading || !!t && !a.loaded,
|
|
52
53
|
loadingMore: !!a.loadingMore,
|
|
53
54
|
error: a.error,
|
|
@@ -56,7 +57,7 @@ function p(e, t, n, r) {
|
|
|
56
57
|
reload: a.reload
|
|
57
58
|
};
|
|
58
59
|
}
|
|
59
|
-
function
|
|
60
|
+
function h(e, t, n = !1) {
|
|
60
61
|
return async (r, i) => {
|
|
61
62
|
let a = typeof r == "number" ? r : 0, o = await t({
|
|
62
63
|
limit: e,
|
|
@@ -68,8 +69,8 @@ function m(e, t, n = !1) {
|
|
|
68
69
|
};
|
|
69
70
|
};
|
|
70
71
|
}
|
|
71
|
-
var
|
|
72
|
-
function
|
|
72
|
+
var g = (...e) => JSON.stringify(e);
|
|
73
|
+
function _(e, t, n, r, i, a) {
|
|
73
74
|
let o = (Math.max(1, r) - 1) * i, s = f(e, t === null ? null : `${t}#${o}`, n, async (e) => {
|
|
74
75
|
let t = await a({
|
|
75
76
|
limit: i + 1,
|
|
@@ -81,7 +82,7 @@ function g(e, t, n, r, i, a) {
|
|
|
81
82
|
};
|
|
82
83
|
});
|
|
83
84
|
return {
|
|
84
|
-
items: s.data?.items ??
|
|
85
|
+
items: s.data?.items ?? p,
|
|
85
86
|
loading: s.loading || !!t && !s.loaded,
|
|
86
87
|
error: s.error,
|
|
87
88
|
hasNext: s.data ? s.data.next !== null : !1,
|
|
@@ -89,6 +90,6 @@ function g(e, t, n, r, i, a) {
|
|
|
89
90
|
};
|
|
90
91
|
}
|
|
91
92
|
//#endregion
|
|
92
|
-
export {
|
|
93
|
+
export { g as keyOf, h as offsetPages, l as useContentScope, m as useList, _ as usePaged, f as useResource };
|
|
93
94
|
|
|
94
95
|
//# sourceMappingURL=use-resource.js.map
|