
Prior to this commit we'd generate a page for each MDX document as that avoids having to load MDX dynamically (which can be tricky) or through static props (which causes issues with serialization). However, Vercel (which hosts for us) has an upper limit on the number of routes, and because of this extensive documentation, we blew passed it with this approach. This changes to a dynamic resolution of MDX content with an async import in the useEffect hook. This should drastically reduce the number of routes and make Vercel happy. I didn't do much digging into the effects of this on SSR. If it turns out it's causes issues, we'll deal with it at that time.
95 lines
2.7 KiB
JavaScript
95 lines
2.7 KiB
JavaScript
// Dependencies
|
|
import React, { useState, useEffect, useContext } from 'react'
|
|
import { useHotkeys } from 'react-hotkeys-hook'
|
|
// Hooks
|
|
import { useTheme } from 'shared/hooks/use-theme.mjs'
|
|
// Components
|
|
import { SwipeWrapper } from 'shared/components/wrappers/swipes.mjs'
|
|
import { LayoutWrapper, ns as layoutNs } from 'site/components/wrappers/layout.mjs'
|
|
import { DocsLayout, ns as docsNs } from 'site/components/layouts/docs.mjs'
|
|
import { Feeds } from 'site/components/feeds.mjs'
|
|
import { ModalContext } from 'shared/context/modal-context.mjs'
|
|
import { NavigationContext } from 'shared/context/navigation-context.mjs'
|
|
|
|
export const ns = [...new Set([...layoutNs, ...docsNs])]
|
|
|
|
/* This component should wrap all page content */
|
|
export const PageWrapper = (props) => {
|
|
/*
|
|
* Deconstruct props
|
|
*/
|
|
const {
|
|
layout = DocsLayout,
|
|
footer = true,
|
|
header = false,
|
|
children = [],
|
|
path = [],
|
|
locale = 'en',
|
|
} = props
|
|
// Title is typically set in props.t but check props.title too
|
|
const pageTitle = props.t ? props.t : props.title ? props.title : null
|
|
|
|
/*
|
|
* Contexts
|
|
*/
|
|
const { modalContent } = useContext(ModalContext)
|
|
const { setNavigation, slug } = useContext(NavigationContext)
|
|
|
|
/*
|
|
* This forces a re-render upon initial bootstrap of the app
|
|
* This is needed to avoid hydration errors because theme can't be set reliably in SSR
|
|
*/
|
|
const [theme, setTheme] = useTheme()
|
|
const [currentTheme, setCurrentTheme] = useState()
|
|
const [navupdates, setNavupdates] = useState(0)
|
|
useEffect(() => setCurrentTheme(theme), [currentTheme, theme])
|
|
|
|
/*
|
|
* Update navigation context with title and path
|
|
*/
|
|
useEffect(() => {
|
|
// Only update if a new page was loaded
|
|
if (path.join('/') !== 'slug') {
|
|
setNavigation({
|
|
title: pageTitle,
|
|
locale,
|
|
path,
|
|
})
|
|
setNavupdates(navupdates + 1)
|
|
}
|
|
}, [path, pageTitle, slug])
|
|
|
|
/*
|
|
* Hotkeys (keyboard actions)
|
|
*/
|
|
// Trigger search with /
|
|
useHotkeys('/', (evt) => {
|
|
evt.preventDefault()
|
|
setSearch(true)
|
|
})
|
|
|
|
// Search state
|
|
const [search, setSearch] = useState(false)
|
|
|
|
// Helper object to pass props down (keeps things DRY)
|
|
const childProps = { footer, header, pageTitle }
|
|
|
|
// Make layout prop into a (uppercase) component
|
|
const Layout = layout
|
|
|
|
// Return wrapper
|
|
return (
|
|
<SwipeWrapper>
|
|
<div
|
|
data-theme={currentTheme} // This facilitates CSS selectors
|
|
key={currentTheme} // This forces the data-theme update
|
|
>
|
|
<Feeds />
|
|
<LayoutWrapper {...childProps}>
|
|
{Layout ? <Layout {...childProps}>{children}</Layout> : children}
|
|
</LayoutWrapper>
|
|
{modalContent}
|
|
</div>
|
|
</SwipeWrapper>
|
|
)
|
|
}
|