@jant/core 0.3.23 → 0.3.25
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/dist/app.js +50 -26
- package/dist/db/schema.js +72 -47
- package/dist/i18n/locales/en.js +1 -1
- package/dist/i18n/locales/zh-Hans.js +1 -1
- package/dist/i18n/locales/zh-Hant.js +1 -1
- package/dist/index.js +5 -11
- package/dist/lib/constants.js +2 -4
- package/dist/lib/excerpt.js +76 -0
- package/dist/lib/feed.js +18 -7
- package/dist/lib/nav-reorder.js +1 -1
- package/dist/lib/navigation.js +30 -6
- package/dist/lib/pagination.js +44 -0
- package/dist/lib/render.js +7 -11
- package/dist/lib/schemas.js +80 -38
- package/dist/lib/theme.js +4 -4
- package/dist/lib/time.js +56 -1
- package/dist/lib/timeline.js +95 -0
- package/dist/lib/view.js +61 -72
- package/dist/routes/api/collections.js +124 -0
- package/dist/routes/api/nav-items.js +104 -0
- package/dist/routes/api/pages.js +91 -0
- package/dist/routes/api/posts.js +27 -33
- package/dist/routes/api/search.js +4 -5
- package/dist/routes/api/settings.js +68 -0
- package/dist/routes/api/upload.js +13 -13
- package/dist/routes/compose.js +48 -0
- package/dist/routes/dash/collections.js +24 -42
- package/dist/routes/dash/index.js +3 -3
- package/dist/routes/dash/media.js +2 -2
- package/dist/routes/dash/pages.js +440 -106
- package/dist/routes/dash/posts.js +27 -37
- package/dist/routes/dash/redirects.js +2 -2
- package/dist/routes/dash/settings.js +79 -5
- package/dist/routes/feed/rss.js +4 -6
- package/dist/routes/feed/sitemap.js +11 -8
- package/dist/routes/pages/archive.js +13 -15
- package/dist/routes/pages/collection.js +12 -9
- package/dist/routes/pages/collections.js +28 -0
- package/dist/routes/pages/featured.js +32 -0
- package/dist/routes/pages/home.js +19 -68
- package/dist/routes/pages/page.js +57 -29
- package/dist/routes/pages/post.js +7 -17
- package/dist/routes/pages/search.js +5 -9
- package/dist/services/collection.js +52 -64
- package/dist/services/index.js +5 -3
- package/dist/services/navigation.js +29 -53
- package/dist/services/page.js +84 -0
- package/dist/services/post.js +102 -69
- package/dist/services/search.js +24 -18
- package/dist/types.js +24 -40
- package/dist/ui/compose/ComposeDialog.js +452 -0
- package/dist/ui/compose/ComposePrompt.js +55 -0
- package/dist/{theme/components/TypeBadge.js → ui/dash/FormatBadge.js} +3 -15
- package/dist/{theme/components → ui/dash}/PageForm.js +15 -15
- package/dist/{theme/components → ui/dash}/PostForm.js +117 -137
- package/dist/{theme/components → ui/dash}/PostList.js +18 -13
- package/dist/ui/dash/StatusBadge.js +46 -0
- package/dist/{theme/components → ui/dash}/index.js +3 -6
- package/dist/ui/feed/LinkCard.js +72 -0
- package/dist/ui/feed/NoteCard.js +58 -0
- package/dist/{themes/minimal/timeline → ui/feed}/QuoteCard.js +29 -14
- package/dist/{themes/minimal/timeline → ui/feed}/ThreadPreview.js +20 -18
- package/dist/ui/feed/TimelineFeed.js +41 -0
- package/dist/ui/feed/TimelineItem.js +27 -0
- package/dist/{theme → ui}/layouts/BaseLayout.js +10 -0
- package/dist/{theme → ui}/layouts/DashLayout.js +0 -8
- package/dist/ui/layouts/SiteLayout.js +141 -0
- package/dist/{themes/minimal → ui}/pages/ArchivePage.js +37 -50
- package/dist/ui/pages/CollectionPage.js +70 -0
- package/dist/ui/pages/CollectionsPage.js +76 -0
- package/dist/ui/pages/FeaturedPage.js +24 -0
- package/dist/ui/pages/HomePage.js +24 -0
- package/dist/{themes/minimal → ui}/pages/PostPage.js +20 -12
- package/dist/{themes/minimal → ui}/pages/SearchPage.js +19 -18
- package/dist/{themes/minimal → ui}/pages/SinglePage.js +5 -4
- package/dist/ui/shared/MediaGallery.js +35 -0
- package/dist/{theme/components → ui/shared}/Pagination.js +41 -2
- package/dist/{theme/components → ui/shared}/ThreadView.js +3 -3
- package/dist/ui/shared/index.js +5 -0
- package/package.json +2 -9
- package/src/__tests__/helpers/app.ts +4 -0
- package/src/__tests__/helpers/db.ts +53 -73
- package/src/app.tsx +56 -28
- package/src/db/migrations/0005_v2_schema_migration.sql +268 -0
- package/src/db/migrations/0006_rename_slug_to_path.sql +5 -0
- package/src/db/migrations/meta/_journal.json +14 -0
- package/src/db/schema.ts +63 -46
- package/src/i18n/locales/en.po +443 -240
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +443 -240
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +443 -240
- package/src/i18n/locales/zh-Hant.ts +1 -1
- package/src/index.ts +29 -42
- package/src/lib/__tests__/excerpt.test.ts +125 -0
- package/src/lib/__tests__/schemas.test.ts +201 -99
- package/src/lib/__tests__/time.test.ts +62 -0
- package/src/{routes/api → lib}/__tests__/timeline.test.ts +81 -75
- package/src/lib/__tests__/view.test.ts +204 -50
- package/src/lib/constants.ts +2 -4
- package/src/lib/excerpt.ts +87 -0
- package/src/lib/feed.ts +22 -7
- package/src/lib/nav-reorder.ts +1 -1
- package/src/lib/navigation.ts +45 -8
- package/src/lib/pagination.ts +50 -0
- package/src/lib/render.tsx +7 -14
- package/src/lib/schemas.ts +119 -51
- package/src/lib/theme.ts +5 -5
- package/src/lib/time.ts +64 -0
- package/src/lib/timeline.ts +141 -0
- package/src/lib/view.ts +80 -82
- package/src/preset.css +46 -0
- package/src/routes/__tests__/compose.test.ts +199 -0
- package/src/routes/api/__tests__/collections.test.ts +249 -0
- package/src/routes/api/__tests__/nav-items.test.ts +222 -0
- package/src/routes/api/__tests__/pages.test.ts +218 -0
- package/src/routes/api/__tests__/posts.test.ts +50 -108
- package/src/routes/api/__tests__/search.test.ts +2 -3
- package/src/routes/api/__tests__/settings.test.ts +132 -0
- package/src/routes/api/collections.ts +143 -0
- package/src/routes/api/nav-items.ts +115 -0
- package/src/routes/api/pages.ts +101 -0
- package/src/routes/api/posts.ts +28 -28
- package/src/routes/api/search.ts +3 -3
- package/src/routes/api/settings.ts +91 -0
- package/src/routes/api/upload.ts +16 -6
- package/src/routes/compose.ts +63 -0
- package/src/routes/dash/__tests__/pages.test.ts +225 -0
- package/src/routes/dash/collections.tsx +20 -42
- package/src/routes/dash/index.tsx +3 -3
- package/src/routes/dash/media.tsx +2 -2
- package/src/routes/dash/pages.tsx +480 -122
- package/src/routes/dash/posts.tsx +42 -54
- package/src/routes/dash/redirects.tsx +2 -2
- package/src/routes/dash/settings.tsx +83 -5
- package/src/routes/feed/rss.ts +4 -3
- package/src/routes/feed/sitemap.ts +15 -5
- package/src/routes/pages/__tests__/collections.test.ts +94 -0
- package/src/routes/pages/__tests__/featured.test.ts +94 -0
- package/src/routes/pages/archive.tsx +15 -15
- package/src/routes/pages/collection.tsx +16 -9
- package/src/routes/pages/collections.tsx +36 -0
- package/src/routes/pages/featured.tsx +38 -0
- package/src/routes/pages/home.tsx +21 -92
- package/src/routes/pages/page.tsx +62 -27
- package/src/routes/pages/post.tsx +6 -18
- package/src/routes/pages/search.tsx +3 -7
- package/src/services/__tests__/collection.test.ts +257 -158
- package/src/services/__tests__/media.test.ts +18 -18
- package/src/services/__tests__/navigation.test.ts +161 -87
- package/src/services/__tests__/page.test.ts +106 -0
- package/src/services/__tests__/post-timeline.test.ts +92 -88
- package/src/services/__tests__/post.test.ts +432 -197
- package/src/services/__tests__/search.test.ts +19 -25
- package/src/services/collection.ts +71 -113
- package/src/services/index.ts +9 -8
- package/src/services/navigation.ts +38 -71
- package/src/services/page.ts +136 -0
- package/src/services/post.ts +141 -101
- package/src/services/search.ts +38 -27
- package/src/styles/tokens.css +47 -0
- package/src/styles/ui.css +491 -0
- package/src/types.ts +212 -198
- package/src/ui/compose/ComposeDialog.tsx +395 -0
- package/src/ui/compose/ComposePrompt.tsx +55 -0
- package/src/ui/dash/FormatBadge.tsx +28 -0
- package/src/{theme/components → ui/dash}/PageForm.tsx +21 -21
- package/src/{theme/components → ui/dash}/PostForm.tsx +110 -131
- package/src/ui/dash/PostList.tsx +101 -0
- package/src/ui/dash/StatusBadge.tsx +61 -0
- package/src/ui/dash/index.ts +10 -0
- package/src/ui/feed/LinkCard.tsx +72 -0
- package/src/ui/feed/NoteCard.tsx +63 -0
- package/src/ui/feed/QuoteCard.tsx +68 -0
- package/src/ui/feed/ThreadPreview.tsx +48 -0
- package/src/ui/feed/TimelineFeed.tsx +49 -0
- package/src/ui/feed/TimelineItem.tsx +45 -0
- package/src/{theme → ui}/layouts/BaseLayout.tsx +11 -1
- package/src/{theme → ui}/layouts/DashLayout.tsx +0 -10
- package/src/ui/layouts/SiteLayout.tsx +150 -0
- package/src/ui/pages/ArchivePage.tsx +162 -0
- package/src/ui/pages/CollectionPage.tsx +70 -0
- package/src/ui/pages/CollectionsPage.tsx +73 -0
- package/src/ui/pages/FeaturedPage.tsx +31 -0
- package/src/ui/pages/HomePage.tsx +37 -0
- package/src/ui/pages/PostPage.tsx +56 -0
- package/src/{themes/minimal → ui}/pages/SearchPage.tsx +24 -20
- package/src/{themes/minimal → ui}/pages/SinglePage.tsx +5 -5
- package/src/ui/shared/MediaGallery.tsx +59 -0
- package/src/{theme/components → ui/shared}/Pagination.tsx +67 -4
- package/src/{theme/components → ui/shared}/ThreadView.tsx +6 -3
- package/src/ui/shared/__tests__/pagination.test.ts +46 -0
- package/src/ui/shared/index.ts +12 -0
- package/bin/jant.js +0 -185
- package/dist/lib/theme-components.js +0 -49
- package/dist/routes/api/timeline.js +0 -120
- package/dist/routes/dash/navigation.js +0 -288
- package/dist/theme/components/MediaGallery.js +0 -107
- package/dist/theme/components/VisibilityBadge.js +0 -37
- package/dist/theme/index.js +0 -18
- package/dist/theme/layouts/index.js +0 -2
- package/dist/themes/minimal/MinimalSiteLayout.js +0 -83
- package/dist/themes/minimal/index.js +0 -65
- package/dist/themes/minimal/pages/CollectionPage.js +0 -65
- package/dist/themes/minimal/pages/HomePage.js +0 -25
- package/dist/themes/minimal/timeline/ArticleCard.js +0 -36
- package/dist/themes/minimal/timeline/ImageCard.js +0 -67
- package/dist/themes/minimal/timeline/LinkCard.js +0 -47
- package/dist/themes/minimal/timeline/NoteCard.js +0 -34
- package/dist/themes/minimal/timeline/TimelineFeed.js +0 -48
- package/dist/themes/minimal/timeline/TimelineItem.js +0 -44
- package/src/lib/__tests__/theme-components.test.ts +0 -126
- package/src/lib/theme-components.ts +0 -68
- package/src/routes/api/timeline.tsx +0 -159
- package/src/routes/dash/navigation.tsx +0 -316
- package/src/theme/components/MediaGallery.tsx +0 -128
- package/src/theme/components/PostList.tsx +0 -92
- package/src/theme/components/TypeBadge.tsx +0 -37
- package/src/theme/components/VisibilityBadge.tsx +0 -45
- package/src/theme/components/index.ts +0 -23
- package/src/theme/index.ts +0 -22
- package/src/theme/layouts/index.ts +0 -7
- package/src/themes/minimal/MinimalSiteLayout.tsx +0 -100
- package/src/themes/minimal/index.ts +0 -83
- package/src/themes/minimal/pages/ArchivePage.tsx +0 -157
- package/src/themes/minimal/pages/CollectionPage.tsx +0 -60
- package/src/themes/minimal/pages/HomePage.tsx +0 -41
- package/src/themes/minimal/pages/PostPage.tsx +0 -43
- package/src/themes/minimal/timeline/ArticleCard.tsx +0 -37
- package/src/themes/minimal/timeline/ImageCard.tsx +0 -63
- package/src/themes/minimal/timeline/LinkCard.tsx +0 -48
- package/src/themes/minimal/timeline/NoteCard.tsx +0 -35
- package/src/themes/minimal/timeline/QuoteCard.tsx +0 -49
- package/src/themes/minimal/timeline/ThreadPreview.tsx +0 -47
- package/src/themes/minimal/timeline/TimelineFeed.tsx +0 -57
- package/src/themes/minimal/timeline/TimelineItem.tsx +0 -75
- /package/dist/{theme → ui}/color-themes.js +0 -0
- /package/dist/{theme/components → ui/dash}/ActionButtons.js +0 -0
- /package/dist/{theme/components → ui/dash}/CrudPageHeader.js +0 -0
- /package/dist/{theme/components → ui/dash}/DangerZone.js +0 -0
- /package/dist/{theme/components → ui/dash}/ListItemRow.js +0 -0
- /package/dist/{theme/components → ui/shared}/EmptyState.js +0 -0
- /package/src/{theme → ui}/color-themes.ts +0 -0
- /package/src/{theme/components → ui/dash}/ActionButtons.tsx +0 -0
- /package/src/{theme/components → ui/dash}/CrudPageHeader.tsx +0 -0
- /package/src/{theme/components → ui/dash}/DangerZone.tsx +0 -0
- /package/src/{theme/components → ui/dash}/ListItemRow.tsx +0 -0
- /package/src/{theme/components → ui/shared}/EmptyState.tsx +0 -0
|
@@ -4,8 +4,8 @@ import { getSiteName } from "../../lib/config.js";
|
|
|
4
4
|
* Dashboard Posts Routes
|
|
5
5
|
*/ import { Hono } from "hono";
|
|
6
6
|
import { useLingui as $_useLingui } from "@jant/core/i18n";
|
|
7
|
-
import { DashLayout } from "../../
|
|
8
|
-
import { PostForm, PostList, CrudPageHeader, ActionButtons } from "../../
|
|
7
|
+
import { DashLayout } from "../../ui/layouts/DashLayout.js";
|
|
8
|
+
import { PostForm, PostList, CrudPageHeader, ActionButtons } from "../../ui/dash/index.js";
|
|
9
9
|
import * as sqid from "../../lib/sqid.js";
|
|
10
10
|
import { dsRedirect } from "../../lib/sse.js";
|
|
11
11
|
export const postsRoutes = new Hono();
|
|
@@ -51,12 +51,7 @@ function NewPostContent({ collections }) {
|
|
|
51
51
|
// List posts
|
|
52
52
|
postsRoutes.get("/", async (c)=>{
|
|
53
53
|
const posts = await c.var.services.posts.list({
|
|
54
|
-
|
|
55
|
-
"featured",
|
|
56
|
-
"quiet",
|
|
57
|
-
"unlisted",
|
|
58
|
-
"draft"
|
|
59
|
-
]
|
|
54
|
+
excludeReplies: true
|
|
60
55
|
});
|
|
61
56
|
const siteName = await getSiteName(c);
|
|
62
57
|
return c.html(/*#__PURE__*/ _jsx(DashLayout, {
|
|
@@ -87,22 +82,21 @@ postsRoutes.get("/new", async (c)=>{
|
|
|
87
82
|
postsRoutes.post("/", async (c)=>{
|
|
88
83
|
const body = await c.req.json();
|
|
89
84
|
const post = await c.var.services.posts.create({
|
|
90
|
-
|
|
85
|
+
format: body.format,
|
|
91
86
|
title: body.title || undefined,
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
87
|
+
body: body.body,
|
|
88
|
+
status: body.status,
|
|
89
|
+
featured: body.featured,
|
|
90
|
+
pinned: body.pinned,
|
|
91
|
+
url: body.url || undefined,
|
|
92
|
+
quoteText: body.quoteText || undefined,
|
|
93
|
+
rating: body.rating || undefined,
|
|
94
|
+
collectionId: body.collectionId || undefined
|
|
97
95
|
});
|
|
98
96
|
// Attach media if provided
|
|
99
97
|
if (body.mediaIds && body.mediaIds.length > 0) {
|
|
100
98
|
await c.var.services.media.attachToPost(post.id, body.mediaIds);
|
|
101
99
|
}
|
|
102
|
-
// Sync collection associations
|
|
103
|
-
if (body.collectionIds) {
|
|
104
|
-
await c.var.services.collections.syncPostCollections(post.id, body.collectionIds);
|
|
105
|
-
}
|
|
106
100
|
return dsRedirect(`/dash/posts/${sqid.encode(post.id)}`);
|
|
107
101
|
});
|
|
108
102
|
function ViewPostContent({ post }) {
|
|
@@ -111,6 +105,7 @@ function ViewPostContent({ post }) {
|
|
|
111
105
|
id: "y28hnO",
|
|
112
106
|
message: "Post"
|
|
113
107
|
});
|
|
108
|
+
const permalink = post.path ? `/${post.path}` : `/p/${sqid.encode(post.id)}`;
|
|
114
109
|
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
115
110
|
children: [
|
|
116
111
|
/*#__PURE__*/ _jsxs("div", {
|
|
@@ -126,7 +121,7 @@ function ViewPostContent({ post }) {
|
|
|
126
121
|
id: "ePK91l",
|
|
127
122
|
message: "Edit"
|
|
128
123
|
}),
|
|
129
|
-
viewHref:
|
|
124
|
+
viewHref: permalink,
|
|
130
125
|
viewLabel: $__i18n._({
|
|
131
126
|
id: "jpctdh",
|
|
132
127
|
message: "View"
|
|
@@ -140,7 +135,7 @@ function ViewPostContent({ post }) {
|
|
|
140
135
|
children: /*#__PURE__*/ _jsx("div", {
|
|
141
136
|
class: "prose",
|
|
142
137
|
dangerouslySetInnerHTML: {
|
|
143
|
-
__html: post.
|
|
138
|
+
__html: post.bodyHtml || ""
|
|
144
139
|
}
|
|
145
140
|
})
|
|
146
141
|
})
|
|
@@ -148,7 +143,7 @@ function ViewPostContent({ post }) {
|
|
|
148
143
|
]
|
|
149
144
|
});
|
|
150
145
|
}
|
|
151
|
-
function EditPostContent({ post, mediaAttachments, r2PublicUrl, imageTransformUrl, s3PublicUrl, collections
|
|
146
|
+
function EditPostContent({ post, mediaAttachments, r2PublicUrl, imageTransformUrl, s3PublicUrl, collections }) {
|
|
152
147
|
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
153
148
|
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
154
149
|
children: [
|
|
@@ -166,8 +161,7 @@ function EditPostContent({ post, mediaAttachments, r2PublicUrl, imageTransformUr
|
|
|
166
161
|
r2PublicUrl: r2PublicUrl,
|
|
167
162
|
imageTransformUrl: imageTransformUrl,
|
|
168
163
|
s3PublicUrl: s3PublicUrl,
|
|
169
|
-
collections: collections
|
|
170
|
-
postCollectionIds: postCollectionIds
|
|
164
|
+
collections: collections
|
|
171
165
|
})
|
|
172
166
|
]
|
|
173
167
|
});
|
|
@@ -202,8 +196,6 @@ postsRoutes.get("/:id/edit", async (c)=>{
|
|
|
202
196
|
const imageTransformUrl = c.env.IMAGE_TRANSFORM_URL;
|
|
203
197
|
const s3PublicUrl = c.env.S3_PUBLIC_URL;
|
|
204
198
|
const collections = await c.var.services.collections.list();
|
|
205
|
-
const postCollections = await c.var.services.collections.getCollectionsForPost(post.id);
|
|
206
|
-
const postCollectionIds = postCollections.map((col)=>col.id);
|
|
207
199
|
return c.html(/*#__PURE__*/ _jsx(DashLayout, {
|
|
208
200
|
c: c,
|
|
209
201
|
title: `Edit: ${post.title || "Post"}`,
|
|
@@ -215,8 +207,7 @@ postsRoutes.get("/:id/edit", async (c)=>{
|
|
|
215
207
|
r2PublicUrl: r2PublicUrl,
|
|
216
208
|
imageTransformUrl: imageTransformUrl,
|
|
217
209
|
s3PublicUrl: s3PublicUrl,
|
|
218
|
-
collections: collections
|
|
219
|
-
postCollectionIds: postCollectionIds
|
|
210
|
+
collections: collections
|
|
220
211
|
})
|
|
221
212
|
}));
|
|
222
213
|
});
|
|
@@ -226,22 +217,21 @@ postsRoutes.post("/:id", async (c)=>{
|
|
|
226
217
|
if (!id) return c.notFound();
|
|
227
218
|
const body = await c.req.json();
|
|
228
219
|
await c.var.services.posts.update(id, {
|
|
229
|
-
|
|
220
|
+
format: body.format,
|
|
230
221
|
title: body.title || null,
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
222
|
+
body: body.body || null,
|
|
223
|
+
status: body.status,
|
|
224
|
+
featured: body.featured,
|
|
225
|
+
pinned: body.pinned,
|
|
226
|
+
url: body.url || null,
|
|
227
|
+
quoteText: body.quoteText || null,
|
|
228
|
+
rating: body.rating || null,
|
|
229
|
+
collectionId: body.collectionId || null
|
|
236
230
|
});
|
|
237
231
|
// Update media attachments if provided
|
|
238
232
|
if (body.mediaIds !== undefined) {
|
|
239
233
|
await c.var.services.media.attachToPost(id, body.mediaIds);
|
|
240
234
|
}
|
|
241
|
-
// Sync collection associations
|
|
242
|
-
if (body.collectionIds !== undefined) {
|
|
243
|
-
await c.var.services.collections.syncPostCollections(id, body.collectionIds);
|
|
244
|
-
}
|
|
245
235
|
return dsRedirect(`/dash/posts/${sqid.encode(id)}`);
|
|
246
236
|
});
|
|
247
237
|
// Delete post
|
|
@@ -4,8 +4,8 @@ import { getSiteName } from "../../lib/config.js";
|
|
|
4
4
|
* Dashboard Redirects Routes
|
|
5
5
|
*/ import { Hono } from "hono";
|
|
6
6
|
import { useLingui as $_useLingui } from "@jant/core/i18n";
|
|
7
|
-
import { DashLayout } from "../../
|
|
8
|
-
import { EmptyState, ListItemRow, ActionButtons, CrudPageHeader } from "../../
|
|
7
|
+
import { DashLayout } from "../../ui/layouts/DashLayout.js";
|
|
8
|
+
import { EmptyState, ListItemRow, ActionButtons, CrudPageHeader } from "../../ui/dash/index.js";
|
|
9
9
|
import { dsRedirect } from "../../lib/sse.js";
|
|
10
10
|
export const redirectsRoutes = new Hono();
|
|
11
11
|
function RedirectsListContent({ redirects }) {
|
|
@@ -5,7 +5,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "hono/jsx/jsx-
|
|
|
5
5
|
* Sub-pages: General, Appearance, Account
|
|
6
6
|
*/ import { Hono } from "hono";
|
|
7
7
|
import { useLingui as $_useLingui } from "@jant/core/i18n";
|
|
8
|
-
import { DashLayout } from "../../
|
|
8
|
+
import { DashLayout } from "../../ui/layouts/DashLayout.js";
|
|
9
9
|
import { sse, dsRedirect, dsToast } from "../../lib/sse.js";
|
|
10
10
|
import { getSiteLanguage, getSiteName, getConfigFallback } from "../../lib/config.js";
|
|
11
11
|
import { SETTINGS_KEYS } from "../../lib/constants.js";
|
|
@@ -272,11 +272,14 @@ function ThemeCard({ theme, selected }) {
|
|
|
272
272
|
})
|
|
273
273
|
});
|
|
274
274
|
}
|
|
275
|
-
function AppearanceContent({ themes, currentThemeId }) {
|
|
275
|
+
function AppearanceContent({ themes, currentThemeId, customCSS }) {
|
|
276
276
|
const { i18n: $__i18n, _: $__ } = $_useLingui();
|
|
277
|
-
const
|
|
277
|
+
const themeSignals = JSON.stringify({
|
|
278
278
|
theme: currentThemeId
|
|
279
279
|
}).replace(/</g, "\\u003c");
|
|
280
|
+
const cssSignals = JSON.stringify({
|
|
281
|
+
customCSS
|
|
282
|
+
}).replace(/</g, "\\u003c");
|
|
280
283
|
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
281
284
|
children: [
|
|
282
285
|
/*#__PURE__*/ _jsx("h1", {
|
|
@@ -290,7 +293,7 @@ function AppearanceContent({ themes, currentThemeId }) {
|
|
|
290
293
|
currentTab: "appearance"
|
|
291
294
|
}),
|
|
292
295
|
/*#__PURE__*/ _jsx("div", {
|
|
293
|
-
"data-signals":
|
|
296
|
+
"data-signals": themeSignals,
|
|
294
297
|
"data-on:change": "@post('/dash/settings/appearance')",
|
|
295
298
|
class: "max-w-3xl",
|
|
296
299
|
children: /*#__PURE__*/ _jsxs("fieldset", {
|
|
@@ -318,6 +321,63 @@ function AppearanceContent({ themes, currentThemeId }) {
|
|
|
318
321
|
})
|
|
319
322
|
]
|
|
320
323
|
})
|
|
324
|
+
}),
|
|
325
|
+
/*#__PURE__*/ _jsxs("form", {
|
|
326
|
+
"data-signals": cssSignals,
|
|
327
|
+
"data-on:submit__prevent": "@post('/dash/settings/custom-css')",
|
|
328
|
+
"data-indicator": "_cssLoading",
|
|
329
|
+
class: "max-w-3xl mt-8",
|
|
330
|
+
children: [
|
|
331
|
+
/*#__PURE__*/ _jsxs("fieldset", {
|
|
332
|
+
children: [
|
|
333
|
+
/*#__PURE__*/ _jsx("legend", {
|
|
334
|
+
class: "text-lg font-semibold",
|
|
335
|
+
children: $__i18n._({
|
|
336
|
+
id: "9+vGLh",
|
|
337
|
+
message: "Custom CSS"
|
|
338
|
+
})
|
|
339
|
+
}),
|
|
340
|
+
/*#__PURE__*/ _jsx("p", {
|
|
341
|
+
class: "text-sm text-muted-foreground mb-4",
|
|
342
|
+
children: $__i18n._({
|
|
343
|
+
id: "vmQmHx",
|
|
344
|
+
message: "Add custom CSS to override any styles. Use data attributes like [data-page], [data-post], [data-format] to target specific elements."
|
|
345
|
+
})
|
|
346
|
+
}),
|
|
347
|
+
/*#__PURE__*/ _jsx("textarea", {
|
|
348
|
+
"data-bind": "customCSS",
|
|
349
|
+
class: "textarea font-mono text-sm min-h-32",
|
|
350
|
+
rows: 8,
|
|
351
|
+
placeholder: $__i18n._({
|
|
352
|
+
id: "wc+17X",
|
|
353
|
+
message: "/* Your custom CSS here */"
|
|
354
|
+
}),
|
|
355
|
+
children: customCSS
|
|
356
|
+
})
|
|
357
|
+
]
|
|
358
|
+
}),
|
|
359
|
+
/*#__PURE__*/ _jsxs("button", {
|
|
360
|
+
type: "submit",
|
|
361
|
+
class: "btn mt-4",
|
|
362
|
+
"data-attr-disabled": "$_cssLoading",
|
|
363
|
+
children: [
|
|
364
|
+
/*#__PURE__*/ _jsx("span", {
|
|
365
|
+
"data-show": "!$_cssLoading",
|
|
366
|
+
children: $__i18n._({
|
|
367
|
+
id: "NU2Fqi",
|
|
368
|
+
message: "Save CSS"
|
|
369
|
+
})
|
|
370
|
+
}),
|
|
371
|
+
/*#__PURE__*/ _jsx("span", {
|
|
372
|
+
"data-show": "$_cssLoading",
|
|
373
|
+
children: $__i18n._({
|
|
374
|
+
id: "k1ifdL",
|
|
375
|
+
message: "Processing..."
|
|
376
|
+
})
|
|
377
|
+
})
|
|
378
|
+
]
|
|
379
|
+
})
|
|
380
|
+
]
|
|
321
381
|
})
|
|
322
382
|
]
|
|
323
383
|
});
|
|
@@ -583,6 +643,7 @@ settingsRoutes.get("/appearance", async (c)=>{
|
|
|
583
643
|
const { settings } = c.var.services;
|
|
584
644
|
const siteName = await getSiteName(c);
|
|
585
645
|
const currentThemeId = await settings.get(SETTINGS_KEYS.THEME) ?? "default";
|
|
646
|
+
const customCSS = await settings.get(SETTINGS_KEYS.CUSTOM_CSS) ?? "";
|
|
586
647
|
const themes = getAvailableThemes(c.var.config);
|
|
587
648
|
const saved = c.req.query("saved") !== undefined;
|
|
588
649
|
return c.html(/*#__PURE__*/ _jsx(DashLayout, {
|
|
@@ -595,7 +656,8 @@ settingsRoutes.get("/appearance", async (c)=>{
|
|
|
595
656
|
} : undefined,
|
|
596
657
|
children: /*#__PURE__*/ _jsx(AppearanceContent, {
|
|
597
658
|
themes: themes,
|
|
598
|
-
currentThemeId: currentThemeId
|
|
659
|
+
currentThemeId: currentThemeId,
|
|
660
|
+
customCSS: customCSS
|
|
599
661
|
})
|
|
600
662
|
}));
|
|
601
663
|
});
|
|
@@ -615,6 +677,18 @@ settingsRoutes.post("/appearance", async (c)=>{
|
|
|
615
677
|
}
|
|
616
678
|
return dsRedirect("/dash/settings/appearance?saved");
|
|
617
679
|
});
|
|
680
|
+
// Save custom CSS
|
|
681
|
+
settingsRoutes.post("/custom-css", async (c)=>{
|
|
682
|
+
const body = await c.req.json();
|
|
683
|
+
const { settings } = c.var.services;
|
|
684
|
+
const css = body.customCSS?.trim() ?? "";
|
|
685
|
+
if (css) {
|
|
686
|
+
await settings.set(SETTINGS_KEYS.CUSTOM_CSS, css);
|
|
687
|
+
} else {
|
|
688
|
+
await settings.remove(SETTINGS_KEYS.CUSTOM_CSS);
|
|
689
|
+
}
|
|
690
|
+
return dsToast("Custom CSS saved successfully.");
|
|
691
|
+
});
|
|
618
692
|
// Account page
|
|
619
693
|
settingsRoutes.get("/account", async (c)=>{
|
|
620
694
|
const siteName = await getSiteName(c);
|
package/dist/routes/feed/rss.js
CHANGED
|
@@ -15,10 +15,8 @@ export const rssRoutes = new Hono();
|
|
|
15
15
|
const siteUrl = c.env.SITE_URL;
|
|
16
16
|
const siteLanguage = await getSiteLanguage(c);
|
|
17
17
|
const posts = await c.var.services.posts.list({
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"quiet"
|
|
21
|
-
],
|
|
18
|
+
status: "published",
|
|
19
|
+
excludeReplies: true,
|
|
22
20
|
limit: 50
|
|
23
21
|
});
|
|
24
22
|
// Batch load media for enclosures
|
|
@@ -42,7 +40,7 @@ export const rssRoutes = new Hono();
|
|
|
42
40
|
// RSS 2.0 Feed - main feed at /feed
|
|
43
41
|
rssRoutes.get("/", async (c)=>{
|
|
44
42
|
const feedData = await buildFeedData(c);
|
|
45
|
-
const renderer = c.var.config.
|
|
43
|
+
const renderer = c.var.config.feed?.rss ?? defaultRssRenderer;
|
|
46
44
|
const xml = renderer(feedData);
|
|
47
45
|
return new Response(xml, {
|
|
48
46
|
headers: {
|
|
@@ -53,7 +51,7 @@ rssRoutes.get("/", async (c)=>{
|
|
|
53
51
|
// Atom Feed
|
|
54
52
|
rssRoutes.get("/atom.xml", async (c)=>{
|
|
55
53
|
const feedData = await buildFeedData(c);
|
|
56
|
-
const renderer = c.var.config.
|
|
54
|
+
const renderer = c.var.config.feed?.atom ?? defaultAtomRenderer;
|
|
57
55
|
const xml = renderer(feedData);
|
|
58
56
|
return new Response(xml, {
|
|
59
57
|
headers: {
|
|
@@ -2,25 +2,28 @@
|
|
|
2
2
|
* Sitemap Routes
|
|
3
3
|
*/ import { Hono } from "hono";
|
|
4
4
|
import { defaultSitemapRenderer } from "../../lib/feed.js";
|
|
5
|
-
import { createMediaContext, toPostViewsFromPosts } from "../../lib/view.js";
|
|
5
|
+
import { createMediaContext, toPostViewsFromPosts, toPageView } from "../../lib/view.js";
|
|
6
6
|
export const sitemapRoutes = new Hono();
|
|
7
7
|
// XML Sitemap
|
|
8
8
|
sitemapRoutes.get("/sitemap.xml", async (c)=>{
|
|
9
9
|
const siteUrl = c.env.SITE_URL;
|
|
10
10
|
const posts = await c.var.services.posts.list({
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"quiet"
|
|
14
|
-
],
|
|
11
|
+
status: "published",
|
|
12
|
+
excludeReplies: true,
|
|
15
13
|
limit: 1000
|
|
16
14
|
});
|
|
17
|
-
//
|
|
15
|
+
// Fetch published pages
|
|
16
|
+
const allPages = await c.var.services.pages.list();
|
|
17
|
+
const publishedPages = allPages.filter((p)=>p.status === "published");
|
|
18
|
+
// Transform to View Models
|
|
18
19
|
const mediaCtx = createMediaContext(c);
|
|
19
20
|
const postViews = toPostViewsFromPosts(posts, mediaCtx);
|
|
20
|
-
const
|
|
21
|
+
const pageViews = publishedPages.map(toPageView);
|
|
22
|
+
const renderer = c.var.config.feed?.sitemap ?? defaultSitemapRenderer;
|
|
21
23
|
const xml = renderer({
|
|
22
24
|
siteUrl,
|
|
23
|
-
posts: postViews
|
|
25
|
+
posts: postViews,
|
|
26
|
+
pages: pageViews
|
|
24
27
|
});
|
|
25
28
|
return new Response(xml, {
|
|
26
29
|
headers: {
|
|
@@ -2,10 +2,10 @@ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
|
|
|
2
2
|
/**
|
|
3
3
|
* Archive Page Route
|
|
4
4
|
*
|
|
5
|
-
* Shows all posts, optionally filtered by
|
|
5
|
+
* Shows all posts, optionally filtered by format or featured status
|
|
6
6
|
*/ import { Hono } from "hono";
|
|
7
|
-
import {
|
|
8
|
-
import { ArchivePage
|
|
7
|
+
import { FORMATS } from "../../types.js";
|
|
8
|
+
import { ArchivePage } from "../../ui/pages/ArchivePage.js";
|
|
9
9
|
import { getNavigationData } from "../../lib/navigation.js";
|
|
10
10
|
import { renderPublicPage } from "../../lib/render.js";
|
|
11
11
|
import { createMediaContext, toArchiveGroups } from "../../lib/view.js";
|
|
@@ -13,19 +13,19 @@ const PAGE_SIZE = 50;
|
|
|
13
13
|
export const archiveRoutes = new Hono();
|
|
14
14
|
// Archive page - all posts
|
|
15
15
|
archiveRoutes.get("/", async (c)=>{
|
|
16
|
-
const
|
|
17
|
-
const
|
|
16
|
+
const formatParam = c.req.query("format");
|
|
17
|
+
const format = formatParam && FORMATS.includes(formatParam) ? formatParam : undefined;
|
|
18
|
+
const featuredParam = c.req.query("featured");
|
|
19
|
+
const featured = featuredParam === "true" ? true : undefined;
|
|
18
20
|
// Parse cursor
|
|
19
21
|
const cursorParam = c.req.query("cursor");
|
|
20
22
|
const cursor = cursorParam ? parseInt(cursorParam, 10) : undefined;
|
|
21
23
|
const navData = await getNavigationData(c);
|
|
22
24
|
// Fetch one extra to check for more
|
|
23
25
|
const posts = await c.var.services.posts.list({
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"quiet"
|
|
28
|
-
],
|
|
26
|
+
format,
|
|
27
|
+
status: "published",
|
|
28
|
+
featured,
|
|
29
29
|
excludeReplies: true,
|
|
30
30
|
cursor,
|
|
31
31
|
limit: PAGE_SIZE + 1
|
|
@@ -48,17 +48,15 @@ archiveRoutes.get("/", async (c)=>{
|
|
|
48
48
|
// Transform to View Models
|
|
49
49
|
const mediaCtx = createMediaContext(c);
|
|
50
50
|
const groups = toArchiveGroups(grouped, mediaCtx);
|
|
51
|
-
const components = c.var.config.theme?.components;
|
|
52
|
-
const Page = components?.ArchivePage ?? DefaultArchivePage;
|
|
53
51
|
return renderPublicPage(c, {
|
|
54
52
|
title: `Archive - ${navData.siteName}`,
|
|
55
53
|
navData,
|
|
56
|
-
content: /*#__PURE__*/ _jsx(
|
|
54
|
+
content: /*#__PURE__*/ _jsx(ArchivePage, {
|
|
57
55
|
groups: groups,
|
|
58
56
|
hasMore: hasMore,
|
|
59
57
|
nextCursor: nextCursor,
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
format: format,
|
|
59
|
+
featured: featured
|
|
62
60
|
})
|
|
63
61
|
});
|
|
64
62
|
});
|
|
@@ -2,30 +2,33 @@ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
|
|
|
2
2
|
/**
|
|
3
3
|
* Collection Page Route
|
|
4
4
|
*/ import { Hono } from "hono";
|
|
5
|
-
import { CollectionPage
|
|
5
|
+
import { CollectionPage } from "../../ui/pages/CollectionPage.js";
|
|
6
6
|
import { getNavigationData } from "../../lib/navigation.js";
|
|
7
7
|
import { renderPublicPage } from "../../lib/render.js";
|
|
8
8
|
import { createMediaContext, toPostViewsFromPosts } from "../../lib/view.js";
|
|
9
9
|
export const collectionRoutes = new Hono();
|
|
10
|
-
collectionRoutes.get("/:
|
|
11
|
-
const
|
|
12
|
-
const collection = await c.var.services.collections.
|
|
10
|
+
collectionRoutes.get("/:slug", async (c)=>{
|
|
11
|
+
const slug = c.req.param("slug");
|
|
12
|
+
const collection = await c.var.services.collections.getBySlug(slug);
|
|
13
13
|
if (!collection) return c.notFound();
|
|
14
|
-
|
|
14
|
+
// Fetch posts in this collection
|
|
15
|
+
const posts = await c.var.services.posts.list({
|
|
16
|
+
collectionId: collection.id,
|
|
17
|
+
status: "published",
|
|
18
|
+
excludeReplies: true
|
|
19
|
+
});
|
|
15
20
|
const navData = await getNavigationData(c);
|
|
16
21
|
// Transform to View Models
|
|
17
22
|
const mediaCtx = createMediaContext(c);
|
|
18
23
|
const postViews = toPostViewsFromPosts(posts, mediaCtx);
|
|
19
|
-
const components = c.var.config.theme?.components;
|
|
20
|
-
const Page = components?.CollectionPage ?? DefaultCollectionPage;
|
|
21
24
|
return renderPublicPage(c, {
|
|
22
25
|
title: `${collection.title} - ${navData.siteName}`,
|
|
23
26
|
description: collection.description ?? undefined,
|
|
24
27
|
navData,
|
|
25
|
-
content: /*#__PURE__*/ _jsx(
|
|
28
|
+
content: /*#__PURE__*/ _jsx(CollectionPage, {
|
|
26
29
|
collection: collection,
|
|
27
30
|
posts: postViews,
|
|
28
|
-
|
|
31
|
+
hasMore: false
|
|
29
32
|
})
|
|
30
33
|
});
|
|
31
34
|
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx as _jsx } from "hono/jsx/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Collections Listing Page Route
|
|
4
|
+
*
|
|
5
|
+
* Lists all collections with their post counts.
|
|
6
|
+
*/ import { Hono } from "hono";
|
|
7
|
+
import { getNavigationData } from "../../lib/navigation.js";
|
|
8
|
+
import { renderPublicPage } from "../../lib/render.js";
|
|
9
|
+
import { CollectionsPage } from "../../ui/pages/CollectionsPage.js";
|
|
10
|
+
export const collectionsPageRoutes = new Hono();
|
|
11
|
+
collectionsPageRoutes.get("/", async (c)=>{
|
|
12
|
+
const [allCollections, postCounts] = await Promise.all([
|
|
13
|
+
c.var.services.collections.list(),
|
|
14
|
+
c.var.services.collections.getPostCounts()
|
|
15
|
+
]);
|
|
16
|
+
const collections = allCollections.map((col)=>({
|
|
17
|
+
...col,
|
|
18
|
+
postCount: postCounts.get(col.id) ?? 0
|
|
19
|
+
}));
|
|
20
|
+
const navData = await getNavigationData(c);
|
|
21
|
+
return renderPublicPage(c, {
|
|
22
|
+
title: `Collections - ${navData.siteName}`,
|
|
23
|
+
navData,
|
|
24
|
+
content: /*#__PURE__*/ _jsx(CollectionsPage, {
|
|
25
|
+
collections: collections
|
|
26
|
+
})
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx as _jsx } from "hono/jsx/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Featured Page Route
|
|
4
|
+
*
|
|
5
|
+
* Shows featured posts as a timeline feed.
|
|
6
|
+
*/ import { Hono } from "hono";
|
|
7
|
+
import { getNavigationData } from "../../lib/navigation.js";
|
|
8
|
+
import { renderPublicPage } from "../../lib/render.js";
|
|
9
|
+
import { createMediaContext, toPostViewsFromPosts } from "../../lib/view.js";
|
|
10
|
+
import { FeaturedPage } from "../../ui/pages/FeaturedPage.js";
|
|
11
|
+
export const featuredRoutes = new Hono();
|
|
12
|
+
featuredRoutes.get("/", async (c)=>{
|
|
13
|
+
const posts = await c.var.services.posts.list({
|
|
14
|
+
featured: true,
|
|
15
|
+
status: "published",
|
|
16
|
+
excludeReplies: true
|
|
17
|
+
});
|
|
18
|
+
const navData = await getNavigationData(c);
|
|
19
|
+
const mediaCtx = createMediaContext(c);
|
|
20
|
+
const postViews = toPostViewsFromPosts(posts, mediaCtx);
|
|
21
|
+
// Convert to timeline items (simple — no thread previews)
|
|
22
|
+
const items = postViews.map((post)=>({
|
|
23
|
+
post
|
|
24
|
+
}));
|
|
25
|
+
return renderPublicPage(c, {
|
|
26
|
+
title: `Featured - ${navData.siteName}`,
|
|
27
|
+
navData,
|
|
28
|
+
content: /*#__PURE__*/ _jsx(FeaturedPage, {
|
|
29
|
+
items: items
|
|
30
|
+
})
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -3,86 +3,37 @@ import { jsx as _jsx } from "hono/jsx/jsx-runtime";
|
|
|
3
3
|
* Home Page Route
|
|
4
4
|
*
|
|
5
5
|
* Timeline feed with per-type card components and thread previews.
|
|
6
|
+
* Uses page-based pagination.
|
|
6
7
|
*/ import { Hono } from "hono";
|
|
7
|
-
import { buildMediaMap } from "../../lib/media-helpers.js";
|
|
8
8
|
import { getNavigationData } from "../../lib/navigation.js";
|
|
9
9
|
import { renderPublicPage } from "../../lib/render.js";
|
|
10
|
-
import {
|
|
11
|
-
import { createMediaContext,
|
|
12
|
-
|
|
10
|
+
import { assembleTimeline } from "../../lib/timeline.js";
|
|
11
|
+
import { createMediaContext, toPostViewsFromPosts } from "../../lib/view.js";
|
|
12
|
+
import { HomePage } from "../../ui/pages/HomePage.js";
|
|
13
13
|
export const homeRoutes = new Hono();
|
|
14
14
|
homeRoutes.get("/", async (c)=>{
|
|
15
|
+
const pageParam = c.req.query("page");
|
|
16
|
+
const page = pageParam ? Math.max(1, parseInt(pageParam, 10) || 1) : 1;
|
|
17
|
+
const { items, currentPage, totalPages } = await assembleTimeline(c, {
|
|
18
|
+
page
|
|
19
|
+
});
|
|
15
20
|
const navData = await getNavigationData(c);
|
|
16
|
-
// Fetch
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
],
|
|
22
|
-
excludeReplies: true,
|
|
23
|
-
excludeTypes: [
|
|
24
|
-
"page"
|
|
25
|
-
],
|
|
26
|
-
limit: PAGE_SIZE + 1
|
|
21
|
+
// Fetch pinned posts
|
|
22
|
+
const pinnedPosts = await c.var.services.posts.list({
|
|
23
|
+
pinned: true,
|
|
24
|
+
status: "published",
|
|
25
|
+
excludeReplies: true
|
|
27
26
|
});
|
|
28
|
-
const hasMore = posts.length > PAGE_SIZE;
|
|
29
|
-
const displayPosts = hasMore ? posts.slice(0, PAGE_SIZE) : posts;
|
|
30
|
-
// Batch load media attachments
|
|
31
|
-
const postIds = displayPosts.map((p)=>p.id);
|
|
32
|
-
const rawMediaMap = await c.var.services.media.getByPostIds(postIds);
|
|
33
27
|
const mediaCtx = createMediaContext(c);
|
|
34
|
-
const
|
|
35
|
-
// Get reply counts to identify thread roots
|
|
36
|
-
const replyCounts = await c.var.services.posts.getReplyCounts(postIds);
|
|
37
|
-
const threadRootIds = postIds.filter((id)=>(replyCounts.get(id) ?? 0) > 0);
|
|
38
|
-
// Batch load thread previews
|
|
39
|
-
const threadPreviews = await c.var.services.posts.getThreadPreviews(threadRootIds, 3);
|
|
40
|
-
// Batch load media for preview replies
|
|
41
|
-
const previewReplyIds = [];
|
|
42
|
-
for (const replies of threadPreviews.values()){
|
|
43
|
-
for (const reply of replies){
|
|
44
|
-
previewReplyIds.push(reply.id);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
const previewMediaMap = previewReplyIds.length > 0 ? buildMediaMap(await c.var.services.media.getByPostIds(previewReplyIds), mediaCtx.r2PublicUrl, mediaCtx.imageTransformUrl, mediaCtx.s3PublicUrl) : new Map();
|
|
48
|
-
// Assemble timeline items with View Models
|
|
49
|
-
const items = displayPosts.map((post)=>{
|
|
50
|
-
const postView = toPostView({
|
|
51
|
-
...post,
|
|
52
|
-
mediaAttachments: mediaMap.get(post.id) ?? []
|
|
53
|
-
}, mediaCtx);
|
|
54
|
-
const replyCount = replyCounts.get(post.id) ?? 0;
|
|
55
|
-
const previewReplies = threadPreviews.get(post.id);
|
|
56
|
-
if (replyCount > 0 && previewReplies) {
|
|
57
|
-
return {
|
|
58
|
-
post: postView,
|
|
59
|
-
threadPreview: {
|
|
60
|
-
replies: toPostViews(previewReplies.map((r)=>({
|
|
61
|
-
...r,
|
|
62
|
-
mediaAttachments: previewMediaMap.get(r.id) ?? []
|
|
63
|
-
})), mediaCtx),
|
|
64
|
-
totalReplyCount: replyCount
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
return {
|
|
69
|
-
post: postView
|
|
70
|
-
};
|
|
71
|
-
});
|
|
72
|
-
// Determine next cursor
|
|
73
|
-
const lastPost = displayPosts[displayPosts.length - 1];
|
|
74
|
-
const nextCursor = hasMore && lastPost ? lastPost.id : undefined;
|
|
75
|
-
// Resolve page component
|
|
76
|
-
const components = c.var.config.theme?.components;
|
|
77
|
-
const Page = components?.HomePage ?? DefaultHomePage;
|
|
28
|
+
const pinnedItems = toPostViewsFromPosts(pinnedPosts, mediaCtx);
|
|
78
29
|
return renderPublicPage(c, {
|
|
79
30
|
title: navData.siteName,
|
|
80
31
|
navData,
|
|
81
|
-
content: /*#__PURE__*/ _jsx(
|
|
32
|
+
content: /*#__PURE__*/ _jsx(HomePage, {
|
|
82
33
|
items: items,
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
34
|
+
pinnedItems: pinnedItems,
|
|
35
|
+
currentPage: currentPage,
|
|
36
|
+
totalPages: totalPages
|
|
86
37
|
})
|
|
87
38
|
});
|
|
88
39
|
});
|