@openrails/contentkit-ui 0.104.0-rc.1 → 0.104.0-rc.2
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 +6 -6
- package/dist/client/beacons.js +4 -1
- package/dist/client/beacons.js.map +1 -1
- package/dist/client/contents.d.ts +7 -1
- package/dist/client/contents.js +8 -0
- package/dist/client/contents.js.map +1 -1
- package/dist/client/discovery.d.ts +6 -2
- package/dist/client/discovery.js +1 -1
- package/dist/client/discovery.js.map +1 -1
- package/dist/client/generated/routes.d.ts +41 -28
- package/dist/client/generated/routes.js +63 -61
- package/dist/client/generated/routes.js.map +1 -1
- package/dist/client/generated/wire.d.ts +36 -74
- package/dist/client/generated/wire.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/media/windows.d.ts +2 -0
- package/dist/client/media/windows.js +4 -1
- package/dist/client/media/windows.js.map +1 -1
- package/dist/client/stats.d.ts +4 -20
- package/dist/client/stats.js +0 -11
- package/dist/client/stats.js.map +1 -1
- package/dist/client/versions.d.ts +8 -6
- package/dist/client/versions.js +15 -10
- package/dist/client/versions.js.map +1 -1
- package/dist/components/admin/bulk-drafts.d.ts +17 -0
- package/dist/components/admin/bulk-drafts.js +41 -0
- package/dist/components/admin/bulk-drafts.js.map +1 -0
- package/dist/components/admin/content-admin.d.ts +1 -1
- package/dist/components/admin/content-admin.js.map +1 -1
- package/dist/components/admin/content-manager.d.ts +11 -3
- package/dist/components/admin/content-manager.js +739 -380
- package/dist/components/admin/content-manager.js.map +1 -1
- package/dist/components/admin/group-manager.d.ts +8 -3
- package/dist/components/admin/group-manager.js +218 -146
- package/dist/components/admin/group-manager.js.map +1 -1
- package/dist/components/admin/page-notices.d.ts +16 -0
- package/dist/components/admin/page-notices.js +113 -0
- package/dist/components/admin/page-notices.js.map +1 -0
- package/dist/components/admin/page-picker.d.ts +38 -0
- package/dist/components/admin/page-picker.js +159 -0
- package/dist/components/admin/page-picker.js.map +1 -0
- package/dist/components/admin/reader-content-editor.js +537 -500
- package/dist/components/admin/reader-content-editor.js.map +1 -1
- package/dist/components/admin/reader-manifest.js +181 -179
- package/dist/components/admin/reader-manifest.js.map +1 -1
- package/dist/components/admin/shell.d.ts +10 -2
- package/dist/components/admin/shell.js +114 -42
- package/dist/components/admin/shell.js.map +1 -1
- package/dist/components/admin/stats-dashboard.d.ts +4 -9
- package/dist/components/admin/stats-dashboard.js +300 -202
- package/dist/components/admin/stats-dashboard.js.map +1 -1
- package/dist/components/admin/upload-queue.d.ts +14 -0
- package/dist/components/admin/upload-queue.js +115 -98
- package/dist/components/admin/upload-queue.js.map +1 -1
- package/dist/components/content/card.d.ts +31 -3
- package/dist/components/content/card.js +324 -158
- package/dist/components/content/card.js.map +1 -1
- package/dist/components/content/daily-releases.js +14 -12
- package/dist/components/content/daily-releases.js.map +1 -1
- package/dist/components/content/editor-save.js +27 -25
- package/dist/components/content/editor-save.js.map +1 -1
- package/dist/components/content/editors.js +274 -253
- package/dist/components/content/editors.js.map +1 -1
- package/dist/components/content/favorites.d.ts +1 -1
- package/dist/components/content/favorites.js +36 -22
- package/dist/components/content/favorites.js.map +1 -1
- package/dist/components/content/grid.d.ts +11 -4
- package/dist/components/content/grid.js +52 -43
- package/dist/components/content/grid.js.map +1 -1
- package/dist/components/content/history.js +83 -63
- package/dist/components/content/history.js.map +1 -1
- package/dist/components/content/index.d.ts +5 -3
- package/dist/components/content/listing.d.ts +14 -3
- package/dist/components/content/listing.js +65 -56
- package/dist/components/content/listing.js.map +1 -1
- package/dist/components/content/offer-fields.js +6 -6
- package/dist/components/content/paged-grid.d.ts +10 -0
- package/dist/components/content/paged-grid.js +116 -0
- package/dist/components/content/paged-grid.js.map +1 -0
- package/dist/components/content/pagination.d.ts +19 -0
- package/dist/components/content/pagination.js +97 -0
- package/dist/components/content/pagination.js.map +1 -0
- package/dist/components/content/paywall.d.ts +9 -0
- package/dist/components/content/paywall.js +27 -3
- package/dist/components/content/paywall.js.map +1 -1
- package/dist/components/content/post-list.d.ts +37 -0
- package/dist/components/content/post-list.js +203 -0
- package/dist/components/content/post-list.js.map +1 -0
- package/dist/components/content/reader-settings.d.ts +2 -0
- package/dist/components/content/reader-settings.js +1 -1
- package/dist/components/content/reader-settings.js.map +1 -1
- package/dist/components/groups/banner.d.ts +6 -2
- package/dist/components/groups/banner.js +79 -61
- package/dist/components/groups/banner.js.map +1 -1
- package/dist/components/groups/directory.d.ts +12 -2
- package/dist/components/groups/directory.js +168 -105
- package/dist/components/groups/directory.js.map +1 -1
- package/dist/components/groups/editor.js +10 -10
- package/dist/components/groups/editor.js.map +1 -1
- package/dist/components/groups/entity-chips.d.ts +61 -0
- package/dist/components/groups/entity-chips.js +382 -0
- package/dist/components/groups/entity-chips.js.map +1 -0
- package/dist/components/groups/group-members.d.ts +29 -0
- package/dist/components/groups/group-members.js +474 -0
- package/dist/components/groups/group-members.js.map +1 -0
- package/dist/components/groups/header.js +53 -45
- package/dist/components/groups/header.js.map +1 -1
- package/dist/components/groups/index.d.ts +3 -1
- package/dist/components/groups/memberships.d.ts +3 -36
- package/dist/components/groups/memberships.js +105 -480
- package/dist/components/groups/memberships.js.map +1 -1
- package/dist/components/groups/scoped.d.ts +23 -0
- package/dist/components/groups/scoped.js +92 -0
- package/dist/components/groups/scoped.js.map +1 -0
- package/dist/components/media-folder-editor.d.ts +5 -0
- package/dist/components/media-folder-editor.js +418 -230
- package/dist/components/media-folder-editor.js.map +1 -1
- package/dist/components/sortable-list.d.ts +5 -1
- package/dist/components/sortable-list.js +97 -81
- package/dist/components/sortable-list.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +41 -37
- package/dist/locales/admin/de.js +78 -45
- package/dist/locales/admin/de.js.map +1 -1
- package/dist/locales/admin/en.d.ts +74 -41
- package/dist/locales/admin/en.js +78 -45
- package/dist/locales/admin/en.js.map +1 -1
- package/dist/locales/admin/es.js +78 -45
- package/dist/locales/admin/es.js.map +1 -1
- package/dist/locales/admin/ja.js +78 -45
- package/dist/locales/admin/ja.js.map +1 -1
- package/dist/locales/admin/ko.js +78 -45
- package/dist/locales/admin/ko.js.map +1 -1
- package/dist/locales/admin/zh.js +78 -45
- package/dist/locales/admin/zh.js.map +1 -1
- package/dist/locales/de.js +1 -0
- package/dist/locales/de.js.map +1 -1
- package/dist/locales/discovery/de.js +25 -1
- package/dist/locales/discovery/de.js.map +1 -1
- package/dist/locales/discovery/en.d.ts +24 -0
- package/dist/locales/discovery/en.js +25 -1
- package/dist/locales/discovery/en.js.map +1 -1
- package/dist/locales/discovery/es.js +25 -1
- package/dist/locales/discovery/es.js.map +1 -1
- package/dist/locales/discovery/ja.js +25 -1
- package/dist/locales/discovery/ja.js.map +1 -1
- package/dist/locales/discovery/ko.js +25 -1
- package/dist/locales/discovery/ko.js.map +1 -1
- package/dist/locales/discovery/zh.js +25 -1
- package/dist/locales/discovery/zh.js.map +1 -1
- package/dist/locales/en.d.ts +119 -43
- 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/groups/de.js +22 -4
- package/dist/locales/groups/de.js.map +1 -1
- package/dist/locales/groups/en.d.ts +20 -2
- package/dist/locales/groups/en.js +22 -4
- package/dist/locales/groups/en.js.map +1 -1
- package/dist/locales/groups/es.js +22 -4
- package/dist/locales/groups/es.js.map +1 -1
- package/dist/locales/groups/ja.js +22 -4
- package/dist/locales/groups/ja.js.map +1 -1
- package/dist/locales/groups/ko.js +22 -4
- package/dist/locales/groups/ko.js.map +1 -1
- package/dist/locales/groups/zh.js +22 -4
- package/dist/locales/groups/zh.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/discovery.d.ts +16 -0
- package/dist/react/discovery.js +69 -37
- package/dist/react/discovery.js.map +1 -1
- package/dist/react/groups.d.ts +19 -3
- package/dist/react/groups.js +49 -22
- package/dist/react/groups.js.map +1 -1
- package/dist/react/index.d.ts +6 -4
- package/dist/react/page-notices.d.ts +13 -0
- package/dist/react/page-notices.js +38 -0
- package/dist/react/page-notices.js.map +1 -0
- package/dist/react/post-reads.d.ts +23 -0
- package/dist/react/post-reads.js +90 -0
- package/dist/react/post-reads.js.map +1 -0
- package/dist/react/remembered-sort.d.ts +5 -0
- package/dist/react/remembered-sort.js +29 -0
- package/dist/react/remembered-sort.js.map +1 -0
- package/dist/react/resources.js +2 -2
- package/dist/react/resources.js.map +1 -1
- package/dist/react/stats.d.ts +3 -7
- package/dist/react/stats.js +2 -6
- package/dist/react/stats.js.map +1 -1
- package/dist/react/use-engagement.d.ts +2 -0
- package/dist/react/use-engagement.js +8 -3
- package/dist/react/use-engagement.js.map +1 -1
- package/dist/react.js +29 -27
- package/dist/styles.css +1 -1
- package/dist/urls/index.d.ts +1 -1
- package/dist/urls.js +2 -2
- package/dist/urls.js.map +1 -1
- package/package.json +1 -1
- package/dist/components/admin/page-review.d.ts +0 -13
- package/dist/components/admin/page-review.js +0 -225
- package/dist/components/admin/page-review.js.map +0 -1
- package/dist/react/page-review.d.ts +0 -14
- package/dist/react/page-review.js +0 -37
- package/dist/react/page-review.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resources.js","names":[],"sources":["../../src/react/resources.ts"],"sourcesContent":["import type { ContentKitClient } from \"../client/client.js\";\nimport { toContentKitError, type ContentKitError } from \"../client/errors.js\";\nimport { sameTarget, type Target } from \"../client/content/types.js\";\nimport type { Card, Comment, Content, Item, Poll } from \"../client/generated/wire.js\";\nimport type { ContentKitChange } from \"../client/http.js\";\n\n/** One shared read: its data, whether a fetch is in flight, and its last failure. */\nexport interface Resource<T> {\n data: T | undefined;\n loading: boolean;\n /** A fetch or set has completed. */\n loaded: boolean;\n /** A list's next page is in flight. */\n loadingMore?: boolean;\n error?: ContentKitError;\n}\n\n/** A page of a list and how to get the next one (null: no more). */\nexport interface Page<T, M = unknown> {\n items: T[];\n next: string | number | null;\n /** Metadata belongs to the first page of this selection. */\n metadata?: M;\n}\n\nexport type Fetcher<T> = (signal: AbortSignal) => Promise<T>;\n\n/** What a slot holds, for matching changes: its resource type and the ids it depends on. */\nexport interface Tag {\n type: string;\n readScope?: string;\n target?: Target;\n language?: string;\n id?: string;\n scope?: string;\n}\n\ninterface Slot {\n tag: Tag;\n listeners: Set<() => void>;\n entry: Resource<unknown>;\n bindings: Map<symbol, Binding>;\n ctl?: AbortController;\n moreCtl?: AbortController;\n /** Bumped by mark(): an optimistic write rolls back only if no newer one started. */\n version: number;\n /** Identifies the full read; item patches and appended pages retain it. */\n readVersion: number;\n}\n\ninterface Binding {\n fetcher: Fetcher<unknown>;\n more?: (cursor: string | number, signal: AbortSignal) => Promise<Page<unknown>>;\n}\n\nconst activeBinding = (s: Slot) => [...s.bindings.values()].at(-1);\n\nconst IDLE: Resource<never> = { data: undefined, loading: false, loaded: false };\n\n/**\n * The content reads hooks share, per client: one request per key while any\n * hook shows it, optimistic writes with rollback, and every change the client\n * makes applied in place (or refetched) for every hook showing it.\n */\nexport class ResourceStore {\n private readonly slots = new Map<string, Slot>();\n // Tokens must remain unique when a scope leaves and its slots are recreated.\n private version = 0;\n\n constructor(client: ContentKitClient) {\n client.subscribe((c, scope) => this.apply(c, scope));\n }\n\n subscribe(key: string, tag: Tag, listener: () => void): () => void {\n let s = this.slots.get(key);\n if (!s) this.slots.set(key, (s = { tag, listeners: new Set(), bindings: new Map(), entry: IDLE, version: 0, readVersion: ++this.version }));\n const slot = s;\n slot.listeners.add(listener);\n return () => {\n slot.listeners.delete(listener);\n // Kept across a StrictMode remount; dropped once no hook shows it.\n queueMicrotask(() => {\n if (slot.listeners.size || this.slots.get(key) !== slot) return;\n slot.ctl?.abort();\n slot.moreCtl?.abort();\n this.slots.delete(key);\n });\n };\n }\n\n snapshot<T>(key: string): Resource<T> {\n return (this.slots.get(key)?.entry ?? IDLE) as Resource<T>;\n }\n\n /** Registers one subscriber's key-bound fetchers; cleanup releases only it. */\n bind(key: string, fetcher: Fetcher<unknown>, more?: Binding[\"more\"]): () => void {\n const s = this.slots.get(key);\n if (!s) return () => {};\n const owner = Symbol();\n s.bindings.set(owner, { fetcher, more });\n return () => { s.bindings.delete(owner); };\n }\n\n /** Fetches unless loaded or in flight; force refetches (a list from its first page). */\n load(key: string, force = false): void {\n const s = this.slots.get(key);\n const binding = s && activeBinding(s);\n if (!s || !binding || (!force && (s.entry.loaded || s.ctl))) return;\n s.ctl?.abort();\n s.moreCtl?.abort();\n s.moreCtl = undefined;\n const ctl = (s.ctl = new AbortController());\n this.update(s, { loading: true, loadingMore: false, error: undefined });\n binding.fetcher(ctl.signal).then(\n (data) => {\n if (s.ctl !== ctl) return;\n s.ctl = undefined;\n s.readVersion = ++this.version;\n this.update(s, { data, loading: false, loaded: true, error: undefined });\n },\n (e) => {\n if (s.ctl !== ctl) return;\n s.ctl = undefined;\n const error = toContentKitError(e);\n if (error.code !== \"aborted\") this.update(s, { loading: false, loaded: true, error });\n },\n );\n }\n\n /** Appends a list's next page; resolves once it landed. */\n async loadMore(key: string): Promise<void> {\n const s = this.slots.get(key);\n const binding = s && activeBinding(s);\n const page = s?.entry.data as Page<{ id: string }> | undefined;\n if (!s || !binding?.more || !page || page.next === null || s.moreCtl || s.ctl) return;\n const ctl = (s.moreCtl = new AbortController());\n this.update(s, { loadingMore: true });\n try {\n const next = (await binding.more(page.next, ctl.signal)) as Page<{ id: string }>;\n if (s.moreCtl !== ctl) return;\n const cur = s.entry.data as Page<{ id: string }>;\n const seen = new Set(cur.items.map((x) => x.id));\n this.update(s, { data: { ...cur, items: [...cur.items, ...next.items.filter((x) => !seen.has(x.id))], next: next.next }, error: undefined });\n } catch (e) {\n const error = toContentKitError(e);\n if (s.moreCtl === ctl && error.code !== \"aborted\") this.update(s, { error });\n } finally {\n if (s.moreCtl === ctl) {\n s.moreCtl = undefined;\n this.update(s, { loadingMore: false });\n }\n }\n }\n\n /** Replaces a key's data for every hook showing it. */\n set<T>(key: string, data: T | ((prev: T | undefined) => T)): void {\n const s = this.slots.get(key);\n if (!s) return;\n const next = typeof data === \"function\" ? (data as (p: T | undefined) => T)(s.entry.data as T | undefined) : data;\n s.readVersion = ++this.version;\n this.update(s, { data: next, loaded: true, error: undefined });\n }\n\n /** Starts an optimistic write on key: the token rollback() checks. */\n mark(key: string): number {\n const s = this.slots.get(key);\n return s ? (s.version = ++this.version) : 0;\n }\n\n /** Restores data unless a newer optimistic write started since mark(). */\n rollback<T>(key: string, token: number, data: T): void {\n const s = this.slots.get(key);\n if (s && s.version === token) this.update(s, { data, loaded: true });\n }\n\n /** Applies fn to matching slots, with the read identity for scoped rollback. */\n patch<T>(match: (tag: Tag) => boolean, fn: (data: T, readVersion: number) => T): void {\n for (const s of this.slots.values()) {\n if (s.entry.data !== undefined && match(s.tag)) this.update(s, { data: fn(s.entry.data as T, s.readVersion) });\n }\n }\n\n /** Refetches every shown slot whose tag matches. */\n invalidate(match: (tag: Tag) => boolean): void {\n for (const [key, s] of this.slots) if (match(s.tag) && s.listeners.size) this.load(key, true);\n }\n\n private apply(c: ContentKitChange, scope: string | null): void {\n const patch = <T>(match: (tag: Tag) => boolean, fn: (data: T) => T) => {\n this.patch<T>((t) => match(t) && (t.readScope ?? \"{}\") === scope, fn);\n this.invalidate((t) => match(t) && (t.readScope ?? \"{}\") !== scope);\n };\n const items = <T extends { id: string }>(types: string[], fn: (item: T) => T | null) =>\n patch<Page<T>>(\n (t) => types.includes(t.type),\n (page) => {\n let changed = false;\n const out: T[] = [];\n for (const it of page.items) {\n const next = fn(it);\n if (next !== it) changed = true;\n if (next) out.push(next);\n }\n return changed ? { ...page, items: out } : page;\n },\n );\n const comments = [\"comments\", \"replies\", \"latest\", \"admin-comments\"];\n // Cards in lists (pages) and rails (arrays) show a content's counts and the viewer's state.\n const cards = (id: string, fn: (card: Card) => Card) =>\n patch<Page<Card> | Card[]>(\n (t) => t.type === \"cards\" || t.type === \"history\",\n (data) => {\n const each = (list: Card[]) => (list.some((x) => x.id === id) ? list.map((x) => (x.id === id ? fn(x) : x)) : list);\n if (Array.isArray(data)) return each(data);\n const items = each(data.items);\n return items === data.items ? data : { ...data, items };\n },\n );\n switch (c.type) {\n case \"media.committed\":\n case \"media.processed\":\n this.invalidate((t) => t.type === \"page-review\" && t.id === c.ref.version);\n break;\n case \"notifications.updated\":\n this.updateNotification(c.item, scope);\n break;\n case \"notifications.changed\":\n this.invalidate((t) => t.type === \"notifications\");\n break;\n case \"comment.created\": {\n const parent = c.comment.reply_to_id;\n const published = !c.comment.moderation;\n const thread = (t: Tag) => sameTarget(t.target, c.target) && (!t.language || t.language === c.comment.language);\n const add = (t: Tag) => (parent ? t.type === \"replies\" && t.id === parent : t.type === \"comments\" && thread(t));\n patch<Page<Comment>>(add, (p) => (p.items.some((x) => x.id === c.comment.id) ? p : { ...p, items: parent ? [...p.items, c.comment] : [c.comment, ...p.items] }));\n if (parent && published) items<Comment>([\"comments\"], (x) => (x.id === parent ? { ...x, reply_count: x.reply_count + 1 } : x));\n this.invalidate((t) => t.type === \"latest\" || t.type === \"admin-comments\");\n if (!parent) this.invalidate((t) => t.type === \"standing\" && thread(t));\n break;\n }\n case \"comment.updated\":\n items<Comment>(comments, (x) => (x.id === c.comment.id ? { ...x, ...c.comment, author: c.comment.author ?? x.author } : x));\n this.invalidate((t) => t.type === \"standing\");\n break;\n case \"comment.reacted\":\n items<Comment>(comments, (x) => (x.id === c.id ? { ...x, ...c.counts } : x));\n break;\n case \"comment.deleted\": {\n let parent: string | undefined;\n items<Comment>([\"comments\", \"replies\"], (x) => {\n if (x.id !== c.id || x.deleted) return x;\n if (!x.moderation) parent = x.reply_to_id;\n return { ...x, deleted: true, body: \"[deleted]\", author: undefined, user_id: undefined, anon_name: undefined };\n });\n if (parent) items<Comment>([\"comments\"], (x) => (x.id === parent ? { ...x, reply_count: Math.max(0, x.reply_count - 1) } : x));\n items<Comment>([\"latest\"], (x) => (x.id === c.id ? null : x));\n items<Comment & { deleted_at?: string }>([\"admin-comments\"], (x) => (x.id === c.id ? { ...x, deleted: true, deleted_at: new Date().toISOString() } : x));\n this.invalidate((t) => t.type === \"standing\");\n break;\n }\n case \"comment.restored\":\n this.invalidate((t) => comments.includes(t.type) || t.type === \"standing\");\n break;\n case \"reaction.changed\":\n patch((t) => t.type === \"reaction\" && sameTarget(t.target, c.target), () => c.counts);\n if (c.target.type === \"contents\") {\n cards(c.target.id, (x) => ({ ...x, counts: { ...x.counts, likes: c.counts.likes, dislikes: c.counts.dislikes } }));\n this.invalidate((t) => t.type === \"daily-releases\");\n }\n this.invalidate((t) => t.type === \"reactions\");\n break;\n case \"favorite.changed\":\n patch((t) => t.type === \"favorite\" && sameTarget(t.target, c.target), () => ({ favorited: c.favorited, count: c.count }));\n if (c.target.type === \"contents\") {\n cards(c.target.id, (x) => ({ ...x, counts: { ...x.counts, favorites: c.count }, viewer: x.viewer && { ...x.viewer, favorited: c.favorited } }));\n this.invalidate((t) => t.type === \"daily-releases\");\n }\n this.invalidate((t) => t.type === \"favorites\");\n break;\n case \"content.saved\":\n patch<Content | null>((t) => t.type === \"content\" && t.id === c.content.id, () => c.content);\n // A state, kind or text change can move it between lists: refetch them.\n this.invalidate((t) => t.type === \"contents\" || t.type === \"cards\" || t.type === \"daily-releases\" || (t.type === \"content-detail\" && t.id === c.content.id));\n this.invalidate((t) => t.type === \"standing\" && t.target?.type === \"contents\" && t.target.id === c.content.id);\n break;\n case \"content.changed\":\n this.invalidate((t) => ((t.type === \"content\" || t.type === \"content-detail\") && t.id === c.id) || t.type === \"contents\" || t.type === \"cards\" || t.type === \"daily-releases\");\n this.invalidate((t) => t.type === \"standing\" && t.target?.type === \"contents\" && t.target.id === c.id);\n break;\n case \"content.deleted\":\n items<{ id: string }>([\"contents\", \"cards\", \"history\"], (x) => (x.id === c.id ? null : x));\n this.invalidate((t) => ((t.type === \"content\" || t.type === \"content-detail\") && t.id === c.id) || (t.type === \"contents\" && t.scope === \"deleted\") || t.type === \"daily-releases\");\n this.invalidate((t) => t.type === \"standing\" && t.target?.type === \"contents\" && t.target.id === c.id);\n break;\n case \"poll.created\":\n this.invalidate((t) => t.type === \"polls\" || (t.type === \"poll\" && !t.id));\n break;\n case \"poll.updated\":\n // A latest-poll slot has no id in its tag: match by the poll it holds.\n patch<Poll | null>((t) => t.type === \"poll\", (p) => (p?.id === c.poll.id ? c.poll : p));\n items<Poll>([\"polls\"], (x) => (x.id === c.poll.id ? c.poll : x));\n break;\n case \"poll.changed\":\n this.invalidate((t) => (t.type === \"poll\" && (t.id === c.id || !t.id)) || t.type === \"polls\");\n break;\n case \"poll.deleted\":\n items<Poll>([\"polls\"], (x) => (x.id === c.id ? null : x));\n this.invalidate((t) => t.type === \"poll\" && !t.id);\n break;\n case \"ban.saved\":\n case \"ban.lifted\":\n this.invalidate((t) => (t.type === \"bans\" && t.scope === c.scope) || t.type === \"standing\");\n break;\n case \"entities.changed\":\n this.invalidate((t) => t.type.startsWith(\"group\") || t.type === \"daily-releases\");\n break;\n case \"contentFilters.changed\":\n this.invalidate((t) => t.type === \"cards\" || t.type === \"history\" || t.type === \"release-dates\" || t.type === \"daily-releases\");\n break;\n case \"access.changed\":\n this.invalidate((t) => t.type === \"cards\" || t.type === \"history\" || t.type === \"content-detail\" || t.type === \"reader-plan\" || t.type.startsWith(\"group\") || t.type === \"daily-releases\" || t.type === \"standing\");\n break;\n case \"engagement.changed\":\n this.invalidate((t) => t.type === \"history\" || t.type === \"cards\" || t.type === \"daily-releases\" || t.type === \"standing\" || t.type.startsWith(\"stats\") || (t.type === \"content-detail\" && !!t.id && c.ids.includes(t.id)));\n break;\n case \"history.changed\":\n this.invalidate((t) => t.type === \"history\" || t.type === \"cards\" || t.type === \"daily-releases\");\n break;\n case \"searches.changed\":\n this.invalidate((t) => t.type === \"searches\");\n break;\n case \"rules.changed\":\n this.invalidate((t) => t.type === \"moderation-rules\" && t.scope === c.rules);\n break;\n case \"reader.changed\":\n this.invalidate((t) => (t.type === \"reader-plan\" || t.type === \"page-review\") && t.id === c.version);\n break;\n case \"moderation.resolved\":\n items<{ id: string }>([\"held\"], (x) => (x.id === c.id ? null : x));\n if (c.kind === \"comment\") this.invalidate((t) => comments.includes(t.type) || t.type === \"standing\");\n else this.invalidate((t) => (t.type === \"content\" && t.id === c.id) || t.type === \"contents\");\n break;\n }\n }\n\n // Preserve loaded pages and canonical receipt revisions. Reads that started\n // before a successful mutation cannot overwrite it when they finish later.\n private updateNotification(item: Item, scope: string | null): void {\n for (const [key, slot] of this.slots) {\n if (slot.tag.type !== \"notifications\" || !slot.listeners.size) continue;\n const page = slot.entry.data as Page<Item> | undefined;\n const cached = page?.items.find((n) => n.id === item.id);\n // Mutations from separate mounted views can complete out of order.\n // In particular, an old active receipt must not evict a newer archive.\n if ((slot.tag.readScope ?? \"{}\") === scope && cached && cached.revision > item.revision) continue;\n const archived = slot.tag.scope?.startsWith(\"archived:\") ?? false;\n const matches = archived === !!item.dismissed_at;\n if ((slot.tag.readScope ?? \"{}\") !== scope || !page || slot.tag.scope?.endsWith(\":unread\") || (matches && !page.items.some((n) => n.id === item.id))) {\n this.load(key, true);\n continue;\n }\n if (!matches && !page.items.some((n) => n.id === item.id)) continue;\n slot.ctl?.abort();\n slot.moreCtl?.abort();\n slot.ctl = slot.moreCtl = undefined;\n this.update(slot, { loading: false, loadingMore: false, data: { ...page, items: page.items.flatMap((n) => n.id === item.id ? matches ? [item] : [] : [n]) } });\n }\n }\n\n // A new entry per change: useSyncExternalStore compares snapshots by identity.\n private update(s: Slot, patch: Partial<Resource<unknown>>): void {\n s.entry = { ...s.entry, ...patch };\n for (const l of s.listeners) l();\n }\n}\n\nconst stores = new WeakMap<ContentKitClient, ResourceStore>();\n\n/** The client's shared content store. */\nexport function resourcesFor(client: ContentKitClient): ResourceStore {\n let s = stores.get(client);\n if (!s) stores.set(client, (s = new ResourceStore(client)));\n return s;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"resources.js","names":[],"sources":["../../src/react/resources.ts"],"sourcesContent":["import type { ContentKitClient } from \"../client/client.js\";\nimport { toContentKitError, type ContentKitError } from \"../client/errors.js\";\nimport { sameTarget, type Target } from \"../client/content/types.js\";\nimport type { Card, Comment, Content, Item, Poll } from \"../client/generated/wire.js\";\nimport type { ContentKitChange } from \"../client/http.js\";\n\n/** One shared read: its data, whether a fetch is in flight, and its last failure. */\nexport interface Resource<T> {\n data: T | undefined;\n loading: boolean;\n /** A fetch or set has completed. */\n loaded: boolean;\n /** A list's next page is in flight. */\n loadingMore?: boolean;\n error?: ContentKitError;\n}\n\n/** A page of a list and how to get the next one (null: no more). */\nexport interface Page<T, M = unknown> {\n items: T[];\n next: string | number | null;\n /** Metadata belongs to the first page of this selection. */\n metadata?: M;\n}\n\nexport type Fetcher<T> = (signal: AbortSignal) => Promise<T>;\n\n/** What a slot holds, for matching changes: its resource type and the ids it depends on. */\nexport interface Tag {\n type: string;\n readScope?: string;\n target?: Target;\n language?: string;\n id?: string;\n scope?: string;\n}\n\ninterface Slot {\n tag: Tag;\n listeners: Set<() => void>;\n entry: Resource<unknown>;\n bindings: Map<symbol, Binding>;\n ctl?: AbortController;\n moreCtl?: AbortController;\n /** Bumped by mark(): an optimistic write rolls back only if no newer one started. */\n version: number;\n /** Identifies the full read; item patches and appended pages retain it. */\n readVersion: number;\n}\n\ninterface Binding {\n fetcher: Fetcher<unknown>;\n more?: (cursor: string | number, signal: AbortSignal) => Promise<Page<unknown>>;\n}\n\nconst activeBinding = (s: Slot) => [...s.bindings.values()].at(-1);\n\nconst IDLE: Resource<never> = { data: undefined, loading: false, loaded: false };\n\n/**\n * The content reads hooks share, per client: one request per key while any\n * hook shows it, optimistic writes with rollback, and every change the client\n * makes applied in place (or refetched) for every hook showing it.\n */\nexport class ResourceStore {\n private readonly slots = new Map<string, Slot>();\n // Tokens must remain unique when a scope leaves and its slots are recreated.\n private version = 0;\n\n constructor(client: ContentKitClient) {\n client.subscribe((c, scope) => this.apply(c, scope));\n }\n\n subscribe(key: string, tag: Tag, listener: () => void): () => void {\n let s = this.slots.get(key);\n if (!s) this.slots.set(key, (s = { tag, listeners: new Set(), bindings: new Map(), entry: IDLE, version: 0, readVersion: ++this.version }));\n const slot = s;\n slot.listeners.add(listener);\n return () => {\n slot.listeners.delete(listener);\n // Kept across a StrictMode remount; dropped once no hook shows it.\n queueMicrotask(() => {\n if (slot.listeners.size || this.slots.get(key) !== slot) return;\n slot.ctl?.abort();\n slot.moreCtl?.abort();\n this.slots.delete(key);\n });\n };\n }\n\n snapshot<T>(key: string): Resource<T> {\n return (this.slots.get(key)?.entry ?? IDLE) as Resource<T>;\n }\n\n /** Registers one subscriber's key-bound fetchers; cleanup releases only it. */\n bind(key: string, fetcher: Fetcher<unknown>, more?: Binding[\"more\"]): () => void {\n const s = this.slots.get(key);\n if (!s) return () => {};\n const owner = Symbol();\n s.bindings.set(owner, { fetcher, more });\n return () => { s.bindings.delete(owner); };\n }\n\n /** Fetches unless loaded or in flight; force refetches (a list from its first page). */\n load(key: string, force = false): void {\n const s = this.slots.get(key);\n const binding = s && activeBinding(s);\n if (!s || !binding || (!force && (s.entry.loaded || s.ctl))) return;\n s.ctl?.abort();\n s.moreCtl?.abort();\n s.moreCtl = undefined;\n const ctl = (s.ctl = new AbortController());\n this.update(s, { loading: true, loadingMore: false, error: undefined });\n binding.fetcher(ctl.signal).then(\n (data) => {\n if (s.ctl !== ctl) return;\n s.ctl = undefined;\n s.readVersion = ++this.version;\n this.update(s, { data, loading: false, loaded: true, error: undefined });\n },\n (e) => {\n if (s.ctl !== ctl) return;\n s.ctl = undefined;\n const error = toContentKitError(e);\n if (error.code !== \"aborted\") this.update(s, { loading: false, loaded: true, error });\n },\n );\n }\n\n /** Appends a list's next page; resolves once it landed. */\n async loadMore(key: string): Promise<void> {\n const s = this.slots.get(key);\n const binding = s && activeBinding(s);\n const page = s?.entry.data as Page<{ id: string }> | undefined;\n if (!s || !binding?.more || !page || page.next === null || s.moreCtl || s.ctl) return;\n const ctl = (s.moreCtl = new AbortController());\n this.update(s, { loadingMore: true });\n try {\n const next = (await binding.more(page.next, ctl.signal)) as Page<{ id: string }>;\n if (s.moreCtl !== ctl) return;\n const cur = s.entry.data as Page<{ id: string }>;\n const seen = new Set(cur.items.map((x) => x.id));\n this.update(s, { data: { ...cur, items: [...cur.items, ...next.items.filter((x) => !seen.has(x.id))], next: next.next }, error: undefined });\n } catch (e) {\n const error = toContentKitError(e);\n if (s.moreCtl === ctl && error.code !== \"aborted\") this.update(s, { error });\n } finally {\n if (s.moreCtl === ctl) {\n s.moreCtl = undefined;\n this.update(s, { loadingMore: false });\n }\n }\n }\n\n /** Replaces a key's data for every hook showing it. */\n set<T>(key: string, data: T | ((prev: T | undefined) => T)): void {\n const s = this.slots.get(key);\n if (!s) return;\n const next = typeof data === \"function\" ? (data as (p: T | undefined) => T)(s.entry.data as T | undefined) : data;\n s.readVersion = ++this.version;\n this.update(s, { data: next, loaded: true, error: undefined });\n }\n\n /** Starts an optimistic write on key: the token rollback() checks. */\n mark(key: string): number {\n const s = this.slots.get(key);\n return s ? (s.version = ++this.version) : 0;\n }\n\n /** Restores data unless a newer optimistic write started since mark(). */\n rollback<T>(key: string, token: number, data: T): void {\n const s = this.slots.get(key);\n if (s && s.version === token) this.update(s, { data, loaded: true });\n }\n\n /** Applies fn to matching slots, with the read identity for scoped rollback. */\n patch<T>(match: (tag: Tag) => boolean, fn: (data: T, readVersion: number) => T): void {\n for (const s of this.slots.values()) {\n if (s.entry.data !== undefined && match(s.tag)) this.update(s, { data: fn(s.entry.data as T, s.readVersion) });\n }\n }\n\n /** Refetches every shown slot whose tag matches. */\n invalidate(match: (tag: Tag) => boolean): void {\n for (const [key, s] of this.slots) if (match(s.tag) && s.listeners.size) this.load(key, true);\n }\n\n private apply(c: ContentKitChange, scope: string | null): void {\n const patch = <T>(match: (tag: Tag) => boolean, fn: (data: T) => T) => {\n this.patch<T>((t) => match(t) && (t.readScope ?? \"{}\") === scope, fn);\n this.invalidate((t) => match(t) && (t.readScope ?? \"{}\") !== scope);\n };\n const items = <T extends { id: string }>(types: string[], fn: (item: T) => T | null) =>\n patch<Page<T>>(\n (t) => types.includes(t.type),\n (page) => {\n let changed = false;\n const out: T[] = [];\n for (const it of page.items) {\n const next = fn(it);\n if (next !== it) changed = true;\n if (next) out.push(next);\n }\n return changed ? { ...page, items: out } : page;\n },\n );\n const comments = [\"comments\", \"replies\", \"latest\", \"admin-comments\"];\n // Cards in lists (pages) and rails (arrays) show a content's counts and the viewer's state.\n const cards = (id: string, fn: (card: Card) => Card) =>\n patch<Page<Card> | Card[]>(\n (t) => t.type === \"cards\" || t.type === \"history\",\n (data) => {\n const each = (list: Card[]) => (list.some((x) => x.id === id) ? list.map((x) => (x.id === id ? fn(x) : x)) : list);\n if (Array.isArray(data)) return each(data);\n const items = each(data.items);\n return items === data.items ? data : { ...data, items };\n },\n );\n switch (c.type) {\n case \"media.committed\":\n case \"media.processed\":\n this.invalidate((t) => t.type === \"page-notices\" && t.id === c.ref.version);\n break;\n case \"notifications.updated\":\n this.updateNotification(c.item, scope);\n break;\n case \"notifications.changed\":\n this.invalidate((t) => t.type === \"notifications\");\n break;\n case \"comment.created\": {\n const parent = c.comment.reply_to_id;\n const published = !c.comment.moderation;\n const thread = (t: Tag) => sameTarget(t.target, c.target) && (!t.language || t.language === c.comment.language);\n const add = (t: Tag) => (parent ? t.type === \"replies\" && t.id === parent : t.type === \"comments\" && thread(t));\n patch<Page<Comment>>(add, (p) => (p.items.some((x) => x.id === c.comment.id) ? p : { ...p, items: parent ? [...p.items, c.comment] : [c.comment, ...p.items] }));\n if (parent && published) items<Comment>([\"comments\"], (x) => (x.id === parent ? { ...x, reply_count: x.reply_count + 1 } : x));\n this.invalidate((t) => t.type === \"latest\" || t.type === \"admin-comments\");\n if (!parent) this.invalidate((t) => t.type === \"standing\" && thread(t));\n break;\n }\n case \"comment.updated\":\n items<Comment>(comments, (x) => (x.id === c.comment.id ? { ...x, ...c.comment, author: c.comment.author ?? x.author } : x));\n this.invalidate((t) => t.type === \"standing\");\n break;\n case \"comment.reacted\":\n items<Comment>(comments, (x) => (x.id === c.id ? { ...x, ...c.counts } : x));\n break;\n case \"comment.deleted\": {\n let parent: string | undefined;\n items<Comment>([\"comments\", \"replies\"], (x) => {\n if (x.id !== c.id || x.deleted) return x;\n if (!x.moderation) parent = x.reply_to_id;\n return { ...x, deleted: true, body: \"[deleted]\", author: undefined, user_id: undefined, anon_name: undefined };\n });\n if (parent) items<Comment>([\"comments\"], (x) => (x.id === parent ? { ...x, reply_count: Math.max(0, x.reply_count - 1) } : x));\n items<Comment>([\"latest\"], (x) => (x.id === c.id ? null : x));\n items<Comment & { deleted_at?: string }>([\"admin-comments\"], (x) => (x.id === c.id ? { ...x, deleted: true, deleted_at: new Date().toISOString() } : x));\n this.invalidate((t) => t.type === \"standing\");\n break;\n }\n case \"comment.restored\":\n this.invalidate((t) => comments.includes(t.type) || t.type === \"standing\");\n break;\n case \"reaction.changed\":\n patch((t) => t.type === \"reaction\" && sameTarget(t.target, c.target), () => c.counts);\n if (c.target.type === \"contents\") {\n cards(c.target.id, (x) => ({ ...x, counts: { ...x.counts, likes: c.counts.likes, dislikes: c.counts.dislikes } }));\n this.invalidate((t) => t.type === \"daily-releases\");\n }\n this.invalidate((t) => t.type === \"reactions\");\n break;\n case \"favorite.changed\":\n patch((t) => t.type === \"favorite\" && sameTarget(t.target, c.target), () => ({ favorited: c.favorited, count: c.count }));\n if (c.target.type === \"contents\") {\n cards(c.target.id, (x) => ({ ...x, counts: { ...x.counts, favorites: c.count }, viewer: x.viewer && { ...x.viewer, favorited: c.favorited } }));\n this.invalidate((t) => t.type === \"daily-releases\");\n }\n this.invalidate((t) => t.type === \"favorites\");\n break;\n case \"content.saved\":\n patch<Content | null>((t) => t.type === \"content\" && t.id === c.content.id, () => c.content);\n // A state, kind or text change can move it between lists: refetch them.\n this.invalidate((t) => t.type === \"contents\" || t.type === \"cards\" || t.type === \"daily-releases\" || (t.type === \"content-detail\" && t.id === c.content.id));\n this.invalidate((t) => t.type === \"standing\" && t.target?.type === \"contents\" && t.target.id === c.content.id);\n break;\n case \"content.changed\":\n this.invalidate((t) => ((t.type === \"content\" || t.type === \"content-detail\") && t.id === c.id) || t.type === \"contents\" || t.type === \"cards\" || t.type === \"daily-releases\");\n this.invalidate((t) => t.type === \"standing\" && t.target?.type === \"contents\" && t.target.id === c.id);\n break;\n case \"content.deleted\":\n items<{ id: string }>([\"contents\", \"cards\", \"history\"], (x) => (x.id === c.id ? null : x));\n this.invalidate((t) => ((t.type === \"content\" || t.type === \"content-detail\") && t.id === c.id) || (t.type === \"contents\" && t.scope === \"deleted\") || t.type === \"daily-releases\");\n this.invalidate((t) => t.type === \"standing\" && t.target?.type === \"contents\" && t.target.id === c.id);\n break;\n case \"poll.created\":\n this.invalidate((t) => t.type === \"polls\" || (t.type === \"poll\" && !t.id));\n break;\n case \"poll.updated\":\n // A latest-poll slot has no id in its tag: match by the poll it holds.\n patch<Poll | null>((t) => t.type === \"poll\", (p) => (p?.id === c.poll.id ? c.poll : p));\n items<Poll>([\"polls\"], (x) => (x.id === c.poll.id ? c.poll : x));\n break;\n case \"poll.changed\":\n this.invalidate((t) => (t.type === \"poll\" && (t.id === c.id || !t.id)) || t.type === \"polls\");\n break;\n case \"poll.deleted\":\n items<Poll>([\"polls\"], (x) => (x.id === c.id ? null : x));\n this.invalidate((t) => t.type === \"poll\" && !t.id);\n break;\n case \"ban.saved\":\n case \"ban.lifted\":\n this.invalidate((t) => (t.type === \"bans\" && t.scope === c.scope) || t.type === \"standing\");\n break;\n case \"entities.changed\":\n this.invalidate((t) => t.type.startsWith(\"group\") || t.type === \"daily-releases\");\n break;\n case \"contentFilters.changed\":\n this.invalidate((t) => t.type === \"cards\" || t.type === \"history\" || t.type === \"release-dates\" || t.type === \"daily-releases\");\n break;\n case \"access.changed\":\n this.invalidate((t) => t.type === \"cards\" || t.type === \"history\" || t.type === \"content-detail\" || t.type === \"reader-plan\" || t.type.startsWith(\"group\") || t.type === \"daily-releases\" || t.type === \"standing\");\n break;\n case \"engagement.changed\":\n this.invalidate((t) => t.type === \"history\" || t.type === \"cards\" || t.type === \"daily-releases\" || t.type === \"standing\" || t.type.startsWith(\"stats\") || (t.type === \"content-detail\" && !!t.id && c.ids.includes(t.id)));\n break;\n case \"history.changed\":\n this.invalidate((t) => t.type === \"history\" || t.type === \"cards\" || t.type === \"daily-releases\");\n break;\n case \"searches.changed\":\n this.invalidate((t) => t.type === \"searches\");\n break;\n case \"rules.changed\":\n this.invalidate((t) => t.type === \"moderation-rules\" && t.scope === c.rules);\n break;\n case \"reader.changed\":\n this.invalidate((t) => (t.type === \"reader-plan\" || t.type === \"page-notices\") && t.id === c.version);\n break;\n case \"moderation.resolved\":\n items<{ id: string }>([\"held\"], (x) => (x.id === c.id ? null : x));\n if (c.kind === \"comment\") this.invalidate((t) => comments.includes(t.type) || t.type === \"standing\");\n else this.invalidate((t) => (t.type === \"content\" && t.id === c.id) || t.type === \"contents\");\n break;\n }\n }\n\n // Preserve loaded pages and canonical receipt revisions. Reads that started\n // before a successful mutation cannot overwrite it when they finish later.\n private updateNotification(item: Item, scope: string | null): void {\n for (const [key, slot] of this.slots) {\n if (slot.tag.type !== \"notifications\" || !slot.listeners.size) continue;\n const page = slot.entry.data as Page<Item> | undefined;\n const cached = page?.items.find((n) => n.id === item.id);\n // Mutations from separate mounted views can complete out of order.\n // In particular, an old active receipt must not evict a newer archive.\n if ((slot.tag.readScope ?? \"{}\") === scope && cached && cached.revision > item.revision) continue;\n const archived = slot.tag.scope?.startsWith(\"archived:\") ?? false;\n const matches = archived === !!item.dismissed_at;\n if ((slot.tag.readScope ?? \"{}\") !== scope || !page || slot.tag.scope?.endsWith(\":unread\") || (matches && !page.items.some((n) => n.id === item.id))) {\n this.load(key, true);\n continue;\n }\n if (!matches && !page.items.some((n) => n.id === item.id)) continue;\n slot.ctl?.abort();\n slot.moreCtl?.abort();\n slot.ctl = slot.moreCtl = undefined;\n this.update(slot, { loading: false, loadingMore: false, data: { ...page, items: page.items.flatMap((n) => n.id === item.id ? matches ? [item] : [] : [n]) } });\n }\n }\n\n // A new entry per change: useSyncExternalStore compares snapshots by identity.\n private update(s: Slot, patch: Partial<Resource<unknown>>): void {\n s.entry = { ...s.entry, ...patch };\n for (const l of s.listeners) l();\n }\n}\n\nconst stores = new WeakMap<ContentKitClient, ResourceStore>();\n\n/** The client's shared content store. */\nexport function resourcesFor(client: ContentKitClient): ResourceStore {\n let s = stores.get(client);\n if (!s) stores.set(client, (s = new ResourceStore(client)));\n return s;\n}\n"],"mappings":"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"}
|
package/dist/react/stats.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ContentKitClient } from '../client/client.js';
|
|
2
2
|
import { ContentKitError } from '../client/errors.js';
|
|
3
|
-
import { Activity, MyStats
|
|
4
|
-
import { ActivityQuery
|
|
3
|
+
import { Activity, MyStats } from '../client/generated/wire.js';
|
|
4
|
+
import { ActivityQuery } from '../client/stats.js';
|
|
5
5
|
export interface UseStats<T> {
|
|
6
6
|
data: T | null;
|
|
7
7
|
loading: boolean;
|
|
@@ -10,14 +10,10 @@ export interface UseStats<T> {
|
|
|
10
10
|
unavailable: boolean;
|
|
11
11
|
reload: () => void;
|
|
12
12
|
}
|
|
13
|
-
/** Staff:
|
|
13
|
+
/** Staff: distinct people per UTC day (anonymous, free, premium) who read or played; distinct within each day, not across the period. Null reads nothing. */
|
|
14
14
|
export declare function useActivity(q: ActivityQuery | null, o?: {
|
|
15
15
|
client?: ContentKitClient;
|
|
16
16
|
}): UseStats<Activity>;
|
|
17
|
-
/** Staff: a kind's content ranked by distinct engaged people or comments over a period; null reads nothing. */
|
|
18
|
-
export declare function useTopContents(q: TopQuery | null, o?: {
|
|
19
|
-
client?: ContentKitClient;
|
|
20
|
-
}): UseStats<TopContents>;
|
|
21
17
|
/** The signed-in reader's statistics; nothing signed out. */
|
|
22
18
|
export declare function useMyStats(o?: {
|
|
23
19
|
client?: ContentKitClient;
|
package/dist/react/stats.js
CHANGED
|
@@ -14,15 +14,11 @@ function i(n, i = {}) {
|
|
|
14
14
|
let { scope: a } = t(i.client);
|
|
15
15
|
return r(n ? e("stats", a, "activity", n.kind, n.days) : null, (e, t) => e.stats.activity(n, t), i);
|
|
16
16
|
}
|
|
17
|
-
function a(n
|
|
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 = {}) {
|
|
17
|
+
function a(n = {}) {
|
|
22
18
|
let { scope: i, viewer: a } = t(n.client);
|
|
23
19
|
return r(a === null ? null : e("stats", i, "me"), (e, t) => e.stats.mine(t), n);
|
|
24
20
|
}
|
|
25
21
|
//#endregion
|
|
26
|
-
export { i as useActivity,
|
|
22
|
+
export { i as useActivity, a as useMyStats };
|
|
27
23
|
|
|
28
24
|
//# sourceMappingURL=stats.js.map
|
package/dist/react/stats.js.map
CHANGED
|
@@ -1 +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
|
|
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 } from \"../client/generated/wire.js\";\nimport type { ActivityQuery } 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: distinct people per UTC day (anonymous, free, premium) who read or played; distinct within each day, not across the period. 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/** 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,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"}
|
|
@@ -14,6 +14,8 @@ export interface ReadingEngagement {
|
|
|
14
14
|
page: (index: number, resume?: string) => (element: HTMLImageElement | null) => void;
|
|
15
15
|
/** Attach to the playing video. Seeks do not count as watched intervals. */
|
|
16
16
|
video: (element: HTMLVideoElement | null) => void;
|
|
17
|
+
/** Attach to a text post's body: one unit covered by its visible share. */
|
|
18
|
+
text: (element: HTMLElement | null) => void;
|
|
17
19
|
/** Existing filename/anchor selector to restore, independent of page index. */
|
|
18
20
|
setResume: (resume: string | undefined) => void;
|
|
19
21
|
flush: () => Promise<void>;
|
|
@@ -65,7 +65,7 @@ function f(f, p, m = {}) {
|
|
|
65
65
|
let n = [], r;
|
|
66
66
|
for (let [i, a] of e) {
|
|
67
67
|
let e = a.element;
|
|
68
|
-
if (!e
|
|
68
|
+
if (!e || e instanceof HTMLImageElement && (!e.complete || e.naturalWidth <= 0) || t.get(e) === !1) continue;
|
|
69
69
|
let o = d(e);
|
|
70
70
|
o && (n.push({
|
|
71
71
|
index: i,
|
|
@@ -207,13 +207,18 @@ function f(f, p, m = {}) {
|
|
|
207
207
|
});
|
|
208
208
|
};
|
|
209
209
|
}, [b, w]);
|
|
210
|
-
let T = i((e) => w.bindVideo(e), [w]);
|
|
210
|
+
let T = i((e) => w.bindVideo(e), [w]), E = i((e) => w.page(0)(e), [w]);
|
|
211
211
|
return c(() => ({
|
|
212
212
|
page: w.page,
|
|
213
213
|
video: T,
|
|
214
|
+
text: E,
|
|
214
215
|
setResume: w.setResume,
|
|
215
216
|
flush: w.flush
|
|
216
|
-
}), [
|
|
217
|
+
}), [
|
|
218
|
+
w,
|
|
219
|
+
T,
|
|
220
|
+
E
|
|
221
|
+
]);
|
|
217
222
|
}
|
|
218
223
|
//#endregion
|
|
219
224
|
export { f as useReadingEngagement };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-engagement.js","names":[],"sources":["../../src/react/use-engagement.ts"],"sourcesContent":["import { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useSyncExternalStore } from \"react\";\nimport { readerSession } from \"../client/beacons.js\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport { EngagementTracker, type ReadingObservation, type VisiblePage } from \"../client/engagement.js\";\nimport { ContentKitContext, useContentKitClient } from \"./context.js\";\n\nexport interface ReadingEngagementOptions {\n client?: ContentKitClient;\n enabled?: boolean;\n /** Local guest history grouping only; the server infers kind from content. */\n kind?: string;\n /** Genuine input renews attention for this long; default 60 seconds. */\n idleAfter?: number;\n /** Snapshot interval, at most 120 seconds. Page changes never send requests. */\n checkpointInterval?: number;\n}\n\nexport interface ReadingEngagement {\n /** Attach to the actual loaded image, not its placeholder or thumbnail. */\n page: (index: number, resume?: string) => (element: HTMLImageElement | null) => void;\n /** Attach to the playing video. Seeks do not count as watched intervals. */\n video: (element: HTMLVideoElement | null) => void;\n /** Existing filename/anchor selector to restore, independent of page index. */\n setResume: (resume: string | undefined) => void;\n flush: () => Promise<void>;\n}\n\ninterface PageBinding {\n element: HTMLImageElement | null;\n resume?: string;\n ref: (element: HTMLImageElement | null) => void;\n}\n\nfunction visibleRange(element: HTMLElement): { from: number; to: number; area: number } | undefined {\n if (!element.isConnected) return;\n const style = getComputedStyle(element);\n if (style.display === \"none\" || style.visibility === \"hidden\" || (style.opacity !== \"\" && Number(style.opacity) === 0)) return;\n const box = element.getBoundingClientRect();\n if (box.width <= 0 || box.height <= 0) return;\n let top = box.top, left = box.left, width = box.width, height = box.height;\n if (element instanceof HTMLImageElement && style.objectFit === \"contain\") {\n const scale = Math.min(width / element.naturalWidth, height / element.naturalHeight);\n const imageWidth = element.naturalWidth * scale, imageHeight = element.naturalHeight * scale;\n const [x = \"50%\", y = \"50%\"] = style.objectPosition.split(\" \");\n const offset = (position: string, space: number) => position.endsWith(\"%\") ? space * Number.parseFloat(position) / 100 : space / 2;\n left += offset(x, width - imageWidth); top += offset(y, height - imageHeight);\n width = imageWidth; height = imageHeight;\n }\n const viewport = element.closest(\"[data-reader-viewport]\")?.getBoundingClientRect();\n const visibleTop = Math.max(top, 0, viewport?.top ?? 0);\n const visibleBottom = Math.min(top + height, window.innerHeight, viewport?.bottom ?? window.innerHeight);\n const visibleLeft = Math.max(left, 0, viewport?.left ?? 0);\n const visibleRight = Math.min(left + width, window.innerWidth, viewport?.right ?? window.innerWidth);\n if (visibleBottom <= visibleTop || visibleRight <= visibleLeft) return;\n return { from: Math.max(0, Math.floor((visibleTop - top) / height * 10_000)),\n to: Math.min(10_000, Math.ceil((visibleBottom - top) / height * 10_000)),\n area: (visibleBottom - visibleTop) * (visibleRight - visibleLeft) };\n}\n\n/**\n * Keep this hook in the content page, above horizontal/vertical renderer switches.\n * It records loaded, visible attention, with one visit and bounded access receipts;\n * preloads and route opens alone produce no engagement or network writes.\n */\nexport function useReadingEngagement(contentId: string | null | undefined, versionId?: string, options: ReadingEngagementOptions = {}): ReadingEngagement {\n const client = useContentKitClient(options.client);\n const context = useContext(ContentKitContext);\n const identity = useSyncExternalStore(client.preferences.subscribeIdentity, client.preferences.identity, client.preferences.identity);\n const owner = context ? context.viewer : identity;\n const explicitGuest = context?.viewer === null;\n const permitted = !!contentId && options.enabled !== false && owner !== undefined;\n const current = useRef({ owner, permitted });\n current.current = { owner, permitted };\n const kind = useRef(options.kind);\n kind.current = options.kind;\n const language = useRef(context?.language);\n language.current = context?.language;\n\n const state = useMemo(() => {\n const pages = new Map<number, PageBinding>();\n const intersections = new WeakMap<Element, boolean>();\n let observer: IntersectionObserver | undefined;\n let active = false, hidden = false, buffering = false, video: HTMLVideoElement | null = null, resume: string | undefined;\n const owns = () => permitted && current.current.permitted && current.current.owner === owner && client.preferences.identity() === owner;\n let session: string | undefined;\n const getSession = () => owner === null ? (session ??= readerSession()) : undefined;\n const tracker = new EngagementTracker({\n contentId: contentId ?? \"\", versionId, idleAfter: options.idleAfter, checkpointInterval: options.checkpointInterval,\n begin: async () => {\n if (!owns()) return null;\n const result = await client.discovery.activityContexts([{ content_id: contentId!, version_id: versionId }], getSession(), language.current);\n if (!owns() || !result.enabled) return null;\n return result.contexts.find((receipt) => receipt.content_id === contentId && (!versionId || receipt.version_id === versionId)) ?? null;\n },\n submit: async (snapshot, submitOptions) => {\n if (!owns()) return;\n await client.discovery.engagement([snapshot], { session: getSession(), ...submitOptions });\n },\n onSnapshot: (snapshot) => {\n if (owns() && owner === null && explicitGuest) client.guestHistory.record({ ...snapshot, ...(kind.current ? { kind: kind.current } : {}) });\n },\n });\n const sample = () => {\n if (!active || !owns()) return;\n const visible: VisiblePage[] = [];\n let selected: { area: number; resume?: string } | undefined;\n for (const [index, binding] of pages) {\n const image = binding.element;\n if (!image?.complete || image.naturalWidth <= 0 || intersections.get(image) === false) continue;\n const range = visibleRange(image);\n if (!range) continue;\n visible.push({ index, ranges: [{ from: range.from, to: range.to }] });\n if (!selected || range.area > selected.area) selected = { area: range.area, resume: binding.resume };\n }\n const observation: ReadingObservation = { pages: visible, resume: resume ?? selected?.resume };\n if (video && video.readyState >= 2 && visibleRange(video)) observation.video = {\n positionMs: Math.max(0, Math.round(video.currentTime * 1000)), playing: !buffering && !video.paused && !video.ended && !video.seeking,\n seeking: video.seeking,\n };\n if (observation.video) observation.resume = String(video!.currentTime);\n tracker.observe(observation);\n tracker.attention(!hidden && document.visibilityState !== \"hidden\" && document.hasFocus());\n };\n const page = (index: number, selector?: string) => {\n let binding = pages.get(index);\n if (!binding) {\n binding = { element: null, resume: selector, ref: (element) => {\n if (binding!.element) {\n binding!.element.removeEventListener(\"load\", sample);\n observer?.unobserve(binding!.element);\n }\n binding!.element = element;\n if (element) observer?.observe(element);\n element?.addEventListener(\"load\", sample);\n sample();\n } };\n pages.set(index, binding);\n }\n binding.resume = selector;\n return binding.ref;\n };\n const mediaEvents = [\"play\", \"playing\", \"canplay\", \"pause\", \"seeking\", \"seeked\", \"ended\", \"loadeddata\", \"ratechange\", \"waiting\", \"stalled\"];\n const mediaEvent = (event: Event) => {\n if (event.type === \"waiting\" || event.type === \"stalled\") buffering = true;\n else if ([\"playing\", \"canplay\", \"seeked\", \"loadeddata\"].includes(event.type)) buffering = false;\n sample();\n };\n const bindVideo = (element: HTMLVideoElement | null) => {\n for (const event of mediaEvents) video?.removeEventListener(event, mediaEvent);\n if (video) observer?.unobserve(video);\n video = element; buffering = false;\n if (video) observer?.observe(video);\n for (const event of mediaEvents) video?.addEventListener(event, mediaEvent);\n sample();\n };\n const flush = () => { sample(); return owns() ? tracker.flush() : Promise.resolve(); };\n return {\n tracker, sample, owns, page, bindVideo, flush,\n setResume: (value: string | undefined) => { resume = value; sample(); },\n start: () => {\n active = true;\n if (!observer && typeof IntersectionObserver !== \"undefined\") {\n observer = new IntersectionObserver((entries) => {\n for (const entry of entries) intersections.set(entry.target, entry.isIntersecting);\n sample();\n });\n for (const binding of pages.values()) if (binding.element) observer.observe(binding.element);\n if (video) observer.observe(video);\n }\n sample();\n },\n stop: () => { active = false; },\n isActive: () => active,\n hide: () => { sample(); hidden = true; tracker.attention(false); void tracker.flush(true); },\n show: () => { hidden = false; tracker.activity(); sample(); },\n activity: () => { sample(); tracker.activity(); },\n cleanup: () => {\n observer?.disconnect();\n for (const binding of pages.values()) binding.element?.removeEventListener(\"load\", sample);\n for (const event of mediaEvents) video?.removeEventListener(event, mediaEvent);\n },\n };\n // Language and renderer mode do not start new visits. Owner changes do.\n }, [client, contentId, versionId, owner, explicitGuest, permitted, options.idleAfter, options.checkpointInterval]);\n\n useLayoutEffect(() => client.preferences.subscribeIdentity(() => {\n if (!state.owns()) state.tracker.discard();\n }), [client, state]);\n\n useEffect(() => {\n if (!permitted || !state.owns()) return;\n state.start();\n const timer = setInterval(state.sample, 250);\n const inputEvents = [\"pointerdown\", \"pointermove\", \"keydown\", \"wheel\", \"touchstart\", \"touchmove\"];\n for (const event of inputEvents) window.addEventListener(event, state.activity, { passive: true, capture: true });\n const visibility = () => { if (document.visibilityState === \"hidden\") state.hide(); else state.show(); };\n const blur = () => { state.sample(); state.tracker.attention(false); };\n window.addEventListener(\"scroll\", state.sample, true);\n window.addEventListener(\"resize\", state.sample);\n window.addEventListener(\"focus\", state.show);\n window.addEventListener(\"blur\", blur);\n window.addEventListener(\"pagehide\", state.hide);\n window.addEventListener(\"pageshow\", state.show);\n window.addEventListener(\"online\", state.flush);\n document.addEventListener(\"visibilitychange\", visibility);\n return () => {\n state.stop(); clearInterval(timer);\n for (const event of inputEvents) window.removeEventListener(event, state.activity, true);\n window.removeEventListener(\"scroll\", state.sample, true);\n window.removeEventListener(\"resize\", state.sample);\n window.removeEventListener(\"focus\", state.show);\n window.removeEventListener(\"blur\", blur);\n window.removeEventListener(\"pagehide\", state.hide);\n window.removeEventListener(\"pageshow\", state.show);\n window.removeEventListener(\"online\", state.flush);\n document.removeEventListener(\"visibilitychange\", visibility);\n // StrictMode's immediate setup replay must keep the same visit alive.\n queueMicrotask(() => {\n if (state.isActive()) return;\n state.cleanup();\n if (!state.owns()) { state.tracker.discard(); return; }\n state.tracker.attention(false);\n void state.tracker.flush(true).finally(() => state.tracker.discard());\n });\n };\n }, [permitted, state]);\n\n const video = useCallback((element: HTMLVideoElement | null) => state.bindVideo(element), [state]);\n return useMemo(() => ({ page: state.page, video, setResume: state.setResume, flush: state.flush }), [state, video]);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-engagement.js","names":[],"sources":["../../src/react/use-engagement.ts"],"sourcesContent":["import { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useSyncExternalStore } from \"react\";\nimport { readerSession } from \"../client/beacons.js\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport { EngagementTracker, type ReadingObservation, type VisiblePage } from \"../client/engagement.js\";\nimport { ContentKitContext, useContentKitClient } from \"./context.js\";\n\nexport interface ReadingEngagementOptions {\n client?: ContentKitClient;\n enabled?: boolean;\n /** Local guest history grouping only; the server infers kind from content. */\n kind?: string;\n /** Genuine input renews attention for this long; default 60 seconds. */\n idleAfter?: number;\n /** Snapshot interval, at most 120 seconds. Page changes never send requests. */\n checkpointInterval?: number;\n}\n\nexport interface ReadingEngagement {\n /** Attach to the actual loaded image, not its placeholder or thumbnail. */\n page: (index: number, resume?: string) => (element: HTMLImageElement | null) => void;\n /** Attach to the playing video. Seeks do not count as watched intervals. */\n video: (element: HTMLVideoElement | null) => void;\n /** Attach to a text post's body: one unit covered by its visible share. */\n text: (element: HTMLElement | null) => void;\n /** Existing filename/anchor selector to restore, independent of page index. */\n setResume: (resume: string | undefined) => void;\n flush: () => Promise<void>;\n}\n\ninterface PageBinding {\n element: HTMLElement | null;\n resume?: string;\n ref: (element: HTMLElement | null) => void;\n}\n\nfunction visibleRange(element: HTMLElement): { from: number; to: number; area: number } | undefined {\n if (!element.isConnected) return;\n const style = getComputedStyle(element);\n if (style.display === \"none\" || style.visibility === \"hidden\" || (style.opacity !== \"\" && Number(style.opacity) === 0)) return;\n const box = element.getBoundingClientRect();\n if (box.width <= 0 || box.height <= 0) return;\n let top = box.top, left = box.left, width = box.width, height = box.height;\n if (element instanceof HTMLImageElement && style.objectFit === \"contain\") {\n const scale = Math.min(width / element.naturalWidth, height / element.naturalHeight);\n const imageWidth = element.naturalWidth * scale, imageHeight = element.naturalHeight * scale;\n const [x = \"50%\", y = \"50%\"] = style.objectPosition.split(\" \");\n const offset = (position: string, space: number) => position.endsWith(\"%\") ? space * Number.parseFloat(position) / 100 : space / 2;\n left += offset(x, width - imageWidth); top += offset(y, height - imageHeight);\n width = imageWidth; height = imageHeight;\n }\n const viewport = element.closest(\"[data-reader-viewport]\")?.getBoundingClientRect();\n const visibleTop = Math.max(top, 0, viewport?.top ?? 0);\n const visibleBottom = Math.min(top + height, window.innerHeight, viewport?.bottom ?? window.innerHeight);\n const visibleLeft = Math.max(left, 0, viewport?.left ?? 0);\n const visibleRight = Math.min(left + width, window.innerWidth, viewport?.right ?? window.innerWidth);\n if (visibleBottom <= visibleTop || visibleRight <= visibleLeft) return;\n return { from: Math.max(0, Math.floor((visibleTop - top) / height * 10_000)),\n to: Math.min(10_000, Math.ceil((visibleBottom - top) / height * 10_000)),\n area: (visibleBottom - visibleTop) * (visibleRight - visibleLeft) };\n}\n\n/**\n * Keep this hook in the content page, above horizontal/vertical renderer switches.\n * It records loaded, visible attention, with one visit and bounded access receipts;\n * preloads and route opens alone produce no engagement or network writes.\n */\nexport function useReadingEngagement(contentId: string | null | undefined, versionId?: string, options: ReadingEngagementOptions = {}): ReadingEngagement {\n const client = useContentKitClient(options.client);\n const context = useContext(ContentKitContext);\n const identity = useSyncExternalStore(client.preferences.subscribeIdentity, client.preferences.identity, client.preferences.identity);\n const owner = context ? context.viewer : identity;\n const explicitGuest = context?.viewer === null;\n const permitted = !!contentId && options.enabled !== false && owner !== undefined;\n const current = useRef({ owner, permitted });\n current.current = { owner, permitted };\n const kind = useRef(options.kind);\n kind.current = options.kind;\n const language = useRef(context?.language);\n language.current = context?.language;\n\n const state = useMemo(() => {\n const pages = new Map<number, PageBinding>();\n const intersections = new WeakMap<Element, boolean>();\n let observer: IntersectionObserver | undefined;\n let active = false, hidden = false, buffering = false, video: HTMLVideoElement | null = null, resume: string | undefined;\n const owns = () => permitted && current.current.permitted && current.current.owner === owner && client.preferences.identity() === owner;\n let session: string | undefined;\n const getSession = () => owner === null ? (session ??= readerSession()) : undefined;\n const tracker = new EngagementTracker({\n contentId: contentId ?? \"\", versionId, idleAfter: options.idleAfter, checkpointInterval: options.checkpointInterval,\n begin: async () => {\n if (!owns()) return null;\n const result = await client.discovery.activityContexts([{ content_id: contentId!, version_id: versionId }], getSession(), language.current);\n if (!owns() || !result.enabled) return null;\n return result.contexts.find((receipt) => receipt.content_id === contentId && (!versionId || receipt.version_id === versionId)) ?? null;\n },\n submit: async (snapshot, submitOptions) => {\n if (!owns()) return;\n await client.discovery.engagement([snapshot], { session: getSession(), ...submitOptions });\n },\n onSnapshot: (snapshot) => {\n if (owns() && owner === null && explicitGuest) client.guestHistory.record({ ...snapshot, ...(kind.current ? { kind: kind.current } : {}) });\n },\n });\n const sample = () => {\n if (!active || !owns()) return;\n const visible: VisiblePage[] = [];\n let selected: { area: number; resume?: string } | undefined;\n for (const [index, binding] of pages) {\n const image = binding.element;\n // An image counts once loaded; a text body as soon as it shows.\n if (!image || image instanceof HTMLImageElement && (!image.complete || image.naturalWidth <= 0) || intersections.get(image) === false) continue;\n const range = visibleRange(image);\n if (!range) continue;\n visible.push({ index, ranges: [{ from: range.from, to: range.to }] });\n if (!selected || range.area > selected.area) selected = { area: range.area, resume: binding.resume };\n }\n const observation: ReadingObservation = { pages: visible, resume: resume ?? selected?.resume };\n if (video && video.readyState >= 2 && visibleRange(video)) observation.video = {\n positionMs: Math.max(0, Math.round(video.currentTime * 1000)), playing: !buffering && !video.paused && !video.ended && !video.seeking,\n seeking: video.seeking,\n };\n if (observation.video) observation.resume = String(video!.currentTime);\n tracker.observe(observation);\n tracker.attention(!hidden && document.visibilityState !== \"hidden\" && document.hasFocus());\n };\n const page = (index: number, selector?: string) => {\n let binding = pages.get(index);\n if (!binding) {\n binding = { element: null, resume: selector, ref: (element) => {\n if (binding!.element) {\n binding!.element.removeEventListener(\"load\", sample);\n observer?.unobserve(binding!.element);\n }\n binding!.element = element;\n if (element) observer?.observe(element);\n element?.addEventListener(\"load\", sample);\n sample();\n } };\n pages.set(index, binding);\n }\n binding.resume = selector;\n return binding.ref;\n };\n const mediaEvents = [\"play\", \"playing\", \"canplay\", \"pause\", \"seeking\", \"seeked\", \"ended\", \"loadeddata\", \"ratechange\", \"waiting\", \"stalled\"];\n const mediaEvent = (event: Event) => {\n if (event.type === \"waiting\" || event.type === \"stalled\") buffering = true;\n else if ([\"playing\", \"canplay\", \"seeked\", \"loadeddata\"].includes(event.type)) buffering = false;\n sample();\n };\n const bindVideo = (element: HTMLVideoElement | null) => {\n for (const event of mediaEvents) video?.removeEventListener(event, mediaEvent);\n if (video) observer?.unobserve(video);\n video = element; buffering = false;\n if (video) observer?.observe(video);\n for (const event of mediaEvents) video?.addEventListener(event, mediaEvent);\n sample();\n };\n const flush = () => { sample(); return owns() ? tracker.flush() : Promise.resolve(); };\n return {\n tracker, sample, owns, page, bindVideo, flush,\n setResume: (value: string | undefined) => { resume = value; sample(); },\n start: () => {\n active = true;\n if (!observer && typeof IntersectionObserver !== \"undefined\") {\n observer = new IntersectionObserver((entries) => {\n for (const entry of entries) intersections.set(entry.target, entry.isIntersecting);\n sample();\n });\n for (const binding of pages.values()) if (binding.element) observer.observe(binding.element);\n if (video) observer.observe(video);\n }\n sample();\n },\n stop: () => { active = false; },\n isActive: () => active,\n hide: () => { sample(); hidden = true; tracker.attention(false); void tracker.flush(true); },\n show: () => { hidden = false; tracker.activity(); sample(); },\n activity: () => { sample(); tracker.activity(); },\n cleanup: () => {\n observer?.disconnect();\n for (const binding of pages.values()) binding.element?.removeEventListener(\"load\", sample);\n for (const event of mediaEvents) video?.removeEventListener(event, mediaEvent);\n },\n };\n // Language and renderer mode do not start new visits. Owner changes do.\n }, [client, contentId, versionId, owner, explicitGuest, permitted, options.idleAfter, options.checkpointInterval]);\n\n useLayoutEffect(() => client.preferences.subscribeIdentity(() => {\n if (!state.owns()) state.tracker.discard();\n }), [client, state]);\n\n useEffect(() => {\n if (!permitted || !state.owns()) return;\n state.start();\n const timer = setInterval(state.sample, 250);\n const inputEvents = [\"pointerdown\", \"pointermove\", \"keydown\", \"wheel\", \"touchstart\", \"touchmove\"];\n for (const event of inputEvents) window.addEventListener(event, state.activity, { passive: true, capture: true });\n const visibility = () => { if (document.visibilityState === \"hidden\") state.hide(); else state.show(); };\n const blur = () => { state.sample(); state.tracker.attention(false); };\n window.addEventListener(\"scroll\", state.sample, true);\n window.addEventListener(\"resize\", state.sample);\n window.addEventListener(\"focus\", state.show);\n window.addEventListener(\"blur\", blur);\n window.addEventListener(\"pagehide\", state.hide);\n window.addEventListener(\"pageshow\", state.show);\n window.addEventListener(\"online\", state.flush);\n document.addEventListener(\"visibilitychange\", visibility);\n return () => {\n state.stop(); clearInterval(timer);\n for (const event of inputEvents) window.removeEventListener(event, state.activity, true);\n window.removeEventListener(\"scroll\", state.sample, true);\n window.removeEventListener(\"resize\", state.sample);\n window.removeEventListener(\"focus\", state.show);\n window.removeEventListener(\"blur\", blur);\n window.removeEventListener(\"pagehide\", state.hide);\n window.removeEventListener(\"pageshow\", state.show);\n window.removeEventListener(\"online\", state.flush);\n document.removeEventListener(\"visibilitychange\", visibility);\n // StrictMode's immediate setup replay must keep the same visit alive.\n queueMicrotask(() => {\n if (state.isActive()) return;\n state.cleanup();\n if (!state.owns()) { state.tracker.discard(); return; }\n state.tracker.attention(false);\n void state.tracker.flush(true).finally(() => state.tracker.discard());\n });\n };\n }, [permitted, state]);\n\n const video = useCallback((element: HTMLVideoElement | null) => state.bindVideo(element), [state]);\n const text = useCallback((element: HTMLElement | null) => state.page(0)(element), [state]);\n return useMemo(() => ({ page: state.page, video, text, setResume: state.setResume, flush: state.flush }), [state, video, text]);\n}\n"],"mappings":"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"}
|
package/dist/react.js
CHANGED
|
@@ -3,30 +3,32 @@ import { GuardContext as i, useContentKit as a, useContentKitClient as o, useCon
|
|
|
3
3
|
import { useCrop as d, useEncodeProgress as f, useUpload as p, useUploadQueue as m } from "./react/upload.js";
|
|
4
4
|
import { ResourceStore as h, resourcesFor as g } from "./react/resources.js";
|
|
5
5
|
import { useContentScope as _, usePaged as v } from "./react/use-resource.js";
|
|
6
|
-
import { memberId as y, useContentGroups as b, useGroup as x, useGroupMembers as S, useGroups as C,
|
|
7
|
-
import { useMediaRead as
|
|
8
|
-
import { useAvatar as
|
|
9
|
-
import { editOutput as
|
|
10
|
-
import { round3 as
|
|
11
|
-
import { GALLERY_VIEW_KEY as
|
|
12
|
-
import { useEditorCrop as
|
|
13
|
-
import { nameIn as
|
|
14
|
-
import { useNearViewport as
|
|
15
|
-
import { useAdminComments as
|
|
16
|
-
import { useContentConfig as
|
|
17
|
-
import { useFavorite as
|
|
18
|
-
import { usePoll as
|
|
19
|
-
import { useVersionParam as
|
|
20
|
-
import { useAdminLocation as
|
|
21
|
-
import { beaconsFor as
|
|
22
|
-
import { defaultVersion as
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
|
|
6
|
+
import { memberId as y, useContentGroups as b, useGroup as x, useGroupMembers as S, useGroups as C, useGroupsPage as w, useKinds as T, useMyGroups as E } from "./react/groups.js";
|
|
7
|
+
import { useMediaRead as D } from "./react/read.js";
|
|
8
|
+
import { useAvatar as O, usePresets as k, usePublicImage as A } from "./react/public.js";
|
|
9
|
+
import { editOutput as j, useSlotCrop as M, useSlotImage as N } from "./react/slot.js";
|
|
10
|
+
import { round3 as P, useFrameStrip as F, useVideoFrame as I, useVideoImages as L, useVideoPoster as R } from "./react/video.js";
|
|
11
|
+
import { GALLERY_VIEW_KEY as z, PLAYER_QUALITY_KEY as B, useCarousel as V, useGalleryView as H, useHlsPlayer as U, useRefreshBeforeExpiry as W } from "./react/gallery.js";
|
|
12
|
+
import { useEditorCrop as G } from "./react/crop.js";
|
|
13
|
+
import { nameIn as K, thumbnailOf as q, useMediaFolder as J } from "./react/folder.js";
|
|
14
|
+
import { useNearViewport as Y } from "./react/viewport.js";
|
|
15
|
+
import { useAdminComments as X, useBannedWords as Z, useCanComment as Q, useCommentBans as $, useCommentReplies as ee, useComments as te, useGlobalCommentBan as ne, useIPBans as re, useLatestComments as ie, useModerationQueue as ae, withReaction as oe } from "./react/comments.js";
|
|
16
|
+
import { useContentConfig as se } from "./react/config.js";
|
|
17
|
+
import { useFavorite as ce, useReaction as le } from "./react/engagement.js";
|
|
18
|
+
import { usePoll as ue, usePollEditor as de, usePolls as fe, withVote as pe } from "./react/polls.js";
|
|
19
|
+
import { useVersionParam as me } from "./react/location.js";
|
|
20
|
+
import { useAdminLocation as he, useFlowClient as ge, useStaffAccess as _e } from "./react/admin.js";
|
|
21
|
+
import { beaconsFor as ve, useBeacons as ye, useContentFilters as be, useContents as xe, useContentsPage as Se, useDailyReleases as Ce, useExposure as we, useHistory as Te, useLockedCount as Ee, useMyFavorites as De, useMyReactions as Oe, useRecentSearches as ke, useRecommendations as Ae, useReleaseDates as je, useSearch as Me, useSearchPage as Ne, useSimilar as Pe, useTypeahead as Fe } from "./react/discovery.js";
|
|
22
|
+
import { defaultVersion as Ie, editionOf as Le, findVersion as Re, liveVersions as ze, useContentDetail as Be } from "./react/detail.js";
|
|
23
|
+
import { useContent as Ve, useManagedContents as He } from "./react/contents.js";
|
|
24
|
+
import { usePageNotices as Ue } from "./react/page-notices.js";
|
|
25
|
+
import { downloadMessage as We, nextRelease as Ge, startDownload as Ke, useReaderPlan as qe, useSegmentPages as Je, useVersionDownload as Ye } from "./react/versions.js";
|
|
26
|
+
import { useActivity as Xe, useMyStats as Ze } from "./react/stats.js";
|
|
27
|
+
import { useReadingEngagement as Qe } from "./react/use-engagement.js";
|
|
28
|
+
import { usePostReading as $e, usePostReads as et } from "./react/post-reads.js";
|
|
29
|
+
import { useNotificationUnread as tt, useNotifications as nt } from "./react/notifications.js";
|
|
30
|
+
import { ContentKitProvider as rt } from "./react/provider.js";
|
|
31
|
+
import { useReaderPreferences as it } from "./react/reader-preferences.js";
|
|
32
|
+
import { useCanonicalContent as at } from "./react/canonical.js";
|
|
33
|
+
import { useRememberedSort as ot } from "./react/remembered-sort.js";
|
|
34
|
+
export { rt as ContentKitProvider, z as GALLERY_VIEW_KEY, i as GuardContext, e as HOVER_DELAY, t as InlinePreviewContext, B as PLAYER_QUALITY_KEY, h as ResourceStore, ve as beaconsFor, Ie as defaultVersion, We as downloadMessage, j as editOutput, Le as editionOf, Re as findVersion, ze as liveVersions, y as memberId, K as nameIn, Ge as nextRelease, g as resourcesFor, P as round3, Ke as startDownload, q as thumbnailOf, Xe as useActivity, X as useAdminComments, he as useAdminLocation, O as useAvatar, Z as useBannedWords, ye as useBeacons, Q as useCanComment, at as useCanonicalContent, V as useCarousel, $ as useCommentBans, ee as useCommentReplies, te as useComments, Ve as useContent, se as useContentConfig, Be as useContentDetail, be as useContentFilters, b as useContentGroups, a as useContentKit, o as useContentKitClient, _ as useContentScope, s as useContentURLs, xe as useContents, Se as useContentsPage, d as useCrop, Ce as useDailyReleases, G as useEditorCrop, f as useEncodeProgress, c as useErrorReporter, we as useExposure, ce as useFavorite, ge as useFlowClient, F as useFrameStrip, H as useGalleryView, ne as useGlobalCommentBan, x as useGroup, S as useGroupMembers, C as useGroups, w as useGroupsPage, l as useGuarded, Te as useHistory, U as useHlsPlayer, re as useIPBans, n as useInlinePreview, T as useKinds, ie as useLatestComments, Ee as useLockedCount, He as useManagedContents, J as useMediaFolder, D as useMediaRead, ae as useModerationQueue, De as useMyFavorites, E as useMyGroups, Oe as useMyReactions, Ze as useMyStats, Y as useNearViewport, tt as useNotificationUnread, nt as useNotifications, u as useOptionalContentKitClient, Ue as usePageNotices, v as usePaged, ue as usePoll, de as usePollEditor, fe as usePolls, $e as usePostReading, et as usePostReads, k as usePresets, A as usePublicImage, le as useReaction, qe as useReaderPlan, it as useReaderPreferences, Qe as useReadingEngagement, ke as useRecentSearches, Ae as useRecommendations, r as useReducedMotion, W as useRefreshBeforeExpiry, je as useReleaseDates, ot as useRememberedSort, Me as useSearch, Ne as useSearchPage, Je as useSegmentPages, Pe as useSimilar, M as useSlotCrop, N as useSlotImage, _e as useStaffAccess, Fe as useTypeahead, p as useUpload, m as useUploadQueue, Ye as useVersionDownload, me as useVersionParam, I as useVideoFrame, L as useVideoImages, R as useVideoPoster, oe as withReaction, pe as withVote };
|