From 679131ad22b0aa581ed9460096f8df12a02e5029 Mon Sep 17 00:00:00 2001 From: Travis Fischer Date: Thu, 7 Apr 2022 12:38:58 -0400 Subject: [PATCH 1/4] feat: WIP custom header nav --- .env.example | 2 +- components/NotionPage.tsx | 4 +++- components/NotionPageHeader.tsx | 26 ++++++++++++++++++++++++ lib/config.ts | 17 ++++++++++++---- lib/get-config-value.ts | 9 +++++---- lib/site-config.ts | 34 ++++++++++++++++++++++++++++++++ lib/types.ts | 2 ++ readme.md | 10 +++++----- site.config.js => site.config.ts | 8 ++++++-- styles/notion.css | 2 +- tsconfig.json | 2 +- 11 files changed, 97 insertions(+), 19 deletions(-) create mode 100644 components/NotionPageHeader.tsx create mode 100644 lib/site-config.ts rename site.config.js => site.config.ts (93%) diff --git a/.env.example b/.env.example index 04dfc9a..bca6ff5 100644 --- a/.env.example +++ b/.env.example @@ -18,7 +18,7 @@ # Optional (for persisting preview images to redis) # NOTE: if you want to enable redis, only REDIS_HOST and REDIS_PASSWORD are required -# NOTE: don't forget to set isRedisEnabled to true in the site.config.js file +# NOTE: don't forget to set isRedisEnabled to true in the site.config.ts file #REDIS_HOST= #REDIS_PASSWORD= #REDIS_USER='default' diff --git a/components/NotionPage.tsx b/components/NotionPage.tsx index 38cedd4..e6a6638 100644 --- a/components/NotionPage.tsx +++ b/components/NotionPage.tsx @@ -31,6 +31,7 @@ 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' @@ -181,7 +182,8 @@ export const NotionPage: React.FC = ({ Equation, Pdf, Modal, - Tweet + Tweet, + Header: NotionPageHeader }} recordMap={recordMap} rootPageId={site.rootNotionPageId} diff --git a/components/NotionPageHeader.tsx b/components/NotionPageHeader.tsx new file mode 100644 index 0000000..e4853e4 --- /dev/null +++ b/components/NotionPageHeader.tsx @@ -0,0 +1,26 @@ +import React from 'react' +// import useDarkMode from '@fisch0920/use-dark-mode' + +import { Header, Breadcrumbs, Search } from 'react-notion-x' + +import * as types from 'lib/types' +import { navigationStyle } from 'lib/config' + +// import styles from './styles.module.css' + +export const NotionPageHeader: React.FC<{ + block: types.CollectionViewPageBlock | types.PageBlock +}> = ({ block }) => { + if (navigationStyle === 'default') { + return
+ } + + return ( +
+
+ + +
+
+ ) +} diff --git a/lib/config.ts b/lib/config.ts index d26d842..c77e973 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -1,14 +1,18 @@ /** * Site-wide app configuration. * - * This file pulls from the root "site.config.js" as well as environment variables + * This file pulls from the root "site.config.ts" as well as environment variables * for optional depenencies. */ import { parsePageId } from 'notion-utils' import posthog from 'posthog-js' import { getEnv, getSiteConfig } from './get-config-value' -import { PageUrlOverridesInverseMap, PageUrlOverridesMap } from './types' +import { + PageUrlOverridesInverseMap, + PageUrlOverridesMap, + NavigationStyle +} from './types' export const rootNotionPageId: string = parsePageId( getSiteConfig('rootNotionPageId'), @@ -48,9 +52,9 @@ export const description: string = getSiteConfig('description', 'Notion Blog') // social accounts export const twitter: string | null = getSiteConfig('twitter', null) -export const zhihu: string | null = getSiteConfig('zhihu', null) export const github: string | null = getSiteConfig('github', null) export const linkedin: string | null = getSiteConfig('linkedin', null) +export const zhihu: string | null = getSiteConfig('zhihu', null) // default notion values for site-wide consistency (optional; may be overridden on a per-page basis) export const defaultPageIcon: string | null = getSiteConfig( @@ -78,12 +82,17 @@ export const isTweetEmbedSupportEnabled: boolean = getSiteConfig( true ) -// where it all starts -- the site's root Notion page +// Optional whether or not to include the Notion ID in page URLs or just use slugs export const includeNotionIdInUrls: boolean = getSiteConfig( 'includeNotionIdInUrls', !!isDev ) +export const navigationStyle: NavigationStyle = getSiteConfig( + 'navigationStyle', + 'default' +) + // ---------------------------------------------------------------------------- // Optional redis instance for persisting preview images diff --git a/lib/get-config-value.ts b/lib/get-config-value.ts index a702150..c3b9b7a 100644 --- a/lib/get-config-value.ts +++ b/lib/get-config-value.ts @@ -1,11 +1,12 @@ import rawSiteConfig from '../site.config' +import { SiteConfig } from './site-config' if (!rawSiteConfig) { - throw new Error(`Config error: invalid site.config.js`) + throw new Error(`Config error: invalid site.config.ts`) } -// TODO: allow environment variables to override site.config.js -let siteConfigOverrides +// allow environment variables to override site.config.ts +let siteConfigOverrides: SiteConfig try { if (process.env.NEXT_PUBLIC_SITE_CONFIG) { @@ -16,7 +17,7 @@ try { throw err } -const siteConfig = { +const siteConfig: SiteConfig = { ...rawSiteConfig, ...siteConfigOverrides } diff --git a/lib/site-config.ts b/lib/site-config.ts new file mode 100644 index 0000000..70c5778 --- /dev/null +++ b/lib/site-config.ts @@ -0,0 +1,34 @@ +import * as types from './types' + +export interface SiteConfig { + rootNotionPageId: string + rootNotionSpaceId?: string + + name: string + domain: string + author: string + description?: string + + twitter?: string + github?: string + linkedin?: string + zhihu?: string + + defaultPageIcon?: string | null + defaultPageCover?: string | null + defaultPageCoverPosition?: number | null + + isPreviewImageSupportEnabled?: boolean + isTweetEmbedSupportEnabled?: boolean + isRedisEnabled?: boolean + + includeNotionIdInUrls?: boolean + pageUrlOverrides?: types.PageUrlOverridesMap + pageUrlAdditions?: types.PageUrlOverridesMap + + navigationStyle?: types.NavigationStyle +} + +export const siteConfig = (config: SiteConfig): SiteConfig => { + return config +} diff --git a/lib/types.ts b/lib/types.ts index 3ac70f0..e50121e 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -2,6 +2,8 @@ import { ExtendedRecordMap, PageMap } from 'notion-types' export * from 'notion-types' +export type NavigationStyle = 'default' | 'custom' + export interface PageError { message?: string statusCode: number diff --git a/readme.md b/readme.md index d438d65..61bc574 100644 --- a/readme.md +++ b/readme.md @@ -18,7 +18,7 @@ It uses Notion as a CMS, [react-notion-x](https://github.com/NotionX/react-notio ## Features -- Setup only takes a few minutes ([single config file](./site.config.js)) 💪 +- Setup only takes a few minutes ([single config file](./site.config.ts)) 💪 - Robust support for Notion content via [react-notion-x](https://github.com/NotionX/react-notion-x) - Built using Next.js, TS, and React - Excellent page speeds @@ -38,12 +38,12 @@ It uses Notion as a CMS, [react-notion-x](https://github.com/NotionX/react-notio ## Setup -**All config is defined in [site.config.js](./site.config.js).** +**All config is defined in [site.config.ts](./site.config.ts).** This project requires a recent version of Node.js (>= 14.17). 1. Fork / clone this repo -2. Change a few values in [site.config.js](./site.config.js) +2. Change a few values in [site.config.ts](./site.config.ts) 3. `npm install` 4. `npm run dev` to test locally 5. `npm run deploy` to deploy to vercel 💪 @@ -82,11 +82,11 @@ NOTE: if you have multiple pages in your workspace with the same slugified name, We use [next/image](https://nextjs.org/docs/api-reference/next/image) to serve images efficiently, with preview images optionally generated via [lqip-modern](https://github.com/transitive-bullshit/lqip-modern). This gives us extremely optimized image support for sexy smooth images. -Preview images are **enabled by default**, but they can be slow to generate, so if you want to disable them, set `isPreviewImageSupportEnabled` to `false` in `site.config.js`. +Preview images are **enabled by default**, but they can be slow to generate, so if you want to disable them, set `isPreviewImageSupportEnabled` to `false` in `site.config.ts`. ### Redis -If you want to cache generated preview images to speed up subsequent builds, you'll need to first set up an external [Redis](https://redis.io) data store. To enable redis caching, set `isRedisEnabled` to `true` in `site.config.js` and then set `REDIS_HOST` and `REDIS_PASSWORD` environment variables to point to your redis instance. +If you want to cache generated preview images to speed up subsequent builds, you'll need to first set up an external [Redis](https://redis.io) data store. To enable redis caching, set `isRedisEnabled` to `true` in `site.config.ts` and then set `REDIS_HOST` and `REDIS_PASSWORD` environment variables to point to your redis instance. You can do this locally by adding a `.env` file: diff --git a/site.config.js b/site.config.ts similarity index 93% rename from site.config.js rename to site.config.ts index 7cb9a72..afcde4c 100644 --- a/site.config.js +++ b/site.config.ts @@ -1,4 +1,6 @@ -export default { +import { siteConfig } from './lib/site-config' + +export default siteConfig({ // the site's root Notion page (required) rootNotionPageId: '7875426197cf461698809def95960ebf', @@ -25,6 +27,8 @@ export default { defaultPageCover: null, defaultPageCoverPosition: 0.5, + navigationStyle: 'custom', + // whether or not to enable support for LQIP preview images (optional) isPreviewImageSupportEnabled: true, @@ -42,4 +46,4 @@ export default { // '/bar': '0be6efce9daf42688f65c76b89f8eb27' // } pageUrlOverrides: null -} +}) diff --git a/styles/notion.css b/styles/notion.css index 112429c..2eebe20 100644 --- a/styles/notion.css +++ b/styles/notion.css @@ -39,7 +39,7 @@ width: 100% !important; } -.notion-header .nav-header { +.notion-header .notion-nav-header { max-width: 1100px; margin: 0 auto; } diff --git a/tsconfig.json b/tsconfig.json index 1fc4cb5..73d6662 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,5 +19,5 @@ "incremental": true }, "exclude": ["node_modules"], - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"] + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "site.config.ts"] } From 45b20d9622043e5398a739b7da8a5b28d66bb1ac Mon Sep 17 00:00:00 2001 From: Travis Fischer Date: Thu, 7 Apr 2022 14:53:08 -0400 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=91=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/Footer.tsx | 43 ++++++++---------- components/NotionPage.tsx | 78 +++++++++++++++++++-------------- components/NotionPageHeader.tsx | 66 +++++++++++++++++++++++++--- lib/config.ts | 9 ++++ lib/map-page-url.ts | 6 ++- lib/notion.ts | 35 ++++++++++++++- lib/site-config.ts | 8 ++++ lib/types.ts | 17 ------- site.config.ts | 16 +++++-- styles/notion.css | 4 ++ 10 files changed, 196 insertions(+), 86 deletions(-) diff --git a/components/Footer.tsx b/components/Footer.tsx index fca17b8..77ef2d2 100644 --- a/components/Footer.tsx +++ b/components/Footer.tsx @@ -1,4 +1,5 @@ import React from 'react' +import useDarkMode from '@fisch0920/use-dark-mode' import { FaTwitter } from '@react-icons/all-files/fa/FaTwitter' import { FaZhihu } from '@react-icons/all-files/fa/FaZhihu' import { FaGithub } from '@react-icons/all-files/fa/FaGithub' @@ -11,39 +12,31 @@ import styles from './styles.module.css' // TODO: merge the data and icons from PageSocial with the social links in Footer -export const Footer: React.FC<{ - isDarkMode: boolean - toggleDarkMode: () => void -}> = ({ isDarkMode, toggleDarkMode }) => { - const [hasMounted, setHasMounted] = React.useState(false) - const toggleDarkModeCb = React.useCallback( +export const FooterImpl: React.FC = () => { + const darkMode = useDarkMode(false, { classNameDark: 'dark-mode' }) + + const onToggleDarkMode = React.useCallback( (e) => { e.preventDefault() - toggleDarkMode() + darkMode.toggle() }, - [toggleDarkMode] + [darkMode] ) - React.useEffect(() => { - setHasMounted(true) - }, []) - return ( ) } + +export const Footer = React.memo(FooterImpl) diff --git a/components/NotionPage.tsx b/components/NotionPage.tsx index e6a6638..3e9e397 100644 --- a/components/NotionPage.tsx +++ b/components/NotionPage.tsx @@ -58,7 +58,11 @@ const Pdf = dynamic( } ) const Modal = dynamic( - () => import('react-notion-x/build/third-party/modal').then((m) => m.Modal), + () => + import('react-notion-x/build/third-party/modal').then((m) => { + m.Modal.setAppElement('.notion-viewport') + return m.Modal + }), { ssr: false } @@ -73,15 +77,48 @@ export const NotionPage: React.FC = ({ const router = useRouter() const lite = useSearchParam('lite') - const params: any = {} - if (lite) params.lite = lite + const components = React.useMemo( + () => ({ + nextImage: Image, + nextLink: Link, + Code, + Collection, + Equation, + Pdf, + Modal, + Tweet, + Header: NotionPageHeader + }), + [] + ) + + const twitterContextValue = React.useMemo(() => { + if (!recordMap) { + return null + } + + return { + tweetAstMap: (recordMap as any).tweetAstMap || {}, + swrOptions: { + fetcher: (id: string) => + fetch(`/api/get-tweet-ast/${id}`).then((r) => r.json()) + } + } + }, [recordMap]) // lite mode is for oembed const isLiteMode = lite === 'true' - const searchParams = new URLSearchParams(params) const darkMode = useDarkMode(false, { classNameDark: 'dark-mode' }) + 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]) + if (router.isFallback) { return } @@ -111,8 +148,6 @@ export const NotionPage: React.FC = ({ g.block = block } - const siteMapPageUrl = mapPageUrl(site, recordMap, searchParams) - const canonicalPageUrl = !config.isDev && getCanonicalPageUrl(site, recordMap)(pageId) @@ -147,15 +182,7 @@ export const NotionPage: React.FC = ({ } return ( - - fetch(`/api/get-tweet-ast/${id}`).then((r) => r.json()) - } - }} - > + = ({ styles.notion, pageId === site.rootNotionPageId && 'index-page' )} - components={{ - nextImage: Image, - nextLink: Link, - Code, - Collection, - Equation, - Pdf, - Modal, - Tweet, - Header: NotionPageHeader - }} + components={components} recordMap={recordMap} rootPageId={site.rootNotionPageId} rootDomain={site.domain} @@ -199,14 +216,9 @@ export const NotionPage: React.FC = ({ defaultPageCoverPosition={config.defaultPageCoverPosition} mapPageUrl={siteMapPageUrl} mapImageUrl={mapImageUrl} - searchNotion={searchNotion} + searchNotion={config.isSearchEnabled ? searchNotion : null} pageAside={pageAside} - footer={ -