@openrails/contentkit-ui 0.103.1 → 0.104.0-rc.1

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.
Files changed (145) hide show
  1. package/README.md +105 -43
  2. package/dist/client/beacons.d.ts +3 -13
  3. package/dist/client/beacons.js +4 -45
  4. package/dist/client/beacons.js.map +1 -1
  5. package/dist/client/client.d.ts +3 -0
  6. package/dist/client/client.js +45 -43
  7. package/dist/client/client.js.map +1 -1
  8. package/dist/client/content/favorites.d.ts +2 -0
  9. package/dist/client/content/favorites.js.map +1 -1
  10. package/dist/client/content/types.d.ts +3 -0
  11. package/dist/client/content/types.js.map +1 -1
  12. package/dist/client/discovery.d.ts +28 -3
  13. package/dist/client/discovery.js +22 -1
  14. package/dist/client/discovery.js.map +1 -1
  15. package/dist/client/engagement.d.ts +70 -0
  16. package/dist/client/engagement.js +191 -0
  17. package/dist/client/engagement.js.map +1 -0
  18. package/dist/client/generated/routes.d.ts +35 -9
  19. package/dist/client/generated/routes.js +59 -2
  20. package/dist/client/generated/routes.js.map +1 -1
  21. package/dist/client/generated/wire.d.ts +53 -12
  22. package/dist/client/generated/wire.js.map +1 -1
  23. package/dist/client/guest-reading-history.d.ts +40 -0
  24. package/dist/client/guest-reading-history.js +144 -0
  25. package/dist/client/guest-reading-history.js.map +1 -0
  26. package/dist/client/index.d.ts +4 -2
  27. package/dist/client/stats.d.ts +5 -5
  28. package/dist/client/stats.js.map +1 -1
  29. package/dist/client.js +24 -22
  30. package/dist/components/admin/content-manager.d.ts +1 -1
  31. package/dist/components/admin/content-manager.js +342 -302
  32. package/dist/components/admin/content-manager.js.map +1 -1
  33. package/dist/components/admin/page-review.js +106 -97
  34. package/dist/components/admin/page-review.js.map +1 -1
  35. package/dist/components/admin/quick-search.d.ts +14 -0
  36. package/dist/components/admin/quick-search.js +60 -0
  37. package/dist/components/admin/quick-search.js.map +1 -0
  38. package/dist/components/admin/reader-content-editor.d.ts +6 -0
  39. package/dist/components/admin/reader-content-editor.js +733 -0
  40. package/dist/components/admin/reader-content-editor.js.map +1 -0
  41. package/dist/components/admin/reader-manifest.js +120 -105
  42. package/dist/components/admin/reader-manifest.js.map +1 -1
  43. package/dist/components/admin/stats-dashboard.d.ts +4 -4
  44. package/dist/components/admin/stats-dashboard.js +132 -134
  45. package/dist/components/admin/stats-dashboard.js.map +1 -1
  46. package/dist/components/content/daily-releases.js +143 -191
  47. package/dist/components/content/daily-releases.js.map +1 -1
  48. package/dist/components/content/editor-save.d.ts +32 -0
  49. package/dist/components/content/editor-save.js +51 -0
  50. package/dist/components/content/editor-save.js.map +1 -0
  51. package/dist/components/content/editors.d.ts +17 -2
  52. package/dist/components/content/editors.js +359 -328
  53. package/dist/components/content/editors.js.map +1 -1
  54. package/dist/components/content/favorites.d.ts +6 -0
  55. package/dist/components/content/favorites.js +33 -0
  56. package/dist/components/content/favorites.js.map +1 -0
  57. package/dist/components/content/filter-dialog.js +19 -15
  58. package/dist/components/content/filter-dialog.js.map +1 -1
  59. package/dist/components/content/filters.d.ts +2 -0
  60. package/dist/components/content/filters.js +5 -1
  61. package/dist/components/content/filters.js.map +1 -1
  62. package/dist/components/content/grid.d.ts +34 -13
  63. package/dist/components/content/grid.js +300 -271
  64. package/dist/components/content/grid.js.map +1 -1
  65. package/dist/components/content/history.d.ts +6 -0
  66. package/dist/components/content/history.js +91 -0
  67. package/dist/components/content/history.js.map +1 -0
  68. package/dist/components/content/index.d.ts +1 -0
  69. package/dist/components/content/offer-fields.d.ts +1 -1
  70. package/dist/components/content/offer-fields.js +86 -64
  71. package/dist/components/content/offer-fields.js.map +1 -1
  72. package/dist/components/content/search.d.ts +11 -4
  73. package/dist/components/content/search.js +22 -11
  74. package/dist/components/content/search.js.map +1 -1
  75. package/dist/components/content/summary-tile.d.ts +14 -0
  76. package/dist/components/content/summary-tile.js +63 -0
  77. package/dist/components/content/summary-tile.js.map +1 -0
  78. package/dist/components/groups/memberships.d.ts +11 -1
  79. package/dist/components/groups/memberships.js +1 -1
  80. package/dist/components/groups/memberships.js.map +1 -1
  81. package/dist/components/media-folder-editor.d.ts +4 -0
  82. package/dist/components/media-folder-editor.js +178 -168
  83. package/dist/components/media-folder-editor.js.map +1 -1
  84. package/dist/components/video-mini-player.d.ts +4 -1
  85. package/dist/components/video-mini-player.js +77 -71
  86. package/dist/components/video-mini-player.js.map +1 -1
  87. package/dist/components/video-player.d.ts +4 -1
  88. package/dist/components/video-player.js +280 -276
  89. package/dist/components/video-player.js.map +1 -1
  90. package/dist/index.d.ts +1 -0
  91. package/dist/index.js +17 -14
  92. package/dist/locales/admin/de.js +33 -8
  93. package/dist/locales/admin/de.js.map +1 -1
  94. package/dist/locales/admin/en.d.ts +29 -4
  95. package/dist/locales/admin/en.js +33 -8
  96. package/dist/locales/admin/en.js.map +1 -1
  97. package/dist/locales/admin/es.js +33 -8
  98. package/dist/locales/admin/es.js.map +1 -1
  99. package/dist/locales/admin/ja.js +33 -8
  100. package/dist/locales/admin/ja.js.map +1 -1
  101. package/dist/locales/admin/ko.js +33 -8
  102. package/dist/locales/admin/ko.js.map +1 -1
  103. package/dist/locales/admin/zh.js +33 -8
  104. package/dist/locales/admin/zh.js.map +1 -1
  105. package/dist/locales/discovery/de.js +0 -1
  106. package/dist/locales/discovery/de.js.map +1 -1
  107. package/dist/locales/discovery/en.d.ts +0 -1
  108. package/dist/locales/discovery/en.js +0 -1
  109. package/dist/locales/discovery/en.js.map +1 -1
  110. package/dist/locales/discovery/es.js +0 -1
  111. package/dist/locales/discovery/es.js.map +1 -1
  112. package/dist/locales/discovery/ja.js +0 -1
  113. package/dist/locales/discovery/ja.js.map +1 -1
  114. package/dist/locales/discovery/ko.js +0 -1
  115. package/dist/locales/discovery/ko.js.map +1 -1
  116. package/dist/locales/discovery/zh.js +0 -1
  117. package/dist/locales/discovery/zh.js.map +1 -1
  118. package/dist/locales/en.d.ts +29 -5
  119. package/dist/react/discovery.d.ts +20 -17
  120. package/dist/react/discovery.js +273 -221
  121. package/dist/react/discovery.js.map +1 -1
  122. package/dist/react/index.d.ts +2 -1
  123. package/dist/react/page-review.d.ts +1 -1
  124. package/dist/react/page-review.js +6 -5
  125. package/dist/react/page-review.js.map +1 -1
  126. package/dist/react/resources.d.ts +3 -1
  127. package/dist/react/resources.js +4 -0
  128. package/dist/react/resources.js.map +1 -1
  129. package/dist/react/stats.d.ts +2 -2
  130. package/dist/react/stats.js.map +1 -1
  131. package/dist/react/use-draft.d.ts +8 -0
  132. package/dist/react/use-draft.js +35 -0
  133. package/dist/react/use-draft.js.map +1 -0
  134. package/dist/react/use-engagement.d.ts +26 -0
  135. package/dist/react/use-engagement.js +221 -0
  136. package/dist/react/use-engagement.js.map +1 -0
  137. package/dist/react/use-resource.d.ts +4 -3
  138. package/dist/react/use-resource.js +2 -1
  139. package/dist/react/use-resource.js.map +1 -1
  140. package/dist/react.js +12 -11
  141. package/dist/styles.css +1 -1
  142. package/dist/ui/dialog.d.ts +2 -1
  143. package/dist/ui/dialog.js +23 -20
  144. package/dist/ui/dialog.js.map +1 -1
  145. package/package.json +1 -1
