@openrails/contentkit-ui 0.0.0-stage → 0.68.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.
- package/README.md +470 -2
- package/dist/appearance.d.ts +38 -0
- package/dist/appearance.js +15 -0
- package/dist/appearance.js.map +1 -0
- package/dist/assets/hash.worker-BYfo-7MQ.js.map +1 -0
- package/dist/client/aspect.d.ts +6 -0
- package/dist/client/aspect.js +17 -0
- package/dist/client/aspect.js.map +1 -0
- package/dist/client/client.d.ts +53 -0
- package/dist/client/client.js +36 -0
- package/dist/client/client.js.map +1 -0
- package/dist/client/codes.d.ts +12 -0
- package/dist/client/codes.js +19 -0
- package/dist/client/codes.js.map +1 -0
- package/dist/client/content/bans.d.ts +15 -0
- package/dist/client/content/bans.js +40 -0
- package/dist/client/content/bans.js.map +1 -0
- package/dist/client/content/comments.d.ts +36 -0
- package/dist/client/content/comments.js +95 -0
- package/dist/client/content/comments.js.map +1 -0
- package/dist/client/content/favorites.d.ts +14 -0
- package/dist/client/content/favorites.js +42 -0
- package/dist/client/content/favorites.js.map +1 -0
- package/dist/client/content/moderation.d.ts +18 -0
- package/dist/client/content/moderation.js +37 -0
- package/dist/client/content/moderation.js.map +1 -0
- package/dist/client/content/polls.d.ts +47 -0
- package/dist/client/content/polls.js +151 -0
- package/dist/client/content/polls.js.map +1 -0
- package/dist/client/content/posts.d.ts +44 -0
- package/dist/client/content/posts.js +92 -0
- package/dist/client/content/posts.js.map +1 -0
- package/dist/client/content/reactions.d.ts +12 -0
- package/dist/client/content/reactions.js +36 -0
- package/dist/client/content/reactions.js.map +1 -0
- package/dist/client/content/types.d.ts +91 -0
- package/dist/client/content/types.js +8 -0
- package/dist/client/content/types.js.map +1 -0
- package/dist/client/crop.d.ts +32 -0
- package/dist/client/crop.js +103 -0
- package/dist/client/crop.js.map +1 -0
- package/dist/client/errors.d.ts +54 -0
- package/dist/client/errors.js +80 -0
- package/dist/client/errors.js.map +1 -0
- package/dist/client/gallery.d.ts +43 -0
- package/dist/client/gallery.js +86 -0
- package/dist/client/gallery.js.map +1 -0
- package/dist/client/generated/error-codes.d.ts +5 -0
- package/dist/client/generated/error-codes.js +131 -0
- package/dist/client/generated/error-codes.js.map +1 -0
- package/dist/client/generated/routes.d.ts +1086 -0
- package/dist/client/generated/routes.js +1400 -0
- package/dist/client/generated/routes.js.map +1 -0
- package/dist/client/generated/wire.d.ts +630 -0
- package/dist/client/generated/wire.js +6 -0
- package/dist/client/generated/wire.js.map +1 -0
- package/dist/client/http.d.ts +83 -0
- package/dist/client/http.js +82 -0
- package/dist/client/http.js.map +1 -0
- package/dist/client/image.d.ts +24 -0
- package/dist/client/image.js +65 -0
- package/dist/client/image.js.map +1 -0
- package/dist/client/index.d.ts +33 -0
- package/dist/client/media/api.d.ts +36 -0
- package/dist/client/media/api.js +61 -0
- package/dist/client/media/api.js.map +1 -0
- package/dist/client/media/client.d.ts +247 -0
- package/dist/client/media/client.js +407 -0
- package/dist/client/media/client.js.map +1 -0
- package/dist/client/media/encode.d.ts +3 -0
- package/dist/client/media/encode.js +8 -0
- package/dist/client/media/encode.js.map +1 -0
- package/dist/client/media/hash-stream.d.ts +6 -0
- package/dist/client/media/hash-stream.js +16 -0
- package/dist/client/media/hash-stream.js.map +1 -0
- package/dist/client/media/hash.d.ts +12 -0
- package/dist/client/media/hash.js +48 -0
- package/dist/client/media/hash.js.map +1 -0
- package/dist/client/media/hash.worker-inline.js +18 -0
- package/dist/client/media/hash.worker-inline.js.map +1 -0
- package/dist/client/media/pacer.d.ts +21 -0
- package/dist/client/media/pacer.js +25 -0
- package/dist/client/media/pacer.js.map +1 -0
- package/dist/client/media/queue.d.ts +112 -0
- package/dist/client/media/queue.js +274 -0
- package/dist/client/media/queue.js.map +1 -0
- package/dist/client/media/ref.d.ts +6 -0
- package/dist/client/media/ref.js +10 -0
- package/dist/client/media/ref.js.map +1 -0
- package/dist/client/media/rules.d.ts +54 -0
- package/dist/client/media/rules.js +143 -0
- package/dist/client/media/rules.js.map +1 -0
- package/dist/client/media/transport.d.ts +13 -0
- package/dist/client/media/transport.js +33 -0
- package/dist/client/media/transport.js.map +1 -0
- package/dist/client/media/windows.d.ts +15 -0
- package/dist/client/media/windows.js +39 -0
- package/dist/client/media/windows.js.map +1 -0
- package/dist/client/playback.d.ts +200 -0
- package/dist/client/playback.js +142 -0
- package/dist/client/playback.js.map +1 -0
- package/dist/client/player.d.ts +102 -0
- package/dist/client/player.js +155 -0
- package/dist/client/player.js.map +1 -0
- package/dist/client/public.d.ts +25 -0
- package/dist/client/public.js +18 -0
- package/dist/client/public.js.map +1 -0
- package/dist/client/rendition.d.ts +19 -0
- package/dist/client/rendition.js +16 -0
- package/dist/client/rendition.js.map +1 -0
- package/dist/client/route.d.ts +39 -0
- package/dist/client/route.js +31 -0
- package/dist/client/route.js.map +1 -0
- package/dist/client/taxonomy.d.ts +30 -0
- package/dist/client/taxonomy.js +77 -0
- package/dist/client/taxonomy.js.map +1 -0
- package/dist/client.js +33 -0
- package/dist/components/encode-progress.d.ts +15 -0
- package/dist/components/encode-progress.js +49 -0
- package/dist/components/encode-progress.js.map +1 -0
- package/dist/components/hover-preview.d.ts +29 -0
- package/dist/components/hover-preview.js +49 -0
- package/dist/components/hover-preview.js.map +1 -0
- package/dist/components/image-crop-dialog.d.ts +48 -0
- package/dist/components/image-crop-dialog.js +359 -0
- package/dist/components/image-crop-dialog.js.map +1 -0
- package/dist/components/lazy-mount.d.ts +10 -0
- package/dist/components/lazy-mount.js +22 -0
- package/dist/components/lazy-mount.js.map +1 -0
- package/dist/components/media-folder-editor.d.ts +59 -0
- package/dist/components/media-folder-editor.js +611 -0
- package/dist/components/media-folder-editor.js.map +1 -0
- package/dist/components/media-gallery.d.ts +54 -0
- package/dist/components/media-gallery.js +597 -0
- package/dist/components/media-gallery.js.map +1 -0
- package/dist/components/media-readiness-notice.d.ts +24 -0
- package/dist/components/media-readiness-notice.js +61 -0
- package/dist/components/media-readiness-notice.js.map +1 -0
- package/dist/components/player-controls.d.ts +71 -0
- package/dist/components/player-controls.js +264 -0
- package/dist/components/player-controls.js.map +1 -0
- package/dist/components/progress.d.ts +5 -0
- package/dist/components/progress.js +14 -0
- package/dist/components/progress.js.map +1 -0
- package/dist/components/rendition-img.d.ts +34 -0
- package/dist/components/rendition-img.js +78 -0
- package/dist/components/rendition-img.js.map +1 -0
- package/dist/components/slot-editor.d.ts +95 -0
- package/dist/components/slot-editor.js +182 -0
- package/dist/components/slot-editor.js.map +1 -0
- package/dist/components/slot-image.d.ts +23 -0
- package/dist/components/slot-image.js +48 -0
- package/dist/components/slot-image.js.map +1 -0
- package/dist/components/slot-upload.d.ts +41 -0
- package/dist/components/slot-upload.js +215 -0
- package/dist/components/slot-upload.js.map +1 -0
- package/dist/components/social/bans.d.ts +42 -0
- package/dist/components/social/bans.js +300 -0
- package/dist/components/social/bans.js.map +1 -0
- package/dist/components/social/comments.d.ts +42 -0
- package/dist/components/social/comments.js +517 -0
- package/dist/components/social/comments.js.map +1 -0
- package/dist/components/social/engagement.d.ts +45 -0
- package/dist/components/social/engagement.js +129 -0
- package/dist/components/social/engagement.js.map +1 -0
- package/dist/components/social/index.d.ts +6 -0
- package/dist/components/social/moderation.d.ts +32 -0
- package/dist/components/social/moderation.js +383 -0
- package/dist/components/social/moderation.js.map +1 -0
- package/dist/components/social/parts.d.ts +50 -0
- package/dist/components/social/parts.js +106 -0
- package/dist/components/social/parts.js.map +1 -0
- package/dist/components/social/poll-editor.d.ts +34 -0
- package/dist/components/social/poll-editor.js +567 -0
- package/dist/components/social/poll-editor.js.map +1 -0
- package/dist/components/social/poll.d.ts +30 -0
- package/dist/components/social/poll.js +252 -0
- package/dist/components/social/poll.js.map +1 -0
- package/dist/components/sortable-list.d.ts +29 -0
- package/dist/components/sortable-list.js +121 -0
- package/dist/components/sortable-list.js.map +1 -0
- package/dist/components/video-mini-player.d.ts +35 -0
- package/dist/components/video-mini-player.js +236 -0
- package/dist/components/video-mini-player.js.map +1 -0
- package/dist/components/video-player.d.ts +146 -0
- package/dist/components/video-player.js +630 -0
- package/dist/components/video-player.js.map +1 -0
- package/dist/components/video-poster-picker.d.ts +59 -0
- package/dist/components/video-poster-picker.js +335 -0
- package/dist/components/video-poster-picker.js.map +1 -0
- package/dist/components/video-poster.d.ts +24 -0
- package/dist/components/video-poster.js +46 -0
- package/dist/components/video-poster.js.map +1 -0
- package/dist/i18n/context.d.ts +4 -0
- package/dist/i18n/context.js +11 -0
- package/dist/i18n/context.js.map +1 -0
- package/dist/i18n/messages.d.ts +40 -0
- package/dist/i18n/messages.js +94 -0
- package/dist/i18n/messages.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +41 -0
- package/dist/locales/de.d.ts +2 -0
- package/dist/locales/de.js +271 -0
- package/dist/locales/de.js.map +1 -0
- package/dist/locales/en.d.ts +457 -0
- package/dist/locales/en.js +271 -0
- package/dist/locales/en.js.map +1 -0
- package/dist/locales/es.d.ts +2 -0
- package/dist/locales/es.js +271 -0
- package/dist/locales/es.js.map +1 -0
- package/dist/locales/ja.d.ts +2 -0
- package/dist/locales/ja.js +271 -0
- package/dist/locales/ja.js.map +1 -0
- package/dist/locales/ko.d.ts +2 -0
- package/dist/locales/ko.js +271 -0
- package/dist/locales/ko.js.map +1 -0
- package/dist/locales/social/de.d.ts +2 -0
- package/dist/locales/social/de.js +198 -0
- package/dist/locales/social/de.js.map +1 -0
- package/dist/locales/social/en.d.ts +195 -0
- package/dist/locales/social/en.js +198 -0
- package/dist/locales/social/en.js.map +1 -0
- package/dist/locales/social/es.d.ts +2 -0
- package/dist/locales/social/es.js +198 -0
- package/dist/locales/social/es.js.map +1 -0
- package/dist/locales/social/ja.d.ts +2 -0
- package/dist/locales/social/ja.js +198 -0
- package/dist/locales/social/ja.js.map +1 -0
- package/dist/locales/social/ko.d.ts +2 -0
- package/dist/locales/social/ko.js +198 -0
- package/dist/locales/social/ko.js.map +1 -0
- package/dist/locales/social/zh.d.ts +2 -0
- package/dist/locales/social/zh.js +198 -0
- package/dist/locales/social/zh.js.map +1 -0
- package/dist/locales/zh.d.ts +2 -0
- package/dist/locales/zh.js +271 -0
- package/dist/locales/zh.js.map +1 -0
- package/dist/provider.d.ts +22 -0
- package/dist/provider.js +59 -0
- package/dist/provider.js.map +1 -0
- package/dist/react/canonical.d.ts +33 -0
- package/dist/react/canonical.js +78 -0
- package/dist/react/canonical.js.map +1 -0
- package/dist/react/comments.d.ts +82 -0
- package/dist/react/comments.js +155 -0
- package/dist/react/comments.js.map +1 -0
- package/dist/react/config.d.ts +13 -0
- package/dist/react/config.js +14 -0
- package/dist/react/config.js.map +1 -0
- package/dist/react/context.d.ts +55 -0
- package/dist/react/context.js +42 -0
- package/dist/react/context.js.map +1 -0
- package/dist/react/crop.d.ts +49 -0
- package/dist/react/crop.js +107 -0
- package/dist/react/crop.js.map +1 -0
- package/dist/react/engagement.d.ts +38 -0
- package/dist/react/engagement.js +88 -0
- package/dist/react/engagement.js.map +1 -0
- package/dist/react/folder.d.ts +103 -0
- package/dist/react/folder.js +240 -0
- package/dist/react/folder.js.map +1 -0
- package/dist/react/gallery.d.ts +140 -0
- package/dist/react/gallery.js +477 -0
- package/dist/react/gallery.js.map +1 -0
- package/dist/react/index.d.ts +21 -0
- package/dist/react/inline-preview.d.ts +24 -0
- package/dist/react/inline-preview.js +76 -0
- package/dist/react/inline-preview.js.map +1 -0
- package/dist/react/player.d.ts +70 -0
- package/dist/react/player.js +214 -0
- package/dist/react/player.js.map +1 -0
- package/dist/react/polls.d.ts +74 -0
- package/dist/react/polls.js +176 -0
- package/dist/react/polls.js.map +1 -0
- package/dist/react/posts.d.ts +59 -0
- package/dist/react/posts.js +149 -0
- package/dist/react/posts.js.map +1 -0
- package/dist/react/provider.d.ts +27 -0
- package/dist/react/provider.js +49 -0
- package/dist/react/provider.js.map +1 -0
- package/dist/react/public.d.ts +39 -0
- package/dist/react/public.js +70 -0
- package/dist/react/public.js.map +1 -0
- package/dist/react/read.d.ts +52 -0
- package/dist/react/read.js +144 -0
- package/dist/react/read.js.map +1 -0
- package/dist/react/resources.d.ts +73 -0
- package/dist/react/resources.js +247 -0
- package/dist/react/resources.js.map +1 -0
- package/dist/react/slot.d.ts +104 -0
- package/dist/react/slot.js +185 -0
- package/dist/react/slot.js.map +1 -0
- package/dist/react/store.d.ts +51 -0
- package/dist/react/store.js +165 -0
- package/dist/react/store.js.map +1 -0
- package/dist/react/upload.d.ts +86 -0
- package/dist/react/upload.js +155 -0
- package/dist/react/upload.js.map +1 -0
- package/dist/react/use-resource.d.ts +44 -0
- package/dist/react/use-resource.js +74 -0
- package/dist/react/use-resource.js.map +1 -0
- package/dist/react/video.d.ts +118 -0
- package/dist/react/video.js +184 -0
- package/dist/react/video.js.map +1 -0
- package/dist/react/viewport.d.ts +19 -0
- package/dist/react/viewport.js +30 -0
- package/dist/react/viewport.js.map +1 -0
- package/dist/react.js +21 -0
- package/dist/scope.d.ts +14 -0
- package/dist/scope.js +30 -0
- package/dist/scope.js.map +1 -0
- package/dist/styles.css +3 -0
- package/dist/ui/alert-dialog.d.ts +18 -0
- package/dist/ui/alert-dialog.js +86 -0
- package/dist/ui/alert-dialog.js.map +1 -0
- package/dist/ui/alert.d.ts +10 -0
- package/dist/ui/alert.js +38 -0
- package/dist/ui/alert.js.map +1 -0
- package/dist/ui/avatar.d.ts +11 -0
- package/dist/ui/avatar.js +31 -0
- package/dist/ui/avatar.js.map +1 -0
- package/dist/ui/badge.d.ts +7 -0
- package/dist/ui/badge.js +31 -0
- package/dist/ui/badge.js.map +1 -0
- package/dist/ui/button.d.ts +8 -0
- package/dist/ui/button.js +46 -0
- package/dist/ui/button.js.map +1 -0
- package/dist/ui/dialog.d.ts +17 -0
- package/dist/ui/dialog.js +91 -0
- package/dist/ui/dialog.js.map +1 -0
- package/dist/ui/dropdown-menu.d.ts +15 -0
- package/dist/ui/dropdown-menu.js +105 -0
- package/dist/ui/dropdown-menu.js.map +1 -0
- package/dist/ui/input.d.ts +3 -0
- package/dist/ui/input.js +17 -0
- package/dist/ui/input.js.map +1 -0
- package/dist/ui/label.d.ts +3 -0
- package/dist/ui/label.js +15 -0
- package/dist/ui/label.js.map +1 -0
- package/dist/ui/progress.d.ts +7 -0
- package/dist/ui/progress.js +31 -0
- package/dist/ui/progress.js.map +1 -0
- package/dist/ui/select.d.ts +15 -0
- package/dist/ui/select.js +98 -0
- package/dist/ui/select.js.map +1 -0
- package/dist/ui/skeleton.d.ts +2 -0
- package/dist/ui/skeleton.js +14 -0
- package/dist/ui/skeleton.js.map +1 -0
- package/dist/ui/slider.d.ts +3 -0
- package/dist/ui/slider.js +35 -0
- package/dist/ui/slider.js.map +1 -0
- package/dist/ui/switch.d.ts +5 -0
- package/dist/ui/switch.js +20 -0
- package/dist/ui/switch.js.map +1 -0
- package/dist/ui/tabs.d.ts +10 -0
- package/dist/ui/tabs.js +47 -0
- package/dist/ui/tabs.js.map +1 -0
- package/dist/ui/textarea.d.ts +3 -0
- package/dist/ui/textarea.js +15 -0
- package/dist/ui/textarea.js.map +1 -0
- package/dist/ui/toggle-group.d.ts +5 -0
- package/dist/ui/toggle-group.js +23 -0
- package/dist/ui/toggle-group.js.map +1 -0
- package/dist/urls/index.d.ts +100 -0
- package/dist/urls.js +118 -0
- package/dist/urls.js.map +1 -0
- package/package.json +120 -3
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { defaultImage as e, isPattern as t, ruleDir as n } from "../client/media/rules.js";
|
|
2
|
+
import { useOptionalContentKitClient as r } from "./context.js";
|
|
3
|
+
import { useMediaRead as i } from "./read.js";
|
|
4
|
+
import { useEffect as a, useMemo as o, useState as s } from "react";
|
|
5
|
+
//#region src/react/public.ts
|
|
6
|
+
var c = /* @__PURE__ */ new WeakMap(), l = /* @__PURE__ */ new WeakMap();
|
|
7
|
+
function u(e = {}) {
|
|
8
|
+
let t = r(e.client), [, n] = s(0), i = t ? c.get(t) : void 0;
|
|
9
|
+
return a(() => {
|
|
10
|
+
if (!t || c.has(t)) return;
|
|
11
|
+
let e = !0, r = l.get(t);
|
|
12
|
+
return r || (r = t.media.presets().then((e) => void c.set(t, { presets: e }), (e) => void c.set(t, {
|
|
13
|
+
presets: [],
|
|
14
|
+
error: e
|
|
15
|
+
})).finally(() => l.delete(t)), l.set(t, r)), r.then(() => e && n((e) => e + 1)), () => {
|
|
16
|
+
e = !1;
|
|
17
|
+
};
|
|
18
|
+
}, [t]), {
|
|
19
|
+
presets: i?.presets ?? [],
|
|
20
|
+
loaded: !!i || !t,
|
|
21
|
+
error: i?.error
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function d(a, s, c, l = {}) {
|
|
25
|
+
let d = r(l.client), { presets: f, loaded: p } = u({ client: d }), m = f.find((e) => e.kind === a && e.name === c), h = l.image !== void 0, g = m ? t(m.from) ? n(m.from) : m.from : void 0, _ = i({
|
|
26
|
+
kind: a,
|
|
27
|
+
id: s
|
|
28
|
+
}, {
|
|
29
|
+
prefix: g,
|
|
30
|
+
read: h || !p ? null : l.read,
|
|
31
|
+
client: d
|
|
32
|
+
});
|
|
33
|
+
return {
|
|
34
|
+
...o(() => {
|
|
35
|
+
let t = h ? l.image : _.read?.public?.find((e) => e.preset === c);
|
|
36
|
+
return t && t.renditions.length ? {
|
|
37
|
+
image: {
|
|
38
|
+
preset: c,
|
|
39
|
+
renditions: t.renditions,
|
|
40
|
+
aspect: "aspect" in t && t.aspect || m?.aspect
|
|
41
|
+
},
|
|
42
|
+
isDefault: !1
|
|
43
|
+
} : !h && !_.read ? {
|
|
44
|
+
image: null,
|
|
45
|
+
isDefault: !1
|
|
46
|
+
} : m ? {
|
|
47
|
+
image: e(m, s),
|
|
48
|
+
isDefault: !0
|
|
49
|
+
} : {
|
|
50
|
+
image: null,
|
|
51
|
+
isDefault: !1
|
|
52
|
+
};
|
|
53
|
+
}, [
|
|
54
|
+
h,
|
|
55
|
+
l.image,
|
|
56
|
+
_.read,
|
|
57
|
+
c,
|
|
58
|
+
m,
|
|
59
|
+
s
|
|
60
|
+
]),
|
|
61
|
+
rule: m,
|
|
62
|
+
loading: !p || _.loading,
|
|
63
|
+
error: _.error,
|
|
64
|
+
reload: _.reload
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
//#endregion
|
|
68
|
+
export { u as usePresets, d as usePublicImage };
|
|
69
|
+
|
|
70
|
+
//# sourceMappingURL=public.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public.js","names":[],"sources":["../../src/react/public.ts"],"sourcesContent":["import { useEffect, useMemo, useState } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport type { ReadResult } from \"../client/generated/wire.js\";\nimport type { PresetRule } from \"../client/generated/wire.js\";\nimport { defaultImage, isPattern, ruleDir } from \"../client/media/rules.js\";\nimport type { PublicPreset } from \"../client/public.js\";\nimport { useOptionalContentKitClient } from \"./context.js\";\nimport { useMediaRead } from \"./read.js\";\n\nexport interface UsePresets {\n /** Every kind's public presets; empty until they load (or when the server has none). */\n presets: PresetRule[];\n loaded: boolean;\n error?: ContentKitError;\n}\n\nconst settled = new WeakMap<ContentKitClient, { presets: PresetRule[]; error?: ContentKitError }>();\nconst inflight = new WeakMap<ContentKitClient, Promise<void>>();\n\n/** The public presets (`GET /media/presets`), fetched once per client. */\nexport function usePresets(o: { client?: ContentKitClient | null } = {}): UsePresets {\n const client = useOptionalContentKitClient(o.client);\n const [, rerender] = useState(0);\n const done = client ? settled.get(client) : undefined;\n useEffect(() => {\n if (!client || settled.has(client)) return;\n let live = true;\n let load = inflight.get(client);\n if (!load) {\n load = client.media.presets().then(\n (presets) => void settled.set(client, { presets }),\n (error: ContentKitError) => void settled.set(client, { presets: [], error }),\n ).finally(() => inflight.delete(client));\n inflight.set(client, load);\n }\n void load.then(() => live && rerender((n) => n + 1));\n return () => {\n live = false;\n };\n }, [client]);\n return { presets: done?.presets ?? [], loaded: !!done || !client, error: done?.error };\n}\n\nexport interface PublicImageOptions {\n /** The host's listing of the item's image (its API's): shown without a read. */\n image?: PublicPreset | null;\n /** A read of the item the host already has. */\n read?: ReadResult | null;\n /** Overrides the provider's client. */\n client?: ContentKitClient | null;\n}\n\nexport interface UsePublicImage {\n /** The item's current renditions; the kind's default image when it has none; null while unknown. */\n image: PublicPreset | null;\n /** The preset's rules (aspect, widths, min_width) once the presets load. */\n rule?: PresetRule;\n /** image is the kind's default, not the item's own. */\n isDefault: boolean;\n loading: boolean;\n error?: ContentKitError;\n reload: () => void;\n}\n\n/**\n * An item's public image for a preset (\"avatar\", \"cover\"): the exact\n * renditions from a read's `public` (or the host's listing), else the kind's\n * default image. Reads refresh after commits through the same client.\n */\nexport function usePublicImage(kind: string, id: string, preset: string, o: PublicImageOptions = {}): UsePublicImage {\n const client = useOptionalContentKitClient(o.client);\n const { presets, loaded } = usePresets({ client });\n const rule = presets.find((p) => p.kind === kind && p.name === preset);\n const listed = o.image !== undefined;\n // The read waits for the presets, so it lists only the preset's upload.\n const prefix = rule ? (isPattern(rule.from) ? ruleDir(rule.from) : rule.from) : undefined;\n const r = useMediaRead({ kind, id }, { prefix, read: listed || !loaded ? null : o.read, client });\n const image = useMemo((): { image: PublicPreset | null; isDefault: boolean } => {\n const own = listed ? o.image : r.read?.public?.find((p) => p.preset === preset);\n if (own && own.renditions.length) return { image: { preset, renditions: own.renditions, aspect: (\"aspect\" in own && own.aspect) || rule?.aspect }, isDefault: false };\n if (!listed && !r.read) return { image: null, isDefault: false };\n return rule ? { image: defaultImage(rule, id), isDefault: true } : { image: null, isDefault: false };\n }, [listed, o.image, r.read, preset, rule, id]);\n return { ...image, rule, loading: !loaded || r.loading, error: r.error, reload: r.reload };\n}\n"],"mappings":";;;;;AAiBA,IAAM,oBAAU,IAAI,QAA8E,GAC5F,oBAAW,IAAI,QAAyC;AAG9D,SAAgB,EAAW,IAA0C,CAAC,GAAe;CACnF,IAAM,IAAS,EAA4B,EAAE,MAAM,GAC7C,GAAG,KAAY,EAAS,CAAC,GACzB,IAAO,IAAS,EAAQ,IAAI,CAAM,IAAI,KAAA;CAiB5C,OAhBA,QAAgB;EACd,IAAI,CAAC,KAAU,EAAQ,IAAI,CAAM,GAAG;EACpC,IAAI,IAAO,IACP,IAAO,EAAS,IAAI,CAAM;EAS9B,OARK,MACH,IAAO,EAAO,MAAM,QAAQ,CAAC,CAAC,MAC3B,MAAY,KAAK,EAAQ,IAAI,GAAQ,EAAE,WAAQ,CAAC,IAChD,MAA2B,KAAK,EAAQ,IAAI,GAAQ;GAAE,SAAS,CAAC;GAAG;EAAM,CAAC,CAC7E,CAAC,CAAC,cAAc,EAAS,OAAO,CAAM,CAAC,GACvC,EAAS,IAAI,GAAQ,CAAI,IAE3B,EAAU,WAAW,KAAQ,GAAU,MAAM,IAAI,CAAC,CAAC,SACtC;GACX,IAAO;EACT;CACF,GAAG,CAAC,CAAM,CAAC,GACJ;EAAE,SAAS,GAAM,WAAW,CAAC;EAAG,QAAQ,CAAC,CAAC,KAAQ,CAAC;EAAQ,OAAO,GAAM;CAAM;AACvF;AA4BA,SAAgB,EAAe,GAAc,GAAY,GAAgB,IAAwB,CAAC,GAAmB;CACnH,IAAM,IAAS,EAA4B,EAAE,MAAM,GAC7C,EAAE,YAAS,cAAW,EAAW,EAAE,UAAO,CAAC,GAC3C,IAAO,EAAQ,MAAM,MAAM,EAAE,SAAS,KAAQ,EAAE,SAAS,CAAM,GAC/D,IAAS,EAAE,UAAU,KAAA,GAErB,IAAS,IAAQ,EAAU,EAAK,IAAI,IAAI,EAAQ,EAAK,IAAI,IAAI,EAAK,OAAQ,KAAA,GAC1E,IAAI,EAAa;EAAE;EAAM;CAAG,GAAG;EAAE;EAAQ,MAAM,KAAU,CAAC,IAAS,OAAO,EAAE;EAAM;CAAO,CAAC;CAOhG,OAAO;EAAE,GANK,QAAkE;GAC9E,IAAM,IAAM,IAAS,EAAE,QAAQ,EAAE,MAAM,QAAQ,MAAM,MAAM,EAAE,WAAW,CAAM;GAG9E,OAFI,KAAO,EAAI,WAAW,SAAe;IAAE,OAAO;KAAE;KAAQ,YAAY,EAAI;KAAY,QAAS,YAAY,KAAO,EAAI,UAAW,GAAM;IAAO;IAAG,WAAW;GAAM,IAChK,CAAC,KAAU,CAAC,EAAE,OAAa;IAAE,OAAO;IAAM,WAAW;GAAM,IACxD,IAAO;IAAE,OAAO,EAAa,GAAM,CAAE;IAAG,WAAW;GAAK,IAAI;IAAE,OAAO;IAAM,WAAW;GAAM;EACrG,GAAG;GAAC;GAAQ,EAAE;GAAO,EAAE;GAAM;GAAQ;GAAM;EAAE,CACjC;EAAO;EAAM,SAAS,CAAC,KAAU,EAAE;EAAS,OAAO,EAAE;EAAO,QAAQ,EAAE;CAAO;AAC3F"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { ContentKitClient } from '../client/client.js';
|
|
2
|
+
import { ContentKitError } from '../client/errors.js';
|
|
3
|
+
import { ReadResult, RefBody } from '../client/generated/wire.js';
|
|
4
|
+
import { ReadOptions } from '../client/media/api.js';
|
|
5
|
+
/** Files [start, end) get URLs. */
|
|
6
|
+
export interface ReadWindow {
|
|
7
|
+
start: number;
|
|
8
|
+
end: number;
|
|
9
|
+
/** Files per read. Default 50. */
|
|
10
|
+
chunk?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface MediaReadOptions extends Omit<ReadOptions, "offset" | "limit"> {
|
|
13
|
+
/**
|
|
14
|
+
* Read in windows of `chunk` files and merge them: the files in
|
|
15
|
+
* [start, end) get URLs, and windows once read stay loaded (and refreshed)
|
|
16
|
+
* until the item or options change. Without it, one read of the server's
|
|
17
|
+
* default range (or offset and limit).
|
|
18
|
+
*/
|
|
19
|
+
window?: ReadWindow;
|
|
20
|
+
offset?: number;
|
|
21
|
+
limit?: number;
|
|
22
|
+
/** Poll at this interval (ms) while uploads process. Default 2500 for editor reads, else off. */
|
|
23
|
+
poll?: number | false;
|
|
24
|
+
/** Read again shortly before the URLs expire. Default true. */
|
|
25
|
+
refresh?: boolean;
|
|
26
|
+
/** A read the host already has: shown until reload() or set(). */
|
|
27
|
+
read?: ReadResult | null;
|
|
28
|
+
/** Overrides the provider's client. */
|
|
29
|
+
client?: ContentKitClient | null;
|
|
30
|
+
}
|
|
31
|
+
export interface UseMediaRead {
|
|
32
|
+
/** The read (windows merged); null until it loads or when it failed. */
|
|
33
|
+
read: ReadResult | null;
|
|
34
|
+
loading: boolean;
|
|
35
|
+
error?: ContentKitError;
|
|
36
|
+
/** Uploads are still processing (editor reads). */
|
|
37
|
+
processing: boolean;
|
|
38
|
+
/** Reads again now, restarting a read in flight. */
|
|
39
|
+
reload: () => void;
|
|
40
|
+
/** Reads again unless a read is in flight: a player's grant refresh. */
|
|
41
|
+
refresh: () => void;
|
|
42
|
+
/** Replaces the read without refetching, for every hook showing it. */
|
|
43
|
+
set: (r: ReadResult) => void;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A read of the item, shared with every hook reading the same item and
|
|
47
|
+
* options: refreshed before its URLs expire, retried after a read rate limit,
|
|
48
|
+
* polled while uploads process, reloaded after a commit through the client.
|
|
49
|
+
* A window splits it into merged reads. Without a client (none passed, no
|
|
50
|
+
* provider) it only shows a supplied read.
|
|
51
|
+
*/
|
|
52
|
+
export declare function useMediaRead(ref: RefBody, o?: MediaReadOptions): UseMediaRead;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { useOptionalContentKitClient as e, useReadScope as t } from "./context.js";
|
|
2
|
+
import { chunkOffsets as n, mergeReads as ee, processing as r } from "../client/media/windows.js";
|
|
3
|
+
import { readKey as i, storeFor as a } from "./store.js";
|
|
4
|
+
import { useCallback as o, useEffect as s, useMemo as c, useRef as l, useState as u, useSyncExternalStore as d } from "react";
|
|
5
|
+
//#region src/react/read.ts
|
|
6
|
+
var te = 2500, f = [];
|
|
7
|
+
function p(p, m = {}) {
|
|
8
|
+
let { read: h, client: g, prefix: _, offset: v, limit: y, download: b, editor: x, window: S, refresh: C = !0 } = m, w = m.poll ?? (x ? te : !1), T = e(g), E = t(), D = T ? a(T) : null, O = S?.chunk ?? 50, k = E + i(p, {
|
|
9
|
+
prefix: _,
|
|
10
|
+
download: b,
|
|
11
|
+
editor: x
|
|
12
|
+
}) + (S ? `#${O}` : `#${v ?? 0}:${y ?? 0}`), [A, j] = u({ base: k }), M = A.base === k ? A.n : void 0, N = S ? n(S.start, S.end, M ?? S.start + 1, O) : [], [P, F] = u({
|
|
13
|
+
base: k,
|
|
14
|
+
offsets: []
|
|
15
|
+
}), I = P.base === k ? P.offsets : [];
|
|
16
|
+
S && (P.base !== k || N.some((e) => !I.includes(e))) && F({
|
|
17
|
+
base: k,
|
|
18
|
+
offsets: [.../* @__PURE__ */ new Set([...I, ...N])].sort((e, t) => e - t)
|
|
19
|
+
});
|
|
20
|
+
let L = S ? I.length ? I : N.length ? N : [0] : [v ?? 0], R = c(() => L.map((e) => ({
|
|
21
|
+
offset: e,
|
|
22
|
+
limit: S ? O : y
|
|
23
|
+
})), [L.join(","), S ? O : y]), z = c(() => R.map((e) => E + i(p, {
|
|
24
|
+
prefix: _,
|
|
25
|
+
download: b,
|
|
26
|
+
editor: x,
|
|
27
|
+
offset: e.offset,
|
|
28
|
+
limit: e.limit
|
|
29
|
+
})), [
|
|
30
|
+
E,
|
|
31
|
+
R,
|
|
32
|
+
p.kind,
|
|
33
|
+
p.id,
|
|
34
|
+
_,
|
|
35
|
+
b,
|
|
36
|
+
x
|
|
37
|
+
]), [B, V] = u(null), [H, U] = u(null), W = B?.base === k && B.given === h ? B : null, G = !!D && (h === void 0 || !!W), K = o((e) => {
|
|
38
|
+
if (!D || !G) return () => {};
|
|
39
|
+
let t = z.map((t, n) => D.subscribe(t, {
|
|
40
|
+
kind: p.kind,
|
|
41
|
+
id: p.id
|
|
42
|
+
}, {
|
|
43
|
+
prefix: _,
|
|
44
|
+
download: b,
|
|
45
|
+
editor: x,
|
|
46
|
+
...R[n]
|
|
47
|
+
}, e, {
|
|
48
|
+
poll: w,
|
|
49
|
+
refresh: C
|
|
50
|
+
}, E));
|
|
51
|
+
return () => t.forEach((e) => e());
|
|
52
|
+
}, [
|
|
53
|
+
D,
|
|
54
|
+
G,
|
|
55
|
+
z,
|
|
56
|
+
w,
|
|
57
|
+
C
|
|
58
|
+
]), q = l(f), J = o(() => {
|
|
59
|
+
if (!D || !G) return f;
|
|
60
|
+
let e = z.map((e) => D.snapshot(e)), t = q.current;
|
|
61
|
+
return t.length === e.length && t.every((t, n) => t === e[n]) ? t : q.current = e;
|
|
62
|
+
}, [
|
|
63
|
+
D,
|
|
64
|
+
G,
|
|
65
|
+
z
|
|
66
|
+
]), Y = d(K, J, J);
|
|
67
|
+
s(() => {
|
|
68
|
+
if (D && G) for (let e of z) D.load(e, W ? "reload" : "initial");
|
|
69
|
+
}, [
|
|
70
|
+
D,
|
|
71
|
+
G,
|
|
72
|
+
z,
|
|
73
|
+
W
|
|
74
|
+
]);
|
|
75
|
+
let X = c(() => ee(Y.map((e) => e.read)), [Y]);
|
|
76
|
+
s(() => {
|
|
77
|
+
S && X && X.total !== M && j({
|
|
78
|
+
base: k,
|
|
79
|
+
n: X.total
|
|
80
|
+
});
|
|
81
|
+
}, [
|
|
82
|
+
S,
|
|
83
|
+
X,
|
|
84
|
+
M,
|
|
85
|
+
k
|
|
86
|
+
]);
|
|
87
|
+
let ne = o(() => {
|
|
88
|
+
if (h !== void 0) V({
|
|
89
|
+
base: k,
|
|
90
|
+
given: h
|
|
91
|
+
});
|
|
92
|
+
else if (D) for (let e of z) D.load(e, "reload");
|
|
93
|
+
U(null);
|
|
94
|
+
}, [
|
|
95
|
+
D,
|
|
96
|
+
z,
|
|
97
|
+
k,
|
|
98
|
+
h
|
|
99
|
+
]), re = o(() => {
|
|
100
|
+
if (h !== void 0 && !W) V({
|
|
101
|
+
base: k,
|
|
102
|
+
given: h
|
|
103
|
+
});
|
|
104
|
+
else if (D) for (let e of z) D.load(e, "refresh");
|
|
105
|
+
}, [
|
|
106
|
+
D,
|
|
107
|
+
z,
|
|
108
|
+
k,
|
|
109
|
+
h,
|
|
110
|
+
W
|
|
111
|
+
]), ie = o((e) => {
|
|
112
|
+
if (D && G) for (let t of z) D.set(t, e);
|
|
113
|
+
else U({
|
|
114
|
+
base: k,
|
|
115
|
+
given: h,
|
|
116
|
+
read: e
|
|
117
|
+
});
|
|
118
|
+
}, [
|
|
119
|
+
D,
|
|
120
|
+
G,
|
|
121
|
+
z,
|
|
122
|
+
k,
|
|
123
|
+
h
|
|
124
|
+
]), Z, Q = !1, $;
|
|
125
|
+
if (H && H.base === k && H.given === h) Z = H.read;
|
|
126
|
+
else if (!Y.length) Z = h ?? null;
|
|
127
|
+
else {
|
|
128
|
+
let e = Y.every((e) => e.loaded);
|
|
129
|
+
Z = X ?? (e ? null : h ?? null), Q = Y.some((e) => e.loading || !e.loaded), $ = Y.find((e) => e.error)?.error;
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
read: Z,
|
|
133
|
+
loading: Q,
|
|
134
|
+
error: $,
|
|
135
|
+
processing: r(Z),
|
|
136
|
+
reload: ne,
|
|
137
|
+
refresh: re,
|
|
138
|
+
set: ie
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
//#endregion
|
|
142
|
+
export { p as useMediaRead };
|
|
143
|
+
|
|
144
|
+
//# sourceMappingURL=read.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read.js","names":[],"sources":["../../src/react/read.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport type { ReadResult, RefBody } from \"../client/generated/wire.js\";\nimport type { ReadOptions } from \"../client/media/api.js\";\nimport { chunkOffsets, mergeReads, processing as isProcessing, READ_CHUNK } from \"../client/media/windows.js\";\nimport { useOptionalContentKitClient, useReadScope } from \"./context.js\";\nimport { readKey, storeFor, type ReadEntry } from \"./store.js\";\n\n/** Files [start, end) get URLs. */\nexport interface ReadWindow {\n start: number;\n end: number;\n /** Files per read. Default 50. */\n chunk?: number;\n}\n\nexport interface MediaReadOptions extends Omit<ReadOptions, \"offset\" | \"limit\"> {\n /**\n * Read in windows of `chunk` files and merge them: the files in\n * [start, end) get URLs, and windows once read stay loaded (and refreshed)\n * until the item or options change. Without it, one read of the server's\n * default range (or offset and limit).\n */\n window?: ReadWindow;\n offset?: number;\n limit?: number;\n /** Poll at this interval (ms) while uploads process. Default 2500 for editor reads, else off. */\n poll?: number | false;\n /** Read again shortly before the URLs expire. Default true. */\n refresh?: boolean;\n /** A read the host already has: shown until reload() or set(). */\n read?: ReadResult | null;\n /** Overrides the provider's client. */\n client?: ContentKitClient | null;\n}\n\nexport interface UseMediaRead {\n /** The read (windows merged); null until it loads or when it failed. */\n read: ReadResult | null;\n loading: boolean;\n error?: ContentKitError;\n /** Uploads are still processing (editor reads). */\n processing: boolean;\n /** Reads again now, restarting a read in flight. */\n reload: () => void;\n /** Reads again unless a read is in flight: a player's grant refresh. */\n refresh: () => void;\n /** Replaces the read without refetching, for every hook showing it. */\n set: (r: ReadResult) => void;\n}\n\nconst POLL = 2500;\nconst NONE: readonly ReadEntry[] = [];\n\n/**\n * A read of the item, shared with every hook reading the same item and\n * options: refreshed before its URLs expire, retried after a read rate limit,\n * polled while uploads process, reloaded after a commit through the client.\n * A window splits it into merged reads. Without a client (none passed, no\n * provider) it only shows a supplied read.\n */\nexport function useMediaRead(ref: RefBody, o: MediaReadOptions = {}): UseMediaRead {\n const { read: given, client: own, prefix, offset, limit, download, editor, window, refresh: refreshing = true } = o;\n const poll = o.poll ?? (editor ? POLL : false);\n const client = useOptionalContentKitClient(own);\n const scope = useReadScope();\n const store = client ? storeFor(client) : null;\n const chunk = window?.chunk ?? READ_CHUNK;\n const base = scope + readKey(ref, { prefix, download, editor }) + (window ? `#${chunk}` : `#${offset ?? 0}:${limit ?? 0}`);\n\n // Windows once wanted stay loaded until the item or options change.\n const [total, setTotal] = useState<{ base: string; n?: number }>({ base });\n const known = total.base === base ? total.n : undefined;\n // Until the first read states the total, only the window's first chunk.\n const wanted = window ? chunkOffsets(window.start, window.end, known ?? window.start + 1, chunk) : [];\n const [kept, setKept] = useState<{ base: string; offsets: number[] }>({ base, offsets: [] });\n const prior = kept.base === base ? kept.offsets : [];\n if (window && (kept.base !== base || wanted.some((w) => !prior.includes(w)))) {\n setKept({ base, offsets: [...new Set([...prior, ...wanted])].sort((a, b) => a - b) });\n }\n const offsets = window ? (prior.length ? prior : wanted.length ? wanted : [0]) : [offset ?? 0];\n const windows = useMemo(\n () => offsets.map((off) => ({ offset: off, limit: window ? chunk : limit })),\n // offsets is derived from the stable kept list\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [offsets.join(\",\"), window ? chunk : limit],\n );\n const keys = useMemo(() => windows.map((w) => scope + readKey(ref, { prefix, download, editor, offset: w.offset, limit: w.limit })), [scope, windows, ref.kind, ref.id, prefix, download, editor]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // A supplied read is shown until reload() asks the server or set() replaces it.\n const [requested, setRequested] = useState<{ base: string; given?: ReadResult | null } | null>(null);\n const [local, setLocal] = useState<{ base: string; given?: ReadResult | null; read: ReadResult } | null>(null);\n const asked = requested?.base === base && requested.given === given ? requested : null;\n const fetching = !!store && (given === undefined || !!asked);\n\n const subscribe = useCallback(\n (l: () => void) => {\n if (!store || !fetching) return () => {};\n const offs = keys.map((key, i) => store.subscribe(key, { kind: ref.kind, id: ref.id }, { prefix, download, editor, ...windows[i] }, l, { poll, refresh: refreshing }, scope));\n return () => offs.forEach((off) => off());\n },\n // ref and options are in keys.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [store, fetching, keys, poll, refreshing],\n );\n const last = useRef<readonly ReadEntry[]>(NONE);\n const snapshot = useCallback(() => {\n if (!store || !fetching) return NONE;\n const next = keys.map((k) => store.snapshot(k));\n const prev = last.current;\n if (prev.length === next.length && prev.every((e, i) => e === next[i])) return prev;\n return (last.current = next);\n }, [store, fetching, keys]);\n const entries = useSyncExternalStore(subscribe, snapshot, snapshot);\n useEffect(() => {\n if (store && fetching) for (const k of keys) store.load(k, asked ? \"reload\" : \"initial\");\n }, [store, fetching, keys, asked]);\n\n const merged = useMemo(() => mergeReads(entries.map((e) => e.read)), [entries]);\n useEffect(() => {\n if (window && merged && merged.total !== known) setTotal({ base, n: merged.total });\n }, [window, merged, known, base]);\n\n const reload = useCallback(() => {\n if (given !== undefined) setRequested({ base, given });\n else if (store) for (const k of keys) store.load(k, \"reload\");\n setLocal(null);\n }, [store, keys, base, given]);\n const refresh = useCallback(() => {\n if (given !== undefined && !asked) setRequested({ base, given });\n else if (store) for (const k of keys) store.load(k, \"refresh\");\n }, [store, keys, base, given, asked]);\n const set = useCallback(\n (read: ReadResult) => {\n if (store && fetching) for (const k of keys) store.set(k, read);\n else setLocal({ base, given, read });\n },\n [store, fetching, keys, base, given],\n );\n\n let read: ReadResult | null;\n let loading = false;\n let error: ContentKitError | undefined;\n if (local && local.base === base && local.given === given) read = local.read;\n else if (!entries.length) read = given ?? null;\n else {\n const loaded = entries.every((e) => e.loaded);\n read = merged ?? (loaded ? null : (given ?? null));\n loading = entries.some((e) => e.loading || !e.loaded);\n error = entries.find((e) => e.error)?.error;\n }\n return { read, loading, error, processing: isProcessing(read), reload, refresh, set };\n}\n"],"mappings":";;;;;AAoDA,IAAM,KAAO,MACP,IAA6B,CAAC;AASpC,SAAgB,EAAa,GAAc,IAAsB,CAAC,GAAiB;CACjF,IAAM,EAAE,MAAM,GAAO,QAAQ,GAAK,WAAQ,WAAQ,UAAO,aAAU,WAAQ,WAAQ,SAAS,IAAa,OAAS,GAC5G,IAAO,EAAE,SAAS,IAAS,KAAO,KAClC,IAAS,EAA4B,CAAG,GACxC,IAAQ,EAAa,GACrB,IAAQ,IAAS,EAAS,CAAM,IAAI,MACpC,IAAQ,GAAQ,SAAA,IAChB,IAAO,IAAQ,EAAQ,GAAK;EAAE;EAAQ;EAAU;CAAO,CAAC,KAAK,IAAS,IAAI,MAAU,IAAI,KAAU,EAAE,GAAG,KAAS,MAGhH,CAAC,GAAO,KAAY,EAAuC,EAAE,QAAK,CAAC,GACnE,IAAQ,EAAM,SAAS,IAAO,EAAM,IAAI,KAAA,GAExC,IAAS,IAAS,EAAa,EAAO,OAAO,EAAO,KAAK,KAAS,EAAO,QAAQ,GAAG,CAAK,IAAI,CAAC,GAC9F,CAAC,GAAM,KAAW,EAA8C;EAAE;EAAM,SAAS,CAAC;CAAE,CAAC,GACrF,IAAQ,EAAK,SAAS,IAAO,EAAK,UAAU,CAAC;CACnD,AAAI,MAAW,EAAK,SAAS,KAAQ,EAAO,MAAM,MAAM,CAAC,EAAM,SAAS,CAAC,CAAC,MACxE,EAAQ;EAAE;EAAM,SAAS,CAAC,mBAAG,IAAI,IAAI,CAAC,GAAG,GAAO,GAAG,CAAM,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC;CAAE,CAAC;CAEtF,IAAM,IAAU,IAAU,EAAM,SAAS,IAAQ,EAAO,SAAS,IAAS,CAAC,CAAC,IAAK,CAAC,KAAU,CAAC,GACvF,IAAU,QACR,EAAQ,KAAK,OAAS;EAAE,QAAQ;EAAK,OAAO,IAAS,IAAQ;CAAM,EAAE,GAG3E,CAAC,EAAQ,KAAK,GAAG,GAAG,IAAS,IAAQ,CAAK,CAC5C,GACM,IAAO,QAAc,EAAQ,KAAK,MAAM,IAAQ,EAAQ,GAAK;EAAE;EAAQ;EAAU;EAAQ,QAAQ,EAAE;EAAQ,OAAO,EAAE;CAAM,CAAC,CAAC,GAAG;EAAC;EAAO;EAAS,EAAI;EAAM,EAAI;EAAI;EAAQ;EAAU;CAAM,CAAC,GAG3L,CAAC,GAAW,KAAgB,EAA6D,IAAI,GAC7F,CAAC,GAAO,KAAY,EAA+E,IAAI,GACvG,IAAQ,GAAW,SAAS,KAAQ,EAAU,UAAU,IAAQ,IAAY,MAC5E,IAAW,CAAC,CAAC,MAAU,MAAU,KAAA,KAAa,CAAC,CAAC,IAEhD,IAAY,GACf,MAAkB;EACjB,IAAI,CAAC,KAAS,CAAC,GAAU,aAAa,CAAC;EACvC,IAAM,IAAO,EAAK,KAAK,GAAK,MAAM,EAAM,UAAU,GAAK;GAAE,MAAM,EAAI;GAAM,IAAI,EAAI;EAAG,GAAG;GAAE;GAAQ;GAAU;GAAQ,GAAG,EAAQ;EAAG,GAAG,GAAG;GAAE;GAAM,SAAS;EAAW,GAAG,CAAK,CAAC;EAC5K,aAAa,EAAK,SAAS,MAAQ,EAAI,CAAC;CAC1C,GAGA;EAAC;EAAO;EAAU;EAAM;EAAM;CAAU,CAC1C,GACM,IAAO,EAA6B,CAAI,GACxC,IAAW,QAAkB;EACjC,IAAI,CAAC,KAAS,CAAC,GAAU,OAAO;EAChC,IAAM,IAAO,EAAK,KAAK,MAAM,EAAM,SAAS,CAAC,CAAC,GACxC,IAAO,EAAK;EAElB,OADI,EAAK,WAAW,EAAK,UAAU,EAAK,OAAO,GAAG,MAAM,MAAM,EAAK,EAAE,IAAU,IACvE,EAAK,UAAU;CACzB,GAAG;EAAC;EAAO;EAAU;CAAI,CAAC,GACpB,IAAU,EAAqB,GAAW,GAAU,CAAQ;CAClE,QAAgB;EACd,IAAI,KAAS,GAAU,KAAK,IAAM,KAAK,GAAM,EAAM,KAAK,GAAG,IAAQ,WAAW,SAAS;CACzF,GAAG;EAAC;EAAO;EAAU;EAAM;CAAK,CAAC;CAEjC,IAAM,IAAS,QAAc,GAAW,EAAQ,KAAK,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAO,CAAC;CAC9E,QAAgB;EACd,AAAI,KAAU,KAAU,EAAO,UAAU,KAAO,EAAS;GAAE;GAAM,GAAG,EAAO;EAAM,CAAC;CACpF,GAAG;EAAC;EAAQ;EAAQ;EAAO;CAAI,CAAC;CAEhC,IAAM,KAAS,QAAkB;EAC/B,IAAI,MAAU,KAAA,GAAW,EAAa;GAAE;GAAM;EAAM,CAAC;OAChD,IAAI,GAAO,KAAK,IAAM,KAAK,GAAM,EAAM,KAAK,GAAG,QAAQ;EAC5D,EAAS,IAAI;CACf,GAAG;EAAC;EAAO;EAAM;EAAM;CAAK,CAAC,GACvB,KAAU,QAAkB;EAChC,IAAI,MAAU,KAAA,KAAa,CAAC,GAAO,EAAa;GAAE;GAAM;EAAM,CAAC;OAC1D,IAAI,GAAO,KAAK,IAAM,KAAK,GAAM,EAAM,KAAK,GAAG,SAAS;CAC/D,GAAG;EAAC;EAAO;EAAM;EAAM;EAAO;CAAK,CAAC,GAC9B,KAAM,GACT,MAAqB;EACpB,IAAI,KAAS,GAAU,KAAK,IAAM,KAAK,GAAM,EAAM,IAAI,GAAG,CAAI;OACzD,EAAS;GAAE;GAAM;GAAO;EAAK,CAAC;CACrC,GACA;EAAC;EAAO;EAAU;EAAM;EAAM;CAAK,CACrC,GAEI,GACA,IAAU,IACV;CACJ,IAAI,KAAS,EAAM,SAAS,KAAQ,EAAM,UAAU,GAAO,IAAO,EAAM;MACnE,IAAI,CAAC,EAAQ,QAAQ,IAAO,KAAS;MACrC;EACH,IAAM,IAAS,EAAQ,OAAO,MAAM,EAAE,MAAM;EAG5C,AAFA,IAAO,MAAW,IAAS,OAAQ,KAAS,OAC5C,IAAU,EAAQ,MAAM,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GACpD,IAAQ,EAAQ,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE;CACxC;CACA,OAAO;EAAE;EAAM;EAAS;EAAO,YAAY,EAAa,CAAI;EAAG;EAAQ;EAAS;CAAI;AACtF"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { ContentKitClient } from '../client/client.js';
|
|
2
|
+
import { ContentKitError } from '../client/errors.js';
|
|
3
|
+
import { RefBody } from '../client/generated/wire.js';
|
|
4
|
+
/** One shared read: its data, whether a fetch is in flight, and its last failure. */
|
|
5
|
+
export interface Resource<T> {
|
|
6
|
+
data: T | undefined;
|
|
7
|
+
loading: boolean;
|
|
8
|
+
/** A fetch or set has completed. */
|
|
9
|
+
loaded: boolean;
|
|
10
|
+
/** A list's next page is in flight. */
|
|
11
|
+
loadingMore?: boolean;
|
|
12
|
+
error?: ContentKitError;
|
|
13
|
+
}
|
|
14
|
+
/** A page of a list and how to get the next one (null: no more). */
|
|
15
|
+
export interface Page<T> {
|
|
16
|
+
items: T[];
|
|
17
|
+
next: string | number | null;
|
|
18
|
+
}
|
|
19
|
+
export type Fetcher<T> = (signal: AbortSignal) => Promise<T>;
|
|
20
|
+
/** What a slot holds, for matching changes: its resource type and the ids it depends on. */
|
|
21
|
+
export interface Tag {
|
|
22
|
+
type: string;
|
|
23
|
+
readScope?: string;
|
|
24
|
+
ref?: RefBody;
|
|
25
|
+
id?: string;
|
|
26
|
+
scope?: string;
|
|
27
|
+
}
|
|
28
|
+
interface Slot {
|
|
29
|
+
tag: Tag;
|
|
30
|
+
listeners: Set<() => void>;
|
|
31
|
+
entry: Resource<unknown>;
|
|
32
|
+
fetcher?: Fetcher<unknown>;
|
|
33
|
+
more?: (cursor: string | number, signal: AbortSignal) => Promise<Page<unknown>>;
|
|
34
|
+
ctl?: AbortController;
|
|
35
|
+
moreCtl?: AbortController;
|
|
36
|
+
/** Bumped by mark(): an optimistic write rolls back only if no newer one started. */
|
|
37
|
+
version: number;
|
|
38
|
+
/** Identifies the full read; item patches and appended pages retain it. */
|
|
39
|
+
readVersion: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The content reads hooks share, per client: one request per key while any
|
|
43
|
+
* hook shows it, optimistic writes with rollback, and every change the client
|
|
44
|
+
* makes applied in place (or refetched) for every hook showing it.
|
|
45
|
+
*/
|
|
46
|
+
export declare class ResourceStore {
|
|
47
|
+
private readonly slots;
|
|
48
|
+
private version;
|
|
49
|
+
constructor(client: ContentKitClient);
|
|
50
|
+
subscribe(key: string, tag: Tag, listener: () => void): () => void;
|
|
51
|
+
snapshot<T>(key: string): Resource<T>;
|
|
52
|
+
/** The latest fetchers of a key: its first read, and a list's next page. */
|
|
53
|
+
bind(key: string, fetcher: Fetcher<unknown>, more?: Slot["more"]): void;
|
|
54
|
+
/** Fetches unless loaded or in flight; force refetches (a list from its first page). */
|
|
55
|
+
load(key: string, force?: boolean): void;
|
|
56
|
+
/** Appends a list's next page; resolves once it landed. */
|
|
57
|
+
loadMore(key: string): Promise<void>;
|
|
58
|
+
/** Replaces a key's data for every hook showing it. */
|
|
59
|
+
set<T>(key: string, data: T | ((prev: T | undefined) => T)): void;
|
|
60
|
+
/** Starts an optimistic write on key: the token rollback() checks. */
|
|
61
|
+
mark(key: string): number;
|
|
62
|
+
/** Restores data unless a newer optimistic write started since mark(). */
|
|
63
|
+
rollback<T>(key: string, token: number, data: T): void;
|
|
64
|
+
/** Applies fn to matching slots, with the read identity for scoped rollback. */
|
|
65
|
+
patch<T>(match: (tag: Tag) => boolean, fn: (data: T, readVersion: number) => T): void;
|
|
66
|
+
/** Refetches every shown slot whose tag matches. */
|
|
67
|
+
invalidate(match: (tag: Tag) => boolean): void;
|
|
68
|
+
private apply;
|
|
69
|
+
private update;
|
|
70
|
+
}
|
|
71
|
+
/** The client's shared content store. */
|
|
72
|
+
export declare function resourcesFor(client: ContentKitClient): ResourceStore;
|
|
73
|
+
export {};
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { toContentKitError as e } from "../client/errors.js";
|
|
2
|
+
//#region src/react/resources.ts
|
|
3
|
+
var t = {
|
|
4
|
+
data: void 0,
|
|
5
|
+
loading: !1,
|
|
6
|
+
loaded: !1
|
|
7
|
+
}, n = (e, t) => !!e && !!t && e.kind === t.kind && e.id === t.id, r = class {
|
|
8
|
+
slots = /* @__PURE__ */ new Map();
|
|
9
|
+
version = 0;
|
|
10
|
+
constructor(e) {
|
|
11
|
+
e.subscribe((e, t) => this.apply(e, t));
|
|
12
|
+
}
|
|
13
|
+
subscribe(e, n, r) {
|
|
14
|
+
let i = this.slots.get(e);
|
|
15
|
+
i || this.slots.set(e, i = {
|
|
16
|
+
tag: n,
|
|
17
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
18
|
+
entry: t,
|
|
19
|
+
version: 0,
|
|
20
|
+
readVersion: ++this.version
|
|
21
|
+
});
|
|
22
|
+
let a = i;
|
|
23
|
+
return a.listeners.add(r), () => {
|
|
24
|
+
a.listeners.delete(r), queueMicrotask(() => {
|
|
25
|
+
a.listeners.size || this.slots.get(e) !== a || (a.ctl?.abort(), a.moreCtl?.abort(), this.slots.delete(e));
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
snapshot(e) {
|
|
30
|
+
return this.slots.get(e)?.entry ?? t;
|
|
31
|
+
}
|
|
32
|
+
bind(e, t, n) {
|
|
33
|
+
let r = this.slots.get(e);
|
|
34
|
+
r && Object.assign(r, {
|
|
35
|
+
fetcher: t,
|
|
36
|
+
more: n
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
load(t, n = !1) {
|
|
40
|
+
let r = this.slots.get(t);
|
|
41
|
+
if (!r?.fetcher || !n && (r.entry.loaded || r.ctl)) return;
|
|
42
|
+
r.ctl?.abort(), r.moreCtl?.abort(), r.moreCtl = void 0;
|
|
43
|
+
let i = r.ctl = new AbortController();
|
|
44
|
+
this.update(r, {
|
|
45
|
+
loading: !0,
|
|
46
|
+
loadingMore: !1,
|
|
47
|
+
error: void 0
|
|
48
|
+
}), r.fetcher(i.signal).then((e) => {
|
|
49
|
+
r.ctl === i && (r.ctl = void 0, r.readVersion = ++this.version, this.update(r, {
|
|
50
|
+
data: e,
|
|
51
|
+
loading: !1,
|
|
52
|
+
loaded: !0,
|
|
53
|
+
error: void 0
|
|
54
|
+
}));
|
|
55
|
+
}, (t) => {
|
|
56
|
+
if (r.ctl !== i) return;
|
|
57
|
+
r.ctl = void 0;
|
|
58
|
+
let n = e(t);
|
|
59
|
+
n.code !== "aborted" && this.update(r, {
|
|
60
|
+
loading: !1,
|
|
61
|
+
loaded: !0,
|
|
62
|
+
error: n
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
async loadMore(t) {
|
|
67
|
+
let n = this.slots.get(t), r = n?.entry.data;
|
|
68
|
+
if (!n?.more || !r || r.next === null || n.moreCtl || n.ctl) return;
|
|
69
|
+
let i = n.moreCtl = new AbortController();
|
|
70
|
+
this.update(n, { loadingMore: !0 });
|
|
71
|
+
try {
|
|
72
|
+
let e = await n.more(r.next, i.signal);
|
|
73
|
+
if (n.moreCtl !== i) return;
|
|
74
|
+
let t = n.entry.data, a = new Set(t.items.map((e) => e.id));
|
|
75
|
+
this.update(n, {
|
|
76
|
+
data: {
|
|
77
|
+
items: [...t.items, ...e.items.filter((e) => !a.has(e.id))],
|
|
78
|
+
next: e.next
|
|
79
|
+
},
|
|
80
|
+
error: void 0
|
|
81
|
+
});
|
|
82
|
+
} catch (t) {
|
|
83
|
+
let r = e(t);
|
|
84
|
+
n.moreCtl === i && r.code !== "aborted" && this.update(n, { error: r });
|
|
85
|
+
} finally {
|
|
86
|
+
n.moreCtl === i && (n.moreCtl = void 0, this.update(n, { loadingMore: !1 }));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
set(e, t) {
|
|
90
|
+
let n = this.slots.get(e);
|
|
91
|
+
if (!n) return;
|
|
92
|
+
let r = typeof t == "function" ? t(n.entry.data) : t;
|
|
93
|
+
n.readVersion = ++this.version, this.update(n, {
|
|
94
|
+
data: r,
|
|
95
|
+
loaded: !0,
|
|
96
|
+
error: void 0
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
mark(e) {
|
|
100
|
+
let t = this.slots.get(e);
|
|
101
|
+
return t ? t.version = ++this.version : 0;
|
|
102
|
+
}
|
|
103
|
+
rollback(e, t, n) {
|
|
104
|
+
let r = this.slots.get(e);
|
|
105
|
+
r && r.version === t && this.update(r, {
|
|
106
|
+
data: n,
|
|
107
|
+
loaded: !0
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
patch(e, t) {
|
|
111
|
+
for (let n of this.slots.values()) n.entry.data !== void 0 && e(n.tag) && this.update(n, { data: t(n.entry.data, n.readVersion) });
|
|
112
|
+
}
|
|
113
|
+
invalidate(e) {
|
|
114
|
+
for (let [t, n] of this.slots) e(n.tag) && n.listeners.size && this.load(t, !0);
|
|
115
|
+
}
|
|
116
|
+
apply(e, t) {
|
|
117
|
+
let r = (e, n) => {
|
|
118
|
+
this.patch((n) => e(n) && (n.readScope ?? "{}") === t, n), this.invalidate((n) => e(n) && (n.readScope ?? "{}") !== t);
|
|
119
|
+
}, i = (e, t) => r((t) => e.includes(t.type), (e) => {
|
|
120
|
+
let n = !1, r = [];
|
|
121
|
+
for (let i of e.items) {
|
|
122
|
+
let e = t(i);
|
|
123
|
+
e !== i && (n = !0), e && r.push(e);
|
|
124
|
+
}
|
|
125
|
+
return n ? {
|
|
126
|
+
...e,
|
|
127
|
+
items: r
|
|
128
|
+
} : e;
|
|
129
|
+
}), a = [
|
|
130
|
+
"comments",
|
|
131
|
+
"replies",
|
|
132
|
+
"latest",
|
|
133
|
+
"admin-comments"
|
|
134
|
+
];
|
|
135
|
+
switch (e.type) {
|
|
136
|
+
case "comment.created": {
|
|
137
|
+
let t = e.comment.reply_to_id, a = !e.comment.moderation;
|
|
138
|
+
r((r) => t ? r.type === "replies" && r.id === t : r.type === "comments" && n(r.ref, e.ref), (n) => n.items.some((t) => t.id === e.comment.id) ? n : {
|
|
139
|
+
...n,
|
|
140
|
+
items: t ? [...n.items, e.comment] : [e.comment, ...n.items]
|
|
141
|
+
}), t && a && i(["comments"], (e) => e.id === t ? {
|
|
142
|
+
...e,
|
|
143
|
+
reply_count: e.reply_count + 1
|
|
144
|
+
} : e), this.invalidate((e) => e.type === "latest" || e.type === "admin-comments");
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
case "comment.updated":
|
|
148
|
+
i(a, (t) => t.id === e.comment.id ? {
|
|
149
|
+
...t,
|
|
150
|
+
...e.comment,
|
|
151
|
+
author: e.comment.author ?? t.author
|
|
152
|
+
} : t);
|
|
153
|
+
break;
|
|
154
|
+
case "comment.reacted":
|
|
155
|
+
i(a, (t) => t.id === e.id ? {
|
|
156
|
+
...t,
|
|
157
|
+
...e.counts
|
|
158
|
+
} : t);
|
|
159
|
+
break;
|
|
160
|
+
case "comment.deleted": {
|
|
161
|
+
let t;
|
|
162
|
+
i(["comments", "replies"], (n) => n.id !== e.id || n.deleted ? n : (n.moderation || (t = n.reply_to_id), {
|
|
163
|
+
...n,
|
|
164
|
+
deleted: !0,
|
|
165
|
+
body: "[deleted]",
|
|
166
|
+
author: void 0,
|
|
167
|
+
user_id: void 0,
|
|
168
|
+
anon_name: void 0
|
|
169
|
+
})), t && i(["comments"], (e) => e.id === t ? {
|
|
170
|
+
...e,
|
|
171
|
+
reply_count: Math.max(0, e.reply_count - 1)
|
|
172
|
+
} : e), i(["latest"], (t) => t.id === e.id ? null : t), i(["admin-comments"], (t) => t.id === e.id ? {
|
|
173
|
+
...t,
|
|
174
|
+
deleted: !0,
|
|
175
|
+
deleted_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
176
|
+
} : t);
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
case "comment.restored":
|
|
180
|
+
this.invalidate((e) => a.includes(e.type));
|
|
181
|
+
break;
|
|
182
|
+
case "reaction.changed":
|
|
183
|
+
if (r((t) => t.type === "reaction" && n(t.ref, e.ref), () => e.counts), e.ref.kind === "post") {
|
|
184
|
+
let t = (t) => t.id === e.ref.id ? {
|
|
185
|
+
...t,
|
|
186
|
+
total_likes: e.counts.likes,
|
|
187
|
+
total_dislikes: e.counts.dislikes
|
|
188
|
+
} : t;
|
|
189
|
+
r((t) => t.type === "post" && t.id === e.ref.id, t), i(["posts"], t);
|
|
190
|
+
}
|
|
191
|
+
break;
|
|
192
|
+
case "favorite.changed":
|
|
193
|
+
r((t) => t.type === "favorite" && n(t.ref, e.ref), () => ({
|
|
194
|
+
favorited: e.favorited,
|
|
195
|
+
count: e.count
|
|
196
|
+
})), this.invalidate((e) => e.type === "favorites");
|
|
197
|
+
break;
|
|
198
|
+
case "post.created":
|
|
199
|
+
this.invalidate((e) => e.type === "posts");
|
|
200
|
+
break;
|
|
201
|
+
case "post.updated":
|
|
202
|
+
r((e) => e.type === "post", (t) => t?.id === e.post.id ? e.post : t), i(["posts"], (t) => t.id === e.post.id ? e.post : t);
|
|
203
|
+
break;
|
|
204
|
+
case "post.changed":
|
|
205
|
+
this.invalidate((t) => t.type === "post" && t.id === e.id || t.type === "posts");
|
|
206
|
+
break;
|
|
207
|
+
case "post.deleted":
|
|
208
|
+
i(["posts"], (t) => t.id === e.id ? null : t), this.invalidate((e) => e.type === "posts" && e.scope === "deleted");
|
|
209
|
+
break;
|
|
210
|
+
case "post.restored":
|
|
211
|
+
this.invalidate((t) => t.type === "post" && t.id === e.post.id || t.type === "posts");
|
|
212
|
+
break;
|
|
213
|
+
case "poll.created":
|
|
214
|
+
this.invalidate((e) => e.type === "polls" || e.type === "poll" && !e.id);
|
|
215
|
+
break;
|
|
216
|
+
case "poll.updated":
|
|
217
|
+
r((e) => e.type === "poll", (t) => t?.id === e.poll.id ? e.poll : t), i(["polls"], (t) => t.id === e.poll.id ? e.poll : t);
|
|
218
|
+
break;
|
|
219
|
+
case "poll.changed":
|
|
220
|
+
this.invalidate((t) => t.type === "poll" && (t.id === e.id || !t.id) || t.type === "polls");
|
|
221
|
+
break;
|
|
222
|
+
case "poll.deleted":
|
|
223
|
+
i(["polls"], (t) => t.id === e.id ? null : t), this.invalidate((e) => e.type === "poll" && !e.id);
|
|
224
|
+
break;
|
|
225
|
+
case "ban.saved":
|
|
226
|
+
case "ban.lifted":
|
|
227
|
+
this.invalidate((t) => t.type === "bans" && t.scope === e.scope || t.type === "standing");
|
|
228
|
+
break;
|
|
229
|
+
case "moderation.resolved": i(["held"], (t) => t.id === e.id ? null : t), e.kind === "comment" ? this.invalidate((e) => a.includes(e.type)) : this.invalidate((t) => t.type === "post" && t.id === e.id || t.type === "posts");
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
update(e, t) {
|
|
233
|
+
e.entry = {
|
|
234
|
+
...e.entry,
|
|
235
|
+
...t
|
|
236
|
+
};
|
|
237
|
+
for (let t of e.listeners) t();
|
|
238
|
+
}
|
|
239
|
+
}, i = /* @__PURE__ */ new WeakMap();
|
|
240
|
+
function a(e) {
|
|
241
|
+
let t = i.get(e);
|
|
242
|
+
return t || i.set(e, t = new r(e)), t;
|
|
243
|
+
}
|
|
244
|
+
//#endregion
|
|
245
|
+
export { r as ResourceStore, a as resourcesFor };
|
|
246
|
+
|
|
247
|
+
//# sourceMappingURL=resources.js.map
|
|
@@ -0,0 +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 type { Comment, Poll, Post, RefBody } 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> {\n items: T[];\n next: string | number | null;\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 ref?: RefBody;\n id?: string;\n scope?: string;\n}\n\ninterface Slot {\n tag: Tag;\n listeners: Set<() => void>;\n entry: Resource<unknown>;\n fetcher?: Fetcher<unknown>;\n more?: (cursor: string | number, signal: AbortSignal) => Promise<Page<unknown>>;\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\nconst IDLE: Resource<never> = { data: undefined, loading: false, loaded: false };\n\nconst sameRef = (a?: RefBody, b?: RefBody) => !!a && !!b && a.kind === b.kind && a.id === b.id;\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(), 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 /** The latest fetchers of a key: its first read, and a list's next page. */\n bind(key: string, fetcher: Fetcher<unknown>, more?: Slot[\"more\"]): void {\n const s = this.slots.get(key);\n if (s) Object.assign(s, { fetcher, more });\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 if (!s?.fetcher || (!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 s.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 page = s?.entry.data as Page<{ id: string }> | undefined;\n if (!s?.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 s.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: { 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 switch (c.type) {\n case \"comment.created\": {\n const parent = c.comment.reply_to_id;\n const published = !c.comment.moderation;\n const add = (t: Tag) => (parent ? t.type === \"replies\" && t.id === parent : t.type === \"comments\" && sameRef(t.ref, c.ref));\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 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 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 break;\n }\n case \"comment.restored\":\n this.invalidate((t) => comments.includes(t.type));\n break;\n case \"reaction.changed\":\n patch((t) => t.type === \"reaction\" && sameRef(t.ref, c.ref), () => c.counts);\n if (c.ref.kind === \"post\") {\n const totals = (p: Post): Post => (p.id === c.ref.id ? { ...p, total_likes: c.counts.likes, total_dislikes: c.counts.dislikes } : p);\n patch<Post>((t) => t.type === \"post\" && t.id === c.ref.id, totals);\n items<Post>([\"posts\"], totals);\n }\n break;\n case \"favorite.changed\":\n patch((t) => t.type === \"favorite\" && sameRef(t.ref, c.ref), () => ({ favorited: c.favorited, count: c.count }));\n this.invalidate((t) => t.type === \"favorites\");\n break;\n case \"post.created\":\n this.invalidate((t) => t.type === \"posts\");\n break;\n case \"post.updated\":\n patch<Post | null>((t) => t.type === \"post\", (p) => (p?.id === c.post.id ? c.post : p));\n items<Post>([\"posts\"], (x) => (x.id === c.post.id ? c.post : x));\n break;\n case \"post.changed\":\n this.invalidate((t) => (t.type === \"post\" && t.id === c.id) || t.type === \"posts\");\n break;\n case \"post.deleted\":\n items<Post>([\"posts\"], (x) => (x.id === c.id ? null : x));\n this.invalidate((t) => t.type === \"posts\" && t.scope === \"deleted\");\n break;\n case \"post.restored\":\n this.invalidate((t) => (t.type === \"post\" && t.id === c.post.id) || t.type === \"posts\");\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 \"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));\n else this.invalidate((t) => (t.type === \"post\" && t.id === c.id) || t.type === \"posts\");\n break;\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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"}
|