feat: new window preview
This commit is contained in:
parent
5d450474a5
commit
0230701042
|
|
@ -8,7 +8,13 @@ export const SitePage: React.FC<{
|
|||
}> = ({ page }) => {
|
||||
return (
|
||||
<>
|
||||
<div className="">
|
||||
{page?.preview && (
|
||||
<div className="fixed top-0 left-0 w-full text-center text-orange-500 bg-gray-100 py-2 opacity-80 text-sm">
|
||||
Currently in private preview mode, the content is different from the
|
||||
public
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
{page?.tags?.includes("post") ? (
|
||||
<h2 className="xlog-post-title text-4xl font-bold">{page.title}</h2>
|
||||
) : (
|
||||
|
|
@ -23,7 +29,7 @@ export const SitePage: React.FC<{
|
|||
content={page?.body?.content}
|
||||
toc={true}
|
||||
></PageContent>
|
||||
<PostFooter page={page} />
|
||||
{page?.metadata && <PostFooter page={page} />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,14 +4,20 @@ import { PageVisibilityEnum } from "./types"
|
|||
export const getPageVisibility = ({
|
||||
date_published,
|
||||
metadata,
|
||||
preview,
|
||||
}: {
|
||||
date_published?: string
|
||||
metadata?: Object
|
||||
preview?: boolean
|
||||
}) => {
|
||||
if (!metadata) {
|
||||
return PageVisibilityEnum.Draft
|
||||
} else if (dayjs(date_published).isBefore(new Date())) {
|
||||
return PageVisibilityEnum.Published
|
||||
if (preview) {
|
||||
return PageVisibilityEnum.Modified
|
||||
} else {
|
||||
return PageVisibilityEnum.Published
|
||||
}
|
||||
} else {
|
||||
return PageVisibilityEnum.Scheduled
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ export enum PageVisibilityEnum {
|
|||
Scheduled = "scheduled",
|
||||
Draft = "draft",
|
||||
Crossbell = "crossbell",
|
||||
Modified = "published and modified",
|
||||
}
|
||||
|
||||
export type PostOnSiteHome = {
|
||||
|
|
@ -83,6 +84,7 @@ export type Note = UniNote & {
|
|||
mime_type?: string
|
||||
element?: ReactElement
|
||||
}
|
||||
preview?: boolean
|
||||
}
|
||||
|
||||
export type Notes = {
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ const expandPage = (page: Note, render: boolean) => {
|
|||
|
||||
const getLocalPages = (input: { site: string; isPost?: boolean }) => {
|
||||
const pages: Note[] = []
|
||||
getKeys(`draft-${input.site}-local`).forEach((key) => {
|
||||
getKeys(`draft-${input.site}`).forEach((key) => {
|
||||
const page = getStorage(key)
|
||||
if (input.isPost === undefined || page.isPost === input.isPost) {
|
||||
pages.push({
|
||||
|
|
@ -140,6 +140,7 @@ const getLocalPages = (input: { site: string; isPost?: boolean }) => {
|
|||
content: page.values?.content,
|
||||
mime_type: "text/markdown",
|
||||
},
|
||||
date_updated: new Date(page.date).toISOString(),
|
||||
date_published: page.values?.publishedAt,
|
||||
summary: {
|
||||
content: page.values?.excerpt,
|
||||
|
|
@ -161,6 +162,7 @@ const getLocalPages = (input: { site: string; isPost?: boolean }) => {
|
|||
},
|
||||
],
|
||||
}),
|
||||
preview: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
@ -225,8 +227,20 @@ export async function getPagesBySite(
|
|||
site: input.site,
|
||||
isPost: input.type === "post",
|
||||
})
|
||||
pages.list = pages.list.concat(local)
|
||||
pages.total += local.length
|
||||
local.forEach((localPage) => {
|
||||
const index = pages.list.findIndex((page) => page.id === localPage.id)
|
||||
if (
|
||||
index >= 0 &&
|
||||
new Date(localPage.date_updated) >
|
||||
new Date(pages.list[index].date_updated)
|
||||
) {
|
||||
localPage.metadata = pages.list[index].metadata
|
||||
pages.list[index] = localPage
|
||||
} else {
|
||||
pages.list.push(localPage)
|
||||
pages.total++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (pages?.list) {
|
||||
|
|
@ -285,43 +299,60 @@ export async function getPage<TRender extends boolean = false>(
|
|||
const local = getLocalPages({
|
||||
site: input.site,
|
||||
})
|
||||
const localPage = local.find((page) => page.id === input.pageId)
|
||||
const localPage = local.find(
|
||||
(page) => page.id === input.page || page.id === input.pageId,
|
||||
)
|
||||
|
||||
const mustLocal = input.pageId?.startsWith("local-")
|
||||
|
||||
const pages: Notes | null = mustLocal
|
||||
? null
|
||||
: await (customUnidata || unidata).notes.get({
|
||||
source: "Crossbell Note",
|
||||
identity: input.site,
|
||||
platform: "Crossbell",
|
||||
limit: 1000,
|
||||
...(input.pageId && {
|
||||
filter: {
|
||||
id: input.pageId,
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
||||
let page
|
||||
if (localPage) {
|
||||
page = localPage
|
||||
} else {
|
||||
const pages: Notes | null = await (customUnidata || unidata).notes.get({
|
||||
source: "Crossbell Note",
|
||||
identity: input.site,
|
||||
platform: "Crossbell",
|
||||
limit: 1000,
|
||||
...(input.pageId && {
|
||||
filter: {
|
||||
id: input.pageId,
|
||||
},
|
||||
}),
|
||||
if (input.page) {
|
||||
page = pages?.list.find((item) => {
|
||||
item.slug =
|
||||
item.attributes?.find((a) => a.trait_type === "xlog_slug")?.value ||
|
||||
item.metadata?.raw?._xlog_slug ||
|
||||
item.metadata?.raw?._crosslog_slug ||
|
||||
item.id
|
||||
return item.slug === input.page || item.id === input.page
|
||||
})
|
||||
} else {
|
||||
page = pages?.list[0]
|
||||
}
|
||||
|
||||
if (input.page) {
|
||||
page = pages?.list.find((item) => {
|
||||
item.slug =
|
||||
item.attributes?.find((a) => a.trait_type === "xlog_slug")?.value ||
|
||||
item.metadata?.raw?._xlog_slug ||
|
||||
item.metadata?.raw?._crosslog_slug ||
|
||||
item.id
|
||||
return item.slug === input.page || item.id === input.page
|
||||
})
|
||||
if (localPage) {
|
||||
if (page) {
|
||||
if (new Date(localPage.date_updated) > new Date(page.date_updated)) {
|
||||
localPage.metadata = page.metadata
|
||||
page = localPage
|
||||
}
|
||||
} else {
|
||||
page = pages?.list[0]
|
||||
page = localPage
|
||||
}
|
||||
}
|
||||
|
||||
if (!page) {
|
||||
if (!page && !mustLocal) {
|
||||
throw notFound(`page ${input.page} not found`)
|
||||
}
|
||||
|
||||
expandPage(page, input.render || false)
|
||||
if (page) {
|
||||
expandPage(page, input.render || false)
|
||||
} else {
|
||||
page = null
|
||||
}
|
||||
|
||||
return page
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
import { SiteLayout } from "~/components/site/SiteLayout"
|
||||
import { SitePage } from "~/components/site/SitePage"
|
||||
import { useGetPage } from "~/queries/page"
|
||||
import type { ReactElement } from "react"
|
||||
import { useRouter } from "next/router"
|
||||
|
||||
function SitePagePage() {
|
||||
const router = useRouter()
|
||||
const site = router.query.site as string
|
||||
const pageSlug = router.query.page as string
|
||||
|
||||
const page = useGetPage({
|
||||
site: site,
|
||||
page: pageSlug,
|
||||
render: true,
|
||||
includeAuthors: true,
|
||||
})
|
||||
|
||||
return <SitePage page={page.data} />
|
||||
}
|
||||
|
||||
SitePagePage.getLayout = (page: ReactElement) => {
|
||||
return <SiteLayout>{page}</SiteLayout>
|
||||
}
|
||||
|
||||
export default SitePagePage
|
||||
|
|
@ -24,7 +24,7 @@ import { getPageVisibility } from "~/lib/page-helpers"
|
|||
import { PageVisibilityEnum } from "~/lib/types"
|
||||
import { useGetPage, useCreateOrUpdatePage } from "~/queries/page"
|
||||
import { useGetSite } from "~/queries/site"
|
||||
import { getStorage, setStorage, delStorage } from "~/lib/storage"
|
||||
import { setStorage, delStorage } from "~/lib/storage"
|
||||
import { nanoid } from "nanoid"
|
||||
import { useQueryClient } from "@tanstack/react-query"
|
||||
import { PageContent } from "~/components/common/PageContent"
|
||||
|
|
@ -34,6 +34,7 @@ import type { Root } from "hast"
|
|||
import type { EditorView } from "@codemirror/view"
|
||||
import { Editor } from "~/components/ui/Editor"
|
||||
import { renderPageContent } from "~/markdown"
|
||||
import { Button } from "~/components/ui/Button"
|
||||
|
||||
const getInputDatetimeValue = (date: Date | string) => {
|
||||
const str = dayjs(date).format()
|
||||
|
|
@ -52,9 +53,13 @@ export default function SubdomainEditor() {
|
|||
useEffect(() => {
|
||||
if (subdomain) {
|
||||
if (!pageId) {
|
||||
const key = `draft-${subdomain}-local-${nanoid()}`
|
||||
const randomId = nanoid()
|
||||
const key = `draft-${subdomain}-local-${randomId}`
|
||||
setDraftKey(key)
|
||||
queryClient.invalidateQueries(["getPagesBySite", subdomain])
|
||||
router.replace(
|
||||
`/dashboard/${subdomain}/editor?id=local-${randomId}&type=${router.query.type}`,
|
||||
)
|
||||
} else {
|
||||
setDraftKey(`draft-${subdomain}-${pageId}`)
|
||||
}
|
||||
|
|
@ -65,14 +70,13 @@ export default function SubdomainEditor() {
|
|||
|
||||
const page = useGetPage({
|
||||
site: subdomain!,
|
||||
pageId: pageId,
|
||||
pageId: pageId || draftKey.replace(`draft-${subdomain}-`, ""),
|
||||
render: false,
|
||||
})
|
||||
|
||||
const visibility = page.data
|
||||
? getPageVisibility(page.data)
|
||||
: PageVisibilityEnum.Draft
|
||||
const published = visibility !== PageVisibilityEnum.Draft
|
||||
|
||||
const uploadFile = useUploadFile()
|
||||
|
||||
|
|
@ -161,25 +165,18 @@ export default function SubdomainEditor() {
|
|||
useEffect(() => {
|
||||
if (!page.data || !draftKey) return
|
||||
|
||||
const local = getStorage(draftKey)
|
||||
const useLocal =
|
||||
local?.date && +new Date(local?.date) > +new Date(page.data.date_updated)
|
||||
if (useLocal && local?.values) {
|
||||
setValues(local?.values)
|
||||
} else {
|
||||
setValues({
|
||||
title: page.data.title || "",
|
||||
publishedAt: page.data.date_published,
|
||||
published: !!page.data.id,
|
||||
excerpt: page.data.summary?.content || "",
|
||||
slug: page.data.slug || "",
|
||||
tags:
|
||||
page.data.tags
|
||||
?.filter((tag) => tag !== "post" && tag !== "page")
|
||||
?.join(", ") || "",
|
||||
content: page.data.body?.content || "",
|
||||
})
|
||||
}
|
||||
setValues({
|
||||
title: page.data.title || "",
|
||||
publishedAt: page.data.date_published,
|
||||
published: !!page.data.id,
|
||||
excerpt: page.data.summary?.content || "",
|
||||
slug: page.data.slug || "",
|
||||
tags:
|
||||
page.data.tags
|
||||
?.filter((tag) => tag !== "post" && tag !== "page")
|
||||
?.join(", ") || "",
|
||||
content: page.data.body?.content || "",
|
||||
})
|
||||
}, [page.data, subdomain, draftKey])
|
||||
|
||||
const [currentScrollArea, setCurrentScrollArea] = useState<string>("")
|
||||
|
|
@ -353,25 +350,37 @@ export default function SubdomainEditor() {
|
|||
<div className="flex items-center space-x-3">
|
||||
<span
|
||||
className={clsx(
|
||||
`text-sm`,
|
||||
published ? `text-accent` : `text-zinc-300`,
|
||||
`text-sm capitalize`,
|
||||
visibility === PageVisibilityEnum.Draft
|
||||
? `text-zinc-300`
|
||||
: visibility === PageVisibilityEnum.Modified
|
||||
? "text-orange-600"
|
||||
: "text-green-600",
|
||||
)}
|
||||
>
|
||||
{visibility === PageVisibilityEnum.Published
|
||||
? `Published${
|
||||
getStorage(draftKey)
|
||||
? " and local modifications exist"
|
||||
: ""
|
||||
}`
|
||||
: visibility === PageVisibilityEnum.Scheduled
|
||||
? "Scheduled"
|
||||
: "Draft"}
|
||||
{visibility.toLowerCase()}
|
||||
</span>
|
||||
<Button
|
||||
isAutoWidth
|
||||
onClick={() => {
|
||||
window.open(
|
||||
`/_site/${subdomain}/preview/${draftKey.replace(
|
||||
`draft-${subdomain}-`,
|
||||
"",
|
||||
)}`,
|
||||
)
|
||||
}}
|
||||
>
|
||||
Preview
|
||||
</Button>
|
||||
<PublishButton
|
||||
save={savePage}
|
||||
published={published}
|
||||
published={visibility !== PageVisibilityEnum.Draft}
|
||||
isSaving={createOrUpdatePage.isLoading}
|
||||
isDisabled={published && !getStorage(draftKey) ? true : false}
|
||||
isDisabled={
|
||||
visibility !== PageVisibilityEnum.Modified &&
|
||||
visibility !== PageVisibilityEnum.Draft
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
|||
Loading…
Reference in New Issue