@@ -0,0 +1,221 @@
1
+ import { ContentKitContext as e, useContentKitClient as t } from "./context.js";
2
+ import { readerSession as n } from "../client/beacons.js";
3
+ import { EngagementTracker as r } from "../client/engagement.js";
4
+ import { useCallback as i, useContext as a, useEffect as o, useLayoutEffect as s, useMemo as c, useRef as l, useSyncExternalStore as u } from "react";
5
+ //#region src/react/use-engagement.ts
6
+ function d(e) {
7
+ if (!e.isConnected) return;
8
+ let t = getComputedStyle(e);
9
+ if (t.display === "none" || t.visibility === "hidden" || t.opacity !== "" && Number(t.opacity) === 0) return;
10
+ let n = e.getBoundingClientRect();
11
+ if (n.width <= 0 || n.height <= 0) return;
12
+ let r = n.top, i = n.left, a = n.width, o = n.height;
13
+ if (e instanceof HTMLImageElement && t.objectFit === "contain") {
14
+ let n = Math.min(a / e.naturalWidth, o / e.naturalHeight), s = e.naturalWidth * n, c = e.naturalHeight * n, [l = "50%", u = "50%"] = t.objectPosition.split(" "), d = (e, t) => e.endsWith("%") ? t * Number.parseFloat(e) / 100 : t / 2;
15
+ i += d(l, a - s), r += d(u, o - c), a = s, o = c;
16
+ }
17
+ let s = e.closest("[data-reader-viewport]")?.getBoundingClientRect(), c = Math.max(r, 0, s?.top ?? 0), l = Math.min(r + o, window.innerHeight, s?.bottom ?? window.innerHeight), u = Math.max(i, 0, s?.left ?? 0), d = Math.min(i + a, window.innerWidth, s?.right ?? window.innerWidth);
18
+ if (!(l <= c || d <= u)) return {
19
+ from: Math.max(0, Math.floor((c - r) / o * 1e4)),
20
+ to: Math.min(1e4, Math.ceil((l - r) / o * 1e4)),
21
+ area: (l - c) * (d - u)
22
+ };
23
+ }
24
+ function f(f, p, m = {}) {
25
+ let h = t(m.client), g = a(e), _ = u(h.preferences.subscribeIdentity, h.preferences.identity, h.preferences.identity), v = g ? g.viewer : _, y = g?.viewer === null, b = !!f && m.enabled !== !1 && v !== void 0, x = l({
26
+ owner: v,
27
+ permitted: b
28
+ });
29
+ x.current = {
30
+ owner: v,
31
+ permitted: b
32
+ };
33
+ let S = l(m.kind);
34
+ S.current = m.kind;
35
+ let C = l(g?.language);
36
+ C.current = g?.language;
37
+ let w = c(() => {
38
+ let e = /* @__PURE__ */ new Map(), t = /* @__PURE__ */ new WeakMap(), i, a = !1, o = !1, s = !1, c = null, l, u = () => b && x.current.permitted && x.current.owner === v && h.preferences.identity() === v, g, _ = () => v === null ? g ??= n() : void 0, w = new r({
39
+ contentId: f ?? "",
40
+ versionId: p,
41
+ idleAfter: m.idleAfter,
42
+ checkpointInterval: m.checkpointInterval,
43
+ begin: async () => {
44
+ if (!u()) return null;
45
+ let e = await h.discovery.activityContexts([{
46
+ content_id: f,
47
+ version_id: p
48
+ }], _(), C.current);
49
+ return !u() || !e.enabled ? null : e.contexts.find((e) => e.content_id === f && (!p || e.version_id === p)) ?? null;
50
+ },
51
+ submit: async (e, t) => {
52
+ u() && await h.discovery.engagement([e], {
53
+ session: _(),
54
+ ...t
55
+ });
56
+ },
57
+ onSnapshot: (e) => {
58
+ u() && v === null && y && h.guestHistory.record({
59
+ ...e,
60
+ ...S.current ? { kind: S.current } : {}
61
+ });
62
+ }
63
+ }), T = () => {
64
+ if (!a || !u()) return;
65
+ let n = [], r;
66
+ for (let [i, a] of e) {
67
+ let e = a.element;
68
+ if (!e?.complete || e.naturalWidth <= 0 || t.get(e) === !1) continue;
69
+ let o = d(e);
70
+ o && (n.push({
71
+ index: i,
72
+ ranges: [{
73
+ from: o.from,
74
+ to: o.to
75
+ }]
76
+ }), (!r || o.area > r.area) && (r = {
77
+ area: o.area,
78
+ resume: a.resume
79
+ }));
80
+ }
81
+ let i = {
82
+ pages: n,
83
+ resume: l ?? r?.resume
84
+ };
85
+ c && c.readyState >= 2 && d(c) && (i.video = {
86
+ positionMs: Math.max(0, Math.round(c.currentTime * 1e3)),
87
+ playing: !s && !c.paused && !c.ended && !c.seeking,
88
+ seeking: c.seeking
89
+ }), i.video && (i.resume = String(c.currentTime)), w.observe(i), w.attention(!o && document.visibilityState !== "hidden" && document.hasFocus());
90
+ }, E = (t, n) => {
91
+ let r = e.get(t);
92
+ return r || (r = {
93
+ element: null,
94
+ resume: n,
95
+ ref: (e) => {
96
+ r.element && (r.element.removeEventListener("load", T), i?.unobserve(r.element)), r.element = e, e && i?.observe(e), e?.addEventListener("load", T), T();
97
+ }
98
+ }, e.set(t, r)), r.resume = n, r.ref;
99
+ }, D = [
100
+ "play",
101
+ "playing",
102
+ "canplay",
103
+ "pause",
104
+ "seeking",
105
+ "seeked",
106
+ "ended",
107
+ "loadeddata",
108
+ "ratechange",
109
+ "waiting",
110
+ "stalled"
111
+ ], O = (e) => {
112
+ e.type === "waiting" || e.type === "stalled" ? s = !0 : [
113
+ "playing",
114
+ "canplay",
115
+ "seeked",
116
+ "loadeddata"
117
+ ].includes(e.type) && (s = !1), T();
118
+ };
119
+ return {
120
+ tracker: w,
121
+ sample: T,
122
+ owns: u,
123
+ page: E,
124
+ bindVideo: (e) => {
125
+ for (let e of D) c?.removeEventListener(e, O);
126
+ c && i?.unobserve(c), c = e, s = !1, c && i?.observe(c);
127
+ for (let e of D) c?.addEventListener(e, O);
128
+ T();
129
+ },
130
+ flush: () => (T(), u() ? w.flush() : Promise.resolve()),
131
+ setResume: (e) => {
132
+ l = e, T();
133
+ },
134
+ start: () => {
135
+ if (a = !0, !i && typeof IntersectionObserver < "u") {
136
+ i = new IntersectionObserver((e) => {
137
+ for (let n of e) t.set(n.target, n.isIntersecting);
138
+ T();
139
+ });
140
+ for (let t of e.values()) t.element && i.observe(t.element);
141
+ c && i.observe(c);
142
+ }
143
+ T();
144
+ },
145
+ stop: () => {
146
+ a = !1;
147
+ },
148
+ isActive: () => a,
149
+ hide: () => {
150
+ T(), o = !0, w.attention(!1), w.flush(!0);
151
+ },
152
+ show: () => {
153
+ o = !1, w.activity(), T();
154
+ },
155
+ activity: () => {
156
+ T(), w.activity();
157
+ },
158
+ cleanup: () => {
159
+ i?.disconnect();
160
+ for (let t of e.values()) t.element?.removeEventListener("load", T);
161
+ for (let e of D) c?.removeEventListener(e, O);
162
+ }
163
+ };
164
+ }, [
165
+ h,
166
+ f,
167
+ p,
168
+ v,
169
+ y,
170
+ b,
171
+ m.idleAfter,
172
+ m.checkpointInterval
173
+ ]);
174
+ s(() => h.preferences.subscribeIdentity(() => {
175
+ w.owns() || w.tracker.discard();
176
+ }), [h, w]), o(() => {
177
+ if (!b || !w.owns()) return;
178
+ w.start();
179
+ let e = setInterval(w.sample, 250), t = [
180
+ "pointerdown",
181
+ "pointermove",
182
+ "keydown",
183
+ "wheel",
184
+ "touchstart",
185
+ "touchmove"
186
+ ];
187
+ for (let e of t) window.addEventListener(e, w.activity, {
188
+ passive: !0,
189
+ capture: !0
190
+ });
191
+ let n = () => {
192
+ document.visibilityState === "hidden" ? w.hide() : w.show();
193
+ }, r = () => {
194
+ w.sample(), w.tracker.attention(!1);
195
+ };
196
+ return window.addEventListener("scroll", w.sample, !0), window.addEventListener("resize", w.sample), window.addEventListener("focus", w.show), window.addEventListener("blur", r), window.addEventListener("pagehide", w.hide), window.addEventListener("pageshow", w.show), window.addEventListener("online", w.flush), document.addEventListener("visibilitychange", n), () => {
197
+ w.stop(), clearInterval(e);
198
+ for (let e of t) window.removeEventListener(e, w.activity, !0);
199
+ window.removeEventListener("scroll", w.sample, !0), window.removeEventListener("resize", w.sample), window.removeEventListener("focus", w.show), window.removeEventListener("blur", r), window.removeEventListener("pagehide", w.hide), window.removeEventListener("pageshow", w.show), window.removeEventListener("online", w.flush), document.removeEventListener("visibilitychange", n), queueMicrotask(() => {
200
+ if (!w.isActive()) {
201
+ if (w.cleanup(), !w.owns()) {
202
+ w.tracker.discard();
203
+ return;
204
+ }
205
+ w.tracker.attention(!1), w.tracker.flush(!0).finally(() => w.tracker.discard());
206
+ }
207
+ });
208
+ };
209
+ }, [b, w]);
210
+ let T = i((e) => w.bindVideo(e), [w]);
211
+ return c(() => ({
212
+ page: w.page,
213
+ video: T,
214
+ setResume: w.setResume,
215
+ flush: w.flush
216
+ }), [w, T]);
217
+ }
218
+ //#endregion
219
+ export { f as useReadingEngagement };
220
+
221
+ //# sourceMappingURL=use-engagement.js.map
@@ -0,0 +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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"}
@@ -18,7 +18,7 @@ export declare function useResource<T>(store: ResourceStore | null, key: string
18
18
  reload: () => void;
19
19
  };
