feat: crossbell renovation - site home page
This commit is contained in:
parent
22fd6b3c4c
commit
e53aa3ea02
|
|
@ -1,5 +1,6 @@
|
|||
import { APP_NAME, OUR_DOMAIN } from "~/lib/env"
|
||||
import { UniLink } from "../ui/UniLink"
|
||||
import { IS_PROD } from "~/lib/constants"
|
||||
|
||||
export const SiteFooter: React.FC<{ site: { name: string } }> = ({ site }) => {
|
||||
return (
|
||||
|
|
@ -16,6 +17,13 @@ export const SiteFooter: React.FC<{ site: { name: string } }> = ({ site }) => {
|
|||
className="hover:text-indigo-500"
|
||||
>
|
||||
{APP_NAME}
|
||||
</UniLink>{" "}
|
||||
· {" "}
|
||||
<UniLink
|
||||
href={`${IS_PROD ? "https" : "http"}://${OUR_DOMAIN}/dashboard`}
|
||||
className="hover:text-indigo-500"
|
||||
>
|
||||
Dashboard
|
||||
</UniLink>
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { DashboardIcon } from "../icons/DashboardIcon"
|
|||
import { Avatar } from "../ui/Avatar"
|
||||
import { Button } from "../ui/Button"
|
||||
import { UniLink } from "../ui/UniLink"
|
||||
import { Profile } from "unidata.js"
|
||||
|
||||
export type HeaderLinkType = {
|
||||
icon?: React.ReactNode
|
||||
|
|
@ -43,7 +44,7 @@ export const SiteHeader: React.FC<{
|
|||
icon: string | null | undefined
|
||||
navigation?: HeaderLinkType[]
|
||||
subscribed?: boolean
|
||||
viewer: Viewer | null
|
||||
viewer?: Profile | null
|
||||
}> = ({ siteName, description, icon, navigation, subscribed, viewer }) => {
|
||||
const setSubscribeModalOpened = useStore(
|
||||
(store) => store.setSubscribeModalOpened,
|
||||
|
|
@ -59,20 +60,6 @@ export const SiteHeader: React.FC<{
|
|||
...(navigation || []),
|
||||
]
|
||||
|
||||
const dropdownLinks: HeaderLinkType[] = [
|
||||
{
|
||||
icon: <DashboardIcon />,
|
||||
label: "Writer dashboard",
|
||||
url: `${IS_PROD ? "https" : "http"}://${OUR_DOMAIN}/dashboard`,
|
||||
},
|
||||
{
|
||||
label: "Sign out",
|
||||
onClick() {
|
||||
logout()
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<header className="border-b">
|
||||
<div className="px-5 max-w-screen-md mx-auto">
|
||||
|
|
@ -90,36 +77,6 @@ export const SiteHeader: React.FC<{
|
|||
{description && (
|
||||
<div className="text-gray-500 text-sm">{description}</div>
|
||||
)}
|
||||
<div className="mt-3 text-sm">
|
||||
<Button
|
||||
rounded="full"
|
||||
size="sm"
|
||||
variant={subscribed ? "secondary" : "primary"}
|
||||
onClick={handleClickSubscribe}
|
||||
className="space-x-1"
|
||||
>
|
||||
<span className="pl-1">
|
||||
{subscribed ? (
|
||||
<svg className="w-4 h-4" viewBox="0 0 24 24">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="m23.5 17l-5 5l-3.5-3.5l1.5-1.5l2 2l3.5-3.5l1.5 1.5M13 18H3V8l8 5l8-5v5h2V6c0-1.1-.9-2-2-2H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h10v-2m6-12l-8 5l-8-5h16Z"
|
||||
></path>
|
||||
</svg>
|
||||
) : (
|
||||
<svg className="w-4 h-4" viewBox="0 0 24 24">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M19 15v3h-3v2h3v3h2v-3h3v-2h-3v-3h-2m-5 3H3V8l8 5l8-5v5h2V6c0-1.1-.9-2-2-2H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h11v-2m5-12l-8 5l-8-5h16Z"
|
||||
></path>
|
||||
</svg>
|
||||
)}
|
||||
</span>
|
||||
<span className="pr-1">
|
||||
{subscribed ? "Subscribed" : "Subscribe"}
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -129,41 +86,6 @@ export const SiteHeader: React.FC<{
|
|||
return <HeaderLink link={link} key={`${link.label}${i}`} />
|
||||
})}
|
||||
</div>
|
||||
{viewer ? (
|
||||
<div className="relative group">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex space-x-2 items-center h-10"
|
||||
>
|
||||
<Avatar
|
||||
images={[getUserContentsUrl(viewer.avatar)]}
|
||||
name={viewer.name}
|
||||
size={24}
|
||||
/>
|
||||
<span>{viewer.name}</span>
|
||||
</button>
|
||||
<div className="absolute hidden right-0 pt-1 group-hover:block">
|
||||
<div className="bg-white rounded-lg ring-1 ring-zinc-100 min-w-[140px] shadow-md py-2">
|
||||
{dropdownLinks.map((link, i) => {
|
||||
return (
|
||||
<UniLink
|
||||
key={i}
|
||||
href={link.url}
|
||||
onClick={link.onClick}
|
||||
className="px-4 h-8 flex items-center w-full whitespace-nowrap hover:bg-zinc-100"
|
||||
>
|
||||
{link.label}
|
||||
</UniLink>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<button type="button" onClick={() => setLoginModalOpened(true)}>
|
||||
Sign In
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ import Link from "next/link"
|
|||
import { formatDate } from "~/lib/date"
|
||||
import { Paginated, type PostOnSiteHome } from "~/lib/types"
|
||||
import { EmptyState } from "../ui/EmptyState"
|
||||
import { Notes } from "unidata.js"
|
||||
|
||||
export const SiteHome: React.FC<{
|
||||
posts?: Paginated<PostOnSiteHome>
|
||||
posts?: Notes
|
||||
}> = ({ posts }) => {
|
||||
if (!posts) return null
|
||||
|
||||
|
|
@ -13,14 +14,14 @@ export const SiteHome: React.FC<{
|
|||
{posts.total === 0 && <EmptyState />}
|
||||
{posts.total > 0 && (
|
||||
<div className="space-y-8">
|
||||
{posts.nodes.map((post) => {
|
||||
const excerpt = post.excerpt || post.autoExcerpt
|
||||
{posts.list.map((post) => {
|
||||
const excerpt = post.summary?.content
|
||||
return (
|
||||
<Link key={post.id} href={`/${post.slug}`}>
|
||||
<Link key={post.id} href={`/${post.id}`}>
|
||||
<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">
|
||||
{formatDate(post.publishedAt)}
|
||||
{formatDate(post.date_published)}
|
||||
</div>
|
||||
<div className="mt-3 text-zinc-500">
|
||||
{excerpt}
|
||||
|
|
|
|||
|
|
@ -8,16 +8,11 @@ import { SiteFooter } from "./SiteFooter"
|
|||
import { SiteHeader } from "./SiteHeader"
|
||||
import { useRouter } from "next/router"
|
||||
import { useStore } from "~/lib/store"
|
||||
import { Profile } from "unidata.js"
|
||||
|
||||
export type SiteLayoutProps = {
|
||||
site: {
|
||||
id: string
|
||||
name: string
|
||||
description?: string | null
|
||||
icon?: string | null
|
||||
navigation?: SiteNavigationItem[] | null
|
||||
}
|
||||
viewer: Viewer | null
|
||||
site: Profile
|
||||
viewer?: Profile | null
|
||||
subscription?: { email?: boolean } | null
|
||||
children: React.ReactNode
|
||||
title?: string | null
|
||||
|
|
@ -47,29 +42,22 @@ export const SiteLayout: React.FC<SiteLayoutProps> = ({
|
|||
<>
|
||||
<SEOHead
|
||||
title={title || ""}
|
||||
siteName={site.name}
|
||||
description={ogDescription ?? site.description}
|
||||
image={getUserContentsUrl(site.icon)}
|
||||
siteName={site?.name || ""}
|
||||
description={ogDescription ?? site?.bio}
|
||||
image={getUserContentsUrl(site?.avatars?.[0])}
|
||||
/>
|
||||
<SiteHeader
|
||||
navigation={site!.navigation || []}
|
||||
siteName={site!.name}
|
||||
description={site?.description}
|
||||
icon={site!.icon}
|
||||
navigation={site?.navigation || []}
|
||||
siteName={site?.name}
|
||||
description={site?.bio}
|
||||
icon={site?.avatars?.[0]}
|
||||
subscribed={!!subscription}
|
||||
viewer={viewer}
|
||||
/>
|
||||
<div className={clsx(`max-w-screen-md mx-auto px-5 pb-12`, `pt-12`)}>
|
||||
{children}
|
||||
</div>
|
||||
<SiteFooter site={{ name: site!.name }} />
|
||||
{site.id && (
|
||||
<SubscribeModal
|
||||
siteId={site.id}
|
||||
subscription={subscription}
|
||||
isLoggedIn={!!viewer}
|
||||
/>
|
||||
)}
|
||||
<SiteFooter site={{ name: site?.name || "" }} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
import Unidata from "unidata.js"
|
||||
|
||||
let unidata: Unidata | null = null;
|
||||
if (typeof window !== "undefined") {
|
||||
unidata = new Unidata()
|
||||
}
|
||||
let unidata = new Unidata()
|
||||
|
||||
export default unidata
|
||||
|
|
|
|||
|
|
@ -66,35 +66,28 @@ export async function createOrUpdatePage(
|
|||
isPost?: boolean
|
||||
},
|
||||
) {
|
||||
if (unidata) {
|
||||
return await unidata.notes.set({
|
||||
source: 'Crossbell Note',
|
||||
identity: input.siteId,
|
||||
platform: 'Crossbell',
|
||||
action: input.pageId ? 'update' : 'add',
|
||||
}, {
|
||||
...(input.pageId && { id: input.pageId }),
|
||||
...(input.title && { title: input.title }),
|
||||
...(input.content && {
|
||||
body: {
|
||||
content: input.content,
|
||||
mime_type: 'text/markdown',
|
||||
}}),
|
||||
...(input.publishedAt && { date_published: input.published ? input.publishedAt : new Date('9999-01-01').toISOString() }),
|
||||
...(input.excerpt && {
|
||||
summary: {
|
||||
content: input.excerpt,
|
||||
mime_type: 'text/markdown',
|
||||
}
|
||||
}),
|
||||
tags: [input.isPost ? "post" : "page"],
|
||||
})
|
||||
} else {
|
||||
return {
|
||||
code: 1,
|
||||
message: 'Unidata is not found',
|
||||
}
|
||||
}
|
||||
return await unidata.notes.set({
|
||||
source: 'Crossbell Note',
|
||||
identity: input.siteId,
|
||||
platform: 'Crossbell',
|
||||
action: input.pageId ? 'update' : 'add',
|
||||
}, {
|
||||
...(input.pageId && { id: input.pageId }),
|
||||
...(input.title && { title: input.title }),
|
||||
...(input.content && {
|
||||
body: {
|
||||
content: input.content,
|
||||
mime_type: 'text/markdown',
|
||||
}}),
|
||||
...(input.publishedAt && { date_published: input.published ? input.publishedAt : new Date('9999-01-01').toISOString() }),
|
||||
...(input.excerpt && {
|
||||
summary: {
|
||||
content: input.excerpt,
|
||||
mime_type: 'text/markdown',
|
||||
}
|
||||
}),
|
||||
tags: [input.isPost ? "post" : "page"],
|
||||
})
|
||||
}
|
||||
|
||||
export async function scheduleEmailForPost(
|
||||
|
|
@ -147,63 +140,58 @@ export async function getPagesBySite(
|
|||
|
||||
const visibility = input.visibility || PageVisibilityEnum.All
|
||||
|
||||
let pages: Notes | null = null
|
||||
if (unidata) {
|
||||
pages = await unidata.notes.get({
|
||||
source: 'Crossbell Note',
|
||||
identity: input.site,
|
||||
platform: 'Crossbell',
|
||||
limit: input.take || 100,
|
||||
});
|
||||
let pages = await unidata.notes.get({
|
||||
source: 'Crossbell Note',
|
||||
identity: input.site,
|
||||
platform: 'Crossbell',
|
||||
limit: input.take || 1000,
|
||||
});
|
||||
|
||||
if (pages?.list) {
|
||||
switch (visibility) {
|
||||
case PageVisibilityEnum.Published:
|
||||
pages.list = pages.list.filter(page => +new Date(page.date_published) <= +new Date())
|
||||
break
|
||||
case PageVisibilityEnum.Draft:
|
||||
pages.list = pages.list.filter(page => page.date_published === new Date('9999-01-01').toISOString())
|
||||
break
|
||||
case PageVisibilityEnum.Scheduled:
|
||||
pages.list = pages.list.filter(page => +new Date(page.date_published) > +new Date() && page.date_published !== new Date('9999-01-01').toISOString())
|
||||
break
|
||||
}
|
||||
pages.list = pages.list.filter(page => page.tags?.includes(input.type))
|
||||
pages.total = pages.list.length
|
||||
|
||||
pages.list = await Promise.all(pages?.list.map(async (page) => {
|
||||
if (page.body?.content && page.body?.mime_type === 'text/markdown' && input.render) {
|
||||
const rendered = await renderPageContent(page.body.content)
|
||||
page.body = {
|
||||
content: rendered.contentHTML,
|
||||
if (pages?.list) {
|
||||
switch (visibility) {
|
||||
case PageVisibilityEnum.Published:
|
||||
pages.list = pages.list.filter(page => +new Date(page.date_published) <= +new Date())
|
||||
break
|
||||
case PageVisibilityEnum.Draft:
|
||||
pages.list = pages.list.filter(page => page.date_published === new Date('9999-01-01').toISOString())
|
||||
break
|
||||
case PageVisibilityEnum.Scheduled:
|
||||
pages.list = pages.list.filter(page => +new Date(page.date_published) > +new Date() && page.date_published !== new Date('9999-01-01').toISOString())
|
||||
break
|
||||
}
|
||||
pages.list = pages.list.filter(page => page.tags?.includes(input.type))
|
||||
pages.total = pages.list.length
|
||||
|
||||
pages.list = await Promise.all(pages?.list.map(async (page) => {
|
||||
if (page.body?.content && page.body?.mime_type === 'text/markdown' && input.render) {
|
||||
const rendered = await renderPageContent(page.body.content)
|
||||
page.body = {
|
||||
content: rendered.contentHTML,
|
||||
mime_type: 'text/html'
|
||||
}
|
||||
if (!page.summary) {
|
||||
page.summary = {
|
||||
content: rendered.excerpt,
|
||||
mime_type: 'text/html'
|
||||
}
|
||||
if (!page.summary) {
|
||||
page.summary = {
|
||||
content: rendered.excerpt,
|
||||
mime_type: 'text/html'
|
||||
}
|
||||
}
|
||||
}
|
||||
return page
|
||||
}))
|
||||
}
|
||||
|
||||
return pages
|
||||
}
|
||||
return page
|
||||
}))
|
||||
}
|
||||
|
||||
return pages
|
||||
}
|
||||
|
||||
export async function deletePage({ site, id }: { site: string, id: string }) {
|
||||
if (unidata) {
|
||||
await unidata.notes.set({
|
||||
source: 'Crossbell Note',
|
||||
identity: site,
|
||||
platform: 'Crossbell',
|
||||
action: 'remove',
|
||||
}, {
|
||||
id,
|
||||
})
|
||||
}
|
||||
return await unidata.notes.set({
|
||||
source: 'Crossbell Note',
|
||||
identity: site,
|
||||
platform: 'Crossbell',
|
||||
action: 'remove',
|
||||
}, {
|
||||
id,
|
||||
})
|
||||
}
|
||||
|
||||
export async function getPage<TRender extends boolean = false>(
|
||||
|
|
|
|||
|
|
@ -40,39 +40,31 @@ export const checkSubdomain = async ({
|
|||
}
|
||||
|
||||
export const getUserSites = async (address: string) => {
|
||||
if (unidata) {
|
||||
const profiles = await unidata.profiles.get({
|
||||
source: 'Crossbell Profile',
|
||||
identity: address,
|
||||
platform: 'Ethereum',
|
||||
});
|
||||
const profiles = await unidata.profiles.get({
|
||||
source: 'Crossbell Profile',
|
||||
identity: address,
|
||||
platform: 'Ethereum',
|
||||
});
|
||||
|
||||
const sites = profiles.list?.sort((a, b) => +new Date(b.date_updated || 0) - +new Date(a.date_updated || 0))
|
||||
const sites = profiles.list?.sort((a, b) => +new Date(b.date_updated || 0) - +new Date(a.date_updated || 0))
|
||||
|
||||
if (!sites || !sites.length) return null
|
||||
if (!sites || !sites.length) return null
|
||||
|
||||
return sites
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
return sites
|
||||
}
|
||||
|
||||
export const getSite = async (input: string) => {
|
||||
if (unidata) {
|
||||
const profiles = await unidata.profiles.get({
|
||||
source: 'Crossbell Profile',
|
||||
identity: input,
|
||||
platform: 'Crossbell',
|
||||
});
|
||||
const profiles = await unidata.profiles.get({
|
||||
source: 'Crossbell Profile',
|
||||
identity: input,
|
||||
platform: 'Crossbell',
|
||||
});
|
||||
|
||||
const site = profiles.list?.sort((a, b) => +new Date(b.date_updated || 0) - +new Date(a.date_updated || 0))?.[0]
|
||||
const site = profiles.list?.sort((a, b) => +new Date(b.date_updated || 0) - +new Date(a.date_updated || 0))?.[0]
|
||||
|
||||
if (!site) return null
|
||||
if (!site) return null
|
||||
|
||||
return site
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
return site
|
||||
}
|
||||
|
||||
export const getSubscription = async (data: {
|
||||
|
|
@ -103,24 +95,17 @@ export async function updateSite(
|
|||
navigation?: SiteNavigationItem[] // TODO
|
||||
},
|
||||
) {
|
||||
if (unidata) {
|
||||
return await unidata.profiles.set({
|
||||
source: 'Crossbell Profile',
|
||||
identity: payload.site,
|
||||
platform: 'Crossbell',
|
||||
action: 'update',
|
||||
}, {
|
||||
...(payload.name && { name: payload.name }),
|
||||
...(payload.description && { bio: payload.description }),
|
||||
...(payload.icon && { avatars: [payload.icon] }),
|
||||
...(payload.subdomain && { username: payload.subdomain }),
|
||||
})
|
||||
} else {
|
||||
return {
|
||||
code: 1,
|
||||
message: 'Unidata is not found',
|
||||
}
|
||||
}
|
||||
return await unidata.profiles.set({
|
||||
source: 'Crossbell Profile',
|
||||
identity: payload.site,
|
||||
platform: 'Crossbell',
|
||||
action: 'update',
|
||||
}, {
|
||||
...(payload.name && { name: payload.name }),
|
||||
...(payload.description && { bio: payload.description }),
|
||||
...(payload.icon && { avatars: [payload.icon] }),
|
||||
...(payload.subdomain && { username: payload.subdomain }),
|
||||
})
|
||||
}
|
||||
|
||||
export async function createSite(
|
||||
|
|
|
|||
|
|
@ -1,35 +1,39 @@
|
|||
import { GetServerSideProps } from "next"
|
||||
import { SiteHome } from "~/components/site/SiteHome"
|
||||
import { SiteLayout } from "~/components/site/SiteLayout"
|
||||
import { getAuthUser } from "~/lib/auth.server"
|
||||
import { trpc } from "~/lib/trpc"
|
||||
import { createSSGHelpers } from "@trpc/react/ssg"
|
||||
import { appRouter } from "~/router"
|
||||
import { getTRPCContext } from "~/lib/trpc.server"
|
||||
import { Viewer } from "~/lib/types"
|
||||
import { getViewer } from "~/lib/viewer"
|
||||
import { useAccount } from 'wagmi'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { getUserSites, getSite } from "~/models/site.model"
|
||||
import { getPagesBySite } from "~/models/page.model"
|
||||
import { Profile, Notes } from "unidata.js"
|
||||
import { PageVisibilityEnum } from "~/lib/types"
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
||||
const user = await getAuthUser(ctx.req)
|
||||
const domainOrSubdomain = ctx.params!.site as string
|
||||
|
||||
const viewer = getViewer(user)
|
||||
const trpcContext = await getTRPCContext(ctx)
|
||||
const ssg = createSSGHelpers({ router: appRouter, ctx: trpcContext })
|
||||
|
||||
await Promise.all([
|
||||
ssg.fetchQuery("site", { site: domainOrSubdomain }),
|
||||
ssg.fetchQuery("site.pages", {
|
||||
const [site, posts] = await Promise.all([
|
||||
getSite(domainOrSubdomain),
|
||||
getPagesBySite({
|
||||
site: domainOrSubdomain,
|
||||
take: 1000,
|
||||
includeExcerpt: true,
|
||||
type: "post",
|
||||
visibility: PageVisibilityEnum.Published,
|
||||
}),
|
||||
ssg.fetchQuery("site.subscription", { site: domainOrSubdomain }),
|
||||
])
|
||||
|
||||
return {
|
||||
props: {
|
||||
viewer,
|
||||
site,
|
||||
posts,
|
||||
domainOrSubdomain,
|
||||
trpcState: ssg.dehydrate(),
|
||||
},
|
||||
|
|
@ -37,27 +41,15 @@ export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
|||
}
|
||||
|
||||
function SiteIndexPage({
|
||||
viewer,
|
||||
domainOrSubdomain,
|
||||
site,
|
||||
posts,
|
||||
}: {
|
||||
viewer: Viewer | null
|
||||
site: Profile,
|
||||
posts: Notes,
|
||||
domainOrSubdomain: string
|
||||
}) {
|
||||
const { data: site } = trpc.useQuery(
|
||||
["site", { site: domainOrSubdomain }],
|
||||
{},
|
||||
)
|
||||
const { data: subscription } = trpc.useQuery([
|
||||
"site.subscription",
|
||||
{ site: domainOrSubdomain },
|
||||
])
|
||||
const { data: posts } = trpc.useQuery([
|
||||
"site.pages",
|
||||
{ site: domainOrSubdomain, take: 1000, includeExcerpt: true },
|
||||
])
|
||||
|
||||
return (
|
||||
<SiteLayout site={site!} viewer={viewer} subscription={subscription}>
|
||||
<SiteLayout site={site!}>
|
||||
<SiteHome posts={posts} />
|
||||
</SiteLayout>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -54,23 +54,24 @@ export const siteRouter = createRouter()
|
|||
includeExcerpt: z.boolean().optional(),
|
||||
}),
|
||||
output: z.object({
|
||||
nodes: z.array(
|
||||
list: z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
title: z.string(),
|
||||
contentHTML: z.string().optional(),
|
||||
publishedAt: z.date().transform((v) => v.toISOString()),
|
||||
published: z.boolean(),
|
||||
slug: z.string(),
|
||||
excerpt: z.string().nullable(),
|
||||
autoExcerpt: z.string().optional(),
|
||||
body: z.object({
|
||||
content: z.string(),
|
||||
}),
|
||||
date_published: z.date().transform((v) => v.toISOString()),
|
||||
summary: z.object({
|
||||
content: z.string(),
|
||||
}),
|
||||
}),
|
||||
),
|
||||
total: z.number(),
|
||||
hasMore: z.boolean(),
|
||||
}),
|
||||
async resolve({ input, ctx }) {
|
||||
const result = await getPagesBySite(ctx.gate, input)
|
||||
const result = await getPagesBySite(input)
|
||||
return result
|
||||
},
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue