mirror of
https://github.com/d0zingcat/nextjs-notion-starter-kit.git
synced 2026-05-17 15:10:13 +00:00
feat: minor fixes
This commit is contained in:
43
components/Footer.tsx
Normal file
43
components/Footer.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import * as React from 'react'
|
||||
import { FaTwitter, FaGithub, FaLinkedin } from 'react-icons/fa'
|
||||
|
||||
import styles from './styles.module.css'
|
||||
|
||||
export const Footer: React.FC<{}> = () => {
|
||||
return (
|
||||
<footer className={styles.footer}>
|
||||
<div className={styles.copyright}>Copyright 2021 Travis Fischer</div>
|
||||
<div className={styles.social}>
|
||||
<a
|
||||
className={styles.twitter}
|
||||
href='https://twitter.com/transitive_bs'
|
||||
title='Twitter @transitive_bs'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
<FaTwitter />
|
||||
</a>
|
||||
|
||||
<a
|
||||
className={styles.github}
|
||||
href='https://github.com/transitive-bullshit'
|
||||
title='GitHub @transitive-bullshit'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
<FaGithub />
|
||||
</a>
|
||||
|
||||
<a
|
||||
className={styles.linkedin}
|
||||
href='https://www.linkedin.com/in/fisch2'
|
||||
title='LinkedIn Travis Fischer'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
<FaLinkedin />
|
||||
</a>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import { CustomHtml } from './CustomHtml'
|
||||
import { Loading } from './Loading'
|
||||
import { Page404 } from './Page404'
|
||||
import { PageHead } from './PageHead'
|
||||
import { Footer } from './Footer'
|
||||
|
||||
import styles from './styles.module.css'
|
||||
|
||||
@@ -138,6 +139,7 @@ export const NotionPage: React.FC<types.PageProps> = ({
|
||||
mapPageUrl={siteMapPageUrl}
|
||||
mapImageUrl={mapNotionImageUrl}
|
||||
searchNotion={searchNotion}
|
||||
footer={<Footer />}
|
||||
/>
|
||||
|
||||
<CustomHtml site={site} />
|
||||
|
||||
@@ -42,104 +42,47 @@
|
||||
width: 640px;
|
||||
}
|
||||
|
||||
.notion {
|
||||
padding-bottom: calc(max(10vh, 120px));
|
||||
}
|
||||
|
||||
.demoFooter {
|
||||
position: fixed;
|
||||
z-index: 800;
|
||||
bottom: 1em;
|
||||
left: 10vw;
|
||||
right: 10vw;
|
||||
border-radius: 4px;
|
||||
background: #eb625a;
|
||||
padding: 16px 24px;
|
||||
.footer {
|
||||
width: 100%;
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
padding: 8px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.demoLhs a,
|
||||
.demoLhs a:visited,
|
||||
.demoLhs a:hover,
|
||||
.demoLhs a:active {
|
||||
text-decoration: none;
|
||||
color: #fff;
|
||||
.copyright {
|
||||
font-size: 80%;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.demoLhs {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
.social a {
|
||||
font-size: 2em;
|
||||
display: inline-block;
|
||||
padding: 0.5em;
|
||||
margin-right: 1vw;
|
||||
transition: color 250ms ease-out;
|
||||
}
|
||||
|
||||
.demoTitle {
|
||||
font-size: 1.2em;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
padding-right: 0.5em;
|
||||
margin-right: 0.5em;
|
||||
border-right: 1px solid #fff;
|
||||
white-space: nowrap;
|
||||
.social a:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.demoTitle img {
|
||||
height: 1.5em;
|
||||
margin-right: 0.5em;
|
||||
.social a:hover {
|
||||
transition: color 50ms ease-out;
|
||||
}
|
||||
|
||||
.demoDesc {
|
||||
font-size: 0.9em;
|
||||
.twitter:hover {
|
||||
color: #2795e9;
|
||||
}
|
||||
|
||||
.demoCta {
|
||||
display: block;
|
||||
margin-left: 0.5em;
|
||||
font-size: 0.9em;
|
||||
text-decoration: none;
|
||||
color: #fff;
|
||||
border-radius: 2px;
|
||||
padding: 8px 12px;
|
||||
|
||||
background: transparent;
|
||||
border: 1px solid #fff;
|
||||
transition: all 300ms ease;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
transform: scale(1);
|
||||
.github:hover {
|
||||
color: #c9510c;
|
||||
}
|
||||
|
||||
.demoCta:hover {
|
||||
background: #fff;
|
||||
color: #eb625a;
|
||||
|
||||
transition: all 200ms ease;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.demoCta:active {
|
||||
transition: all 100ms ease;
|
||||
transform: scale(1.03);
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 740px) {
|
||||
.demoFooter {
|
||||
left: 1em;
|
||||
right: 1em;
|
||||
}
|
||||
|
||||
.demoTitle {
|
||||
border: 0 none;
|
||||
}
|
||||
|
||||
.demoDesc {
|
||||
display: none;
|
||||
}
|
||||
.linkedin:hover {
|
||||
color: #0077b5;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ export const fathomConfig = fathomId
|
||||
}
|
||||
: undefined
|
||||
|
||||
// TODO: address duplication between server-side env and client-side config
|
||||
// TODO: fix duplication between server-side env and client-side config
|
||||
export const apiBaseUrl = `/api`
|
||||
export const api = {
|
||||
createPreviewImage: `${apiBaseUrl}/create-preview-image`,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import pMemoize from 'p-memoize'
|
||||
import { getAllPagesInSpace, getCanonicalPageId } from 'notion-utils'
|
||||
|
||||
import * as types from './types'
|
||||
import notion from './notion'
|
||||
|
||||
export const getAllPages = pMemoize(getAllPagesImpl, { maxAge: 60000 * 5 })
|
||||
@@ -8,16 +9,44 @@ export const getAllPages = pMemoize(getAllPagesImpl, { maxAge: 60000 * 5 })
|
||||
export async function getAllPagesImpl(
|
||||
rootNotionPageId: string,
|
||||
rootNotionSpaceId: string
|
||||
): Promise<string[]> {
|
||||
const pages = await getAllPagesInSpace(
|
||||
): Promise<Partial<types.SiteMap>> {
|
||||
const pageMap = await getAllPagesInSpace(
|
||||
rootNotionPageId,
|
||||
rootNotionSpaceId,
|
||||
notion.getPage.bind(notion)
|
||||
)
|
||||
|
||||
const canonicalPageIds = Object.keys(pages)
|
||||
.map((pageId) => getCanonicalPageId(pageId, pages[pageId]))
|
||||
.filter(Boolean)
|
||||
const canonicalPageMap = Object.keys(pageMap).reduce(
|
||||
(map, pageId: string) => {
|
||||
const recordMap = pageMap[pageId]
|
||||
const canonicalPageId = getCanonicalPageId(pageId, recordMap, {
|
||||
uuid: false
|
||||
})
|
||||
|
||||
return canonicalPageIds
|
||||
if (map[canonicalPageId]) {
|
||||
console.error(
|
||||
'duplicate canonical page id',
|
||||
canonicalPageId,
|
||||
pageId,
|
||||
map[canonicalPageId].pageId
|
||||
)
|
||||
|
||||
return map
|
||||
} else {
|
||||
return {
|
||||
...map,
|
||||
[canonicalPageId]: {
|
||||
pageId,
|
||||
recordMap
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{}
|
||||
)
|
||||
|
||||
return {
|
||||
pageMap,
|
||||
canonicalPageMap
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,14 +11,17 @@ export async function getSiteMaps(): Promise<types.SiteMap[]> {
|
||||
sites,
|
||||
async (site, index) => {
|
||||
try {
|
||||
console.log('getSiteMap', index, site)
|
||||
console.log(
|
||||
'getSiteMap',
|
||||
`${index + 1}/${sites.length}`,
|
||||
`(${(((index + 1) / sites.length) * 100) | 0}%)`,
|
||||
site
|
||||
)
|
||||
|
||||
return {
|
||||
site,
|
||||
pageIds: await getAllPages(
|
||||
site.rootNotionPageId,
|
||||
site.rootNotionSpaceId
|
||||
)
|
||||
}
|
||||
...(await getAllPages(site.rootNotionPageId, site.rootNotionSpaceId))
|
||||
} as types.SiteMap
|
||||
} catch (err) {
|
||||
console.warn('site build error', index, site, err)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,10 @@ export const mapPageUrl = (
|
||||
if (uuidToId(pageId) === site.rootNotionPageId) {
|
||||
return createUrl('/', searchParams)
|
||||
} else {
|
||||
return createUrl(`/${getCanonicalPageId(pageId, recordMap)}`, searchParams)
|
||||
return createUrl(
|
||||
`/${getCanonicalPageId(pageId, recordMap, { uuid: false })}`,
|
||||
searchParams
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +25,9 @@ export const getCanonicalPageUrl = (
|
||||
if (uuidToId(pageId) === site.rootNotionPageId) {
|
||||
return `https://${site.domain}`
|
||||
} else {
|
||||
return `https://${site.domain}/${getCanonicalPageId(pageUuid, recordMap)}`
|
||||
return `https://${site.domain}/${getCanonicalPageId(pageUuid, recordMap, {
|
||||
uuid: false
|
||||
})}`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as acl from './acl'
|
||||
import * as types from './types'
|
||||
import { parsePageId } from 'notion-utils'
|
||||
import { getPage } from './notion'
|
||||
import { getSiteMaps } from './get-site-maps'
|
||||
import { getSiteForDomain } from './get-site-for-domain'
|
||||
|
||||
export async function resolveNotionPage(domain: string, rawPageId?: string) {
|
||||
@@ -12,6 +13,24 @@ export async function resolveNotionPage(domain: string, rawPageId?: string) {
|
||||
if (rawPageId && rawPageId !== 'index') {
|
||||
pageId = parsePageId(rawPageId)
|
||||
|
||||
// handle mapping user-friendly canonical page paths to Notion page IDs
|
||||
// e.g., /developer-x-entrepreneur versus /71201624b204481f862630ea25ce62fe
|
||||
if (!pageId) {
|
||||
const siteMaps = await getSiteMaps()
|
||||
const siteMap = siteMaps[0]
|
||||
console.log(siteMap)
|
||||
pageId = siteMap.canonicalPageMap[pageId]?.pageId
|
||||
|
||||
if (!pageId) {
|
||||
return {
|
||||
error: {
|
||||
message: `Invalid notion page ID "${rawPageId}"`,
|
||||
statusCode: 404
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!pageId) {
|
||||
return {
|
||||
error: {
|
||||
|
||||
12
lib/types.ts
12
lib/types.ts
@@ -1,4 +1,4 @@
|
||||
import { Block, ExtendedRecordMap } from 'notion-types'
|
||||
import { ExtendedRecordMap, PageMap } from 'notion-types'
|
||||
|
||||
export * from 'notion-types'
|
||||
|
||||
@@ -47,7 +47,15 @@ export interface Site extends Model {
|
||||
|
||||
export interface SiteMap {
|
||||
site: Site
|
||||
pageIds: string[]
|
||||
pageMap: PageMap
|
||||
canonicalPageMap: CanonicalPageMap
|
||||
}
|
||||
|
||||
export interface CanonicalPageMap {
|
||||
[canonicalPagePath: string]: {
|
||||
pageId: string
|
||||
recordMap: ExtendedRecordMap | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface PreviewImage {
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
"react": "17.0.1",
|
||||
"react-body-classname": "^1.3.1",
|
||||
"react-dom": "17.0.1",
|
||||
"react-icons": "^4.1.0",
|
||||
"react-notion-x": "^3.0.1",
|
||||
"react-use": "^15.3.3"
|
||||
},
|
||||
|
||||
@@ -17,3 +17,8 @@
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.notion-header {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@@ -5071,6 +5071,11 @@ react-dom@17.0.1:
|
||||
object-assign "^4.1.1"
|
||||
scheduler "^0.20.1"
|
||||
|
||||
react-icons@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.1.0.tgz#9ca9bcbf2e3aee8e86e378bb9d465842947bbfc3"
|
||||
integrity sha512-FCXBg1JbbR0vWALXIxmFAfozHdVIJmmwCD81Jk0EKOt7Ax4AdBNcaRkWhR0NaKy9ugJgoY3fFvo0PHpte55pXg==
|
||||
|
||||
react-image@^4.0.3:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/react-image/-/react-image-4.0.3.tgz#6fa722877660b67295298a914bff1ed87ad2cf83"
|
||||
|
||||
Reference in New Issue
Block a user