feat: implement preview endpoints for draft documents; refs #176
This commit is contained in:
parent
d1c5ead6e0
commit
3437da4297
|
@ -15,6 +15,7 @@ type Metadata = {
|
|||
pagePath?: string
|
||||
date?: string | null
|
||||
contentType?: LPE.PostType
|
||||
noIndex?: boolean
|
||||
}
|
||||
|
||||
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL ?? 'https://press.logos.co'
|
||||
|
@ -32,6 +33,7 @@ export default function SEO({
|
|||
pagePath = '',
|
||||
date,
|
||||
contentType,
|
||||
noIndex = false,
|
||||
}: Metadata) {
|
||||
const ogSearchParams = new URLSearchParams()
|
||||
|
||||
|
@ -69,6 +71,11 @@ export default function SEO({
|
|||
<meta name="twitter:site" content={`@${siteConfigs.xHandle}`} />
|
||||
<meta property="twitter:image" content={ogUrl} />
|
||||
<link rel="canonical" href={`${SITE_URL}${pagePath}`} />
|
||||
{noIndex && (
|
||||
<>
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
</>
|
||||
)}
|
||||
</Head>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ const Page: CustomNextPage<PageProps> = ({
|
|||
<SEO
|
||||
title={data.page.title}
|
||||
description={data.page.subtitle}
|
||||
noIndex={data.page.isDraft}
|
||||
pagePath={`/${data.page.slug}`}
|
||||
/>
|
||||
<StaticPage data={data} />
|
||||
|
@ -40,7 +41,7 @@ export const getStaticPaths: GetStaticPaths = async () => {
|
|||
return {
|
||||
paths: data.map((page) => ({
|
||||
params: {
|
||||
slug: page.slug,
|
||||
path: [page.slug],
|
||||
},
|
||||
})),
|
||||
fallback: true,
|
||||
|
@ -48,8 +49,24 @@ export const getStaticPaths: GetStaticPaths = async () => {
|
|||
}
|
||||
|
||||
export const getStaticProps: GetStaticProps<PageProps> = async (ctx) => {
|
||||
const { path } = ctx.params || {}
|
||||
const [slug, idProp, id] = (Array.isArray(path) && path) || []
|
||||
|
||||
if (idProp && (idProp !== 'id' || !id)) {
|
||||
return {
|
||||
notFound: true,
|
||||
props: {},
|
||||
}
|
||||
}
|
||||
|
||||
const { data, errors } = await unbodyApi.getStaticPage({
|
||||
slug: ctx.params!.slug as string,
|
||||
slug: slug as string,
|
||||
...(id
|
||||
? {
|
||||
id,
|
||||
includeDrafts: true,
|
||||
}
|
||||
: {}),
|
||||
})
|
||||
|
||||
if (!data) {
|
|
@ -24,6 +24,7 @@ const ArticlePage = ({ data, errors, why }: ArticleProps) => {
|
|||
<SEO
|
||||
title={data.data.title}
|
||||
description={data.data.summary}
|
||||
noIndex={data.data.isDraft}
|
||||
image={data.data.coverImage}
|
||||
pagePath={`/article/${slug}`}
|
||||
date={data.data.createdAt}
|
||||
|
@ -49,13 +50,21 @@ export async function getStaticPaths() {
|
|||
return {
|
||||
paths: errors
|
||||
? []
|
||||
: posts.map((post) => ({ params: { slug: `${post.slug}` } })),
|
||||
: posts.map((post) => ({ params: { path: [post.slug] } })),
|
||||
fallback: true,
|
||||
}
|
||||
}
|
||||
|
||||
export const getStaticProps = async ({ params }: GetStaticPropsContext) => {
|
||||
const { slug } = params!
|
||||
const { path } = params || {}
|
||||
const [slug, idProp, id] = (Array.isArray(path) && path) || []
|
||||
|
||||
if (idProp && (idProp !== 'id' || !id)) {
|
||||
return {
|
||||
notFound: true,
|
||||
props: {},
|
||||
}
|
||||
}
|
||||
|
||||
if (!slug) {
|
||||
return {
|
||||
|
@ -67,6 +76,7 @@ export const getStaticProps = async ({ params }: GetStaticPropsContext) => {
|
|||
const { data, errors } = await unbodyApi.getArticle({
|
||||
parseContent: true,
|
||||
slug: slug as string,
|
||||
...(id ? { id, includeDrafts: true } : {}),
|
||||
})
|
||||
|
||||
if (!data) {
|
|
@ -26,6 +26,7 @@ const EpisodePage = ({ episode, relatedEpisodes, errors }: EpisodeProps) => {
|
|||
<SEO
|
||||
title={episode.title}
|
||||
description={episode.description}
|
||||
noIndex={episode.isDraft}
|
||||
image={episode.coverImage}
|
||||
imageUrl={undefined}
|
||||
pagePath={getPostLink('podcast', {
|
||||
|
@ -53,7 +54,7 @@ export async function getStaticPaths() {
|
|||
return {
|
||||
params: {
|
||||
showSlug: show.slug,
|
||||
epSlug: episode.slug,
|
||||
path: [episode.slug],
|
||||
},
|
||||
}
|
||||
})
|
||||
|
@ -62,12 +63,20 @@ export async function getStaticPaths() {
|
|||
|
||||
return {
|
||||
paths: paths,
|
||||
fallback: false,
|
||||
fallback: 'blocking',
|
||||
}
|
||||
}
|
||||
|
||||
export const getStaticProps = async ({ params }: GetStaticPropsContext) => {
|
||||
const { showSlug, epSlug } = params!
|
||||
const { showSlug, path } = params!
|
||||
const [epSlug, idProp, id] = (Array.isArray(path) && path) || []
|
||||
|
||||
if (idProp && (idProp !== 'id' || !id)) {
|
||||
return {
|
||||
notFound: true,
|
||||
props: {},
|
||||
}
|
||||
}
|
||||
|
||||
if (!epSlug || !showSlug) {
|
||||
return {
|
||||
|
@ -82,6 +91,12 @@ export const getStaticProps = async ({ params }: GetStaticPropsContext) => {
|
|||
showSlug: showSlug as string,
|
||||
slug: epSlug as string,
|
||||
textBlocks: true,
|
||||
...(id
|
||||
? {
|
||||
id: id as string,
|
||||
includeDraft: true,
|
||||
}
|
||||
: {}),
|
||||
})
|
||||
|
||||
// TODO : error handlings
|
|
@ -0,0 +1,48 @@
|
|||
import { CustomNextPage, GetServerSideProps } from 'next'
|
||||
import SEO from '../components/SEO/SEO'
|
||||
import unbodyApi from '../services/unbody/unbody.service'
|
||||
import { getPostLink } from '../utils/route.utils'
|
||||
|
||||
type PageProps = {}
|
||||
|
||||
const Page: CustomNextPage<PageProps> = (props) => {
|
||||
return (
|
||||
<>
|
||||
<SEO />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
||||
const id = Array.isArray(ctx.query.id) ? ctx.query.id[0] : ctx.query.id
|
||||
if (!id)
|
||||
return {
|
||||
notFound: true,
|
||||
}
|
||||
|
||||
const { data, errors } = await unbodyApi.getDocById({
|
||||
id,
|
||||
includeDrafts: true,
|
||||
})
|
||||
|
||||
if (!data || errors) {
|
||||
return {
|
||||
notFound: typeof errors === 'string' && errors.includes('Not found'),
|
||||
props: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
props: {},
|
||||
redirect: {
|
||||
destination: getPostLink(data.type, {
|
||||
id,
|
||||
postSlug: data.slug,
|
||||
showSlug: (data.type === 'podcast' && data.show?.slug) || null,
|
||||
}),
|
||||
permanent: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default Page
|
|
@ -63,6 +63,7 @@ export const ArticleDataType: UnbodyDataTypeConfig<
|
|||
toc: data.tocObj ?? [],
|
||||
featured: data.path.includes('featured'),
|
||||
highlighted: data.path.includes('highlighted'),
|
||||
isDraft: data.path.includes('draft'),
|
||||
type: LPE.PostTypes.Article,
|
||||
}
|
||||
},
|
||||
|
|
|
@ -114,6 +114,7 @@ export const PodcastEpisodeDataType: UnbodyDataTypeConfig<
|
|||
...(show ? { showId: show.id } : {}),
|
||||
...(coverImage ? { coverImage } : {}),
|
||||
highlighted: data.highlighted,
|
||||
isDraft: data.isDraft,
|
||||
type: LPE.PostTypes.Podcast,
|
||||
}
|
||||
},
|
||||
|
|
|
@ -38,6 +38,7 @@ export const StaticPageDataType: UnbodyDataTypeConfig<
|
|||
modifiedAt: data.modifiedAt || null,
|
||||
content: blocks,
|
||||
type: 'static_page',
|
||||
isDraft: data.pathString.includes('/draft/'),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -306,6 +306,7 @@ export class UnbodyService {
|
|||
}, [])
|
||||
|
||||
findStaticPages = ({
|
||||
id,
|
||||
skip = 0,
|
||||
limit = 10,
|
||||
slug,
|
||||
|
@ -315,6 +316,7 @@ export class UnbodyService {
|
|||
textBlocks = false,
|
||||
includeDrafts = false,
|
||||
}: {
|
||||
id?: string
|
||||
slug?: string
|
||||
skip?: number
|
||||
limit?: number
|
||||
|
@ -352,6 +354,15 @@ export class UnbodyService {
|
|||
'Static pages',
|
||||
includeDrafts ? 'published|draft' : 'published',
|
||||
]),
|
||||
...(id
|
||||
? [
|
||||
{
|
||||
operator: 'Equal',
|
||||
path: ['remoteId'],
|
||||
valueString: id,
|
||||
} as GetObjectsGoogleDocWhereInpObj,
|
||||
]
|
||||
: []),
|
||||
...(slug
|
||||
? [
|
||||
{
|
||||
|
@ -384,15 +395,18 @@ export class UnbodyService {
|
|||
}, [])
|
||||
|
||||
getStaticPage = ({
|
||||
id,
|
||||
slug,
|
||||
includeDrafts = false,
|
||||
}: {
|
||||
id?: string
|
||||
slug: string
|
||||
includeDrafts?: boolean
|
||||
}) =>
|
||||
this.handleRequest<LPE.StaticPage.Document | null>(async () => {
|
||||
const { data } = await this.findStaticPages({
|
||||
limit: 1,
|
||||
id,
|
||||
slug,
|
||||
includeDrafts,
|
||||
textBlocks: true,
|
||||
|
@ -407,6 +421,7 @@ export class UnbodyService {
|
|||
findPostDocs = ({
|
||||
skip = 0,
|
||||
limit = 10,
|
||||
id,
|
||||
slug,
|
||||
toc = false,
|
||||
filter,
|
||||
|
@ -416,6 +431,7 @@ export class UnbodyService {
|
|||
nearText,
|
||||
sort,
|
||||
}: {
|
||||
id?: string
|
||||
slug?: string
|
||||
skip?: number
|
||||
limit?: number
|
||||
|
@ -454,6 +470,15 @@ export class UnbodyService {
|
|||
filter: {
|
||||
operator: 'And',
|
||||
operands: [
|
||||
...(id
|
||||
? [
|
||||
{
|
||||
operator: 'Equal',
|
||||
path: ['remoteId'],
|
||||
valueString: id,
|
||||
} as GetObjectsGoogleDocWhereInpObj,
|
||||
]
|
||||
: []),
|
||||
...(slug
|
||||
? [
|
||||
{
|
||||
|
@ -484,6 +509,7 @@ export class UnbodyService {
|
|||
getArticles = ({
|
||||
skip = 0,
|
||||
limit = 10,
|
||||
id,
|
||||
slug,
|
||||
toc = false,
|
||||
filter,
|
||||
|
@ -492,6 +518,7 @@ export class UnbodyService {
|
|||
includeDrafts = false,
|
||||
highlighted = 'include',
|
||||
}: {
|
||||
id?: string
|
||||
slug?: string
|
||||
skip?: number
|
||||
limit?: number
|
||||
|
@ -529,10 +556,19 @@ export class UnbodyService {
|
|||
this.helpers.args.wherePath([
|
||||
'Articles',
|
||||
highlighted !== 'only' && '|published',
|
||||
highlighted !== 'only' && includeDrafts && '|drafts',
|
||||
highlighted !== 'only' && includeDrafts && '|draft',
|
||||
(highlighted === 'include' || highlighted === 'only') &&
|
||||
'|highlighted',
|
||||
]),
|
||||
...(id
|
||||
? [
|
||||
{
|
||||
operator: 'Equal',
|
||||
path: ['remoteId'],
|
||||
valueString: id,
|
||||
} as GetObjectsGoogleDocWhereInpObj,
|
||||
]
|
||||
: []),
|
||||
...(slug
|
||||
? [
|
||||
{
|
||||
|
@ -565,9 +601,11 @@ export class UnbodyService {
|
|||
}, [])
|
||||
|
||||
getArticle = ({
|
||||
id,
|
||||
slug,
|
||||
includeDrafts = false,
|
||||
}: {
|
||||
id?: string
|
||||
slug?: string
|
||||
parseContent?: boolean
|
||||
includeDrafts?: boolean
|
||||
|
@ -576,6 +614,7 @@ export class UnbodyService {
|
|||
async () =>
|
||||
this.getArticles({
|
||||
limit: 1,
|
||||
id,
|
||||
slug,
|
||||
toc: true,
|
||||
includeDrafts,
|
||||
|
@ -698,6 +737,7 @@ export class UnbodyService {
|
|||
getPodcastEpisodes = ({
|
||||
limit = 10,
|
||||
skip = 0,
|
||||
id,
|
||||
slug,
|
||||
showSlug = '',
|
||||
toc = false,
|
||||
|
@ -709,6 +749,7 @@ export class UnbodyService {
|
|||
includeDrafts = false,
|
||||
highlighted = 'include',
|
||||
}: {
|
||||
id?: string
|
||||
slug?: string
|
||||
showSlug?: string
|
||||
skip?: number
|
||||
|
@ -749,10 +790,19 @@ export class UnbodyService {
|
|||
'Podcasts',
|
||||
showSlug,
|
||||
highlighted !== 'only' && '|published',
|
||||
highlighted !== 'only' && includeDrafts && '|drafts',
|
||||
highlighted !== 'only' && includeDrafts && '|draft',
|
||||
(highlighted === 'include' || highlighted === 'only') &&
|
||||
'|highlighted',
|
||||
]),
|
||||
...(id
|
||||
? [
|
||||
{
|
||||
operator: 'Equal',
|
||||
path: ['remoteId'],
|
||||
valueString: id,
|
||||
} as GetObjectsGoogleDocWhereInpObj,
|
||||
]
|
||||
: []),
|
||||
...(slug
|
||||
? [
|
||||
{
|
||||
|
@ -802,12 +852,14 @@ export class UnbodyService {
|
|||
}, [])
|
||||
|
||||
getPodcastEpisode = ({
|
||||
id,
|
||||
slug,
|
||||
showSlug = '',
|
||||
toc = false,
|
||||
includeDraft = false,
|
||||
textBlocks = false,
|
||||
}: {
|
||||
id?: string
|
||||
slug: string
|
||||
showSlug?: string
|
||||
toc?: boolean
|
||||
|
@ -816,6 +868,7 @@ export class UnbodyService {
|
|||
}) =>
|
||||
this.handleRequest<LPE.Podcast.Document | null>(async () => {
|
||||
const { data } = await this.getPodcastEpisodes({
|
||||
id,
|
||||
slug,
|
||||
showSlug,
|
||||
skip: 0,
|
||||
|
@ -1285,6 +1338,65 @@ export class UnbodyService {
|
|||
return [...blocks].sort((a, b) => (a.score > b.score ? -1 : 1))
|
||||
}, [])
|
||||
|
||||
getDocById = ({
|
||||
id,
|
||||
includeDrafts = false,
|
||||
}: {
|
||||
id: string
|
||||
includeDrafts?: boolean
|
||||
}) =>
|
||||
this.handleRequest<LPE.Post.Document | LPE.StaticPage.Document | null>(
|
||||
async () => {
|
||||
const { data, errors } = await this.findPostDocs({
|
||||
toc: true,
|
||||
skip: 0,
|
||||
limit: 1,
|
||||
textBlocks: false,
|
||||
filter: {
|
||||
operator: 'And',
|
||||
operands: [
|
||||
{
|
||||
operator: 'Equal',
|
||||
valueString: id,
|
||||
path: ['remoteId'],
|
||||
},
|
||||
this.helpers.args.wherePath([
|
||||
'published|highlighted',
|
||||
includeDrafts && '|draft',
|
||||
]),
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
if (errors) throw errors
|
||||
|
||||
const [doc] = data
|
||||
if (!doc) throw 'Not found!'
|
||||
|
||||
const { data: shows } = await this.getPodcastShows({
|
||||
populateEpisodes: false,
|
||||
})
|
||||
|
||||
const transformers = unbodyDataTypes.get({
|
||||
classes: ['document'],
|
||||
objectType: 'GoogleDoc',
|
||||
})
|
||||
|
||||
const transformed = await unbodyDataTypes.transform(
|
||||
transformers,
|
||||
doc,
|
||||
undefined,
|
||||
{
|
||||
shows,
|
||||
parseContent: false,
|
||||
},
|
||||
)
|
||||
|
||||
return transformed
|
||||
},
|
||||
null,
|
||||
)
|
||||
|
||||
getTopics = async (published: boolean = true) =>
|
||||
this.handleRequest(async () => {
|
||||
const { data } = await this.client.query({
|
||||
|
|
|
@ -116,6 +116,7 @@ export namespace LPE {
|
|||
title: string
|
||||
summary: string
|
||||
subtitle: string
|
||||
isDraft?: boolean
|
||||
|
||||
createdAt: string | null
|
||||
modifiedAt: string | null
|
||||
|
@ -147,6 +148,7 @@ export namespace LPE {
|
|||
authors: Author.Document[]
|
||||
tags: string[]
|
||||
highlighted?: boolean
|
||||
isDraft?: boolean
|
||||
|
||||
createdAt: string | null
|
||||
modifiedAt: string | null
|
||||
|
@ -217,6 +219,7 @@ export namespace LPE {
|
|||
episodeNumber: number
|
||||
showId?: string
|
||||
highlighted?: boolean
|
||||
isDraft?: boolean
|
||||
coverImage?: Post.ImageBlock
|
||||
show?: Show
|
||||
type: typeof LPE.PostTypes.Podcast
|
||||
|
|
|
@ -1,29 +1,33 @@
|
|||
import { LPE } from '../types/lpe.types'
|
||||
|
||||
export const getPostLink = (
|
||||
postType: LPE.PostType,
|
||||
postType: LPE.PostType | LPE.StaticPage.Metadata['type'],
|
||||
{
|
||||
id,
|
||||
block,
|
||||
showSlug,
|
||||
postSlug,
|
||||
blockType,
|
||||
}: {
|
||||
id?: string
|
||||
block?: string | number | null
|
||||
showSlug?: string | number | null
|
||||
postSlug?: string | number | null
|
||||
blockType?: LPE.Post.ContentBlockType | null
|
||||
} = {},
|
||||
) => {
|
||||
const basePath =
|
||||
postType === 'article' ? `/article` : `/podcasts/${showSlug || ''}`
|
||||
let path =
|
||||
postType === 'article'
|
||||
? `/article`
|
||||
: postType === 'podcast'
|
||||
? `/podcasts/${showSlug || ''}`
|
||||
: ''
|
||||
|
||||
if (!postSlug) return basePath
|
||||
if (postSlug) path += `/${postSlug}`
|
||||
if (id) path += `/id/${id}`
|
||||
|
||||
const postPath = `${basePath}/${postSlug}`
|
||||
if (blockType && block)
|
||||
path += `#${blockType === 'text' ? 'p' : 'i'}-${block}`
|
||||
|
||||
if (!blockType && !block) return postPath
|
||||
|
||||
const blockPath = `${postPath}/#${blockType === 'text' ? 'p' : 'i'}-${block}`
|
||||
|
||||
return blockPath
|
||||
return path
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue