From c43051cd31e697572f5642c0261e3c5560b52c2a Mon Sep 17 00:00:00 2001 From: Alexey Tukachev Date: Thu, 26 Jan 2023 13:48:27 +0300 Subject: [PATCH] done --- src/app/icons.tsx | 61 ++++++++++++++++++++++++++ src/app/image.tsx | 2 +- src/app/layout.tsx | 13 ++++-- src/app/page.tsx | 104 ++++++++++++++++++++++++++++++++++++-------- src/utils/data.ts | 99 ++++++++++++++++++++++++++++++++++++++--- tailwind.config.cjs | 15 +++++-- 6 files changed, 263 insertions(+), 31 deletions(-) create mode 100644 src/app/icons.tsx diff --git a/src/app/icons.tsx b/src/app/icons.tsx new file mode 100644 index 0000000..7082026 --- /dev/null +++ b/src/app/icons.tsx @@ -0,0 +1,61 @@ +export type IconName = keyof typeof ICONS + +export function Icons({ name }: { name: IconName }) { + return ( + + {ICONS[name]} + + ) +} + +const ICONS = { + behance: ( + + ), + discord: ( + + ), + dribbble: ( + + ), + email: , + facebook: , + github: ( + + ), + instagram: ( + + ), + linkedin: ( + + ), + mastodon: ( + + ), + pinterest: ( + + ), + telegram: , + tiktok: , + twitch: ( + + ), + twitter: ( + + ), + whatsapp: ( + + ), + youtube: , + chevron: , +} as const diff --git a/src/app/image.tsx b/src/app/image.tsx index 415d7d6..f6ccc7d 100644 --- a/src/app/image.tsx +++ b/src/app/image.tsx @@ -30,5 +30,5 @@ export function Image({ ) } // eslint-disable-next-line @next/next/no-img-element - return {alt} + return {alt} } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index bfbfd71..9efcaf7 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,10 +1,15 @@ -import './globals.css'; +import './globals.css' export default function RootLayout({ children }: { children: React.ReactNode }) { return ( - {children} + +
+
+
+ {children} + - ); -} \ No newline at end of file + ) +} diff --git a/src/app/page.tsx b/src/app/page.tsx index d199916..12bd549 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,39 +1,46 @@ -import { getBlocktData, getData, getSocialsData } from '~/utils/data' +import { Suspense } from 'react' +import { getBlockRecordMap, getBlocktData, getData, getSocialsData } from '~/utils/data' import { ClientComponent } from './client' +import { type IconName, Icons } from './icons' import { Image } from './image' export default async function Page() { const data = await getData() return ( -
+
-
+
{data?.name

{data?.name}

{data?.info &&

{data.info}

} -
+
{data?.links?.map((link) => { switch (link.type) { - case 'bookmark': - return ( - - ) + case 'bookmark': { + // @ts-expect-error RSC + return + } case 'toggle': return ( -
- {link.title} -
+
+ +

{link.title}

+
+
+ +
+
+
+
- {/* @ts-expect-error RSC */} - + + {/* @ts-expect-error RSC */} + +
@@ -45,10 +52,52 @@ export default async function Page() {
{/* @ts-expect-error RSC */} +
+ made with 🤍 by{' '} + + accio + +
) } +async function Bookmark({ + link, +}: { + link: { + id: string + type: 'bookmark' + href: string + title: string + } +}) { + const data = await getBlockRecordMap(link.id) + + return ( + +
+ {data?.cover && ( + {link.title} + )} +
+

{data?.title || link.title || link.href}

+
+ ) +} + async function BlockContent({ id }: { id: string }) { const data = await getBlocktData(id) return ( @@ -84,7 +133,26 @@ async function BlockContent({ id }: { id: string }) { async function Socials({ id }: { id: string | undefined }) { const data = await getSocialsData(id) - return <>{data &&
{JSON.stringify(data, null, 2)}
} + return ( +
+ {data?.map((item) => { + return ( + +
+ +
+
+ ) + })} +
+ ) } export const runtime = 'experimental-edge' diff --git a/src/utils/data.ts b/src/utils/data.ts index e64a532..b7f9ae6 100644 --- a/src/utils/data.ts +++ b/src/utils/data.ts @@ -34,7 +34,7 @@ export async function getData() { const content = getBlockContent(block, block.type) if (!content?.url) return [] return { - id: idFromUUID(block.id), + id: block.id, type: block.type, href: content.url, title: richTextToPlainText(content.caption), @@ -43,7 +43,7 @@ export async function getData() { if (block.type === 'toggle' && block.has_children) { const content = getBlockContent(block, block.type) return { - id: idFromUUID(block.id), + id: block.id, type: block.type, title: richTextToPlainText(content?.rich_text), } @@ -60,7 +60,7 @@ export async function getBlocktData(id: string) { if (block.type === 'paragraph') { const content = getBlockContent(block, block.type) return { - id: idFromUUID(block.id), + id: block.id, type: block.type, text: richTextToPlainText(content?.rich_text), } @@ -69,7 +69,7 @@ export async function getBlocktData(id: string) { const content = getBlockContent(block, block.type) if (content?.type !== 'external') return [] return { - id: idFromUUID(block.id), + id: block.id, type: block.type, url: content.external.url, } @@ -84,7 +84,7 @@ export async function getSocialsData(id: string | undefined) { if (!isFullPage(item)) return [] if (item.object === 'page') { return { - id: idFromUUID(item.id), + id: item.id, title: richTextToPlainText(getProperty(item.properties, 'title', 'title')), media: getProperty(item.properties, 'media', 'select')?.name ?? null, url: getProperty(item.properties, 'link', 'url') ?? null, @@ -94,6 +94,8 @@ export async function getSocialsData(id: string | undefined) { }) } +// #region api + export async function getPage(id: string | undefined) { const page = await notion.pages.retrieve({ page_id: idFromUUID(id), @@ -117,6 +119,48 @@ export async function getDatabase(id: string | undefined) { return database } +export async function getBlockRecordMap(id: string | undefined) { + if (!id) return null + + try { + const data = await fetch('https://www.notion.so/api/v3/syncRecordValues', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Cookie: `token_v2=${env.NOTION_TOKEN}`, + }, + body: JSON.stringify({ + requests: [ + { + pointer: { + table: 'block', + id, + }, + version: 1, + }, + ], + }), + }) + const json = (await data.json()) as { recordMap: RecordMap } + const recordMap = json.recordMap + const properties = recordMap.block?.[id]?.value?.properties + const format = recordMap.block?.[id]?.value?.format + return { + id, + title: properties?.title?.[0]?.[0] ?? null, + description: properties?.description?.[0]?.[0] ?? null, + icon: format?.bookmark_icon ?? null, + cover: format?.bookmark_cover ?? null, + } + } catch (error) { + console.error(error) + } +} + +// #endregion + +// #region helpers + export function idFromUUID(id: string | null | undefined): string { return id?.replace(/-/g, '') ?? '' } @@ -164,3 +208,48 @@ type Block = PartialBlockObjectResponse | BlockObjectResponse type Properties = PageObjectResponse['properties'] type Property = NonNullable type File = Extract['files'][number] + +export interface RecordMap { + block: TBlock +} + +export interface TBlock { + [key: string]: TEditor +} + +export interface TEditor { + role: string + value: Value +} + +export interface Value { + id: string + version: number + type: string + properties: TProperties + format: Format + created_time: number + last_edited_time: number + parent_id: string + parent_table: string + alive: boolean + created_by_table: string + created_by_id: string + last_edited_by_table: string + last_edited_by_id: string + space_id: string +} + +export interface TProperties { + link: string[][] + title: string[][] + caption: string[][] + description: string[][] +} + +export interface Format { + bookmark_icon: string + bookmark_cover: string +} + +// #endregion diff --git a/tailwind.config.cjs b/tailwind.config.cjs index 54331dc..c55e00e 100644 --- a/tailwind.config.cjs +++ b/tailwind.config.cjs @@ -1,8 +1,17 @@ /** @type {import('tailwindcss').Config} */ module.exports = { - content: ["./src/**/*.{js,ts,jsx,tsx}"], + content: ['./src/**/*.{js,ts,jsx,tsx}'], theme: { - extend: {}, + extend: { + animation: { + rotation: 'rotation 30s linear infinite', + }, + keyframes: { + rotation: { + to: { transform: 'rotate(360deg)' }, + }, + }, + }, }, plugins: [], -}; +}