@jant/core 0.3.7 → 0.3.9
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 +11 -4
- package/dist/client.js +1 -0
- package/dist/db/schema.js +15 -1
- 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/lib/image.js +39 -15
- package/dist/lib/media-helpers.js +49 -0
- package/dist/lib/nav-reorder.js +27 -0
- package/dist/lib/navigation.js +35 -0
- package/dist/lib/storage.js +164 -0
- package/dist/lib/theme-components.js +49 -0
- package/dist/routes/api/posts.js +12 -7
- package/dist/routes/api/timeline.js +116 -0
- package/dist/routes/api/upload.js +35 -24
- package/dist/routes/dash/media.js +24 -14
- package/dist/routes/dash/navigation.js +274 -0
- package/dist/routes/dash/posts.js +4 -1
- package/dist/routes/feed/rss.js +3 -2
- package/dist/routes/pages/archive.js +14 -27
- package/dist/routes/pages/collection.js +10 -19
- package/dist/routes/pages/home.js +84 -126
- package/dist/routes/pages/page.js +19 -38
- package/dist/routes/pages/post.js +47 -56
- package/dist/routes/pages/search.js +13 -26
- package/dist/services/index.js +3 -1
- package/dist/services/media.js +8 -6
- package/dist/services/navigation.js +115 -0
- package/dist/services/post.js +26 -1
- package/dist/theme/components/PostForm.js +4 -3
- package/dist/theme/components/PostList.js +5 -0
- package/dist/theme/components/index.js +2 -0
- package/dist/theme/components/timeline/ArticleCard.js +50 -0
- package/dist/theme/components/timeline/ImageCard.js +86 -0
- package/dist/theme/components/timeline/LinkCard.js +62 -0
- package/dist/theme/components/timeline/NoteCard.js +37 -0
- package/dist/theme/components/timeline/QuoteCard.js +51 -0
- package/dist/theme/components/timeline/ThreadPreview.js +52 -0
- package/dist/theme/components/timeline/TimelineFeed.js +43 -0
- package/dist/theme/components/timeline/TimelineItem.js +25 -0
- package/dist/theme/components/timeline/index.js +8 -0
- package/dist/theme/layouts/DashLayout.js +8 -0
- package/dist/theme/layouts/SiteLayout.js +160 -0
- package/dist/theme/layouts/index.js +1 -0
- package/dist/types/sortablejs.d.js +5 -0
- package/dist/types.js +32 -0
- package/package.json +4 -2
- package/src/__tests__/helpers/app.ts +1 -0
- package/src/__tests__/helpers/db.ts +20 -0
- package/src/app.tsx +12 -7
- package/src/client.ts +1 -0
- package/src/db/migrations/0003_add_navigation_links.sql +8 -0
- package/src/db/migrations/0004_add_storage_provider.sql +3 -0
- package/src/db/migrations/meta/0003_snapshot.json +821 -0
- package/src/db/migrations/meta/_journal.json +21 -0
- package/src/db/schema.ts +15 -1
- package/src/i18n/locales/en.po +148 -80
- package/src/i18n/locales/en.ts +1 -1
- package/src/i18n/locales/zh-Hans.po +150 -103
- package/src/i18n/locales/zh-Hans.ts +1 -1
- package/src/i18n/locales/zh-Hant.po +150 -103
- package/src/i18n/locales/zh-Hant.ts +1 -1
- package/src/index.ts +5 -0
- package/src/lib/__tests__/image.test.ts +96 -0
- package/src/lib/__tests__/storage.test.ts +162 -0
- package/src/lib/__tests__/theme-components.test.ts +107 -0
- package/src/lib/image.ts +46 -16
- package/src/lib/media-helpers.ts +65 -0
- package/src/lib/nav-reorder.ts +26 -0
- package/src/lib/navigation.ts +46 -0
- package/src/lib/storage.ts +236 -0
- package/src/lib/theme-components.ts +76 -0
- package/src/routes/api/__tests__/posts.test.ts +8 -8
- package/src/routes/api/__tests__/timeline.test.ts +242 -0
- package/src/routes/api/posts.ts +20 -6
- package/src/routes/api/timeline.tsx +152 -0
- package/src/routes/api/upload.ts +52 -25
- package/src/routes/dash/media.tsx +40 -8
- package/src/routes/dash/navigation.tsx +306 -0
- package/src/routes/dash/posts.tsx +5 -0
- package/src/routes/feed/rss.ts +3 -2
- package/src/routes/pages/archive.tsx +15 -23
- package/src/routes/pages/collection.tsx +8 -15
- package/src/routes/pages/home.tsx +118 -122
- package/src/routes/pages/page.tsx +17 -30
- package/src/routes/pages/post.tsx +63 -60
- package/src/routes/pages/search.tsx +18 -22
- package/src/services/__tests__/media.test.ts +73 -28
- package/src/services/__tests__/navigation.test.ts +213 -0
- package/src/services/__tests__/post-timeline.test.ts +220 -0
- package/src/services/index.ts +7 -0
- package/src/services/media.ts +12 -8
- package/src/services/navigation.ts +165 -0
- package/src/services/post.ts +48 -1
- package/src/styles/components.css +59 -0
- package/src/theme/components/PostForm.tsx +13 -2
- package/src/theme/components/PostList.tsx +7 -0
- package/src/theme/components/index.ts +12 -0
- package/src/theme/components/timeline/ArticleCard.tsx +57 -0
- package/src/theme/components/timeline/ImageCard.tsx +80 -0
- package/src/theme/components/timeline/LinkCard.tsx +66 -0
- package/src/theme/components/timeline/NoteCard.tsx +41 -0
- package/src/theme/components/timeline/QuoteCard.tsx +55 -0
- package/src/theme/components/timeline/ThreadPreview.tsx +49 -0
- package/src/theme/components/timeline/TimelineFeed.tsx +52 -0
- package/src/theme/components/timeline/TimelineItem.tsx +39 -0
- package/src/theme/components/timeline/index.ts +8 -0
- package/src/theme/layouts/DashLayout.tsx +10 -0
- package/src/theme/layouts/SiteLayout.tsx +184 -0
- package/src/theme/layouts/index.ts +1 -0
- package/src/types/sortablejs.d.ts +23 -0
- package/src/types.ts +102 -1
- package/dist/app.d.ts +0 -38
- package/dist/app.d.ts.map +0 -1
- package/dist/auth.d.ts +0 -25
- package/dist/auth.d.ts.map +0 -1
- package/dist/db/index.d.ts +0 -10
- package/dist/db/index.d.ts.map +0 -1
- package/dist/db/schema.d.ts +0 -1543
- package/dist/db/schema.d.ts.map +0 -1
- package/dist/i18n/Trans.d.ts +0 -25
- package/dist/i18n/Trans.d.ts.map +0 -1
- package/dist/i18n/context.d.ts +0 -69
- package/dist/i18n/context.d.ts.map +0 -1
- package/dist/i18n/detect.d.ts +0 -20
- package/dist/i18n/detect.d.ts.map +0 -1
- package/dist/i18n/i18n.d.ts +0 -32
- package/dist/i18n/i18n.d.ts.map +0 -1
- package/dist/i18n/index.d.ts +0 -41
- package/dist/i18n/index.d.ts.map +0 -1
- package/dist/i18n/locales/en.d.ts +0 -3
- package/dist/i18n/locales/en.d.ts.map +0 -1
- package/dist/i18n/locales/zh-Hans.d.ts +0 -3
- package/dist/i18n/locales/zh-Hans.d.ts.map +0 -1
- package/dist/i18n/locales/zh-Hant.d.ts +0 -3
- package/dist/i18n/locales/zh-Hant.d.ts.map +0 -1
- package/dist/i18n/locales.d.ts +0 -11
- package/dist/i18n/locales.d.ts.map +0 -1
- package/dist/i18n/middleware.d.ts +0 -21
- package/dist/i18n/middleware.d.ts.map +0 -1
- package/dist/index.d.ts +0 -16
- package/dist/index.d.ts.map +0 -1
- package/dist/lib/config.d.ts +0 -83
- package/dist/lib/config.d.ts.map +0 -1
- package/dist/lib/constants.d.ts +0 -37
- package/dist/lib/constants.d.ts.map +0 -1
- package/dist/lib/image.d.ts +0 -73
- package/dist/lib/image.d.ts.map +0 -1
- package/dist/lib/index.d.ts +0 -9
- package/dist/lib/index.d.ts.map +0 -1
- package/dist/lib/markdown.d.ts +0 -60
- package/dist/lib/markdown.d.ts.map +0 -1
- package/dist/lib/schemas.d.ts +0 -130
- package/dist/lib/schemas.d.ts.map +0 -1
- package/dist/lib/sqid.d.ts +0 -60
- package/dist/lib/sqid.d.ts.map +0 -1
- package/dist/lib/sse.d.ts +0 -192
- package/dist/lib/sse.d.ts.map +0 -1
- package/dist/lib/theme.d.ts +0 -44
- package/dist/lib/theme.d.ts.map +0 -1
- package/dist/lib/time.d.ts +0 -90
- package/dist/lib/time.d.ts.map +0 -1
- package/dist/lib/url.d.ts +0 -82
- package/dist/lib/url.d.ts.map +0 -1
- package/dist/middleware/auth.d.ts +0 -24
- package/dist/middleware/auth.d.ts.map +0 -1
- package/dist/middleware/onboarding.d.ts +0 -26
- package/dist/middleware/onboarding.d.ts.map +0 -1
- package/dist/routes/api/posts.d.ts +0 -13
- package/dist/routes/api/posts.d.ts.map +0 -1
- package/dist/routes/api/search.d.ts +0 -13
- package/dist/routes/api/search.d.ts.map +0 -1
- package/dist/routes/api/upload.d.ts +0 -16
- package/dist/routes/api/upload.d.ts.map +0 -1
- package/dist/routes/dash/collections.d.ts +0 -13
- package/dist/routes/dash/collections.d.ts.map +0 -1
- package/dist/routes/dash/index.d.ts +0 -15
- package/dist/routes/dash/index.d.ts.map +0 -1
- package/dist/routes/dash/media.d.ts +0 -16
- package/dist/routes/dash/media.d.ts.map +0 -1
- package/dist/routes/dash/pages.d.ts +0 -15
- package/dist/routes/dash/pages.d.ts.map +0 -1
- package/dist/routes/dash/posts.d.ts +0 -13
- package/dist/routes/dash/posts.d.ts.map +0 -1
- package/dist/routes/dash/redirects.d.ts +0 -13
- package/dist/routes/dash/redirects.d.ts.map +0 -1
- package/dist/routes/dash/settings.d.ts +0 -15
- package/dist/routes/dash/settings.d.ts.map +0 -1
- package/dist/routes/feed/rss.d.ts +0 -13
- package/dist/routes/feed/rss.d.ts.map +0 -1
- package/dist/routes/feed/sitemap.d.ts +0 -13
- package/dist/routes/feed/sitemap.d.ts.map +0 -1
- package/dist/routes/pages/archive.d.ts +0 -15
- package/dist/routes/pages/archive.d.ts.map +0 -1
- package/dist/routes/pages/collection.d.ts +0 -13
- package/dist/routes/pages/collection.d.ts.map +0 -1
- package/dist/routes/pages/home.d.ts +0 -13
- package/dist/routes/pages/home.d.ts.map +0 -1
- package/dist/routes/pages/page.d.ts +0 -15
- package/dist/routes/pages/page.d.ts.map +0 -1
- package/dist/routes/pages/post.d.ts +0 -13
- package/dist/routes/pages/post.d.ts.map +0 -1
- package/dist/routes/pages/search.d.ts +0 -13
- package/dist/routes/pages/search.d.ts.map +0 -1
- package/dist/services/collection.d.ts +0 -32
- package/dist/services/collection.d.ts.map +0 -1
- package/dist/services/index.d.ts +0 -28
- package/dist/services/index.d.ts.map +0 -1
- package/dist/services/media.d.ts +0 -34
- package/dist/services/media.d.ts.map +0 -1
- package/dist/services/post.d.ts +0 -31
- package/dist/services/post.d.ts.map +0 -1
- package/dist/services/redirect.d.ts +0 -15
- package/dist/services/redirect.d.ts.map +0 -1
- package/dist/services/search.d.ts +0 -26
- package/dist/services/search.d.ts.map +0 -1
- package/dist/services/settings.d.ts +0 -18
- package/dist/services/settings.d.ts.map +0 -1
- package/dist/theme/color-themes.d.ts +0 -30
- package/dist/theme/color-themes.d.ts.map +0 -1
- package/dist/theme/components/ActionButtons.d.ts +0 -43
- package/dist/theme/components/ActionButtons.d.ts.map +0 -1
- package/dist/theme/components/CrudPageHeader.d.ts +0 -23
- package/dist/theme/components/CrudPageHeader.d.ts.map +0 -1
- package/dist/theme/components/DangerZone.d.ts +0 -36
- package/dist/theme/components/DangerZone.d.ts.map +0 -1
- package/dist/theme/components/EmptyState.d.ts +0 -27
- package/dist/theme/components/EmptyState.d.ts.map +0 -1
- package/dist/theme/components/ListItemRow.d.ts +0 -15
- package/dist/theme/components/ListItemRow.d.ts.map +0 -1
- package/dist/theme/components/MediaGallery.d.ts +0 -13
- package/dist/theme/components/MediaGallery.d.ts.map +0 -1
- package/dist/theme/components/PageForm.d.ts +0 -14
- package/dist/theme/components/PageForm.d.ts.map +0 -1
- package/dist/theme/components/Pagination.d.ts +0 -46
- package/dist/theme/components/Pagination.d.ts.map +0 -1
- package/dist/theme/components/PostForm.d.ts +0 -16
- package/dist/theme/components/PostForm.d.ts.map +0 -1
- package/dist/theme/components/PostList.d.ts +0 -10
- package/dist/theme/components/PostList.d.ts.map +0 -1
- package/dist/theme/components/ThreadView.d.ts +0 -15
- package/dist/theme/components/ThreadView.d.ts.map +0 -1
- package/dist/theme/components/TypeBadge.d.ts +0 -12
- package/dist/theme/components/TypeBadge.d.ts.map +0 -1
- package/dist/theme/components/VisibilityBadge.d.ts +0 -12
- package/dist/theme/components/VisibilityBadge.d.ts.map +0 -1
- package/dist/theme/components/index.d.ts +0 -14
- package/dist/theme/components/index.d.ts.map +0 -1
- package/dist/theme/index.d.ts +0 -21
- package/dist/theme/index.d.ts.map +0 -1
- package/dist/theme/layouts/BaseLayout.d.ts +0 -23
- package/dist/theme/layouts/BaseLayout.d.ts.map +0 -1
- package/dist/theme/layouts/DashLayout.d.ts +0 -17
- package/dist/theme/layouts/DashLayout.d.ts.map +0 -1
- package/dist/theme/layouts/index.d.ts +0 -3
- package/dist/theme/layouts/index.d.ts.map +0 -1
- package/dist/types.d.ts +0 -237
- package/dist/types.d.ts.map +0 -1
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { getSiteName } from "../../lib/config.js";
|
|
2
|
+
/**
|
|
3
|
+
* Dashboard Navigation Links Routes
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Hono } from "hono";
|
|
7
|
+
import { useLingui } from "@lingui/react/macro";
|
|
8
|
+
import type { Bindings, NavigationLink } from "../../types.js";
|
|
9
|
+
import type { AppVariables } from "../../app.js";
|
|
10
|
+
import { DashLayout } from "../../theme/layouts/index.js";
|
|
11
|
+
import {
|
|
12
|
+
EmptyState,
|
|
13
|
+
ListItemRow,
|
|
14
|
+
ActionButtons,
|
|
15
|
+
CrudPageHeader,
|
|
16
|
+
} from "../../theme/components/index.js";
|
|
17
|
+
import { dsRedirect, dsToast } from "../../lib/sse.js";
|
|
18
|
+
|
|
19
|
+
type Env = { Bindings: Bindings; Variables: AppVariables };
|
|
20
|
+
|
|
21
|
+
export const navigationRoutes = new Hono<Env>();
|
|
22
|
+
|
|
23
|
+
function NavigationListContent({ links }: { links: NavigationLink[] }) {
|
|
24
|
+
const { t } = useLingui();
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<>
|
|
28
|
+
<CrudPageHeader
|
|
29
|
+
title={t({
|
|
30
|
+
message: "Navigation",
|
|
31
|
+
comment: "@context: Dashboard heading",
|
|
32
|
+
})}
|
|
33
|
+
ctaLabel={t({
|
|
34
|
+
message: "New Link",
|
|
35
|
+
comment: "@context: Button to create new navigation link",
|
|
36
|
+
})}
|
|
37
|
+
ctaHref="/dash/navigation/new"
|
|
38
|
+
/>
|
|
39
|
+
|
|
40
|
+
{links.length === 0 ? (
|
|
41
|
+
<EmptyState
|
|
42
|
+
message={t({
|
|
43
|
+
message: "No navigation links configured.",
|
|
44
|
+
comment: "@context: Empty state message",
|
|
45
|
+
})}
|
|
46
|
+
ctaText={t({
|
|
47
|
+
message: "New Link",
|
|
48
|
+
comment: "@context: Button to create new navigation link",
|
|
49
|
+
})}
|
|
50
|
+
ctaHref="/dash/navigation/new"
|
|
51
|
+
/>
|
|
52
|
+
) : (
|
|
53
|
+
<>
|
|
54
|
+
<div id="nav-links-list" class="flex flex-col divide-y">
|
|
55
|
+
{links.map((link) => (
|
|
56
|
+
<ListItemRow
|
|
57
|
+
key={link.id}
|
|
58
|
+
actions={
|
|
59
|
+
<ActionButtons
|
|
60
|
+
editHref={`/dash/navigation/${link.id}/edit`}
|
|
61
|
+
editLabel={t({
|
|
62
|
+
message: "Edit",
|
|
63
|
+
comment: "@context: Button to edit navigation link",
|
|
64
|
+
})}
|
|
65
|
+
deleteAction={`/dash/navigation/${link.id}/delete`}
|
|
66
|
+
deleteLabel={t({
|
|
67
|
+
message: "Delete",
|
|
68
|
+
comment: "@context: Button to delete navigation link",
|
|
69
|
+
})}
|
|
70
|
+
/>
|
|
71
|
+
}
|
|
72
|
+
>
|
|
73
|
+
<div
|
|
74
|
+
class="flex items-center gap-3 cursor-grab"
|
|
75
|
+
data-id={link.id}
|
|
76
|
+
>
|
|
77
|
+
<span class="text-muted-foreground select-none">⠿</span>
|
|
78
|
+
<div class="flex items-center gap-2">
|
|
79
|
+
<span class="font-medium">{link.label}</span>
|
|
80
|
+
<code class="text-sm text-muted-foreground bg-muted px-1 rounded">
|
|
81
|
+
{link.url}
|
|
82
|
+
</code>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
</ListItemRow>
|
|
86
|
+
))}
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
{/* SortableJS is initialized by client.ts via lib/nav-reorder.ts */}
|
|
90
|
+
</>
|
|
91
|
+
)}
|
|
92
|
+
</>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function NavigationFormContent({
|
|
97
|
+
link,
|
|
98
|
+
isEdit,
|
|
99
|
+
}: {
|
|
100
|
+
link?: NavigationLink;
|
|
101
|
+
isEdit?: boolean;
|
|
102
|
+
}) {
|
|
103
|
+
const { t } = useLingui();
|
|
104
|
+
const title = isEdit
|
|
105
|
+
? t({ message: "Edit Link", comment: "@context: Page heading" })
|
|
106
|
+
: t({ message: "New Link", comment: "@context: Page heading" });
|
|
107
|
+
|
|
108
|
+
const signals = JSON.stringify({
|
|
109
|
+
label: link?.label ?? "",
|
|
110
|
+
url: link?.url ?? "",
|
|
111
|
+
}).replace(/</g, "\\u003c");
|
|
112
|
+
|
|
113
|
+
const action = isEdit ? `/dash/navigation/${link?.id}` : "/dash/navigation";
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<>
|
|
117
|
+
<h1 class="text-2xl font-semibold mb-6">{title}</h1>
|
|
118
|
+
|
|
119
|
+
<form
|
|
120
|
+
data-signals={signals}
|
|
121
|
+
data-on:submit__prevent={`@post('${action}')`}
|
|
122
|
+
class="flex flex-col gap-4 max-w-lg"
|
|
123
|
+
>
|
|
124
|
+
<div class="field">
|
|
125
|
+
<label class="label">
|
|
126
|
+
{t({
|
|
127
|
+
message: "Label",
|
|
128
|
+
comment: "@context: Navigation link form field",
|
|
129
|
+
})}
|
|
130
|
+
</label>
|
|
131
|
+
<input
|
|
132
|
+
type="text"
|
|
133
|
+
data-bind="label"
|
|
134
|
+
class="input"
|
|
135
|
+
placeholder="Home"
|
|
136
|
+
required
|
|
137
|
+
/>
|
|
138
|
+
<p class="text-xs text-muted-foreground mt-1">
|
|
139
|
+
{t({
|
|
140
|
+
message: "Display text for the link",
|
|
141
|
+
comment: "@context: Navigation label help text",
|
|
142
|
+
})}
|
|
143
|
+
</p>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<div class="field">
|
|
147
|
+
<label class="label">
|
|
148
|
+
{t({
|
|
149
|
+
message: "URL",
|
|
150
|
+
comment: "@context: Navigation link form field",
|
|
151
|
+
})}
|
|
152
|
+
</label>
|
|
153
|
+
<input
|
|
154
|
+
type="text"
|
|
155
|
+
data-bind="url"
|
|
156
|
+
class="input"
|
|
157
|
+
placeholder="/archive or https://..."
|
|
158
|
+
required
|
|
159
|
+
/>
|
|
160
|
+
<p class="text-xs text-muted-foreground mt-1">
|
|
161
|
+
{t({
|
|
162
|
+
message:
|
|
163
|
+
"Path (e.g. /archive) or full URL (e.g. https://example.com)",
|
|
164
|
+
comment: "@context: Navigation URL help text",
|
|
165
|
+
})}
|
|
166
|
+
</p>
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
<div class="flex gap-2">
|
|
170
|
+
<button type="submit" class="btn">
|
|
171
|
+
{isEdit
|
|
172
|
+
? t({
|
|
173
|
+
message: "Save Changes",
|
|
174
|
+
comment: "@context: Button to save edited navigation link",
|
|
175
|
+
})
|
|
176
|
+
: t({
|
|
177
|
+
message: "Create Link",
|
|
178
|
+
comment: "@context: Button to save new navigation link",
|
|
179
|
+
})}
|
|
180
|
+
</button>
|
|
181
|
+
<a href="/dash/navigation" class="btn-outline">
|
|
182
|
+
{t({
|
|
183
|
+
message: "Cancel",
|
|
184
|
+
comment: "@context: Button to cancel form",
|
|
185
|
+
})}
|
|
186
|
+
</a>
|
|
187
|
+
</div>
|
|
188
|
+
</form>
|
|
189
|
+
</>
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// List navigation links
|
|
194
|
+
navigationRoutes.get("/", async (c) => {
|
|
195
|
+
const siteName = await getSiteName(c);
|
|
196
|
+
const links = await c.var.services.navigationLinks.list();
|
|
197
|
+
|
|
198
|
+
return c.html(
|
|
199
|
+
<DashLayout
|
|
200
|
+
c={c}
|
|
201
|
+
title="Navigation"
|
|
202
|
+
siteName={siteName}
|
|
203
|
+
currentPath="/dash/navigation"
|
|
204
|
+
>
|
|
205
|
+
<NavigationListContent links={links} />
|
|
206
|
+
</DashLayout>,
|
|
207
|
+
);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// New link form
|
|
211
|
+
navigationRoutes.get("/new", async (c) => {
|
|
212
|
+
const siteName = await getSiteName(c);
|
|
213
|
+
|
|
214
|
+
return c.html(
|
|
215
|
+
<DashLayout
|
|
216
|
+
c={c}
|
|
217
|
+
title="New Link"
|
|
218
|
+
siteName={siteName}
|
|
219
|
+
currentPath="/dash/navigation"
|
|
220
|
+
>
|
|
221
|
+
<NavigationFormContent />
|
|
222
|
+
</DashLayout>,
|
|
223
|
+
);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// Create link
|
|
227
|
+
navigationRoutes.post("/", async (c) => {
|
|
228
|
+
const body = await c.req.json<{ label: string; url: string }>();
|
|
229
|
+
|
|
230
|
+
if (!body.label || !body.url) {
|
|
231
|
+
return dsToast("Label and URL are required", "error");
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
await c.var.services.navigationLinks.create({
|
|
235
|
+
label: body.label,
|
|
236
|
+
url: body.url,
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
return dsRedirect("/dash/navigation");
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// Reorder links (must be before /:id to avoid "reorder" matching as :id)
|
|
243
|
+
navigationRoutes.post("/reorder", async (c) => {
|
|
244
|
+
const body = await c.req.json<{ ids: number[] }>();
|
|
245
|
+
|
|
246
|
+
if (!Array.isArray(body.ids)) {
|
|
247
|
+
return dsToast("Invalid request", "error");
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
await c.var.services.navigationLinks.reorder(body.ids);
|
|
251
|
+
|
|
252
|
+
return dsToast("Order saved");
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// Edit link form
|
|
256
|
+
navigationRoutes.get("/:id/edit", async (c) => {
|
|
257
|
+
const id = parseInt(c.req.param("id"), 10);
|
|
258
|
+
if (isNaN(id)) return c.notFound();
|
|
259
|
+
|
|
260
|
+
const link = await c.var.services.navigationLinks.getById(id);
|
|
261
|
+
if (!link) return c.notFound();
|
|
262
|
+
|
|
263
|
+
const siteName = await getSiteName(c);
|
|
264
|
+
|
|
265
|
+
return c.html(
|
|
266
|
+
<DashLayout
|
|
267
|
+
c={c}
|
|
268
|
+
title="Edit Link"
|
|
269
|
+
siteName={siteName}
|
|
270
|
+
currentPath="/dash/navigation"
|
|
271
|
+
>
|
|
272
|
+
<NavigationFormContent link={link} isEdit />
|
|
273
|
+
</DashLayout>,
|
|
274
|
+
);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// Update link
|
|
278
|
+
navigationRoutes.post("/:id", async (c) => {
|
|
279
|
+
const id = parseInt(c.req.param("id"), 10);
|
|
280
|
+
if (isNaN(id)) return c.notFound();
|
|
281
|
+
|
|
282
|
+
const body = await c.req.json<{ label: string; url: string }>();
|
|
283
|
+
|
|
284
|
+
if (!body.label || !body.url) {
|
|
285
|
+
return dsToast("Label and URL are required", "error");
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const updated = await c.var.services.navigationLinks.update(id, {
|
|
289
|
+
label: body.label,
|
|
290
|
+
url: body.url,
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
if (!updated) return c.notFound();
|
|
294
|
+
|
|
295
|
+
return dsRedirect("/dash/navigation");
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// Delete link
|
|
299
|
+
navigationRoutes.post("/:id/delete", async (c) => {
|
|
300
|
+
const id = parseInt(c.req.param("id"), 10);
|
|
301
|
+
if (!isNaN(id)) {
|
|
302
|
+
await c.var.services.navigationLinks.delete(id);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return dsRedirect("/dash/navigation");
|
|
306
|
+
});
|
|
@@ -168,6 +168,7 @@ function EditPostContent({
|
|
|
168
168
|
mediaAttachments,
|
|
169
169
|
r2PublicUrl,
|
|
170
170
|
imageTransformUrl,
|
|
171
|
+
s3PublicUrl,
|
|
171
172
|
collections,
|
|
172
173
|
postCollectionIds,
|
|
173
174
|
}: {
|
|
@@ -175,6 +176,7 @@ function EditPostContent({
|
|
|
175
176
|
mediaAttachments: Media[];
|
|
176
177
|
r2PublicUrl?: string;
|
|
177
178
|
imageTransformUrl?: string;
|
|
179
|
+
s3PublicUrl?: string;
|
|
178
180
|
collections: Collection[];
|
|
179
181
|
postCollectionIds: number[];
|
|
180
182
|
}) {
|
|
@@ -190,6 +192,7 @@ function EditPostContent({
|
|
|
190
192
|
mediaAttachments={mediaAttachments}
|
|
191
193
|
r2PublicUrl={r2PublicUrl}
|
|
192
194
|
imageTransformUrl={imageTransformUrl}
|
|
195
|
+
s3PublicUrl={s3PublicUrl}
|
|
193
196
|
collections={collections}
|
|
194
197
|
postCollectionIds={postCollectionIds}
|
|
195
198
|
/>
|
|
@@ -232,6 +235,7 @@ postsRoutes.get("/:id/edit", async (c) => {
|
|
|
232
235
|
const mediaAttachments = await c.var.services.media.getByPostId(post.id);
|
|
233
236
|
const r2PublicUrl = c.env.R2_PUBLIC_URL;
|
|
234
237
|
const imageTransformUrl = c.env.IMAGE_TRANSFORM_URL;
|
|
238
|
+
const s3PublicUrl = c.env.S3_PUBLIC_URL;
|
|
235
239
|
const collections = await c.var.services.collections.list();
|
|
236
240
|
const postCollections =
|
|
237
241
|
await c.var.services.collections.getCollectionsForPost(post.id);
|
|
@@ -249,6 +253,7 @@ postsRoutes.get("/:id/edit", async (c) => {
|
|
|
249
253
|
mediaAttachments={mediaAttachments}
|
|
250
254
|
r2PublicUrl={r2PublicUrl}
|
|
251
255
|
imageTransformUrl={imageTransformUrl}
|
|
256
|
+
s3PublicUrl={s3PublicUrl}
|
|
252
257
|
collections={collections}
|
|
253
258
|
postCollectionIds={postCollectionIds}
|
|
254
259
|
/>
|
package/src/routes/feed/rss.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type { Bindings } from "../../types.js";
|
|
|
7
7
|
import type { AppVariables } from "../../app.js";
|
|
8
8
|
import * as sqid from "../../lib/sqid.js";
|
|
9
9
|
import * as time from "../../lib/time.js";
|
|
10
|
-
import { getMediaUrl } from "../../lib/image.js";
|
|
10
|
+
import { getMediaUrl, getPublicUrlForProvider } from "../../lib/image.js";
|
|
11
11
|
|
|
12
12
|
type Env = { Bindings: Bindings; Variables: AppVariables };
|
|
13
13
|
|
|
@@ -20,6 +20,7 @@ rssRoutes.get("/", async (c) => {
|
|
|
20
20
|
const siteDescription = all["SITE_DESCRIPTION"] ?? "";
|
|
21
21
|
const siteUrl = c.env.SITE_URL;
|
|
22
22
|
const r2PublicUrl = c.env.R2_PUBLIC_URL;
|
|
23
|
+
const s3PublicUrl = c.env.S3_PUBLIC_URL;
|
|
23
24
|
|
|
24
25
|
const posts = await c.var.services.posts.list({
|
|
25
26
|
visibility: ["featured", "quiet"],
|
|
@@ -40,7 +41,7 @@ rssRoutes.get("/", async (c) => {
|
|
|
40
41
|
const postMedia = mediaMap.get(post.id);
|
|
41
42
|
const firstMedia = postMedia?.[0];
|
|
42
43
|
const enclosure = firstMedia
|
|
43
|
-
? `\n <enclosure url="${getMediaUrl(firstMedia.id, firstMedia.
|
|
44
|
+
? `\n <enclosure url="${getMediaUrl(firstMedia.id, firstMedia.storageKey, getPublicUrlForProvider(firstMedia.provider, r2PublicUrl, s3PublicUrl))}" length="${firstMedia.size}" type="${firstMedia.mimeType}"/>`
|
|
44
45
|
: "";
|
|
45
46
|
|
|
46
47
|
return `
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { getSiteName } from "../../lib/config.js";
|
|
2
1
|
/**
|
|
3
2
|
* Archive Page Route
|
|
4
3
|
*
|
|
@@ -9,11 +8,12 @@ import { Hono } from "hono";
|
|
|
9
8
|
import { useLingui } from "@lingui/react/macro";
|
|
10
9
|
import type { Bindings, Post, PostType } from "../../types.js";
|
|
11
10
|
import type { AppVariables } from "../../app.js";
|
|
12
|
-
import { BaseLayout } from "../../theme/layouts/index.js";
|
|
11
|
+
import { BaseLayout, SiteLayout } from "../../theme/layouts/index.js";
|
|
13
12
|
import { Pagination } from "../../theme/components/index.js";
|
|
14
13
|
import { POST_TYPES } from "../../types.js";
|
|
15
14
|
import * as sqid from "../../lib/sqid.js";
|
|
16
15
|
import * as time from "../../lib/time.js";
|
|
16
|
+
import { getNavigationData } from "../../lib/navigation.js";
|
|
17
17
|
|
|
18
18
|
type Env = { Bindings: Bindings; Variables: AppVariables };
|
|
19
19
|
|
|
@@ -102,7 +102,7 @@ function ArchiveContent({
|
|
|
102
102
|
: t({ message: "Archive", comment: "@context: Archive page title" });
|
|
103
103
|
|
|
104
104
|
return (
|
|
105
|
-
<div
|
|
105
|
+
<div>
|
|
106
106
|
<header class="mb-8">
|
|
107
107
|
<h1 class="text-2xl font-semibold">{title}</h1>
|
|
108
108
|
|
|
@@ -202,16 +202,6 @@ function ArchiveContent({
|
|
|
202
202
|
hasMore={hasMore}
|
|
203
203
|
nextCursor={nextCursor}
|
|
204
204
|
/>
|
|
205
|
-
|
|
206
|
-
<nav class="mt-4">
|
|
207
|
-
<a href="/" class="text-sm hover:underline">
|
|
208
|
-
←{" "}
|
|
209
|
-
{t({
|
|
210
|
-
message: "Back to home",
|
|
211
|
-
comment: "@context: Navigation link back to home page",
|
|
212
|
-
})}
|
|
213
|
-
</a>
|
|
214
|
-
</nav>
|
|
215
205
|
</div>
|
|
216
206
|
);
|
|
217
207
|
}
|
|
@@ -226,7 +216,7 @@ archiveRoutes.get("/", async (c) => {
|
|
|
226
216
|
const cursorParam = c.req.query("cursor");
|
|
227
217
|
const cursor = cursorParam ? parseInt(cursorParam, 10) : undefined;
|
|
228
218
|
|
|
229
|
-
const
|
|
219
|
+
const navData = await getNavigationData(c);
|
|
230
220
|
|
|
231
221
|
// Fetch one extra to check for more
|
|
232
222
|
const posts = await c.var.services.posts.list({
|
|
@@ -264,15 +254,17 @@ archiveRoutes.get("/", async (c) => {
|
|
|
264
254
|
}
|
|
265
255
|
|
|
266
256
|
return c.html(
|
|
267
|
-
<BaseLayout title={`Archive - ${siteName}`} c={c}>
|
|
268
|
-
<
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
257
|
+
<BaseLayout title={`Archive - ${navData.siteName}`} c={c}>
|
|
258
|
+
<SiteLayout {...navData}>
|
|
259
|
+
<ArchiveContent
|
|
260
|
+
displayPosts={displayPosts}
|
|
261
|
+
hasMore={hasMore}
|
|
262
|
+
nextCursor={nextCursor}
|
|
263
|
+
type={type}
|
|
264
|
+
grouped={grouped}
|
|
265
|
+
replyCounts={replyCounts}
|
|
266
|
+
/>
|
|
267
|
+
</SiteLayout>
|
|
276
268
|
</BaseLayout>,
|
|
277
269
|
);
|
|
278
270
|
});
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { getSiteName } from "../../lib/config.js";
|
|
2
1
|
/**
|
|
3
2
|
* Collection Page Route
|
|
4
3
|
*/
|
|
@@ -7,9 +6,10 @@ import { Hono } from "hono";
|
|
|
7
6
|
import { useLingui } from "@lingui/react/macro";
|
|
8
7
|
import type { Bindings, Collection, Post } from "../../types.js";
|
|
9
8
|
import type { AppVariables } from "../../app.js";
|
|
10
|
-
import { BaseLayout } from "../../theme/layouts/index.js";
|
|
9
|
+
import { BaseLayout, SiteLayout } from "../../theme/layouts/index.js";
|
|
11
10
|
import * as sqid from "../../lib/sqid.js";
|
|
12
11
|
import * as time from "../../lib/time.js";
|
|
12
|
+
import { getNavigationData } from "../../lib/navigation.js";
|
|
13
13
|
|
|
14
14
|
type Env = { Bindings: Bindings; Variables: AppVariables };
|
|
15
15
|
|
|
@@ -25,7 +25,7 @@ function CollectionContent({
|
|
|
25
25
|
const { t } = useLingui();
|
|
26
26
|
|
|
27
27
|
return (
|
|
28
|
-
<div
|
|
28
|
+
<div>
|
|
29
29
|
<header class="mb-8">
|
|
30
30
|
<h1 class="text-2xl font-semibold">{collection.title}</h1>
|
|
31
31
|
{collection.description && (
|
|
@@ -70,15 +70,6 @@ function CollectionContent({
|
|
|
70
70
|
))
|
|
71
71
|
)}
|
|
72
72
|
</main>
|
|
73
|
-
|
|
74
|
-
<nav class="mt-8">
|
|
75
|
-
<a href="/" class="text-sm hover:underline">
|
|
76
|
-
{t({
|
|
77
|
-
message: "← Back to home",
|
|
78
|
-
comment: "@context: Navigation link",
|
|
79
|
-
})}
|
|
80
|
-
</a>
|
|
81
|
-
</nav>
|
|
82
73
|
</div>
|
|
83
74
|
);
|
|
84
75
|
}
|
|
@@ -90,15 +81,17 @@ collectionRoutes.get("/:path", async (c) => {
|
|
|
90
81
|
if (!collection) return c.notFound();
|
|
91
82
|
|
|
92
83
|
const posts = await c.var.services.collections.getPosts(collection.id);
|
|
93
|
-
const
|
|
84
|
+
const navData = await getNavigationData(c);
|
|
94
85
|
|
|
95
86
|
return c.html(
|
|
96
87
|
<BaseLayout
|
|
97
|
-
title={`${collection.title} - ${siteName}`}
|
|
88
|
+
title={`${collection.title} - ${navData.siteName}`}
|
|
98
89
|
description={collection.description ?? undefined}
|
|
99
90
|
c={c}
|
|
100
91
|
>
|
|
101
|
-
<
|
|
92
|
+
<SiteLayout {...navData}>
|
|
93
|
+
<CollectionContent collection={collection} posts={posts} />
|
|
94
|
+
</SiteLayout>
|
|
102
95
|
</BaseLayout>,
|
|
103
96
|
);
|
|
104
97
|
});
|