@jant/core 0.3.39 → 0.3.40
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 +1 -0
- package/dist/{app-BfoG98VD.js → app-CAtsuLLh.js} +306 -72
- package/dist/client/_assets/client-auth.js +77 -68
- package/dist/client/_assets/client.css +1 -1
- package/dist/index.js +1 -1
- package/dist/node.js +2 -2
- package/package.json +1 -1
- package/src/app.tsx +5 -0
- package/src/client/components/__tests__/jant-collection-sidebar.test.ts +56 -0
- package/src/client/components/jant-collection-form.ts +2 -0
- package/src/client/components/jant-collection-sidebar.ts +19 -6
- package/src/i18n/locales/en.po +14 -0
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +14 -0
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +14 -0
- package/src/i18n/locales/zh-Hant.ts +1 -1
- package/src/lib/__tests__/collection-groups.test.ts +63 -0
- package/src/lib/__tests__/constants.test.ts +23 -1
- package/src/lib/__tests__/schemas.test.ts +18 -0
- package/src/lib/__tests__/slug-format.test.ts +8 -0
- package/src/lib/__tests__/timeline.test.ts +84 -2
- package/src/lib/collection-groups.ts +69 -0
- package/src/lib/constants.ts +20 -0
- package/src/lib/schemas.ts +8 -1
- package/src/lib/slug-format.ts +8 -0
- package/src/lib/timeline.ts +30 -23
- package/src/routes/pages/collection.tsx +89 -37
- package/src/runtime/__tests__/readiness.test.ts +89 -0
- package/src/runtime/readiness.ts +129 -0
- package/src/services/__tests__/collection.test.ts +45 -0
- package/src/services/__tests__/post-timeline.test.ts +58 -0
- package/src/services/__tests__/post.test.ts +35 -1
- package/src/services/collection.ts +55 -0
- package/src/services/post.ts +121 -12
- package/src/styles/ui.css +17 -0
- package/src/types/props.ts +2 -1
- package/src/ui/pages/CollectionPage.tsx +84 -17
- package/src/ui/shared/CollectionDirectory.tsx +18 -4
|
@@ -16,11 +16,12 @@ const escapeJson = (data: unknown) =>
|
|
|
16
16
|
JSON.stringify(data).replace(/</g, "\\u003c");
|
|
17
17
|
|
|
18
18
|
export const CollectionPage: FC<CollectionPageProps> = ({
|
|
19
|
-
|
|
19
|
+
collections,
|
|
20
20
|
items,
|
|
21
21
|
totalThreadCount,
|
|
22
22
|
currentPage,
|
|
23
23
|
totalPages,
|
|
24
|
+
pagePath,
|
|
24
25
|
baseUrl,
|
|
25
26
|
currentSort,
|
|
26
27
|
defaultSort,
|
|
@@ -28,14 +29,24 @@ export const CollectionPage: FC<CollectionPageProps> = ({
|
|
|
28
29
|
isAuthenticated,
|
|
29
30
|
sitePathPrefix = "",
|
|
30
31
|
}) => {
|
|
32
|
+
const primaryCollection = collections[0];
|
|
33
|
+
if (!primaryCollection) return null;
|
|
34
|
+
|
|
31
35
|
const { t } = useLingui();
|
|
32
|
-
const
|
|
36
|
+
const isAggregate = collections.length > 1;
|
|
37
|
+
const selectionTitle = collections
|
|
38
|
+
.map((collection) => collection.title)
|
|
39
|
+
.join(" + ");
|
|
40
|
+
const collectionUrl = toPublicPath(pagePath, sitePathPrefix);
|
|
33
41
|
const editCollectionUrl = toPublicPath(
|
|
34
|
-
`/c/${
|
|
42
|
+
`/c/${primaryCollection.slug}/edit?returnTo=${encodeURIComponent(collectionUrl)}`,
|
|
35
43
|
sitePathPrefix,
|
|
36
44
|
);
|
|
37
|
-
const
|
|
38
|
-
|
|
45
|
+
const sortUiId = isAggregate
|
|
46
|
+
? collections.map((collection) => collection.slug).join("-")
|
|
47
|
+
: primaryCollection.id;
|
|
48
|
+
const sortTriggerId = `collection-sort-trigger-${sortUiId}`;
|
|
49
|
+
const sortPopoverId = `collection-sort-popover-${sortUiId}`;
|
|
39
50
|
const pageLabel =
|
|
40
51
|
currentPage > 1 ? formatPageLabel(currentPage, totalPages) : null;
|
|
41
52
|
const mutationLabels = getCollectionMutationLabels(t);
|
|
@@ -125,7 +136,15 @@ export const CollectionPage: FC<CollectionPageProps> = ({
|
|
|
125
136
|
sortOptions[0].label;
|
|
126
137
|
|
|
127
138
|
return (
|
|
128
|
-
<div
|
|
139
|
+
<div
|
|
140
|
+
class="py-6"
|
|
141
|
+
data-page="collection"
|
|
142
|
+
data-collection-mode={isAggregate ? "aggregate" : "single"}
|
|
143
|
+
data-collection-id={isAggregate ? undefined : primaryCollection.id}
|
|
144
|
+
data-collection-slugs={collections
|
|
145
|
+
.map((collection) => collection.slug)
|
|
146
|
+
.join(",")}
|
|
147
|
+
>
|
|
129
148
|
<header class="collection-page-header">
|
|
130
149
|
<div class="collection-page-topbar">
|
|
131
150
|
<nav
|
|
@@ -160,7 +179,7 @@ export const CollectionPage: FC<CollectionPageProps> = ({
|
|
|
160
179
|
</svg>
|
|
161
180
|
</li>
|
|
162
181
|
<li>
|
|
163
|
-
<span>{
|
|
182
|
+
<span>{selectionTitle}</span>
|
|
164
183
|
</li>
|
|
165
184
|
</ol>
|
|
166
185
|
</nav>
|
|
@@ -168,16 +187,56 @@ export const CollectionPage: FC<CollectionPageProps> = ({
|
|
|
168
187
|
|
|
169
188
|
<div class="collection-page-title-block">
|
|
170
189
|
<h1 class="collection-page-title">
|
|
171
|
-
<span>{
|
|
190
|
+
<span>{selectionTitle}</span>
|
|
172
191
|
</h1>
|
|
173
|
-
{
|
|
174
|
-
<p class="collection-page-description">
|
|
192
|
+
{!isAggregate && primaryCollection.description ? (
|
|
193
|
+
<p class="collection-page-description">
|
|
194
|
+
{primaryCollection.description}
|
|
195
|
+
</p>
|
|
196
|
+
) : null}
|
|
197
|
+
{isAggregate ? (
|
|
198
|
+
<div class="collection-page-meta">
|
|
199
|
+
<span>
|
|
200
|
+
{t({
|
|
201
|
+
message: "Includes",
|
|
202
|
+
comment:
|
|
203
|
+
"@context: Label above the included collections list on an aggregate collection page",
|
|
204
|
+
})}
|
|
205
|
+
</span>{" "}
|
|
206
|
+
{collections.map((collection, index) => (
|
|
207
|
+
<span key={collection.id}>
|
|
208
|
+
{index > 0 ? <span>, </span> : null}
|
|
209
|
+
<a
|
|
210
|
+
href={toPublicPath(`/c/${collection.slug}`, sitePathPrefix)}
|
|
211
|
+
>
|
|
212
|
+
{collection.title}
|
|
213
|
+
</a>
|
|
214
|
+
</span>
|
|
215
|
+
))}
|
|
216
|
+
</div>
|
|
175
217
|
) : null}
|
|
176
218
|
</div>
|
|
177
219
|
|
|
178
220
|
<div class="collection-page-subhead">
|
|
179
221
|
<div class="collection-page-meta-row">
|
|
180
222
|
<p class="collection-page-meta">
|
|
223
|
+
{isAggregate ? (
|
|
224
|
+
<>
|
|
225
|
+
{collections.length}{" "}
|
|
226
|
+
{collections.length === 1
|
|
227
|
+
? t({
|
|
228
|
+
message: "collection",
|
|
229
|
+
comment:
|
|
230
|
+
"@context: Singular collection count label on an aggregate collection page",
|
|
231
|
+
})
|
|
232
|
+
: t({
|
|
233
|
+
message: "collections",
|
|
234
|
+
comment:
|
|
235
|
+
"@context: Plural collection count label on an aggregate collection page",
|
|
236
|
+
})}
|
|
237
|
+
<span> / </span>
|
|
238
|
+
</>
|
|
239
|
+
) : null}
|
|
181
240
|
{totalThreadCount}{" "}
|
|
182
241
|
{totalThreadCount === 1
|
|
183
242
|
? t({
|
|
@@ -291,14 +350,14 @@ export const CollectionPage: FC<CollectionPageProps> = ({
|
|
|
291
350
|
</div>
|
|
292
351
|
</div>
|
|
293
352
|
|
|
294
|
-
{isAuthenticated ? (
|
|
353
|
+
{isAuthenticated && !isAggregate ? (
|
|
295
354
|
<div class="collection-page-owner-tools">
|
|
296
355
|
<button
|
|
297
356
|
type="button"
|
|
298
357
|
class="collection-page-compose-trigger"
|
|
299
358
|
aria-label={newPostLabel}
|
|
300
359
|
title={newPostLabel}
|
|
301
|
-
data-on:click={`document.getElementById('compose-dialog')?.querySelector('jant-compose-dialog')?.openNew(${escapeJson({ collectionId:
|
|
360
|
+
data-on:click={`document.getElementById('compose-dialog')?.querySelector('jant-compose-dialog')?.openNew(${escapeJson({ collectionId: primaryCollection.id })})`}
|
|
302
361
|
>
|
|
303
362
|
<svg
|
|
304
363
|
xmlns="http://www.w3.org/2000/svg"
|
|
@@ -320,7 +379,7 @@ export const CollectionPage: FC<CollectionPageProps> = ({
|
|
|
320
379
|
<div
|
|
321
380
|
class="collection-page-manage"
|
|
322
381
|
data-collection-page-actions
|
|
323
|
-
data-collection-id={
|
|
382
|
+
data-collection-id={primaryCollection.id}
|
|
324
383
|
data-collection-page-labels={escapeJson(mutationLabels)}
|
|
325
384
|
data-collection-page-redirect-url={toPublicPath(
|
|
326
385
|
"/c",
|
|
@@ -424,10 +483,18 @@ export const CollectionPage: FC<CollectionPageProps> = ({
|
|
|
424
483
|
<main>
|
|
425
484
|
{items.length === 0 ? (
|
|
426
485
|
<p class="text-muted-foreground">
|
|
427
|
-
{
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
486
|
+
{isAggregate
|
|
487
|
+
? t({
|
|
488
|
+
message:
|
|
489
|
+
"Nothing here yet. Add posts to one of these collections to fill this view.",
|
|
490
|
+
comment:
|
|
491
|
+
"@context: Empty state message on an aggregate collection page",
|
|
492
|
+
})
|
|
493
|
+
: t({
|
|
494
|
+
message:
|
|
495
|
+
"This collection is empty. Add posts from the editor.",
|
|
496
|
+
comment: "@context: Empty state message",
|
|
497
|
+
})}
|
|
431
498
|
</p>
|
|
432
499
|
) : (
|
|
433
500
|
<TimelineFeed
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { FC } from "hono/jsx";
|
|
2
2
|
import { useLingui } from "@lingui/react/macro";
|
|
3
3
|
import type { CollectionDirectoryItem } from "../../types.js";
|
|
4
|
+
import { getDividerCollectionGroup } from "../../lib/collection-groups.js";
|
|
4
5
|
import { formatRelativeAge, toISOString } from "../../lib/time.js";
|
|
5
6
|
import { toPublicPath } from "../../lib/url.js";
|
|
6
7
|
|
|
@@ -39,9 +40,10 @@ export const CollectionDirectory: FC<CollectionDirectoryProps> = ({
|
|
|
39
40
|
|
|
40
41
|
return (
|
|
41
42
|
<div class="collection-directory">
|
|
42
|
-
{items.map((item) => {
|
|
43
|
+
{items.map((item, index) => {
|
|
43
44
|
if (item.type === "divider" || !item.collection) {
|
|
44
45
|
const hasLabel = !!item.label;
|
|
46
|
+
const group = getDividerCollectionGroup(items, index);
|
|
45
47
|
return (
|
|
46
48
|
<div key={item.id} class="collection-directory-divider">
|
|
47
49
|
<div
|
|
@@ -50,9 +52,21 @@ export const CollectionDirectory: FC<CollectionDirectoryProps> = ({
|
|
|
50
52
|
>
|
|
51
53
|
{hasLabel ? (
|
|
52
54
|
<>
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
{group ? (
|
|
56
|
+
<a
|
|
57
|
+
href={toPublicPath(
|
|
58
|
+
`/c/${group.slugExpression}`,
|
|
59
|
+
sitePathPrefix,
|
|
60
|
+
)}
|
|
61
|
+
class="collection-directory-divider-link collection-directory-divider-text"
|
|
62
|
+
>
|
|
63
|
+
{item.label}
|
|
64
|
+
</a>
|
|
65
|
+
) : (
|
|
66
|
+
<span class="collection-directory-divider-text">
|
|
67
|
+
{item.label}
|
|
68
|
+
</span>
|
|
69
|
+
)}
|
|
56
70
|
<hr class="collection-directory-divider-line" />
|
|
57
71
|
</>
|
|
58
72
|
) : (
|