@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.
Files changed (134) hide show
  1. package/README.md +122 -10
  2. package/dist/client/content/bans.d.ts +2 -0
  3. package/dist/client/content/bans.js +24 -12
  4. package/dist/client/content/bans.js.map +1 -1
  5. package/dist/client/content/comments.d.ts +2 -0
  6. package/dist/client/content/comments.js.map +1 -1
  7. package/dist/client/content/polls.d.ts +9 -2
  8. package/dist/client/content/polls.js.map +1 -1
  9. package/dist/client/generated/routes.d.ts +28 -2
  10. package/dist/client/generated/routes.js +40 -0
  11. package/dist/client/generated/routes.js.map +1 -1
  12. package/dist/client/generated/wire.d.ts +4 -0
  13. package/dist/client/generated/wire.js.map +1 -1
  14. package/dist/client/index.d.ts +3 -3
  15. package/dist/client/media/queue.d.ts +1 -1
  16. package/dist/client/media/queue.js +1 -1
  17. package/dist/client/media/queue.js.map +1 -1
  18. package/dist/client/taxonomy.d.ts +5 -1
  19. package/dist/client/taxonomy.js +7 -0
  20. package/dist/client/taxonomy.js.map +1 -1
  21. package/dist/client.js +2 -2
  22. package/dist/components/admin/media-item-panel.d.ts +27 -0
  23. package/dist/components/admin/media-item-panel.js +397 -0
  24. package/dist/components/admin/media-item-panel.js.map +1 -0
  25. package/dist/components/admin/poll-manager.d.ts +28 -0
  26. package/dist/components/admin/poll-manager.js +297 -0
  27. package/dist/components/admin/poll-manager.js.map +1 -0
  28. package/dist/components/admin/post-manager.d.ts +45 -0
  29. package/dist/components/admin/post-manager.js +602 -0
  30. package/dist/components/admin/post-manager.js.map +1 -0
  31. package/dist/components/admin/shell.d.ts +73 -0
  32. package/dist/components/admin/shell.js +148 -0
  33. package/dist/components/admin/shell.js.map +1 -0
  34. package/dist/components/admin/taxonomy-manager.d.ts +54 -0
  35. package/dist/components/admin/taxonomy-manager.js +976 -0
  36. package/dist/components/admin/taxonomy-manager.js.map +1 -0
  37. package/dist/components/admin/upload-queue.d.ts +23 -0
  38. package/dist/components/admin/upload-queue.js +365 -0
  39. package/dist/components/admin/upload-queue.js.map +1 -0
  40. package/dist/components/admin/user-comment-standing.d.ts +25 -0
  41. package/dist/components/admin/user-comment-standing.js +229 -0
  42. package/dist/components/admin/user-comment-standing.js.map +1 -0
  43. package/dist/components/social/bans.d.ts +5 -2
  44. package/dist/components/social/bans.js +132 -134
  45. package/dist/components/social/bans.js.map +1 -1
  46. package/dist/components/social/index.d.ts +1 -1
  47. package/dist/components/social/moderation.d.ts +11 -13
  48. package/dist/components/social/moderation.js +299 -218
  49. package/dist/components/social/moderation.js.map +1 -1
  50. package/dist/components/social/parts.d.ts +3 -2
  51. package/dist/components/social/parts.js +74 -48
  52. package/dist/components/social/parts.js.map +1 -1
  53. package/dist/components/social/poll-editor.js +1 -0
  54. package/dist/components/social/poll-editor.js.map +1 -1
  55. package/dist/editor/index.d.ts +1 -0
  56. package/dist/editor/rich-text-editor.d.ts +13 -0
  57. package/dist/editor/rich-text-editor.js +228 -0
  58. package/dist/editor/rich-text-editor.js.map +1 -0
  59. package/dist/editor.js +2 -0
  60. package/dist/index.d.ts +8 -0
  61. package/dist/index.js +15 -8
  62. package/dist/locales/admin/de.d.ts +2 -0
  63. package/dist/locales/admin/de.js +227 -0
  64. package/dist/locales/admin/de.js.map +1 -0
  65. package/dist/locales/admin/en.d.ts +222 -0
  66. package/dist/locales/admin/en.js +227 -0
  67. package/dist/locales/admin/en.js.map +1 -0
  68. package/dist/locales/admin/es.d.ts +2 -0
  69. package/dist/locales/admin/es.js +227 -0
  70. package/dist/locales/admin/es.js.map +1 -0
  71. package/dist/locales/admin/ja.d.ts +2 -0
  72. package/dist/locales/admin/ja.js +227 -0
  73. package/dist/locales/admin/ja.js.map +1 -0
  74. package/dist/locales/admin/ko.d.ts +2 -0
  75. package/dist/locales/admin/ko.js +227 -0
  76. package/dist/locales/admin/ko.js.map +1 -0
  77. package/dist/locales/admin/zh.d.ts +2 -0
  78. package/dist/locales/admin/zh.js +227 -0
  79. package/dist/locales/admin/zh.js.map +1 -0
  80. package/dist/locales/de.js +5 -3
  81. package/dist/locales/de.js.map +1 -1
  82. package/dist/locales/en.d.ts +227 -0
  83. package/dist/locales/en.js +5 -3
  84. package/dist/locales/en.js.map +1 -1
  85. package/dist/locales/es.js +5 -3
  86. package/dist/locales/es.js.map +1 -1
  87. package/dist/locales/ja.js +5 -3
  88. package/dist/locales/ja.js.map +1 -1
  89. package/dist/locales/ko.js +5 -3
  90. package/dist/locales/ko.js.map +1 -1
  91. package/dist/locales/social/de.js +8 -1
  92. package/dist/locales/social/de.js.map +1 -1
  93. package/dist/locales/social/en.d.ts +7 -0
  94. package/dist/locales/social/en.js +8 -1
  95. package/dist/locales/social/en.js.map +1 -1
  96. package/dist/locales/social/es.js +8 -1
  97. package/dist/locales/social/es.js.map +1 -1
  98. package/dist/locales/social/ja.js +8 -1
  99. package/dist/locales/social/ja.js.map +1 -1
  100. package/dist/locales/social/ko.js +8 -1
  101. package/dist/locales/social/ko.js.map +1 -1
  102. package/dist/locales/social/zh.js +8 -1
  103. package/dist/locales/social/zh.js.map +1 -1
  104. package/dist/locales/zh.js +5 -3
  105. package/dist/locales/zh.js.map +1 -1
  106. package/dist/provider.d.ts +2 -0
  107. package/dist/provider.js +23 -19
  108. package/dist/provider.js.map +1 -1
  109. package/dist/react/admin.d.ts +55 -0
  110. package/dist/react/admin.js +97 -0
  111. package/dist/react/admin.js.map +1 -0
  112. package/dist/react/comments.d.ts +18 -2
  113. package/dist/react/comments.js +24 -8
  114. package/dist/react/comments.js.map +1 -1
  115. package/dist/react/context.d.ts +7 -1
  116. package/dist/react/context.js +6 -1
  117. package/dist/react/context.js.map +1 -1
  118. package/dist/react/folder.js.map +1 -1
  119. package/dist/react/index.d.ts +4 -3
  120. package/dist/react/polls.js +88 -87
  121. package/dist/react/polls.js.map +1 -1
  122. package/dist/react/posts.js +123 -122
  123. package/dist/react/posts.js.map +1 -1
  124. package/dist/react/resources.js +3 -0
  125. package/dist/react/resources.js.map +1 -1
  126. package/dist/react/upload.d.ts +10 -10
  127. package/dist/react/upload.js +1 -1
  128. package/dist/react/upload.js.map +1 -1
  129. package/dist/react/use-resource.d.ts +16 -0
  130. package/dist/react/use-resource.js +20 -1
  131. package/dist/react/use-resource.js.map +1 -1
  132. package/dist/react.js +21 -20
  133. package/dist/styles.css +1 -1
  134. package/package.json +37 -2
