@openrails/contentkit-ui 0.70.0 → 0.71.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/dist/client/content/comments.d.ts +2 -2
- package/dist/client/content/comments.js.map +1 -1
- package/dist/client/content/polls.d.ts +2 -2
- package/dist/client/content/polls.js.map +1 -1
- package/dist/client/content/posts.d.ts +4 -4
- package/dist/client/content/posts.js.map +1 -1
- package/dist/client/generated/error-codes.js +2 -2
- package/dist/client/generated/error-codes.js.map +1 -1
- package/dist/client/generated/routes.d.ts +118 -118
- package/dist/client/generated/routes.js +205 -117
- package/dist/client/generated/routes.js.map +1 -1
- package/dist/client/generated/wire.d.ts +1 -0
- package/dist/client/generated/wire.js.map +1 -1
- package/dist/client/taxonomy.d.ts +1 -1
- package/dist/client/taxonomy.js.map +1 -1
- package/dist/components/social/poll-editor.d.ts +1 -1
- package/dist/components/social/poll-editor.js.map +1 -1
- package/dist/react/comments.d.ts +1 -1
- package/dist/react/comments.js.map +1 -1
- package/dist/react/polls.d.ts +2 -2
- package/dist/react/polls.js.map +1 -1
- package/dist/react/posts.d.ts +3 -3
- package/dist/react/posts.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comments.js","names":[],"sources":["../../src/react/comments.ts"],"sourcesContent":["import { useCallback } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { BanScope, Decision, HeldKind, Reaction, Sort } from \"../client/content/types.js\";\nimport type { AdminComment, BanInput, Comment, CommentBan, CommentStanding, FeedItem, HeldItem, ReactionCounts, RefBody } from \"../client/generated/wire.js\";\nimport type { Page, ResourceStore } from \"./resources.js\";\nimport { keyOf, offsetPages, useContentScope, useList, useResource, type UseList } from \"./use-resource.js\";\n\n/** Counts after the caller's reaction changes from counts.mine to value. */\nexport function withReaction(counts: ReactionCounts, value: Reaction): ReactionCounts {\n const likes = counts.likes - (counts.mine === 1 ? 1 : 0) + (value === 1 ? 1 : 0);\n const dislikes = counts.dislikes - (counts.mine === -1 ? 1 : 0) + (value === -1 ? 1 : 0);\n return { likes, dislikes, mine: value };\n}\n\nconst commentLists = [\"comments\", \"replies\", \"latest\", \"admin-comments\"];\nconst tokens = new WeakMap<ResourceStore, Map<string, number>>();\n\n/** Sets the caller's reaction to a comment in every list showing it at once; rolls back on failure unless a newer one started. */\nasync function reactToComment(client: ContentKitClient, store: ResourceStore, scope: string, c: Pick<Comment, \"id\" | \"likes\" | \"dislikes\" | \"mine\">, value: Reaction) {\n let t = tokens.get(store);\n if (!t) tokens.set(store, (t = new Map()));\n const token = (t.get(c.id) ?? 0) + 1;\n t.set(c.id, token);\n const reads = new Set<number>();\n const put = (counts: ReactionCounts, rollback = false) =>\n store.patch<Page<Comment>>(\n (tag) => commentLists.includes(tag.type) && tag.readScope === scope,\n (page, readVersion) => {\n if (rollback && !reads.has(readVersion)) return page;\n reads.add(readVersion);\n return { ...page, items: page.items.map((x) => (x.id === c.id ? { ...x, ...counts } : x)) };\n },\n );\n put(withReaction({ likes: c.likes, dislikes: c.dislikes, mine: c.mine }, value));\n try {\n await client.comments.react(c.id, value);\n } catch (e) {\n if (t.get(c.id) === token) put({ likes: c.likes, dislikes: c.dislikes, mine: c.mine }, true);\n throw e;\n }\n}\n\nexport interface UseCommentsOptions {\n sort?: Sort;\n /** Top-level comments per page. Default 20. */\n pageSize?: number;\n client?: ContentKitClient;\n}\n\nexport interface UseComments extends UseList<Comment> {\n /** Comments on the item, or replies to a top-level comment (replyTo); anonymous callers give anonName. */\n post: (body: string, o?: { replyTo?: string; anonName?: string }) => Promise<Comment>;\n edit: (id: string, body: string) => Promise<Comment>;\n /** Leaves a tombstone. */\n remove: (id: string) => Promise<void>;\n /** Sets the caller's reaction at once, rolled back if the server refuses it. */\n react: (comment: Comment, value: Reaction) => Promise<void>;\n /** Restores a deleted comment (moderators). */\n restore: (id: string) => Promise<void>;\n}\n\n/** An item's top-level comments, a page at a time, with every write; new comments and replies land in place. */\nexport function useComments(ref: RefBody, o: UseCommentsOptions = {}): UseComments {\n const { client, store, scope } = useContentScope(o.client);\n const size = o.pageSize ?? 20;\n const sort = o.sort ?? \"newest\";\n const key = keyOf(\"comments\", scope, ref.kind, ref.id, sort, size);\n const list = useList(store, key, { type: \"comments\", ref: { kind: ref.kind, id: ref.id } }, offsetPages(size, (q, signal) => client.comments.list(ref, { ...q, sort }, signal)));\n const { kind, id } = ref;\n const post = useCallback<UseComments[\"post\"]>(\n (body, p = {}) => client.comments.create({ kind, id }, { body, reply_to_id: p.replyTo, anon_name: p.anonName }),\n [client, kind, id],\n );\n const edit = useCallback((cid: string, body: string) => client.comments.edit(cid, body), [client]);\n const remove = useCallback((cid: string) => client.comments.delete(cid), [client]);\n const restore = useCallback((cid: string) => client.comments.restore(cid), [client]);\n const react = useCallback((c: Comment, v: Reaction) => reactToComment(client, store, scope, c, v), [client, store, scope]);\n return { ...list, post, edit, remove, react, restore };\n}\n\n/** A top-level comment's replies, oldest first, a page at a time; enabled false defers the first read. */\nexport function useCommentReplies(id: string, o: { pageSize?: number; enabled?: boolean; client?: ContentKitClient } = {}): UseList<Comment> {\n const { client, store, scope } = useContentScope(o.client);\n const size = o.pageSize ?? 10;\n const key = o.enabled === false ? null : keyOf(\"replies\", scope, id, size);\n return useList(store, key, { type: \"replies\", id }, offsetPages(size, (q, signal) => client.comments.replies(id, q, signal)));\n}\n\n/** Whether the caller may comment on the item, the ban that stops it, and what it may do to others' comments. */\nexport function useCanComment(ref: RefBody, o: { client?: ContentKitClient } = {}) {\n const { client, store, scope } = useContentScope(o.client);\n const r = useResource<CommentStanding>(store, keyOf(\"standing\", scope, ref.kind, ref.id), { type: \"standing\", ref: { kind: ref.kind, id: ref.id } }, (s) =>\n client.comments.standing(ref, s),\n );\n return { standing: r.data ?? null, loading: r.loading || !r.loaded, error: r.error, reload: r.reload };\n}\n\n/** The newest published comments across the site, with their items (the host shows titles). */\nexport function useLatestComments(o: { pageSize?: number; client?: ContentKitClient } = {}): UseList<FeedItem> {\n const { client, store, scope } = useContentScope(o.client);\n const size = o.pageSize ?? 20;\n return useList(store, keyOf(\"latest\", scope, size), { type: \"latest\" }, offsetPages(size, (q, signal) => client.comments.latest(q, signal), true));\n}\n\nexport interface UseAdminComments extends UseList<AdminComment> {\n remove: (id: string) => Promise<void>;\n restore: (id: string) => Promise<void>;\n react: (comment: Comment, value: Reaction) => Promise<void>;\n}\n\n/** Staff: every comment, newest first, deleted, held and rejected ones with their bodies (CommentModerate). */\nexport function useAdminComments(o: { contentKind?: string; pageSize?: number; client?: ContentKitClient } = {}): UseAdminComments {\n const { client, store, scope } = useContentScope(o.client);\n const size = o.pageSize ?? 25;\n const kind = o.contentKind || undefined;\n const list = useList(\n store,\n keyOf(\"admin-comments\", scope, kind, size),\n { type: \"admin-comments\" },\n offsetPages(size, (q, signal) => client.comments.adminList({ ...q, contentKind: kind }, signal)),\n );\n const remove = useCallback((id: string) => client.comments.delete(id), [client]);\n const restore = useCallback((id: string) => client.comments.restore(id), [client]);\n const react = useCallback((c: Comment, v: Reaction) => reactToComment(client, store, scope, c, v), [client, store, scope]);\n return { ...list, remove, restore, react };\n}\n\nexport interface UseModerationQueue extends UseList<HeldItem> {\n /** Approves (publishes) or rejects at the revision the queue listed; the item leaves the queue. */\n resolve: (item: HeldItem, decision: Decision, reason?: string) => Promise<void>;\n}\n\n/** Staff: held comments or posts awaiting review, oldest first (ModerationReview). */\nexport function useModerationQueue(o: { kind?: HeldKind; pageSize?: number; client?: ContentKitClient } = {}): UseModerationQueue {\n const { client, store, scope } = useContentScope(o.client);\n const kind = o.kind ?? \"comment\";\n const size = o.pageSize ?? 20;\n const list = useList<HeldItem>(store, keyOf(\"held\", scope, kind, size), { type: \"held\" }, async (cursor, signal) => {\n const page = await client.moderation.held({ kind, cursor: typeof cursor === \"string\" ? cursor : undefined, limit: size }, signal);\n return { items: page.items, next: page.next ?? null };\n });\n const resolve = useCallback(async (item: HeldItem, d: Decision, reason?: string) => void (await client.moderation.resolve(item, d, reason)), [client]);\n return { ...list, resolve };\n}\n\nexport interface UseCommentBans extends UseList<CommentBan & { id: string }> {\n ban: (user: string, input?: BanInput) => Promise<CommentBan>;\n lift: (user: string) => Promise<void>;\n}\n\n/** The bans of one scope: the caller's own (\"owner\") or the site's (\"global\", CommentBan), newest first. */\nexport function useCommentBans(o: { scope?: BanScope; pageSize?: number; enabled?: boolean; client?: ContentKitClient } = {}): UseCommentBans {\n const { client, store, scope: readScope } = useContentScope(o.client);\n const scope = o.scope ?? \"global\";\n const size = o.pageSize ?? 25;\n const list = useList(\n store,\n o.enabled === false ? null : keyOf(\"bans\", readScope, scope, size),\n { type: \"bans\", scope },\n offsetPages(size, async (q, signal) => (await client.bans.list(scope, q, signal)).map((b) => ({ ...b, id: b.user_id }))),\n );\n const ban = useCallback((user: string, input?: BanInput) => client.bans.ban(scope, user, input), [client, scope]);\n const lift = useCallback((user: string) => client.bans.lift(scope, user), [client, scope]);\n return { ...list, ban, lift };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"comments.js","names":[],"sources":["../../src/react/comments.ts"],"sourcesContent":["import { useCallback } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { BanScope, Decision, HeldKind, Reaction, Sort } from \"../client/content/types.js\";\nimport type { AdminComment, BanInput, Comment, CommentBan, CommentStanding, FeedItem, HeldItem, ReactionCounts, RefBody } from \"../client/generated/wire.js\";\nimport type { Page, ResourceStore } from \"./resources.js\";\nimport { keyOf, offsetPages, useContentScope, useList, useResource, type UseList } from \"./use-resource.js\";\n\n/** Counts after the caller's reaction changes from counts.mine to value. */\nexport function withReaction(counts: ReactionCounts, value: Reaction): ReactionCounts {\n const likes = counts.likes - (counts.mine === 1 ? 1 : 0) + (value === 1 ? 1 : 0);\n const dislikes = counts.dislikes - (counts.mine === -1 ? 1 : 0) + (value === -1 ? 1 : 0);\n return { likes, dislikes, mine: value };\n}\n\nconst commentLists = [\"comments\", \"replies\", \"latest\", \"admin-comments\"];\nconst tokens = new WeakMap<ResourceStore, Map<string, number>>();\n\n/** Sets the caller's reaction to a comment in every list showing it at once; rolls back on failure unless a newer one started. */\nasync function reactToComment(client: ContentKitClient, store: ResourceStore, scope: string, c: Pick<Comment, \"id\" | \"likes\" | \"dislikes\" | \"mine\">, value: Reaction) {\n let t = tokens.get(store);\n if (!t) tokens.set(store, (t = new Map()));\n const token = (t.get(c.id) ?? 0) + 1;\n t.set(c.id, token);\n const reads = new Set<number>();\n const put = (counts: ReactionCounts, rollback = false) =>\n store.patch<Page<Comment>>(\n (tag) => commentLists.includes(tag.type) && tag.readScope === scope,\n (page, readVersion) => {\n if (rollback && !reads.has(readVersion)) return page;\n reads.add(readVersion);\n return { ...page, items: page.items.map((x) => (x.id === c.id ? { ...x, ...counts } : x)) };\n },\n );\n put(withReaction({ likes: c.likes, dislikes: c.dislikes, mine: c.mine }, value));\n try {\n await client.comments.react(c.id, value);\n } catch (e) {\n if (t.get(c.id) === token) put({ likes: c.likes, dislikes: c.dislikes, mine: c.mine }, true);\n throw e;\n }\n}\n\nexport interface UseCommentsOptions {\n sort?: Sort;\n /** Top-level comments per page. Default 20. */\n pageSize?: number;\n client?: ContentKitClient;\n}\n\nexport interface UseComments extends UseList<Comment> {\n /** Comments on the item, or replies to a top-level comment (replyTo); anonymous callers give anonName. */\n post: (body: string, o?: { replyTo?: string; anonName?: string }) => Promise<Comment>;\n edit: (id: string, body: string) => Promise<Comment>;\n /** Leaves a tombstone. */\n remove: (id: string) => Promise<void>;\n /** Sets the caller's reaction at once, rolled back if the server refuses it. */\n react: (comment: Comment, value: Reaction) => Promise<void>;\n /** Restores a deleted comment (moderators). */\n restore: (id: string) => Promise<void>;\n}\n\n/** An item's top-level comments, a page at a time, with every write; new comments and replies land in place. */\nexport function useComments(ref: RefBody, o: UseCommentsOptions = {}): UseComments {\n const { client, store, scope } = useContentScope(o.client);\n const size = o.pageSize ?? 20;\n const sort = o.sort ?? \"newest\";\n const key = keyOf(\"comments\", scope, ref.kind, ref.id, sort, size);\n const list = useList(store, key, { type: \"comments\", ref: { kind: ref.kind, id: ref.id } }, offsetPages(size, (q, signal) => client.comments.list(ref, { ...q, sort }, signal)));\n const { kind, id } = ref;\n const post = useCallback<UseComments[\"post\"]>(\n (body, p = {}) => client.comments.create({ kind, id }, { body, reply_to_id: p.replyTo, anon_name: p.anonName }),\n [client, kind, id],\n );\n const edit = useCallback((cid: string, body: string) => client.comments.edit(cid, body), [client]);\n const remove = useCallback((cid: string) => client.comments.delete(cid), [client]);\n const restore = useCallback((cid: string) => client.comments.restore(cid), [client]);\n const react = useCallback((c: Comment, v: Reaction) => reactToComment(client, store, scope, c, v), [client, store, scope]);\n return { ...list, post, edit, remove, react, restore };\n}\n\n/** A top-level comment's replies, oldest first, a page at a time; enabled false defers the first read. */\nexport function useCommentReplies(id: string, o: { pageSize?: number; enabled?: boolean; client?: ContentKitClient } = {}): UseList<Comment> {\n const { client, store, scope } = useContentScope(o.client);\n const size = o.pageSize ?? 10;\n const key = o.enabled === false ? null : keyOf(\"replies\", scope, id, size);\n return useList(store, key, { type: \"replies\", id }, offsetPages(size, (q, signal) => client.comments.replies(id, q, signal)));\n}\n\n/** Whether the caller may comment on the item, the ban that stops it, and what it may do to others' comments. */\nexport function useCanComment(ref: RefBody, o: { client?: ContentKitClient } = {}) {\n const { client, store, scope } = useContentScope(o.client);\n const r = useResource<CommentStanding>(store, keyOf(\"standing\", scope, ref.kind, ref.id), { type: \"standing\", ref: { kind: ref.kind, id: ref.id } }, (s) =>\n client.comments.standing(ref, s),\n );\n return { standing: r.data ?? null, loading: r.loading || !r.loaded, error: r.error, reload: r.reload };\n}\n\n/** The newest published comments across the site, with their items (the host shows titles). */\nexport function useLatestComments(o: { pageSize?: number; client?: ContentKitClient } = {}): UseList<FeedItem> {\n const { client, store, scope } = useContentScope(o.client);\n const size = o.pageSize ?? 20;\n return useList(store, keyOf(\"latest\", scope, size), { type: \"latest\" }, offsetPages(size, (q, signal) => client.comments.latest(q, signal), true));\n}\n\nexport interface UseAdminComments extends UseList<AdminComment> {\n remove: (id: string) => Promise<void>;\n restore: (id: string) => Promise<void>;\n react: (comment: Comment, value: Reaction) => Promise<void>;\n}\n\n/** Staff: every comment, newest first, deleted, held and rejected ones with their bodies (root:comments:moderate). */\nexport function useAdminComments(o: { contentKind?: string; pageSize?: number; client?: ContentKitClient } = {}): UseAdminComments {\n const { client, store, scope } = useContentScope(o.client);\n const size = o.pageSize ?? 25;\n const kind = o.contentKind || undefined;\n const list = useList(\n store,\n keyOf(\"admin-comments\", scope, kind, size),\n { type: \"admin-comments\" },\n offsetPages(size, (q, signal) => client.comments.adminList({ ...q, contentKind: kind }, signal)),\n );\n const remove = useCallback((id: string) => client.comments.delete(id), [client]);\n const restore = useCallback((id: string) => client.comments.restore(id), [client]);\n const react = useCallback((c: Comment, v: Reaction) => reactToComment(client, store, scope, c, v), [client, store, scope]);\n return { ...list, remove, restore, react };\n}\n\nexport interface UseModerationQueue extends UseList<HeldItem> {\n /** Approves (publishes) or rejects at the revision the queue listed; the item leaves the queue. */\n resolve: (item: HeldItem, decision: Decision, reason?: string) => Promise<void>;\n}\n\n/** Staff: held comments or posts awaiting review, oldest first (ModerationReview). */\nexport function useModerationQueue(o: { kind?: HeldKind; pageSize?: number; client?: ContentKitClient } = {}): UseModerationQueue {\n const { client, store, scope } = useContentScope(o.client);\n const kind = o.kind ?? \"comment\";\n const size = o.pageSize ?? 20;\n const list = useList<HeldItem>(store, keyOf(\"held\", scope, kind, size), { type: \"held\" }, async (cursor, signal) => {\n const page = await client.moderation.held({ kind, cursor: typeof cursor === \"string\" ? cursor : undefined, limit: size }, signal);\n return { items: page.items, next: page.next ?? null };\n });\n const resolve = useCallback(async (item: HeldItem, d: Decision, reason?: string) => void (await client.moderation.resolve(item, d, reason)), [client]);\n return { ...list, resolve };\n}\n\nexport interface UseCommentBans extends UseList<CommentBan & { id: string }> {\n ban: (user: string, input?: BanInput) => Promise<CommentBan>;\n lift: (user: string) => Promise<void>;\n}\n\n/** The bans of one scope: the caller's own (\"owner\") or the site's (\"global\", CommentBan), newest first. */\nexport function useCommentBans(o: { scope?: BanScope; pageSize?: number; enabled?: boolean; client?: ContentKitClient } = {}): UseCommentBans {\n const { client, store, scope: readScope } = useContentScope(o.client);\n const scope = o.scope ?? \"global\";\n const size = o.pageSize ?? 25;\n const list = useList(\n store,\n o.enabled === false ? null : keyOf(\"bans\", readScope, scope, size),\n { type: \"bans\", scope },\n offsetPages(size, async (q, signal) => (await client.bans.list(scope, q, signal)).map((b) => ({ ...b, id: b.user_id }))),\n );\n const ban = useCallback((user: string, input?: BanInput) => client.bans.ban(scope, user, input), [client, scope]);\n const lift = useCallback((user: string) => client.bans.lift(scope, user), [client, scope]);\n return { ...list, ban, lift };\n}\n"],"mappings":"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"}
|
package/dist/react/polls.d.ts
CHANGED
|
@@ -32,7 +32,7 @@ export interface PollFilter {
|
|
|
32
32
|
month?: string;
|
|
33
33
|
/** YYYY-MM-DD */
|
|
34
34
|
date?: string;
|
|
35
|
-
/** Staff: every poll, scheduled and inactive ones included (
|
|
35
|
+
/** Staff: every poll, scheduled and inactive ones included (root:polls:write). */
|
|
36
36
|
admin?: boolean;
|
|
37
37
|
pageSize?: number;
|
|
38
38
|
}
|
|
@@ -68,7 +68,7 @@ export interface UsePollEditor {
|
|
|
68
68
|
setImage: (file: Blob | null) => Promise<void>;
|
|
69
69
|
setOptionImage: (option: string, file: Blob | null) => Promise<void>;
|
|
70
70
|
}
|
|
71
|
-
/** Staff: one poll's editor (
|
|
71
|
+
/** Staff: one poll's editor (root:polls:write); null creates a new poll. */
|
|
72
72
|
export declare function usePollEditor(id: string | null | undefined, o?: {
|
|
73
73
|
client?: ContentKitClient;
|
|
74
74
|
}): UsePollEditor;
|
package/dist/react/polls.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"polls.js","names":[],"sources":["../../src/react/polls.ts"],"sourcesContent":["import { useCallback, useState } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport { toContentKitError, type ContentKitError } from \"../client/errors.js\";\nimport type { Poll, PollInput, PollOption, PollUpdate } from \"../client/generated/wire.js\";\nimport { keyOf, offsetPages, useContentScope, useList, useResource, type UseList } from \"./use-resource.js\";\n\n/** The poll after the caller votes for option; a vote is final. */\nexport function withVote(p: Poll, option: string): Poll {\n if (p.voted) return p;\n return { ...p, voted: true, my_option: option, total_votes: p.total_votes + 1, options: p.options.map((o) => (o.id === option ? { ...o, vote_count: o.vote_count + 1 } : o)) };\n}\n\nexport interface UsePoll {\n /** null: no such poll (or no live one). */\n poll: Poll | null;\n loading: boolean;\n error?: ContentKitError;\n reload: () => void;\n /** A vote or answer is in flight. */\n pending: boolean;\n /** Votes at once (a vote is final); rolled back if the server refuses it. */\n vote: (option: string) => Promise<void>;\n /** Stores or replaces the signed-in caller's free-text answer. */\n answer: (text: string) => Promise<void>;\n}\n\n/**\n * A poll with the caller's vote or answer. Without an id it is the newest\n * live poll (in language): the one a home page shows.\n */\nexport function usePoll(id?: string | null, o: { initial?: Poll; language?: string; client?: ContentKitClient } = {}): UsePoll {\n const { client, store, scope } = useContentScope(o.client);\n const key = id ? keyOf(\"poll\", scope, id) : keyOf(\"poll\", scope, \"latest\", o.language ?? \"\");\n const r = useResource<Poll | null>(\n store,\n key,\n { type: \"poll\", id: id ?? undefined },\n async (s) => (id ? client.polls.get(id, s) : ((await client.polls.list({ language: o.language, limit: 1 }, s))[0] ?? null)),\n { initial: o.initial },\n );\n const [pending, setPending] = useState(false);\n const poll = r.data ?? null;\n const vote = useCallback(\n async (option: string) => {\n const prev = store.snapshot<Poll | null>(key).data ?? poll;\n if (!prev) return;\n const token = store.mark(key);\n store.set(key, withVote(prev, option));\n setPending(true);\n try {\n await client.polls.vote(prev.id, option);\n } catch (e) {\n store.rollback(key, token, prev);\n throw toContentKitError(e);\n } finally {\n setPending(false);\n }\n },\n [client, store, key, poll],\n );\n const answer = useCallback(\n async (text: string) => {\n if (!poll) return;\n setPending(true);\n try {\n await client.polls.answer(poll.id, text);\n } finally {\n setPending(false);\n }\n },\n [client, poll],\n );\n return { poll, loading: r.loading || (!r.loaded && o.initial === undefined), error: r.error, reload: r.reload, pending, vote, answer };\n}\n\nexport interface PollFilter {\n language?: string;\n /** YYYY-MM */\n month?: string;\n /** YYYY-MM-DD */\n date?: string;\n /** Staff: every poll, scheduled and inactive ones included (PollWrite). */\n admin?: boolean;\n pageSize?: number;\n}\n\n/** Polls, newest first, a page at a time. */\nexport function usePolls(f: PollFilter = {}, o: { client?: ContentKitClient } = {}): UseList<Poll> {\n const { client, store, scope } = useContentScope(o.client);\n const size = f.pageSize ?? 20;\n const q = { language: f.language, month: f.month, date: f.date };\n return useList(\n store,\n keyOf(\"polls\", scope, !!f.admin, q.language, q.month, q.date, size),\n { type: \"polls\" },\n offsetPages(size, (page, signal) => (f.admin ? client.polls.adminList({ ...q, ...page }, signal) : client.polls.list({ ...q, ...page }, signal))),\n );\n}\n\n/** Images for a new poll, uploaded once it exists: the question's, and each option's by index. */\nexport interface PollImages {\n question?: Blob | null;\n options?: readonly (Blob | null | undefined)[];\n}\n\nexport interface UsePollEditor {\n /** null before create (or while loading). */\n poll: Poll | null;\n loading: boolean;\n error?: ContentKitError;\n /** Writes in flight. */\n saving: boolean;\n /** Creates the poll, then uploads its images; the editor then edits it. Image failures are returned, not thrown. */\n create: (input: PollInput, images?: PollImages) => Promise<{ poll: Poll; imageErrors: ContentKitError[] }>;\n update: (patch: PollUpdate) => Promise<Poll>;\n remove: () => Promise<void>;\n addOption: (label: string, image?: Blob | null) => Promise<PollOption>;\n renameOption: (option: string, label: string) => Promise<void>;\n /** Removes an option and its votes; a poll keeps at least two. */\n removeOption: (option: string) => Promise<void>;\n /** Moves an option to index to, at once; refetched if a write fails. */\n moveOption: (option: string, to: number) => Promise<void>;\n setImage: (file: Blob | null) => Promise<void>;\n setOptionImage: (option: string, file: Blob | null) => Promise<void>;\n}\n\n/** Staff: one poll's editor (PollWrite); null creates a new poll. */\nexport function usePollEditor(id: string | null | undefined, o: { client?: ContentKitClient } = {}): UsePollEditor {\n const { client, store, scope } = useContentScope(o.client);\n const [created, setCreated] = useState<string | null>(null);\n const pid = id ?? created;\n const key = pid ? keyOf(\"poll\", scope, pid) : null;\n const r = useResource<Poll | null>(store, key, { type: \"poll\", id: pid ?? undefined }, (s) => client.polls.get(pid!, s));\n const poll = r.data ?? null;\n const [inflight, setInflight] = useState(0);\n const run = useCallback(async <T>(fn: () => Promise<T>): Promise<T> => {\n setInflight((n) => n + 1);\n try {\n return await fn();\n } finally {\n setInflight((n) => n - 1);\n }\n }, []);\n const need = useCallback(() => {\n if (!pid) throw new Error(\"contentkit-ui: create the poll first\");\n return pid;\n }, [pid]);\n\n const create = useCallback<UsePollEditor[\"create\"]>(\n (input, images = {}) =>\n run(async () => {\n const made = await client.polls.create(input);\n const imageErrors: ContentKitError[] = [];\n const tries: Promise<unknown>[] = [];\n if (images.question) tries.push(client.polls.uploadImage(made.id, images.question));\n const byPosition = [...made.options].sort((a, b) => a.position - b.position);\n (input.options ?? []).forEach((opt, i) => {\n const file = images.options?.[i];\n const target = byPosition.find((x) => x.position === opt.position) ?? byPosition[i];\n if (file && target) tries.push(client.polls.uploadOptionImage(made.id, target.id, file));\n });\n for (const res of await Promise.allSettled(tries)) if (res.status === \"rejected\") imageErrors.push(toContentKitError(res.reason));\n setCreated(made.id);\n return { poll: tries.length ? await client.polls.get(made.id) : made, imageErrors };\n }),\n [client, run],\n );\n const update = useCallback((patch: PollUpdate) => run(() => client.polls.update(need(), patch)), [client, run, need]);\n const remove = useCallback(() => run(() => client.polls.delete(need())), [client, run, need]);\n const addOption = useCallback(\n (label: string, image?: Blob | null) =>\n run(async () => {\n const opt = await client.polls.addOption(need(), { label });\n if (image) opt.image_url = (await client.polls.uploadOptionImage(need(), opt.id, image)) ?? undefined;\n return opt;\n }),\n [client, run, need],\n );\n const renameOption = useCallback((option: string, label: string) => run(async () => void (await client.polls.updateOption(need(), option, { label }))), [client, run, need]);\n const removeOption = useCallback((option: string) => run(() => client.polls.deleteOption(need(), option)), [client, run, need]);\n const moveOption = useCallback(\n (option: string, to: number) =>\n run(async () => {\n const current = store.snapshot<Poll | null>(key!).data;\n if (!current) return;\n const order = [...current.options].sort((a, b) => a.position - b.position);\n const from = order.findIndex((x) => x.id === option);\n if (from < 0 || to < 0 || to >= order.length || from === to) return;\n order.splice(to, 0, ...order.splice(from, 1));\n store.set<Poll | null>(key!, { ...current, options: order.map((x, position) => ({ ...x, position })) });\n try {\n await client.polls.reorderOptions(current.id, order);\n } catch (e) {\n store.load(key!, true);\n throw e;\n }\n }),\n [client, store, key, run],\n );\n const setImage = useCallback((file: Blob | null) => run(async () => void (await client.polls.uploadImage(need(), file))), [client, run, need]);\n const setOptionImage = useCallback(\n (option: string, file: Blob | null) => run(async () => void (await client.polls.uploadOptionImage(need(), option, file))),\n [client, run, need],\n );\n return {\n poll,\n loading: !!key && (r.loading || !r.loaded),\n error: r.error,\n saving: inflight > 0,\n create,\n update,\n remove,\n addOption,\n renameOption,\n removeOption,\n moveOption,\n setImage,\n setOptionImage,\n };\n}\n"],"mappings":";;;;AAOA,SAAgB,EAAS,GAAS,GAAsB;CAEtD,OADI,EAAE,QAAc,IACb;EAAE,GAAG;EAAG,OAAO;EAAM,WAAW;EAAQ,aAAa,EAAE,cAAc;EAAG,SAAS,EAAE,QAAQ,KAAK,MAAO,EAAE,OAAO,IAAS;GAAE,GAAG;GAAG,YAAY,EAAE,aAAa;EAAE,IAAI,CAAE;CAAE;AAC/K;AAoBA,SAAgB,EAAQ,GAAoB,IAAsE,CAAC,GAAY;CAC7H,IAAM,EAAE,WAAQ,UAAO,aAAU,EAAgB,EAAE,MAAM,GACnD,IAAM,IAAK,EAAM,QAAQ,GAAO,CAAE,IAAI,EAAM,QAAQ,GAAO,UAAU,EAAE,YAAY,EAAE,GACrF,IAAI,EACR,GACA,GACA;EAAE,MAAM;EAAQ,IAAI,KAAM,KAAA;CAAU,GACpC,OAAO,MAAO,IAAK,EAAO,MAAM,IAAI,GAAI,CAAC,KAAM,MAAM,EAAO,MAAM,KAAK;EAAE,UAAU,EAAE;EAAU,OAAO;CAAE,GAAG,CAAC,EAAA,CAAG,MAAM,MACrH,EAAE,SAAS,EAAE,QAAQ,CACvB,GACM,CAAC,GAAS,KAAc,EAAS,EAAK,GACtC,IAAO,EAAE,QAAQ,MACjB,IAAO,EACX,OAAO,MAAmB;EACxB,IAAM,IAAO,EAAM,SAAsB,CAAG,CAAC,CAAC,QAAQ;EACtD,IAAI,CAAC,GAAM;EACX,IAAM,IAAQ,EAAM,KAAK,CAAG;EAE5B,AADA,EAAM,IAAI,GAAK,EAAS,GAAM,CAAM,CAAC,GACrC,EAAW,EAAI;EACf,IAAI;GACF,MAAM,EAAO,MAAM,KAAK,EAAK,IAAI,CAAM;EACzC,SAAS,GAAG;GAEV,MADA,EAAM,SAAS,GAAK,GAAO,CAAI,GACzB,EAAkB,CAAC;EAC3B,UAAU;GACR,EAAW,EAAK;EAClB;CACF,GACA;EAAC;EAAQ;EAAO;EAAK;CAAI,CAC3B,GACM,IAAS,EACb,OAAO,MAAiB;EACjB,OACL;KAAW,EAAI;GACf,IAAI;IACF,MAAM,EAAO,MAAM,OAAO,EAAK,IAAI,CAAI;GACzC,UAAU;IACR,EAAW,EAAK;GAClB;EALe;CAMjB,GACA,CAAC,GAAQ,CAAI,CACf;CACA,OAAO;EAAE;EAAM,SAAS,EAAE,WAAY,CAAC,EAAE,UAAU,EAAE,YAAY,KAAA;EAAY,OAAO,EAAE;EAAO,QAAQ,EAAE;EAAQ;EAAS;EAAM;CAAO;AACvI;AAcA,SAAgB,EAAS,IAAgB,CAAC,GAAG,IAAmC,CAAC,GAAkB;CACjG,IAAM,EAAE,WAAQ,UAAO,aAAU,EAAgB,EAAE,MAAM,GACnD,IAAO,EAAE,YAAY,IACrB,IAAI;EAAE,UAAU,EAAE;EAAU,OAAO,EAAE;EAAO,MAAM,EAAE;CAAK;CAC/D,OAAO,EACL,GACA,EAAM,SAAS,GAAO,CAAC,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,CAAI,GAClE,EAAE,MAAM,QAAQ,GAChB,EAAY,IAAO,GAAM,MAAY,EAAE,QAAQ,EAAO,MAAM,UAAU;EAAE,GAAG;EAAG,GAAG;CAAK,GAAG,CAAM,IAAI,EAAO,MAAM,KAAK;EAAE,GAAG;EAAG,GAAG;CAAK,GAAG,CAAM,CAAE,CAClJ;AACF;AA8BA,SAAgB,EAAc,GAA+B,IAAmC,CAAC,GAAkB;CACjH,IAAM,EAAE,WAAQ,UAAO,aAAU,EAAgB,EAAE,MAAM,GACnD,CAAC,GAAS,KAAc,EAAwB,IAAI,GACpD,IAAM,KAAM,GACZ,IAAM,IAAM,EAAM,QAAQ,GAAO,CAAG,IAAI,MACxC,IAAI,EAAyB,GAAO,GAAK;EAAE,MAAM;EAAQ,IAAI,KAAO,KAAA;CAAU,IAAI,MAAM,EAAO,MAAM,IAAI,GAAM,CAAC,CAAC,GACjH,IAAO,EAAE,QAAQ,MACjB,CAAC,GAAU,KAAe,EAAS,CAAC,GACpC,IAAM,EAAY,OAAU,MAAqC;EACrE,GAAa,MAAM,IAAI,CAAC;EACxB,IAAI;GACF,OAAO,MAAM,EAAG;EAClB,UAAU;GACR,GAAa,MAAM,IAAI,CAAC;EAC1B;CACF,GAAG,CAAC,CAAC,GACC,IAAO,QAAkB;EAC7B,IAAI,CAAC,GAAK,MAAU,MAAM,sCAAsC;EAChE,OAAO;CACT,GAAG,CAAC,CAAG,CAAC,GAEF,IAAS,GACZ,GAAO,IAAS,CAAC,MAChB,EAAI,YAAY;EACd,IAAM,IAAO,MAAM,EAAO,MAAM,OAAO,CAAK,GACtC,IAAiC,CAAC,GAClC,IAA4B,CAAC;EACnC,AAAI,EAAO,YAAU,EAAM,KAAK,EAAO,MAAM,YAAY,EAAK,IAAI,EAAO,QAAQ,CAAC;EAClF,IAAM,IAAa,CAAC,GAAG,EAAK,OAAO,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ;EAC3E,CAAC,EAAM,WAAW,CAAC,EAAA,CAAG,SAAS,GAAK,MAAM;GACxC,IAAM,IAAO,EAAO,UAAU,IACxB,IAAS,EAAW,MAAM,MAAM,EAAE,aAAa,EAAI,QAAQ,KAAK,EAAW;GACjF,AAAI,KAAQ,KAAQ,EAAM,KAAK,EAAO,MAAM,kBAAkB,EAAK,IAAI,EAAO,IAAI,CAAI,CAAC;EACzF,CAAC;EACD,KAAK,IAAM,KAAO,MAAM,QAAQ,WAAW,CAAK,GAAG,AAAI,EAAI,WAAW,cAAY,EAAY,KAAK,EAAkB,EAAI,MAAM,CAAC;EAEhI,OADA,EAAW,EAAK,EAAE,GACX;GAAE,MAAM,EAAM,SAAS,MAAM,EAAO,MAAM,IAAI,EAAK,EAAE,IAAI;GAAM;EAAY;CACpF,CAAC,GACH,CAAC,GAAQ,CAAG,CACd,GACM,IAAS,GAAa,MAAsB,QAAU,EAAO,MAAM,OAAO,EAAK,GAAG,CAAK,CAAC,GAAG;EAAC;EAAQ;EAAK;CAAI,CAAC,GAC9G,IAAS,QAAkB,QAAU,EAAO,MAAM,OAAO,EAAK,CAAC,CAAC,GAAG;EAAC;EAAQ;EAAK;CAAI,CAAC,GACtF,IAAY,GACf,GAAe,MACd,EAAI,YAAY;EACd,IAAM,IAAM,MAAM,EAAO,MAAM,UAAU,EAAK,GAAG,EAAE,SAAM,CAAC;EAE1D,OADI,MAAO,EAAI,YAAa,MAAM,EAAO,MAAM,kBAAkB,EAAK,GAAG,EAAI,IAAI,CAAK,KAAM,KAAA,IACrF;CACT,CAAC,GACH;EAAC;EAAQ;EAAK;CAAI,CACpB,GACM,IAAe,GAAa,GAAgB,MAAkB,EAAI,YAAY,KAAM,MAAM,EAAO,MAAM,aAAa,EAAK,GAAG,GAAQ,EAAE,SAAM,CAAC,CAAE,GAAG;EAAC;EAAQ;EAAK;CAAI,CAAC,GACrK,IAAe,GAAa,MAAmB,QAAU,EAAO,MAAM,aAAa,EAAK,GAAG,CAAM,CAAC,GAAG;EAAC;EAAQ;EAAK;CAAI,CAAC,GACxH,IAAa,GAChB,GAAgB,MACf,EAAI,YAAY;EACd,IAAM,IAAU,EAAM,SAAsB,CAAI,CAAC,CAAC;EAClD,IAAI,CAAC,GAAS;EACd,IAAM,IAAQ,CAAC,GAAG,EAAQ,OAAO,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ,GACnE,IAAO,EAAM,WAAW,MAAM,EAAE,OAAO,CAAM;EAC/C,UAAO,KAAK,IAAK,KAAK,KAAM,EAAM,UAAU,MAAS,IAEzD;GADA,EAAM,OAAO,GAAI,GAAG,GAAG,EAAM,OAAO,GAAM,CAAC,CAAC,GAC5C,EAAM,IAAiB,GAAM;IAAE,GAAG;IAAS,SAAS,EAAM,KAAK,GAAG,OAAc;KAAE,GAAG;KAAG;IAAS,EAAE;GAAE,CAAC;GACtG,IAAI;IACF,MAAM,EAAO,MAAM,eAAe,EAAQ,IAAI,CAAK;GACrD,SAAS,GAAG;IAEV,MADA,EAAM,KAAK,GAAM,EAAI,GACf;GACR;EANsG;CAOxG,CAAC,GACH;EAAC;EAAQ;EAAO;EAAK;CAAG,CAC1B,GACM,IAAW,GAAa,MAAsB,EAAI,YAAY,KAAM,MAAM,EAAO,MAAM,YAAY,EAAK,GAAG,CAAI,CAAE,GAAG;EAAC;EAAQ;EAAK;CAAI,CAAC,GACvI,IAAiB,GACpB,GAAgB,MAAsB,EAAI,YAAY,KAAM,MAAM,EAAO,MAAM,kBAAkB,EAAK,GAAG,GAAQ,CAAI,CAAE,GACxH;EAAC;EAAQ;EAAK;CAAI,CACpB;CACA,OAAO;EACL;EACA,SAAS,CAAC,CAAC,MAAQ,EAAE,WAAW,CAAC,EAAE;EACnC,OAAO,EAAE;EACT,QAAQ,IAAW;EACnB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"polls.js","names":[],"sources":["../../src/react/polls.ts"],"sourcesContent":["import { useCallback, useState } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport { toContentKitError, type ContentKitError } from \"../client/errors.js\";\nimport type { Poll, PollInput, PollOption, PollUpdate } from \"../client/generated/wire.js\";\nimport { keyOf, offsetPages, useContentScope, useList, useResource, type UseList } from \"./use-resource.js\";\n\n/** The poll after the caller votes for option; a vote is final. */\nexport function withVote(p: Poll, option: string): Poll {\n if (p.voted) return p;\n return { ...p, voted: true, my_option: option, total_votes: p.total_votes + 1, options: p.options.map((o) => (o.id === option ? { ...o, vote_count: o.vote_count + 1 } : o)) };\n}\n\nexport interface UsePoll {\n /** null: no such poll (or no live one). */\n poll: Poll | null;\n loading: boolean;\n error?: ContentKitError;\n reload: () => void;\n /** A vote or answer is in flight. */\n pending: boolean;\n /** Votes at once (a vote is final); rolled back if the server refuses it. */\n vote: (option: string) => Promise<void>;\n /** Stores or replaces the signed-in caller's free-text answer. */\n answer: (text: string) => Promise<void>;\n}\n\n/**\n * A poll with the caller's vote or answer. Without an id it is the newest\n * live poll (in language): the one a home page shows.\n */\nexport function usePoll(id?: string | null, o: { initial?: Poll; language?: string; client?: ContentKitClient } = {}): UsePoll {\n const { client, store, scope } = useContentScope(o.client);\n const key = id ? keyOf(\"poll\", scope, id) : keyOf(\"poll\", scope, \"latest\", o.language ?? \"\");\n const r = useResource<Poll | null>(\n store,\n key,\n { type: \"poll\", id: id ?? undefined },\n async (s) => (id ? client.polls.get(id, s) : ((await client.polls.list({ language: o.language, limit: 1 }, s))[0] ?? null)),\n { initial: o.initial },\n );\n const [pending, setPending] = useState(false);\n const poll = r.data ?? null;\n const vote = useCallback(\n async (option: string) => {\n const prev = store.snapshot<Poll | null>(key).data ?? poll;\n if (!prev) return;\n const token = store.mark(key);\n store.set(key, withVote(prev, option));\n setPending(true);\n try {\n await client.polls.vote(prev.id, option);\n } catch (e) {\n store.rollback(key, token, prev);\n throw toContentKitError(e);\n } finally {\n setPending(false);\n }\n },\n [client, store, key, poll],\n );\n const answer = useCallback(\n async (text: string) => {\n if (!poll) return;\n setPending(true);\n try {\n await client.polls.answer(poll.id, text);\n } finally {\n setPending(false);\n }\n },\n [client, poll],\n );\n return { poll, loading: r.loading || (!r.loaded && o.initial === undefined), error: r.error, reload: r.reload, pending, vote, answer };\n}\n\nexport interface PollFilter {\n language?: string;\n /** YYYY-MM */\n month?: string;\n /** YYYY-MM-DD */\n date?: string;\n /** Staff: every poll, scheduled and inactive ones included (root:polls:write). */\n admin?: boolean;\n pageSize?: number;\n}\n\n/** Polls, newest first, a page at a time. */\nexport function usePolls(f: PollFilter = {}, o: { client?: ContentKitClient } = {}): UseList<Poll> {\n const { client, store, scope } = useContentScope(o.client);\n const size = f.pageSize ?? 20;\n const q = { language: f.language, month: f.month, date: f.date };\n return useList(\n store,\n keyOf(\"polls\", scope, !!f.admin, q.language, q.month, q.date, size),\n { type: \"polls\" },\n offsetPages(size, (page, signal) => (f.admin ? client.polls.adminList({ ...q, ...page }, signal) : client.polls.list({ ...q, ...page }, signal))),\n );\n}\n\n/** Images for a new poll, uploaded once it exists: the question's, and each option's by index. */\nexport interface PollImages {\n question?: Blob | null;\n options?: readonly (Blob | null | undefined)[];\n}\n\nexport interface UsePollEditor {\n /** null before create (or while loading). */\n poll: Poll | null;\n loading: boolean;\n error?: ContentKitError;\n /** Writes in flight. */\n saving: boolean;\n /** Creates the poll, then uploads its images; the editor then edits it. Image failures are returned, not thrown. */\n create: (input: PollInput, images?: PollImages) => Promise<{ poll: Poll; imageErrors: ContentKitError[] }>;\n update: (patch: PollUpdate) => Promise<Poll>;\n remove: () => Promise<void>;\n addOption: (label: string, image?: Blob | null) => Promise<PollOption>;\n renameOption: (option: string, label: string) => Promise<void>;\n /** Removes an option and its votes; a poll keeps at least two. */\n removeOption: (option: string) => Promise<void>;\n /** Moves an option to index to, at once; refetched if a write fails. */\n moveOption: (option: string, to: number) => Promise<void>;\n setImage: (file: Blob | null) => Promise<void>;\n setOptionImage: (option: string, file: Blob | null) => Promise<void>;\n}\n\n/** Staff: one poll's editor (root:polls:write); null creates a new poll. */\nexport function usePollEditor(id: string | null | undefined, o: { client?: ContentKitClient } = {}): UsePollEditor {\n const { client, store, scope } = useContentScope(o.client);\n const [created, setCreated] = useState<string | null>(null);\n const pid = id ?? created;\n const key = pid ? keyOf(\"poll\", scope, pid) : null;\n const r = useResource<Poll | null>(store, key, { type: \"poll\", id: pid ?? undefined }, (s) => client.polls.get(pid!, s));\n const poll = r.data ?? null;\n const [inflight, setInflight] = useState(0);\n const run = useCallback(async <T>(fn: () => Promise<T>): Promise<T> => {\n setInflight((n) => n + 1);\n try {\n return await fn();\n } finally {\n setInflight((n) => n - 1);\n }\n }, []);\n const need = useCallback(() => {\n if (!pid) throw new Error(\"contentkit-ui: create the poll first\");\n return pid;\n }, [pid]);\n\n const create = useCallback<UsePollEditor[\"create\"]>(\n (input, images = {}) =>\n run(async () => {\n const made = await client.polls.create(input);\n const imageErrors: ContentKitError[] = [];\n const tries: Promise<unknown>[] = [];\n if (images.question) tries.push(client.polls.uploadImage(made.id, images.question));\n const byPosition = [...made.options].sort((a, b) => a.position - b.position);\n (input.options ?? []).forEach((opt, i) => {\n const file = images.options?.[i];\n const target = byPosition.find((x) => x.position === opt.position) ?? byPosition[i];\n if (file && target) tries.push(client.polls.uploadOptionImage(made.id, target.id, file));\n });\n for (const res of await Promise.allSettled(tries)) if (res.status === \"rejected\") imageErrors.push(toContentKitError(res.reason));\n setCreated(made.id);\n return { poll: tries.length ? await client.polls.get(made.id) : made, imageErrors };\n }),\n [client, run],\n );\n const update = useCallback((patch: PollUpdate) => run(() => client.polls.update(need(), patch)), [client, run, need]);\n const remove = useCallback(() => run(() => client.polls.delete(need())), [client, run, need]);\n const addOption = useCallback(\n (label: string, image?: Blob | null) =>\n run(async () => {\n const opt = await client.polls.addOption(need(), { label });\n if (image) opt.image_url = (await client.polls.uploadOptionImage(need(), opt.id, image)) ?? undefined;\n return opt;\n }),\n [client, run, need],\n );\n const renameOption = useCallback((option: string, label: string) => run(async () => void (await client.polls.updateOption(need(), option, { label }))), [client, run, need]);\n const removeOption = useCallback((option: string) => run(() => client.polls.deleteOption(need(), option)), [client, run, need]);\n const moveOption = useCallback(\n (option: string, to: number) =>\n run(async () => {\n const current = store.snapshot<Poll | null>(key!).data;\n if (!current) return;\n const order = [...current.options].sort((a, b) => a.position - b.position);\n const from = order.findIndex((x) => x.id === option);\n if (from < 0 || to < 0 || to >= order.length || from === to) return;\n order.splice(to, 0, ...order.splice(from, 1));\n store.set<Poll | null>(key!, { ...current, options: order.map((x, position) => ({ ...x, position })) });\n try {\n await client.polls.reorderOptions(current.id, order);\n } catch (e) {\n store.load(key!, true);\n throw e;\n }\n }),\n [client, store, key, run],\n );\n const setImage = useCallback((file: Blob | null) => run(async () => void (await client.polls.uploadImage(need(), file))), [client, run, need]);\n const setOptionImage = useCallback(\n (option: string, file: Blob | null) => run(async () => void (await client.polls.uploadOptionImage(need(), option, file))),\n [client, run, need],\n );\n return {\n poll,\n loading: !!key && (r.loading || !r.loaded),\n error: r.error,\n saving: inflight > 0,\n create,\n update,\n remove,\n addOption,\n renameOption,\n removeOption,\n moveOption,\n setImage,\n setOptionImage,\n };\n}\n"],"mappings":"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"}
|
package/dist/react/posts.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { UseList } from './use-resource.js';
|
|
|
6
6
|
export interface PostFilter {
|
|
7
7
|
language?: string;
|
|
8
8
|
sort?: Sort;
|
|
9
|
-
/** Staff: every post, drafts and scheduled ones included (
|
|
9
|
+
/** Staff: every post, drafts and scheduled ones included (root:posts:write). */
|
|
10
10
|
admin?: boolean;
|
|
11
11
|
/** With admin: true only drafts, false no drafts. */
|
|
12
12
|
draft?: boolean;
|
|
@@ -32,7 +32,7 @@ export interface UsePost {
|
|
|
32
32
|
/** Updates the given fields; a body from the editor is stored with image references (storedBody). */
|
|
33
33
|
update: (patch: PostInput) => Promise<Post>;
|
|
34
34
|
remove: () => Promise<void>;
|
|
35
|
-
/** Restores the deleted post (
|
|
35
|
+
/** Restores the deleted post (root:posts:write). */
|
|
36
36
|
restore: () => Promise<Post>;
|
|
37
37
|
/** Uploads the cover (null clears it); resolves with a URL that shows it now. */
|
|
38
38
|
setCover: (file: Blob | null) => Promise<string | null>;
|
|
@@ -54,7 +54,7 @@ export interface UsePost {
|
|
|
54
54
|
}
|
|
55
55
|
/** Whether readers see the post, and so its images' public files exist. */
|
|
56
56
|
export declare function isPublished(post: Post, now?: number): boolean;
|
|
57
|
-
/** One post: read, and for staff (
|
|
57
|
+
/** One post: read, and for staff (root:posts:write) create, update, delete, restore, cover and body images. null creates a new post. */
|
|
58
58
|
export declare function usePost(id: string | null | undefined, o?: {
|
|
59
59
|
initial?: Post;
|
|
60
60
|
client?: ContentKitClient;
|
package/dist/react/posts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"posts.js","names":[],"sources":["../../src/react/posts.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 { Sort } from \"../client/content/types.js\";\nimport type { Post, PostInput } from \"../client/generated/wire.js\";\nimport { IMAGE_SCHEME, imageRef, imageRefs } from \"../client/content/posts.js\";\nimport { useMediaRead } from \"./read.js\";\nimport { keyOf, offsetPages, useContentScope, useList, useResource, type UseList } from \"./use-resource.js\";\n\nexport interface PostFilter {\n language?: string;\n sort?: Sort;\n /** Staff: every post, drafts and scheduled ones included (PostWrite). */\n admin?: boolean;\n /** With admin: true only drafts, false no drafts. */\n draft?: boolean;\n /** With admin: the deleted posts instead of the live ones. */\n deleted?: boolean;\n /** With admin: only posts whose title, excerpt or body contains this text. */\n q?: string;\n pageSize?: number;\n}\n\n/** Posts, a page at a time: the published ones, or every one for staff. */\nexport function usePosts(f: PostFilter = {}, o: { client?: ContentKitClient } = {}): UseList<Post> {\n const { client, store, scope } = useContentScope(o.client);\n const size = f.pageSize ?? 20;\n return useList(\n store,\n keyOf(\"posts\", scope, !!f.admin, f.language, f.sort, f.draft, f.deleted, f.q, size),\n { type: \"posts\", scope: f.admin && f.deleted ? \"deleted\" : undefined },\n offsetPages(size, (q, signal) =>\n f.admin\n ? client.posts.adminList({ ...q, language: f.language, draft: f.draft, deleted: f.deleted, q: f.q }, signal)\n : client.posts.list({ ...q, language: f.language, sort: f.sort }, signal),\n ),\n );\n}\n\nexport interface UsePost {\n /** null before create (or while loading). */\n post: Post | null;\n loading: boolean;\n error?: ContentKitError;\n /** Writes in flight. */\n saving: boolean;\n /** Creates the post; the hook then edits it. */\n create: (input: PostInput) => Promise<Post>;\n /** Updates the given fields; a body from the editor is stored with image references (storedBody). */\n update: (patch: PostInput) => Promise<Post>;\n remove: () => Promise<void>;\n /** Restores the deleted post (PostWrite). */\n restore: () => Promise<Post>;\n /** Uploads the cover (null clears it); resolves with a URL that shows it now. */\n setCover: (file: Blob | null) => Promise<string | null>;\n /** Uploads an image for the body; resolves with a URL that shows it now, to place in the editor. */\n uploadImage: (file: Blob) => Promise<string>;\n /**\n * A URL that shows one of the post's images now, by name (\"i-{uuid}\") or\n * reference: a fresh upload from its file, else its public file\n * (`post.images`), else, while it has none (an unpublished post's), its\n * signed editor view from an editor read. Anything else is returned as is.\n */\n imageSrc: (image: string | null | undefined) => string | undefined;\n /** The cover through imageSrc. */\n coverSrc: string | undefined;\n /** post.body with its images through imageSrc: what a rich-text editor shows. */\n editorBody: string | undefined;\n /** HTML from the editor with every image URL this hook showed turned back into the image's reference. */\n storedBody: (html: string) => string;\n}\n\n/** The names of the images a body still references, once each. */\nconst refNames = (body: string | undefined) => [...new Set([...(body ?? \"\").matchAll(imageRefs())].map((m) => m[1]!))];\nconst uploadName = (path: string) => {\n const base = path.slice(path.lastIndexOf(\"/\") + 1);\n const dot = base.lastIndexOf(\".\");\n return dot > 0 ? base.slice(0, dot) : base;\n};\n\n/** Whether readers see the post, and so its images' public files exist. */\nexport function isPublished(post: Post, now = Date.now()): boolean {\n return !post.is_draft && !post.moderation && (!post.live_at || Date.parse(post.live_at) <= now);\n}\n\n/** One post: read, and for staff (PostWrite) create, update, delete, restore, cover and body images. null creates a new post. */\nexport function usePost(id: string | null | undefined, o: { initial?: Post; client?: ContentKitClient } = {}): UsePost {\n const { client, store, scope } = useContentScope(o.client);\n const [created, setCreated] = useState<string | null>(null);\n const pid = id ?? created;\n const key = pid ? keyOf(\"post\", scope, pid) : null;\n const r = useResource<Post | null>(store, key, { type: \"post\", id: pid ?? undefined }, (s) => client.posts.get(pid!, s), { initial: o.initial });\n const post = r.data ?? null;\n const [inflight, setInflight] = useState(0);\n const run = useCallback(async <T>(fn: () => Promise<T>): Promise<T> => {\n setInflight((n) => n + 1);\n try {\n return await fn();\n } finally {\n setInflight((n) => n - 1);\n }\n }, []);\n const need = useCallback(() => {\n if (!pid) throw new Error(\"contentkit-ui: create the post first\");\n return pid;\n }, [pid]);\n\n // Images: fresh uploads show from their file, published ones from post.images,\n // the rest (an unpublished post's) from an editor read.\n const [previews, setPreviews] = useState<ReadonlyMap<string, string>>(new Map());\n const blobs = useRef<string[]>([]);\n useEffect(() => {\n const urls = blobs.current;\n return () => {\n for (const u of urls) URL.revokeObjectURL(u);\n };\n }, []);\n const published = useMemo(() => new Map(Object.entries(post?.images ?? {})), [post?.images]);\n const pending = useMemo(() => {\n const names = refNames(post?.body);\n if (post?.cover && !post.cover_url && !names.includes(post.cover)) names.push(post.cover);\n return names.filter((n) => !previews.has(n));\n }, [post?.body, post?.cover, post?.cover_url, previews]);\n const folder = useMemo(() => client.media.postRef(pid ?? \"none\"), [client, pid]);\n const read = useMediaRead(folder, { editor: true, read: pending.length ? undefined : null, client });\n const views = useMemo(() => {\n const out = new Map<string, string>();\n for (const f of read.read?.files ?? []) if (f.upload && f.editor_url) out.set(uploadName(f.path), f.editor_url);\n return out;\n }, [read.read]);\n // Editor views render on the first editor read that misses them: read again, backing off.\n const failed = (n: string) => !!read.read?.files.some((f) => f.failed && uploadName(f.path) === n);\n const missing = !!read.read && pending.some((n) => !views.has(n) && !failed(n));\n const tries = useRef(0);\n const { reload } = read;\n useEffect(() => {\n if (!missing) {\n tries.current = 0;\n return;\n }\n if (tries.current >= 6) return;\n const t = setTimeout(() => {\n tries.current++;\n reload();\n }, 1000 * 2 ** tries.current);\n return () => clearTimeout(t);\n }, [missing, read.read, reload]);\n\n // Every URL shown for an image, of this and earlier reads, back to its name.\n const shown = useRef(new Map<string, string>());\n useEffect(() => {\n for (const m of [previews, published, views]) for (const [n, u] of m) shown.current.set(u, n);\n }, [previews, published, views]);\n\n const imageSrc = useCallback(\n (image: string | null | undefined) => {\n if (!image) return undefined;\n const n = image.startsWith(IMAGE_SCHEME) ? image.slice(IMAGE_SCHEME.length) : image;\n return previews.get(n) ?? published.get(n) ?? views.get(n) ?? image;\n },\n [previews, published, views],\n );\n const editorBody = useMemo(() => post?.body.replace(imageRefs(), (ref, n: string) => previews.get(n) ?? views.get(n) ?? ref), [post?.body, previews, views]);\n const storedBody = useCallback(\n (html: string) => {\n const back = new Map(shown.current);\n for (const m of [previews, published, views]) for (const [n, u] of m) back.set(u, n);\n let out = html;\n for (const [u, n] of back) out = out.split(u).join(imageRef(n)).split(u.replaceAll(\"&\", \"&\")).join(imageRef(n));\n return out;\n },\n [previews, published, views],\n );\n const preview = useCallback((name: string, file: Blob): string | undefined => {\n if (typeof URL.createObjectURL !== \"function\") return undefined;\n const url = URL.createObjectURL(file);\n blobs.current.push(url);\n setPreviews((m) => new Map(m).set(name, url));\n return url;\n }, []);\n\n const create = useCallback(\n (input: PostInput) =>\n run(async () => {\n const post = await client.posts.create(input);\n setCreated(post.id);\n return post;\n }),\n [client, run],\n );\n const update = useCallback(\n (patch: PostInput) => run(() => client.posts.update(need(), patch.body === undefined ? patch : { ...patch, body: storedBody(patch.body) })),\n [client, run, need, storedBody],\n );\n const remove = useCallback(() => run(() => client.posts.delete(need())), [client, run, need]);\n const restore = useCallback(() => run(() => client.posts.restore(need())), [client, run, need]);\n const setCover = useCallback(\n (file: Blob | null) =>\n run(async () => {\n if (!file) return client.posts.setCover(need(), null);\n const up = await client.media.uploadNamed(client.media.postRef(need()), file);\n const cover = await client.posts.setCover(need(), up.name);\n const url = preview(up.name, file) ?? cover;\n if (url) shown.current.set(url, up.name);\n return url;\n }),\n [client, run, need, preview],\n );\n const uploadImage = useCallback(\n (file: Blob) =>\n run(async () => {\n const up = await client.media.uploadInline(need(), file);\n const url = preview(up.name, file) ?? up.url ?? up.ref;\n shown.current.set(url, up.name);\n return url;\n }),\n [client, run, need, preview],\n );\n return {\n post,\n loading: !!key && (r.loading || (!r.loaded && o.initial === undefined)),\n error: r.error,\n saving: inflight > 0,\n create,\n update,\n remove,\n restore,\n setCover,\n uploadImage,\n imageSrc,\n coverSrc: imageSrc(post?.cover),\n editorBody,\n storedBody,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"posts.js","names":[],"sources":["../../src/react/posts.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 { Sort } from \"../client/content/types.js\";\nimport type { Post, PostInput } from \"../client/generated/wire.js\";\nimport { IMAGE_SCHEME, imageRef, imageRefs } from \"../client/content/posts.js\";\nimport { useMediaRead } from \"./read.js\";\nimport { keyOf, offsetPages, useContentScope, useList, useResource, type UseList } from \"./use-resource.js\";\n\nexport interface PostFilter {\n language?: string;\n sort?: Sort;\n /** Staff: every post, drafts and scheduled ones included (root:posts:write). */\n admin?: boolean;\n /** With admin: true only drafts, false no drafts. */\n draft?: boolean;\n /** With admin: the deleted posts instead of the live ones. */\n deleted?: boolean;\n /** With admin: only posts whose title, excerpt or body contains this text. */\n q?: string;\n pageSize?: number;\n}\n\n/** Posts, a page at a time: the published ones, or every one for staff. */\nexport function usePosts(f: PostFilter = {}, o: { client?: ContentKitClient } = {}): UseList<Post> {\n const { client, store, scope } = useContentScope(o.client);\n const size = f.pageSize ?? 20;\n return useList(\n store,\n keyOf(\"posts\", scope, !!f.admin, f.language, f.sort, f.draft, f.deleted, f.q, size),\n { type: \"posts\", scope: f.admin && f.deleted ? \"deleted\" : undefined },\n offsetPages(size, (q, signal) =>\n f.admin\n ? client.posts.adminList({ ...q, language: f.language, draft: f.draft, deleted: f.deleted, q: f.q }, signal)\n : client.posts.list({ ...q, language: f.language, sort: f.sort }, signal),\n ),\n );\n}\n\nexport interface UsePost {\n /** null before create (or while loading). */\n post: Post | null;\n loading: boolean;\n error?: ContentKitError;\n /** Writes in flight. */\n saving: boolean;\n /** Creates the post; the hook then edits it. */\n create: (input: PostInput) => Promise<Post>;\n /** Updates the given fields; a body from the editor is stored with image references (storedBody). */\n update: (patch: PostInput) => Promise<Post>;\n remove: () => Promise<void>;\n /** Restores the deleted post (root:posts:write). */\n restore: () => Promise<Post>;\n /** Uploads the cover (null clears it); resolves with a URL that shows it now. */\n setCover: (file: Blob | null) => Promise<string | null>;\n /** Uploads an image for the body; resolves with a URL that shows it now, to place in the editor. */\n uploadImage: (file: Blob) => Promise<string>;\n /**\n * A URL that shows one of the post's images now, by name (\"i-{uuid}\") or\n * reference: a fresh upload from its file, else its public file\n * (`post.images`), else, while it has none (an unpublished post's), its\n * signed editor view from an editor read. Anything else is returned as is.\n */\n imageSrc: (image: string | null | undefined) => string | undefined;\n /** The cover through imageSrc. */\n coverSrc: string | undefined;\n /** post.body with its images through imageSrc: what a rich-text editor shows. */\n editorBody: string | undefined;\n /** HTML from the editor with every image URL this hook showed turned back into the image's reference. */\n storedBody: (html: string) => string;\n}\n\n/** The names of the images a body still references, once each. */\nconst refNames = (body: string | undefined) => [...new Set([...(body ?? \"\").matchAll(imageRefs())].map((m) => m[1]!))];\nconst uploadName = (path: string) => {\n const base = path.slice(path.lastIndexOf(\"/\") + 1);\n const dot = base.lastIndexOf(\".\");\n return dot > 0 ? base.slice(0, dot) : base;\n};\n\n/** Whether readers see the post, and so its images' public files exist. */\nexport function isPublished(post: Post, now = Date.now()): boolean {\n return !post.is_draft && !post.moderation && (!post.live_at || Date.parse(post.live_at) <= now);\n}\n\n/** One post: read, and for staff (root:posts:write) create, update, delete, restore, cover and body images. null creates a new post. */\nexport function usePost(id: string | null | undefined, o: { initial?: Post; client?: ContentKitClient } = {}): UsePost {\n const { client, store, scope } = useContentScope(o.client);\n const [created, setCreated] = useState<string | null>(null);\n const pid = id ?? created;\n const key = pid ? keyOf(\"post\", scope, pid) : null;\n const r = useResource<Post | null>(store, key, { type: \"post\", id: pid ?? undefined }, (s) => client.posts.get(pid!, s), { initial: o.initial });\n const post = r.data ?? null;\n const [inflight, setInflight] = useState(0);\n const run = useCallback(async <T>(fn: () => Promise<T>): Promise<T> => {\n setInflight((n) => n + 1);\n try {\n return await fn();\n } finally {\n setInflight((n) => n - 1);\n }\n }, []);\n const need = useCallback(() => {\n if (!pid) throw new Error(\"contentkit-ui: create the post first\");\n return pid;\n }, [pid]);\n\n // Images: fresh uploads show from their file, published ones from post.images,\n // the rest (an unpublished post's) from an editor read.\n const [previews, setPreviews] = useState<ReadonlyMap<string, string>>(new Map());\n const blobs = useRef<string[]>([]);\n useEffect(() => {\n const urls = blobs.current;\n return () => {\n for (const u of urls) URL.revokeObjectURL(u);\n };\n }, []);\n const published = useMemo(() => new Map(Object.entries(post?.images ?? {})), [post?.images]);\n const pending = useMemo(() => {\n const names = refNames(post?.body);\n if (post?.cover && !post.cover_url && !names.includes(post.cover)) names.push(post.cover);\n return names.filter((n) => !previews.has(n));\n }, [post?.body, post?.cover, post?.cover_url, previews]);\n const folder = useMemo(() => client.media.postRef(pid ?? \"none\"), [client, pid]);\n const read = useMediaRead(folder, { editor: true, read: pending.length ? undefined : null, client });\n const views = useMemo(() => {\n const out = new Map<string, string>();\n for (const f of read.read?.files ?? []) if (f.upload && f.editor_url) out.set(uploadName(f.path), f.editor_url);\n return out;\n }, [read.read]);\n // Editor views render on the first editor read that misses them: read again, backing off.\n const failed = (n: string) => !!read.read?.files.some((f) => f.failed && uploadName(f.path) === n);\n const missing = !!read.read && pending.some((n) => !views.has(n) && !failed(n));\n const tries = useRef(0);\n const { reload } = read;\n useEffect(() => {\n if (!missing) {\n tries.current = 0;\n return;\n }\n if (tries.current >= 6) return;\n const t = setTimeout(() => {\n tries.current++;\n reload();\n }, 1000 * 2 ** tries.current);\n return () => clearTimeout(t);\n }, [missing, read.read, reload]);\n\n // Every URL shown for an image, of this and earlier reads, back to its name.\n const shown = useRef(new Map<string, string>());\n useEffect(() => {\n for (const m of [previews, published, views]) for (const [n, u] of m) shown.current.set(u, n);\n }, [previews, published, views]);\n\n const imageSrc = useCallback(\n (image: string | null | undefined) => {\n if (!image) return undefined;\n const n = image.startsWith(IMAGE_SCHEME) ? image.slice(IMAGE_SCHEME.length) : image;\n return previews.get(n) ?? published.get(n) ?? views.get(n) ?? image;\n },\n [previews, published, views],\n );\n const editorBody = useMemo(() => post?.body.replace(imageRefs(), (ref, n: string) => previews.get(n) ?? views.get(n) ?? ref), [post?.body, previews, views]);\n const storedBody = useCallback(\n (html: string) => {\n const back = new Map(shown.current);\n for (const m of [previews, published, views]) for (const [n, u] of m) back.set(u, n);\n let out = html;\n for (const [u, n] of back) out = out.split(u).join(imageRef(n)).split(u.replaceAll(\"&\", \"&\")).join(imageRef(n));\n return out;\n },\n [previews, published, views],\n );\n const preview = useCallback((name: string, file: Blob): string | undefined => {\n if (typeof URL.createObjectURL !== \"function\") return undefined;\n const url = URL.createObjectURL(file);\n blobs.current.push(url);\n setPreviews((m) => new Map(m).set(name, url));\n return url;\n }, []);\n\n const create = useCallback(\n (input: PostInput) =>\n run(async () => {\n const post = await client.posts.create(input);\n setCreated(post.id);\n return post;\n }),\n [client, run],\n );\n const update = useCallback(\n (patch: PostInput) => run(() => client.posts.update(need(), patch.body === undefined ? patch : { ...patch, body: storedBody(patch.body) })),\n [client, run, need, storedBody],\n );\n const remove = useCallback(() => run(() => client.posts.delete(need())), [client, run, need]);\n const restore = useCallback(() => run(() => client.posts.restore(need())), [client, run, need]);\n const setCover = useCallback(\n (file: Blob | null) =>\n run(async () => {\n if (!file) return client.posts.setCover(need(), null);\n const up = await client.media.uploadNamed(client.media.postRef(need()), file);\n const cover = await client.posts.setCover(need(), up.name);\n const url = preview(up.name, file) ?? cover;\n if (url) shown.current.set(url, up.name);\n return url;\n }),\n [client, run, need, preview],\n );\n const uploadImage = useCallback(\n (file: Blob) =>\n run(async () => {\n const up = await client.media.uploadInline(need(), file);\n const url = preview(up.name, file) ?? up.url ?? up.ref;\n shown.current.set(url, up.name);\n return url;\n }),\n [client, run, need, preview],\n );\n return {\n post,\n loading: !!key && (r.loading || (!r.loaded && o.initial === undefined)),\n error: r.error,\n saving: inflight > 0,\n create,\n update,\n remove,\n restore,\n setCover,\n uploadImage,\n imageSrc,\n coverSrc: imageSrc(post?.cover),\n editorBody,\n storedBody,\n };\n}\n"],"mappings":";;;;;AAwBA,SAAgB,EAAS,IAAgB,CAAC,GAAG,IAAmC,CAAC,GAAkB;CACjG,IAAM,EAAE,WAAQ,UAAO,aAAU,EAAgB,EAAE,MAAM,GACnD,IAAO,EAAE,YAAY;CAC3B,OAAO,EACL,GACA,EAAM,SAAS,GAAO,CAAC,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,CAAI,GAClF;EAAE,MAAM;EAAS,OAAO,EAAE,SAAS,EAAE,UAAU,YAAY,KAAA;CAAU,GACrE,EAAY,IAAO,GAAG,MACpB,EAAE,QACE,EAAO,MAAM,UAAU;EAAE,GAAG;EAAG,UAAU,EAAE;EAAU,OAAO,EAAE;EAAO,SAAS,EAAE;EAAS,GAAG,EAAE;CAAE,GAAG,CAAM,IACzG,EAAO,MAAM,KAAK;EAAE,GAAG;EAAG,UAAU,EAAE;EAAU,MAAM,EAAE;CAAK,GAAG,CAAM,CAC5E,CACF;AACF;AAoCA,IAAM,KAAY,MAA6B,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,KAAQ,GAAA,CAAI,SAAS,EAAU,CAAC,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,EAAG,CAAC,CAAC,GAC/G,KAAc,MAAiB;CACnC,IAAM,IAAO,EAAK,MAAM,EAAK,YAAY,GAAG,IAAI,CAAC,GAC3C,IAAM,EAAK,YAAY,GAAG;CAChC,OAAO,IAAM,IAAI,EAAK,MAAM,GAAG,CAAG,IAAI;AACxC;AAGA,SAAgB,EAAY,GAAY,IAAM,KAAK,IAAI,GAAY;CACjE,OAAO,CAAC,EAAK,YAAY,CAAC,EAAK,eAAe,CAAC,EAAK,WAAW,KAAK,MAAM,EAAK,OAAO,KAAK;AAC7F;AAGA,SAAgB,EAAQ,GAA+B,IAAmD,CAAC,GAAY;CACrH,IAAM,EAAE,WAAQ,UAAO,aAAU,EAAgB,EAAE,MAAM,GACnD,CAAC,GAAS,KAAc,EAAwB,IAAI,GACpD,IAAM,KAAM,GACZ,IAAM,IAAM,EAAM,QAAQ,GAAO,CAAG,IAAI,MACxC,IAAI,EAAyB,GAAO,GAAK;EAAE,MAAM;EAAQ,IAAI,KAAO,KAAA;CAAU,IAAI,MAAM,EAAO,MAAM,IAAI,GAAM,CAAC,GAAG,EAAE,SAAS,EAAE,QAAQ,CAAC,GACzI,IAAO,EAAE,QAAQ,MACjB,CAAC,GAAU,KAAe,EAAS,CAAC,GACpC,IAAM,EAAY,OAAU,MAAqC;EACrE,GAAa,MAAM,IAAI,CAAC;EACxB,IAAI;GACF,OAAO,MAAM,EAAG;EAClB,UAAU;GACR,GAAa,MAAM,IAAI,CAAC;EAC1B;CACF,GAAG,CAAC,CAAC,GACC,IAAO,QAAkB;EAC7B,IAAI,CAAC,GAAK,MAAU,MAAM,sCAAsC;EAChE,OAAO;CACT,GAAG,CAAC,CAAG,CAAC,GAIF,CAAC,GAAU,KAAe,kBAAsC,IAAI,IAAI,CAAC,GACzE,IAAQ,EAAiB,CAAC,CAAC;CACjC,QAAgB;EACd,IAAM,IAAO,EAAM;EACnB,aAAa;GACX,KAAK,IAAM,KAAK,GAAM,IAAI,gBAAgB,CAAC;EAC7C;CACF,GAAG,CAAC,CAAC;CACL,IAAM,IAAY,QAAc,IAAI,IAAI,OAAO,QAAQ,GAAM,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,GAAM,MAAM,CAAC,GACrF,IAAU,QAAc;EAC5B,IAAM,IAAQ,EAAS,GAAM,IAAI;EAEjC,OADI,GAAM,SAAS,CAAC,EAAK,aAAa,CAAC,EAAM,SAAS,EAAK,KAAK,KAAG,EAAM,KAAK,EAAK,KAAK,GACjF,EAAM,QAAQ,MAAM,CAAC,EAAS,IAAI,CAAC,CAAC;CAC7C,GAAG;EAAC,GAAM;EAAM,GAAM;EAAO,GAAM;EAAW;CAAQ,CAAC,GACjD,IAAS,QAAc,EAAO,MAAM,QAAQ,KAAO,MAAM,GAAG,CAAC,GAAQ,CAAG,CAAC,GACzE,IAAO,EAAa,GAAQ;EAAE,QAAQ;EAAM,MAAM,EAAQ,SAAS,KAAA,IAAY;EAAM;CAAO,CAAC,GAC7F,IAAQ,QAAc;EAC1B,IAAM,oBAAM,IAAI,IAAoB;EACpC,KAAK,IAAM,KAAK,EAAK,MAAM,SAAS,CAAC,GAAG,AAAI,EAAE,UAAU,EAAE,cAAY,EAAI,IAAI,EAAW,EAAE,IAAI,GAAG,EAAE,UAAU;EAC9G,OAAO;CACT,GAAG,CAAC,EAAK,IAAI,CAAC,GAER,KAAU,MAAc,CAAC,CAAC,EAAK,MAAM,MAAM,MAAM,MAAM,EAAE,UAAU,EAAW,EAAE,IAAI,MAAM,CAAC,GAC3F,IAAU,CAAC,CAAC,EAAK,QAAQ,EAAQ,MAAM,MAAM,CAAC,EAAM,IAAI,CAAC,KAAK,CAAC,EAAO,CAAC,CAAC,GACxE,IAAQ,EAAO,CAAC,GAChB,EAAE,cAAW;CACnB,QAAgB;EACd,IAAI,CAAC,GAAS;GACZ,EAAM,UAAU;GAChB;EACF;EACA,IAAI,EAAM,WAAW,GAAG;EACxB,IAAM,IAAI,iBAAiB;GAEzB,AADA,EAAM,WACN,EAAO;EACT,GAAG,MAAO,KAAK,EAAM,OAAO;EAC5B,aAAa,aAAa,CAAC;CAC7B,GAAG;EAAC;EAAS,EAAK;EAAM;CAAM,CAAC;CAG/B,IAAM,IAAQ,kBAAO,IAAI,IAAoB,CAAC;CAC9C,QAAgB;EACd,KAAK,IAAM,KAAK;GAAC;GAAU;GAAW;EAAK,GAAG,KAAK,IAAM,CAAC,GAAG,MAAM,GAAG,EAAM,QAAQ,IAAI,GAAG,CAAC;CAC9F,GAAG;EAAC;EAAU;EAAW;CAAK,CAAC;CAE/B,IAAM,IAAW,GACd,MAAqC;EACpC,IAAI,CAAC,GAAO;EACZ,IAAM,IAAI,EAAM,WAAA,aAAuB,IAAI,EAAM,MAAM,EAAa,MAAM,IAAI;EAC9E,OAAO,EAAS,IAAI,CAAC,KAAK,EAAU,IAAI,CAAC,KAAK,EAAM,IAAI,CAAC,KAAK;CAChE,GACA;EAAC;EAAU;EAAW;CAAK,CAC7B,GACM,IAAa,QAAc,GAAM,KAAK,QAAQ,EAAU,IAAI,GAAK,MAAc,EAAS,IAAI,CAAC,KAAK,EAAM,IAAI,CAAC,KAAK,CAAG,GAAG;EAAC,GAAM;EAAM;EAAU;CAAK,CAAC,GACrJ,IAAa,GAChB,MAAiB;EAChB,IAAM,IAAO,IAAI,IAAI,EAAM,OAAO;EAClC,KAAK,IAAM,KAAK;GAAC;GAAU;GAAW;EAAK,GAAG,KAAK,IAAM,CAAC,GAAG,MAAM,GAAG,EAAK,IAAI,GAAG,CAAC;EACnF,IAAI,IAAM;EACV,KAAK,IAAM,CAAC,GAAG,MAAM,GAAM,IAAM,EAAI,MAAM,CAAC,CAAC,CAAC,KAAK,EAAS,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,EAAS,CAAC,CAAC;EAClH,OAAO;CACT,GACA;EAAC;EAAU;EAAW;CAAK,CAC7B,GACM,IAAU,GAAa,GAAc,MAAmC;EAC5E,IAAI,OAAO,IAAI,mBAAoB,YAAY;EAC/C,IAAM,IAAM,IAAI,gBAAgB,CAAI;EAGpC,OAFA,EAAM,QAAQ,KAAK,CAAG,GACtB,GAAa,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,GAAM,CAAG,CAAC,GACrC;CACT,GAAG,CAAC,CAAC,GAEC,IAAS,GACZ,MACC,EAAI,YAAY;EACd,IAAM,IAAO,MAAM,EAAO,MAAM,OAAO,CAAK;EAE5C,OADA,EAAW,EAAK,EAAE,GACX;CACT,CAAC,GACH,CAAC,GAAQ,CAAG,CACd,GACM,IAAS,GACZ,MAAqB,QAAU,EAAO,MAAM,OAAO,EAAK,GAAG,EAAM,SAAS,KAAA,IAAY,IAAQ;EAAE,GAAG;EAAO,MAAM,EAAW,EAAM,IAAI;CAAE,CAAC,CAAC,GAC1I;EAAC;EAAQ;EAAK;EAAM;CAAU,CAChC,GACM,IAAS,QAAkB,QAAU,EAAO,MAAM,OAAO,EAAK,CAAC,CAAC,GAAG;EAAC;EAAQ;EAAK;CAAI,CAAC,GACtF,IAAU,QAAkB,QAAU,EAAO,MAAM,QAAQ,EAAK,CAAC,CAAC,GAAG;EAAC;EAAQ;EAAK;CAAI,CAAC,GACxF,IAAW,GACd,MACC,EAAI,YAAY;EACd,IAAI,CAAC,GAAM,OAAO,EAAO,MAAM,SAAS,EAAK,GAAG,IAAI;EACpD,IAAM,IAAK,MAAM,EAAO,MAAM,YAAY,EAAO,MAAM,QAAQ,EAAK,CAAC,GAAG,CAAI,GACtE,IAAQ,MAAM,EAAO,MAAM,SAAS,EAAK,GAAG,EAAG,IAAI,GACnD,IAAM,EAAQ,EAAG,MAAM,CAAI,KAAK;EAEtC,OADI,KAAK,EAAM,QAAQ,IAAI,GAAK,EAAG,IAAI,GAChC;CACT,CAAC,GACH;EAAC;EAAQ;EAAK;EAAM;CAAO,CAC7B,GACM,IAAc,GACjB,MACC,EAAI,YAAY;EACd,IAAM,IAAK,MAAM,EAAO,MAAM,aAAa,EAAK,GAAG,CAAI,GACjD,IAAM,EAAQ,EAAG,MAAM,CAAI,KAAK,EAAG,OAAO,EAAG;EAEnD,OADA,EAAM,QAAQ,IAAI,GAAK,EAAG,IAAI,GACvB;CACT,CAAC,GACH;EAAC;EAAQ;EAAK;EAAM;CAAO,CAC7B;CACA,OAAO;EACL;EACA,SAAS,CAAC,CAAC,MAAQ,EAAE,WAAY,CAAC,EAAE,UAAU,EAAE,YAAY,KAAA;EAC5D,OAAO,EAAE;EACT,QAAQ,IAAW;EACnB;EACA;EACA;EACA;EACA;EACA;EACA;EACA,UAAU,EAAS,GAAM,KAAK;EAC9B;EACA;CACF;AACF"}
|
package/package.json
CHANGED