This commit is contained in:
tangly1024
2021-09-27 09:33:21 +08:00
parent 22ca7f6d63
commit dfc0f645d4
76 changed files with 3650 additions and 2 deletions

View File

@@ -0,0 +1,20 @@
import { idToUuid } from 'notion-utils'
export default function getAllPageIds (collectionQuery, viewId) {
const views = Object.values(collectionQuery)[0]
if (!views) {
return []
}
let pageIds = []
if (viewId) {
const vId = idToUuid(viewId)
pageIds = views[vId]?.blockIds
} else {
const pageSet = new Set()
Object.values(views).forEach(view => {
view?.blockIds?.forEach(id => pageSet.add(id))
})
pageIds = [...pageSet]
}
return pageIds
}

101
lib/notion/getAllPosts.js Normal file
View File

@@ -0,0 +1,101 @@
import BLOG from '@/blog.config'
import { idToUuid } from 'notion-utils'
import getAllPageIds from './getAllPageIds'
import getPageProperties from './getPageProperties'
import { defaultMapImageUrl } from 'react-notion-x'
import { getDataFromCache, setDataToCache } from '@/lib/cache/cache_manager'
import { getPostBlocks } from '@/lib/notion/getPostBlocks'
export async function getAllPosts () {
const data = await getDataFromCache('posts_list')
if (data) {
return data
}
let id = BLOG.notionPageId
const pageRecordMap = await getPostBlocks(id)
if (!pageRecordMap) {
return <>获取数据异常</>
}
id = idToUuid(id)
const collection = Object.values(pageRecordMap.collection)[0]?.value
const collectionQuery = pageRecordMap.collection_query
const block = pageRecordMap.block
const schema = collection?.schema
const rawMetadata = block[id].value
// Check Type 兼容Page-Database和Inline-Database
if (rawMetadata?.type !== 'collection_view_page' && rawMetadata?.type !== 'collection_view') {
console.warn(`pageId "${id}" is not a database`)
return null
} else {
// Construct Data
const pageIds = getAllPageIds(collectionQuery)
const data = []
for (let i = 0; i < pageIds.length; i++) {
const id = pageIds[i]
const properties = (await getPageProperties(id, block, schema)) || null
// Add fullwidth, createdtime to properties
properties.createdTime = new Date(
block[id].value?.created_time
).toString()
properties.fullWidth = block[id].value?.format?.page_full_width ?? false
properties.page_cover = getPostCover(id, block, pageRecordMap) ?? getContentFirstImage(id, block, pageRecordMap)
properties.content = block[id].value?.content ?? []
data.push(properties)
}
// remove all the the items doesn't meet requirements
const posts = data.filter(post => {
return (
post.title &&
post.slug &&
post?.status?.[0] === 'Published' &&
(post?.type?.[0] === 'Post' || post?.type?.[0] === 'Page')
)
})
// Sort by date
if (BLOG.sortByDate) {
posts.sort((a, b) => {
const dateA = new Date(a?.date?.start_date || a.createdTime)
const dateB = new Date(b?.date?.start_date || b.createdTime)
return dateB - dateA
})
}
if (posts) {
await setDataToCache('posts_list', posts)
}
return posts
}
}
// 从Block获取封面图;优先取PageCover否则取内容图片
function getPostCover (id, block, pageRecordMap) {
const pageCover = block[id].value?.format?.page_cover
if (pageCover) {
if (pageCover.startsWith('/')) return 'https://www.notion.so' + pageCover
if (pageCover.startsWith('http')) return defaultMapImageUrl(pageCover, block[id].value)
}
}
// 取文章的第一个图片内容作为封面
function getContentFirstImage (id, block, pageRecordMap) {
const pageBlock = block[id]?.value
const contentBlockId = pageBlock?.content?.find((blockId) => {
const block = pageRecordMap.block[blockId]?.value
if (block?.type === 'image') {
return true
}
})
if (contentBlockId) {
const contentBlock = pageRecordMap.block[contentBlockId]?.value
const source = contentBlock.properties?.source?.[0]?.[0] ??
contentBlock.format?.display_source
return defaultMapImageUrl(source, contentBlock)
}
return ''
}

23
lib/notion/getAllTags.js Normal file
View File

