import { asyncComponent, mediaMapInterface } from "../../types/types";
import { isFullBlock } from "@notionhq/client";
import {
BlockObjectResponse,
PartialBlockObjectResponse,
} from "@notionhq/client/build/src/api-endpoints";
import Image from "next/image";
import { RichText } from "./RichText";
import { getBlocks } from "../../index";
import React from "react";
import { Code } from "./Code";
import { Table } from "./Table";
/**
* A recursive component that renders a Notion block and child blocks.
* @param mediaMap is an object that notion-on-next generates for you. Find it in public/notion-media/media-map.json.
*/
export const Block = asyncComponent(
async ({
block,
blocks,
mediaMap,
databaseId,
pageId,
}: {
block: BlockObjectResponse | PartialBlockObjectResponse;
blocks: (BlockObjectResponse | PartialBlockObjectResponse)[];
mediaMap?: mediaMapInterface;
databaseId?: string;
pageId?: string;
}) => {
if (!isFullBlock(block)) {
return <>>;
}
let children: React.ReactNode[] | undefined;
// Table blocks are handled a bit differently. See Table.tsx
if (block.has_children && block.type !== "table") {
const childBlocks = await getBlocks(block.id);
children = childBlocks?.map(
(child: BlockObjectResponse | PartialBlockObjectResponse) => {
if (child) {
return (
);
} else {
// Prevents undefined block error
return <>>;
}
}
);
}
// Add support for any block type here. You can add custom styling wherever you'd like.
switch (block.type) {
case "heading_1":
//@ts-ignore Notion types are incorrect
if (block.heading_1.is_toggleable) {
return (
{children}
);
}
return (
);
case "heading_2":
//@ts-ignore Notion types are incorrect
if (block.heading_2.is_toggleable) {
return (
<>
{children}
>
);
}
return (
);
case "heading_3":
//@ts-ignore Notion types are incorrect
if (block.heading_3.is_toggleable) {
return (
<>
{children}
>
);
}
return (
);
case "paragraph":
return (
);
case "image":
// If Media map does not exist, use the external url or file url from Notion. Be aware that these links expire after 1 hr. https://developers.notion.com/docs/working-with-files-and-media
const imageUrl: string =
databaseId && pageId && mediaMap
? (mediaMap[databaseId][pageId][block.id] as string)
: block.image.type == "external"
? block.image.external.url
: block.image.file.url;
return (
{block.image.caption && (
)}
);
case "video":
// If Media map does not exist, use the external url or file url from Notion. Be aware that these links expire after 1 hr. https://developers.notion.com/docs/working-with-files-and-media
const videoUrl: string =
databaseId && pageId && mediaMap
? (mediaMap[databaseId][pageId][block.id] as string)
: block.video.type == "external"
? block.video.external.url
: block.video.file.url;
if (videoUrl) {
return (
{block.video.caption && (
)}
);
} else {
return
Video URL not found
;
}
case "bulleted_list_item":
return (
{children}
);
case "numbered_list_item":
const itemPosition = blocks.findIndex(
(blocksBlock) => block.id === blocksBlock.id
);
// Count backwards to find the number of numbered_list_item blocks before hitting a non-numbered_list_item block
// Notions API does not give any information about the position of the block in the list so we need to calculate it
let listNumber = 0;
for (let i = itemPosition; i >= 0; i--) {
let blocksBlock = blocks[i] as BlockObjectResponse;
if (blocksBlock.type === "numbered_list_item") {
listNumber++;
} else {
break;
}
}
return (