20
20
  /** A shared list, a page at a time. */
21
- export interface UseList<T> {
21
+ export interface UseList<T, M = unknown> {
22
22
  items: T[];
23
23
  /** The first page is loading (also on reload). */
24
24
  loading: boolean;
@@ -27,11 +27,12 @@ export interface UseList<T> {
27
27
  hasMore: boolean;
28
28
  loadMore: () => Promise<void>;
29
29
  reload: () => void;
30
+ metadata?: M;
30
31
  }
31
- export type PageFetcher<T> = (cursor: string | number | undefined, signal: AbortSignal) => Promise<Page<T>>;
32
+ export type PageFetcher<T, M = unknown> = (cursor: string | number | undefined, signal: AbortSignal) => Promise<Page<T, M>>;
32
33
  export declare function useList<T extends {
33
34
  id: string;
34
- }>(store: ResourceStore, key: string | null, tag: Tag, page: PageFetcher<T>): UseList<T>;
35
+ }, M = unknown>(store: ResourceStore, key: string | null, tag: Tag, page: PageFetcher<T, M>): UseList<T, M>;
35
36
  /**
36
37
  * A limit/offset list as pages. A full page means more may follow; with
37
38
  * underfill (the latest feed drops items the caller may not see) only an
@@ -64,7 +64,8 @@ function m(e, t, n, r) {
64
64
  error: a.error,
65
65
  hasMore: a.data ? a.data.next !== null : !1,
66
66
  loadMore: o,
67
- reload: a.reload
67
+ reload: a.reload,
68
+ metadata: a.data?.metadata
68
69
  };
69
70
  }
70
71
  function h(e, t, n = !1) {
@@ -1 +1 @@
1
- {"version":3,"file":"use-resource.js","names":[],"sources":["../../src/react/use-resource.ts"],"sourcesContent":["import { useCallback, useContext, useEffect, useMemo, useSyncExternalStore } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport { ContentKitContext, useContentKitClient, useReadScope } from \"./context.js\";\nimport { resourcesFor, type Fetcher, type Page, type Resource, type ResourceStore, type Tag } from \"./resources.js\";\n\n/** The client, its shared store, and the provider's viewer, language and sign-in. */\nexport function useContentScope(own?: ContentKitClient | null) {\n const ctx = useContext(ContentKitContext);\n const client = useContentKitClient(own);\n return { client, store: resourcesFor(client), scope: useReadScope(), viewer: ctx?.viewer, language: ctx?.language, onSignIn: ctx?.onSignIn };\n}\n\nconst IDLE: Resource<never> = { data: undefined, loading: false, loaded: false };\nconst none = () => () => {};\n\n/** A shared read: fetched once per key while shown; initial is shown (and seeds the store) until it loads. */\nexport function useResource<T>(\n store: ResourceStore | null,\n key: string | null,\n tag: Tag,\n fetcher: Fetcher<T>,\n o: { initial?: T; more?: (cursor: string | number, signal: AbortSignal) => Promise<Page<unknown>> } = {},\n): Resource<T> & { reload: () => void } {\n const scope = useReadScope();\n // Each key owns its closure cell: switching this subscriber to another key\n // cannot redirect a binding still used by a different subscriber.\n const source = useMemo(() => ({ fetcher, more: o.more, initial: o.initial }),\n // Fetcher identity changes on ordinary renders; only a key change creates a cell.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [store, key]);\n useEffect(() => {\n source.fetcher = fetcher;\n source.more = o.more;\n source.initial = o.initial;\n });\n const subscribe = useCallback(\n (l: () => void) => (key && store ? store.subscribe(key, { ...tag, readScope: scope }, l) : none()),\n // The tag is part of the key.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [store, key, scope],\n );\n const snapshot = useCallback(() => (key && store ? store.snapshot<T>(key) : IDLE), [store, key]);\n const entry = useSyncExternalStore(subscribe, snapshot, snapshot);\n useEffect(() => {\n if (!key || !store) return;\n const unbind = store.bind(key, (s) => source.fetcher(s), source.more && ((c, s) => source.more!(c, s)));\n if (source.initial !== undefined && !store.snapshot(key).loaded && !store.snapshot(key).loading) store.set(key, source.initial);\n else store.load(key);\n return unbind;\n }, [store, key, source]);\n const reload = useCallback(() => key && store?.load(key, true), [store, key]);\n if (!entry.loaded && o.initial !== undefined) return { ...entry, data: o.initial, reload };\n return { ...entry, reload };\n}\n\n/** A shared list, a page at a time. */\nexport interface UseList<T> {\n items: T[];\n /** The first page is loading (also on reload). */\n loading: boolean;\n loadingMore: boolean;\n error?: ContentKitError;\n hasMore: boolean;\n loadMore: () => Promise<void>;\n reload: () => void;\n}\n\nexport type PageFetcher<T> = (cursor: string | number | undefined, signal: AbortSignal) => Promise<Page<T>>;\n\n// The items of a list not loaded: one array, so effects keyed on items don't run every render.\nconst NO_ITEMS: never[] = [];\n\nexport function useList<T extends { id: string }>(store: ResourceStore, key: string | null, tag: Tag, page: PageFetcher<T>): UseList<T> {\n const r = useResource<Page<T>>(store, key, tag, (s) => page(undefined, s), { more: (c, s) => page(c, s) });\n const loadMore = useCallback(() => (key ? store.loadMore(key) : Promise.resolve()), [store, key]);\n return {\n items: r.data?.items ?? NO_ITEMS,\n loading: r.loading || (!!key && !r.loaded),\n loadingMore: !!r.loadingMore,\n error: r.error,\n hasMore: r.data ? r.data.next !== null : false,\n loadMore,\n reload: r.reload,\n };\n}\n\n/**\n * A limit/offset list as pages. A full page means more may follow; with\n * underfill (the latest feed drops items the caller may not see) only an\n * empty page ends it.\n */\nexport function offsetPages<T>(limit: number, list: (q: { limit: number; offset: number }, signal: AbortSignal) => Promise<T[]>, underfill = false): PageFetcher<T> {\n return async (cursor, signal) => {\n const offset = typeof cursor === \"number\" ? cursor : 0;\n const items = await list({ limit, offset }, signal);\n const end = underfill ? items.length === 0 : items.length < limit;\n return { items, next: end ? null : offset + limit };\n };\n}\n\n/** A stable store key. */\nexport const keyOf = (...parts: unknown[]) => JSON.stringify(parts);\n\n/** One numbered page of a list. */\nexport interface UsePaged<T> {\n items: T[];\n loading: boolean;\n error?: ContentKitError;\n /** Another page follows. */\n hasNext: boolean;\n reload: () => void;\n}\n\n/** Page `page` (from 1) of a limit/offset list: one read of limit + 1 tells whether another follows. */\nexport function usePaged<T extends { id: string }>(\n store: ResourceStore,\n key: string | null,\n tag: Tag,\n page: number,\n limit: number,\n list: (q: { limit: number; offset: number }, signal: AbortSignal) => Promise<T[]>,\n): UsePaged<T> {\n const offset = (Math.max(1, page) - 1) * limit;\n const r = useResource<Page<T>>(store, key === null ? null : `${key}#${offset}`, tag, async (signal) => {\n const items = await list({ limit: limit + 1, offset }, signal);\n return { items: items.slice(0, limit), next: items.length > limit ? offset + limit : null };\n });\n return { items: r.data?.items ?? NO_ITEMS, loading: r.loading || (!!key && !r.loaded), error: r.error, hasNext: r.data ? r.data.next !== null : false, reload: r.reload };\n}\n"],"mappings":";;;;AAOA,SAAgB,EAAgB,GAA+B;CAC7D,IAAM,IAAM,EAAW,CAAiB,GAClC,IAAS,EAAoB,CAAG;CACtC,OAAO;EAAE;EAAQ,OAAO,EAAa,CAAM;EAAG,OAAO,EAAa;EAAG,QAAQ,GAAK;EAAQ,UAAU,GAAK;EAAU,UAAU,GAAK;CAAS;AAC7I;AAEA,IAAM,IAAwB;CAAE,MAAM,KAAA;CAAW,SAAS;CAAO,QAAQ;AAAM,GACzE,gBAAmB,CAAC;AAG1B,SAAgB,EACd,GACA,GACA,GACA,GACA,IAAsG,CAAC,GACjE;CACtC,IAAM,IAAQ,EAAa,GAGrB,IAAS,SAAe;EAAE;EAAS,MAAM,EAAE;EAAM,SAAS,EAAE;CAAQ,IAGxE,CAAC,GAAO,CAAG,CAAC;CACd,QAAgB;EAGd,AAFA,EAAO,UAAU,GACjB,EAAO,OAAO,EAAE,MAChB,EAAO,UAAU,EAAE;CACrB,CAAC;CACD,IAAM,IAAY,GACf,MAAmB,KAAO,IAAQ,EAAM,UAAU,GAAK;EAAE,GAAG;EAAK,WAAW;CAAM,GAAG,CAAC,IAAI,EAAK,GAGhG;EAAC;EAAO;EAAK;CAAK,CACpB,GACM,IAAW,QAAmB,KAAO,IAAQ,EAAM,SAAY,CAAG,IAAI,GAAO,CAAC,GAAO,CAAG,CAAC,GACzF,IAAQ,EAAqB,GAAW,GAAU,CAAQ;CAChE,QAAgB;EACd,IAAI,CAAC,KAAO,CAAC,GAAO;EACpB,IAAM,IAAS,EAAM,KAAK,IAAM,MAAM,EAAO,QAAQ,CAAC,GAAG,EAAO,UAAU,GAAG,MAAM,EAAO,KAAM,GAAG,CAAC,EAAE;EAGtG,OAFI,EAAO,YAAY,KAAA,KAAa,CAAC,EAAM,SAAS,CAAG,CAAC,CAAC,UAAU,CAAC,EAAM,SAAS,CAAG,CAAC,CAAC,UAAS,EAAM,IAAI,GAAK,EAAO,OAAO,IACzH,EAAM,KAAK,CAAG,GACZ;CACT,GAAG;EAAC;EAAO;EAAK;CAAM,CAAC;CACvB,IAAM,IAAS,QAAkB,KAAO,GAAO,KAAK,GAAK,EAAI,GAAG,CAAC,GAAO,CAAG,CAAC;CAE5E,OADI,CAAC,EAAM,UAAU,EAAE,YAAY,KAAA,IAAkB;EAAE,GAAG;EAAO,MAAM,EAAE;EAAS;CAAO,IAClF;EAAE,GAAG;EAAO;CAAO;AAC5B;AAiBA,IAAM,IAAoB,CAAC;AAE3B,SAAgB,EAAkC,GAAsB,GAAoB,GAAU,GAAkC;CACtI,IAAM,IAAI,EAAqB,GAAO,GAAK,IAAM,MAAM,EAAK,KAAA,GAAW,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,EAAK,GAAG,CAAC,EAAE,CAAC,GACnG,IAAW,QAAmB,IAAM,EAAM,SAAS,CAAG,IAAI,QAAQ,QAAQ,GAAI,CAAC,GAAO,CAAG,CAAC;CAChG,OAAO;EACL,OAAO,EAAE,MAAM,SAAS;EACxB,SAAS,EAAE,WAAY,CAAC,CAAC,KAAO,CAAC,EAAE;EACnC,aAAa,CAAC,CAAC,EAAE;EACjB,OAAO,EAAE;EACT,SAAS,EAAE,OAAO,EAAE,KAAK,SAAS,OAAO;EACzC;EACA,QAAQ,EAAE;CACZ;AACF;AAOA,SAAgB,EAAe,GAAe,GAAmF,IAAY,IAAuB;CAClK,OAAO,OAAO,GAAQ,MAAW;EAC/B,IAAM,IAAS,OAAO,KAAW,WAAW,IAAS,GAC/C,IAAQ,MAAM,EAAK;GAAE;GAAO;EAAO,GAAG,CAAM;EAElD,OAAO;GAAE;GAAO,OADJ,IAAY,EAAM,WAAW,IAAI,EAAM,SAAS,KAChC,OAAO,IAAS;EAAM;CACpD;AACF;AAGA,IAAa,KAAS,GAAG,MAAqB,KAAK,UAAU,CAAK;AAalE,SAAgB,EACd,GACA,GACA,GACA,GACA,GACA,GACa;CACb,IAAM,KAAU,KAAK,IAAI,GAAG,CAAI,IAAI,KAAK,GACnC,IAAI,EAAqB,GAAO,MAAQ,OAAO,OAAO,GAAG,EAAI,GAAG,KAAU,GAAK,OAAO,MAAW;EACrG,IAAM,IAAQ,MAAM,EAAK;GAAE,OAAO,IAAQ;GAAG;EAAO,GAAG,CAAM;EAC7D,OAAO;GAAE,OAAO,EAAM,MAAM,GAAG,CAAK;GAAG,MAAM,EAAM,SAAS,IAAQ,IAAS,IAAQ;EAAK;CAC5F,CAAC;CACD,OAAO;EAAE,OAAO,EAAE,MAAM,SAAS;EAAU,SAAS,EAAE,WAAY,CAAC,CAAC,KAAO,CAAC,EAAE;EAAS,OAAO,EAAE;EAAO,SAAS,EAAE,OAAO,EAAE,KAAK,SAAS,OAAO;EAAO,QAAQ,EAAE;CAAO;AAC1K"}
1
+ {"version":3,"file":"use-resource.js","names":[],"sources":["../../src/react/use-resource.ts"],"sourcesContent":["import { useCallback, useContext, useEffect, useMemo, useSyncExternalStore } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport { ContentKitContext, useContentKitClient, useReadScope } from \"./context.js\";\nimport { resourcesFor, type Fetcher, type Page, type Resource, type ResourceStore, type Tag } from \"./resources.js\";\n\n/** The client, its shared store, and the provider's viewer, language and sign-in. */\nexport function useContentScope(own?: ContentKitClient | null) {\n const ctx = useContext(ContentKitContext);\n const client = useContentKitClient(own);\n return { client, store: resourcesFor(client), scope: useReadScope(), viewer: ctx?.viewer, language: ctx?.language, onSignIn: ctx?.onSignIn };\n}\n\nconst IDLE: Resource<never> = { data: undefined, loading: false, loaded: false };\nconst none = () => () => {};\n\n/** A shared read: fetched once per key while shown; initial is shown (and seeds the store) until it loads. */\nexport function useResource<T>(\n store: ResourceStore | null,\n key: string | null,\n tag: Tag,\n fetcher: Fetcher<T>,\n o: { initial?: T; more?: (cursor: string | number, signal: AbortSignal) => Promise<Page<unknown>> } = {},\n): Resource<T> & { reload: () => void } {\n const scope = useReadScope();\n // Each key owns its closure cell: switching this subscriber to another key\n // cannot redirect a binding still used by a different subscriber.\n const source = useMemo(() => ({ fetcher, more: o.more, initial: o.initial }),\n // Fetcher identity changes on ordinary renders; only a key change creates a cell.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [store, key]);\n useEffect(() => {\n source.fetcher = fetcher;\n source.more = o.more;\n source.initial = o.initial;\n });\n const subscribe = useCallback(\n (l: () => void) => (key && store ? store.subscribe(key, { ...tag, readScope: scope }, l) : none()),\n // The tag is part of the key.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [store, key, scope],\n );\n const snapshot = useCallback(() => (key && store ? store.snapshot<T>(key) : IDLE), [store, key]);\n const entry = useSyncExternalStore(subscribe, snapshot, snapshot);\n useEffect(() => {\n if (!key || !store) return;\n const unbind = store.bind(key, (s) => source.fetcher(s), source.more && ((c, s) => source.more!(c, s)));\n if (source.initial !== undefined && !store.snapshot(key).loaded && !store.snapshot(key).loading) store.set(key, source.initial);\n else store.load(key);\n return unbind;\n }, [store, key, source]);\n const reload = useCallback(() => key && store?.load(key, true), [store, key]);\n if (!entry.loaded && o.initial !== undefined) return { ...entry, data: o.initial, reload };\n return { ...entry, reload };\n}\n\n/** A shared list, a page at a time. */\nexport interface UseList<T, M = unknown> {\n items: T[];\n /** The first page is loading (also on reload). */\n loading: boolean;\n loadingMore: boolean;\n error?: ContentKitError;\n hasMore: boolean;\n loadMore: () => Promise<void>;\n reload: () => void;\n metadata?: M;\n}\n\nexport type PageFetcher<T, M = unknown> = (cursor: string | number | undefined, signal: AbortSignal) => Promise<Page<T, M>>;\n\n// The items of a list not loaded: one array, so effects keyed on items don't run every render.\nconst NO_ITEMS: never[] = [];\n\nexport function useList<T extends { id: string }, M = unknown>(store: ResourceStore, key: string | null, tag: Tag, page: PageFetcher<T, M>): UseList<T, M> {\n const r = useResource<Page<T, M>>(store, key, tag, (s) => page(undefined, s), { more: (c, s) => page(c, s) });\n const loadMore = useCallback(() => (key ? store.loadMore(key) : Promise.resolve()), [store, key]);\n return {\n items: r.data?.items ?? NO_ITEMS,\n loading: r.loading || (!!key && !r.loaded),\n loadingMore: !!r.loadingMore,\n error: r.error,\n hasMore: r.data ? r.data.next !== null : false,\n loadMore,\n reload: r.reload,\n metadata: r.data?.metadata,\n };\n}\n\n/**\n * A limit/offset list as pages. A full page means more may follow; with\n * underfill (the latest feed drops items the caller may not see) only an\n * empty page ends it.\n */\nexport function offsetPages<T>(limit: number, list: (q: { limit: number; offset: number }, signal: AbortSignal) => Promise<T[]>, underfill = false): PageFetcher<T> {\n return async (cursor, signal) => {\n const offset = typeof cursor === \"number\" ? cursor : 0;\n const items = await list({ limit, offset }, signal);\n const end = underfill ? items.length === 0 : items.length < limit;\n return { items, next: end ? null : offset + limit };\n };\n}\n\n/** A stable store key. */\nexport const keyOf = (...parts: unknown[]) => JSON.stringify(parts);\n\n/** One numbered page of a list. */\nexport interface UsePaged<T> {\n items: T[];\n loading: boolean;\n error?: ContentKitError;\n /** Another page follows. */\n hasNext: boolean;\n reload: () => void;\n}\n\n/** Page `page` (from 1) of a limit/offset list: one read of limit + 1 tells whether another follows. */\nexport function usePaged<T extends { id: string }>(\n store: ResourceStore,\n key: string | null,\n tag: Tag,\n page: number,\n limit: number,\n list: (q: { limit: number; offset: number }, signal: AbortSignal) => Promise<T[]>,\n): UsePaged<T> {\n const offset = (Math.max(1, page) - 1) * limit;\n const r = useResource<Page<T>>(store, key === null ? null : `${key}#${offset}`, tag, async (signal) => {\n const items = await list({ limit: limit + 1, offset }, signal);\n return { items: items.slice(0, limit), next: items.length > limit ? offset + limit : null };\n });\n return { items: r.data?.items ?? NO_ITEMS, loading: r.loading || (!!key && !r.loaded), error: r.error, hasNext: r.data ? r.data.next !== null : false, reload: r.reload };\n}\n"],"mappings":"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"}
package/dist/react.js CHANGED
@@ -18,14 +18,15 @@ import { useFavorite as se, useReaction as ce } from "./react/engagement.js";
18
18
  import { usePoll as le, usePollEditor as ue, usePolls as de, withVote as fe } from "./react/polls.js";
19
19
  import { useVersionParam as pe } from "./react/location.js";
20
20
  import { useAdminLocation as me, useFlowClient as he, useStaffAccess as ge } from "./react/admin.js";
21
- import { beaconsFor as _e, useBeacons as ve, useContentFilters as ye, useContents as be, useDailyReleases as xe, useExposure as Se, useHistory as Ce, useLockedCount as we, useMyFavorites as Te, useMyReactions as Ee, useRecentSearches as De, useRecommendations as Oe, useReleaseDates as ke, useSearch as Ae, useSimilar as je, useTypeahead as Me, useViewSignal as Ne } from "./react/discovery.js";
22
- import { defaultVersion as Pe, editionOf as Fe, findVersion as Ie, liveVersions as Le, useContentDetail as Re } from "./react/detail.js";
23
- import { downloadMessage as ze, nextRelease as Be, startDownload as Ve, useReaderPlan as He, useSegmentPages as Ue, useVersionDownload as We } from "./react/versions.js";
24
- import { usePageReview as Ge } from "./react/page-review.js";
25
- import { useActivity as Ke, useMyStats as qe, useTopContents as Je } from "./react/stats.js";
26
- import { useContent as Ye, useManagedContents as Xe } from "./react/contents.js";
27
- import { useNotificationUnread as Ze, useNotifications as Qe } from "./react/notifications.js";
28
- import { ContentKitProvider as $e } from "./react/provider.js";
29
- import { useReaderPreferences as et } from "./react/reader-preferences.js";
30
- import { useCanonicalContent as tt } from "./react/canonical.js";
31
- export { $e as ContentKitProvider, R as GALLERY_VIEW_KEY, i as GuardContext, e as HOVER_DELAY, t as InlinePreviewContext, z as PLAYER_QUALITY_KEY, h as ResourceStore, _e as beaconsFor, Pe as defaultVersion, ze as downloadMessage, A as editOutput, Fe as editionOf, Ie as findVersion, Le as liveVersions, y as memberId, G as nameIn, Be as nextRelease, g as resourcesFor, N as round3, Ve as startDownload, K as thumbnailOf, Ke as useActivity, Y as useAdminComments, me as useAdminLocation, D as useAvatar, X as useBannedWords, ve as useBeacons, Z as useCanComment, tt as useCanonicalContent, B as useCarousel, Q as useCommentBans, $ as useCommentReplies, ee as useComments, Ye as useContent, oe as useContentConfig, Re as useContentDetail, ye as useContentFilters, b as useContentGroups, a as useContentKit, o as useContentKitClient, _ as useContentScope, s as useContentURLs, be as useContents, d as useCrop, xe as useDailyReleases, W as useEditorCrop, f as useEncodeProgress, c as useErrorReporter, Se as useExposure, se as useFavorite, he as useFlowClient, P as useFrameStrip, V as useGalleryView, te as useGlobalCommentBan, x as useGroup, S as useGroupMembers, C as useGroups, l as useGuarded, Ce as useHistory, H as useHlsPlayer, ne as useIPBans, n as useInlinePreview, w as useKinds, re as useLatestComments, we as useLockedCount, Xe as useManagedContents, q as useMediaFolder, E as useMediaRead, ie as useModerationQueue, Te as useMyFavorites, T as useMyGroups, Ee as useMyReactions, qe as useMyStats, J as useNearViewport, Ze as useNotificationUnread, Qe as useNotifications, u as useOptionalContentKitClient, Ge as usePageReview, v as usePaged, le as usePoll, ue as usePollEditor, de as usePolls, O as usePresets, k as usePublicImage, ce as useReaction, He as useReaderPlan, et as useReaderPreferences, De as useRecentSearches, Oe as useRecommendations, r as useReducedMotion, U as useRefreshBeforeExpiry, ke as useReleaseDates, Ae as useSearch, Ue as useSegmentPages, je as useSimilar, j as useSlotCrop, M as useSlotImage, ge as useStaffAccess, Je as useTopContents, Me as useTypeahead, p as useUpload, m as useUploadQueue, We as useVersionDownload, pe as useVersionParam, F as useVideoFrame, I as useVideoImages, L as useVideoPoster, Ne as useViewSignal, ae as withReaction, fe as withVote };
21
+ import { beaconsFor as _e, useBeacons as ve, useContentFilters as ye, useContents as be, useDailyReleases as xe, useExposure as Se, useHistory as Ce, useLockedCount as we, useMyFavorites as Te, useMyReactions as Ee, useRecentSearches as De, useRecommendations as Oe, useReleaseDates as ke, useSearch as Ae, useSimilar as je, useTypeahead as Me } from "./react/discovery.js";
22
+ import { defaultVersion as Ne, editionOf as Pe, findVersion as Fe, liveVersions as Ie, useContentDetail as Le } from "./react/detail.js";
23
+ import { downloadMessage as Re, nextRelease as ze, startDownload as Be, useReaderPlan as Ve, useSegmentPages as He, useVersionDownload as Ue } from "./react/versions.js";
24
+ import { usePageReview as We } from "./react/page-review.js";
25
+ import { useActivity as Ge, useMyStats as Ke, useTopContents as qe } from "./react/stats.js";
26
+ import { useContent as Je, useManagedContents as Ye } from "./react/contents.js";
27
+ import { useNotificationUnread as Xe, useNotifications as Ze } from "./react/notifications.js";
28
+ import { ContentKitProvider as Qe } from "./react/provider.js";
29
+ import { useReaderPreferences as $e } from "./react/reader-preferences.js";
30
+ import { useCanonicalContent as et } from "./react/canonical.js";
31
+ import { useReadingEngagement as tt } from "./react/use-engagement.js";
32
+ export { Qe as ContentKitProvider, R as GALLERY_VIEW_KEY, i as GuardContext, e as HOVER_DELAY, t as InlinePreviewContext, z as PLAYER_QUALITY_KEY, h as ResourceStore, _e as beaconsFor, Ne as defaultVersion, Re as downloadMessage, A as editOutput, Pe as editionOf, Fe as findVersion, Ie as liveVersions, y as memberId, G as nameIn, ze as nextRelease, g as resourcesFor, N as round3, Be as startDownload, K as thumbnailOf, Ge as useActivity, Y as useAdminComments, me as useAdminLocation, D as useAvatar, X as useBannedWords, ve as useBeacons, Z as useCanComment, et as useCanonicalContent, B as useCarousel, Q as useCommentBans, $ as useCommentReplies, ee as useComments, Je as useContent, oe as useContentConfig, Le as useContentDetail, ye as useContentFilters, b as useContentGroups, a as useContentKit, o as useContentKitClient, _ as useContentScope, s as useContentURLs, be as useContents, d as useCrop, xe as useDailyReleases, W as useEditorCrop, f as useEncodeProgress, c as useErrorReporter, Se as useExposure, se as useFavorite, he as useFlowClient, P as useFrameStrip, V as useGalleryView, te as useGlobalCommentBan, x as useGroup, S as useGroupMembers, C as useGroups, l as useGuarded, Ce as useHistory, H as useHlsPlayer, ne as useIPBans, n as useInlinePreview, w as useKinds, re as useLatestComments, we as useLockedCount, Ye as useManagedContents, q as useMediaFolder, E as useMediaRead, ie as useModerationQueue, Te as useMyFavorites, T as useMyGroups, Ee as useMyReactions, Ke as useMyStats, J as useNearViewport, Xe as useNotificationUnread, Ze as useNotifications, u as useOptionalContentKitClient, We as usePageReview, v as usePaged, le as usePoll, ue as usePollEditor, de as usePolls, O as usePresets, k as usePublicImage, ce as useReaction, Ve as useReaderPlan, $e as useReaderPreferences, tt as useReadingEngagement, De as useRecentSearches, Oe as useRecommendations, r as useReducedMotion, U as useRefreshBeforeExpiry, ke as useReleaseDates, Ae as useSearch, He as useSegmentPages, je as useSimilar, j as useSlotCrop, M as useSlotImage, ge as useStaffAccess, qe as useTopContents, Me as useTypeahead, p as useUpload, m as useUploadQueue, Ue as useVersionDownload, pe as useVersionParam, F as useVideoFrame, I as useVideoImages, L as useVideoPoster, ae as withReaction, fe as withVote };