import cs from 'classnames' import dynamic from 'next/dynamic' import Image from 'next/legacy/image' import Link from 'next/link' import { useRouter } from 'next/router' import { type PageBlock } from 'notion-types' import { formatDate, getBlockTitle, getPageProperty } from 'notion-utils' import * as React from 'react' import BodyClassName from 'react-body-classname' import { NotionRenderer } from 'react-notion-x' import TweetEmbed from 'react-tweet-embed' import { useSearchParam } from 'react-use' import type * as types from '@/lib/types' import * as config from '@/lib/config' import { mapImageUrl } from '@/lib/map-image-url' import { getCanonicalPageUrl, mapPageUrl } from '@/lib/map-page-url' import { searchNotion } from '@/lib/search-notion' import { useDarkMode } from '@/lib/use-dark-mode' import { Footer } from './Footer' import { GitHubShareButton } from './GitHubShareButton' import { Loading } from './Loading' import { NotionPageHeader } from './NotionPageHeader' import { Page404 } from './Page404' import { PageAside } from './PageAside' import { PageHead } from './PageHead' import styles from './styles.module.css' // ----------------------------------------------------------------------------- // dynamic imports for optional components // ----------------------------------------------------------------------------- const Code = dynamic(() => import('react-notion-x/build/third-party/code').then(async (m) => { // add / remove any prism syntaxes here await Promise.allSettled([ import('prismjs/components/prism-markup-templating.js'), import('prismjs/components/prism-markup.js'), import('prismjs/components/prism-bash.js'), import('prismjs/components/prism-c.js'), import('prismjs/components/prism-cpp.js'), import('prismjs/components/prism-csharp.js'), import('prismjs/components/prism-docker.js'), import('prismjs/components/prism-java.js'), import('prismjs/components/prism-js-templates.js'), import('prismjs/components/prism-coffeescript.js'), import('prismjs/components/prism-diff.js'), import('prismjs/components/prism-git.js'), import('prismjs/components/prism-go.js'), import('prismjs/components/prism-graphql.js'), import('prismjs/components/prism-handlebars.js'), import('prismjs/components/prism-less.js'), import('prismjs/components/prism-makefile.js'), import('prismjs/components/prism-markdown.js'), import('prismjs/components/prism-objectivec.js'), import('prismjs/components/prism-ocaml.js'), import('prismjs/components/prism-python.js'), import('prismjs/components/prism-reason.js'), import('prismjs/components/prism-rust.js'), import('prismjs/components/prism-sass.js'), import('prismjs/components/prism-scss.js'), import('prismjs/components/prism-solidity.js'), import('prismjs/components/prism-sql.js'), import('prismjs/components/prism-stylus.js'), import('prismjs/components/prism-swift.js'), import('prismjs/components/prism-wasm.js'), import('prismjs/components/prism-yaml.js') ]) return 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.setAppElement('.notion-viewport') return m.Modal }), { ssr: false } ) function Tweet({ id }: { id: string }) { return } const propertyLastEditedTimeValue = ( { block, pageHeader }, defaultFn: () => React.ReactNode ) => { if (pageHeader && block?.last_edited_time) { return `Last updated ${formatDate(block?.last_edited_time, { month: 'long' })}` } return defaultFn() } const propertyDateValue = ( { data, schema, pageHeader }, defaultFn: () => React.ReactNode ) => { if (pageHeader && schema?.name?.toLowerCase() === 'published') { const publishDate = data?.[0]?.[1]?.[0]?.[1]?.start_date if (publishDate) { return `${formatDate(publishDate, { month: 'long' })}` } } return defaultFn() } const propertyTextValue = ( { schema, pageHeader }, defaultFn: () => React.ReactNode ) => { if (pageHeader && schema?.name?.toLowerCase() === 'author') { return {defaultFn()} } return defaultFn() } export function NotionPage({ site, recordMap, error, pageId }: types.PageProps) { const router = useRouter() const lite = useSearchParam('lite') const components = React.useMemo( () => ({ nextImage: Image, nextLink: Link, Code, Collection, Equation, Pdf, Modal, Tweet, Header: NotionPageHeader, propertyLastEditedTimeValue, propertyTextValue, propertyDateValue }), [] ) // lite mode is for oembed const isLiteMode = lite === 'true' const { isDarkMode } = useDarkMode() const siteMapPageUrl = React.useMemo(() => { const params: any = {} if (lite) params.lite = lite const searchParams = new URLSearchParams(params) return mapPageUrl(site, recordMap, searchParams) }, [site, recordMap, lite]) const keys = Object.keys(recordMap?.block || {}) const block = recordMap?.block?.[keys[0]]?.value // const isRootPage = // parsePageId(block?.id) === parsePageId(site?.rootNotionPageId) const isBlogPost = block?.type === 'page' && block?.parent_table === 'collection' const showTableOfContents = !!isBlogPost const minTableOfContentsItems = 3 const pageAside = React.useMemo( () => ( ), [block, recordMap, isBlogPost] ) const footer = React.useMemo(() =>