feat: support json feed
This commit is contained in:
parent
bdc789ccf3
commit
525af6fa03
|
|
@ -22,6 +22,12 @@ export const SEOHead: React.FC<{
|
|||
<meta name="twitter:image" content={image} />
|
||||
</>
|
||||
)}
|
||||
<link
|
||||
rel="alternate"
|
||||
href="/feed"
|
||||
title={title}
|
||||
type="application/feed+json"
|
||||
></link>
|
||||
</Head>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
import React from "react"
|
||||
|
||||
export const RSS3Icon: React.FC<React.ComponentPropsWithoutRef<"svg">> = (
|
||||
props,
|
||||
) => {
|
||||
return (
|
||||
<svg
|
||||
width="1em"
|
||||
height="1em"
|
||||
{...props}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 604.24 150.74"
|
||||
>
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="Layer_1-2" data-name="Layer 1">
|
||||
<g id="Layer_2-2" data-name="Layer 2">
|
||||
<g id="图层_1" data-name="图层 1">
|
||||
<path d="M423.88,89c-8.5-12.62-29-19.34-43.67-23.3-1.71-.45-3.49-.9-5.38-1.37h-.15c-7.9-2-16.87-4.21-23.85-8-8-4.38-11.92-10.06-11.92-17.36a17.44,17.44,0,0,1,.33-3.53v-.21a14.29,14.29,0,0,1,.42-1.59,1.72,1.72,0,0,1,.07-.21v-.08a20.29,20.29,0,0,1,5.06-8c5.93-6,15.39-9.37,26-9.37h.33a55.82,55.82,0,0,1,22.57,5.31A60.82,60.82,0,0,1,412.5,34.63l.13,15.21V50h13.2V4.2H412.66V14.58a.45.45,0,0,1-.21.38.6.6,0,0,1-.36-.16A67.44,67.44,0,0,0,393.7,4.89a70.74,70.74,0,0,0-22-3.82c-8.59,0-15.61,1-21.47,2.91a41.17,41.17,0,0,0-13.35,7.43A35,35,0,0,0,326.81,24a35.93,35.93,0,0,0-3.46,15.18,32.19,32.19,0,0,0,1.15,9.21,34.51,34.51,0,0,0,4.5,9.33c.17.27.37.53.55.79s.19.25.28.38l.12.16c8.65,12,28,17.24,42.05,21.13l.39.1,3.13.87,3.39.91a138.79,138.79,0,0,1,21.48,7.16,25.37,25.37,0,0,1,9.35,7.09,21.89,21.89,0,0,1,4.59,9.83,21.41,21.41,0,0,1-.79,10.77,27.15,27.15,0,0,1-6.69,10.55A32,32,0,0,1,395.44,134a47.86,47.86,0,0,1-12.66,2.27A64.28,64.28,0,0,1,362.36,134c-11.25-3.56-21.52-10.43-28.18-18.82a2.63,2.63,0,0,1-.49-.92l-.13-13.08V101h-13.2v47.88h13.21v-14a57.5,57.5,0,0,0,18.84,11.06,78.21,78.21,0,0,0,25.27,4.82h1.07c7.33,0,24.48,0,37.93-11.39a39.26,39.26,0,0,0,9.09-11.48A40.12,40.12,0,0,0,430,114.4a37.23,37.23,0,0,0-.62-13.51A31.08,31.08,0,0,0,423.88,89Z" />
|
||||
<path d="M293.54,89c-8.33-12.56-28.91-19.31-43.67-23.29-1.68-.44-3.42-.87-5.27-1.33-16-3.95-35.92-8.88-36.15-25.45A17.71,17.71,0,0,1,210,31.69a21.44,21.44,0,0,1,4.52-6.4c6-5.92,15.41-9.32,25.94-9.32h.33a55.69,55.69,0,0,1,22.55,5.31A60.07,60.07,0,0,1,282,34.62l.26,15.21V50h13.2V4.2H282.33V14.58a.48.48,0,0,1-.2.38.43.43,0,0,1-.37-.16,67.44,67.44,0,0,0-18.39-9.91,70.74,70.74,0,0,0-22-3.82c-8.64,0-15.68,1-21.52,2.91a41.2,41.2,0,0,0-13.3,7.43A35,35,0,0,0,196.48,24,36,36,0,0,0,193,39.22a32.87,32.87,0,0,0,1.13,9.2,35.19,35.19,0,0,0,4.38,9.3c8.29,12.92,28.57,18.47,43.38,22.53l.35.09,2.94.81,3.1.83.29.08a138.79,138.79,0,0,1,21.48,7.16,25.86,25.86,0,0,1,9.4,7.14,22.25,22.25,0,0,1,4.61,9.92,21.46,21.46,0,0,1-.82,10.78,27.1,27.1,0,0,1-6.73,10.53,31.34,31.34,0,0,1-11.36,6.49,47.22,47.22,0,0,1-12.69,2.22c-.89,0-1.82.06-2.76.06a65.49,65.49,0,0,1-17.83-2.43c-.67-.21-1.35-.44-2-.68h-.1c-1.35-.48-2.7-1-4-1.61a60.4,60.4,0,0,1-17.63-11.79l-.26-.25c-.8-.8-1.61-1.65-2.39-2.54l-.6-.69c-.35-.41-.69-.81-1-1.23a2.66,2.66,0,0,1-.48-.91l-.13-13.08V101H189.89v47.88h13.34v-14a57.5,57.5,0,0,0,18.84,11.06,78.21,78.21,0,0,0,25.27,4.82h1.07c7.29,0,24.36,0,37.93-11.39a39.26,39.26,0,0,0,9.09-11.48,40.11,40.11,0,0,0,4.25-13.38,37.23,37.23,0,0,0-.62-13.51A31.14,31.14,0,0,0,293.54,89Z" />
|
||||
<path d="M158.15,137.47,122.07,82.92a39.84,39.84,0,0,0,17.78-3.45c7.89-4,14.23-9.07,18.12-16.57a43.72,43.72,0,0,0,4.78-20,41.63,41.63,0,0,0-12-29.21C142.41,6.13,132,2.45,118.91,2.45H49V17.54H63.84V137.65H47.7v12.62H94.55V137.65H79.46V83.14h25.25l35.35,54.51H124v12.62h50.42V137.65H158.65A.66.66,0,0,1,158.15,137.47ZM79.46,16.86h38.67c11.88,0,21,4.72,25.62,12.83a26.81,26.81,0,0,1-.42,26.47c-4.74,8-10.34,12.42-22.76,12.42H79.46Z" />
|
||||
<path d="M550.46,106.56c0-18.11-9.15-31.19-24.33-36.8C539,63,544.68,51.88,544.68,40.43,544.68,14,522.51,0,493.6,0,477.82.12,461,5.37,449.51,21.5l12,7.83C467.94,20.68,477.1,14,493.6,14c21,0,35.9,8.53,35.9,24.77C529.5,54.33,514.56,63,499.26,63H478.42V78h22.05c19.51,0,34.93,10.51,34.45,28.39-1.09,19-17.83,29.21-39.75,29.21-14.1,0-29.76-4.44-41.45-16.94l-.6-.23-10.24,11.21.48.12c14.7,13.79,32.17,20.92,51.81,20.92C525.41,150.62,550.46,134.26,550.46,106.56Z" />
|
||||
<rect y="68.48" width="37.98" height="15.51" />
|
||||
<rect x="566.26" y="68.48" width="37.98" height="15.51" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ import {
|
|||
} from "~/queries/site"
|
||||
import { useConnectModal } from "@rainbow-me/rainbowkit"
|
||||
import { BlockchainIcon } from "~/components/icons/BlockchainIcon"
|
||||
import { RSS3Icon } from "~/components/icons/RSS3Icon"
|
||||
import { DotsHorizontalIcon, RssIcon } from "@heroicons/react/solid"
|
||||
import type { Link } from "unidata.js"
|
||||
import { getSiteSubscriptions } from "~/models/site.model"
|
||||
|
|
@ -146,7 +147,7 @@ export const SiteHeader: React.FC<{
|
|||
},
|
||||
{
|
||||
text: "View on RSS3",
|
||||
icon: <RssIcon className="w-4 h-4" />,
|
||||
icon: <RSS3Icon />,
|
||||
url: `https://rss3.io/result?search=${site?.metadata?.owner}`,
|
||||
},
|
||||
{
|
||||
|
|
@ -154,6 +155,11 @@ export const SiteHeader: React.FC<{
|
|||
icon: <BlockchainIcon />,
|
||||
url: `${CSB_SCAN}/address/${site?.metadata?.owner}/transactions`,
|
||||
},
|
||||
{
|
||||
text: "JSON Feed",
|
||||
icon: <RssIcon className="w-4 h-4" />,
|
||||
url: `/feed`,
|
||||
},
|
||||
]
|
||||
|
||||
const [siteSubscriptionList, setSiteSubscriptionList] = useState<Link[]>([])
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export const UniLink: React.FC<{
|
|||
return <span className={className}>{children}</span>
|
||||
}
|
||||
|
||||
const isExternal = href && /^https?:\/\//.test(href)
|
||||
const isExternal = href && (/^https?:\/\//.test(href) || href === "/feed")
|
||||
|
||||
if (isExternal) {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
import { GetServerSideProps } from "next"
|
||||
import { SiteHome } from "~/components/site/SiteHome"
|
||||
import { SiteLayout } from "~/components/site/SiteLayout"
|
||||
import { queryClientServer } from "~/lib/query-client.server"
|
||||
import { fetchGetSite } from "~/queries/site.server"
|
||||
import { useGetSite } from "~/queries/site"
|
||||
import { dehydrate, QueryClient } from "@tanstack/react-query"
|
||||
import { useGetPagesBySite } from "~/queries/page"
|
||||
import { fetchGetPagesBySite } from "~/queries/page.server"
|
||||
import { PageVisibilityEnum } from "~/lib/types"
|
||||
import { getSiteLink } from "~/lib/helpers"
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
||||
ctx.res.setHeader("Content-Type", "application/feed+json")
|
||||
const domainOrSubdomain = ctx.params!.site as string
|
||||
|
||||
const site = await fetchGetSite(domainOrSubdomain)
|
||||
const pages = await fetchGetPagesBySite({
|
||||
site: domainOrSubdomain,
|
||||
take: 1000,
|
||||
type: "post",
|
||||
visibility: PageVisibilityEnum.Published,
|
||||
render: true,
|
||||
})
|
||||
|
||||
const link = getSiteLink({
|
||||
subdomain: site.username || "",
|
||||
})
|
||||
ctx.res.write(
|
||||
JSON.stringify({
|
||||
version: "https://jsonfeed.org/version/1.1",
|
||||
title: site.name,
|
||||
description: site.description,
|
||||
icon: site.avatars?.[0],
|
||||
home_page_url: link,
|
||||
feed_url: `${link}/feed.json`,
|
||||
items: pages.list?.map((page) => ({
|
||||
id: page.id,
|
||||
content_html: page.body?.content,
|
||||
summary: page.summary?.content,
|
||||
url: `${link}/${page.slug || page.id}`,
|
||||
image: page.cover,
|
||||
date_published: page.date_published,
|
||||
date_modified: page.date_updated,
|
||||
authors: page.authors?.map((author) => ({
|
||||
name: author,
|
||||
})),
|
||||
tags: page.tags,
|
||||
})),
|
||||
}),
|
||||
)
|
||||
ctx.res.end()
|
||||
|
||||
return {
|
||||
props: {},
|
||||
}
|
||||
}
|
||||
|
||||
const SiteFeed: React.FC = () => null
|
||||
|
||||
export default SiteFeed
|
||||
|
|
@ -34,3 +34,21 @@ export const prefetchGetPagesBySite = async (
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const fetchGetPagesBySite = async (
|
||||
input: Parameters<typeof pageModel.getPagesBySite>[0],
|
||||
) => {
|
||||
const key = ["getPagesBySite", input.site, input]
|
||||
const data: ReturnType<typeof pageModel.getPagesBySite> | undefined =
|
||||
queryClientServer.getQueryData(key)
|
||||
if (!data) {
|
||||
return await queryClientServer.fetchQuery(key, async () => {
|
||||
return pageModel.getPagesBySite(input)
|
||||
})
|
||||
} else {
|
||||
queryClientServer.fetchQuery(key, async () => {
|
||||
return pageModel.getPagesBySite(input)
|
||||
})
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,3 +14,19 @@ export const prefetchGetSite = async (input: string) => {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const fetchGetSite = async (input: string) => {
|
||||
const key = ["getSite", input]
|
||||
const data: ReturnType<typeof siteModel.getSite> | undefined =
|
||||
queryClientServer.getQueryData(key)
|
||||
if (!data) {
|
||||
return await queryClientServer.fetchQuery(["getSite", input], async () => {
|
||||
return siteModel.getSite(input)
|
||||
})
|
||||
} else {
|
||||
queryClientServer.fetchQuery(["getSite", input], async () => {
|
||||
return siteModel.getSite(input)
|
||||
})
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue