feat: crossbell renovation - support slug
This commit is contained in:
parent
0fb54b42fc
commit
b8286c662a
|
|
@ -1,8 +1,7 @@
|
|||
import Link from "next/link"
|
||||
import { formatDate } from "~/lib/date"
|
||||
import { Paginated, type PostOnSiteHome } from "~/lib/types"
|
||||
import { Paginated, type PostOnSiteHome, Notes } from "~/lib/types"
|
||||
import { EmptyState } from "../ui/EmptyState"
|
||||
import { Notes } from "unidata.js"
|
||||
|
||||
export const SiteHome: React.FC<{
|
||||
posts?: Notes
|
||||
|
|
@ -17,7 +16,7 @@ export const SiteHome: React.FC<{
|
|||
{posts.list.map((post) => {
|
||||
const excerpt = post.summary?.content
|
||||
return (
|
||||
<Link key={post.id} href={`/${post.id}`}>
|
||||
<Link key={post.slug} href={`/${post.slug}`}>
|
||||
<a className="block hover:bg-zinc-100 transition-colors p-5 -mx-5 -my-5 md:rounded-xl">
|
||||
<h3 className="text-2xl font-bold">{post.title}</h3>
|
||||
<div className="text-sm text-zinc-400 mt-1">
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import type { Note as UniNote } from "unidata.js"
|
||||
|
||||
export type Site = {
|
||||
id: string
|
||||
name: string
|
||||
|
|
@ -68,3 +70,12 @@ export type SiteNavigationItem = {
|
|||
label: string
|
||||
url: string
|
||||
}
|
||||
|
||||
export type Note = UniNote & {
|
||||
slug?: string
|
||||
}
|
||||
|
||||
export type Notes = {
|
||||
total: number,
|
||||
list: Note[],
|
||||
}
|
||||
|
|
@ -9,13 +9,12 @@ import {
|
|||
import { type Gate } from "~/lib/gate.server"
|
||||
import { Rendered, renderPageContent } from "~/markdown"
|
||||
import { notFound } from "~/lib/server-side-props"
|
||||
import { PageVisibilityEnum } from "~/lib/types"
|
||||
import { PageVisibilityEnum, Notes } from "~/lib/types"
|
||||
import { isUUID } from "~/lib/uuid"
|
||||
import { getSite } from "./site.model"
|
||||
import { stripHTML } from "~/lib/utils"
|
||||
import { sendEmailForNewPost } from "~/lib/mailgun.server"
|
||||
import unidata from "~/lib/unidata"
|
||||
import { Notes } from "unidata.js"
|
||||
|
||||
const checkPageSlug = async ({
|
||||
slug,
|
||||
|
|
@ -57,6 +56,7 @@ export async function createOrUpdatePage(
|
|||
input: {
|
||||
pageId?: string
|
||||
siteId: string
|
||||
slug?: string
|
||||
title?: string
|
||||
content?: string
|
||||
published?: boolean
|
||||
|
|
@ -86,7 +86,7 @@ export async function createOrUpdatePage(
|
|||
mime_type: 'text/markdown',
|
||||
}
|
||||
}),
|
||||
tags: [input.isPost ? "post" : "page"],
|
||||
tags: [input.isPost ? "post" : "page", ...(input.slug ? ["slug:" + input.slug] : [])],
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -140,7 +140,7 @@ export async function getPagesBySite(
|
|||
|
||||
const visibility = input.visibility || PageVisibilityEnum.All
|
||||
|
||||
let pages = await unidata.notes.get({
|
||||
let pages: Notes | null = await unidata.notes.get({
|
||||
source: 'Crossbell Note',
|
||||
identity: input.site,
|
||||
platform: 'Crossbell',
|
||||
|
|
@ -176,6 +176,7 @@ export async function getPagesBySite(
|
|||
}
|
||||
}
|
||||
}
|
||||
page.slug = page.tags?.find((tag) => tag.startsWith("slug:"))?.replace(/^slug:/, "") || page.id
|
||||
return page
|
||||
}))
|
||||
}
|
||||
|
|
@ -197,7 +198,8 @@ export async function deletePage({ site, id }: { site: string, id: string }) {
|
|||
export async function getPage<TRender extends boolean = false>(
|
||||
input: {
|
||||
/** page slug or id, `site` is needed when `page` is a slug */
|
||||
page: string
|
||||
page?: string
|
||||
pageId?: string
|
||||
site?: string
|
||||
render?: TRender
|
||||
includeAuthors?: boolean
|
||||
|
|
@ -207,30 +209,32 @@ export async function getPage<TRender extends boolean = false>(
|
|||
throw notFound(`site not found`)
|
||||
}
|
||||
|
||||
const isPageUUID = isUUID(input.page)
|
||||
if (!isPageUUID && !input.site) {
|
||||
throw new Error("input.site is required because input.page is a slug")
|
||||
}
|
||||
|
||||
const pages = await unidata.notes.get({
|
||||
const pages: Notes | null = await unidata.notes.get({
|
||||
source: 'Crossbell Note',
|
||||
identity: input.site,
|
||||
platform: 'Crossbell',
|
||||
filter: {
|
||||
id: input.page,
|
||||
}
|
||||
limit: 1000,
|
||||
...(input.pageId && {
|
||||
filter: {
|
||||
id: input.pageId,
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const page = pages?.list[0]
|
||||
let page;
|
||||
if (input.page) {
|
||||
page = pages?.list.find((item) => {
|
||||
item.slug = item.tags?.find((tag) => tag.startsWith("slug:"))?.replace(/^slug:/, "") || item.id
|
||||
return item.slug === input.page
|
||||
})
|
||||
} else {
|
||||
page = pages?.list[0]
|
||||
}
|
||||
|
||||
if (!page) {
|
||||
throw notFound(`page ${input.page} not found`)
|
||||
}
|
||||
|
||||
if (new Date(page.date_published) > new Date()) {
|
||||
throw notFound()
|
||||
}
|
||||
|
||||
if (pages?.list) {
|
||||
pages.list = await Promise.all(pages?.list.map(async (page) => {
|
||||
if (page.body?.content && page.body?.mime_type === 'text/markdown' && input.render) {
|
||||
|
|
@ -246,6 +250,7 @@ export async function getPage<TRender extends boolean = false>(
|
|||
}
|
||||
}
|
||||
}
|
||||
page.slug = page.tags?.find((tag) => tag.startsWith("slug:"))?.replace(/^slug:/, "") || page.id
|
||||
return page
|
||||
}))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import { getUserSites, getSite } from "~/models/site.model"
|
|||
import { getPage } from "~/models/page.model"
|
||||
import { Profile, Note } from "unidata.js"
|
||||
import { PageVisibilityEnum } from "~/lib/types"
|
||||
import { notFound } from "~/lib/server-side-props"
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = serverSidePropsHandler(
|
||||
async (ctx) => {
|
||||
|
|
@ -28,7 +29,11 @@ export const getServerSideProps: GetServerSideProps = serverSidePropsHandler(
|
|||
includeAuthors: true,
|
||||
}),
|
||||
])
|
||||
|
||||
|
||||
if (new Date(page.date_published) > new Date()) {
|
||||
throw notFound()
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
site,
|
||||
|
|
|
|||
|
|
@ -17,13 +17,12 @@ import { useUploadFile } from "~/hooks/useUploadFile"
|
|||
import { inLocalTimezone } from "~/lib/date"
|
||||
import { getSiteLink } from "~/lib/helpers"
|
||||
import { getPageVisibility } from "~/lib/page-helpers"
|
||||
import { PageVisibilityEnum } from "~/lib/types"
|
||||
import { PageVisibilityEnum, Note } from "~/lib/types"
|
||||
import { FieldLabel } from "~/components/ui/FieldLabel"
|
||||
import { Button } from "~/components/ui/Button"
|
||||
import { EmailPostModal } from "~/components/common/EmailPostModal"
|
||||
import { useStore } from "~/lib/store"
|
||||
import type { Note } from "unidata.js"
|
||||
import { getPagesBySite, createOrUpdatePage } from "~/models/page.model"
|
||||
import { getPage, createOrUpdatePage } from "~/models/page.model"
|
||||
|
||||
const getInputDatetimeValue = (date: Date | string) => {
|
||||
const str = dayjs(date).format()
|
||||
|
|
@ -40,15 +39,12 @@ export default function SubdomainEditor() {
|
|||
let [page, setPage] = useState<Note | undefined>(undefined)
|
||||
|
||||
useEffect(() => {
|
||||
if (subdomain) {
|
||||
getPagesBySite({
|
||||
type: isPost ? "post" : "page",
|
||||
if (subdomain && pageId) {
|
||||
getPage({
|
||||
site: subdomain!,
|
||||
take: 1000,
|
||||
pageId: pageId,
|
||||
render: false
|
||||
}).then((pages) => {
|
||||
console.log(pages, pageId)
|
||||
const page = (pages?.list || []).find((p) => p.id === pageId)
|
||||
}).then((page) => {
|
||||
setPage(page)
|
||||
})
|
||||
}
|
||||
|
|
@ -70,6 +66,7 @@ export default function SubdomainEditor() {
|
|||
publishedAt: new Date().toISOString(),
|
||||
published: false,
|
||||
excerpt: "",
|
||||
slug: "",
|
||||
})
|
||||
const [content, setContent] = useState("")
|
||||
|
||||
|
|
@ -152,6 +149,7 @@ export default function SubdomainEditor() {
|
|||
publishedAt: page.date_published === new Date('9999-01-01').toISOString() ? new Date().toISOString() : page.date_published,
|
||||
published: page.date_published !== new Date('9999-01-01').toISOString(),
|
||||
excerpt: page.summary?.content || "",
|
||||
slug: page.slug!,
|
||||
})
|
||||
}, [page])
|
||||
|
||||
|
|
@ -238,6 +236,37 @@ export default function SubdomainEditor() {
|
|||
} will be accesisble from this time`}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Input
|
||||
name="slug"
|
||||
value={values.slug}
|
||||
label="Page slug"
|
||||
id="slug"
|
||||
isBlock
|
||||
placeholder="some-slug"
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||
updateValue("slug", e.target.value)
|
||||
}
|
||||
help={
|
||||
<>
|
||||
{values.slug && (
|
||||
<>
|
||||
This {isPost ? "post" : "page"} will be accessible at{" "}
|
||||
<UniLink
|
||||
href={`${getSiteLink({ subdomain })}/${
|
||||
values.slug
|
||||
}`}
|
||||
className="hover:underline"
|
||||
>
|
||||
{getSiteLink({ subdomain, noProtocol: true })}/
|
||||
{values.slug}
|
||||
</UniLink>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Input
|
||||
label="Excerpt"
|
||||
|
|
|
|||
Loading…
Reference in New Issue