@openrails/contentkit-ui 0.68.0 → 0.69.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 +55 -10
- package/dist/client/client.d.ts +4 -2
- package/dist/client/client.js +1 -0
- package/dist/client/client.js.map +1 -1
- package/dist/client/content/bans.js +7 -7
- package/dist/client/content/bans.js.map +1 -1
- package/dist/client/content/comments.js +16 -14
- package/dist/client/content/comments.js.map +1 -1
- package/dist/client/content/favorites.js +5 -5
- package/dist/client/content/favorites.js.map +1 -1
- package/dist/client/content/moderation.js +5 -5
- package/dist/client/content/moderation.js.map +1 -1
- package/dist/client/content/polls.js +34 -31
- package/dist/client/content/polls.js.map +1 -1
- package/dist/client/content/posts.js +21 -20
- package/dist/client/content/posts.js.map +1 -1
- package/dist/client/content/reactions.js +4 -4
- package/dist/client/content/reactions.js.map +1 -1
- package/dist/client/generated/error-codes.d.ts +1 -1
- package/dist/client/generated/error-codes.js +4 -0
- package/dist/client/generated/error-codes.js.map +1 -1
- package/dist/client/generated/routes.js.map +1 -1
- package/dist/client/http.d.ts +6 -2
- package/dist/client/http.js +11 -3
- package/dist/client/http.js.map +1 -1
- package/dist/client/media/client.js +24 -24
- package/dist/client/media/client.js.map +1 -1
- package/dist/client/media/rules.d.ts +2 -0
- package/dist/client/media/rules.js +6 -2
- package/dist/client/media/rules.js.map +1 -1
- package/dist/client/taxonomy.js +2 -2
- package/dist/client/taxonomy.js.map +1 -1
- package/dist/components/media-folder-editor.d.ts +17 -3
- package/dist/components/media-folder-editor.js +146 -99
- package/dist/components/media-folder-editor.js.map +1 -1
- package/dist/components/slot-upload.js +102 -100
- package/dist/components/slot-upload.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 +1 -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/canonical.d.ts +2 -2
- package/dist/react/canonical.js +2 -12
- package/dist/react/canonical.js.map +1 -1
- package/dist/react/comments.js +35 -27
- package/dist/react/comments.js.map +1 -1
- package/dist/react/context.d.ts +7 -0
- package/dist/react/context.js +20 -15
- package/dist/react/context.js.map +1 -1
- package/dist/react/engagement.js +2 -2
- package/dist/react/engagement.js.map +1 -1
- package/dist/react/folder.d.ts +29 -9
- package/dist/react/folder.js +170 -113
- package/dist/react/folder.js.map +1 -1
- package/dist/react/polls.js +3 -3
- package/dist/react/polls.js.map +1 -1
- package/dist/react/posts.js +2 -2
- package/dist/react/posts.js.map +1 -1
- package/dist/react/provider.d.ts +7 -3
- package/dist/react/provider.js +34 -20
- package/dist/react/provider.js.map +1 -1
- package/dist/react/public.js +11 -11
- package/dist/react/public.js.map +1 -1
- package/dist/react/read.js +93 -92
- package/dist/react/read.js.map +1 -1
- package/dist/react/resources.d.ts +6 -3
- package/dist/react/resources.js +40 -40
- package/dist/react/resources.js.map +1 -1
- package/dist/react/slot.d.ts +4 -2
- package/dist/react/slot.js +96 -92
- package/dist/react/slot.js.map +1 -1
- package/dist/react/store.d.ts +1 -1
- package/dist/react/store.js +21 -20
- package/dist/react/store.js.map +1 -1
- package/dist/react/upload.d.ts +3 -2
- package/dist/react/upload.js +20 -15
- package/dist/react/upload.js.map +1 -1
- package/dist/react/use-resource.d.ts +1 -0
- package/dist/react/use-resource.js +38 -30
- package/dist/react/use-resource.js.map +1 -1
- package/dist/react.js +15 -15
- package/package.json +7 -5
package/README.md
CHANGED
|
@@ -41,6 +41,8 @@ const urls = createContentURLs({ routes: { video: "watch", gallery: "g" }, langu
|
|
|
41
41
|
<ContentKitProvider
|
|
42
42
|
client={contentkit}
|
|
43
43
|
viewer={user?.id ?? null} // the signed-in user as ContentKit sees it
|
|
44
|
+
language={lang} // same value as the client's language callback
|
|
45
|
+
accessRevision={accessRevision} // increment when permissions/entitlements change
|
|
44
46
|
onSignIn={() => openSignIn()} // asks signed-out visitors to sign in where the server refuses them
|
|
45
47
|
urls={urls}
|
|
46
48
|
navigate={(to, o) => navigate(to, o)}
|
|
@@ -53,6 +55,22 @@ const urls = createContentURLs({ routes: { video: "watch", gallery: "g" }, langu
|
|
|
53
55
|
</ContentKitProvider>;
|
|
54
56
|
```
|
|
55
57
|
|
|
58
|
+
Keep `viewer`, `language` and `accessRevision` current on the provider. A change
|
|
59
|
+
starts fresh media and social reads without remounting the children. Old requests
|
|
60
|
+
are abandoned when their last reader leaves; their results cannot fill the new
|
|
61
|
+
scope. `accessRevision` is optional when the host has no changing access state.
|
|
62
|
+
It is a cache signal, not an authorization decision. Without a provider, replace
|
|
63
|
+
the client when its session or request language changes. Host-supplied `read` and
|
|
64
|
+
`initial` data must also belong to the current viewer and language.
|
|
65
|
+
|
|
66
|
+
Writes retain the cache scope they started in. After a scope change, a late
|
|
67
|
+
notification refetches affected reads instead of copying its response into them;
|
|
68
|
+
`onChange` still runs. Use a separate client for each independent session or
|
|
69
|
+
request-language context, not one client across differently scoped providers.
|
|
70
|
+
The framework-free client exposes `setReadScope(key)` and passes that key as
|
|
71
|
+
the second `subscribe` / `onChange` argument, or `null` when the scope changed during the
|
|
72
|
+
operation. This does not change the operation's result or its authentication.
|
|
73
|
+
|
|
56
74
|
- **Mounts.** Without `mounts`, modules live under `baseUrl`: content at the
|
|
57
75
|
root, `/media` (reads, HLS), `/media/upload`, `/codes`, `/taxonomy`.
|
|
58
76
|
- **`client.media`**: `upload`, `put` (upload, commit, wait), `commit`, `read`,
|
|
@@ -77,7 +95,7 @@ const urls = createContentURLs({ routes: { video: "watch", gallery: "g" }, langu
|
|
|
77
95
|
unpublished post's images to its editors (below).
|
|
78
96
|
- **`client.config()`** says what the content module allows: which
|
|
79
97
|
interactions signed-out visitors may make (`content.Options.Anonymous`,
|
|
80
|
-
none by default) and the longest comment (`CommentMaxLength`,
|
|
98
|
+
none by default) and the longest comment (`CommentMaxLength`, 2,200 by
|
|
81
99
|
default; longer is `comment_too_long` with `details.max`).
|
|
82
100
|
- **`client.subscribe(listener)`** receives every successful mutation
|
|
83
101
|
(`media.committed`, `media.processed`, `comment.created`, `reaction.changed`,
|
|
@@ -165,7 +183,7 @@ loaded. `refresh()` joins a read in flight (a player's grant refresh);
|
|
|
165
183
|
|
|
166
184
|
```tsx
|
|
167
185
|
const folder = useMediaFolder(ref, { paths: ["images/{name}", "videos/{name}"], commit: "manual" });
|
|
168
|
-
folder.add(files); // screened
|
|
186
|
+
folder.add(files); // screened and queued; refusals in folder.refused
|
|
169
187
|
await folder.commit(); // the uploaded files, in queue order
|
|
170
188
|
await folder.move(path, 0); await folder.rename(path, "cover");
|
|
171
189
|
await folder.edit(path, edit); await folder.replace(path, file); await folder.remove([path]);
|
|
@@ -176,7 +194,13 @@ An item's folder for its editors: the editor read (all windows), an upload
|
|
|
176
194
|
queue, and the kind's upload rules from the editor read, which screen type,
|
|
177
195
|
size and file caps before anything uploads and name files around names
|
|
178
196
|
taken. `groups` lists each path's uploads; `commit: "auto"` commits each
|
|
179
|
-
upload once those before it have (a draft).
|
|
197
|
+
upload once those before it have (a draft). Files added before the editor
|
|
198
|
+
read arrives wait in `waiting` and are screened when its rules do (or
|
|
199
|
+
refused if the read fails or states no rules). `ref` may be null until the item exists,
|
|
200
|
+
so a composer can create its draft on the first drop. `busy` is true while
|
|
201
|
+
work is in flight (files waiting, uploads queued or running, a commit or
|
|
202
|
+
update); `fileCount` counts the uploads and the files on their way in. A
|
|
203
|
+
committed file leaves the queue once the read lists it. Failures go to the provider's
|
|
180
204
|
`onError` with an operation (`upload`, `folder.commit`, `folder.update`,
|
|
181
205
|
`folder.process` for uploads the worker fails after the editor opened) and
|
|
182
206
|
the file's name.
|
|
@@ -216,7 +240,8 @@ Once a content page's link arrives, replaces the address with its canonical
|
|
|
216
240
|
path (code spelling, merged code, current slug) through the provider's
|
|
217
241
|
`navigate`, else `history.replaceState`, and keeps `<link rel=canonical>`,
|
|
218
242
|
`og:url`, `og:title`, `og:image` and hreflang alternates (each language's
|
|
219
|
-
own slug, plus `x-default`) in the head while mounted.
|
|
243
|
+
own slug, plus `x-default`) in the head while mounted. Unmounting removes
|
|
244
|
+
them, server-rendered ones included, so the next page sets its own. Needs the provider's
|
|
220
245
|
`urls`; `/urls` also exports `hreflang()` and `canonicalURL()` (drops
|
|
221
246
|
tracking parameters) for server rendering.
|
|
222
247
|
|
|
@@ -238,7 +263,10 @@ save and re-crop flow inside a host layout); `SlotImage`; `ImageCropDialog`;
|
|
|
238
263
|
`MediaGallery`, `VideoPlayer` and `VideoMiniPlayer` (HLS with ABR, inline
|
|
239
264
|
muted previews, a grant refresh before `expires`, and a reason, Retry and
|
|
240
265
|
support code for every failure; see below). A `PublicPreset` (`{ preset, aspect, renditions }`)
|
|
241
|
-
is what a slot or poster shows; a read's `public` lists `PublicImage`s.
|
|
266
|
+
is what a slot or poster shows; a read's `public` lists `PublicImage`s. A
|
|
267
|
+
slot crops at its image's aspect, else its public preset's
|
|
268
|
+
(`GET /media/presets`), so an item without an image yet still crops a 3:1
|
|
269
|
+
cover at 3:1.
|
|
242
270
|
|
|
243
271
|
### MediaFolderEditor
|
|
244
272
|
|
|
@@ -246,6 +274,11 @@ is what a slot or poster shows; a read's `public` lists `PublicImage`s.
|
|
|
246
274
|
<MediaFolderEditor item={post} paths={["images/{name}", "videos/{name}"]} commit="auto" ref={handle}
|
|
247
275
|
toolbar={<ZipImport />} rowActions={(f) => <SetCover file={f} />} footer={note} />
|
|
248
276
|
handle.current.discard();
|
|
277
|
+
|
|
278
|
+
// Or the host owns the folder and renders with its state:
|
|
279
|
+
const folder = useMediaFolder(draft, { paths, commit: "auto" }); // draft: null until created
|
|
280
|
+
<MediaFolderEditor folder={folder} />
|
|
281
|
+
<Button disabled={folder.busy || (!title && !text && !folder.fileCount)}>Publish</Button>
|
|
249
282
|
```
|
|
250
283
|
|
|
251
284
|
`useMediaFolder` with its UI: a drop zone stating the rules, a queue with
|
|
@@ -411,13 +444,25 @@ code has a message in every bundle (`useMessages().error(e)`).
|
|
|
411
444
|
## Development
|
|
412
445
|
|
|
413
446
|
```sh
|
|
414
|
-
pnpm check # typecheck, lint, unit
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
pnpm
|
|
418
|
-
|
|
447
|
+
pnpm check # typecheck, lint, unit tests (pure math and client-free components), build
|
|
448
|
+
pnpm build
|
|
449
|
+
pnpm test:e2e # the integration project and the browser specs against a real ContentKit
|
|
450
|
+
pnpm test:integration # or either alone
|
|
451
|
+
pnpm test:browser # (demo/ in Chromium, light/dark × desktop/mobile screenshots)
|
|
452
|
+
pnpm demo # the stack with seeded demo pages, until Ctrl-C
|
|
419
453
|
```
|
|
420
454
|
|
|
455
|
+
The suites need Docker, Go and ffmpeg. `e2e/server` composes the real
|
|
456
|
+
`contentkit.Runtime` (content, taxonomy, codes, media upload and read) with
|
|
457
|
+
AuthKit for identity over PGroonga PostgreSQL 18, MinIO, and media-worker and
|
|
458
|
+
media-gateway built from this checkout (`e2e/compose.yaml`). Each run brings
|
|
459
|
+
the stack up under its own project name and takes it down; the server exits
|
|
460
|
+
with its runner. Pages sign in with auth-ui and pass `auth.authFetch`. Tests
|
|
461
|
+
set up through `/__test`: accounts (`staff`, `moderator`, `editor` roles),
|
|
462
|
+
items the host's resolver knows, stored-object checks, and faults (no CORS,
|
|
463
|
+
429 and expired tokens at the gateway, dropped or held bucket requests, API
|
|
464
|
+
error replies).
|
|
465
|
+
|
|
421
466
|
The wire types, route table and error codes in `src/client/generated/` are
|
|
422
467
|
generated from ContentKit's route catalog (`pnpm contract`; CI runs
|
|
423
468
|
`pnpm contract:check`). UI primitives come from
|
package/dist/client/client.d.ts
CHANGED
|
@@ -45,7 +45,9 @@ export interface ContentKitClient {
|
|
|
45
45
|
config(signal?: AbortSignal): Promise<Config>;
|
|
46
46
|
/** A module's base URL as configured (no trailing slash). */
|
|
47
47
|
url(module: ContentKitModule, ref?: RefBody): string;
|
|
48
|
-
/**
|
|
49
|
-
|
|
48
|
+
/** Cache context only, not authentication. The React provider keeps this in sync. */
|
|
49
|
+
setReadScope(scope: string): void;
|
|
50
|
+
/** Every successful mutation; a null scope means its data predates a context change. */
|
|
51
|
+
subscribe(listener: (change: ContentKitChange, scope: string | null) => void): () => void;
|
|
50
52
|
}
|
|
51
53
|
export declare function createContentKitClient(options: ContentKitClientOptions): ContentKitClient;
|
package/dist/client/client.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":[],"sources":["../../src/client/client.ts"],"sourcesContent":["import { CodesClient } from \"./codes.js\";\nimport { BansClient } from \"./content/bans.js\";\nimport { CommentsClient } from \"./content/comments.js\";\nimport { FavoritesClient } from \"./content/favorites.js\";\nimport { ModerationClient } from \"./content/moderation.js\";\nimport { PollsClient } from \"./content/polls.js\";\nimport { PostsClient } from \"./content/posts.js\";\nimport { ReactionsClient } from \"./content/reactions.js\";\nimport type { Config, RefBody } from \"./generated/wire.js\";\nimport { Http, type ContentKitChange, type ContentKitModule, type HttpOptions } from \"./http.js\";\nimport { MediaClient, type ContentFolders, type MediaOptions } from \"./media/client.js\";\nimport { call } from \"./route.js\";\nimport { TaxonomyClient } from \"./taxonomy.js\";\n\nexport interface ContentKitClientOptions extends HttpOptions {\n /** Upload tuning (transport, part concurrency, retries). */\n media?: MediaOptions;\n /** The media kinds of post and poll folders, when the host renamed them (content.Media). */\n folders?: ContentFolders;\n}\n\n/**\n * One ContentKit deployment as the browser sees it. Each module is a property\n * over one shared transport (mounts, auth, language, errors, change events).\n */\nexport interface ContentKitClient {\n /** Uploads, multipart, commits, reads, frames, HLS bases, hls.js `xhrSetup` and inline images. */\n readonly media: MediaClient;\n /** Published posts, staff CRUD, reactions, cover and body images. */\n readonly posts: PostsClient;\n /** Threads, replies, edits, tombstones, reactions, standing, the latest feed, moderation. */\n readonly comments: CommentsClient;\n /** Likes and dislikes of an item. */\n readonly reactions: ReactionsClient;\n /** The signed-in caller's favorites. */\n readonly favorites: FavoritesClient;\n /** Polls: votes, free-text answers and the staff editor. */\n readonly polls: PollsClient;\n /** Comment bans, owner and global. */\n readonly bans: BansClient;\n /** The review queue of held comments and posts. */\n readonly moderation: ModerationClient;\n /** The taxonomy admin API. */\n readonly taxonomy: TaxonomyClient;\n /** Content-code lookup. */\n readonly codes: CodesClient;\n\n /** What the content module allows: which interactions signed-out visitors may make. */\n config(signal?: AbortSignal): Promise<Config>;\n /** A module's base URL as configured (no trailing slash). */\n url(module: ContentKitModule, ref?: RefBody): string;\n /** Every successful mutation
|
|
1
|
+
{"version":3,"file":"client.js","names":[],"sources":["../../src/client/client.ts"],"sourcesContent":["import { CodesClient } from \"./codes.js\";\nimport { BansClient } from \"./content/bans.js\";\nimport { CommentsClient } from \"./content/comments.js\";\nimport { FavoritesClient } from \"./content/favorites.js\";\nimport { ModerationClient } from \"./content/moderation.js\";\nimport { PollsClient } from \"./content/polls.js\";\nimport { PostsClient } from \"./content/posts.js\";\nimport { ReactionsClient } from \"./content/reactions.js\";\nimport type { Config, RefBody } from \"./generated/wire.js\";\nimport { Http, type ContentKitChange, type ContentKitModule, type HttpOptions } from \"./http.js\";\nimport { MediaClient, type ContentFolders, type MediaOptions } from \"./media/client.js\";\nimport { call } from \"./route.js\";\nimport { TaxonomyClient } from \"./taxonomy.js\";\n\nexport interface ContentKitClientOptions extends HttpOptions {\n /** Upload tuning (transport, part concurrency, retries). */\n media?: MediaOptions;\n /** The media kinds of post and poll folders, when the host renamed them (content.Media). */\n folders?: ContentFolders;\n}\n\n/**\n * One ContentKit deployment as the browser sees it. Each module is a property\n * over one shared transport (mounts, auth, language, errors, change events).\n */\nexport interface ContentKitClient {\n /** Uploads, multipart, commits, reads, frames, HLS bases, hls.js `xhrSetup` and inline images. */\n readonly media: MediaClient;\n /** Published posts, staff CRUD, reactions, cover and body images. */\n readonly posts: PostsClient;\n /** Threads, replies, edits, tombstones, reactions, standing, the latest feed, moderation. */\n readonly comments: CommentsClient;\n /** Likes and dislikes of an item. */\n readonly reactions: ReactionsClient;\n /** The signed-in caller's favorites. */\n readonly favorites: FavoritesClient;\n /** Polls: votes, free-text answers and the staff editor. */\n readonly polls: PollsClient;\n /** Comment bans, owner and global. */\n readonly bans: BansClient;\n /** The review queue of held comments and posts. */\n readonly moderation: ModerationClient;\n /** The taxonomy admin API. */\n readonly taxonomy: TaxonomyClient;\n /** Content-code lookup. */\n readonly codes: CodesClient;\n\n /** What the content module allows: which interactions signed-out visitors may make. */\n config(signal?: AbortSignal): Promise<Config>;\n /** A module's base URL as configured (no trailing slash). */\n url(module: ContentKitModule, ref?: RefBody): string;\n /** Cache context only, not authentication. The React provider keeps this in sync. */\n setReadScope(scope: string): void;\n /** Every successful mutation; a null scope means its data predates a context change. */\n subscribe(listener: (change: ContentKitChange, scope: string | null) => void): () => void;\n}\n\nexport function createContentKitClient(options: ContentKitClientOptions): ContentKitClient {\n const { media: mediaOptions, folders, ...http } = options;\n const h = new Http(http);\n const media = new MediaClient(h, mediaOptions, folders);\n return {\n media,\n posts: new PostsClient(h, media),\n comments: new CommentsClient(h),\n reactions: new ReactionsClient(h),\n favorites: new FavoritesClient(h),\n polls: new PollsClient(h, media),\n bans: new BansClient(h),\n moderation: new ModerationClient(h),\n taxonomy: new TaxonomyClient(h),\n codes: new CodesClient(h),\n config: (signal) => call(h, \"GET\", \"/config\", { signal }),\n url: (module, ref) => h.mount(module, ref),\n setReadScope: (scope) => h.setReadScope(scope),\n subscribe: (listener) => h.subscribe(listener),\n };\n}\n"],"mappings":";;;;;;;;;;;;;AAyDA,SAAgB,EAAuB,GAAoD;CACzF,IAAM,EAAE,OAAO,GAAc,YAAS,GAAG,MAAS,GAC5C,IAAI,IAAI,EAAK,CAAI,GACjB,IAAQ,IAAI,EAAY,GAAG,GAAc,CAAO;CACtD,OAAO;EACL;EACA,OAAO,IAAI,EAAY,GAAG,CAAK;EAC/B,UAAU,IAAI,EAAe,CAAC;EAC9B,WAAW,IAAI,EAAgB,CAAC;EAChC,WAAW,IAAI,EAAgB,CAAC;EAChC,OAAO,IAAI,EAAY,GAAG,CAAK;EAC/B,MAAM,IAAI,EAAW,CAAC;EACtB,YAAY,IAAI,EAAiB,CAAC;EAClC,UAAU,IAAI,EAAe,CAAC;EAC9B,OAAO,IAAI,EAAY,CAAC;EACxB,SAAS,MAAW,EAAK,GAAG,OAAO,WAAW,EAAE,UAAO,CAAC;EACxD,MAAM,GAAQ,MAAQ,EAAE,MAAM,GAAQ,CAAG;EACzC,eAAe,MAAU,EAAE,aAAa,CAAK;EAC7C,YAAY,MAAa,EAAE,UAAU,CAAQ;CAC/C;AACF"}
|
|
@@ -15,19 +15,19 @@ var t = class {
|
|
|
15
15
|
});
|
|
16
16
|
}
|
|
17
17
|
async ban(t, n, r = {}) {
|
|
18
|
-
let i = {
|
|
18
|
+
let i = this.http.captureChanges(), a = {
|
|
19
19
|
params: { user: n },
|
|
20
20
|
body: r
|
|
21
|
-
},
|
|
22
|
-
return
|
|
21
|
+
}, o = await (t === "global" ? e(this.http, "PUT", "/global-comment-bans/{user}", a) : e(this.http, "PUT", "/comment-bans/{user}", a));
|
|
22
|
+
return i({
|
|
23
23
|
type: "ban.saved",
|
|
24
24
|
scope: t,
|
|
25
|
-
ban:
|
|
26
|
-
}),
|
|
25
|
+
ban: o
|
|
26
|
+
}), o;
|
|
27
27
|
}
|
|
28
28
|
async lift(t, n) {
|
|
29
|
-
let r = { params: { user: n } };
|
|
30
|
-
await (t === "global" ? e(this.http, "DELETE", "/global-comment-bans/{user}",
|
|
29
|
+
let r = this.http.captureChanges(), i = { params: { user: n } };
|
|
30
|
+
await (t === "global" ? e(this.http, "DELETE", "/global-comment-bans/{user}", i) : e(this.http, "DELETE", "/comment-bans/{user}", i)), r({
|
|
31
31
|
type: "ban.lifted",
|
|
32
32
|
scope: t,
|
|
33
33
|
user: n
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bans.js","names":[],"sources":["../../../src/client/content/bans.ts"],"sourcesContent":["import type { BanInput, CommentBan } from \"../generated/wire.js\";\nimport type { Http } from \"../http.js\";\nimport { call, type PageQuery } from \"../route.js\";\nimport type { BanScope } from \"./types.js\";\n\n/** Comment bans: \"owner\" on the caller's own content, \"global\" across the site (CommentBan). */\nexport class BansClient {\n constructor(private readonly http: Http) {}\n\n /** The scope's bans, newest first, expired ones included. */\n list(scope: BanScope, q: PageQuery = {}, signal?: AbortSignal): Promise<CommentBan[]> {\n return scope === \"global\"\n ? call(this.http, \"GET\", \"/global-comment-bans\", { query: q, signal })\n : call(this.http, \"GET\", \"/comment-bans\", { query: q, signal });\n }\n /** Bans user from commenting in the scope, replacing any ban there; no until lasts until lifted. */\n async ban(scope: BanScope, user: string, input: BanInput = {}): Promise<CommentBan> {\n const o = { params: { user }, body: input };\n const ban: CommentBan = await (scope === \"global\"\n ? call(this.http, \"PUT\", \"/global-comment-bans/{user}\", o)\n : call(this.http, \"PUT\", \"/comment-bans/{user}\", o));\n
|
|
1
|
+
{"version":3,"file":"bans.js","names":[],"sources":["../../../src/client/content/bans.ts"],"sourcesContent":["import type { BanInput, CommentBan } from \"../generated/wire.js\";\nimport type { Http } from \"../http.js\";\nimport { call, type PageQuery } from \"../route.js\";\nimport type { BanScope } from \"./types.js\";\n\n/** Comment bans: \"owner\" on the caller's own content, \"global\" across the site (CommentBan). */\nexport class BansClient {\n constructor(private readonly http: Http) {}\n\n /** The scope's bans, newest first, expired ones included. */\n list(scope: BanScope, q: PageQuery = {}, signal?: AbortSignal): Promise<CommentBan[]> {\n return scope === \"global\"\n ? call(this.http, \"GET\", \"/global-comment-bans\", { query: q, signal })\n : call(this.http, \"GET\", \"/comment-bans\", { query: q, signal });\n }\n /** Bans user from commenting in the scope, replacing any ban there; no until lasts until lifted. */\n async ban(scope: BanScope, user: string, input: BanInput = {}): Promise<CommentBan> {\n const emit = this.http.captureChanges();\n const o = { params: { user }, body: input };\n const ban: CommentBan = await (scope === \"global\"\n ? call(this.http, \"PUT\", \"/global-comment-bans/{user}\", o)\n : call(this.http, \"PUT\", \"/comment-bans/{user}\", o));\n emit({ type: \"ban.saved\", scope, ban });\n return ban;\n }\n /** Lifts user's ban in the scope; idempotent. */\n async lift(scope: BanScope, user: string): Promise<void> {\n const emit = this.http.captureChanges();\n const o = { params: { user } };\n await (scope === \"global\" ? call(this.http, \"DELETE\", \"/global-comment-bans/{user}\", o) : call(this.http, \"DELETE\", \"/comment-bans/{user}\", o));\n emit({ type: \"ban.lifted\", scope, user });\n }\n}\n"],"mappings":";;AAMA,IAAa,IAAb,MAAwB;CACO;CAA7B,YAAY,GAA6B;EAAZ,KAAA,OAAA;CAAa;CAG1C,KAAK,GAAiB,IAAe,CAAC,GAAG,GAA6C;EACpF,OAAO,MAAU,WACb,EAAK,KAAK,MAAM,OAAO,wBAAwB;GAAE,OAAO;GAAG;EAAO,CAAC,IACnE,EAAK,KAAK,MAAM,OAAO,iBAAiB;GAAE,OAAO;GAAG;EAAO,CAAC;CAClE;CAEA,MAAM,IAAI,GAAiB,GAAc,IAAkB,CAAC,GAAwB;EAClF,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAI;GAAE,QAAQ,EAAE,QAAK;GAAG,MAAM;EAAM,GACpC,IAAkB,OAAO,MAAU,WACrC,EAAK,KAAK,MAAM,OAAO,+BAA+B,CAAC,IACvD,EAAK,KAAK,MAAM,OAAO,wBAAwB,CAAC;EAEpD,OADA,EAAK;GAAE,MAAM;GAAa;GAAO;EAAI,CAAC,GAC/B;CACT;CAEA,MAAM,KAAK,GAAiB,GAA6B;EACvD,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAI,EAAE,QAAQ,EAAE,QAAK,EAAE;EAE7B,AADA,OAAO,MAAU,WAAW,EAAK,KAAK,MAAM,UAAU,+BAA+B,CAAC,IAAI,EAAK,KAAK,MAAM,UAAU,wBAAwB,CAAC,IAC7I,EAAK;GAAE,MAAM;GAAc;GAAO;EAAK,CAAC;CAC1C;AACF"}
|
|
@@ -24,39 +24,40 @@ var n = (e) => ({
|
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
26
|
async create(t, r) {
|
|
27
|
-
let i = await e(this.http, "POST", "/{kind}/{id}/comments", {
|
|
27
|
+
let i = this.http.captureChanges(), a = await e(this.http, "POST", "/{kind}/{id}/comments", {
|
|
28
28
|
params: n(t),
|
|
29
29
|
body: r
|
|
30
30
|
});
|
|
31
|
-
return
|
|
31
|
+
return i({
|
|
32
32
|
type: "comment.created",
|
|
33
33
|
ref: n(t),
|
|
34
|
-
comment:
|
|
35
|
-
}),
|
|
34
|
+
comment: a
|
|
35
|
+
}), a;
|
|
36
36
|
}
|
|
37
37
|
async edit(t, n) {
|
|
38
|
-
let r = await e(this.http, "PATCH", "/comments/{cid}", {
|
|
38
|
+
let r = this.http.captureChanges(), i = await e(this.http, "PATCH", "/comments/{cid}", {
|
|
39
39
|
params: { cid: t },
|
|
40
40
|
body: { body: n }
|
|
41
41
|
});
|
|
42
|
-
return
|
|
42
|
+
return r({
|
|
43
43
|
type: "comment.updated",
|
|
44
|
-
comment:
|
|
45
|
-
}),
|
|
44
|
+
comment: i
|
|
45
|
+
}), i;
|
|
46
46
|
}
|
|
47
47
|
async delete(t) {
|
|
48
|
-
|
|
48
|
+
let n = this.http.captureChanges();
|
|
49
|
+
await e(this.http, "DELETE", "/comments/{cid}", { params: { cid: t } }), n({
|
|
49
50
|
type: "comment.deleted",
|
|
50
51
|
id: t
|
|
51
52
|
});
|
|
52
53
|
}
|
|
53
54
|
async react(n, r) {
|
|
54
|
-
let i = await e(this.http, "POST", `/comments/{cid}/${t(r)}`, { params: { cid: n } });
|
|
55
|
-
return
|
|
55
|
+
let i = this.http.captureChanges(), a = await e(this.http, "POST", `/comments/{cid}/${t(r)}`, { params: { cid: n } });
|
|
56
|
+
return i({
|
|
56
57
|
type: "comment.reacted",
|
|
57
58
|
id: n,
|
|
58
|
-
counts:
|
|
59
|
-
}),
|
|
59
|
+
counts: a
|
|
60
|
+
}), a;
|
|
60
61
|
}
|
|
61
62
|
standing(t, r) {
|
|
62
63
|
return e(this.http, "GET", "/{kind}/{id}/can-comment", {
|
|
@@ -81,7 +82,8 @@ var n = (e) => ({
|
|
|
81
82
|
});
|
|
82
83
|
}
|
|
83
84
|
async restore(t) {
|
|
84
|
-
|
|
85
|
+
let n = this.http.captureChanges();
|
|
86
|
+
await e(this.http, "POST", "/comments/{cid}/restore", { params: { cid: t } }), n({
|
|
85
87
|
type: "comment.restored",
|
|
86
88
|
id: t
|
|
87
89
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comments.js","names":[],"sources":["../../../src/client/content/comments.ts"],"sourcesContent":["import type { AdminComment, Comment, CommentInput, CommentStanding, FeedItem, ReactionCounts, RefBody } from \"../generated/wire.js\";\nimport type { Http } from \"../http.js\";\nimport { call, type PageQuery } from \"../route.js\";\nimport { reactionVerb, type Reaction, type Sort } from \"./types.js\";\n\nexport interface CommentQuery extends PageQuery {\n sort?: Sort;\n}\n\nexport interface AdminCommentQuery extends PageQuery {\n /** Only comments on this content kind. */\n contentKind?: string;\n}\n\n// Content ids are the host's opaque text (not only media's UUIDv7s).\nconst target = (ref: RefBody) => ({ kind: ref.kind, id: ref.id });\n\n/** Comments on an item: threads, replies, edits, tombstones, reactions, standing, the latest feed and moderation. */\nexport class CommentsClient {\n constructor(private readonly http: Http) {}\n\n /** The item's top-level comments with reply counts; the caller also gets its own held and rejected ones. */\n list(ref: RefBody, q: CommentQuery = {}, signal?: AbortSignal): Promise<Comment[]> {\n return call(this.http, \"GET\", \"/{kind}/{id}/comments\", { params: target(ref), query: q, signal });\n }\n /** A top-level comment's replies, oldest first. */\n replies(id: string, q: PageQuery = {}, signal?: AbortSignal): Promise<Comment[]> {\n return call(this.http, \"GET\", \"/comments/{cid}/replies\", { params: { cid: id }, query: q, signal });\n }\n /** Comments on the item, or replies to a top-level comment (reply_to_id); anonymous callers give anon_name. */\n async create(ref: RefBody, input: CommentInput): Promise<Comment> {\n const comment: Comment = await call(this.http, \"POST\", \"/{kind}/{id}/comments\", { params: target(ref), body: input });\n
|
|
1
|
+
{"version":3,"file":"comments.js","names":[],"sources":["../../../src/client/content/comments.ts"],"sourcesContent":["import type { AdminComment, Comment, CommentInput, CommentStanding, FeedItem, ReactionCounts, RefBody } from \"../generated/wire.js\";\nimport type { Http } from \"../http.js\";\nimport { call, type PageQuery } from \"../route.js\";\nimport { reactionVerb, type Reaction, type Sort } from \"./types.js\";\n\nexport interface CommentQuery extends PageQuery {\n sort?: Sort;\n}\n\nexport interface AdminCommentQuery extends PageQuery {\n /** Only comments on this content kind. */\n contentKind?: string;\n}\n\n// Content ids are the host's opaque text (not only media's UUIDv7s).\nconst target = (ref: RefBody) => ({ kind: ref.kind, id: ref.id });\n\n/** Comments on an item: threads, replies, edits, tombstones, reactions, standing, the latest feed and moderation. */\nexport class CommentsClient {\n constructor(private readonly http: Http) {}\n\n /** The item's top-level comments with reply counts; the caller also gets its own held and rejected ones. */\n list(ref: RefBody, q: CommentQuery = {}, signal?: AbortSignal): Promise<Comment[]> {\n return call(this.http, \"GET\", \"/{kind}/{id}/comments\", { params: target(ref), query: q, signal });\n }\n /** A top-level comment's replies, oldest first. */\n replies(id: string, q: PageQuery = {}, signal?: AbortSignal): Promise<Comment[]> {\n return call(this.http, \"GET\", \"/comments/{cid}/replies\", { params: { cid: id }, query: q, signal });\n }\n /** Comments on the item, or replies to a top-level comment (reply_to_id); anonymous callers give anon_name. */\n async create(ref: RefBody, input: CommentInput): Promise<Comment> {\n const emit = this.http.captureChanges();\n const comment: Comment = await call(this.http, \"POST\", \"/{kind}/{id}/comments\", { params: target(ref), body: input });\n emit({ type: \"comment.created\", ref: target(ref), comment });\n return comment;\n }\n /** Replaces the body: its author, or a moderator. */\n async edit(id: string, body: string): Promise<Comment> {\n const emit = this.http.captureChanges();\n const comment: Comment = await call(this.http, \"PATCH\", \"/comments/{cid}\", { params: { cid: id }, body: { body } });\n emit({ type: \"comment.updated\", comment });\n return comment;\n }\n /** Leaves a tombstone: its author, or a moderator. */\n async delete(id: string): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"DELETE\", \"/comments/{cid}\", { params: { cid: id } });\n emit({ type: \"comment.deleted\", id });\n }\n /** Sets the caller's reaction to a comment. */\n async react(id: string, value: Reaction): Promise<ReactionCounts> {\n const emit = this.http.captureChanges();\n const counts: ReactionCounts = await call(this.http, \"POST\", `/comments/{cid}/${reactionVerb(value)}` as const, { params: { cid: id } });\n emit({ type: \"comment.reacted\", id, counts });\n return counts;\n }\n /** Whether the caller may comment on the item, the ban that stops it, and what it may do to others' comments. */\n standing(ref: RefBody, signal?: AbortSignal): Promise<CommentStanding> {\n return call(this.http, \"GET\", \"/{kind}/{id}/can-comment\", { params: target(ref), signal });\n }\n /** The newest published comments across the site, with their items; a page may under-fill. */\n latest(q: PageQuery = {}, signal?: AbortSignal): Promise<FeedItem[]> {\n return call(this.http, \"GET\", \"/comments/latest\", { query: q, signal });\n }\n /** Every comment, newest first, deleted, held and rejected ones with their bodies (CommentModerate). */\n adminList(q: AdminCommentQuery = {}, signal?: AbortSignal): Promise<AdminComment[]> {\n const { contentKind, ...page } = q;\n return call(this.http, \"GET\", \"/comments/admin\", { query: { ...page, content_kind: contentKind }, signal });\n }\n /** Restores a deleted comment (CommentModerate). */\n async restore(id: string): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"POST\", \"/comments/{cid}/restore\", { params: { cid: id } });\n emit({ type: \"comment.restored\", id });\n }\n}\n"],"mappings":";;;AAeA,IAAM,KAAU,OAAkB;CAAE,MAAM,EAAI;CAAM,IAAI,EAAI;AAAG,IAGlD,IAAb,MAA4B;CACG;CAA7B,YAAY,GAA6B;EAAZ,KAAA,OAAA;CAAa;CAG1C,KAAK,GAAc,IAAkB,CAAC,GAAG,GAA0C;EACjF,OAAO,EAAK,KAAK,MAAM,OAAO,yBAAyB;GAAE,QAAQ,EAAO,CAAG;GAAG,OAAO;GAAG;EAAO,CAAC;CAClG;CAEA,QAAQ,GAAY,IAAe,CAAC,GAAG,GAA0C;EAC/E,OAAO,EAAK,KAAK,MAAM,OAAO,2BAA2B;GAAE,QAAQ,EAAE,KAAK,EAAG;GAAG,OAAO;GAAG;EAAO,CAAC;CACpG;CAEA,MAAM,OAAO,GAAc,GAAuC;EAChE,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAmB,MAAM,EAAK,KAAK,MAAM,QAAQ,yBAAyB;GAAE,QAAQ,EAAO,CAAG;GAAG,MAAM;EAAM,CAAC;EAEpH,OADA,EAAK;GAAE,MAAM;GAAmB,KAAK,EAAO,CAAG;GAAG;EAAQ,CAAC,GACpD;CACT;CAEA,MAAM,KAAK,GAAY,GAAgC;EACrD,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAmB,MAAM,EAAK,KAAK,MAAM,SAAS,mBAAmB;GAAE,QAAQ,EAAE,KAAK,EAAG;GAAG,MAAM,EAAE,QAAK;EAAE,CAAC;EAElH,OADA,EAAK;GAAE,MAAM;GAAmB;EAAQ,CAAC,GAClC;CACT;CAEA,MAAM,OAAO,GAA2B;EACtC,IAAM,IAAO,KAAK,KAAK,eAAe;EAEtC,AADA,MAAM,EAAK,KAAK,MAAM,UAAU,mBAAmB,EAAE,QAAQ,EAAE,KAAK,EAAG,EAAE,CAAC,GAC1E,EAAK;GAAE,MAAM;GAAmB;EAAG,CAAC;CACtC;CAEA,MAAM,MAAM,GAAY,GAA0C;EAChE,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAyB,MAAM,EAAK,KAAK,MAAM,QAAQ,mBAAmB,EAAa,CAAK,KAAc,EAAE,QAAQ,EAAE,KAAK,EAAG,EAAE,CAAC;EAEvI,OADA,EAAK;GAAE,MAAM;GAAmB;GAAI;EAAO,CAAC,GACrC;CACT;CAEA,SAAS,GAAc,GAAgD;EACrE,OAAO,EAAK,KAAK,MAAM,OAAO,4BAA4B;GAAE,QAAQ,EAAO,CAAG;GAAG;EAAO,CAAC;CAC3F;CAEA,OAAO,IAAe,CAAC,GAAG,GAA2C;EACnE,OAAO,EAAK,KAAK,MAAM,OAAO,oBAAoB;GAAE,OAAO;GAAG;EAAO,CAAC;CACxE;CAEA,UAAU,IAAuB,CAAC,GAAG,GAA+C;EAClF,IAAM,EAAE,gBAAa,GAAG,MAAS;EACjC,OAAO,EAAK,KAAK,MAAM,OAAO,mBAAmB;GAAE,OAAO;IAAE,GAAG;IAAM,cAAc;GAAY;GAAG;EAAO,CAAC;CAC5G;CAEA,MAAM,QAAQ,GAA2B;EACvC,IAAM,IAAO,KAAK,KAAK,eAAe;EAEtC,AADA,MAAM,EAAK,KAAK,MAAM,QAAQ,2BAA2B,EAAE,QAAQ,EAAE,KAAK,EAAG,EAAE,CAAC,GAChF,EAAK;GAAE,MAAM;GAAoB;EAAG,CAAC;CACvC;AACF"}
|
|
@@ -15,19 +15,19 @@ var t = class {
|
|
|
15
15
|
});
|
|
16
16
|
}
|
|
17
17
|
async set(t, n) {
|
|
18
|
-
let r = await e(this.http, n ? "POST" : "DELETE", "/{kind}/{id}/favorite", { params: {
|
|
18
|
+
let r = this.http.captureChanges(), i = await e(this.http, n ? "POST" : "DELETE", "/{kind}/{id}/favorite", { params: {
|
|
19
19
|
kind: t.kind,
|
|
20
20
|
id: t.id
|
|
21
21
|
} });
|
|
22
|
-
return
|
|
22
|
+
return r({
|
|
23
23
|
type: "favorite.changed",
|
|
24
24
|
ref: {
|
|
25
25
|
kind: t.kind,
|
|
26
26
|
id: t.id
|
|
27
27
|
},
|
|
28
|
-
favorited:
|
|
29
|
-
count:
|
|
30
|
-
}),
|
|
28
|
+
favorited: i.favorited,
|
|
29
|
+
count: i.count
|
|
30
|
+
}), i;
|
|
31
31
|
}
|
|
32
32
|
list(t = {}, n) {
|
|
33
33
|
return e(this.http, "GET", "/favorites", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"favorites.js","names":[],"sources":["../../../src/client/content/favorites.ts"],"sourcesContent":["import type { FavoriteItem, FavoriteState, RefBody } from \"../generated/wire.js\";\nimport type { Http } from \"../http.js\";\nimport { call, type PageQuery } from \"../route.js\";\n\n/** Favorites: the item's count for everyone, the signed-in caller's own. */\nexport class FavoritesClient {\n constructor(private readonly http: Http) {}\n\n /** The item's favorite count and whether the caller favorited it (never, signed out). */\n get(ref: RefBody, signal?: AbortSignal): Promise<FavoriteState> {\n return call(this.http, \"GET\", \"/{kind}/{id}/favorite\", { params: { kind: ref.kind, id: ref.id }, signal });\n }\n /** Favorites or unfavorites the item (signed in); repeating is a no-op. Resolves with the new state and count. */\n async set(ref: RefBody, favorited: boolean): Promise<FavoriteState> {\n const state: FavoriteState = await call(this.http, favorited ? \"POST\" : \"DELETE\", \"/{kind}/{id}/favorite\", { params: { kind: ref.kind, id: ref.id } });\n
|
|
1
|
+
{"version":3,"file":"favorites.js","names":[],"sources":["../../../src/client/content/favorites.ts"],"sourcesContent":["import type { FavoriteItem, FavoriteState, RefBody } from \"../generated/wire.js\";\nimport type { Http } from \"../http.js\";\nimport { call, type PageQuery } from \"../route.js\";\n\n/** Favorites: the item's count for everyone, the signed-in caller's own. */\nexport class FavoritesClient {\n constructor(private readonly http: Http) {}\n\n /** The item's favorite count and whether the caller favorited it (never, signed out). */\n get(ref: RefBody, signal?: AbortSignal): Promise<FavoriteState> {\n return call(this.http, \"GET\", \"/{kind}/{id}/favorite\", { params: { kind: ref.kind, id: ref.id }, signal });\n }\n /** Favorites or unfavorites the item (signed in); repeating is a no-op. Resolves with the new state and count. */\n async set(ref: RefBody, favorited: boolean): Promise<FavoriteState> {\n const emit = this.http.captureChanges();\n const state: FavoriteState = await call(this.http, favorited ? \"POST\" : \"DELETE\", \"/{kind}/{id}/favorite\", { params: { kind: ref.kind, id: ref.id } });\n emit({ type: \"favorite.changed\", ref: { kind: ref.kind, id: ref.id }, favorited: state.favorited, count: state.count });\n return state;\n }\n /** The caller's favorites, newest first (references only; the host hydrates them). */\n list(q: PageQuery = {}, signal?: AbortSignal): Promise<FavoriteItem[]> {\n return call(this.http, \"GET\", \"/favorites\", { query: q, signal });\n }\n}\n"],"mappings":";;AAKA,IAAa,IAAb,MAA6B;CACE;CAA7B,YAAY,GAA6B;EAAZ,KAAA,OAAA;CAAa;CAG1C,IAAI,GAAc,GAA8C;EAC9D,OAAO,EAAK,KAAK,MAAM,OAAO,yBAAyB;GAAE,QAAQ;IAAE,MAAM,EAAI;IAAM,IAAI,EAAI;GAAG;GAAG;EAAO,CAAC;CAC3G;CAEA,MAAM,IAAI,GAAc,GAA4C;EAClE,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAuB,MAAM,EAAK,KAAK,MAAM,IAAY,SAAS,UAAU,yBAAyB,EAAE,QAAQ;GAAE,MAAM,EAAI;GAAM,IAAI,EAAI;EAAG,EAAE,CAAC;EAErJ,OADA,EAAK;GAAE,MAAM;GAAoB,KAAK;IAAE,MAAM,EAAI;IAAM,IAAI,EAAI;GAAG;GAAG,WAAW,EAAM;GAAW,OAAO,EAAM;EAAM,CAAC,GAC/G;CACT;CAEA,KAAK,IAAe,CAAC,GAAG,GAA+C;EACrE,OAAO,EAAK,KAAK,MAAM,OAAO,cAAc;GAAE,OAAO;GAAG;EAAO,CAAC;CAClE;AACF"}
|
|
@@ -12,9 +12,9 @@ var t = class {
|
|
|
12
12
|
});
|
|
13
13
|
}
|
|
14
14
|
async resolve(t, n, r) {
|
|
15
|
-
let i = t.kind,
|
|
15
|
+
let i = this.http.captureChanges(), a = t.kind, o = await e(this.http, "POST", "/moderation/{kind}/{id}/resolve", {
|
|
16
16
|
params: {
|
|
17
|
-
kind:
|
|
17
|
+
kind: a,
|
|
18
18
|
id: t.id
|
|
19
19
|
},
|
|
20
20
|
body: {
|
|
@@ -23,12 +23,12 @@ var t = class {
|
|
|
23
23
|
...r ? { reason: r } : {}
|
|
24
24
|
}
|
|
25
25
|
});
|
|
26
|
-
return
|
|
26
|
+
return i({
|
|
27
27
|
type: "moderation.resolved",
|
|
28
|
-
kind:
|
|
28
|
+
kind: a,
|
|
29
29
|
id: t.id,
|
|
30
30
|
decision: n
|
|
31
|
-
}),
|
|
31
|
+
}), o;
|
|
32
32
|
}
|
|
33
33
|
};
|
|
34
34
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"moderation.js","names":[],"sources":["../../../src/client/content/moderation.ts"],"sourcesContent":["import type { HeldItem, HeldPage, ReviewOutcome } from \"../generated/wire.js\";\nimport type { Http } from \"../http.js\";\nimport { call } from \"../route.js\";\nimport type { Decision, HeldKind } from \"./types.js\";\n\nexport interface HeldQuery {\n kind: HeldKind;\n /** The previous page's next. */\n cursor?: string;\n limit?: number;\n}\n\n/** The review queue of held comments and posts (ModerationReview). */\nexport class ModerationClient {\n constructor(private readonly http: Http) {}\n\n /** Held items awaiting review, oldest first. */\n held(q: HeldQuery, signal?: AbortSignal): Promise<HeldPage> {\n return call(this.http, \"GET\", \"/moderation/held\", { query: { ...q }, signal });\n }\n /** Approves (publishes) or rejects a held item at the revision the queue listed; conflict when it was edited since. */\n async resolve(item: Pick<HeldItem, \"kind\" | \"id\" | \"revision\">, decision: Decision, reason?: string): Promise<ReviewOutcome> {\n const kind = item.kind as HeldKind;\n const out: ReviewOutcome = await call(this.http, \"POST\", \"/moderation/{kind}/{id}/resolve\", {\n params: { kind, id: item.id },\n body: { decision, revision: item.revision, ...(reason ? { reason } : {}) },\n });\n
|
|
1
|
+
{"version":3,"file":"moderation.js","names":[],"sources":["../../../src/client/content/moderation.ts"],"sourcesContent":["import type { HeldItem, HeldPage, ReviewOutcome } from \"../generated/wire.js\";\nimport type { Http } from \"../http.js\";\nimport { call } from \"../route.js\";\nimport type { Decision, HeldKind } from \"./types.js\";\n\nexport interface HeldQuery {\n kind: HeldKind;\n /** The previous page's next. */\n cursor?: string;\n limit?: number;\n}\n\n/** The review queue of held comments and posts (ModerationReview). */\nexport class ModerationClient {\n constructor(private readonly http: Http) {}\n\n /** Held items awaiting review, oldest first. */\n held(q: HeldQuery, signal?: AbortSignal): Promise<HeldPage> {\n return call(this.http, \"GET\", \"/moderation/held\", { query: { ...q }, signal });\n }\n /** Approves (publishes) or rejects a held item at the revision the queue listed; conflict when it was edited since. */\n async resolve(item: Pick<HeldItem, \"kind\" | \"id\" | \"revision\">, decision: Decision, reason?: string): Promise<ReviewOutcome> {\n const emit = this.http.captureChanges();\n const kind = item.kind as HeldKind;\n const out: ReviewOutcome = await call(this.http, \"POST\", \"/moderation/{kind}/{id}/resolve\", {\n params: { kind, id: item.id },\n body: { decision, revision: item.revision, ...(reason ? { reason } : {}) },\n });\n emit({ type: \"moderation.resolved\", kind, id: item.id, decision });\n return out;\n }\n}\n"],"mappings":";;AAaA,IAAa,IAAb,MAA8B;CACC;CAA7B,YAAY,GAA6B;EAAZ,KAAA,OAAA;CAAa;CAG1C,KAAK,GAAc,GAAyC;EAC1D,OAAO,EAAK,KAAK,MAAM,OAAO,oBAAoB;GAAE,OAAO,EAAE,GAAG,EAAE;GAAG;EAAO,CAAC;CAC/E;CAEA,MAAM,QAAQ,GAAkD,GAAoB,GAAyC;EAC3H,IAAM,IAAO,KAAK,KAAK,eAAe,GAChC,IAAO,EAAK,MACZ,IAAqB,MAAM,EAAK,KAAK,MAAM,QAAQ,mCAAmC;GAC1F,QAAQ;IAAE;IAAM,IAAI,EAAK;GAAG;GAC5B,MAAM;IAAE;IAAU,UAAU,EAAK;IAAU,GAAI,IAAS,EAAE,UAAO,IAAI,CAAC;GAAG;EAC3E,CAAC;EAED,OADA,EAAK;GAAE,MAAM;GAAuB;GAAM,IAAI,EAAK;GAAI;EAAS,CAAC,GAC1D;CACT;AACF"}
|
|
@@ -25,94 +25,97 @@ var t = class {
|
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
27
|
async create(t) {
|
|
28
|
-
let n = await e(this.http, "POST", "/polls", { body: t });
|
|
29
|
-
return
|
|
28
|
+
let n = this.http.captureChanges(), r = await e(this.http, "POST", "/polls", { body: t });
|
|
29
|
+
return n({
|
|
30
30
|
type: "poll.created",
|
|
31
|
-
poll:
|
|
32
|
-
}),
|
|
31
|
+
poll: r
|
|
32
|
+
}), r;
|
|
33
33
|
}
|
|
34
34
|
async update(t, n) {
|
|
35
|
-
let r = await e(this.http, "PATCH", "/polls/{id}", {
|
|
35
|
+
let r = this.http.captureChanges(), i = await e(this.http, "PATCH", "/polls/{id}", {
|
|
36
36
|
params: { id: t },
|
|
37
37
|
body: n
|
|
38
38
|
});
|
|
39
|
-
return
|
|
39
|
+
return r({
|
|
40
40
|
type: "poll.updated",
|
|
41
|
-
poll:
|
|
42
|
-
}),
|
|
41
|
+
poll: i
|
|
42
|
+
}), i;
|
|
43
43
|
}
|
|
44
44
|
async delete(t) {
|
|
45
|
-
|
|
45
|
+
let n = this.http.captureChanges();
|
|
46
|
+
await e(this.http, "DELETE", "/polls/{id}", { params: { id: t } }), n({
|
|
46
47
|
type: "poll.deleted",
|
|
47
48
|
id: t
|
|
48
49
|
});
|
|
49
50
|
}
|
|
50
51
|
async vote(t, n) {
|
|
51
|
-
let r = await e(this.http, "POST", "/polls/{id}/vote", {
|
|
52
|
+
let r = this.http.captureChanges(), i = await e(this.http, "POST", "/polls/{id}/vote", {
|
|
52
53
|
params: { id: t },
|
|
53
54
|
body: { option_id: n }
|
|
54
55
|
});
|
|
55
|
-
return
|
|
56
|
+
return r({
|
|
56
57
|
type: "poll.updated",
|
|
57
|
-
poll:
|
|
58
|
-
}),
|
|
58
|
+
poll: i
|
|
59
|
+
}), i;
|
|
59
60
|
}
|
|
60
61
|
async answer(t, n) {
|
|
61
|
-
let r = await e(this.http, "POST", "/polls/{id}/answer", {
|
|
62
|
+
let r = this.http.captureChanges(), i = await e(this.http, "POST", "/polls/{id}/answer", {
|
|
62
63
|
params: { id: t },
|
|
63
64
|
body: { text: n }
|
|
64
65
|
});
|
|
65
|
-
return
|
|
66
|
+
return r({
|
|
66
67
|
type: "poll.updated",
|
|
67
|
-
poll:
|
|
68
|
-
}),
|
|
68
|
+
poll: i
|
|
69
|
+
}), i;
|
|
69
70
|
}
|
|
70
71
|
async setImage(t, n) {
|
|
71
|
-
let r = await e(this.http, "PUT", "/polls/{id}/image", {
|
|
72
|
+
let r = this.http.captureChanges(), i = await e(this.http, "PUT", "/polls/{id}/image", {
|
|
72
73
|
params: { id: t },
|
|
73
74
|
body: { image: n ?? "" }
|
|
74
75
|
});
|
|
75
|
-
return
|
|
76
|
+
return r({
|
|
76
77
|
type: "poll.changed",
|
|
77
78
|
id: t
|
|
78
|
-
}),
|
|
79
|
+
}), i.image_url;
|
|
79
80
|
}
|
|
80
81
|
async uploadImage(e, t, n) {
|
|
81
82
|
return this.setImage(e, t ? (await this.media.uploadNamed(this.media.pollRef(e), t, n)).name : null);
|
|
82
83
|
}
|
|
83
84
|
async addOption(t, n) {
|
|
84
|
-
let r = await e(this.http, "POST", "/polls/{id}/options", {
|
|
85
|
+
let r = this.http.captureChanges(), i = await e(this.http, "POST", "/polls/{id}/options", {
|
|
85
86
|
params: { id: t },
|
|
86
87
|
body: n
|
|
87
88
|
});
|
|
88
|
-
return
|
|
89
|
+
return r({
|
|
89
90
|
type: "poll.changed",
|
|
90
91
|
id: t
|
|
91
|
-
}),
|
|
92
|
+
}), i;
|
|
92
93
|
}
|
|
93
94
|
async updateOption(t, n, r) {
|
|
94
|
-
let i = await e(this.http, "PATCH", "/polls/{id}/options/{oid}", {
|
|
95
|
+
let i = this.http.captureChanges(), a = await e(this.http, "PATCH", "/polls/{id}/options/{oid}", {
|
|
95
96
|
params: {
|
|
96
97
|
id: t,
|
|
97
98
|
oid: n
|
|
98
99
|
},
|
|
99
100
|
body: r
|
|
100
101
|
});
|
|
101
|
-
return
|
|
102
|
+
return i({
|
|
102
103
|
type: "poll.changed",
|
|
103
104
|
id: t
|
|
104
|
-
}),
|
|
105
|
+
}), a;
|
|
105
106
|
}
|
|
106
107
|
async deleteOption(t, n) {
|
|
108
|
+
let r = this.http.captureChanges();
|
|
107
109
|
await e(this.http, "DELETE", "/polls/{id}/options/{oid}", { params: {
|
|
108
110
|
id: t,
|
|
109
111
|
oid: n
|
|
110
|
-
} }),
|
|
112
|
+
} }), r({
|
|
111
113
|
type: "poll.changed",
|
|
112
114
|
id: t
|
|
113
115
|
});
|
|
114
116
|
}
|
|
115
117
|
async reorderOptions(t, n) {
|
|
118
|
+
let r = this.http.captureChanges();
|
|
116
119
|
for (let [r, i] of n.entries()) i.position !== r && await e(this.http, "PATCH", "/polls/{id}/options/{oid}", {
|
|
117
120
|
params: {
|
|
118
121
|
id: t,
|
|
@@ -120,23 +123,23 @@ var t = class {
|
|
|
120
123
|
},
|
|
121
124
|
body: { position: r }
|
|
122
125
|
});
|
|
123
|
-
|
|
126
|
+
r({
|
|
124
127
|
type: "poll.changed",
|
|
125
128
|
id: t
|
|
126
129
|
});
|
|
127
130
|
}
|
|
128
131
|
async setOptionImage(t, n, r) {
|
|
129
|
-
let i = await e(this.http, "PUT", "/polls/{id}/options/{oid}/image", {
|
|
132
|
+
let i = this.http.captureChanges(), a = await e(this.http, "PUT", "/polls/{id}/options/{oid}/image", {
|
|
130
133
|
params: {
|
|
131
134
|
id: t,
|
|
132
135
|
oid: n
|
|
133
136
|
},
|
|
134
137
|
body: { image: r ?? "" }
|
|
135
138
|
});
|
|
136
|
-
return
|
|
139
|
+
return i({
|
|
137
140
|
type: "poll.changed",
|
|
138
141
|
id: t
|
|
139
|
-
}),
|
|
142
|
+
}), a.image_url;
|
|
140
143
|
}
|
|
141
144
|
async uploadOptionImage(e, t, n, r) {
|
|
142
145
|
return this.setOptionImage(e, t, n ? (await this.media.uploadNamed(this.media.pollRef(e), n, r)).name : null);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"polls.js","names":[],"sources":["../../../src/client/content/polls.ts"],"sourcesContent":["import type { Poll, PollImage, PollInput, PollOption, PollOptionPatch, PollUpdate } from \"../generated/wire.js\";\nimport type { Http } from \"../http.js\";\nimport type { MediaClient, NamedOptions } from \"../media/client.js\";\nimport { call, type PageQuery } from \"../route.js\";\n\nexport interface PollQuery extends PageQuery {\n language?: string;\n /** Polls live in this month, YYYY-MM. */\n month?: string;\n /** Polls live on this day, YYYY-MM-DD. */\n date?: string;\n}\n\n/** Site-wide polls: multiple choice or free text; staff write them (PollWrite). */\nexport class PollsClient {\n constructor(\n private readonly http: Http,\n private readonly media: MediaClient,\n ) {}\n\n /** Live polls, newest first. */\n list(q: PollQuery = {}, signal?: AbortSignal): Promise<Poll[]> {\n return call(this.http, \"GET\", \"/polls\", { query: q, signal });\n }\n /** Every poll, scheduled and inactive ones included (PollWrite). */\n adminList(q: PollQuery = {}, signal?: AbortSignal): Promise<Poll[]> {\n return call(this.http, \"GET\", \"/polls/admin\", { query: q, signal });\n }\n /** A poll with the caller's vote or answer. */\n get(id: string, signal?: AbortSignal): Promise<Poll> {\n return call(this.http, \"GET\", \"/polls/{id}\", { params: { id }, signal });\n }\n /** Creates a poll with its options (images are set once it exists). */\n async create(input: PollInput): Promise<Poll> {\n const poll: Poll = await call(this.http, \"POST\", \"/polls\", { body: input });\n
|
|
1
|
+
{"version":3,"file":"polls.js","names":[],"sources":["../../../src/client/content/polls.ts"],"sourcesContent":["import type { Poll, PollImage, PollInput, PollOption, PollOptionPatch, PollUpdate } from \"../generated/wire.js\";\nimport type { Http } from \"../http.js\";\nimport type { MediaClient, NamedOptions } from \"../media/client.js\";\nimport { call, type PageQuery } from \"../route.js\";\n\nexport interface PollQuery extends PageQuery {\n language?: string;\n /** Polls live in this month, YYYY-MM. */\n month?: string;\n /** Polls live on this day, YYYY-MM-DD. */\n date?: string;\n}\n\n/** Site-wide polls: multiple choice or free text; staff write them (PollWrite). */\nexport class PollsClient {\n constructor(\n private readonly http: Http,\n private readonly media: MediaClient,\n ) {}\n\n /** Live polls, newest first. */\n list(q: PollQuery = {}, signal?: AbortSignal): Promise<Poll[]> {\n return call(this.http, \"GET\", \"/polls\", { query: q, signal });\n }\n /** Every poll, scheduled and inactive ones included (PollWrite). */\n adminList(q: PollQuery = {}, signal?: AbortSignal): Promise<Poll[]> {\n return call(this.http, \"GET\", \"/polls/admin\", { query: q, signal });\n }\n /** A poll with the caller's vote or answer. */\n get(id: string, signal?: AbortSignal): Promise<Poll> {\n return call(this.http, \"GET\", \"/polls/{id}\", { params: { id }, signal });\n }\n /** Creates a poll with its options (images are set once it exists). */\n async create(input: PollInput): Promise<Poll> {\n const emit = this.http.captureChanges();\n const poll: Poll = await call(this.http, \"POST\", \"/polls\", { body: input });\n emit({ type: \"poll.created\", poll });\n return poll;\n }\n /** Updates the given fields. */\n async update(id: string, patch: PollUpdate): Promise<Poll> {\n const emit = this.http.captureChanges();\n const poll: Poll = await call(this.http, \"PATCH\", \"/polls/{id}\", { params: { id }, body: patch });\n emit({ type: \"poll.updated\", poll });\n return poll;\n }\n /** Deletes the poll and its images. */\n async delete(id: string): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"DELETE\", \"/polls/{id}\", { params: { id } });\n emit({ type: \"poll.deleted\", id });\n }\n /** Votes for an option of an open multiple-choice poll; a vote is final, and voting again changes nothing. */\n async vote(id: string, option: string): Promise<Poll> {\n const emit = this.http.captureChanges();\n const poll: Poll = await call(this.http, \"POST\", \"/polls/{id}/vote\", { params: { id }, body: { option_id: option } });\n emit({ type: \"poll.updated\", poll });\n return poll;\n }\n /** Stores or replaces the signed-in caller's answer to an open free-text poll. */\n async answer(id: string, text: string): Promise<Poll> {\n const emit = this.http.captureChanges();\n const poll: Poll = await call(this.http, \"POST\", \"/polls/{id}/answer\", { params: { id }, body: { text } });\n emit({ type: \"poll.updated\", poll });\n return poll;\n }\n /** Sets the question image to an inline image name of the poll's folder, or clears it; resolves with its URL. */\n async setImage(id: string, image: string | null): Promise<string | null> {\n const emit = this.http.captureChanges();\n const r: PollImage = await call(this.http, \"PUT\", \"/polls/{id}/image\", { params: { id }, body: { image: image ?? \"\" } });\n emit({ type: \"poll.changed\", id });\n return r.image_url;\n }\n /** Uploads file to the poll's folder as the question image (null clears it). */\n async uploadImage(id: string, file: Blob | null, o?: NamedOptions): Promise<string | null> {\n return this.setImage(id, file ? (await this.media.uploadNamed(this.media.pollRef(id), file, o)).name : null);\n }\n /** Adds an option, at the end unless position is given. */\n async addOption(id: string, option: PollOptionPatch): Promise<PollOption> {\n const emit = this.http.captureChanges();\n const o: PollOption = await call(this.http, \"POST\", \"/polls/{id}/options\", { params: { id }, body: option });\n emit({ type: \"poll.changed\", id });\n return o;\n }\n async updateOption(id: string, option: string, patch: PollOptionPatch): Promise<PollOption> {\n const emit = this.http.captureChanges();\n const o: PollOption = await call(this.http, \"PATCH\", \"/polls/{id}/options/{oid}\", { params: { id, oid: option }, body: patch });\n emit({ type: \"poll.changed\", id });\n return o;\n }\n /** Removes an option and its votes; a poll keeps at least two. */\n async deleteOption(id: string, option: string): Promise<void> {\n const emit = this.http.captureChanges();\n await call(this.http, \"DELETE\", \"/polls/{id}/options/{oid}\", { params: { id, oid: option } });\n emit({ type: \"poll.changed\", id });\n }\n /** Gives the options the positions of their order, patching those that moved. */\n async reorderOptions(id: string, options: readonly Pick<PollOption, \"id\" | \"position\">[]): Promise<void> {\n const emit = this.http.captureChanges();\n for (const [position, o] of options.entries()) {\n if (o.position !== position) await call(this.http, \"PATCH\", \"/polls/{id}/options/{oid}\", { params: { id, oid: o.id }, body: { position } });\n }\n emit({ type: \"poll.changed\", id });\n }\n async setOptionImage(id: string, option: string, image: string | null): Promise<string | null> {\n const emit = this.http.captureChanges();\n const r: PollImage = await call(this.http, \"PUT\", \"/polls/{id}/options/{oid}/image\", { params: { id, oid: option }, body: { image: image ?? \"\" } });\n emit({ type: \"poll.changed\", id });\n return r.image_url;\n }\n /** Uploads file to the poll's folder as the option's image (null clears it). */\n async uploadOptionImage(id: string, option: string, file: Blob | null, o?: NamedOptions): Promise<string | null> {\n return this.setOptionImage(id, option, file ? (await this.media.uploadNamed(this.media.pollRef(id), file, o)).name : null);\n }\n}\n"],"mappings":"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"}
|