@openrails/contentkit-ui 0.85.1 → 0.86.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -4
- package/dist/client/beacons.d.ts +13 -4
- package/dist/client/beacons.js +14 -7
- package/dist/client/beacons.js.map +1 -1
- package/dist/client/client.d.ts +3 -0
- package/dist/client/client.js +29 -27
- package/dist/client/client.js.map +1 -1
- package/dist/client/codes.d.ts +6 -1
- package/dist/client/codes.js +4 -1
- package/dist/client/codes.js.map +1 -1
- package/dist/client/generated/error-codes.js +1 -1
- package/dist/client/generated/error-codes.js.map +1 -1
- package/dist/client/generated/routes.d.ts +14 -1
- package/dist/client/generated/routes.js +17 -1
- package/dist/client/generated/routes.js.map +1 -1
- package/dist/client/generated/wire.d.ts +29 -0
- package/dist/client/generated/wire.js.map +1 -1
- package/dist/client/index.d.ts +2 -1
- package/dist/client/media/queue.d.ts +4 -2
- package/dist/client/media/queue.js +18 -12
- package/dist/client/media/queue.js.map +1 -1
- package/dist/client/media/rules.d.ts +7 -1
- package/dist/client/media/rules.js +27 -21
- package/dist/client/media/rules.js.map +1 -1
- package/dist/client/stats.d.ts +32 -0
- package/dist/client/stats.js +35 -0
- package/dist/client/stats.js.map +1 -0
- package/dist/client.js +30 -29
- package/dist/components/admin/content-manager.js +206 -206
- package/dist/components/admin/content-manager.js.map +1 -1
- package/dist/components/admin/group-manager.js +71 -70
- package/dist/components/admin/group-manager.js.map +1 -1
- package/dist/components/admin/media-item-panel.d.ts +24 -7
- package/dist/components/admin/media-item-panel.js +209 -197
- package/dist/components/admin/media-item-panel.js.map +1 -1
- package/dist/components/admin/poll-manager.js +25 -25
- package/dist/components/admin/stats-dashboard.d.ts +22 -0
- package/dist/components/admin/stats-dashboard.js +257 -0
- package/dist/components/admin/stats-dashboard.js.map +1 -0
- package/dist/components/content/card.js +42 -42
- package/dist/components/content/card.js.map +1 -1
- package/dist/components/content/editors.js +455 -427
- package/dist/components/content/editors.js.map +1 -1
- package/dist/components/content/grid.js +47 -47
- package/dist/components/content/index.d.ts +2 -1
- package/dist/components/content/listing.js +34 -34
- package/dist/components/content/listing.js.map +1 -1
- package/dist/components/content/paywall.d.ts +17 -5
- package/dist/components/content/paywall.js +115 -100
- package/dist/components/content/paywall.js.map +1 -1
- package/dist/components/content/reader-stats.d.ts +12 -0
- package/dist/components/content/reader-stats.js +47 -0
- package/dist/components/content/reader-stats.js.map +1 -0
- package/dist/components/groups/banner.js +33 -33
- package/dist/components/groups/directory.js +11 -11
- package/dist/components/groups/editor.js +293 -293
- package/dist/components/groups/editor.js.map +1 -1
- package/dist/components/groups/header.js +27 -27
- package/dist/components/groups/header.js.map +1 -1
- package/dist/components/groups/memberships.js +180 -180
- package/dist/components/groups/parts.d.ts +0 -2
- package/dist/components/groups/parts.js +9 -27
- package/dist/components/groups/parts.js.map +1 -1
- package/dist/components/media-folder-editor.js +239 -189
- package/dist/components/media-folder-editor.js.map +1 -1
- package/dist/components/notifications.js +17 -17
- package/dist/components/slot-upload.d.ts +2 -2
- package/dist/components/slot-upload.js +84 -79
- package/dist/components/slot-upload.js.map +1 -1
- package/dist/components/social/bans.js +34 -34
- package/dist/components/social/moderation.js +52 -48
- package/dist/components/social/moderation.js.map +1 -1
- package/dist/components/video-player.d.ts +2 -0
- package/dist/components/video-player.js +318 -314
- package/dist/components/video-player.js.map +1 -1
- package/dist/i18n/context.d.ts +2 -0
- package/dist/i18n/context.js +23 -5
- package/dist/i18n/context.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +12 -10
- package/dist/locales/admin/de.js +27 -0
- package/dist/locales/admin/de.js.map +1 -1
- package/dist/locales/admin/en.d.ts +27 -0
- package/dist/locales/admin/en.js +27 -0
- package/dist/locales/admin/en.js.map +1 -1
- package/dist/locales/admin/es.js +27 -0
- package/dist/locales/admin/es.js.map +1 -1
- package/dist/locales/admin/ja.js +27 -0
- package/dist/locales/admin/ja.js.map +1 -1
- package/dist/locales/admin/ko.js +27 -0
- package/dist/locales/admin/ko.js.map +1 -1
- package/dist/locales/admin/zh.js +27 -0
- package/dist/locales/admin/zh.js.map +1 -1
- package/dist/locales/content/de.js +6 -0
- package/dist/locales/content/de.js.map +1 -1
- package/dist/locales/content/en.d.ts +6 -0
- package/dist/locales/content/en.js +6 -0
- package/dist/locales/content/en.js.map +1 -1
- package/dist/locales/content/es.js +6 -0
- package/dist/locales/content/es.js.map +1 -1
- package/dist/locales/content/ja.js +6 -0
- package/dist/locales/content/ja.js.map +1 -1
- package/dist/locales/content/ko.js +6 -0
- package/dist/locales/content/ko.js.map +1 -1
- package/dist/locales/content/zh.js +6 -0
- package/dist/locales/content/zh.js.map +1 -1
- package/dist/locales/de.js +1 -0
- package/dist/locales/de.js.map +1 -1
- package/dist/locales/en.d.ts +34 -0
- package/dist/locales/en.js +1 -0
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es.js +1 -0
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/ja.js +1 -0
- package/dist/locales/ja.js.map +1 -1
- package/dist/locales/ko.js +1 -0
- package/dist/locales/ko.js.map +1 -1
- package/dist/locales/zh.js +1 -0
- package/dist/locales/zh.js.map +1 -1
- package/dist/react/admin.js +22 -23
- package/dist/react/admin.js.map +1 -1
- package/dist/react/canonical.d.ts +4 -3
- package/dist/react/canonical.js +34 -33
- package/dist/react/canonical.js.map +1 -1
- package/dist/react/contents.js +144 -144
- package/dist/react/contents.js.map +1 -1
- package/dist/react/detail.d.ts +12 -6
- package/dist/react/detail.js +31 -24
- package/dist/react/detail.js.map +1 -1
- package/dist/react/folder.d.ts +7 -2
- package/dist/react/folder.js +165 -147
- package/dist/react/folder.js.map +1 -1
- package/dist/react/groups.d.ts +0 -1
- package/dist/react/groups.js +13 -12
- package/dist/react/groups.js.map +1 -1
- package/dist/react/index.d.ts +4 -2
- package/dist/react/location.d.ts +11 -0
- package/dist/react/location.js +18 -0
- package/dist/react/location.js.map +1 -0
- package/dist/react/public.d.ts +8 -0
- package/dist/react/public.js +32 -27
- package/dist/react/public.js.map +1 -1
- package/dist/react/stats.d.ts +24 -0
- package/dist/react/stats.js +28 -0
- package/dist/react/stats.js.map +1 -0
- package/dist/react/use-resource.js +8 -7
- package/dist/react/use-resource.js.map +1 -1
- package/dist/react/versions.js +20 -20
- package/dist/react.js +26 -24
- package/dist/styles.css +1 -1
- package/dist/urls/index.d.ts +35 -10
- package/dist/urls.js +100 -70
- package/dist/urls.js.map +1 -1
- 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 {
|
|
5
|
-
import {
|
|
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, {
|
|
70
|
-
|
|
71
|
-
children:
|
|
72
|
-
|
|
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 } =
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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:
|
|
311
|
-
kind:
|
|
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":
|
|
322
|
-
children: ["comment", "content"].map((t) => /* @__PURE__ */ H(
|
|
325
|
+
"aria-label": i("moderation.queue"),
|
|
326
|
+
children: ["comment", "content"].map((t) => /* @__PURE__ */ H(s, {
|
|
323
327
|
size: "sm",
|
|
324
|
-
variant:
|
|
325
|
-
"aria-pressed":
|
|
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:
|
|
331
|
+
children: i(t === "content" ? "moderation.queueContent" : "moderation.queueComments")
|
|
328
332
|
}, t))
|
|
329
333
|
}),
|
|
330
334
|
/* @__PURE__ */ H($, {
|
|
331
|
-
loading:
|
|
332
|
-
error:
|
|
333
|
-
empty:
|
|
334
|
-
count:
|
|
335
|
-
reload:
|
|
336
|
-
children:
|
|
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:
|
|
341
|
-
reload:
|
|
344
|
+
onResolve: o.resolve,
|
|
345
|
+
reload: o.reload,
|
|
342
346
|
onError: r.onError
|
|
343
347
|
}, e.id))
|
|
344
348
|
}),
|
|
345
|
-
|
|
349
|
+
o.hasMore && /* @__PURE__ */ H(s, {
|
|
346
350
|
variant: "outline",
|
|
347
351
|
className: "justify-self-center",
|
|
348
|
-
disabled:
|
|
349
|
-
onClick: () => void
|
|
350
|
-
children:
|
|
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(
|
|
356
|
-
let
|
|
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(() =>
|
|
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(
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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:
|
|
420
|
-
"aria-label":
|
|
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(
|
|
431
|
+
/* @__PURE__ */ H(s, {
|
|
428
432
|
size: "sm",
|
|
429
433
|
disabled: g,
|
|
430
434
|
onClick: () => void D("approve"),
|
|
431
|
-
children:
|
|
435
|
+
children: o("moderation.approve")
|
|
432
436
|
}),
|
|
433
|
-
/* @__PURE__ */ H(
|
|
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:
|
|
442
|
+
children: o("moderation.reject")
|
|
439
443
|
}),
|
|
440
|
-
f && /* @__PURE__ */ H(
|
|
444
|
+
f && /* @__PURE__ */ H(s, {
|
|
441
445
|
size: "sm",
|
|
442
446
|
variant: "ghost",
|
|
443
447
|
onClick: () => p(!1),
|
|
444
|
-
children:
|
|
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(
|
|
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
|