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 (
+
+ )
+}
+
+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
+ return
}
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?.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 */}
+
)
}
+async function Bookmark({
+ link,
+}: {
+ link: {
+ id: string
+ type: 'bookmark'
+ href: string
+ title: string
+ }
+}) {
+ const data = await getBlockRecordMap(link.id)
+
+ return (
+
+
+ {data?.cover && (
+
+ )}
+
+ {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: [],
-};
+}