Created
September 13, 2023 10:00
-
-
Save edlaver/0ad518e1a5db5016de6f959e897621d2 to your computer and use it in GitHub Desktop.
Example service to fetch a Notion record map from a cf-notion-cache worker, and render it in a Polaris container
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from "react"; | |
import { | |
Layout, | |
LegacyCard, | |
Page, | |
SkeletonBodyText, | |
SkeletonDisplayText, | |
TextContainer, | |
} from "@shopify/polaris"; | |
import { useQuery } from "@tanstack/react-query"; | |
import { NotionRenderer } from "react-notion-x"; | |
import "react-notion-x/src/styles.css"; | |
import { extractNotionTocHtml } from "@/services/notionService"; | |
//----------------------------------------------------------------// | |
// Setup some sort of loader to get the Notion record map... | |
// In this snippet, I'm using React Query, with a function called `notionQuery` | |
// e.g. | |
import { fetchNotionPageFullAndTocOmittedRecordMaps } from "@/services/notionService"; | |
const notionQuery = () => ({ | |
queryKey: ["notion"], | |
queryFn: async () => { | |
log.debug("Executing: notionQuery..."); | |
// Your Notion page, e.g. | |
// const docsPageUrl = `https://yournotionsite.notion.site/Docs-4006f958c7a6426ca9f18390444f3111`; <-- Just need the ID part | |
const docsPageUrl = `4006f958c7a6426ca9f18390444f3111`; | |
// Load Notion RecordMap from Gadget API | |
return await fetchNotionPageFullAndTocOmittedRecordMaps(docsPageUrl); | |
}, | |
}); | |
//----------------------------------------------------------------// | |
function HelpView() { | |
const { data: notionData } = useQuery(notionQuery()); // <-- References loader from above, but you'll need to setup something different | |
const { fullRecordMap, tocOmittedRecordMap } = notionData; | |
return ( | |
<Layout> | |
<Layout.Section secondary subdued> | |
<LegacyCard sectioned> | |
<div | |
className="notion-toc-container" | |
dangerouslySetInnerHTML={{ | |
__html: extractNotionTocHtml( | |
<NotionRenderer | |
// Use the full recordMap (i.e. containing the TOC), so we can extract that HTML | |
recordMap={fullRecordMap} | |
// Set fullPage to false to render page content only | |
// this will remove the header, cover image, and footer | |
fullPage={false} | |
// TODO: Set based on user's prefs | |
darkMode={false} | |
// previewImages={false} | |
showTableOfContents={true} | |
/> | |
), | |
}} | |
></div> | |
</LegacyCard> | |
</Layout.Section> | |
<Layout.Section> | |
<LegacyCard sectioned> | |
<div> | |
<NotionRenderer | |
recordMap={tocOmittedRecordMap} | |
// Set fullPage to false to render page content only | |
// this will remove the header, cover image, and footer | |
fullPage={false} | |
// TODO: Set based on user's prefs | |
darkMode={false} | |
// previewImages={false} | |
//// Neither of these seem to do anything: | |
// showTableOfContents={false} | |
// minTableOfContentsItems={1000} | |
/> | |
</div> | |
</LegacyCard> | |
</Layout.Section> | |
</Layout> | |
); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as ReactDOMServer from "react-dom/server"; | |
import ky from "ky"; | |
import { cloneDeep, omitBy } from "lodash"; | |
import { parsePageId } from "notion-utils"; | |
// TODO: Move to .env file | |
const notionCacheBaseUrl = `https://cf-notion-cache.workers.dev/cache/`; // Update with your Cloudflare worker URL | |
const fetchNotionPageRecordMapByPageId = async (pageId) => { | |
let recordMap; | |
let notionCacheUrl = notionCacheBaseUrl + pageId; | |
if (process.env.NODE_ENV === "development") { | |
// Force a refresh of the cached version in development | |
notionCacheUrl += "?refetch=true"; | |
log.debug({ notionCacheUrl }); | |
} | |
recordMap = await ky.get(notionCacheUrl).json(); | |
// log.debug({ recordMap }); | |
return recordMap; | |
}; | |
const fetchNotionPageRecordMap = async (pageUrlOrId) => { | |
const pageId = parsePageId(pageUrlOrId); | |
const recordMap = await fetchNotionPageRecordMapByPageId(pageId); | |
return recordMap; | |
}; | |
const fetchNotionPageFullAndTocOmittedRecordMaps = async (pageUrlOrId) => { | |
const recordMap = await fetchNotionPageRecordMap(pageUrlOrId); | |
// log.debug("fetchNotionHelpPageData: recordMap", recordMap); //=> a JSON blob returned by the global action | |
// Extract table of contents into separate record map | |
const fullRecordMap = cloneDeep(recordMap); | |
const tocOmittedRecordMap = cloneDeep(recordMap); | |
// Exclude table of contents section | |
tocOmittedRecordMap.block = omitBy(tocOmittedRecordMap.block, (block) => { | |
return block?.value?.type === "table_of_contents"; | |
}); | |
// TODO: Try to use: notion-utils/src/get-page-table-of-contents.ts | |
// to get the table of contents structure, and then just render it ourselves? | |
// log.debug("fetchNotionHelpPageData: returning: ", fullRecordMap, prunedRecordMap); | |
return { fullRecordMap, tocOmittedRecordMap }; | |
}; | |
// Function to render the TOC from the Notion RecordMap as an HTML string | |
// Note: Uses browsers DOM Parser, so can only work client side._online_store_editor_live_setting | |
// TODO: Extract to a .client file if migrated to Remix. | |
const extractNotionTocHtml = (renderedTocRecordMapElement) => { | |
// Render the RecordMap element to HTML string | |
const renderedTocRecordMapStr = ReactDOMServer.renderToStaticMarkup( | |
renderedTocRecordMapElement | |
); | |
// Parse the HTML string so we can get the specific elements using a className selector | |
const domParser = new DOMParser(); | |
const parsedRenderedTocRecordMap = domParser.parseFromString( | |
renderedTocRecordMapStr, | |
"text/html" | |
); | |
// Find the specific TOC element using a className selector | |
const tocElementsCollection = | |
parsedRenderedTocRecordMap.getElementsByClassName( | |
"notion-table-of-contents" | |
); | |
const tocElement = tocElementsCollection.item(0); | |
return tocElement?.outerHTML; | |
}; | |
export { | |
fetchNotionPageRecordMapByPageId, | |
fetchNotionPageRecordMap, | |
fetchNotionPageFullAndTocOmittedRecordMaps, | |
extractNotionTocHtml, | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
To be used in conjunction with the cf-notion-cache worker at: https://github.com/edlaver/cf-notion-cache