Files
nextjs-notion-starter-kit/components/NotionPage.tsx
2022-04-07 17:26:36 -04:00

216 lines
5.9 KiB
TypeScript

import React from 'react'
import Link from 'next/link'
import Image from 'next/image'
import dynamic from 'next/dynamic'
import cs from 'classnames'
import { useRouter } from 'next/router'
import { useSearchParam } from 'react-use'
import BodyClassName from 'react-body-classname'
import useDarkMode from '@fisch0920/use-dark-mode'
import { PageBlock } from 'notion-types'
import { Tweet, TwitterContextProvider } from 'react-static-tweets'
// core notion renderer
import { NotionRenderer } from 'react-notion-x'
// utils
import { getBlockTitle, getPageProperty } from 'notion-utils'
import { mapPageUrl, getCanonicalPageUrl } from 'lib/map-page-url'
import { mapImageUrl } from 'lib/map-image-url'
import { getPageTweet } from 'lib/get-page-tweet'
import { searchNotion } from 'lib/search-notion'
import * as types from 'lib/types'
import * as config from 'lib/config'
// components
import { CustomFont } from './CustomFont'
import { Loading } from './Loading'
import { Page404 } from './Page404'
import { PageHead } from './PageHead'
import { PageActions } from './PageActions'
import { Footer } from './Footer'
import { PageSocial } from './PageSocial'
import { NotionPageHeader } from './NotionPageHeader'
import { GitHubShareButton } from './GitHubShareButton'
import styles from './styles.module.css'
// -----------------------------------------------------------------------------
// dynamic imports for optional components
// -----------------------------------------------------------------------------
const Code = dynamic(() =>
import('react-notion-x/build/third-party/code').then((m) => m.Code)
)
const Collection = dynamic(() =>
import('react-notion-x/build/third-party/collection').then(
(m) => m.Collection
)
)
const Equation = dynamic(() =>
import('react-notion-x/build/third-party/equation').then((m) => m.Equation)
)
const Pdf = dynamic(
() => import('react-notion-x/build/third-party/pdf').then((m) => m.Pdf),
{
ssr: false
}
)
const Modal = dynamic(
() => import('react-notion-x/build/third-party/modal').then((m) => m.Modal),
{
ssr: false
}
)
export const NotionPage: React.FC<types.PageProps> = ({
site,
recordMap,
error,
pageId
}) => {
const router = useRouter()
const lite = useSearchParam('lite')
const params: any = {}
if (lite) params.lite = lite
// lite mode is for oembed
const isLiteMode = lite === 'true'
const searchParams = new URLSearchParams(params)
const darkMode = useDarkMode(false, { classNameDark: 'dark-mode' })
if (router.isFallback) {
return <Loading />
}
const keys = Object.keys(recordMap?.block || {})
const block = recordMap?.block?.[keys[0]]?.value
if (error || !site || !keys.length || !block) {
return <Page404 site={site} pageId={pageId} error={error} />
}
const title = getBlockTitle(block, recordMap) || site.name
console.log('notion page', {
isDev: config.isDev,
title,
pageId,
rootNotionPageId: site.rootNotionPageId,
recordMap
})
if (!config.isServer) {
// add important objects to the window global for easy debugging
const g = window as any
g.pageId = pageId
g.recordMap = recordMap
g.block = block
}
const siteMapPageUrl = mapPageUrl(site, recordMap, searchParams)
const canonicalPageUrl =
!config.isDev && getCanonicalPageUrl(site, recordMap)(pageId)
// const isRootPage =
// parsePageId(block.id) === parsePageId(site.rootNotionPageId)
const isBlogPost =
block.type === 'page' && block.parent_table === 'collection'
const showTableOfContents = !!isBlogPost
const minTableOfContentsItems = 3
const socialImage = mapImageUrl(
getPageProperty<string>('Social Image', block, recordMap) ||
(block as PageBlock).format?.page_cover ||
config.defaultPageCover,
block
)
const socialDescription =
getPageProperty<string>('Description', block, recordMap) ||
config.description
let pageAside: React.ReactNode = null
// only display comments and page actions on blog post pages
if (isBlogPost) {
const tweet = getPageTweet(block, recordMap)
if (tweet) {
pageAside = <PageActions tweet={tweet} />
}
} else {
pageAside = <PageSocial />
}
return (
<TwitterContextProvider
value={{
tweetAstMap: (recordMap as any).tweetAstMap || {},
swrOptions: {
fetcher: (id) =>
fetch(`/api/get-tweet-ast/${id}`).then((r) => r.json())
}
}}
>
<PageHead
pageId={pageId}
site={site}
title={title}
description={socialDescription}
image={socialImage}
url={canonicalPageUrl}
/>
<CustomFont site={site} />
{isLiteMode && <BodyClassName className='notion-lite' />}
<NotionRenderer
bodyClassName={cs(
styles.notion,
pageId === site.rootNotionPageId && 'index-page'
)}
components={{
nextImage: Image,
nextLink: Link,
Code,
Collection,
Equation,
Pdf,
Modal,
Tweet,
Header: NotionPageHeader
}}
recordMap={recordMap}
rootPageId={site.rootNotionPageId}
rootDomain={site.domain}
fullPage={!isLiteMode}
darkMode={darkMode.value}
previewImages={!!recordMap.preview_images}
showCollectionViewDropdown={false}
showTableOfContents={showTableOfContents}
minTableOfContentsItems={minTableOfContentsItems}
defaultPageIcon={config.defaultPageIcon}
defaultPageCover={config.defaultPageCover}
defaultPageCoverPosition={config.defaultPageCoverPosition}
mapPageUrl={siteMapPageUrl}
mapImageUrl={mapImageUrl}
searchNotion={searchNotion}
pageAside={pageAside}
footer={
<Footer
isDarkMode={darkMode.value}
toggleDarkMode={darkMode.toggle}
/>
}
/>
<GitHubShareButton />
</TwitterContextProvider>
)
}