diff --git a/src/components/dashboard/PagesManager.tsx b/src/components/dashboard/PagesManager.tsx index ab76b6c7..ab3a1d55 100644 --- a/src/components/dashboard/PagesManager.tsx +++ b/src/components/dashboard/PagesManager.tsx @@ -11,7 +11,7 @@ import Link from "next/link" import toast from "react-hot-toast" import { EmptyState } from "../ui/EmptyState" import type { Note } from "unidata.js" -import { getPagesBySite, deletePage } from "~/models/page.model" +import { useGetPagesBySite, useDeletePage } from "~/queries/page" export const PagesManager: React.FC<{ isPost: boolean @@ -27,39 +27,21 @@ export const PagesManager: React.FC<{ [router.query.visibility] ) - let [pages, setPages] = useState(null) - - const pageDelete = (id: string) => { - deletePage({ - site: subdomain, - id: id, - }).then(() => { - toast.success("Deleted!") - getPagesBySite({ - type: isPost ? "post" : "page", - site: subdomain, - take: 1000, - visibility, - render: true, - }).then((pages) => { - setPages(pages?.list || []) - }) - }) - } + const deletePage = useDeletePage() useEffect(() => { - if (subdomain) { - getPagesBySite({ - type: isPost ? "post" : "page", - site: subdomain!, - take: 1000, - visibility, - render: true, - }).then((pages) => { - setPages(pages?.list || []) - }) + if (deletePage.isSuccess) { + toast.success("Deleted!") } - }, [subdomain, isPost, visibility]) + }, [deletePage.isSuccess]) + + const pages = useGetPagesBySite({ + type: isPost ? "post" : "page", + site: subdomain!, + take: 1000, + visibility, + render: true, + }) const tabItems: TabItem[] = [ { @@ -186,11 +168,11 @@ export const PagesManager: React.FC<{
- {pages && pages.length === 0 && ( + {pages.data?.list.length === 0 && ( )} - {pages?.map((page) => { + {pages.data?.list?.map((page) => { return ( diff --git a/src/components/site/SiteArchives.tsx b/src/components/site/SiteArchives.tsx index 2e8ad3ad..ed78e97b 100644 --- a/src/components/site/SiteArchives.tsx +++ b/src/components/site/SiteArchives.tsx @@ -5,7 +5,7 @@ import { EmptyState } from "../ui/EmptyState" import { Notes, Note } from "~/lib/types" export const SiteArchives: React.FC<{ - posts: Notes + posts?: Notes }> = ({ posts }) => { const groupedByYear = useMemo>(() => { const map = new Map() diff --git a/src/components/site/SitePage.tsx b/src/components/site/SitePage.tsx index c0571197..e9f8afa9 100644 --- a/src/components/site/SitePage.tsx +++ b/src/components/site/SitePage.tsx @@ -6,17 +6,17 @@ import { Profile, Note } from "~/lib/types" export const SitePage: React.FC<{ site?: Profile, - page: Note + page?: Note }> = ({ site, page }) => { return ( <>
- {page.tags?.includes("post") ? ( + {page?.tags?.includes("post") ? (

{page.title}

) : ( -

{page.title}

+

{page?.title}

)} - {page.tags?.includes("post") && ( + {page?.tags?.includes("post") && (
-

🔔 This {page.tags?.includes("post") ? "post" : "page"} has been permanently stored on-chain and signed by its creator.

+

🔔 This {page?.tags?.includes("post") ? "post" : "page"} has been permanently stored on-chain and signed by its creator.

diff --git a/src/models/page.model.ts b/src/models/page.model.ts index 97272a94..d29493c2 100644 --- a/src/models/page.model.ts +++ b/src/models/page.model.ts @@ -119,7 +119,7 @@ export async function scheduleEmailForPost( export async function getPagesBySite( input: { - site: string + site?: string type: "post" | "page" visibility?: PageVisibilityEnum | null take?: number | null @@ -138,12 +138,15 @@ export async function getPagesBySite( const visibility = input.visibility || PageVisibilityEnum.All - let pages: Notes | null = await unidata.notes.get({ + let pages: Notes = await unidata.notes.get({ source: 'Crossbell Note', identity: input.site, platform: 'Crossbell', limit: input.take || 1000, - }); + }) || { + total: 0, + list: [], + }; if (pages?.list) { switch (visibility) { @@ -203,8 +206,8 @@ export async function getPage( includeAuthors?: boolean }, ) { - if (!input.site) { - throw notFound(`site not found`) + if (!input.site || !(input.page || input.pageId)) { + return undefined } const pages: Notes | null = await unidata.notes.get({ diff --git a/src/pages/_site/[site]/[page].tsx b/src/pages/_site/[site]/[page].tsx index eeb0343b..03b43b86 100644 --- a/src/pages/_site/[site]/[page].tsx +++ b/src/pages/_site/[site]/[page].tsx @@ -10,7 +10,8 @@ import { Viewer, Profile, Note } from "~/lib/types" import { queryClient, prefetchGetSite } from "~/queries/site.server" import { useGetSite } from "~/queries/site" import { dehydrate, QueryClient } from '@tanstack/react-query' -import { getPage } from "~/models/page.model" +import { fetchGetPage } from "~/queries/page.server" +import { useGetPage } from "~/queries/page" import { PageVisibilityEnum } from "~/lib/types" import { notFound } from "~/lib/server-side-props" @@ -20,15 +21,12 @@ export const getServerSideProps: GetServerSideProps = serverSidePropsHandler( const pageSlug = ctx.params!.page as string await prefetchGetSite(domainOrSubdomain) - - const [page] = await Promise.all([ - getPage({ - site: domainOrSubdomain, - page: pageSlug, - render: true, - includeAuthors: true, - }), - ]) + const page = await fetchGetPage({ + site: domainOrSubdomain, + page: pageSlug, + render: true, + includeAuthors: true, + }) if (new Date(page.date_published) > new Date()) { throw notFound() @@ -37,30 +35,37 @@ export const getServerSideProps: GetServerSideProps = serverSidePropsHandler( return { props: { dehydratedState: dehydrate(queryClient), - page, domainOrSubdomain, + pageSlug, }, } }, ) function SitePagePage({ - page, domainOrSubdomain, + pageSlug, }: { - page: Note, domainOrSubdomain: string + pageSlug: string }) { - const ogDescription = page.summary?.content || page.body?.content + const page = useGetPage({ + site: domainOrSubdomain, + page: pageSlug, + render: true, + includeAuthors: true, + }) + const ogDescription = page.data?.summary?.content || page.data?.body?.content + const site = useGetSite(domainOrSubdomain) return ( - + ) } diff --git a/src/pages/_site/[site]/archives.tsx b/src/pages/_site/[site]/archives.tsx index ea1f6bd4..a7bd563d 100644 --- a/src/pages/_site/[site]/archives.tsx +++ b/src/pages/_site/[site]/archives.tsx @@ -10,7 +10,8 @@ import { Viewer, Profile, Notes } from "~/lib/types" import { queryClient, prefetchGetSite } from "~/queries/site.server" import { useGetSite } from "~/queries/site" import { dehydrate, QueryClient } from '@tanstack/react-query' -import { getPagesBySite } from "~/models/page.model" +import { useGetPagesBySite } from "~/queries/page" +import { prefetchGetPagesBySite } from "~/queries/page.server" import { PageVisibilityEnum } from "~/lib/types" export const getServerSideProps: GetServerSideProps = serverSidePropsHandler( @@ -18,20 +19,16 @@ export const getServerSideProps: GetServerSideProps = serverSidePropsHandler( const domainOrSubdomain = ctx.params!.site as string await prefetchGetSite(domainOrSubdomain) - - const [posts] = await Promise.all([ - getPagesBySite({ - site: domainOrSubdomain, - take: 1000, - type: "post", - visibility: PageVisibilityEnum.Published, - }), - ]) + await prefetchGetPagesBySite({ + site: domainOrSubdomain, + take: 1000, + type: "post", + visibility: PageVisibilityEnum.Published, + }) return { props: { dehydratedState: dehydrate(queryClient), - posts, domainOrSubdomain, }, } @@ -39,7 +36,6 @@ export const getServerSideProps: GetServerSideProps = serverSidePropsHandler( ) function SiteArchivesPage({ - posts, domainOrSubdomain, }: { site: Profile, @@ -47,13 +43,19 @@ function SiteArchivesPage({ domainOrSubdomain: string }) { const site = useGetSite(domainOrSubdomain) + const posts = useGetPagesBySite({ + site: domainOrSubdomain, + take: 1000, + type: "post", + visibility: PageVisibilityEnum.Published, + }) return ( - + ) } diff --git a/src/pages/_site/[site]/index.tsx b/src/pages/_site/[site]/index.tsx index cc6c1199..1f91d5aa 100644 --- a/src/pages/_site/[site]/index.tsx +++ b/src/pages/_site/[site]/index.tsx @@ -10,45 +10,45 @@ import { useEffect, useState } from 'react' import { queryClient, prefetchGetSite } from "~/queries/site.server" import { useGetSite } from "~/queries/site" import { dehydrate, QueryClient } from '@tanstack/react-query' -import { getPagesBySite } from "~/models/page.model" +import { useGetPagesBySite } from "~/queries/page" +import { prefetchGetPagesBySite } from "~/queries/page.server" import { PageVisibilityEnum } from "~/lib/types" export const getServerSideProps: GetServerSideProps = async (ctx) => { const domainOrSubdomain = ctx.params!.site as string await prefetchGetSite(domainOrSubdomain) - - const [posts] = await Promise.all([ - getPagesBySite({ - site: domainOrSubdomain, - take: 1000, - type: "post", - visibility: PageVisibilityEnum.Published, - }), - ]) + await prefetchGetPagesBySite({ + site: domainOrSubdomain, + take: 1000, + type: "post", + visibility: PageVisibilityEnum.Published, + }) return { props: { dehydratedState: dehydrate(queryClient), - posts, domainOrSubdomain, }, } } function SiteIndexPage({ - posts, domainOrSubdomain, }: { - site: Profile, - posts: Notes, domainOrSubdomain: string }) { const site = useGetSite(domainOrSubdomain) + const posts = useGetPagesBySite({ + site: domainOrSubdomain, + take: 1000, + type: "post", + visibility: PageVisibilityEnum.Published, + }) return ( - + ) } diff --git a/src/pages/dashboard/[subdomain]/editor.tsx b/src/pages/dashboard/[subdomain]/editor.tsx index e6590750..faed2993 100644 --- a/src/pages/dashboard/[subdomain]/editor.tsx +++ b/src/pages/dashboard/[subdomain]/editor.tsx @@ -21,7 +21,7 @@ import { PageVisibilityEnum, Note } from "~/lib/types" import { FieldLabel } from "~/components/ui/FieldLabel" import { Button } from "~/components/ui/Button" import { useStore } from "~/lib/store" -import { getPage, createOrUpdatePage } from "~/models/page.model" +import { useGetPage, useCreateOrUpdatePage } from "~/queries/page" const getInputDatetimeValue = (date: Date | string) => { const str = dayjs(date).format() @@ -35,29 +35,19 @@ export default function SubdomainEditor() { const isPost = router.query.type === "post" const pageId = router.query.id as string | undefined - let [page, setPage] = useState(undefined) + const page = useGetPage({ + site: subdomain!, + pageId: pageId, + render: false + }) - useEffect(() => { - if (subdomain && pageId) { - getPage({ - site: subdomain!, - pageId: pageId, - render: false - }).then((page) => { - setPage(page) - }) - } - }, [subdomain, isPost, pageId]) - - const pageContent = useMemo(() => page?.body?.content, [page?.body?.content]) + const pageContent = useMemo(() => page.data?.body?.content, [page.data?.body?.content]) const visibility = getPageVisibility({ - date_published: page?.date_published, + date_published: page.data?.date_published, }) const published = visibility !== PageVisibilityEnum.Draft - const [createOrUpdatePageStatus, setCreateOrUpdatePageStatus] = useState('') - const uploadFile = useUploadFile() const [values, setValues] = useState({ @@ -83,26 +73,29 @@ export default function SubdomainEditor() { [setValues, values], ) + const createOrUpdatePage = useCreateOrUpdatePage() + const savePage = (published: boolean) => { - setCreateOrUpdatePageStatus("loading") - createOrUpdatePage({ + createOrUpdatePage.mutate({ ...values, siteId: subdomain, pageId: pageId, isPost: isPost, published, content, - }).then((result) => { - if (result && result.code === 0) { - setCreateOrUpdatePageStatus("success") - toast.success(values.published ? "Updated" : "Saved!") - } else { - setCreateOrUpdatePageStatus("error") - toast.error(result.message) - } }) } + useEffect(() => { + if (createOrUpdatePage.data) { + if (createOrUpdatePage.data?.code === 0) { + toast.success(values.published ? "Updated" : "Saved!") + } else { + toast.error("Error: " + createOrUpdatePage.data?.message) + } + } + }, [createOrUpdatePage.data, values.published]) + const handleDropFile = useCallback( async (file: File, view: EditorView) => { const toastId = toast.loading("Uploading...") @@ -141,16 +134,16 @@ export default function SubdomainEditor() { }) useEffect(() => { - if (!page) return + if (!page.data) return setValues({ - title: page.title || '', - 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!, + title: page.data.title || '', + publishedAt: page.data.date_published === new Date('9999-01-01').toISOString() ? new Date().toISOString() : page.data.date_published, + published: page.data.date_published !== new Date('9999-01-01').toISOString(), + excerpt: page.data.summary?.content || "", + slug: page.data.slug!, }) - }, [page]) + }, [page.data]) useEffect(() => { if (typeof pageContent === "string") { @@ -186,7 +179,7 @@ export default function SubdomainEditor() {
diff --git a/src/pages/dashboard/[subdomain]/settings/domains.tsx b/src/pages/dashboard/[subdomain]/settings/domains.tsx index 1979f96d..315f3296 100644 --- a/src/pages/dashboard/[subdomain]/settings/domains.tsx +++ b/src/pages/dashboard/[subdomain]/settings/domains.tsx @@ -52,7 +52,7 @@ export default function SettingsDomainsPage() { if (site.isSuccess && site.data) { form.setValue("subdomain", site.data.username || "") } - }, [form, site]) + }, [form, site.data, site.isSuccess]) const title = "Site Settings" return ( diff --git a/src/pages/dashboard/[subdomain]/settings/navigation.tsx b/src/pages/dashboard/[subdomain]/settings/navigation.tsx index 6e3a7e9a..0f9db613 100644 --- a/src/pages/dashboard/[subdomain]/settings/navigation.tsx +++ b/src/pages/dashboard/[subdomain]/settings/navigation.tsx @@ -78,7 +78,7 @@ export default function SiteSettingsNavigationPage() { const itemsModified = useMemo(() => { if (!site.isSuccess) return false return !equal(items, site.data.navigation) - }, [items, site]) + }, [items, site.data, site.isSuccess]) const updateItem: UpdateItem = (id, newItem) => { setItems((items) => { diff --git a/src/queries/page.server.ts b/src/queries/page.server.ts new file mode 100644 index 00000000..7cdfedb9 --- /dev/null +++ b/src/queries/page.server.ts @@ -0,0 +1,17 @@ +import * as pageModel from "~/models/page.model" +import { queryClientServer, cacheMaxAge } from "~/queries/server" + +export const queryClient = queryClientServer + +export const fetchGetPage = async (input: Parameters[0]) => { + return await queryClientServer.fetchQuery(['getPage', input.page, input], async () => { + return pageModel.getPage(input) + }, { staleTime: cacheMaxAge }) +} + +export const prefetchGetPagesBySite = async (input: Parameters[0]) => { + await queryClientServer.fetchQuery(['getPagesBySite', input.site, input], async () => { + return pageModel.getPagesBySite(input) + }, { staleTime: cacheMaxAge }) + } + \ No newline at end of file diff --git a/src/queries/page.ts b/src/queries/page.ts new file mode 100644 index 00000000..f618863f --- /dev/null +++ b/src/queries/page.ts @@ -0,0 +1,41 @@ +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' +import * as pageModel from "~/models/page.model" +import { PageVisibilityEnum } from "~/lib/types" + +export const useGetPagesBySite = (input: Parameters[0]) => { + return useQuery(['getPagesBySite', input.site, input], async () => { + return pageModel.getPagesBySite(input) + }) +} + +export const useGetPage = (input: Parameters[0]) => { + return useQuery(['getPage', input.page, input], async () => { + return pageModel.getPage(input) + }) +} + +export function useCreateOrUpdatePage() { + const queryClient = useQueryClient(); + const mutation = useMutation(async ( + payload: Parameters[0]) => { + return pageModel.createOrUpdatePage(payload) + }, { + onSuccess: (data, variables) => { + queryClient.invalidateQueries(['getPagesBySite', variables.siteId]) + queryClient.invalidateQueries(['getPage', variables.pageId]) + }, + }) + return mutation +} + +export function useDeletePage() { + const queryClient = useQueryClient(); + return useMutation(async (input: Parameters[0]) => { + return pageModel.deletePage(input) + }, { + onSuccess: (data, variables) => { + queryClient.invalidateQueries(['getPagesBySite', variables.site]) + queryClient.invalidateQueries(['getPage', variables.id]) + }, + }) +} diff --git a/src/queries/server.ts b/src/queries/server.ts new file mode 100644 index 00000000..f3b306f1 --- /dev/null +++ b/src/queries/server.ts @@ -0,0 +1,7 @@ +import { QueryClient } from '@tanstack/react-query' + +const queryClientServerIn = new QueryClient() + +export const cacheMaxAge = parseInt(process.env.CACHE_MAX_AGE || "600000") + +export const queryClientServer = queryClientServerIn diff --git a/src/queries/site.server.ts b/src/queries/site.server.ts index eb27974f..844e4af7 100644 --- a/src/queries/site.server.ts +++ b/src/queries/site.server.ts @@ -1,8 +1,5 @@ import * as siteModel from "~/models/site.model" -import { QueryClient } from '@tanstack/react-query' - -const queryClientServer = new QueryClient() -const cacheMaxAge = parseInt(process.env.CACHE_MAX_AGE || "600000") +import { queryClientServer, cacheMaxAge } from "~/queries/server" export const queryClient = queryClientServer