@openrails/contentkit-ui 0.85.1 → 0.87.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/README.md +28 -7
  2. package/dist/client/beacons.d.ts +13 -4
  3. package/dist/client/beacons.js +14 -7
  4. package/dist/client/beacons.js.map +1 -1
  5. package/dist/client/client.d.ts +3 -0
  6. package/dist/client/client.js +29 -27
  7. package/dist/client/client.js.map +1 -1
  8. package/dist/client/codes.d.ts +6 -1
  9. package/dist/client/codes.js +4 -1
  10. package/dist/client/codes.js.map +1 -1
  11. package/dist/client/content/types.d.ts +4 -2
  12. package/dist/client/content/types.js.map +1 -1
  13. package/dist/client/discovery.js +3 -0
  14. package/dist/client/discovery.js.map +1 -1
  15. package/dist/client/errors.d.ts +6 -0
  16. package/dist/client/errors.js +6 -2
  17. package/dist/client/errors.js.map +1 -1
  18. package/dist/client/gallery.d.ts +3 -5
  19. package/dist/client/gallery.js +16 -20
  20. package/dist/client/gallery.js.map +1 -1
  21. package/dist/client/generated/error-codes.d.ts +1 -1
  22. package/dist/client/generated/error-codes.js +9 -1
  23. package/dist/client/generated/error-codes.js.map +1 -1
  24. package/dist/client/generated/routes.d.ts +29 -3
  25. package/dist/client/generated/routes.js +44 -3
  26. package/dist/client/generated/routes.js.map +1 -1
  27. package/dist/client/generated/wire.d.ts +60 -10
  28. package/dist/client/generated/wire.js.map +1 -1
  29. package/dist/client/index.d.ts +2 -1
  30. package/dist/client/media/queue.d.ts +4 -2
  31. package/dist/client/media/queue.js +18 -12
  32. package/dist/client/media/queue.js.map +1 -1
  33. package/dist/client/media/rules.d.ts +7 -1
  34. package/dist/client/media/rules.js +27 -21
  35. package/dist/client/media/rules.js.map +1 -1
  36. package/dist/client/media/windows.js +1 -1
  37. package/dist/client/media/windows.js.map +1 -1
  38. package/dist/client/stats.d.ts +32 -0
  39. package/dist/client/stats.js +35 -0
  40. package/dist/client/stats.js.map +1 -0
  41. package/dist/client/versions.d.ts +6 -4
  42. package/dist/client/versions.js +24 -26
  43. package/dist/client/versions.js.map +1 -1
  44. package/dist/client.js +30 -29
  45. package/dist/components/admin/content-manager.js +206 -206
  46. package/dist/components/admin/content-manager.js.map +1 -1
  47. package/dist/components/admin/group-manager.js +71 -70
  48. package/dist/components/admin/group-manager.js.map +1 -1
  49. package/dist/components/admin/media-item-panel.d.ts +24 -7
  50. package/dist/components/admin/media-item-panel.js +209 -197
  51. package/dist/components/admin/media-item-panel.js.map +1 -1
  52. package/dist/components/admin/poll-manager.js +25 -25
  53. package/dist/components/admin/stats-dashboard.d.ts +22 -0
  54. package/dist/components/admin/stats-dashboard.js +257 -0
  55. package/dist/components/admin/stats-dashboard.js.map +1 -0
  56. package/dist/components/content/card.d.ts +5 -3
  57. package/dist/components/content/card.js +155 -133
  58. package/dist/components/content/card.js.map +1 -1
  59. package/dist/components/content/download-button.d.ts +26 -0
  60. package/dist/components/content/download-button.js +51 -0
  61. package/dist/components/content/download-button.js.map +1 -0
  62. package/dist/components/content/editors.js +455 -427
  63. package/dist/components/content/editors.js.map +1 -1
  64. package/dist/components/content/grid.d.ts +8 -1
  65. package/dist/components/content/grid.js +153 -104
  66. package/dist/components/content/grid.js.map +1 -1
  67. package/dist/components/content/index.d.ts +4 -2
  68. package/dist/components/content/listing.d.ts +3 -1
  69. package/dist/components/content/listing.js +34 -34
  70. package/dist/components/content/listing.js.map +1 -1
  71. package/dist/components/content/paywall.d.ts +17 -5
  72. package/dist/components/content/paywall.js +115 -100
  73. package/dist/components/content/paywall.js.map +1 -1
  74. package/dist/components/content/reader-stats.d.ts +12 -0
  75. package/dist/components/content/reader-stats.js +47 -0
  76. package/dist/components/content/reader-stats.js.map +1 -0
  77. package/dist/components/groups/banner.js +33 -33
  78. package/dist/components/groups/directory.js +11 -11
  79. package/dist/components/groups/editor.js +293 -293
  80. package/dist/components/groups/editor.js.map +1 -1
  81. package/dist/components/groups/header.js +27 -27
  82. package/dist/components/groups/header.js.map +1 -1
  83. package/dist/components/groups/memberships.js +180 -180
  84. package/dist/components/groups/parts.d.ts +0 -2
  85. package/dist/components/groups/parts.js +9 -27
  86. package/dist/components/groups/parts.js.map +1 -1
  87. package/dist/components/media-folder-editor.js +239 -189
  88. package/dist/components/media-folder-editor.js.map +1 -1
  89. package/dist/components/media-gallery.d.ts +1 -4
  90. package/dist/components/media-gallery.js +19 -34
  91. package/dist/components/media-gallery.js.map +1 -1
  92. package/dist/components/notifications.js +17 -17
  93. package/dist/components/slot-upload.d.ts +2 -2
  94. package/dist/components/slot-upload.js +84 -79
  95. package/dist/components/slot-upload.js.map +1 -1
  96. package/dist/components/social/bans.js +34 -34
  97. package/dist/components/social/comments.js +1 -1
  98. package/dist/components/social/comments.js.map +1 -1
  99. package/dist/components/social/engagement.d.ts +2 -1
  100. package/dist/components/social/engagement.js +5 -3
  101. package/dist/components/social/engagement.js.map +1 -1
  102. package/dist/components/social/moderation.js +52 -48
  103. package/dist/components/social/moderation.js.map +1 -1
  104. package/dist/components/video-player.d.ts +2 -0
  105. package/dist/components/video-player.js +318 -314
  106. package/dist/components/video-player.js.map +1 -1
  107. package/dist/i18n/context.d.ts +2 -0
  108. package/dist/i18n/context.js +23 -5
  109. package/dist/i18n/context.js.map +1 -1
  110. package/dist/index.d.ts +2 -1
  111. package/dist/index.js +13 -10
  112. package/dist/locales/admin/de.js +27 -0
  113. package/dist/locales/admin/de.js.map +1 -1
  114. package/dist/locales/admin/en.d.ts +27 -0
  115. package/dist/locales/admin/en.js +27 -0
  116. package/dist/locales/admin/en.js.map +1 -1
  117. package/dist/locales/admin/es.js +27 -0
  118. package/dist/locales/admin/es.js.map +1 -1
  119. package/dist/locales/admin/ja.js +27 -0
  120. package/dist/locales/admin/ja.js.map +1 -1
  121. package/dist/locales/admin/ko.js +27 -0
  122. package/dist/locales/admin/ko.js.map +1 -1
  123. package/dist/locales/admin/zh.js +27 -0
  124. package/dist/locales/admin/zh.js.map +1 -1
  125. package/dist/locales/content/de.js +6 -0
  126. package/dist/locales/content/de.js.map +1 -1
  127. package/dist/locales/content/en.d.ts +6 -0
  128. package/dist/locales/content/en.js +6 -0
  129. package/dist/locales/content/en.js.map +1 -1
  130. package/dist/locales/content/es.js +6 -0
  131. package/dist/locales/content/es.js.map +1 -1
  132. package/dist/locales/content/ja.js +6 -0
  133. package/dist/locales/content/ja.js.map +1 -1
  134. package/dist/locales/content/ko.js +6 -0
  135. package/dist/locales/content/ko.js.map +1 -1
  136. package/dist/locales/content/zh.js +6 -0
  137. package/dist/locales/content/zh.js.map +1 -1
  138. package/dist/locales/de.js +14 -2
  139. package/dist/locales/de.js.map +1 -1
  140. package/dist/locales/discovery/de.js +3 -0
  141. package/dist/locales/discovery/de.js.map +1 -1
  142. package/dist/locales/discovery/en.d.ts +3 -0
  143. package/dist/locales/discovery/en.js +3 -0
  144. package/dist/locales/discovery/en.js.map +1 -1
  145. package/dist/locales/discovery/es.js +3 -0
  146. package/dist/locales/discovery/es.js.map +1 -1
  147. package/dist/locales/discovery/ja.js +3 -0
  148. package/dist/locales/discovery/ja.js.map +1 -1
  149. package/dist/locales/discovery/ko.js +3 -0
  150. package/dist/locales/discovery/ko.js.map +1 -1
  151. package/dist/locales/discovery/zh.js +3 -0
  152. package/dist/locales/discovery/zh.js.map +1 -1
  153. package/dist/locales/en.d.ts +50 -2
  154. package/dist/locales/en.js +14 -2
  155. package/dist/locales/en.js.map +1 -1
  156. package/dist/locales/es.js +14 -2
  157. package/dist/locales/es.js.map +1 -1
  158. package/dist/locales/ja.js +14 -2
  159. package/dist/locales/ja.js.map +1 -1
  160. package/dist/locales/ko.js +14 -2
  161. package/dist/locales/ko.js.map +1 -1
  162. package/dist/locales/zh.js +14 -2
  163. package/dist/locales/zh.js.map +1 -1
  164. package/dist/react/admin.js +22 -23
  165. package/dist/react/admin.js.map +1 -1
  166. package/dist/react/canonical.d.ts +4 -3
  167. package/dist/react/canonical.js +34 -33
  168. package/dist/react/canonical.js.map +1 -1
  169. package/dist/react/comments.d.ts +1 -1
  170. package/dist/react/comments.js +3 -2
  171. package/dist/react/comments.js.map +1 -1
  172. package/dist/react/contents.js +144 -144
  173. package/dist/react/contents.js.map +1 -1
  174. package/dist/react/context.d.ts +1 -1
  175. package/dist/react/context.js.map +1 -1
  176. package/dist/react/detail.d.ts +12 -6
  177. package/dist/react/detail.js +31 -24
  178. package/dist/react/detail.js.map +1 -1
  179. package/dist/react/discovery.d.ts +11 -0
  180. package/dist/react/discovery.js +49 -30
  181. package/dist/react/discovery.js.map +1 -1
  182. package/dist/react/engagement.d.ts +6 -2
  183. package/dist/react/engagement.js +2 -6
  184. package/dist/react/engagement.js.map +1 -1
  185. package/dist/react/folder.d.ts +7 -2
  186. package/dist/react/folder.js +165 -147
  187. package/dist/react/folder.js.map +1 -1
  188. package/dist/react/groups.d.ts +0 -1
  189. package/dist/react/groups.js +13 -12
  190. package/dist/react/groups.js.map +1 -1
  191. package/dist/react/index.d.ts +6 -4
  192. package/dist/react/location.d.ts +11 -0
  193. package/dist/react/location.js +18 -0
  194. package/dist/react/location.js.map +1 -0
  195. package/dist/react/public.d.ts +8 -0
  196. package/dist/react/public.js +32 -27
  197. package/dist/react/public.js.map +1 -1
  198. package/dist/react/stats.d.ts +24 -0
  199. package/dist/react/stats.js +28 -0
  200. package/dist/react/stats.js.map +1 -0
  201. package/dist/react/use-resource.js +8 -7
  202. package/dist/react/use-resource.js.map +1 -1
  203. package/dist/react/versions.d.ts +25 -5
  204. package/dist/react/versions.js +61 -40
  205. package/dist/react/versions.js.map +1 -1
  206. package/dist/react.js +26 -24
  207. package/dist/styles.css +1 -1
  208. package/dist/urls/index.d.ts +35 -10
  209. package/dist/urls.js +100 -70
  210. package/dist/urls.js.map +1 -1
  211. package/package.json +1 -1