@@ -0,0 +1,23 @@
import { getAllPosts } from './getAllPosts'
export async function getAllTags (posts) {
if (!posts) {
const response = await getAllPosts()
posts = response.filter(
post =>
post.status[0] === 'Published' && post.type[0] === 'Post' && post.tags
)
}
let tags = posts.map(p => p.tags)
tags = [...tags.flat()]
const tagObj = {}
tags.forEach(tag => {
if (tag in tagObj) {
tagObj[tag]++
} else {
tagObj[tag] = 1
}
})
return tagObj
}

11
lib/notion/getMetadata.js Normal file
View File

@@ -0,0 +1,11 @@
export default function getMetadata(rawMetadata) {
const metadata = {
locked: rawMetadata?.format?.block_locked,
page_full_width: rawMetadata?.format?.page_full_width,
page_font: rawMetadata?.format?.page_font,
page_small_text: rawMetadata?.format?.page_small_text,
created_time: rawMetadata.created_time,
last_edited_time: rawMetadata.last_edited_time
}
return metadata
}

View File

@@ -0,0 +1,60 @@
import { getTextContent, getDateValue } from 'notion-utils'
import { NotionAPI } from 'notion-client'
async function getPageProperties (id, block, schema, authToken) {
const rawProperties = Object.entries(block?.[id]?.value?.properties || [])
const excludeProperties = ['date', 'select', 'multi_select', 'person']
const properties = {}
for (let i = 0; i < rawProperties.length; i++) {
const [key, val] = rawProperties[i]
properties.id = id
if (schema[key]?.type && !excludeProperties.includes(schema[key].type)) {
properties[schema[key].name] = getTextContent(val)
} else {
switch (schema[key]?.type) {
case 'date': {
const dateProperty = getDateValue(val)
delete dateProperty.type
properties[schema[key].name] = dateProperty
break
}
case 'select':
case 'multi_select': {
const selects = getTextContent(val)
if (selects[0]?.length) {
properties[schema[key].name] = selects.split(',')
}
break
}
case 'person': {
const rawUsers = val.flat()
const users = []
const api = new NotionAPI({ authToken })
for (let i = 0; i < rawUsers.length; i++) {
if (rawUsers[i][0][1]) {
const userId = rawUsers[i][0]
const res = await api.getUsers(userId)
const resValue =
res?.recordMapWithRoles?.notion_user?.[userId[1]]?.value
const user = {
id: resValue?.id,
first_name: resValue?.given_name,
last_name: resValue?.family_name,
profile_photo: resValue?.profile_photo
}
users.push(user)
}
}
properties[schema[key].name] = users
break
}
default:
break
}
}
}
return properties
}
export { getPageProperties as default }

View File

@@ -0,0 +1,69 @@
const indentLevels = {
header: 0,
sub_header: 1,
sub_sub_header: 2
}
export const getPageTableOfContents = (page,recordMap)=> {
// 获取 header sub_header sub_sub_header
const toc = (page.content ?? [])
.map((blockId) => {
const block = recordMap.block[blockId]?.value
if (block) {
const { type } = block
if (
type === 'header' ||
type === 'sub_header' ||
type === 'sub_sub_header'
) {
return {
id: blockId,
type,
indentLevel: indentLevels[type]
}
}
}
return null
})
.filter(Boolean)
const indentLevelStack = [
{
actual: -1,
effective: -1
}
]
// Adjust indent levels to always change smoothly.
// This is a little tricky, but the key is that when increasing indent levels,
// they should never jump more than one at a time.
for (const tocItem of toc) {
const { indentLevel } = tocItem
const actual = indentLevel
do {
const prevIndent = indentLevelStack[indentLevelStack.length - 1]
const { actual: prevActual, effective: prevEffective } = prevIndent
if (actual > prevActual) {
tocItem.indentLevel = prevEffective + 1
indentLevelStack.push({
actual,
effective: tocItem.indentLevel
})
} else if (actual === prevActual) {
tocItem.indentLevel = prevEffective
break
} else {
indentLevelStack.pop()
}
} while (true)
}
return toc
}

View File

@@ -0,0 +1,17 @@
import BLOG from '@/blog.config'
import { NotionAPI } from 'notion-client'
import { getDataFromCache, setDataToCache } from '@/lib/cache/cache_manager'
export async function getPostBlocks (id) {
let pageBlock = await getDataFromCache('page_block_' + id)
if (pageBlock) {
return pageBlock
}
const authToken = BLOG.notionAccessToken || null
const api = new NotionAPI({ authToken })
pageBlock = await api.getPage(id)
if (pageBlock) {
await setDataToCache('page_block_' + id, pageBlock)
}
return pageBlock
}