feat: crossbell renovation - support slug

This commit is contained in:
DIYgod 2022-06-29 20:55:50 +01:00
parent 0fb54b42fc
commit b8286c662a
No known key found for this signature in database
GPG Key ID: D159328F47A80DCA
5 changed files with 82 additions and 33 deletions

View File

@ -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">

View File

@ -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[],
}

View File

@ -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
}))
}

View File

@ -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,

View File

@ -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"