@@ -1,8 +1,8 @@
1
1
  import { toContentKitError as e } from "../../client/errors.js";
2
2
  import { useMessages as t } from "../../i18n/context.js";
3
3
  import { useErrorReporter as n, useGuarded as r } from "../../react/context.js";
4
- import { Button as i } from "../../ui/button.js";
5
- import { keyOf as a, useContentScope as o, usePaged as s } from "../../react/use-resource.js";
4
+ import { keyOf as i, useContentScope as a, usePaged as o } from "../../react/use-resource.js";
5
+ import { Button as s } from "../../ui/button.js";
6
6
  import { useModerationQueue as c } from "../../react/comments.js";
7
7
  import { Badge as l } from "../../ui/badge.js";
8
8
  import { Input as u } from "../../ui/input.js";
@@ -66,10 +66,14 @@ function J({ sections: e, basePath: n, navigation: r, ...i }) {
66
66
  return /* @__PURE__ */ U(V, { children: [u, /* @__PURE__ */ U(N, {
67
67
  value: p,
68
68
  onValueChange: (e) => o.go(d(e)),
69
- children: [c.length > 1 && /* @__PURE__ */ H(F, { children: c.map((e) => /* @__PURE__ */ H(I, {
70
- value: e,
71
- children: l[e]
72
- }, e)) }), /* @__PURE__ */ U(P, {
69
+ children: [c.length > 1 && /* @__PURE__ */ H(F, {
70
+ className: "flex-wrap group-data-horizontal/tabs:h-auto",
71
+ children: c.map((e) => /* @__PURE__ */ H(I, {
72
+ value: e,
73
+ className: "h-7.5",
74
+ children: l[e]
75
+ }, e))
76
+ }), /* @__PURE__ */ U(P, {
73
77
  value: p,
74
78
  className: "pt-3",
75
79
  children: [
@@ -117,7 +121,7 @@ function X({ loc: c, view: _, client: S, ...C }) {
117
121
  ...e
118
122
  }), [F, I] = B(j);
119
123
  z(() => I(j), [j]);
120
- let { store: L, scope: R } = o(S), V = s(L, a("admin-comments-page", R, A, j, N), { type: "admin-comments" }, M, N, (e, t) => S.comments.adminList({
124
+ let { store: L, scope: R } = a(S), V = o(L, i("admin-comments-page", R, A, j, N), { type: "admin-comments" }, M, N, (e, t) => S.comments.adminList({
121
125
  ...e,
122
126
  contentKind: A || void 0,
123
127
  author: j || void 0
@@ -164,7 +168,7 @@ function X({ loc: c, view: _, client: S, ...C }) {
164
168
  placeholder: w("moderation.authorPlaceholder"),
165
169
  onChange: (e) => I(e.target.value)
166
170
  })]
167
- }), /* @__PURE__ */ H(i, {
171
+ }), /* @__PURE__ */ H(s, {
168
172
  type: "submit",
169
173
  size: "sm",
170
174
  variant: "outline",
@@ -175,7 +179,7 @@ function X({ loc: c, view: _, client: S, ...C }) {
175
179
  j && /* @__PURE__ */ U("div", {
176
180
  className: "flex flex-wrap items-center gap-2",
177
181
  "data-ckui": "author-filter",
178
- children: [/* @__PURE__ */ H("span", { children: w("moderation.byAuthor", { name: ne }) }), /* @__PURE__ */ H(i, {
182
+ children: [/* @__PURE__ */ H("span", { children: w("moderation.byAuthor", { name: ne }) }), /* @__PURE__ */ H(s, {
179
183
  size: "sm",
180
184
  variant: "ghost",
181
185
  onClick: () => P({ author: "" }),
@@ -253,24 +257,24 @@ function X({ loc: c, view: _, client: S, ...C }) {
253
257
  /* @__PURE__ */ U("div", {
254
258
  className: "flex flex-wrap gap-2",
255
259
  children: [
256
- e.deleted ? /* @__PURE__ */ H(i, {
260
+ e.deleted ? /* @__PURE__ */ H(s, {
257
261
  size: "sm",
258
262
  variant: "outline",
259
263
  onClick: () => Q("comment.restore", () => S.comments.restore(e.id)),
260
264
  children: w("moderation.restore")
261
- }) : /* @__PURE__ */ H(i, {
265
+ }) : /* @__PURE__ */ H(s, {
262
266
  size: "sm",
263
267
  variant: "destructive",
264
268
  onClick: () => Q("comment.delete", () => S.comments.delete(e.id)),
265
269
  children: w("moderation.delete")
266
270
  }),
267
- e.user_id && e.user_id !== j && /* @__PURE__ */ H(i, {
271
+ e.user_id && e.user_id !== j && /* @__PURE__ */ H(s, {
268
272
  size: "sm",
269
273
  variant: "ghost",
270
274
  onClick: () => P({ author: e.user_id }),
271
275
  children: w("moderation.theirComments")
272
276
  }),
273
- e.user_id && /* @__PURE__ */ H(i, {
277
+ e.user_id && /* @__PURE__ */ H(s, {
274
278
  size: "sm",
275
279
  variant: "ghost",
276
280
  onClick: () => Z({
@@ -307,8 +311,8 @@ function X({ loc: c, view: _, client: S, ...C }) {
307
311
  });
308
312
  }
309
313
  function Z({ loc: e, view: n, ...r }) {
310
- let { t: a } = t(), o = e.query.get("kind") === "content" ? "content" : "comment", s = c({
311
- kind: o,
314
+ let { t: i } = t(), a = e.query.get("kind") === "content" ? "content" : "comment", o = c({
315
+ kind: a,
312
316
  pageSize: r.pageSize,
313
317
  client: r.client
314
318
  });
@@ -318,51 +322,51 @@ function Z({ loc: e, view: n, ...r }) {
318
322
  /* @__PURE__ */ H("div", {
319
323
  className: "flex gap-2",
320
324
  role: "group",
321
- "aria-label": a("moderation.queue"),
322
- children: ["comment", "content"].map((t) => /* @__PURE__ */ H(i, {
325
+ "aria-label": i("moderation.queue"),
326
+ children: ["comment", "content"].map((t) => /* @__PURE__ */ H(s, {
323
327
  size: "sm",
324
- variant: o === t ? "default" : "outline",
325
- "aria-pressed": o === t,
328
+ variant: a === t ? "default" : "outline",
329
+ "aria-pressed": a === t,
326
330
  onClick: () => e.go(n, { kind: t === "comment" ? void 0 : t }),
327
- children: a(t === "content" ? "moderation.queueContent" : "moderation.queueComments")
331
+ children: i(t === "content" ? "moderation.queueContent" : "moderation.queueComments")
328
332
  }, t))
329
333
  }),
330
334
  /* @__PURE__ */ H($, {
331
- loading: s.loading,
332
- error: s.error,
333
- empty: a("moderation.queueEmpty"),
334
- count: s.items.length,
335
- reload: s.reload,
336
- children: s.items.map((e) => /* @__PURE__ */ H(Q, {
335
+ loading: o.loading,
336
+ error: o.error,
337
+ empty: i("moderation.queueEmpty"),
338
+ count: o.items.length,
339
+ reload: o.reload,
340
+ children: o.items.map((e) => /* @__PURE__ */ H(Q, {
337
341
  item: e,
338
342
  itemHref: r.itemHref,
339
343
  userHref: r.userHref,
340
- onResolve: s.resolve,
341
- reload: s.reload,
344
+ onResolve: o.resolve,
345
+ reload: o.reload,
342
346
  onError: r.onError
343
347
  }, e.id))
344
348
  }),
345
- s.hasMore && /* @__PURE__ */ H(i, {
349
+ o.hasMore && /* @__PURE__ */ H(s, {
346
350
  variant: "outline",
347
351
  className: "justify-self-center",
348
- disabled: s.loadingMore,
349
- onClick: () => void s.loadMore(),
350
- children: a("moderation.loadMore")
352
+ disabled: o.loadingMore,
353
+ onClick: () => void o.loadMore(),
354
+ children: i("moderation.loadMore")
351
355
  })
352
356
  ]
353
357
  });
354
358
  }
355
- function Q(a) {
356
- let o = t(), { t: s } = o, c = n(a.onError), l = r(), { item: d } = a, [f, p] = B(!1), [m, h] = B(""), [g, _] = B(!1), [C, w] = B(null), T = d.author?.username ?? d.anon_name ?? d.author_id ?? s("comments.anonymous"), E = a.itemHref?.({
359
+ function Q(i) {
360
+ let a = t(), { t: o } = a, c = n(i.onError), l = r(), { item: d } = i, [f, p] = B(!1), [m, h] = B(""), [g, _] = B(!1), [C, w] = B(null), T = d.author?.username ?? d.anon_name ?? d.author_id ?? o("comments.anonymous"), E = i.itemHref?.({
357
361
  kind: d.ref.content_kind,
358
362
  id: d.ref.content_id
359
363
  }), D = async (t) => {
360
364
  _(!0), w(null);
361
365
  try {
362
- await l(() => a.onResolve(d, t, t === "reject" && m.trim() || void 0));
366
+ await l(() => i.onResolve(d, t, t === "reject" && m.trim() || void 0));
363
367
  } catch (t) {
364
368
  let n = e(t);
365
- c(n, "moderation.resolve"), n.code === "not_found" || n.code === "conflict" ? (w(s("moderation.stale")), a.reload()) : w(S(o, n));
369
+ c(n, "moderation.resolve"), n.code === "not_found" || n.code === "conflict" ? (w(o("moderation.stale")), i.reload()) : w(S(a, n));
366
370
  } finally {
367
371
  _(!1);
368
372
  }
@@ -382,7 +386,7 @@ function Q(a) {
382
386
  /* @__PURE__ */ H(x, {
383
387
  user: d.author,
384
388
  name: T,
385
- href: a.userHref
389
+ href: i.userHref
386
390
  }),
387
391
  /* @__PURE__ */ H(y, {
388
392
  at: d.held_at,
@@ -392,7 +396,7 @@ function Q(a) {
392
396
  className: "text-xs text-muted-foreground hover:underline",
393
397
  href: E,
394
398
  children: [
395
- s("moderation.on"),
399
+ o("moderation.on"),
396
400
  " ",
397
401
  d.ref.content_kind,
398
402
  "/",
@@ -411,37 +415,37 @@ function Q(a) {
411
415
  }),
412
416
  (d.reason || d.error) && /* @__PURE__ */ H("p", {
413
417
  className: "text-xs text-muted-foreground",
414
- children: s("moderation.heldBecause", { reason: d.reason || d.error || "" })
418
+ children: o("moderation.heldBecause", { reason: d.reason || d.error || "" })
415
419
  }),
416
420
  f && /* @__PURE__ */ H(u, {
417
421
  value: m,
418
422
  maxLength: 500,
419
- placeholder: s("moderation.rejectReason"),
420
- "aria-label": s("moderation.rejectReason"),
423
+ placeholder: o("moderation.rejectReason"),
424
+ "aria-label": o("moderation.rejectReason"),
421
425
  onChange: (e) => h(e.target.value),
422
426
  autoFocus: !0
423
427
  }),
424
428
  /* @__PURE__ */ U("div", {
425
429
  className: "flex flex-wrap gap-2",
426
430
  children: [
427
- /* @__PURE__ */ H(i, {
431
+ /* @__PURE__ */ H(s, {
428
432
  size: "sm",
429
433
  disabled: g,
430
434
  onClick: () => void D("approve"),
431
- children: s("moderation.approve")
435
+ children: o("moderation.approve")
432
436
  }),
433
- /* @__PURE__ */ H(i, {
437
+ /* @__PURE__ */ H(s, {
434
438
  size: "sm",
435
439
  variant: "destructive",
436
440
  disabled: g,
437
441
  onClick: () => f ? void D("reject") : p(!0),
438
- children: s("moderation.reject")
442
+ children: o("moderation.reject")
439
443
  }),
440
- f && /* @__PURE__ */ H(i, {
444
+ f && /* @__PURE__ */ H(s, {
441
445
  size: "sm",
442
446
  variant: "ghost",
443
447
  onClick: () => p(!1),
444
- children: s("common.cancel")
448
+ children: o("common.cancel")
445
449
  })
446
450
  ]
447
451
  }),
@@ -457,7 +461,7 @@ function $(e) {
457
461
  children: [/* @__PURE__ */ H(_, { className: "h-16 w-full" }), /* @__PURE__ */ H(_, { className: "h-16 w-full" })]
458
462
  }) : e.error && !e.count ? /* @__PURE__ */ U("div", {
459
463
  className: "grid justify-items-start gap-2",
460
- children: [/* @__PURE__ */ H(v, { children: e.error.code === "forbidden" || e.error.code === "unauthorized" ? S(n, e.error) : r("moderation.loadFailed") }), /* @__PURE__ */ H(i, {
464
+ children: [/* @__PURE__ */ H(v, { children: e.error.code === "forbidden" || e.error.code === "unauthorized" ? S(n, e.error) : r("moderation.loadFailed") }), /* @__PURE__ */ H(s, {
461
465
  size: "sm",
462
466
  variant: "outline",
463
467
  onClick: e.reload,
@@ -1 +1 @@
1
- {"version":3,"file":"moderation.js","names":[],"sources":["../../../src/components/social/moderation.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useEffect, useId, useMemo, useState } from \"react\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { Decision, HeldKind } from \"../../client/content/types.js\";\nimport { toContentKitError, type ContentKitError } from \"../../client/errors.js\";\nimport type { AdminComment, HeldItem, PublicUser } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useAdminLocation, useFlowClient, useStaffAccess, type AdminLocation, type FlowClientOptions, type StaffAccess } from \"../../react/admin.js\";\nimport { useModerationQueue } from \"../../react/comments.js\";\nimport { useErrorReporter, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { keyOf, useContentScope, usePaged } from \"../../react/use-resource.js\";\nimport { Badge } from \"#ckui/ui/badge\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Input } from \"#ckui/ui/input\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"#ckui/ui/tabs\";\nimport { AdminRoot, NoAccess, Pager, Rows, pageOf, useGuarded, type AdminFlowProps, type AdminRouteProps } from \"../admin/shell.js\";\nimport { CommentBanDialog, CommentBans } from \"./bans.js\";\nimport { BannedWords, IPBans } from \"./rules.js\";\nimport { contentError, ErrorLine, RelativeTime, UserAvatar, UserName } from \"./parts.js\";\n\nexport type ModerationSection = \"comments\" | \"queue\" | \"bans\" | \"words\" | \"ips\";\n\nexport interface CommentModerationProps extends AdminFlowProps, AdminRouteProps {\n /** Content kinds offered as a filter of the comment list: codes, or codes with labels. */\n contentKinds?: readonly (string | { value: string; label: string })[];\n /** A link to a commented item. */\n itemHref?: (item: { kind: string; id: string }) => string | undefined;\n userHref?: (user: PublicUser) => string | undefined;\n /** The sections, in order; the first is at basePath, the others at basePath/{section}. Default all. */\n sections?: readonly ModerationSection[];\n pageSize?: number;\n}\n\nconst ALL_SECTIONS: readonly ModerationSection[] = [\"comments\", \"queue\", \"bans\", \"words\", \"ips\"];\n\n/** Which moderation sections the caller may use: every comment, the review queue, the site's bans, banned words and IP bans. */\nexport function useCommentModerationAccess(o: FlowClientOptions = {}): StaffAccess<ModerationSection> {\n return useStaffAccess(ALL_SECTIONS, o);\n}\n\n/**\n * Staff: every comment (deleted, held and rejected ones with their text) to\n * delete, restore or ban its author, by content kind or author; the review\n * queue of held comments and content to approve or reject; the site's\n * comment bans; and the rules every comment and answer meets (banned words,\n * IP bans). Sections, filters and pages live in the URL under basePath;\n * the caller sees only the sections its permissions allow.\n */\nexport function CommentModeration({ client: own, baseUrl, auth, guard, appearance, className, language, messages, sections = ALL_SECTIONS, ...p }: CommentModerationProps) {\n const client = useFlowClient({ client: own, baseUrl, auth });\n return (\n <AdminRoot flow=\"comment-moderation\" appearance={appearance} className={className} language={language} messages={messages} guard={guard}>\n <Moderation {...p} client={client} sections={sections} />\n </AdminRoot>\n );\n}\n\ntype Rest = Omit<CommentModerationProps, \"client\" | \"baseUrl\" | \"auth\" | \"guard\" | \"appearance\" | \"className\" | \"language\" | \"messages\" | \"sections\" | \"basePath\" | \"navigation\"> & {\n client: ContentKitClient;\n};\n\nfunction Moderation({ sections, basePath, navigation, ...p }: Rest & { sections: readonly ModerationSection[]; basePath?: string; navigation?: CommentModerationProps[\"navigation\"] }) {\n const { t } = useMessages();\n const loc = useAdminLocation(basePath, navigation);\n const access = useCommentModerationAccess({ client: p.client });\n const allowed = sections.filter((s) => access.allowed[s]);\n const label: Record<ModerationSection, string> = {\n comments: t(\"moderation.all\"),\n queue: t(\"moderation.queue\"),\n bans: t(\"moderation.bans\"),\n words: t(\"moderationRules.words\"),\n ips: t(\"moderationRules.ips\"),\n };\n const title = <h2 className=\"text-base font-semibold\">{t(\"moderation.title\")}</h2>;\n if (access.loading)\n return (\n <>\n {title}\n <Rows />\n </>\n );\n if (!allowed.length)\n return (\n <>\n {title}\n <NoAccess>{t(\"moderation.noAccess\")}</NoAccess>\n </>\n );\n const viewOf = (s: ModerationSection) => (s === sections[0] ? \"\" : s);\n const asked = (loc.view || sections[0]) as ModerationSection;\n const section = allowed.includes(asked) ? asked : allowed[0]!;\n const view = viewOf(section);\n return (\n <>\n {title}\n <Tabs value={section} onValueChange={(v) => loc.go(viewOf(v as ModerationSection))}>\n {allowed.length > 1 && (\n <TabsList>\n {allowed.map((s) => (\n <TabsTrigger key={s} value={s}>\n {label[s]}\n </TabsTrigger>\n ))}\n </TabsList>\n )}\n <TabsContent value={section} className=\"pt-3\">\n {section === \"comments\" && <AllComments {...p} loc={loc} view={view} />}\n {section === \"queue\" && <Queue {...p} loc={loc} view={view} />}\n {section === \"bans\" && (\n <CommentBans\n scope=\"global\"\n pageSize={p.pageSize}\n page={pageOf(loc.query)}\n onPageChange={(n) => loc.go(view, { page: n > 1 ? n : undefined })}\n userHref={p.userHref}\n client={p.client}\n onError={p.onError}\n />\n )}\n {section === \"words\" && <BannedWords client={p.client} onError={p.onError} />}\n {section === \"ips\" && <IPBans client={p.client} onError={p.onError} />}\n </TabsContent>\n </Tabs>\n </>\n );\n}\n\ntype SectionProps = Rest & { loc: AdminLocation; view: string };\n\nfunction status(c: AdminComment): \"deleted\" | \"held\" | \"rejected\" | \"published\" {\n if (c.deleted) return \"deleted\";\n return c.moderation === \"held\" ? \"held\" : c.moderation === \"rejected\" ? \"rejected\" : \"published\";\n}\n\nfunction AllComments({ loc, view, client, ...p }: SectionProps) {\n const m = useMessages();\n const { t } = m;\n const ids = useId();\n const kinds = useMemo(() => (p.contentKinds ?? []).map((k) => (typeof k === \"string\" ? { value: k, label: k } : k)), [p.contentKinds]);\n const kind = loc.query.get(\"kind\") ?? \"\";\n const author = loc.query.get(\"author\") ?? \"\";\n const page = pageOf(loc.query);\n const size = p.pageSize ?? 25;\n const filter = (next: { kind?: string; author?: string }) => loc.go(view, { kind, author, ...next });\n const [authorInput, setAuthorInput] = useState(author);\n useEffect(() => setAuthorInput(author), [author]);\n const { store, scope } = useContentScope(client);\n const list = usePaged(store, keyOf(\"admin-comments-page\", scope, kind, author, size), { type: \"admin-comments\" }, page, size, (q, signal) =>\n client.comments.adminList({ ...q, contentKind: kind || undefined, author: author || undefined }, signal),\n );\n const report = useErrorReporter(p.onError);\n const guarded = useGuarded();\n const [error, setError] = useState<ContentKitError | null>(null);\n const [banning, setBanning] = useState<{ id: string; name: string } | null>(null);\n const act = (op: \"comment.delete\" | \"comment.restore\", fn: () => Promise<unknown>) => {\n setError(null);\n guarded(fn).catch((e) => {\n const err = toContentKitError(e);\n setError(err);\n report(err, op);\n });\n };\n const byName = author ? (list.items.find((c) => c.user_id === author)?.author?.username ?? author) : \"\";\n return (\n <div className=\"grid gap-3\">\n <div className=\"flex flex-wrap items-end gap-2\">\n {kinds.length > 0 && (\n <Select value={kind} onValueChange={(v) => filter({ kind: String(v ?? \"\") })}>\n <SelectTrigger size=\"sm\" aria-label={t(\"moderation.kind\")}>\n <SelectValue>{(v: string) => (v ? (kinds.find((k) => k.value === v)?.label ?? v) : t(\"moderation.allKinds\"))}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"\">{t(\"moderation.allKinds\")}</SelectItem>\n {kinds.map((k) => (\n <SelectItem key={k.value} value={k.value}>\n {k.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n <form\n className=\"flex min-w-56 flex-1 items-end gap-2\"\n onSubmit={(e) => {\n e.preventDefault();\n filter({ author: authorInput.trim() });\n }}\n >\n <div className=\"grid flex-1 gap-1\">\n <Label htmlFor={`${ids}-author`} className=\"sr-only\">\n {t(\"moderation.author\")}\n </Label>\n <Input id={`${ids}-author`} className=\"h-8\" value={authorInput} placeholder={t(\"moderation.authorPlaceholder\")} onChange={(e) => setAuthorInput(e.target.value)} />\n </div>\n <Button type=\"submit\" size=\"sm\" variant=\"outline\">\n {t(\"moderation.filter\")}\n </Button>\n </form>\n </div>\n {author && (\n <div className=\"flex flex-wrap items-center gap-2\" data-ckui=\"author-filter\">\n <span>{t(\"moderation.byAuthor\", { name: byName })}</span>\n <Button size=\"sm\" variant=\"ghost\" onClick={() => filter({ author: \"\" })}>\n {t(\"moderation.clearAuthor\")}\n </Button>\n </div>\n )}\n <ErrorLine error={error} />\n <Listing loading={list.loading} error={list.error} empty={t(\"moderation.empty\")} count={list.items.length} reload={list.reload}>\n {list.items.map((c) => {\n const name = c.author?.username ?? c.anon_name ?? c.user_id ?? t(\"comments.anonymous\");\n const s = status(c);\n const href = p.itemHref?.({ kind: c.content_kind, id: c.content_id });\n return (\n <li key={c.id} className=\"grid gap-2 p-3\" data-ckui=\"admin-comment\" data-status={s}>\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1\">\n <UserAvatar user={c.author} name={name} size=\"sm\" />\n <UserName user={c.author} name={name} href={p.userHref} />\n <RelativeTime at={c.created_at} className=\"text-xs text-muted-foreground\" />\n <Badge variant={s === \"published\" ? \"outline\" : s === \"held\" ? \"secondary\" : \"destructive\"}>{t(`moderation.${s}`)}</Badge>\n {c.reply_to_id && <Badge variant=\"outline\">{t(\"moderation.reply\")}</Badge>}\n <span className=\"text-xs text-muted-foreground\">\n {t(\"moderation.on\")}{\" \"}\n {href ? (\n <a className=\"hover:underline\" href={href}>\n {c.content_kind}/{c.content_id}\n </a>\n ) : (\n `${c.content_kind}/${c.content_id}`\n )}\n </span>\n </div>\n <p className={cn(\"line-clamp-4 break-words whitespace-pre-wrap\", c.deleted && \"text-muted-foreground line-through\")}>{c.body}</p>\n {c.moderation_reason && <p className=\"text-xs text-muted-foreground\">{t(\"moderation.heldBecause\", { reason: c.moderation_reason })}</p>}\n <div className=\"flex flex-wrap gap-2\">\n {c.deleted ? (\n <Button size=\"sm\" variant=\"outline\" onClick={() => act(\"comment.restore\", () => client.comments.restore(c.id))}>\n {t(\"moderation.restore\")}\n </Button>\n ) : (\n <Button size=\"sm\" variant=\"destructive\" onClick={() => act(\"comment.delete\", () => client.comments.delete(c.id))}>\n {t(\"moderation.delete\")}\n </Button>\n )}\n {c.user_id && c.user_id !== author && (\n <Button size=\"sm\" variant=\"ghost\" onClick={() => filter({ author: c.user_id! })}>\n {t(\"moderation.theirComments\")}\n </Button>\n )}\n {c.user_id && (\n <Button size=\"sm\" variant=\"ghost\" onClick={() => setBanning({ id: c.user_id!, name })}>\n {t(\"comments.ban\")}\n </Button>\n )}\n </div>\n </li>\n );\n })}\n </Listing>\n <Pager page={page} hasNext={list.hasNext} disabled={list.loading} onPage={(n) => loc.go(view, { kind, author, page: n > 1 ? n : undefined })} />\n {banning && <CommentBanDialog open onOpenChange={(o) => !o && setBanning(null)} user={banning} scopes={[\"global\"]} client={client} onError={p.onError} />}\n </div>\n );\n}\n\nfunction Queue({ loc, view, ...p }: SectionProps) {\n const m = useMessages();\n const { t } = m;\n const kind: HeldKind = loc.query.get(\"kind\") === \"content\" ? \"content\" : \"comment\";\n const queue = useModerationQueue({ kind, pageSize: p.pageSize, client: p.client });\n return (\n <div className=\"grid gap-3\">\n <div className=\"flex gap-2\" role=\"group\" aria-label={t(\"moderation.queue\")}>\n {([\"comment\", \"content\"] as const).map((k) => (\n <Button key={k} size=\"sm\" variant={kind === k ? \"default\" : \"outline\"} aria-pressed={kind === k} onClick={() => loc.go(view, { kind: k === \"comment\" ? undefined : k })}>\n {t(k === \"content\" ? \"moderation.queueContent\" : \"moderation.queueComments\")}\n </Button>\n ))}\n </div>\n <Listing loading={queue.loading} error={queue.error} empty={t(\"moderation.queueEmpty\")} count={queue.items.length} reload={queue.reload}>\n {queue.items.map((it) => (\n <HeldRow key={it.id} item={it} itemHref={p.itemHref} userHref={p.userHref} onResolve={queue.resolve} reload={queue.reload} onError={p.onError} />\n ))}\n </Listing>\n {queue.hasMore && (\n <Button variant=\"outline\" className=\"justify-self-center\" disabled={queue.loadingMore} onClick={() => void queue.loadMore()}>\n {t(\"moderation.loadMore\")}\n </Button>\n )}\n </div>\n );\n}\n\nfunction HeldRow(p: {\n item: HeldItem;\n itemHref?: Rest[\"itemHref\"];\n userHref?: Rest[\"userHref\"];\n onResolve: (item: HeldItem, d: Decision, reason?: string) => Promise<void>;\n reload: () => void;\n onError?: ContentKitErrorHandler;\n}) {\n const m = useMessages();\n const { t } = m;\n const report = useErrorReporter(p.onError);\n const guarded = useGuarded();\n const { item: it } = p;\n const [rejecting, setRejecting] = useState(false);\n const [reason, setReason] = useState(\"\");\n const [pending, setPending] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const name = it.author?.username ?? it.anon_name ?? it.author_id ?? t(\"comments.anonymous\");\n const href = p.itemHref?.({ kind: it.ref.content_kind, id: it.ref.content_id });\n const resolve = async (d: Decision) => {\n setPending(true);\n setError(null);\n try {\n await guarded(() => p.onResolve(it, d, d === \"reject\" ? reason.trim() || undefined : undefined));\n } catch (e) {\n const err = toContentKitError(e);\n report(err, \"moderation.resolve\");\n // A stale revision: the text changed since it was listed.\n if (err.code === \"not_found\" || err.code === \"conflict\") {\n setError(t(\"moderation.stale\"));\n p.reload();\n } else setError(contentError(m, err));\n } finally {\n setPending(false);\n }\n };\n return (\n <li className=\"grid gap-2 p-3\" data-ckui=\"held-item\">\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1\">\n <UserAvatar user={it.author} name={name} size=\"sm\" />\n <UserName user={it.author} name={name} href={p.userHref} />\n <RelativeTime at={it.held_at} className=\"text-xs text-muted-foreground\" />\n {href && (\n <a className=\"text-xs text-muted-foreground hover:underline\" href={href}>\n {t(\"moderation.on\")} {it.ref.content_kind}/{it.ref.content_id}\n </a>\n )}\n </div>\n {it.title && <p className=\"font-medium\">{it.title}</p>}\n <p className=\"line-clamp-6 break-words whitespace-pre-wrap\">{it.body}</p>\n {(it.reason || it.error) && <p className=\"text-xs text-muted-foreground\">{t(\"moderation.heldBecause\", { reason: it.reason || it.error || \"\" })}</p>}\n {rejecting && (\n <Input value={reason} maxLength={500} placeholder={t(\"moderation.rejectReason\")} aria-label={t(\"moderation.rejectReason\")} onChange={(e) => setReason(e.target.value)} autoFocus />\n )}\n <div className=\"flex flex-wrap gap-2\">\n <Button size=\"sm\" disabled={pending} onClick={() => void resolve(\"approve\")}>\n {t(\"moderation.approve\")}\n </Button>\n <Button size=\"sm\" variant=\"destructive\" disabled={pending} onClick={() => (rejecting ? void resolve(\"reject\") : setRejecting(true))}>\n {t(\"moderation.reject\")}\n </Button>\n {rejecting && (\n <Button size=\"sm\" variant=\"ghost\" onClick={() => setRejecting(false)}>\n {t(\"common.cancel\")}\n </Button>\n )}\n </div>\n {error && <ErrorLine>{error}</ErrorLine>}\n </li>\n );\n}\n\nfunction Listing(p: { loading: boolean; error?: ContentKitError; empty: string; count: number; reload: () => void; children: React.ReactNode }) {\n const m = useMessages();\n const { t } = m;\n if (p.loading && !p.count) {\n return (\n <div className=\"grid gap-3\" aria-busy>\n <Skeleton className=\"h-16 w-full\" />\n <Skeleton className=\"h-16 w-full\" />\n </div>\n );\n }\n if (p.error && !p.count) {\n return (\n <div className=\"grid justify-items-start gap-2\">\n <ErrorLine>{p.error.code === \"forbidden\" || p.error.code === \"unauthorized\" ? contentError(m, p.error) : t(\"moderation.loadFailed\")}</ErrorLine>\n <Button size=\"sm\" variant=\"outline\" onClick={p.reload}>\n {t(\"common.retry\")}\n </Button>\n </div>\n );\n }\n if (!p.count) return <p className=\"text-muted-foreground\">{p.empty}</p>;\n return <ul className=\"grid divide-y divide-border rounded-lg border border-border\">{p.children}</ul>;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"moderation.js","names":[],"sources":["../../../src/components/social/moderation.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useEffect, useId, useMemo, useState } from \"react\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { Decision, HeldKind } from \"../../client/content/types.js\";\nimport { toContentKitError, type ContentKitError } from \"../../client/errors.js\";\nimport type { AdminComment, HeldItem, PublicUser } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useAdminLocation, useFlowClient, useStaffAccess, type AdminLocation, type FlowClientOptions, type StaffAccess } from \"../../react/admin.js\";\nimport { useModerationQueue } from \"../../react/comments.js\";\nimport { useErrorReporter, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { keyOf, useContentScope, usePaged } from \"../../react/use-resource.js\";\nimport { Badge } from \"#ckui/ui/badge\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Input } from \"#ckui/ui/input\";\nimport { Label } from \"#ckui/ui/label\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"#ckui/ui/tabs\";\nimport { AdminRoot, NoAccess, Pager, Rows, pageOf, useGuarded, type AdminFlowProps, type AdminRouteProps } from \"../admin/shell.js\";\nimport { CommentBanDialog, CommentBans } from \"./bans.js\";\nimport { BannedWords, IPBans } from \"./rules.js\";\nimport { contentError, ErrorLine, RelativeTime, UserAvatar, UserName } from \"./parts.js\";\n\nexport type ModerationSection = \"comments\" | \"queue\" | \"bans\" | \"words\" | \"ips\";\n\nexport interface CommentModerationProps extends AdminFlowProps, AdminRouteProps {\n /** Content kinds offered as a filter of the comment list: codes, or codes with labels. */\n contentKinds?: readonly (string | { value: string; label: string })[];\n /** A link to a commented item. */\n itemHref?: (item: { kind: string; id: string }) => string | undefined;\n userHref?: (user: PublicUser) => string | undefined;\n /** The sections, in order; the first is at basePath, the others at basePath/{section}. Default all. */\n sections?: readonly ModerationSection[];\n pageSize?: number;\n}\n\nconst ALL_SECTIONS: readonly ModerationSection[] = [\"comments\", \"queue\", \"bans\", \"words\", \"ips\"];\n\n/** Which moderation sections the caller may use: every comment, the review queue, the site's bans, banned words and IP bans. */\nexport function useCommentModerationAccess(o: FlowClientOptions = {}): StaffAccess<ModerationSection> {\n return useStaffAccess(ALL_SECTIONS, o);\n}\n\n/**\n * Staff: every comment (deleted, held and rejected ones with their text) to\n * delete, restore or ban its author, by content kind or author; the review\n * queue of held comments and content to approve or reject; the site's\n * comment bans; and the rules every comment and answer meets (banned words,\n * IP bans). Sections, filters and pages live in the URL under basePath;\n * the caller sees only the sections its permissions allow.\n */\nexport function CommentModeration({ client: own, baseUrl, auth, guard, appearance, className, language, messages, sections = ALL_SECTIONS, ...p }: CommentModerationProps) {\n const client = useFlowClient({ client: own, baseUrl, auth });\n return (\n <AdminRoot flow=\"comment-moderation\" appearance={appearance} className={className} language={language} messages={messages} guard={guard}>\n <Moderation {...p} client={client} sections={sections} />\n </AdminRoot>\n );\n}\n\ntype Rest = Omit<CommentModerationProps, \"client\" | \"baseUrl\" | \"auth\" | \"guard\" | \"appearance\" | \"className\" | \"language\" | \"messages\" | \"sections\" | \"basePath\" | \"navigation\"> & {\n client: ContentKitClient;\n};\n\nfunction Moderation({ sections, basePath, navigation, ...p }: Rest & { sections: readonly ModerationSection[]; basePath?: string; navigation?: CommentModerationProps[\"navigation\"] }) {\n const { t } = useMessages();\n const loc = useAdminLocation(basePath, navigation);\n const access = useCommentModerationAccess({ client: p.client });\n const allowed = sections.filter((s) => access.allowed[s]);\n const label: Record<ModerationSection, string> = {\n comments: t(\"moderation.all\"),\n queue: t(\"moderation.queue\"),\n bans: t(\"moderation.bans\"),\n words: t(\"moderationRules.words\"),\n ips: t(\"moderationRules.ips\"),\n };\n const title = <h2 className=\"text-base font-semibold\">{t(\"moderation.title\")}</h2>;\n if (access.loading)\n return (\n <>\n {title}\n <Rows />\n </>\n );\n if (!allowed.length)\n return (\n <>\n {title}\n <NoAccess>{t(\"moderation.noAccess\")}</NoAccess>\n </>\n );\n const viewOf = (s: ModerationSection) => (s === sections[0] ? \"\" : s);\n const asked = (loc.view || sections[0]) as ModerationSection;\n const section = allowed.includes(asked) ? asked : allowed[0]!;\n const view = viewOf(section);\n return (\n <>\n {title}\n <Tabs value={section} onValueChange={(v) => loc.go(viewOf(v as ModerationSection))}>\n {allowed.length > 1 && (\n <TabsList className=\"flex-wrap group-data-horizontal/tabs:h-auto\">\n {allowed.map((s) => (\n <TabsTrigger key={s} value={s} className=\"h-7.5\">\n {label[s]}\n </TabsTrigger>\n ))}\n </TabsList>\n )}\n <TabsContent value={section} className=\"pt-3\">\n {section === \"comments\" && <AllComments {...p} loc={loc} view={view} />}\n {section === \"queue\" && <Queue {...p} loc={loc} view={view} />}\n {section === \"bans\" && (\n <CommentBans\n scope=\"global\"\n pageSize={p.pageSize}\n page={pageOf(loc.query)}\n onPageChange={(n) => loc.go(view, { page: n > 1 ? n : undefined })}\n userHref={p.userHref}\n client={p.client}\n onError={p.onError}\n />\n )}\n {section === \"words\" && <BannedWords client={p.client} onError={p.onError} />}\n {section === \"ips\" && <IPBans client={p.client} onError={p.onError} />}\n </TabsContent>\n </Tabs>\n </>\n );\n}\n\ntype SectionProps = Rest & { loc: AdminLocation; view: string };\n\nfunction status(c: AdminComment): \"deleted\" | \"held\" | \"rejected\" | \"published\" {\n if (c.deleted) return \"deleted\";\n return c.moderation === \"held\" ? \"held\" : c.moderation === \"rejected\" ? \"rejected\" : \"published\";\n}\n\nfunction AllComments({ loc, view, client, ...p }: SectionProps) {\n const m = useMessages();\n const { t } = m;\n const ids = useId();\n const kinds = useMemo(() => (p.contentKinds ?? []).map((k) => (typeof k === \"string\" ? { value: k, label: k } : k)), [p.contentKinds]);\n const kind = loc.query.get(\"kind\") ?? \"\";\n const author = loc.query.get(\"author\") ?? \"\";\n const page = pageOf(loc.query);\n const size = p.pageSize ?? 25;\n const filter = (next: { kind?: string; author?: string }) => loc.go(view, { kind, author, ...next });\n const [authorInput, setAuthorInput] = useState(author);\n useEffect(() => setAuthorInput(author), [author]);\n const { store, scope } = useContentScope(client);\n const list = usePaged(store, keyOf(\"admin-comments-page\", scope, kind, author, size), { type: \"admin-comments\" }, page, size, (q, signal) =>\n client.comments.adminList({ ...q, contentKind: kind || undefined, author: author || undefined }, signal),\n );\n const report = useErrorReporter(p.onError);\n const guarded = useGuarded();\n const [error, setError] = useState<ContentKitError | null>(null);\n const [banning, setBanning] = useState<{ id: string; name: string } | null>(null);\n const act = (op: \"comment.delete\" | \"comment.restore\", fn: () => Promise<unknown>) => {\n setError(null);\n guarded(fn).catch((e) => {\n const err = toContentKitError(e);\n setError(err);\n report(err, op);\n });\n };\n const byName = author ? (list.items.find((c) => c.user_id === author)?.author?.username ?? author) : \"\";\n return (\n <div className=\"grid gap-3\">\n <div className=\"flex flex-wrap items-end gap-2\">\n {kinds.length > 0 && (\n <Select value={kind} onValueChange={(v) => filter({ kind: String(v ?? \"\") })}>\n <SelectTrigger size=\"sm\" aria-label={t(\"moderation.kind\")}>\n <SelectValue>{(v: string) => (v ? (kinds.find((k) => k.value === v)?.label ?? v) : t(\"moderation.allKinds\"))}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"\">{t(\"moderation.allKinds\")}</SelectItem>\n {kinds.map((k) => (\n <SelectItem key={k.value} value={k.value}>\n {k.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n <form\n className=\"flex min-w-56 flex-1 items-end gap-2\"\n onSubmit={(e) => {\n e.preventDefault();\n filter({ author: authorInput.trim() });\n }}\n >\n <div className=\"grid flex-1 gap-1\">\n <Label htmlFor={`${ids}-author`} className=\"sr-only\">\n {t(\"moderation.author\")}\n </Label>\n <Input id={`${ids}-author`} className=\"h-8\" value={authorInput} placeholder={t(\"moderation.authorPlaceholder\")} onChange={(e) => setAuthorInput(e.target.value)} />\n </div>\n <Button type=\"submit\" size=\"sm\" variant=\"outline\">\n {t(\"moderation.filter\")}\n </Button>\n </form>\n </div>\n {author && (\n <div className=\"flex flex-wrap items-center gap-2\" data-ckui=\"author-filter\">\n <span>{t(\"moderation.byAuthor\", { name: byName })}</span>\n <Button size=\"sm\" variant=\"ghost\" onClick={() => filter({ author: \"\" })}>\n {t(\"moderation.clearAuthor\")}\n </Button>\n </div>\n )}\n <ErrorLine error={error} />\n <Listing loading={list.loading} error={list.error} empty={t(\"moderation.empty\")} count={list.items.length} reload={list.reload}>\n {list.items.map((c) => {\n const name = c.author?.username ?? c.anon_name ?? c.user_id ?? t(\"comments.anonymous\");\n const s = status(c);\n const href = p.itemHref?.({ kind: c.content_kind, id: c.content_id });\n return (\n <li key={c.id} className=\"grid gap-2 p-3\" data-ckui=\"admin-comment\" data-status={s}>\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1\">\n <UserAvatar user={c.author} name={name} size=\"sm\" />\n <UserName user={c.author} name={name} href={p.userHref} />\n <RelativeTime at={c.created_at} className=\"text-xs text-muted-foreground\" />\n <Badge variant={s === \"published\" ? \"outline\" : s === \"held\" ? \"secondary\" : \"destructive\"}>{t(`moderation.${s}`)}</Badge>\n {c.reply_to_id && <Badge variant=\"outline\">{t(\"moderation.reply\")}</Badge>}\n <span className=\"text-xs text-muted-foreground\">\n {t(\"moderation.on\")}{\" \"}\n {href ? (\n <a className=\"hover:underline\" href={href}>\n {c.content_kind}/{c.content_id}\n </a>\n ) : (\n `${c.content_kind}/${c.content_id}`\n )}\n </span>\n </div>\n <p className={cn(\"line-clamp-4 break-words whitespace-pre-wrap\", c.deleted && \"text-muted-foreground line-through\")}>{c.body}</p>\n {c.moderation_reason && <p className=\"text-xs text-muted-foreground\">{t(\"moderation.heldBecause\", { reason: c.moderation_reason })}</p>}\n <div className=\"flex flex-wrap gap-2\">\n {c.deleted ? (\n <Button size=\"sm\" variant=\"outline\" onClick={() => act(\"comment.restore\", () => client.comments.restore(c.id))}>\n {t(\"moderation.restore\")}\n </Button>\n ) : (\n <Button size=\"sm\" variant=\"destructive\" onClick={() => act(\"comment.delete\", () => client.comments.delete(c.id))}>\n {t(\"moderation.delete\")}\n </Button>\n )}\n {c.user_id && c.user_id !== author && (\n <Button size=\"sm\" variant=\"ghost\" onClick={() => filter({ author: c.user_id! })}>\n {t(\"moderation.theirComments\")}\n </Button>\n )}\n {c.user_id && (\n <Button size=\"sm\" variant=\"ghost\" onClick={() => setBanning({ id: c.user_id!, name })}>\n {t(\"comments.ban\")}\n </Button>\n )}\n </div>\n </li>\n );\n })}\n </Listing>\n <Pager page={page} hasNext={list.hasNext} disabled={list.loading} onPage={(n) => loc.go(view, { kind, author, page: n > 1 ? n : undefined })} />\n {banning && <CommentBanDialog open onOpenChange={(o) => !o && setBanning(null)} user={banning} scopes={[\"global\"]} client={client} onError={p.onError} />}\n </div>\n );\n}\n\nfunction Queue({ loc, view, ...p }: SectionProps) {\n const m = useMessages();\n const { t } = m;\n const kind: HeldKind = loc.query.get(\"kind\") === \"content\" ? \"content\" : \"comment\";\n const queue = useModerationQueue({ kind, pageSize: p.pageSize, client: p.client });\n return (\n <div className=\"grid gap-3\">\n <div className=\"flex gap-2\" role=\"group\" aria-label={t(\"moderation.queue\")}>\n {([\"comment\", \"content\"] as const).map((k) => (\n <Button key={k} size=\"sm\" variant={kind === k ? \"default\" : \"outline\"} aria-pressed={kind === k} onClick={() => loc.go(view, { kind: k === \"comment\" ? undefined : k })}>\n {t(k === \"content\" ? \"moderation.queueContent\" : \"moderation.queueComments\")}\n </Button>\n ))}\n </div>\n <Listing loading={queue.loading} error={queue.error} empty={t(\"moderation.queueEmpty\")} count={queue.items.length} reload={queue.reload}>\n {queue.items.map((it) => (\n <HeldRow key={it.id} item={it} itemHref={p.itemHref} userHref={p.userHref} onResolve={queue.resolve} reload={queue.reload} onError={p.onError} />\n ))}\n </Listing>\n {queue.hasMore && (\n <Button variant=\"outline\" className=\"justify-self-center\" disabled={queue.loadingMore} onClick={() => void queue.loadMore()}>\n {t(\"moderation.loadMore\")}\n </Button>\n )}\n </div>\n );\n}\n\nfunction HeldRow(p: {\n item: HeldItem;\n itemHref?: Rest[\"itemHref\"];\n userHref?: Rest[\"userHref\"];\n onResolve: (item: HeldItem, d: Decision, reason?: string) => Promise<void>;\n reload: () => void;\n onError?: ContentKitErrorHandler;\n}) {\n const m = useMessages();\n const { t } = m;\n const report = useErrorReporter(p.onError);\n const guarded = useGuarded();\n const { item: it } = p;\n const [rejecting, setRejecting] = useState(false);\n const [reason, setReason] = useState(\"\");\n const [pending, setPending] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const name = it.author?.username ?? it.anon_name ?? it.author_id ?? t(\"comments.anonymous\");\n const href = p.itemHref?.({ kind: it.ref.content_kind, id: it.ref.content_id });\n const resolve = async (d: Decision) => {\n setPending(true);\n setError(null);\n try {\n await guarded(() => p.onResolve(it, d, d === \"reject\" ? reason.trim() || undefined : undefined));\n } catch (e) {\n const err = toContentKitError(e);\n report(err, \"moderation.resolve\");\n // A stale revision: the text changed since it was listed.\n if (err.code === \"not_found\" || err.code === \"conflict\") {\n setError(t(\"moderation.stale\"));\n p.reload();\n } else setError(contentError(m, err));\n } finally {\n setPending(false);\n }\n };\n return (\n <li className=\"grid gap-2 p-3\" data-ckui=\"held-item\">\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1\">\n <UserAvatar user={it.author} name={name} size=\"sm\" />\n <UserName user={it.author} name={name} href={p.userHref} />\n <RelativeTime at={it.held_at} className=\"text-xs text-muted-foreground\" />\n {href && (\n <a className=\"text-xs text-muted-foreground hover:underline\" href={href}>\n {t(\"moderation.on\")} {it.ref.content_kind}/{it.ref.content_id}\n </a>\n )}\n </div>\n {it.title && <p className=\"font-medium\">{it.title}</p>}\n <p className=\"line-clamp-6 break-words whitespace-pre-wrap\">{it.body}</p>\n {(it.reason || it.error) && <p className=\"text-xs text-muted-foreground\">{t(\"moderation.heldBecause\", { reason: it.reason || it.error || \"\" })}</p>}\n {rejecting && (\n <Input value={reason} maxLength={500} placeholder={t(\"moderation.rejectReason\")} aria-label={t(\"moderation.rejectReason\")} onChange={(e) => setReason(e.target.value)} autoFocus />\n )}\n <div className=\"flex flex-wrap gap-2\">\n <Button size=\"sm\" disabled={pending} onClick={() => void resolve(\"approve\")}>\n {t(\"moderation.approve\")}\n </Button>\n <Button size=\"sm\" variant=\"destructive\" disabled={pending} onClick={() => (rejecting ? void resolve(\"reject\") : setRejecting(true))}>\n {t(\"moderation.reject\")}\n </Button>\n {rejecting && (\n <Button size=\"sm\" variant=\"ghost\" onClick={() => setRejecting(false)}>\n {t(\"common.cancel\")}\n </Button>\n )}\n </div>\n {error && <ErrorLine>{error}</ErrorLine>}\n </li>\n );\n}\n\nfunction Listing(p: { loading: boolean; error?: ContentKitError; empty: string; count: number; reload: () => void; children: React.ReactNode }) {\n const m = useMessages();\n const { t } = m;\n if (p.loading && !p.count) {\n return (\n <div className=\"grid gap-3\" aria-busy>\n <Skeleton className=\"h-16 w-full\" />\n <Skeleton className=\"h-16 w-full\" />\n </div>\n );\n }\n if (p.error && !p.count) {\n return (\n <div className=\"grid justify-items-start gap-2\">\n <ErrorLine>{p.error.code === \"forbidden\" || p.error.code === \"unauthorized\" ? contentError(m, p.error) : t(\"moderation.loadFailed\")}</ErrorLine>\n <Button size=\"sm\" variant=\"outline\" onClick={p.reload}>\n {t(\"common.retry\")}\n </Button>\n </div>\n );\n }\n if (!p.count) return <p className=\"text-muted-foreground\">{p.empty}</p>;\n return <ul className=\"grid divide-y divide-border rounded-lg border border-border\">{p.children}</ul>;\n}\n"],"mappings":"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"}
@@ -125,6 +125,8 @@ export interface VideoPlayerProps extends Omit<HlsPlayerOptions, "src"> {
125
125
  style?: CSSProperties;
126
126
  appearance?: ContentKitUiAppearance;
127
127
  }
128
+ /** A track's name: its playlist name, or its language's name when the name is only the language code. */
129
+ export declare function trackLabel(tr: Pick<PlayerTrack, "label" | "lang">, languageName: (code: string) => string): string;
128
130
  /**
129
131
  * Whether a shortcut key belongs to the player: never while typing or in a
130
132
  * menu, not the arrows on a slider (it steps itself), and outside the