@openrails/contentkit-ui 0.71.0 → 0.73.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -10
- package/dist/client/content/bans.d.ts +2 -0
- package/dist/client/content/bans.js +24 -12
- package/dist/client/content/bans.js.map +1 -1
- package/dist/client/content/comments.d.ts +2 -0
- package/dist/client/content/comments.js.map +1 -1
- package/dist/client/content/polls.d.ts +9 -2
- package/dist/client/content/polls.js.map +1 -1
- package/dist/client/generated/routes.d.ts +28 -2
- package/dist/client/generated/routes.js +40 -0
- package/dist/client/generated/routes.js.map +1 -1
- package/dist/client/generated/wire.d.ts +4 -0
- package/dist/client/generated/wire.js.map +1 -1
- package/dist/client/index.d.ts +3 -3
- package/dist/client/media/queue.d.ts +1 -1
- package/dist/client/media/queue.js +1 -1
- package/dist/client/media/queue.js.map +1 -1
- package/dist/client/taxonomy.d.ts +5 -1
- package/dist/client/taxonomy.js +7 -0
- package/dist/client/taxonomy.js.map +1 -1
- package/dist/client.js +2 -2
- package/dist/components/admin/media-item-panel.d.ts +27 -0
- package/dist/components/admin/media-item-panel.js +397 -0
- package/dist/components/admin/media-item-panel.js.map +1 -0
- package/dist/components/admin/poll-manager.d.ts +28 -0
- package/dist/components/admin/poll-manager.js +297 -0
- package/dist/components/admin/poll-manager.js.map +1 -0
- package/dist/components/admin/post-manager.d.ts +45 -0
- package/dist/components/admin/post-manager.js +602 -0
- package/dist/components/admin/post-manager.js.map +1 -0
- package/dist/components/admin/shell.d.ts +73 -0
- package/dist/components/admin/shell.js +148 -0
- package/dist/components/admin/shell.js.map +1 -0
- package/dist/components/admin/taxonomy-manager.d.ts +54 -0
- package/dist/components/admin/taxonomy-manager.js +976 -0
- package/dist/components/admin/taxonomy-manager.js.map +1 -0
- package/dist/components/admin/upload-queue.d.ts +23 -0
- package/dist/components/admin/upload-queue.js +365 -0
- package/dist/components/admin/upload-queue.js.map +1 -0
- package/dist/components/admin/user-comment-standing.d.ts +25 -0
- package/dist/components/admin/user-comment-standing.js +229 -0
- package/dist/components/admin/user-comment-standing.js.map +1 -0
- package/dist/components/social/bans.d.ts +5 -2
- package/dist/components/social/bans.js +132 -134
- package/dist/components/social/bans.js.map +1 -1
- package/dist/components/social/index.d.ts +1 -1
- package/dist/components/social/moderation.d.ts +11 -13
- package/dist/components/social/moderation.js +299 -218
- package/dist/components/social/moderation.js.map +1 -1
- package/dist/components/social/parts.d.ts +3 -2
- package/dist/components/social/parts.js +74 -48
- package/dist/components/social/parts.js.map +1 -1
- package/dist/components/social/poll-editor.js +1 -0
- package/dist/components/social/poll-editor.js.map +1 -1
- package/dist/editor/index.d.ts +1 -0
- package/dist/editor/rich-text-editor.d.ts +13 -0
- package/dist/editor/rich-text-editor.js +228 -0
- package/dist/editor/rich-text-editor.js.map +1 -0
- package/dist/editor.js +2 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +15 -8
- package/dist/locales/admin/de.d.ts +2 -0
- package/dist/locales/admin/de.js +227 -0
- package/dist/locales/admin/de.js.map +1 -0
- package/dist/locales/admin/en.d.ts +222 -0
- package/dist/locales/admin/en.js +227 -0
- package/dist/locales/admin/en.js.map +1 -0
- package/dist/locales/admin/es.d.ts +2 -0
- package/dist/locales/admin/es.js +227 -0
- package/dist/locales/admin/es.js.map +1 -0
- package/dist/locales/admin/ja.d.ts +2 -0
- package/dist/locales/admin/ja.js +227 -0
- package/dist/locales/admin/ja.js.map +1 -0
- package/dist/locales/admin/ko.d.ts +2 -0
- package/dist/locales/admin/ko.js +227 -0
- package/dist/locales/admin/ko.js.map +1 -0
- package/dist/locales/admin/zh.d.ts +2 -0
- package/dist/locales/admin/zh.js +227 -0
- package/dist/locales/admin/zh.js.map +1 -0
- package/dist/locales/de.js +5 -3
- package/dist/locales/de.js.map +1 -1
- package/dist/locales/en.d.ts +227 -0
- package/dist/locales/en.js +5 -3
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es.js +5 -3
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/ja.js +5 -3
- package/dist/locales/ja.js.map +1 -1
- package/dist/locales/ko.js +5 -3
- package/dist/locales/ko.js.map +1 -1
- package/dist/locales/social/de.js +8 -1
- package/dist/locales/social/de.js.map +1 -1
- package/dist/locales/social/en.d.ts +7 -0
- package/dist/locales/social/en.js +8 -1
- package/dist/locales/social/en.js.map +1 -1
- package/dist/locales/social/es.js +8 -1
- package/dist/locales/social/es.js.map +1 -1
- package/dist/locales/social/ja.js +8 -1
- package/dist/locales/social/ja.js.map +1 -1
- package/dist/locales/social/ko.js +8 -1
- package/dist/locales/social/ko.js.map +1 -1
- package/dist/locales/social/zh.js +8 -1
- package/dist/locales/social/zh.js.map +1 -1
- package/dist/locales/zh.js +5 -3
- package/dist/locales/zh.js.map +1 -1
- package/dist/provider.d.ts +2 -0
- package/dist/provider.js +23 -19
- package/dist/provider.js.map +1 -1
- package/dist/react/admin.d.ts +55 -0
- package/dist/react/admin.js +97 -0
- package/dist/react/admin.js.map +1 -0
- package/dist/react/comments.d.ts +18 -2
- package/dist/react/comments.js +24 -8
- package/dist/react/comments.js.map +1 -1
- package/dist/react/context.d.ts +7 -1
- package/dist/react/context.js +6 -1
- package/dist/react/context.js.map +1 -1
- package/dist/react/folder.js.map +1 -1
- package/dist/react/index.d.ts +4 -3
- package/dist/react/polls.js +88 -87
- package/dist/react/polls.js.map +1 -1
- package/dist/react/posts.js +123 -122
- package/dist/react/posts.js.map +1 -1
- package/dist/react/resources.js +3 -0
- package/dist/react/resources.js.map +1 -1
- package/dist/react/upload.d.ts +10 -10
- package/dist/react/upload.js +1 -1
- package/dist/react/upload.js.map +1 -1
- package/dist/react/use-resource.d.ts +16 -0
- package/dist/react/use-resource.js +20 -1
- package/dist/react/use-resource.js.map +1 -1
- package/dist/react.js +21 -20
- package/dist/styles.css +1 -1
- package/package.json +37 -2
package/dist/react/polls.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { toContentKitError as e } from "../client/errors.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { useGuarded as t } from "./context.js";
|
|
3
|
+
import { keyOf as n, offsetPages as r, useContentScope as i, useList as a, useResource as o } from "./use-resource.js";
|
|
4
|
+
import { useCallback as s, useState as c } from "react";
|
|
4
5
|
//#region src/react/polls.ts
|
|
5
|
-
function
|
|
6
|
+
function l(e, t) {
|
|
6
7
|
return e.voted ? e : {
|
|
7
8
|
...e,
|
|
8
9
|
voted: !0,
|
|
@@ -14,43 +15,43 @@ function c(e, t) {
|
|
|
14
15
|
} : e)
|
|
15
16
|
};
|
|
16
17
|
}
|
|
17
|
-
function
|
|
18
|
-
let { client:
|
|
18
|
+
function u(t, r = {}) {
|
|
19
|
+
let { client: a, store: u, scope: d } = i(r.client), f = t ? n("poll", d, t) : n("poll", d, "latest", r.language ?? ""), p = o(u, f, {
|
|
19
20
|
type: "poll",
|
|
20
|
-
id:
|
|
21
|
-
}, async (e) =>
|
|
22
|
-
language:
|
|
21
|
+
id: t ?? void 0
|
|
22
|
+
}, async (e) => t ? a.polls.get(t, e) : (await a.polls.list({
|
|
23
|
+
language: r.language,
|
|
23
24
|
limit: 1
|
|
24
|
-
}, e))[0] ?? null, { initial:
|
|
25
|
+
}, e))[0] ?? null, { initial: r.initial }), [m, h] = c(!1), g = p.data ?? null, _ = s(async (t) => {
|
|
25
26
|
let n = u.snapshot(f).data ?? g;
|
|
26
27
|
if (!n) return;
|
|
27
28
|
let r = u.mark(f);
|
|
28
|
-
u.set(f,
|
|
29
|
+
u.set(f, l(n, t)), h(!0);
|
|
29
30
|
try {
|
|
30
|
-
await
|
|
31
|
+
await a.polls.vote(n.id, t);
|
|
31
32
|
} catch (t) {
|
|
32
33
|
throw u.rollback(f, r, n), e(t);
|
|
33
34
|
} finally {
|
|
34
35
|
h(!1);
|
|
35
36
|
}
|
|
36
37
|
}, [
|
|
37
|
-
|
|
38
|
+
a,
|
|
38
39
|
u,
|
|
39
40
|
f,
|
|
40
41
|
g
|
|
41
|
-
]), v =
|
|
42
|
+
]), v = s(async (e) => {
|
|
42
43
|
if (g) {
|
|
43
44
|
h(!0);
|
|
44
45
|
try {
|
|
45
|
-
await
|
|
46
|
+
await a.polls.answer(g.id, e);
|
|
46
47
|
} finally {
|
|
47
48
|
h(!1);
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
|
-
}, [
|
|
51
|
+
}, [a, g]);
|
|
51
52
|
return {
|
|
52
53
|
poll: g,
|
|
53
|
-
loading: p.loading || !p.loaded &&
|
|
54
|
+
loading: p.loading || !p.loaded && r.initial === void 0,
|
|
54
55
|
error: p.error,
|
|
55
56
|
reload: p.reload,
|
|
56
57
|
pending: m,
|
|
@@ -58,13 +59,13 @@ function l(n, i = {}) {
|
|
|
58
59
|
answer: v
|
|
59
60
|
};
|
|
60
61
|
}
|
|
61
|
-
function
|
|
62
|
-
let { client: o, store: s, scope: c } =
|
|
62
|
+
function d(e = {}, t = {}) {
|
|
63
|
+
let { client: o, store: s, scope: c } = i(t.client), l = e.pageSize ?? 20, u = {
|
|
63
64
|
language: e.language,
|
|
64
65
|
month: e.month,
|
|
65
66
|
date: e.date
|
|
66
67
|
};
|
|
67
|
-
return
|
|
68
|
+
return a(s, n("polls", c, !!e.admin, u.language, u.month, u.date, l), { type: "polls" }, r(l, (t, n) => e.admin ? o.polls.adminList({
|
|
68
69
|
...u,
|
|
69
70
|
...t
|
|
70
71
|
}, n) : o.polls.list({
|
|
@@ -72,62 +73,62 @@ function u(e = {}, a = {}) {
|
|
|
72
73
|
...t
|
|
73
74
|
}, n)));
|
|
74
75
|
}
|
|
75
|
-
function
|
|
76
|
-
let { client:
|
|
76
|
+
function f(r, a = {}) {
|
|
77
|
+
let { client: l, store: u, scope: d } = i(a.client), [f, p] = c(null), m = r ?? f, h = m ? n("poll", d, m) : null, g = o(u, h, {
|
|
77
78
|
type: "poll",
|
|
78
|
-
id:
|
|
79
|
-
}, (e) =>
|
|
80
|
-
|
|
79
|
+
id: m ?? void 0
|
|
80
|
+
}, (e) => l.polls.get(m, e)), _ = g.data ?? null, [v, y] = c(0), b = t(), x = s(async (e) => {
|
|
81
|
+
y((e) => e + 1);
|
|
81
82
|
try {
|
|
82
|
-
return await e
|
|
83
|
+
return await b(e);
|
|
83
84
|
} finally {
|
|
84
|
-
|
|
85
|
+
y((e) => e - 1);
|
|
85
86
|
}
|
|
86
|
-
}, []),
|
|
87
|
-
if (!
|
|
88
|
-
return
|
|
89
|
-
}, [
|
|
90
|
-
let r = await
|
|
91
|
-
n.question && a.push(
|
|
87
|
+
}, [b]), S = s(() => {
|
|
88
|
+
if (!m) throw Error("contentkit-ui: create the poll first");
|
|
89
|
+
return m;
|
|
90
|
+
}, [m]), C = s((t, n = {}) => x(async () => {
|
|
91
|
+
let r = await l.polls.create(t), i = [], a = [];
|
|
92
|
+
n.question && a.push(l.polls.uploadImage(r.id, n.question));
|
|
92
93
|
let o = [...r.options].sort((e, t) => e.position - t.position);
|
|
93
94
|
(t.options ?? []).forEach((e, t) => {
|
|
94
95
|
let i = n.options?.[t], s = o.find((t) => t.position === e.position) ?? o[t];
|
|
95
|
-
i && s && a.push(
|
|
96
|
+
i && s && a.push(l.polls.uploadOptionImage(r.id, s.id, i));
|
|
96
97
|
});
|
|
97
98
|
for (let t of await Promise.allSettled(a)) t.status === "rejected" && i.push(e(t.reason));
|
|
98
|
-
return
|
|
99
|
-
poll: a.length ? await
|
|
99
|
+
return p(r.id), {
|
|
100
|
+
poll: a.length ? await l.polls.get(r.id) : r,
|
|
100
101
|
imageErrors: i
|
|
101
102
|
};
|
|
102
|
-
}), [
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
]),
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
]),
|
|
111
|
-
let n = await
|
|
112
|
-
return t && (n.image_url = await
|
|
103
|
+
}), [l, x]), w = s((e) => x(() => l.polls.update(S(), e)), [
|
|
104
|
+
l,
|
|
105
|
+
x,
|
|
106
|
+
S
|
|
107
|
+
]), T = s(() => x(() => l.polls.delete(S())), [
|
|
108
|
+
l,
|
|
109
|
+
x,
|
|
110
|
+
S
|
|
111
|
+
]), E = s((e, t) => x(async () => {
|
|
112
|
+
let n = await l.polls.addOption(S(), { label: e });
|
|
113
|
+
return t && (n.image_url = await l.polls.uploadOptionImage(S(), n.id, t) ?? void 0), n;
|
|
113
114
|
}), [
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
]),
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
]),
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
]),
|
|
126
|
-
let n =
|
|
115
|
+
l,
|
|
116
|
+
x,
|
|
117
|
+
S
|
|
118
|
+
]), D = s((e, t) => x(async () => void await l.polls.updateOption(S(), e, { label: t })), [
|
|
119
|
+
l,
|
|
120
|
+
x,
|
|
121
|
+
S
|
|
122
|
+
]), O = s((e) => x(() => l.polls.deleteOption(S(), e)), [
|
|
123
|
+
l,
|
|
124
|
+
x,
|
|
125
|
+
S
|
|
126
|
+
]), k = s((e, t) => x(async () => {
|
|
127
|
+
let n = u.snapshot(h).data;
|
|
127
128
|
if (!n) return;
|
|
128
129
|
let r = [...n.options].sort((e, t) => e.position - t.position), i = r.findIndex((t) => t.id === e);
|
|
129
130
|
if (!(i < 0 || t < 0 || t >= r.length || i === t)) {
|
|
130
|
-
r.splice(t, 0, ...r.splice(i, 1)),
|
|
131
|
+
r.splice(t, 0, ...r.splice(i, 1)), u.set(h, {
|
|
131
132
|
...n,
|
|
132
133
|
options: r.map((e, t) => ({
|
|
133
134
|
...e,
|
|
@@ -135,42 +136,42 @@ function d(n, i = {}) {
|
|
|
135
136
|
}))
|
|
136
137
|
});
|
|
137
138
|
try {
|
|
138
|
-
await
|
|
139
|
+
await l.polls.reorderOptions(n.id, r);
|
|
139
140
|
} catch (e) {
|
|
140
|
-
throw
|
|
141
|
+
throw u.load(h, !0), e;
|
|
141
142
|
}
|
|
142
143
|
}
|
|
143
144
|
}), [
|
|
144
|
-
c,
|
|
145
145
|
l,
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
146
|
+
u,
|
|
147
|
+
h,
|
|
148
|
+
x
|
|
149
|
+
]), A = s((e) => x(async () => void await l.polls.uploadImage(S(), e)), [
|
|
150
|
+
l,
|
|
151
|
+
x,
|
|
152
|
+
S
|
|
153
|
+
]), j = s((e, t) => x(async () => void await l.polls.uploadOptionImage(S(), e, t)), [
|
|
154
|
+
l,
|
|
155
|
+
x,
|
|
156
|
+
S
|
|
156
157
|
]);
|
|
157
158
|
return {
|
|
158
|
-
poll:
|
|
159
|
-
loading: !!
|
|
160
|
-
error:
|
|
161
|
-
saving:
|
|
162
|
-
create:
|
|
163
|
-
update:
|
|
164
|
-
remove:
|
|
165
|
-
addOption:
|
|
166
|
-
renameOption:
|
|
167
|
-
removeOption:
|
|
168
|
-
moveOption:
|
|
169
|
-
setImage:
|
|
170
|
-
setOptionImage:
|
|
159
|
+
poll: _,
|
|
160
|
+
loading: !!h && (g.loading || !g.loaded),
|
|
161
|
+
error: g.error,
|
|
162
|
+
saving: v > 0,
|
|
163
|
+
create: C,
|
|
164
|
+
update: w,
|
|
165
|
+
remove: T,
|
|
166
|
+
addOption: E,
|
|
167
|
+
renameOption: D,
|
|
168
|
+
removeOption: O,
|
|
169
|
+
moveOption: k,
|
|
170
|
+
setImage: A,
|
|
171
|
+
setOptionImage: j
|
|
171
172
|
};
|
|
172
173
|
}
|
|
173
174
|
//#endregion
|
|
174
|
-
export {
|
|
175
|
+
export { u as usePoll, f as usePollEditor, d as usePolls, l as withVote };
|
|
175
176
|
|
|
176
177
|
//# sourceMappingURL=polls.js.map
|
package/dist/react/polls.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"polls.js","names":[],"sources":["../../src/react/polls.ts"],"sourcesContent":["import { useCallback, useState } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport { toContentKitError, type ContentKitError } from \"../client/errors.js\";\nimport type { Poll, PollInput, PollOption, PollUpdate } from \"../client/generated/wire.js\";\nimport { keyOf, offsetPages, useContentScope, useList, useResource, type UseList } from \"./use-resource.js\";\n\n/** The poll after the caller votes for option; a vote is final. */\nexport function withVote(p: Poll, option: string): Poll {\n if (p.voted) return p;\n return { ...p, voted: true, my_option: option, total_votes: p.total_votes + 1, options: p.options.map((o) => (o.id === option ? { ...o, vote_count: o.vote_count + 1 } : o)) };\n}\n\nexport interface UsePoll {\n /** null: no such poll (or no live one). */\n poll: Poll | null;\n loading: boolean;\n error?: ContentKitError;\n reload: () => void;\n /** A vote or answer is in flight. */\n pending: boolean;\n /** Votes at once (a vote is final); rolled back if the server refuses it. */\n vote: (option: string) => Promise<void>;\n /** Stores or replaces the signed-in caller's free-text answer. */\n answer: (text: string) => Promise<void>;\n}\n\n/**\n * A poll with the caller's vote or answer. Without an id it is the newest\n * live poll (in language): the one a home page shows.\n */\nexport function usePoll(id?: string | null, o: { initial?: Poll; language?: string; client?: ContentKitClient } = {}): UsePoll {\n const { client, store, scope } = useContentScope(o.client);\n const key = id ? keyOf(\"poll\", scope, id) : keyOf(\"poll\", scope, \"latest\", o.language ?? \"\");\n const r = useResource<Poll | null>(\n store,\n key,\n { type: \"poll\", id: id ?? undefined },\n async (s) => (id ? client.polls.get(id, s) : ((await client.polls.list({ language: o.language, limit: 1 }, s))[0] ?? null)),\n { initial: o.initial },\n );\n const [pending, setPending] = useState(false);\n const poll = r.data ?? null;\n const vote = useCallback(\n async (option: string) => {\n const prev = store.snapshot<Poll | null>(key).data ?? poll;\n if (!prev) return;\n const token = store.mark(key);\n store.set(key, withVote(prev, option));\n setPending(true);\n try {\n await client.polls.vote(prev.id, option);\n } catch (e) {\n store.rollback(key, token, prev);\n throw toContentKitError(e);\n } finally {\n setPending(false);\n }\n },\n [client, store, key, poll],\n );\n const answer = useCallback(\n async (text: string) => {\n if (!poll) return;\n setPending(true);\n try {\n await client.polls.answer(poll.id, text);\n } finally {\n setPending(false);\n }\n },\n [client, poll],\n );\n return { poll, loading: r.loading || (!r.loaded && o.initial === undefined), error: r.error, reload: r.reload, pending, vote, answer };\n}\n\nexport interface PollFilter {\n language?: string;\n /** YYYY-MM */\n month?: string;\n /** YYYY-MM-DD */\n date?: string;\n /** Staff: every poll, scheduled and inactive ones included (root:polls:write). */\n admin?: boolean;\n pageSize?: number;\n}\n\n/** Polls, newest first, a page at a time. */\nexport function usePolls(f: PollFilter = {}, o: { client?: ContentKitClient } = {}): UseList<Poll> {\n const { client, store, scope } = useContentScope(o.client);\n const size = f.pageSize ?? 20;\n const q = { language: f.language, month: f.month, date: f.date };\n return useList(\n store,\n keyOf(\"polls\", scope, !!f.admin, q.language, q.month, q.date, size),\n { type: \"polls\" },\n offsetPages(size, (page, signal) => (f.admin ? client.polls.adminList({ ...q, ...page }, signal) : client.polls.list({ ...q, ...page }, signal))),\n );\n}\n\n/** Images for a new poll, uploaded once it exists: the question's, and each option's by index. */\nexport interface PollImages {\n question?: Blob | null;\n options?: readonly (Blob | null | undefined)[];\n}\n\nexport interface UsePollEditor {\n /** null before create (or while loading). */\n poll: Poll | null;\n loading: boolean;\n error?: ContentKitError;\n /** Writes in flight. */\n saving: boolean;\n /** Creates the poll, then uploads its images; the editor then edits it. Image failures are returned, not thrown. */\n create: (input: PollInput, images?: PollImages) => Promise<{ poll: Poll; imageErrors: ContentKitError[] }>;\n update: (patch: PollUpdate) => Promise<Poll>;\n remove: () => Promise<void>;\n addOption: (label: string, image?: Blob | null) => Promise<PollOption>;\n renameOption: (option: string, label: string) => Promise<void>;\n /** Removes an option and its votes; a poll keeps at least two. */\n removeOption: (option: string) => Promise<void>;\n /** Moves an option to index to, at once; refetched if a write fails. */\n moveOption: (option: string, to: number) => Promise<void>;\n setImage: (file: Blob | null) => Promise<void>;\n setOptionImage: (option: string, file: Blob | null) => Promise<void>;\n}\n\n/** Staff: one poll's editor (root:polls:write); null creates a new poll. */\nexport function usePollEditor(id: string | null | undefined, o: { client?: ContentKitClient } = {}): UsePollEditor {\n const { client, store, scope } = useContentScope(o.client);\n const [created, setCreated] = useState<string | null>(null);\n const pid = id ?? created;\n const key = pid ? keyOf(\"poll\", scope, pid) : null;\n const r = useResource<Poll | null>(store, key, { type: \"poll\", id: pid ?? undefined }, (s) => client.polls.get(pid!, s));\n const poll = r.data ?? null;\n const [inflight, setInflight] = useState(0);\n const run = useCallback(async <T>(fn: () => Promise<T>): Promise<T> => {\n setInflight((n) => n + 1);\n try {\n return await fn();\n } finally {\n setInflight((n) => n - 1);\n }\n }, []);\n const need = useCallback(() => {\n if (!pid) throw new Error(\"contentkit-ui: create the poll first\");\n return pid;\n }, [pid]);\n\n const create = useCallback<UsePollEditor[\"create\"]>(\n (input, images = {}) =>\n run(async () => {\n const made = await client.polls.create(input);\n const imageErrors: ContentKitError[] = [];\n const tries: Promise<unknown>[] = [];\n if (images.question) tries.push(client.polls.uploadImage(made.id, images.question));\n const byPosition = [...made.options].sort((a, b) => a.position - b.position);\n (input.options ?? []).forEach((opt, i) => {\n const file = images.options?.[i];\n const target = byPosition.find((x) => x.position === opt.position) ?? byPosition[i];\n if (file && target) tries.push(client.polls.uploadOptionImage(made.id, target.id, file));\n });\n for (const res of await Promise.allSettled(tries)) if (res.status === \"rejected\") imageErrors.push(toContentKitError(res.reason));\n setCreated(made.id);\n return { poll: tries.length ? await client.polls.get(made.id) : made, imageErrors };\n }),\n [client, run],\n );\n const update = useCallback((patch: PollUpdate) => run(() => client.polls.update(need(), patch)), [client, run, need]);\n const remove = useCallback(() => run(() => client.polls.delete(need())), [client, run, need]);\n const addOption = useCallback(\n (label: string, image?: Blob | null) =>\n run(async () => {\n const opt = await client.polls.addOption(need(), { label });\n if (image) opt.image_url = (await client.polls.uploadOptionImage(need(), opt.id, image)) ?? undefined;\n return opt;\n }),\n [client, run, need],\n );\n const renameOption = useCallback((option: string, label: string) => run(async () => void (await client.polls.updateOption(need(), option, { label }))), [client, run, need]);\n const removeOption = useCallback((option: string) => run(() => client.polls.deleteOption(need(), option)), [client, run, need]);\n const moveOption = useCallback(\n (option: string, to: number) =>\n run(async () => {\n const current = store.snapshot<Poll | null>(key!).data;\n if (!current) return;\n const order = [...current.options].sort((a, b) => a.position - b.position);\n const from = order.findIndex((x) => x.id === option);\n if (from < 0 || to < 0 || to >= order.length || from === to) return;\n order.splice(to, 0, ...order.splice(from, 1));\n store.set<Poll | null>(key!, { ...current, options: order.map((x, position) => ({ ...x, position })) });\n try {\n await client.polls.reorderOptions(current.id, order);\n } catch (e) {\n store.load(key!, true);\n throw e;\n }\n }),\n [client, store, key, run],\n );\n const setImage = useCallback((file: Blob | null) => run(async () => void (await client.polls.uploadImage(need(), file))), [client, run, need]);\n const setOptionImage = useCallback(\n (option: string, file: Blob | null) => run(async () => void (await client.polls.uploadOptionImage(need(), option, file))),\n [client, run, need],\n );\n return {\n poll,\n loading: !!key && (r.loading || !r.loaded),\n error: r.error,\n saving: inflight > 0,\n create,\n update,\n remove,\n addOption,\n renameOption,\n removeOption,\n moveOption,\n setImage,\n setOptionImage,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"polls.js","names":[],"sources":["../../src/react/polls.ts"],"sourcesContent":["import { useCallback, useState } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport { toContentKitError, type ContentKitError } from \"../client/errors.js\";\nimport type { Poll, PollInput, PollOption, PollUpdate } from \"../client/generated/wire.js\";\nimport { useGuarded } from \"./context.js\";\nimport { keyOf, offsetPages, useContentScope, useList, useResource, type UseList } from \"./use-resource.js\";\n\n/** The poll after the caller votes for option; a vote is final. */\nexport function withVote(p: Poll, option: string): Poll {\n if (p.voted) return p;\n return { ...p, voted: true, my_option: option, total_votes: p.total_votes + 1, options: p.options.map((o) => (o.id === option ? { ...o, vote_count: o.vote_count + 1 } : o)) };\n}\n\nexport interface UsePoll {\n /** null: no such poll (or no live one). */\n poll: Poll | null;\n loading: boolean;\n error?: ContentKitError;\n reload: () => void;\n /** A vote or answer is in flight. */\n pending: boolean;\n /** Votes at once (a vote is final); rolled back if the server refuses it. */\n vote: (option: string) => Promise<void>;\n /** Stores or replaces the signed-in caller's free-text answer. */\n answer: (text: string) => Promise<void>;\n}\n\n/**\n * A poll with the caller's vote or answer. Without an id it is the newest\n * live poll (in language): the one a home page shows.\n */\nexport function usePoll(id?: string | null, o: { initial?: Poll; language?: string; client?: ContentKitClient } = {}): UsePoll {\n const { client, store, scope } = useContentScope(o.client);\n const key = id ? keyOf(\"poll\", scope, id) : keyOf(\"poll\", scope, \"latest\", o.language ?? \"\");\n const r = useResource<Poll | null>(\n store,\n key,\n { type: \"poll\", id: id ?? undefined },\n async (s) => (id ? client.polls.get(id, s) : ((await client.polls.list({ language: o.language, limit: 1 }, s))[0] ?? null)),\n { initial: o.initial },\n );\n const [pending, setPending] = useState(false);\n const poll = r.data ?? null;\n const vote = useCallback(\n async (option: string) => {\n const prev = store.snapshot<Poll | null>(key).data ?? poll;\n if (!prev) return;\n const token = store.mark(key);\n store.set(key, withVote(prev, option));\n setPending(true);\n try {\n await client.polls.vote(prev.id, option);\n } catch (e) {\n store.rollback(key, token, prev);\n throw toContentKitError(e);\n } finally {\n setPending(false);\n }\n },\n [client, store, key, poll],\n );\n const answer = useCallback(\n async (text: string) => {\n if (!poll) return;\n setPending(true);\n try {\n await client.polls.answer(poll.id, text);\n } finally {\n setPending(false);\n }\n },\n [client, poll],\n );\n return { poll, loading: r.loading || (!r.loaded && o.initial === undefined), error: r.error, reload: r.reload, pending, vote, answer };\n}\n\nexport interface PollFilter {\n language?: string;\n /** YYYY-MM */\n month?: string;\n /** YYYY-MM-DD */\n date?: string;\n /** Staff: every poll, scheduled and inactive ones included (root:polls:write). */\n admin?: boolean;\n pageSize?: number;\n}\n\n/** Polls, newest first, a page at a time. */\nexport function usePolls(f: PollFilter = {}, o: { client?: ContentKitClient } = {}): UseList<Poll> {\n const { client, store, scope } = useContentScope(o.client);\n const size = f.pageSize ?? 20;\n const q = { language: f.language, month: f.month, date: f.date };\n return useList(\n store,\n keyOf(\"polls\", scope, !!f.admin, q.language, q.month, q.date, size),\n { type: \"polls\" },\n offsetPages(size, (page, signal) => (f.admin ? client.polls.adminList({ ...q, ...page }, signal) : client.polls.list({ ...q, ...page }, signal))),\n );\n}\n\n/** Images for a new poll, uploaded once it exists: the question's, and each option's by index. */\nexport interface PollImages {\n question?: Blob | null;\n options?: readonly (Blob | null | undefined)[];\n}\n\nexport interface UsePollEditor {\n /** null before create (or while loading). */\n poll: Poll | null;\n loading: boolean;\n error?: ContentKitError;\n /** Writes in flight. */\n saving: boolean;\n /** Creates the poll, then uploads its images; the editor then edits it. Image failures are returned, not thrown. */\n create: (input: PollInput, images?: PollImages) => Promise<{ poll: Poll; imageErrors: ContentKitError[] }>;\n update: (patch: PollUpdate) => Promise<Poll>;\n remove: () => Promise<void>;\n addOption: (label: string, image?: Blob | null) => Promise<PollOption>;\n renameOption: (option: string, label: string) => Promise<void>;\n /** Removes an option and its votes; a poll keeps at least two. */\n removeOption: (option: string) => Promise<void>;\n /** Moves an option to index to, at once; refetched if a write fails. */\n moveOption: (option: string, to: number) => Promise<void>;\n setImage: (file: Blob | null) => Promise<void>;\n setOptionImage: (option: string, file: Blob | null) => Promise<void>;\n}\n\n/** Staff: one poll's editor (root:polls:write); null creates a new poll. */\nexport function usePollEditor(id: string | null | undefined, o: { client?: ContentKitClient } = {}): UsePollEditor {\n const { client, store, scope } = useContentScope(o.client);\n const [created, setCreated] = useState<string | null>(null);\n const pid = id ?? created;\n const key = pid ? keyOf(\"poll\", scope, pid) : null;\n const r = useResource<Poll | null>(store, key, { type: \"poll\", id: pid ?? undefined }, (s) => client.polls.get(pid!, s));\n const poll = r.data ?? null;\n const [inflight, setInflight] = useState(0);\n const guarded = useGuarded();\n const run = useCallback(async <T>(fn: () => Promise<T>): Promise<T> => {\n setInflight((n) => n + 1);\n try {\n return await guarded(fn);\n } finally {\n setInflight((n) => n - 1);\n }\n }, [guarded]);\n const need = useCallback(() => {\n if (!pid) throw new Error(\"contentkit-ui: create the poll first\");\n return pid;\n }, [pid]);\n\n const create = useCallback<UsePollEditor[\"create\"]>(\n (input, images = {}) =>\n run(async () => {\n const made = await client.polls.create(input);\n const imageErrors: ContentKitError[] = [];\n const tries: Promise<unknown>[] = [];\n if (images.question) tries.push(client.polls.uploadImage(made.id, images.question));\n const byPosition = [...made.options].sort((a, b) => a.position - b.position);\n (input.options ?? []).forEach((opt, i) => {\n const file = images.options?.[i];\n const target = byPosition.find((x) => x.position === opt.position) ?? byPosition[i];\n if (file && target) tries.push(client.polls.uploadOptionImage(made.id, target.id, file));\n });\n for (const res of await Promise.allSettled(tries)) if (res.status === \"rejected\") imageErrors.push(toContentKitError(res.reason));\n setCreated(made.id);\n return { poll: tries.length ? await client.polls.get(made.id) : made, imageErrors };\n }),\n [client, run],\n );\n const update = useCallback((patch: PollUpdate) => run(() => client.polls.update(need(), patch)), [client, run, need]);\n const remove = useCallback(() => run(() => client.polls.delete(need())), [client, run, need]);\n const addOption = useCallback(\n (label: string, image?: Blob | null) =>\n run(async () => {\n const opt = await client.polls.addOption(need(), { label });\n if (image) opt.image_url = (await client.polls.uploadOptionImage(need(), opt.id, image)) ?? undefined;\n return opt;\n }),\n [client, run, need],\n );\n const renameOption = useCallback((option: string, label: string) => run(async () => void (await client.polls.updateOption(need(), option, { label }))), [client, run, need]);\n const removeOption = useCallback((option: string) => run(() => client.polls.deleteOption(need(), option)), [client, run, need]);\n const moveOption = useCallback(\n (option: string, to: number) =>\n run(async () => {\n const current = store.snapshot<Poll | null>(key!).data;\n if (!current) return;\n const order = [...current.options].sort((a, b) => a.position - b.position);\n const from = order.findIndex((x) => x.id === option);\n if (from < 0 || to < 0 || to >= order.length || from === to) return;\n order.splice(to, 0, ...order.splice(from, 1));\n store.set<Poll | null>(key!, { ...current, options: order.map((x, position) => ({ ...x, position })) });\n try {\n await client.polls.reorderOptions(current.id, order);\n } catch (e) {\n store.load(key!, true);\n throw e;\n }\n }),\n [client, store, key, run],\n );\n const setImage = useCallback((file: Blob | null) => run(async () => void (await client.polls.uploadImage(need(), file))), [client, run, need]);\n const setOptionImage = useCallback(\n (option: string, file: Blob | null) => run(async () => void (await client.polls.uploadOptionImage(need(), option, file))),\n [client, run, need],\n );\n return {\n poll,\n loading: !!key && (r.loading || !r.loaded),\n error: r.error,\n saving: inflight > 0,\n create,\n update,\n remove,\n addOption,\n renameOption,\n removeOption,\n moveOption,\n setImage,\n setOptionImage,\n };\n}\n"],"mappings":"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"}
|
package/dist/react/posts.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { useGuarded as e } from "./context.js";
|
|
2
|
+
import { useMediaRead as t } from "./read.js";
|
|
3
|
+
import { keyOf as n, offsetPages as r, useContentScope as i, useList as a, useResource as o } from "./use-resource.js";
|
|
4
|
+
import { IMAGE_SCHEME as s, imageRef as c, imageRefs as l } from "../client/content/posts.js";
|
|
5
|
+
import { useCallback as u, useEffect as d, useMemo as f, useRef as p, useState as m } from "react";
|
|
5
6
|
//#region src/react/posts.ts
|
|
6
|
-
function
|
|
7
|
-
let { client: o, store: s, scope: c } =
|
|
8
|
-
return
|
|
7
|
+
function h(e = {}, t = {}) {
|
|
8
|
+
let { client: o, store: s, scope: c } = i(t.client), l = e.pageSize ?? 20;
|
|
9
|
+
return a(s, n("posts", c, !!e.admin, e.language, e.sort, e.draft, e.deleted, e.q, l), {
|
|
9
10
|
type: "posts",
|
|
10
11
|
scope: e.admin && e.deleted ? "deleted" : void 0
|
|
11
|
-
},
|
|
12
|
+
}, r(l, (t, n) => e.admin ? o.posts.adminList({
|
|
12
13
|
...t,
|
|
13
14
|
language: e.language,
|
|
14
15
|
draft: e.draft,
|
|
@@ -20,163 +21,163 @@ function m(e = {}, a = {}) {
|
|
|
20
21
|
sort: e.sort
|
|
21
22
|
}, n)));
|
|
22
23
|
}
|
|
23
|
-
var
|
|
24
|
+
var g = (e) => [...new Set([...(e ?? "").matchAll(l())].map((e) => e[1]))], _ = (e) => {
|
|
24
25
|
let t = e.slice(e.lastIndexOf("/") + 1), n = t.lastIndexOf(".");
|
|
25
26
|
return n > 0 ? t.slice(0, n) : t;
|
|
26
27
|
};
|
|
27
|
-
function
|
|
28
|
+
function v(e, t = Date.now()) {
|
|
28
29
|
return !e.is_draft && !e.moderation && (!e.live_at || Date.parse(e.live_at) <= t);
|
|
29
30
|
}
|
|
30
|
-
function
|
|
31
|
-
let { client:
|
|
31
|
+
function y(r, a = {}) {
|
|
32
|
+
let { client: h, store: v, scope: y } = i(a.client), [b, x] = m(null), S = r ?? b, C = S ? n("post", y, S) : null, w = o(v, C, {
|
|
32
33
|
type: "post",
|
|
33
|
-
id:
|
|
34
|
-
}, (e) =>
|
|
35
|
-
|
|
34
|
+
id: S ?? void 0
|
|
35
|
+
}, (e) => h.posts.get(S, e), { initial: a.initial }), T = w.data ?? null, [E, D] = m(0), O = e(), k = u(async (e) => {
|
|
36
|
+
D((e) => e + 1);
|
|
36
37
|
try {
|
|
37
|
-
return await e
|
|
38
|
+
return await O(e);
|
|
38
39
|
} finally {
|
|
39
|
-
|
|
40
|
+
D((e) => e - 1);
|
|
40
41
|
}
|
|
41
|
-
}, []),
|
|
42
|
-
if (!
|
|
43
|
-
return
|
|
44
|
-
}, [
|
|
45
|
-
|
|
46
|
-
let e =
|
|
42
|
+
}, [O]), A = u(() => {
|
|
43
|
+
if (!S) throw Error("contentkit-ui: create the post first");
|
|
44
|
+
return S;
|
|
45
|
+
}, [S]), [j, M] = m(/* @__PURE__ */ new Map()), N = p([]);
|
|
46
|
+
d(() => {
|
|
47
|
+
let e = N.current;
|
|
47
48
|
return () => {
|
|
48
49
|
for (let t of e) URL.revokeObjectURL(t);
|
|
49
50
|
};
|
|
50
51
|
}, []);
|
|
51
|
-
let
|
|
52
|
-
let e =
|
|
53
|
-
return
|
|
52
|
+
let P = f(() => new Map(Object.entries(T?.images ?? {})), [T?.images]), F = f(() => {
|
|
53
|
+
let e = g(T?.body);
|
|
54
|
+
return T?.cover && !T.cover_url && !e.includes(T.cover) && e.push(T.cover), e.filter((e) => !j.has(e));
|
|
54
55
|
}, [
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
]),
|
|
56
|
+
T?.body,
|
|
57
|
+
T?.cover,
|
|
58
|
+
T?.cover_url,
|
|
59
|
+
j
|
|
60
|
+
]), I = f(() => h.media.postRef(S ?? "none"), [h, S]), L = t(I, {
|
|
60
61
|
editor: !0,
|
|
61
|
-
read:
|
|
62
|
-
client:
|
|
63
|
-
}),
|
|
62
|
+
read: F.length ? void 0 : null,
|
|
63
|
+
client: h
|
|
64
|
+
}), R = f(() => {
|
|
64
65
|
let e = /* @__PURE__ */ new Map();
|
|
65
|
-
for (let t of
|
|
66
|
+
for (let t of L.read?.files ?? []) t.upload && t.editor_url && e.set(_(t.path), t.editor_url);
|
|
66
67
|
return e;
|
|
67
|
-
}, [
|
|
68
|
-
|
|
69
|
-
if (!
|
|
70
|
-
|
|
68
|
+
}, [L.read]), z = (e) => !!L.read?.files.some((t) => t.failed && _(t.path) === e), B = !!L.read && F.some((e) => !R.has(e) && !z(e)), V = p(0), { reload: H } = L;
|
|
69
|
+
d(() => {
|
|
70
|
+
if (!B) {
|
|
71
|
+
V.current = 0;
|
|
71
72
|
return;
|
|
72
73
|
}
|
|
73
|
-
if (
|
|
74
|
+
if (V.current >= 6) return;
|
|
74
75
|
let e = setTimeout(() => {
|
|
75
|
-
|
|
76
|
-
}, 1e3 * 2 **
|
|
76
|
+
V.current++, H();
|
|
77
|
+
}, 1e3 * 2 ** V.current);
|
|
77
78
|
return () => clearTimeout(e);
|
|
78
79
|
}, [
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
B,
|
|
81
|
+
L.read,
|
|
82
|
+
H
|
|
82
83
|
]);
|
|
83
|
-
let
|
|
84
|
-
|
|
84
|
+
let U = p(/* @__PURE__ */ new Map());
|
|
85
|
+
d(() => {
|
|
85
86
|
for (let e of [
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
]) for (let [t, n] of e)
|
|
87
|
+
j,
|
|
88
|
+
P,
|
|
89
|
+
R
|
|
90
|
+
]) for (let [t, n] of e) U.current.set(n, t);
|
|
90
91
|
}, [
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
j,
|
|
93
|
+
P,
|
|
94
|
+
R
|
|
94
95
|
]);
|
|
95
|
-
let
|
|
96
|
+
let W = u((e) => {
|
|
96
97
|
if (!e) return;
|
|
97
|
-
let t = e.startsWith("contentkit:") ? e.slice(
|
|
98
|
-
return
|
|
98
|
+
let t = e.startsWith("contentkit:") ? e.slice(s.length) : e;
|
|
99
|
+
return j.get(t) ?? P.get(t) ?? R.get(t) ?? e;
|
|
99
100
|
}, [
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
]),
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
]),
|
|
108
|
-
let t = new Map(
|
|
101
|
+
j,
|
|
102
|
+
P,
|
|
103
|
+
R
|
|
104
|
+
]), G = f(() => T?.body.replace(l(), (e, t) => j.get(t) ?? R.get(t) ?? e), [
|
|
105
|
+
T?.body,
|
|
106
|
+
j,
|
|
107
|
+
R
|
|
108
|
+
]), K = u((e) => {
|
|
109
|
+
let t = new Map(U.current);
|
|
109
110
|
for (let e of [
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
j,
|
|
112
|
+
P,
|
|
113
|
+
R
|
|
113
114
|
]) for (let [n, r] of e) t.set(r, n);
|
|
114
115
|
let n = e;
|
|
115
|
-
for (let [e, r] of t) n = n.split(e).join(
|
|
116
|
+
for (let [e, r] of t) n = n.split(e).join(c(r)).split(e.replaceAll("&", "&")).join(c(r));
|
|
116
117
|
return n;
|
|
117
118
|
}, [
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
]),
|
|
119
|
+
j,
|
|
120
|
+
P,
|
|
121
|
+
R
|
|
122
|
+
]), q = u((e, t) => {
|
|
122
123
|
if (typeof URL.createObjectURL != "function") return;
|
|
123
124
|
let n = URL.createObjectURL(t);
|
|
124
|
-
return
|
|
125
|
-
}, []),
|
|
126
|
-
let t = await
|
|
127
|
-
return
|
|
128
|
-
}), [
|
|
125
|
+
return N.current.push(n), M((t) => new Map(t).set(e, n)), n;
|
|
126
|
+
}, []), J = u((e) => k(async () => {
|
|
127
|
+
let t = await h.posts.create(e);
|
|
128
|
+
return x(t.id), t;
|
|
129
|
+
}), [h, k]), Y = u((e) => k(() => h.posts.update(A(), e.body === void 0 ? e : {
|
|
129
130
|
...e,
|
|
130
|
-
body:
|
|
131
|
+
body: K(e.body)
|
|
131
132
|
})), [
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
]),
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
]),
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
]),
|
|
145
|
-
if (!e) return
|
|
146
|
-
let t = await
|
|
147
|
-
return r &&
|
|
133
|
+
h,
|
|
134
|
+
k,
|
|
135
|
+
A,
|
|
136
|
+
K
|
|
137
|
+
]), X = u(() => k(() => h.posts.delete(A())), [
|
|
138
|
+
h,
|
|
139
|
+
k,
|
|
140
|
+
A
|
|
141
|
+
]), Z = u(() => k(() => h.posts.restore(A())), [
|
|
142
|
+
h,
|
|
143
|
+
k,
|
|
144
|
+
A
|
|
145
|
+
]), Q = u((e) => k(async () => {
|
|
146
|
+
if (!e) return h.posts.setCover(A(), null);
|
|
147
|
+
let t = await h.media.uploadNamed(h.media.postRef(A()), e), n = await h.posts.setCover(A(), t.name), r = q(t.name, e) ?? n;
|
|
148
|
+
return r && U.current.set(r, t.name), r;
|
|
148
149
|
}), [
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
]),
|
|
154
|
-
let t = await
|
|
155
|
-
return
|
|
150
|
+
h,
|
|
151
|
+
k,
|
|
152
|
+
A,
|
|
153
|
+
q
|
|
154
|
+
]), $ = u((e) => k(async () => {
|
|
155
|
+
let t = await h.media.uploadInline(A(), e), n = q(t.name, e) ?? t.url ?? t.ref;
|
|
156
|
+
return U.current.set(n, t.name), n;
|
|
156
157
|
}), [
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
158
|
+
h,
|
|
159
|
+
k,
|
|
160
|
+
A,
|
|
161
|
+
q
|
|
161
162
|
]);
|
|
162
163
|
return {
|
|
163
|
-
post:
|
|
164
|
-
loading: !!
|
|
165
|
-
error:
|
|
166
|
-
saving:
|
|
167
|
-
create:
|
|
168
|
-
update:
|
|
169
|
-
remove:
|
|
170
|
-
restore:
|
|
171
|
-
setCover:
|
|
172
|
-
uploadImage:
|
|
173
|
-
imageSrc:
|
|
174
|
-
coverSrc:
|
|
175
|
-
editorBody:
|
|
176
|
-
storedBody:
|
|
164
|
+
post: T,
|
|
165
|
+
loading: !!C && (w.loading || !w.loaded && a.initial === void 0),
|
|
166
|
+
error: w.error,
|
|
167
|
+
saving: E > 0,
|
|
168
|
+
create: J,
|
|
169
|
+
update: Y,
|
|
170
|
+
remove: X,
|
|
171
|
+
restore: Z,
|
|
172
|
+
setCover: Q,
|
|
173
|
+
uploadImage: $,
|
|
174
|
+
imageSrc: W,
|
|
175
|
+
coverSrc: W(T?.cover),
|
|
176
|
+
editorBody: G,
|
|
177
|
+
storedBody: K
|
|
177
178
|
};
|
|
178
179
|
}
|
|
179
180
|
//#endregion
|
|
180
|
-
export {
|
|
181
|
+
export { v as isPublished, y as usePost, h as usePosts };
|
|
181
182
|
|
|
182
183
|
//# sourceMappingURL=posts.js.map
|