diff --git a/components/Footer.tsx b/components/Footer.tsx
new file mode 100644
index 0000000..01840f5
--- /dev/null
+++ b/components/Footer.tsx
@@ -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 (
+
+ )
+}
diff --git a/components/NotionPage.tsx b/components/NotionPage.tsx
index ea9f27a..4a55a89 100644
--- a/components/NotionPage.tsx
+++ b/components/NotionPage.tsx
@@ -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 = ({
mapPageUrl={siteMapPageUrl}
mapImageUrl={mapNotionImageUrl}
searchNotion={searchNotion}
+ footer={}
/>
diff --git a/components/styles.module.css b/components/styles.module.css
index 777e36d..5cb9d8f 100644
--- a/components/styles.module.css
+++ b/components/styles.module.css
@@ -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;
}
diff --git a/lib/config.ts b/lib/config.ts
index f73030b..b022422 100644
--- a/lib/config.ts
+++ b/lib/config.ts
@@ -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`,
diff --git a/lib/get-all-pages.ts b/lib/get-all-pages.ts
index dbfaa84..2796356 100644
--- a/lib/get-all-pages.ts
+++ b/lib/get-all-pages.ts
@@ -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 {
- const pages = await getAllPagesInSpace(
+): Promise> {
+ 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
+ }
}
diff --git a/lib/get-site-maps.ts b/lib/get-site-maps.ts
index d3269bd..55173da 100644
--- a/lib/get-site-maps.ts
+++ b/lib/get-site-maps.ts
@@ -11,14 +11,17 @@ export async function getSiteMaps(): Promise {
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)
}
diff --git a/lib/map-page-url.ts b/lib/map-page-url.ts
index 6ad7ebc..c15bf30 100644
--- a/lib/map-page-url.ts
+++ b/lib/map-page-url.ts
@@ -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
+ })}`
}
}
diff --git a/lib/resolve-notion-page.ts b/lib/resolve-notion-page.ts
index aedd7eb..e474e8c 100644
--- a/lib/resolve-notion-page.ts
+++ b/lib/resolve-notion-page.ts
@@ -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: {
diff --git a/lib/types.ts b/lib/types.ts
index 8dbe42b..244ffba 100644
--- a/lib/types.ts
+++ b/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 {
diff --git a/package.json b/package.json
index 61c2334..92ad8bd 100644
--- a/package.json
+++ b/package.json
@@ -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"
},
diff --git a/styles/notion.css b/styles/notion.css
index 059b772..1b37945 100644
--- a/styles/notion.css
+++ b/styles/notion.css
@@ -17,3 +17,8 @@
margin-top: 1em;
margin-bottom: 1em;
}
+
+.notion-header {
+ max-width: 1100px;
+ margin: 0 auto;
+}
diff --git a/yarn.lock b/yarn.lock
index 407f88a..fcebbd7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -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"