@@ -1,8 +1,9 @@
1
1
  import { toContentKitError as e } from "../client/errors.js";
2
- import { keyOf as t, offsetPages as n, useContentScope as r, useList as i, useResource as a } from "./use-resource.js";
3
- import { useCallback as o, useState as s } from "react";
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 c(e, t) {
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 l(n, i = {}) {
18
- let { client: l, store: u, scope: d } = r(i.client), f = n ? t("poll", d, n) : t("poll", d, "latest", i.language ?? ""), p = a(u, f, {
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: n ?? void 0
21
- }, async (e) => n ? l.polls.get(n, e) : (await l.polls.list({
22
- language: i.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: i.initial }), [m, h] = s(!1), g = p.data ?? null, _ = o(async (t) => {
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, c(n, t)), h(!0);
29
+ u.set(f, l(n, t)), h(!0);
29
30
  try {
30
- await l.polls.vote(n.id, t);
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
- l,
38
+ a,
38
39
  u,
39
40
  f,
40
41
  g
41
- ]), v = o(async (e) => {
42
+ ]), v = s(async (e) => {
42
43
  if (g) {
43
44
  h(!0);
44
45
  try {
45
- await l.polls.answer(g.id, e);
46
+ await a.polls.answer(g.id, e);
46
47
  } finally {
47
48
  h(!1);
48
49
  }
49
50
  }
50
- }, [l, g]);
51
+ }, [a, g]);
51
52
  return {
52
53
  poll: g,
53
- loading: p.loading || !p.loaded && i.initial === void 0,
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 u(e = {}, a = {}) {
62
- let { client: o, store: s, scope: c } = r(a.client), l = e.pageSize ?? 20, u = {
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 i(s, t("polls", c, !!e.admin, u.language, u.month, u.date, l), { type: "polls" }, n(l, (t, n) => e.admin ? o.polls.adminList({
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 d(n, i = {}) {
76
- let { client: c, store: l, scope: u } = r(i.client), [d, f] = s(null), p = n ?? d, m = p ? t("poll", u, p) : null, h = a(l, m, {
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: p ?? void 0
79
- }, (e) => c.polls.get(p, e)), g = h.data ?? null, [_, v] = s(0), y = o(async (e) => {
80
- v((e) => e + 1);
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
- v((e) => e - 1);
85
+ y((e) => e - 1);
85
86
  }
86
- }, []), b = o(() => {
87
- if (!p) throw Error("contentkit-ui: create the poll first");
88
- return p;
89
- }, [p]), x = o((t, n = {}) => y(async () => {
90
- let r = await c.polls.create(t), i = [], a = [];
91
- n.question && a.push(c.polls.uploadImage(r.id, n.question));
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(c.polls.uploadOptionImage(r.id, s.id, i));
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 f(r.id), {
99
- poll: a.length ? await c.polls.get(r.id) : r,
99
+ return p(r.id), {
100
+ poll: a.length ? await l.polls.get(r.id) : r,
100
101
  imageErrors: i
101
102
  };
102
- }), [c, y]), S = o((e) => y(() => c.polls.update(b(), e)), [
103
- c,
104
- y,
105
- b
106
- ]), C = o(() => y(() => c.polls.delete(b())), [
107
- c,
108
- y,
109
- b
110
- ]), w = o((e, t) => y(async () => {
111
- let n = await c.polls.addOption(b(), { label: e });
112
- return t && (n.image_url = await c.polls.uploadOptionImage(b(), n.id, t) ?? void 0), n;
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
- c,
115
- y,
116
- b
117
- ]), T = o((e, t) => y(async () => void await c.polls.updateOption(b(), e, { label: t })), [
118
- c,
119
- y,
120
- b
121
- ]), E = o((e) => y(() => c.polls.deleteOption(b(), e)), [
122
- c,
123
- y,
124
- b
125
- ]), D = o((e, t) => y(async () => {
126
- let n = l.snapshot(m).data;
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)), l.set(m, {
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 c.polls.reorderOptions(n.id, r);
139
+ await l.polls.reorderOptions(n.id, r);
139
140
  } catch (e) {
140
- throw l.load(m, !0), e;
141
+ throw u.load(h, !0), e;
141
142
  }
142
143
  }
143
144
  }), [
144
- c,
145
145
  l,
146
- m,
147
- y
148
- ]), O = o((e) => y(async () => void await c.polls.uploadImage(b(), e)), [
149
- c,
150
- y,
151
- b
152
- ]), k = o((e, t) => y(async () => void await c.polls.uploadOptionImage(b(), e, t)), [
153
- c,
154
- y,
155
- b
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: g,
159
- loading: !!m && (h.loading || !h.loaded),
160
- error: h.error,
161
- saving: _ > 0,
162
- create: x,
163
- update: S,
164
- remove: C,
165
- addOption: w,
166
- renameOption: T,
167
- removeOption: E,
168
- moveOption: D,
169
- setImage: O,
170
- setOptionImage: k
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 { l as usePoll, d as usePollEditor, u as usePolls, c as withVote };
175
+ export { u as usePoll, f as usePollEditor, d as usePolls, l as withVote };
175
176
 
176
177
  //# sourceMappingURL=polls.js.map
@@ -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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"}
@@ -1,14 +1,15 @@
1
- import { useMediaRead as e } from "./read.js";
2
- import { keyOf as t, offsetPages as n, useContentScope as r, useList as i, useResource as a } from "./use-resource.js";
3
- import { IMAGE_SCHEME as o, imageRef as s, imageRefs as c } from "../client/content/posts.js";
4
- import { useCallback as l, useEffect as u, useMemo as d, useRef as f, useState as p } from "react";
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 m(e = {}, a = {}) {
7
- let { client: o, store: s, scope: c } = r(a.client), l = e.pageSize ?? 20;
8
- return i(s, t("posts", c, !!e.admin, e.language, e.sort, e.draft, e.deleted, e.q, l), {
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
- }, n(l, (t, n) => e.admin ? o.posts.adminList({
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 h = (e) => [...new Set([...(e ?? "").matchAll(c())].map((e) => e[1]))], g = (e) => {
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 _(e, t = Date.now()) {
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 v(n, i = {}) {
31
- let { client: m, store: _, scope: v } = r(i.client), [y, b] = p(null), x = n ?? y, S = x ? t("post", v, x) : null, C = a(_, S, {
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: x ?? void 0
34
- }, (e) => m.posts.get(x, e), { initial: i.initial }), w = C.data ?? null, [T, E] = p(0), D = l(async (e) => {
35
- E((e) => e + 1);
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
- E((e) => e - 1);
40
+ D((e) => e - 1);
40
41
  }
41
- }, []), O = l(() => {
42
- if (!x) throw Error("contentkit-ui: create the post first");
43
- return x;
44
- }, [x]), [k, A] = p(/* @__PURE__ */ new Map()), j = f([]);
45
- u(() => {
46
- let e = j.current;
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 M = d(() => new Map(Object.entries(w?.images ?? {})), [w?.images]), N = d(() => {
52
- let e = h(w?.body);
53
- return w?.cover && !w.cover_url && !e.includes(w.cover) && e.push(w.cover), e.filter((e) => !k.has(e));
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
- w?.body,
56
- w?.cover,
57
- w?.cover_url,
58
- k
59
- ]), P = d(() => m.media.postRef(x ?? "none"), [m, x]), F = e(P, {
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: N.length ? void 0 : null,
62
- client: m
63
- }), I = d(() => {
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 F.read?.files ?? []) t.upload && t.editor_url && e.set(g(t.path), t.editor_url);
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
- }, [F.read]), L = (e) => !!F.read?.files.some((t) => t.failed && g(t.path) === e), R = !!F.read && N.some((e) => !I.has(e) && !L(e)), z = f(0), { reload: B } = F;
68
- u(() => {
69
- if (!R) {
70
- z.current = 0;
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 (z.current >= 6) return;
74
+ if (V.current >= 6) return;
74
75
  let e = setTimeout(() => {
75
- z.current++, B();
76
- }, 1e3 * 2 ** z.current);
76
+ V.current++, H();
77
+ }, 1e3 * 2 ** V.current);
77
78
  return () => clearTimeout(e);
78
79
  }, [
79
- R,
80
- F.read,
81
- B
80
+ B,
81
+ L.read,
82
+ H
82
83
  ]);
83
- let V = f(/* @__PURE__ */ new Map());
84
- u(() => {
84
+ let U = p(/* @__PURE__ */ new Map());
85
+ d(() => {
85
86
  for (let e of [
86
- k,
87
- M,
88
- I
89
- ]) for (let [t, n] of e) V.current.set(n, t);
87
+ j,
88
+ P,
89
+ R
90
+ ]) for (let [t, n] of e) U.current.set(n, t);
90
91
  }, [
91
- k,
92
- M,
93
- I
92
+ j,
93
+ P,
94
+ R
94
95
  ]);
95
- let H = l((e) => {
96
+ let W = u((e) => {
96
97
  if (!e) return;
97
- let t = e.startsWith("contentkit:") ? e.slice(o.length) : e;
98
- return k.get(t) ?? M.get(t) ?? I.get(t) ?? e;
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
- k,
101
- M,
102
- I
103
- ]), U = d(() => w?.body.replace(c(), (e, t) => k.get(t) ?? I.get(t) ?? e), [
104
- w?.body,
105
- k,
106
- I
107
- ]), W = l((e) => {
108
- let t = new Map(V.current);
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
- k,
111
- M,
112
- I
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(s(r)).split(e.replaceAll("&", "&amp;")).join(s(r));
116
+ for (let [e, r] of t) n = n.split(e).join(c(r)).split(e.replaceAll("&", "&amp;")).join(c(r));
116
117
  return n;
117
118
  }, [
118
- k,
119
- M,
120
- I
121
- ]), G = l((e, t) => {
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 j.current.push(n), A((t) => new Map(t).set(e, n)), n;
125
- }, []), K = l((e) => D(async () => {
126
- let t = await m.posts.create(e);
127
- return b(t.id), t;
128
- }), [m, D]), q = l((e) => D(() => m.posts.update(O(), e.body === void 0 ? e : {
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: W(e.body)
131
+ body: K(e.body)
131
132
  })), [
132
- m,
133
- D,
134
- O,
135
- W
136
- ]), J = l(() => D(() => m.posts.delete(O())), [
137
- m,
138
- D,
139
- O
140
- ]), Y = l(() => D(() => m.posts.restore(O())), [
141
- m,
142
- D,
143
- O
144
- ]), X = l((e) => D(async () => {
145
- if (!e) return m.posts.setCover(O(), null);
146
- let t = await m.media.uploadNamed(m.media.postRef(O()), e), n = await m.posts.setCover(O(), t.name), r = G(t.name, e) ?? n;
147
- return r && V.current.set(r, t.name), 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
- m,
150
- D,
151
- O,
152
- G
153
- ]), Z = l((e) => D(async () => {
154
- let t = await m.media.uploadInline(O(), e), n = G(t.name, e) ?? t.url ?? t.ref;
155
- return V.current.set(n, t.name), n;
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
- m,
158
- D,
159
- O,
160
- G
158
+ h,
159
+ k,
160
+ A,
161
+ q
161
162
  ]);
162
163
  return {
163
- post: w,
164
- loading: !!S && (C.loading || !C.loaded && i.initial === void 0),
165
- error: C.error,
166
- saving: T > 0,
167
- create: K,
168
- update: q,
169
- remove: J,
170
- restore: Y,
171
- setCover: X,
172
- uploadImage: Z,
173
- imageSrc: H,
174
- coverSrc: H(w?.cover),
175
- editorBody: U,
176
- storedBody: W
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 { _ as isPublished, v as usePost, m as usePosts };
181
+ export { v as isPublished, y as usePost, h as usePosts };
181
182
 
182
183
  //# sourceMappingURL=posts.js.map