mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-06-04 07:26:47 +00:00
Merge branch 'main' into feature_facebook_messenger_customer_chat_plugin
This commit is contained in:
@@ -9,8 +9,10 @@ const BLOG = {
|
|||||||
process.env.NOTION_PAGE_ID || '02ab3b8678004aa69e9e415905ef32a5', // Important page_id!!!Duplicate Template from https://www.notion.so/tanghh/02ab3b8678004aa69e9e415905ef32a5
|
process.env.NOTION_PAGE_ID || '02ab3b8678004aa69e9e415905ef32a5', // Important page_id!!!Duplicate Template from https://www.notion.so/tanghh/02ab3b8678004aa69e9e415905ef32a5
|
||||||
NOTION_ACCESS_TOKEN: process.env.NOTION_ACCESS_TOKEN || '', // Useful if you prefer not to make your database public
|
NOTION_ACCESS_TOKEN: process.env.NOTION_ACCESS_TOKEN || '', // Useful if you prefer not to make your database public
|
||||||
DEBUG: process.env.NEXT_PUBLIC_DEBUG || false, // 是否显示调试按钮
|
DEBUG: process.env.NEXT_PUBLIC_DEBUG || false, // 是否显示调试按钮
|
||||||
|
|
||||||
FACEBOOK_PAGE_ID: process.env.NEXT_PUBLIC_FACEBOOK_PAGE_ID || '', //Facebook Page ID 來啟用 messenger 聊天功能
|
FACEBOOK_PAGE_ID: process.env.NEXT_PUBLIC_FACEBOOK_PAGE_ID || '', //Facebook Page ID 來啟用 messenger 聊天功能
|
||||||
FACEBOOK_APP_ID: process.env.NEXT_PUBLIC_FACEBOOK_APP_ID || '', //Facebook App ID 來啟用 messenger 聊天功能
|
FACEBOOK_APP_ID: process.env.NEXT_PUBLIC_FACEBOOK_APP_ID || '', //Facebook App ID 來啟用 messenger 聊天功能
|
||||||
|
FACEBOOK_PAGE: process.env.NEXT_PUBLIC_FACEBOOK_PAGE || 'https://www.facebook.com/tw.andys.pro', // Facebook Page 的連結
|
||||||
|
|
||||||
THEME: process.env.NEXT_PUBLIC_THEME || 'next', // 主题, 支持 ['next','hexo',"fukasawa','medium']
|
THEME: process.env.NEXT_PUBLIC_THEME || 'next', // 主题, 支持 ['next','hexo',"fukasawa','medium']
|
||||||
THEME_SWITCH: process.env.NEXT_PUBLIC_THEME_SWITCH || false, // 是否显示切换主题按钮
|
THEME_SWITCH: process.env.NEXT_PUBLIC_THEME_SWITCH || false, // 是否显示切换主题按钮
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ const CommonHead = ({ meta, children }) => {
|
|||||||
const description = meta?.description || BLOG.DESCRIPTION
|
const description = meta?.description || BLOG.DESCRIPTION
|
||||||
const type = meta?.type || 'website'
|
const type = meta?.type || 'website'
|
||||||
const keywords = meta?.tags || BLOG.KEYWORDS
|
const keywords = meta?.tags || BLOG.KEYWORDS
|
||||||
|
const lang = BLOG.LANG.replace('-', '_') // Facebook OpenGraph 要 zh_CN 這樣的格式才抓得到語言
|
||||||
|
const category = meta?.category || BLOG.KEYWORDS || '軟體科技' // section 主要是像是 category 這樣的分類,Facebook 用這個來抓連結的分類
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Head>
|
<Head>
|
||||||
@@ -31,11 +33,12 @@ const CommonHead = ({ meta, children }) => {
|
|||||||
)}
|
)}
|
||||||
<meta name="keywords" content={keywords} />
|
<meta name="keywords" content={keywords} />
|
||||||
<meta name="description" content={description} />
|
<meta name="description" content={description} />
|
||||||
<meta property="og:locale" content={BLOG.LANG} />
|
<meta property="og:locale" content={lang} />
|
||||||
<meta property="og:title" content={title} />
|
<meta property="og:title" content={title} />
|
||||||
<meta property="og:description" content={description} />
|
<meta property="og:description" content={description} />
|
||||||
<meta property="og:url" content={url} />
|
<meta property="og:url" content={url} />
|
||||||
<meta property="og:image" content={image} />
|
<meta property="og:image" content={image} />
|
||||||
|
<meta property="og:site_name" content={BLOG.TITLE} />
|
||||||
<meta property="og:type" content={type} />
|
<meta property="og:type" content={type} />
|
||||||
<meta name="twitter:card" content="summary_large_image" />
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
<meta name="twitter:description" content={description} />
|
<meta name="twitter:description" content={description} />
|
||||||
@@ -50,6 +53,8 @@ const CommonHead = ({ meta, children }) => {
|
|||||||
content={meta.date || meta.createdTime}
|
content={meta.date || meta.createdTime}
|
||||||
/>
|
/>
|
||||||
<meta property="article:author" content={BLOG.AUTHOR} />
|
<meta property="article:author" content={BLOG.AUTHOR} />
|
||||||
|
<meta property="article:section" content={category} />
|
||||||
|
<meta property="article:publisher" content={BLOG.FACEBOOK_PAGE} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
91
components/NotionPage.js
Normal file
91
components/NotionPage.js
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
|
||||||
|
import 'prismjs'
|
||||||
|
import 'prismjs/components/prism-bash'
|
||||||
|
import 'prismjs/components/prism-javascript'
|
||||||
|
import 'prismjs/components/prism-markup'
|
||||||
|
import 'prismjs/components/prism-python'
|
||||||
|
import 'prismjs/components/prism-typescript'
|
||||||
|
import 'prismjs/components/prism-java'
|
||||||
|
|
||||||
|
import { NotionRenderer } from 'react-notion-x'
|
||||||
|
import mediumZoom from 'medium-zoom'
|
||||||
|
import { useEffect, useRef } from 'react'
|
||||||
|
import dynamic from 'next/dynamic'
|
||||||
|
|
||||||
|
const Code = dynamic(() =>
|
||||||
|
import('react-notion-x/build/third-party/code').then((m) => m.Code)
|
||||||
|
)
|
||||||
|
const Collection = dynamic(() =>
|
||||||
|
import('react-notion-x/build/third-party/collection').then(
|
||||||
|
(m) => m.Collection
|
||||||
|
)
|
||||||
|
)
|
||||||
|
const Equation = dynamic(() =>
|
||||||
|
import('react-notion-x/build/third-party/equation').then((m) => m.Equation)
|
||||||
|
)
|
||||||
|
const Pdf = dynamic(
|
||||||
|
() => import('react-notion-x/build/third-party/pdf').then((m) => m.Pdf),
|
||||||
|
{
|
||||||
|
ssr: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const Modal = dynamic(
|
||||||
|
() => import('react-notion-x/build/third-party/modal').then((m) => m.Modal),
|
||||||
|
{
|
||||||
|
ssr: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const NotionPage = ({ post }) => {
|
||||||
|
const zoom = typeof window !== 'undefined' && mediumZoom({
|
||||||
|
container: '.notion-viewport',
|
||||||
|
background: 'rgba(0, 0, 0, 0.2)',
|
||||||
|
margin: getMediumZoomMargin()
|
||||||
|
})
|
||||||
|
const zoomRef = useRef(zoom ? zoom.clone() : null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// 将所有container下的所有图片添加medium-zoom
|
||||||
|
const container = document?.getElementById('container')
|
||||||
|
const imgList = container?.getElementsByTagName('img')
|
||||||
|
if (imgList && zoomRef.current) {
|
||||||
|
for (let i = 0; i < imgList.length; i++) {
|
||||||
|
(zoomRef.current).attach(imgList[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return <NotionRenderer
|
||||||
|
recordMap={post.blockMap}
|
||||||
|
mapPageUrl={mapPageUrl}
|
||||||
|
components={{
|
||||||
|
Code,
|
||||||
|
Collection,
|
||||||
|
Equation,
|
||||||
|
Modal,
|
||||||
|
Pdf
|
||||||
|
}} />
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapPageUrl = id => {
|
||||||
|
return 'https://www.notion.so/' + id.replace(/-/g, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMediumZoomMargin() {
|
||||||
|
const width = window.innerWidth
|
||||||
|
|
||||||
|
if (width < 500) {
|
||||||
|
return 8
|
||||||
|
} else if (width < 800) {
|
||||||
|
return 20
|
||||||
|
} else if (width < 1280) {
|
||||||
|
return 30
|
||||||
|
} else if (width < 1600) {
|
||||||
|
return 40
|
||||||
|
} else if (width < 1920) {
|
||||||
|
return 48
|
||||||
|
} else {
|
||||||
|
return 72
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NotionPage
|
||||||
@@ -32,7 +32,9 @@ export default {
|
|||||||
ARTICLE_DETAIL: 'Article Details',
|
ARTICLE_DETAIL: 'Article Details',
|
||||||
PASSWORD_ERROR: 'Password Error!',
|
PASSWORD_ERROR: 'Password Error!',
|
||||||
ARTICLE_LOCK_TIPS: 'Please Enter the password:',
|
ARTICLE_LOCK_TIPS: 'Please Enter the password:',
|
||||||
SUBMIT: 'Submit'
|
SUBMIT: 'Submit',
|
||||||
|
POST_TIME: 'Post on',
|
||||||
|
LAST_EDITED_TIME: 'Last edited'
|
||||||
},
|
},
|
||||||
PAGINATION: {
|
PAGINATION: {
|
||||||
PREV: 'Prev',
|
PREV: 'Prev',
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ export default {
|
|||||||
ARTICLE_DETAIL: '文章详情',
|
ARTICLE_DETAIL: '文章详情',
|
||||||
PASSWORD_ERROR: '密码错误!',
|
PASSWORD_ERROR: '密码错误!',
|
||||||
ARTICLE_LOCK_TIPS: '文章已上锁,请输入访问密码',
|
ARTICLE_LOCK_TIPS: '文章已上锁,请输入访问密码',
|
||||||
SUBMIT: '提交'
|
SUBMIT: '提交',
|
||||||
|
POST_TIME: '发布于',
|
||||||
|
LAST_EDITED_TIME: '最后更新'
|
||||||
},
|
},
|
||||||
PAGINATION: {
|
PAGINATION: {
|
||||||
PREV: '上一页',
|
PREV: '上一页',
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ export default {
|
|||||||
ARTICLE_DETAIL: '完整文章',
|
ARTICLE_DETAIL: '完整文章',
|
||||||
PASSWORD_ERROR: '密碼錯誤!',
|
PASSWORD_ERROR: '密碼錯誤!',
|
||||||
ARTICLE_LOCK_TIPS: '文章已上鎖,請輸入訪問密碼',
|
ARTICLE_LOCK_TIPS: '文章已上鎖,請輸入訪問密碼',
|
||||||
SUBMIT: '提交'
|
SUBMIT: '提交',
|
||||||
|
POST_TIME: '发布于',
|
||||||
|
LAST_EDITED_TIME: '最后更新'
|
||||||
},
|
},
|
||||||
PAGINATION: {
|
PAGINATION: {
|
||||||
PREV: '上一頁',
|
PREV: '上一頁',
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import BLOG from '@/blog.config'
|
|||||||
import getAllPageIds from './getAllPageIds'
|
import getAllPageIds from './getAllPageIds'
|
||||||
import getPageProperties from './getPageProperties'
|
import getPageProperties from './getPageProperties'
|
||||||
import { defaultMapImageUrl } from 'react-notion-x'
|
import { defaultMapImageUrl } from 'react-notion-x'
|
||||||
|
import formatDate from '@/lib/formatDate'
|
||||||
import { getNotionPageData } from '@/lib/notion/getNotionData'
|
import { getNotionPageData } from '@/lib/notion/getNotionData'
|
||||||
import { delCacheData } from '@/lib/cache/cache_manager'
|
import { delCacheData } from '@/lib/cache/cache_manager'
|
||||||
|
|
||||||
@@ -12,7 +13,7 @@ import { delCacheData } from '@/lib/cache/cache_manager'
|
|||||||
* @param pageType 页面类型数组 ['Post','Page']
|
* @param pageType 页面类型数组 ['Post','Page']
|
||||||
* @returns {Promise<*[]>}
|
* @returns {Promise<*[]>}
|
||||||
*/
|
*/
|
||||||
export async function getAllPosts ({ notionPageData, from, pageType }) {
|
export async function getAllPosts({ notionPageData, from, pageType }) {
|
||||||
if (!notionPageData) {
|
if (!notionPageData) {
|
||||||
notionPageData = await getNotionPageData({ from })
|
notionPageData = await getNotionPageData({ from })
|
||||||
}
|
}
|
||||||
@@ -31,8 +32,8 @@ export async function getAllPosts ({ notionPageData, from, pageType }) {
|
|||||||
const id = pageIds[i]
|
const id = pageIds[i]
|
||||||
const properties = (await getPageProperties(id, pageBlock, schema)) || null
|
const properties = (await getPageProperties(id, pageBlock, schema)) || null
|
||||||
properties.slug = properties.slug ?? properties.id
|
properties.slug = properties.slug ?? properties.id
|
||||||
properties.createdTime = new Date(pageBlock[id].value?.created_time).toString() // FIXME 似乎没有created_time 字段了
|
properties.createdTime = formatDate(new Date(pageBlock[id].value?.created_time).toString(), BLOG.LANG)
|
||||||
properties.lastEditedTime = new Date(pageBlock[id].value?.last_edited_time).toString() // FIXME 似乎没有created_time 字段了
|
properties.lastEditedTime = formatDate(new Date(pageBlock[id].value?.last_edited_time).toString(), BLOG.LANG)
|
||||||
properties.fullWidth = pageBlock[id].value?.format?.page_full_width ?? false
|
properties.fullWidth = pageBlock[id].value?.format?.page_full_width ?? false
|
||||||
properties.page_cover = getPostCover(id, pageBlock) ?? null
|
properties.page_cover = getPostCover(id, pageBlock) ?? null
|
||||||
properties.content = pageBlock[id].value?.content ?? []
|
properties.content = pageBlock[id].value?.content ?? []
|
||||||
@@ -65,7 +66,7 @@ export async function getAllPosts ({ notionPageData, from, pageType }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 从Block获取封面图;优先取PageCover,否则取内容图片
|
// 从Block获取封面图;优先取PageCover,否则取内容图片
|
||||||
function getPostCover (id, block) {
|
function getPostCover(id, block) {
|
||||||
const pageCover = block[id].value?.format?.page_cover
|
const pageCover = block[id].value?.format?.page_cover
|
||||||
if (pageCover) {
|
if (pageCover) {
|
||||||
if (pageCover.startsWith('/')) return 'https://www.notion.so' + pageCover
|
if (pageCover.startsWith('/')) return 'https://www.notion.so' + pageCover
|
||||||
@@ -78,7 +79,7 @@ function getPostCover (id, block) {
|
|||||||
* @param {*} param0
|
* @param {*} param0
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export async function getAllPostCount ({ notionPageData, from }) {
|
export async function getAllPostCount({ notionPageData, from }) {
|
||||||
if (!notionPageData) {
|
if (!notionPageData) {
|
||||||
notionPageData = await getNotionPageData({ from })
|
notionPageData = await getNotionPageData({ from })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { getTextContent, getDateValue } from 'notion-utils'
|
import { getTextContent, getDateValue } from 'notion-utils'
|
||||||
import { NotionAPI } from 'notion-client'
|
import { NotionAPI } from 'notion-client'
|
||||||
|
|
||||||
async function getPageProperties (id, block, schema, authToken) {
|
async function getPageProperties(id, block, schema, authToken) {
|
||||||
const rawProperties = Object.entries(block?.[id]?.value?.properties || [])
|
const rawProperties = Object.entries(block?.[id]?.value?.properties || [])
|
||||||
const excludeProperties = ['date', 'select', 'multi_select', 'person']
|
const excludeProperties = ['date', 'select', 'multi_select', 'person']
|
||||||
const properties = {}
|
const properties = {}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import BLOG from '@/blog.config'
|
|||||||
import { NotionAPI } from 'notion-client'
|
import { NotionAPI } from 'notion-client'
|
||||||
import { getDataFromCache, setDataToCache } from '@/lib/cache/cache_manager'
|
import { getDataFromCache, setDataToCache } from '@/lib/cache/cache_manager'
|
||||||
|
|
||||||
export async function getPostBlocks (id, from, slice, retryCount = 3) {
|
export async function getPostBlocks(id, from, slice, retryCount = 3) {
|
||||||
const cacheKey = 'page_block_' + id
|
const cacheKey = 'page_block_' + id
|
||||||
let pageBlock = await getDataFromCache(cacheKey)
|
let pageBlock = await getDataFromCache(cacheKey)
|
||||||
if (pageBlock) {
|
if (pageBlock) {
|
||||||
@@ -32,13 +32,13 @@ export async function getPostBlocks (id, from, slice, retryCount = 3) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* 获取到的blockMap删除不需要的字段
|
||||||
* @param {*} id 页面ID
|
* @param {*} id 页面ID
|
||||||
* @param {*} pageBlock 页面元素
|
* @param {*} pageBlock 页面元素
|
||||||
* @param {*} slice 截取数量
|
* @param {*} slice 截取数量
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function filterPostBlocks (id, pageBlock, slice) {
|
function filterPostBlocks(id, pageBlock, slice) {
|
||||||
const clonePageBlock = deepClone(pageBlock)
|
const clonePageBlock = deepClone(pageBlock)
|
||||||
let count = 0
|
let count = 0
|
||||||
|
|
||||||
@@ -51,8 +51,6 @@ function filterPostBlocks (id, pageBlock, slice) {
|
|||||||
count++
|
count++
|
||||||
delete b?.role
|
delete b?.role
|
||||||
delete b?.value?.version
|
delete b?.value?.version
|
||||||
delete b?.value?.created_time
|
|
||||||
delete b?.value?.last_edited_time
|
|
||||||
delete b?.value?.created_by_table
|
delete b?.value?.created_by_table
|
||||||
delete b?.value?.created_by_id
|
delete b?.value?.created_by_id
|
||||||
delete b?.value?.last_edited_by_table
|
delete b?.value?.last_edited_by_table
|
||||||
@@ -67,7 +65,7 @@ function filterPostBlocks (id, pageBlock, slice) {
|
|||||||
return clonePageBlock
|
return clonePageBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
function deepClone (obj) {
|
function deepClone(obj) {
|
||||||
const newObj = Array.isArray(obj) ? [] : {}
|
const newObj = Array.isArray(obj) ? [] : {}
|
||||||
if (obj && typeof obj === 'object') {
|
if (obj && typeof obj === 'object') {
|
||||||
for (const key in obj) {
|
for (const key in obj) {
|
||||||
|
|||||||
23
lib/rss.js
23
lib/rss.js
@@ -1,16 +1,8 @@
|
|||||||
import { Feed } from 'feed'
|
import { Feed } from 'feed'
|
||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
import ReactDOMServer from 'react-dom/server'
|
import ReactDOMServer from 'react-dom/server'
|
||||||
import {
|
|
||||||
NotionRenderer,
|
|
||||||
Equation,
|
|
||||||
Code,
|
|
||||||
Collection,
|
|
||||||
CollectionRow
|
|
||||||
} from 'react-notion-x'
|
|
||||||
import { getPostBlocks } from './notion'
|
import { getPostBlocks } from './notion'
|
||||||
|
import NotionPage from '@/components/NotionPage'
|
||||||
const mapPageUrl = id => 'https://www.notion.so/' + id.replace(/-/g, '')
|
|
||||||
|
|
||||||
const createFeedContent = async post => {
|
const createFeedContent = async post => {
|
||||||
// 加密的文章内容只返回摘要
|
// 加密的文章内容只返回摘要
|
||||||
@@ -19,18 +11,7 @@ const createFeedContent = async post => {
|
|||||||
}
|
}
|
||||||
const blockMap = await getPostBlocks(post.id, 'rss-content')
|
const blockMap = await getPostBlocks(post.id, 'rss-content')
|
||||||
if (blockMap) {
|
if (blockMap) {
|
||||||
const content = ReactDOMServer.renderToString(
|
const content = ReactDOMServer.renderToString(<NotionPage post={post} />)
|
||||||
<NotionRenderer
|
|
||||||
recordMap={blockMap}
|
|
||||||
components={{
|
|
||||||
equation: Equation,
|
|
||||||
code: Code,
|
|
||||||
collection: Collection,
|
|
||||||
collectionRow: CollectionRow
|
|
||||||
}}
|
|
||||||
mapPageUrl={mapPageUrl}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
const regexExp =
|
const regexExp =
|
||||||
/<div class="notion-collection-row"><div class="notion-collection-row-body"><div class="notion-collection-row-property"><div class="notion-collection-column-title"><svg.*?class="notion-collection-column-title-icon">.*?<\/svg><div class="notion-collection-column-title-body">.*?<\/div><\/div><div class="notion-collection-row-value">.*?<\/div><\/div><\/div><\/div>/g
|
/<div class="notion-collection-row"><div class="notion-collection-row-body"><div class="notion-collection-row-property"><div class="notion-collection-column-title"><svg.*?class="notion-collection-column-title-icon">.*?<\/svg><div class="notion-collection-column-title-body">.*?<\/div><\/div><div class="notion-collection-row-value">.*?<\/div><\/div><\/div><\/div>/g
|
||||||
return content.replace(regexExp, '')
|
return content.replace(regexExp, '')
|
||||||
|
|||||||
@@ -32,15 +32,15 @@
|
|||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"memory-cache": "^0.2.0",
|
"memory-cache": "^0.2.0",
|
||||||
"next": "^12.0.5",
|
"next": "^12.0.5",
|
||||||
"notion-client": "4.16.0",
|
"notion-client": "6.5.0",
|
||||||
"notion-utils": "4.16.0",
|
"notion-utils": "6.5.0",
|
||||||
"preact": "^10.5.15",
|
"preact": "^10.5.15",
|
||||||
"qrcode.react": "^1.0.1",
|
"qrcode.react": "^1.0.1",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-cookies": "^0.1.1",
|
"react-cookies": "^0.1.1",
|
||||||
"react-cusdis": "^2.1.3",
|
"react-cusdis": "^2.1.3",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "17.0.2",
|
||||||
"react-notion-x": "4.16.0",
|
"react-notion-x": "6.6.2",
|
||||||
"smoothscroll-polyfill": "^0.4.4",
|
"smoothscroll-polyfill": "^0.4.4",
|
||||||
"typed.js": "^2.0.12",
|
"typed.js": "^2.0.12",
|
||||||
"use-ackee": "^3.0.0"
|
"use-ackee": "^3.0.0"
|
||||||
|
|||||||
@@ -7,11 +7,12 @@ import 'react-notion-x/src/styles.css'
|
|||||||
import '@/styles/notion.css' // 重写部分样式
|
import '@/styles/notion.css' // 重写部分样式
|
||||||
|
|
||||||
// used for collection views (optional)
|
// used for collection views (optional)
|
||||||
import 'rc-dropdown/assets/index.css'
|
// import 'rc-dropdown/assets/index.css'
|
||||||
// used for code syntax highlighting (optional)
|
// used for code syntax highlighting (optional)
|
||||||
import 'prismjs/themes/prism-okaidia.css'
|
import 'prismjs/themes/prism-okaidia.css'
|
||||||
// used for rendering equations (optional)
|
// used for rendering equations (optional)
|
||||||
import 'katex/dist/katex.min.css'
|
import 'react-notion-x/build/third-party/equation.css'
|
||||||
|
|
||||||
import dynamic from 'next/dynamic'
|
import dynamic from 'next/dynamic'
|
||||||
import { GlobalContextProvider } from '@/lib/global'
|
import { GlobalContextProvider } from '@/lib/global'
|
||||||
import { DebugPanel } from '@/components/DebugPanel'
|
import { DebugPanel } from '@/components/DebugPanel'
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ const Slug = props => {
|
|||||||
type: 'article',
|
type: 'article',
|
||||||
slug: 'article/' + props.post.slug,
|
slug: 'article/' + props.post.slug,
|
||||||
image: props.post.page_cover,
|
image: props.post.page_cover,
|
||||||
|
category: props.post.category?.[0],
|
||||||
tags: props.post.tags
|
tags: props.post.tags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,10 @@
|
|||||||
import { getPageTableOfContents } from 'notion-utils'
|
import { getPageTableOfContents } from 'notion-utils'
|
||||||
import 'prismjs'
|
|
||||||
import 'prismjs/components/prism-bash'
|
|
||||||
import 'prismjs/components/prism-javascript'
|
|
||||||
import 'prismjs/components/prism-markup'
|
|
||||||
import 'prismjs/components/prism-python'
|
|
||||||
import 'prismjs/components/prism-typescript'
|
|
||||||
import {
|
|
||||||
Code,
|
|
||||||
Collection,
|
|
||||||
CollectionRow,
|
|
||||||
Equation,
|
|
||||||
NotionRenderer
|
|
||||||
} from 'react-notion-x'
|
|
||||||
import LayoutBase from './LayoutBase'
|
import LayoutBase from './LayoutBase'
|
||||||
import { useRef, useEffect } from 'react'
|
|
||||||
import { ArticleLock } from './components/ArticleLock'
|
import { ArticleLock } from './components/ArticleLock'
|
||||||
import mediumZoom from 'medium-zoom'
|
import NotionPage from '@/components/NotionPage'
|
||||||
|
import Link from 'next/link'
|
||||||
const mapPageUrl = id => {
|
import { useGlobal } from '@/lib/global'
|
||||||
return 'https://www.notion.so/' + id.replace(/-/g, '')
|
import formatDate from '@/lib/formatDate'
|
||||||
}
|
|
||||||
|
|
||||||
export const LayoutSlug = props => {
|
export const LayoutSlug = props => {
|
||||||
const { post, lock, validPassword } = props
|
const { post, lock, validPassword } = props
|
||||||
@@ -28,24 +13,9 @@ export const LayoutSlug = props => {
|
|||||||
post.toc = getPageTableOfContents(post, post.blockMap)
|
post.toc = getPageTableOfContents(post, post.blockMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
const zoom =
|
const { locale } = useGlobal()
|
||||||
typeof window !== 'undefined' &&
|
const date = formatDate(post?.date?.start_date || post.createdTime, locale.LOCALE)
|
||||||
mediumZoom({
|
|
||||||
container: '.notion-viewport',
|
|
||||||
background: 'rgba(0, 0, 0, 0.2)',
|
|
||||||
margin: getMediumZoomMargin()
|
|
||||||
})
|
|
||||||
const zoomRef = useRef(zoom ? zoom.clone() : null)
|
|
||||||
useEffect(() => {
|
|
||||||
// 将所有container下的所有图片添加medium-zoom
|
|
||||||
const container = document.getElementById('notion-article')
|
|
||||||
const imgList = container?.getElementsByTagName('img')
|
|
||||||
if (imgList && zoomRef.current) {
|
|
||||||
for (let i = 0; i < imgList.length; i++) {
|
|
||||||
zoomRef.current.attach(imgList[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return (
|
return (
|
||||||
<LayoutBase {...props}>
|
<LayoutBase {...props}>
|
||||||
<div>
|
<div>
|
||||||
@@ -54,39 +24,46 @@ export const LayoutSlug = props => {
|
|||||||
{lock && <ArticleLock password={post.password} validPassword={validPassword} />}
|
{lock && <ArticleLock password={post.password} validPassword={validPassword} />}
|
||||||
|
|
||||||
{!lock && <section id="notion-article" className="px-1">
|
{!lock && <section id="notion-article" className="px-1">
|
||||||
{post.blockMap && (
|
<section className="flex-wrap flex mt-2 text-gray-400 dark:text-gray-400 font-light leading-8">
|
||||||
<NotionRenderer
|
<div>
|
||||||
recordMap={post.blockMap}
|
<Link href={`/category/${post.category}`} passHref>
|
||||||
mapPageUrl={mapPageUrl}
|
<a className="cursor-pointer text-md mr-2 hover:text-black dark:hover:text-white border-b dark:border-gray-500 border-dashed">
|
||||||
components={{
|
<i className="mr-1 fas fa-folder-open" />
|
||||||
equation: Equation,
|
{post.category}
|
||||||
code: Code,
|
</a>
|
||||||
collectionRow: CollectionRow,
|
</Link>
|
||||||
collection: Collection
|
<span className='mr-2'>|</span>
|
||||||
}}
|
|
||||||
/>
|
{post.type[0] !== 'Page' && (<>
|
||||||
)}
|
<Link
|
||||||
</section>}
|
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
||||||
|
passHref
|
||||||
|
>
|
||||||
|
<a className="pl-1 mr-2 cursor-pointer hover:text-gray-700 dark:hover:text-gray-200 border-b dark:border-gray-500 border-dashed">
|
||||||
|
{date}
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
<span className='mr-2'>|</span>
|
||||||
|
<span className='mx-2 text-gray-400 dark:text-gray-500'>
|
||||||
|
{locale.COMMON.LAST_EDITED_TIME}: {post.lastEditedTime}
|
||||||
|
</span>
|
||||||
|
<span className='mr-2'>|</span>
|
||||||
|
|
||||||
|
</>)}
|
||||||
|
|
||||||
|
<span className="hidden busuanzi_container_page_pv font-light mr-2">
|
||||||
|
<i className='mr-1 fas fa-eye' />
|
||||||
|
|
||||||
|
<span className="mr-2 busuanzi_value_page_pv" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{post.blockMap && <NotionPage post={post} />}
|
||||||
|
</section>}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</LayoutBase>
|
</LayoutBase>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMediumZoomMargin () {
|
|
||||||
const width = window.innerWidth
|
|
||||||
|
|
||||||
if (width < 500) {
|
|
||||||
return 8
|
|
||||||
} else if (width < 800) {
|
|
||||||
return 20
|
|
||||||
} else if (width < 1280) {
|
|
||||||
return 30
|
|
||||||
} else if (width < 1600) {
|
|
||||||
return 40
|
|
||||||
} else if (width < 1920) {
|
|
||||||
return 48
|
|
||||||
} else {
|
|
||||||
return 72
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,16 +1,8 @@
|
|||||||
import Comment from '@/components/Comment'
|
import Comment from '@/components/Comment'
|
||||||
|
import NotionPage from '@/components/NotionPage'
|
||||||
import formatDate from '@/lib/formatDate'
|
import formatDate from '@/lib/formatDate'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import mediumZoom from 'medium-zoom'
|
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import 'prismjs'
|
|
||||||
import 'prismjs/components/prism-bash'
|
|
||||||
import 'prismjs/components/prism-javascript'
|
|
||||||
import 'prismjs/components/prism-markup'
|
|
||||||
import 'prismjs/components/prism-python'
|
|
||||||
import 'prismjs/components/prism-typescript'
|
|
||||||
import { useEffect, useRef } from 'react'
|
|
||||||
import { Code, Collection, CollectionRow, Equation, NotionRenderer } from 'react-notion-x'
|
|
||||||
import ArticleAround from './ArticleAround'
|
import ArticleAround from './ArticleAround'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,34 +10,15 @@ import ArticleAround from './ArticleAround'
|
|||||||
* @param {*} param0
|
* @param {*} param0
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export default function ArticleDetail ({ post, recommendPosts, prev, next }) {
|
export default function ArticleDetail({ post, recommendPosts, prev, next }) {
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
const date = formatDate(post?.date?.start_date || post.createdTime, locale.LOCALE)
|
const date = formatDate(post?.date?.start_date || post.createdTime, locale.LOCALE)
|
||||||
|
|
||||||
const zoom = typeof window !== 'undefined' && mediumZoom({
|
|
||||||
container: '.notion-viewport',
|
|
||||||
background: 'rgba(0, 0, 0, 0.2)',
|
|
||||||
margin: getMediumZoomMargin()
|
|
||||||
})
|
|
||||||
const zoomRef = useRef(zoom ? zoom.clone() : null)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// 将所有container下的所有图片添加medium-zoom
|
|
||||||
const container = document.getElementById('container')
|
|
||||||
const imgList = container.getElementsByTagName('img')
|
|
||||||
if (imgList && zoomRef.current) {
|
|
||||||
for (let i = 0; i < imgList.length; i++) {
|
|
||||||
(zoomRef.current).attach(imgList[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return (<div id="container" className="max-w-5xl overflow-x-auto flex-grow mx-auto w-screen md:w-full ">
|
return (<div id="container" className="max-w-5xl overflow-x-auto flex-grow mx-auto w-screen md:w-full ">
|
||||||
{post.type && !post.type.includes('Page') && post?.page_cover && (
|
{post.type && !post.type.includes('Page') && post?.page_cover && (
|
||||||
<div className="w-full relative md:flex-shrink-0 overflow-hidden">
|
<div className="w-full relative md:flex-shrink-0 overflow-hidden">
|
||||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||||
<img alt={post.title} src={post?.page_cover} className='object-center w-full' />
|
<img alt={post.title} src={post?.page_cover} className='object-center w-full' />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<article itemScope itemType="https://schema.org/Movie"
|
<article itemScope itemType="https://schema.org/Movie"
|
||||||
className="subpixel-antialiased py-10 px-5 lg:pt-24 md:px-32 dark:border-gray-700 bg-white dark:bg-gray-800"
|
className="subpixel-antialiased py-10 px-5 lg:pt-24 md:px-32 dark:border-gray-700 bg-white dark:bg-gray-800"
|
||||||
@@ -53,59 +26,50 @@ export default function ArticleDetail ({ post, recommendPosts, prev, next }) {
|
|||||||
|
|
||||||
<header className='animate__slideInDown animate__animated'>
|
<header className='animate__slideInDown animate__animated'>
|
||||||
|
|
||||||
{/* 文章Title */}
|
{/* 文章Title */}
|
||||||
<div className="font-bold text-3xl text-black dark:text-white font-serif pt-10">
|
<div className="font-bold text-3xl text-black dark:text-white font-serif pt-10">
|
||||||
{post.title}
|
{post.title}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section className="flex-wrap flex mt-2 text-gray-400 dark:text-gray-400 font-light leading-8">
|
<section className="flex-wrap flex mt-2 text-gray-400 dark:text-gray-400 font-light leading-8">
|
||||||
<div>
|
<div>
|
||||||
<Link href={`/category/${post.category}`} passHref>
|
<Link href={`/category/${post.category}`} passHref>
|
||||||
<a className="cursor-pointer text-md mr-2 hover:text-black dark:hover:text-white border-b dark:border-gray-500 border-dashed">
|
<a className="cursor-pointer text-md mr-2 hover:text-black dark:hover:text-white border-b dark:border-gray-500 border-dashed">
|
||||||
<i className="mr-1 fas fa-folder-open" />
|
<i className="mr-1 fas fa-folder-open" />
|
||||||
{post.category}
|
{post.category}
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
<span className='mr-2'>|</span>
|
||||||
|
|
||||||
|
{post.type[0] !== 'Page' && (<>
|
||||||
|
<Link
|
||||||
|
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
||||||
|
passHref
|
||||||
|
>
|
||||||
|
<a className="pl-1 mr-2 cursor-pointer hover:text-gray-700 dark:hover:text-gray-200 border-b dark:border-gray-500 border-dashed">
|
||||||
|
{date}
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
<span className='mr-2'>|</span>
|
<span className='mr-2'>|</span>
|
||||||
|
<span className='mx-2 text-gray-400 dark:text-gray-500'>
|
||||||
|
{locale.COMMON.LAST_EDITED_TIME}: {post.lastEditedTime}
|
||||||
|
</span>
|
||||||
|
</>)}
|
||||||
|
|
||||||
{post.type[0] !== 'Page' && (<>
|
<div className=" busuanzi_container_page_pv font-light mr-2">
|
||||||
<Link
|
<i className='mr-1 fas fa-eye' />
|
||||||
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
|
||||||
passHref
|
<span className="mr-2 busuanzi_value_page_pv" />
|
||||||
>
|
|
||||||
<a className="pl-1 mr-2 cursor-pointer hover:text-gray-700 dark:hover:text-gray-200 border-b dark:border-gray-500 border-dashed">
|
|
||||||
{date}
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
<span className='mr-2'>|</span>
|
|
||||||
</>)}
|
|
||||||
|
|
||||||
<div className="hidden busuanzi_container_page_pv font-light mr-2">
|
|
||||||
<i className='mr-1 fas fa-eye'/>
|
|
||||||
|
|
||||||
<span className="mr-2 busuanzi_value_page_pv"/>
|
|
||||||
<span className='mr-2'>|</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
{/* Notion文章主体 */}
|
{/* Notion文章主体 */}
|
||||||
<section id='notion-article' className='px-1'>
|
<section id='notion-article' className='px-1'>
|
||||||
{post.blockMap && (
|
{post.blockMap && <NotionPage post={post} />}
|
||||||
<NotionRenderer
|
|
||||||
recordMap={post.blockMap}
|
|
||||||
mapPageUrl={mapPageUrl}
|
|
||||||
components={{
|
|
||||||
equation: Equation,
|
|
||||||
code: Code,
|
|
||||||
collectionRow: CollectionRow,
|
|
||||||
collection: Collection
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="px-1 py-2 my-1 text-sm font-light overflow-auto text-gray-600 dark:text-gray-400">
|
<section className="px-1 py-2 my-1 text-sm font-light overflow-auto text-gray-600 dark:text-gray-400">
|
||||||
@@ -116,38 +80,16 @@ export default function ArticleDetail ({ post, recommendPosts, prev, next }) {
|
|||||||
data-ad-layout="in-article"
|
data-ad-layout="in-article"
|
||||||
data-ad-format="fluid"
|
data-ad-format="fluid"
|
||||||
data-ad-client="ca-pub-2708419466378217"
|
data-ad-client="ca-pub-2708419466378217"
|
||||||
data-ad-slot="3806269138"/>
|
data-ad-slot="3806269138" />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<ArticleAround prev={prev} next={next}/>
|
<ArticleAround prev={prev} next={next} />
|
||||||
|
|
||||||
{/* 评论互动 */}
|
{/* 评论互动 */}
|
||||||
<div className="duration-200 shadow px-12 w-screen md:w-full overflow-x-auto dark:border-gray-700 bg-white dark:bg-gray-800">
|
<div className="duration-200 shadow px-12 w-screen md:w-full overflow-x-auto dark:border-gray-700 bg-white dark:bg-gray-800">
|
||||||
<Comment frontMatter={post} />
|
<Comment frontMatter={post} />
|
||||||
</div>
|
</div>
|
||||||
</div>)
|
</div>)
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapPageUrl = id => {
|
|
||||||
return 'https://www.notion.so/' + id.replace(/-/g, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMediumZoomMargin () {
|
|
||||||
const width = window.innerWidth
|
|
||||||
|
|
||||||
if (width < 500) {
|
|
||||||
return 8
|
|
||||||
} else if (width < 800) {
|
|
||||||
return 20
|
|
||||||
} else if (width < 1280) {
|
|
||||||
return 30
|
|
||||||
} else if (width < 1600) {
|
|
||||||
return 40
|
|
||||||
} else if (width < 1920) {
|
|
||||||
return 48
|
|
||||||
} else {
|
|
||||||
return 72
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,19 +1,16 @@
|
|||||||
import { getPageTableOfContents } from 'notion-utils'
|
import { getPageTableOfContents } from 'notion-utils'
|
||||||
import 'prismjs'
|
|
||||||
import 'prismjs/components/prism-bash'
|
|
||||||
import 'prismjs/components/prism-java'
|
|
||||||
import 'prismjs/components/prism-javascript'
|
|
||||||
import 'prismjs/components/prism-markup'
|
|
||||||
import 'prismjs/components/prism-python'
|
|
||||||
import 'prismjs/components/prism-typescript'
|
|
||||||
import { useRef } from 'react'
|
import { useRef } from 'react'
|
||||||
import ArticleDetail from './components/ArticleDetail'
|
|
||||||
import { ArticleLock } from './components/ArticleLock'
|
import { ArticleLock } from './components/ArticleLock'
|
||||||
import HeaderArticle from './components/HeaderArticle'
|
import HeaderArticle from './components/HeaderArticle'
|
||||||
import JumpToCommentButton from './components/JumpToCommentButton'
|
import JumpToCommentButton from './components/JumpToCommentButton'
|
||||||
import TocDrawer from './components/TocDrawer'
|
import TocDrawer from './components/TocDrawer'
|
||||||
import TocDrawerButton from './components/TocDrawerButton'
|
import TocDrawerButton from './components/TocDrawerButton'
|
||||||
import LayoutBase from './LayoutBase'
|
import LayoutBase from './LayoutBase'
|
||||||
|
import Comment from '@/components/Comment'
|
||||||
|
import NotionPage from '@/components/NotionPage'
|
||||||
|
import ArticleAdjacent from './components/ArticleAdjacent'
|
||||||
|
import ArticleCopyright from './components/ArticleCopyright'
|
||||||
|
import ArticleRecommend from './components/ArticleRecommend'
|
||||||
|
|
||||||
export const LayoutSlug = props => {
|
export const LayoutSlug = props => {
|
||||||
const { post, lock, validPassword } = props
|
const { post, lock, validPassword } = props
|
||||||
@@ -27,27 +24,58 @@ export const LayoutSlug = props => {
|
|||||||
const targetRef = typeof window !== 'undefined' ? document.getElementById('container') : null
|
const targetRef = typeof window !== 'undefined' ? document.getElementById('container') : null
|
||||||
|
|
||||||
const floatSlot = <>
|
const floatSlot = <>
|
||||||
{post?.toc?.length > 1 && <div className="block lg:hidden">
|
{post?.toc?.length > 1 && <div className="block lg:hidden">
|
||||||
<TocDrawerButton
|
<TocDrawerButton
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
drawerRight?.current?.handleSwitchVisible()
|
drawerRight?.current?.handleSwitchVisible()
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>}
|
</div>}
|
||||||
<JumpToCommentButton/>
|
<JumpToCommentButton />
|
||||||
</>
|
</>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LayoutBase
|
<LayoutBase
|
||||||
headerSlot={<HeaderArticle {...props}/>}
|
headerSlot={<HeaderArticle {...props} />}
|
||||||
{...props}
|
{...props}
|
||||||
showCategory={false}
|
showCategory={false}
|
||||||
showTag={false}
|
showTag={false}
|
||||||
floatSlot={floatSlot}
|
floatSlot={floatSlot}
|
||||||
>
|
>
|
||||||
<div className="w-full lg:shadow-sm lg:hover:shadow lg:border lg:rounded-xl lg:px-2 lg:py-4 bg-white dark:bg-hexo-black-gray dark:border-black">
|
<div className="w-full lg:shadow-sm lg:hover:shadow lg:border lg:rounded-xl lg:px-2 lg:py-4 bg-white dark:bg-hexo-black-gray dark:border-black">
|
||||||
{!lock && <ArticleDetail {...props} />}
|
|
||||||
{lock && <ArticleLock password={post.password} validPassword={validPassword} />}
|
{lock && <ArticleLock password={post.password} validPassword={validPassword} />}
|
||||||
|
|
||||||
|
{!lock && <div id="container" className="max-w-5xl overflow-x-auto flex-grow mx-auto md:w-full md:px-5 ">
|
||||||
|
|
||||||
|
<article itemScope itemType="https://schema.org/Movie" className="subpixel-antialiased" >
|
||||||
|
{/* Notion文章主体 */}
|
||||||
|
<section id='notion-article' className='px-5'>
|
||||||
|
{post.blockMap && <NotionPage post={post} />}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="px-1 py-2 my-1 text-sm font-light overflow-auto text-gray-600 dark:text-gray-400">
|
||||||
|
{/* 文章内嵌广告 */}
|
||||||
|
<ins className="adsbygoogle"
|
||||||
|
style={{ display: 'block', textAlign: 'center' }}
|
||||||
|
data-adtest="on"
|
||||||
|
data-ad-layout="in-article"
|
||||||
|
data-ad-format="fluid"
|
||||||
|
data-ad-client="ca-pub-2708419466378217"
|
||||||
|
data-ad-slot="3806269138" />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<ArticleCopyright {...props} />
|
||||||
|
<ArticleRecommend {...props} />
|
||||||
|
<ArticleAdjacent {...props} />
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<hr className='border-dashed' />
|
||||||
|
|
||||||
|
{/* 评论互动 */}
|
||||||
|
<div className="duration-200 overflow-x-auto bg-white dark:bg-hexo-black-gray px-3">
|
||||||
|
<Comment frontMatter={post} />
|
||||||
|
</div>
|
||||||
|
</div>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='block lg:hidden'>
|
<div className='block lg:hidden'>
|
||||||
|
|||||||
@@ -1,105 +0,0 @@
|
|||||||
import Comment from '@/components/Comment'
|
|
||||||
import mediumZoom from 'medium-zoom'
|
|
||||||
import 'prismjs'
|
|
||||||
import 'prismjs/components/prism-bash'
|
|
||||||
import 'prismjs/components/prism-javascript'
|
|
||||||
import 'prismjs/components/prism-markup'
|
|
||||||
import 'prismjs/components/prism-python'
|
|
||||||
import 'prismjs/components/prism-typescript'
|
|
||||||
import { useEffect, useRef } from 'react'
|
|
||||||
import { Code, Collection, CollectionRow, Equation, NotionRenderer } from 'react-notion-x'
|
|
||||||
import ArticleAdjacent from './ArticleAdjacent'
|
|
||||||
import ArticleCopyright from './ArticleCopyright'
|
|
||||||
import ArticleRecommend from './ArticleRecommend'
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {*} param0
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export default function ArticleDetail (props) {
|
|
||||||
const { post } = props
|
|
||||||
const zoom = typeof window !== 'undefined' && mediumZoom({
|
|
||||||
container: '.notion-viewport',
|
|
||||||
background: 'rgba(0, 0, 0, 0.2)',
|
|
||||||
margin: getMediumZoomMargin()
|
|
||||||
})
|
|
||||||
const zoomRef = useRef(zoom ? zoom.clone() : null)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// 将所有container下的所有图片添加medium-zoom
|
|
||||||
const container = document?.getElementById('container')
|
|
||||||
const imgList = container?.getElementsByTagName('img')
|
|
||||||
if (imgList && zoomRef.current) {
|
|
||||||
for (let i = 0; i < imgList.length; i++) {
|
|
||||||
(zoomRef.current).attach(imgList[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return (<div id="container" className="max-w-5xl overflow-x-auto flex-grow mx-auto md:w-full md:px-5 ">
|
|
||||||
<article itemScope itemType="https://schema.org/Movie" className="subpixel-antialiased" >
|
|
||||||
|
|
||||||
{/* Notion文章主体 */}
|
|
||||||
<section id='notion-article' className='px-5'>
|
|
||||||
{post.blockMap && (
|
|
||||||
<NotionRenderer
|
|
||||||
recordMap={post.blockMap}
|
|
||||||
mapPageUrl={mapPageUrl}
|
|
||||||
components={{
|
|
||||||
equation: Equation,
|
|
||||||
code: Code,
|
|
||||||
collectionRow: CollectionRow,
|
|
||||||
collection: Collection
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="px-1 py-2 my-1 text-sm font-light overflow-auto text-gray-600 dark:text-gray-400">
|
|
||||||
{/* 文章内嵌广告 */}
|
|
||||||
<ins className="adsbygoogle"
|
|
||||||
style={{ display: 'block', textAlign: 'center' }}
|
|
||||||
data-adtest="on"
|
|
||||||
data-ad-layout="in-article"
|
|
||||||
data-ad-format="fluid"
|
|
||||||
data-ad-client="ca-pub-2708419466378217"
|
|
||||||
data-ad-slot="3806269138"/>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<ArticleCopyright {...props}/>
|
|
||||||
<ArticleRecommend {...props}/>
|
|
||||||
<ArticleAdjacent {...props}/>
|
|
||||||
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<hr className='border-dashed'/>
|
|
||||||
|
|
||||||
{/* 评论互动 */}
|
|
||||||
<div className="duration-200 overflow-x-auto bg-white dark:bg-hexo-black-gray px-3">
|
|
||||||
<Comment frontMatter={post} />
|
|
||||||
</div>
|
|
||||||
</div>)
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapPageUrl = id => {
|
|
||||||
return 'https://www.notion.so/' + id.replace(/-/g, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMediumZoomMargin () {
|
|
||||||
const width = window.innerWidth
|
|
||||||
|
|
||||||
if (width < 500) {
|
|
||||||
return 8
|
|
||||||
} else if (width < 800) {
|
|
||||||
return 20
|
|
||||||
} else if (width < 1280) {
|
|
||||||
return 30
|
|
||||||
} else if (width < 1600) {
|
|
||||||
return 40
|
|
||||||
} else if (width < 1920) {
|
|
||||||
return 48
|
|
||||||
} else {
|
|
||||||
return 72
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,9 @@
|
|||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {
|
|
||||||
Code,
|
|
||||||
Collection,
|
|
||||||
CollectionRow,
|
|
||||||
Equation,
|
|
||||||
NotionRenderer
|
|
||||||
} from 'react-notion-x'
|
|
||||||
import TagItemMini from './TagItemMini'
|
import TagItemMini from './TagItemMini'
|
||||||
import CONFIG_HEXO from '../config_hexo'
|
import CONFIG_HEXO from '../config_hexo'
|
||||||
|
import NotionPage from '@/components/NotionPage'
|
||||||
|
|
||||||
const BlogPostCard = ({ post, showSummary }) => {
|
const BlogPostCard = ({ post, showSummary }) => {
|
||||||
const showPreview = CONFIG_HEXO.POST_LIST_PREVIEW && post.blockMap
|
const showPreview = CONFIG_HEXO.POST_LIST_PREVIEW && post.blockMap
|
||||||
@@ -22,18 +16,16 @@ const BlogPostCard = ({ post, showSummary }) => {
|
|||||||
<div className="lg:p-8 p-4 flex flex-col w-full">
|
<div className="lg:p-8 p-4 flex flex-col w-full">
|
||||||
<Link href={`${BLOG.SUB_PATH}/article/${post.slug}`} passHref>
|
<Link href={`${BLOG.SUB_PATH}/article/${post.slug}`} passHref>
|
||||||
<a
|
<a
|
||||||
className={`replace cursor-pointer hover:underline text-2xl font-sans ${
|
className={`replace cursor-pointer hover:underline text-2xl font-sans ${showPreview ? 'text-center' : ''
|
||||||
showPreview ? 'text-center' : ''
|
} leading-tight text-gray-700 dark:text-gray-100 hover:text-indigo-700 dark:hover:text-indigo-400`}
|
||||||
} leading-tight text-gray-700 dark:text-gray-100 hover:text-indigo-700 dark:hover:text-indigo-400`}
|
|
||||||
>
|
>
|
||||||
{post.title}
|
{post.title}
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={`flex mt-2 items-center ${
|
className={`flex mt-2 items-center ${showPreview ? 'justify-center' : 'justify-start'
|
||||||
showPreview ? 'justify-center' : 'justify-start'
|
} flex-wrap dark:text-gray-500 text-gray-400 hover:text-indigo-700 dark:hover:text-indigo-400`}
|
||||||
} flex-wrap dark:text-gray-500 text-gray-400 hover:text-indigo-700 dark:hover:text-indigo-400`}
|
|
||||||
>
|
>
|
||||||
<Link
|
<Link
|
||||||
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
||||||
@@ -54,17 +46,7 @@ const BlogPostCard = ({ post, showSummary }) => {
|
|||||||
|
|
||||||
{showPreview && (
|
{showPreview && (
|
||||||
<div className="overflow-ellipsis truncate">
|
<div className="overflow-ellipsis truncate">
|
||||||
<NotionRenderer
|
<NotionPage post={post} />
|
||||||
bodyClassName="max-h-full"
|
|
||||||
recordMap={post.blockMap}
|
|
||||||
mapPageUrl={mapPageUrl}
|
|
||||||
components={{
|
|
||||||
equation: Equation,
|
|
||||||
code: Code,
|
|
||||||
collectionRow: CollectionRow,
|
|
||||||
collection: Collection
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -104,8 +86,4 @@ const BlogPostCard = ({ post, showSummary }) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapPageUrl = id => {
|
|
||||||
return 'https://www.notion.so/' + id.replace(/-/g, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
export default BlogPostCard
|
export default BlogPostCard
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useGlobal } from '@/lib/global'
|
|||||||
import formatDate from '@/lib/formatDate'
|
import formatDate from '@/lib/formatDate'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
|
|
||||||
export default function HeaderArticle ({ post, siteInfo }) {
|
export default function HeaderArticle({ post, siteInfo }) {
|
||||||
const headerImage = post?.page_cover ? `url("${post.page_cover}")` : `url("${siteInfo?.pageCover}")`
|
const headerImage = post?.page_cover ? `url("${post.page_cover}")` : `url("${siteInfo?.pageCover}")`
|
||||||
const { isDarkMode } = useGlobal()
|
const { isDarkMode } = useGlobal()
|
||||||
|
|
||||||
@@ -47,52 +47,53 @@ export default function HeaderArticle ({ post, siteInfo }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
id="header"
|
id="header"
|
||||||
className="w-full h-96 relative md:flex-shrink-0 overflow-hidden bg-cover bg-center bg-no-repeat animate__animated animate__fadeIn"
|
className="w-full h-96 relative md:flex-shrink-0 overflow-hidden bg-cover bg-center bg-no-repeat animate__animated animate__fadeIn"
|
||||||
style={{ backgroundImage: headerImage }}
|
style={{ backgroundImage: headerImage }}
|
||||||
>
|
>
|
||||||
<header className="animate__slideInDown animate__animated bg-black bg-opacity-70 absolute top-0 w-full h-96 py-10 flex justify-center items-center font-sans">
|
<header className="animate__slideInDown animate__animated bg-black bg-opacity-70 absolute top-0 w-full h-96 py-10 flex justify-center items-center font-sans">
|
||||||
<div className='mt-24'>
|
<div className='mt-24'>
|
||||||
{/* 文章Title */}
|
{/* 文章Title */}
|
||||||
<div className="font-bold text-xl shadow-text flex justify-center text-white dark:text-white font-sans">
|
<div className="font-bold text-xl shadow-text flex justify-center text-white dark:text-white font-sans">
|
||||||
{post.title}
|
{post.title}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section className="flex-wrap shadow-text flex text-sm justify-center mt-2 text-white dark:text-gray-400 font-light leading-8">
|
<section className="flex-wrap shadow-text flex text-sm justify-center mt-2 text-white dark:text-gray-400 font-light leading-8">
|
||||||
<div className='dark:text-gray-200'>
|
<div className='dark:text-gray-200'>
|
||||||
{post.category && <>
|
{post.category && <>
|
||||||
<Link href={`/category/${post.category}`} passHref>
|
<Link href={`/category/${post.category}`} passHref>
|
||||||
<a className="cursor-pointer mr-2 dark:hover:text-white border-b dark:border-gray-500 border-dashed">
|
<div className="cursor-pointer mr-2 dark:hover:text-white border-b dark:border-gray-500 border-dashed">
|
||||||
<i className="mr-1 fas fa-folder-open" />
|
<i className="mr-1 fas fa-folder-open" />
|
||||||
{post.category}
|
{post.category}
|
||||||
</a>
|
</div>
|
||||||
|
</Link>
|
||||||
|
</>}
|
||||||
|
</div>
|
||||||
|
<div className='flex justify-center'>
|
||||||
|
{post.type[0] !== 'Page' && (
|
||||||
|
<>
|
||||||
|
<Link
|
||||||
|
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
||||||
|
passHref
|
||||||
|
>
|
||||||
|
<a className="pl-1 mr-2 cursor-pointer border-b dark:border-gray-500 border-dashed">
|
||||||
|
{locale.COMMON.POST_TIME}: {date}
|
||||||
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
<span className="mr-2">|</span>
|
</>
|
||||||
</>}
|
)}
|
||||||
|
<div className="pl-1 mr-2">
|
||||||
{post.type[0] !== 'Page' && (
|
{locale.COMMON.LAST_EDITED_TIME}: {post.lastEditedTime}
|
||||||
<>
|
|
||||||
<Link
|
|
||||||
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
|
||||||
passHref
|
|
||||||
>
|
|
||||||
<a className="pl-1 mr-2 cursor-pointer hover:underline border-b dark:border-gray-500 border-dashed">
|
|
||||||
<i className="far fa-calendar-alt mr-1"/> {date}
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="hidden busuanzi_container_page_pv font-light mr-2">
|
|
||||||
<span className="mr-2">|</span>
|
|
||||||
<span className="mr-2 busuanzi_value_page_pv" />
|
|
||||||
次访问
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</div>
|
||||||
</div>
|
<div className=" busuanzi_container_page_pv font-light mr-2">
|
||||||
</header>
|
<span className="mr-2 busuanzi_value_page_pv" />
|
||||||
</div>
|
次访问
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import CONFIG_HEXO from '../config_hexo'
|
import CONFIG_HEXO from '../config_hexo'
|
||||||
|
let wrapperTop = 0
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 跳转到评论区
|
* 跳转到评论区
|
||||||
@@ -10,16 +11,30 @@ const JumpToCommentButton = () => {
|
|||||||
if (!CONFIG_HEXO.WIDGET_TO_COMMENT) {
|
if (!CONFIG_HEXO.WIDGET_TO_COMMENT) {
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
function navToComment () {
|
|
||||||
const commentElement = document.getElementById('comment')
|
function updateHeaderHeight() {
|
||||||
if (commentElement) {
|
setTimeout(() => {
|
||||||
commentElement?.scrollIntoView({ behavior: 'smooth', block: 'start', inline: 'nearest' })
|
if (window) {
|
||||||
}
|
const wrapperElement = document.getElementById('comment')
|
||||||
|
wrapperTop = wrapperElement?.offsetTop
|
||||||
|
}
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
function navToComment() {
|
||||||
|
window.scrollTo({ top: wrapperTop, behavior: 'smooth' })
|
||||||
|
// 兼容性不好
|
||||||
|
// const commentElement = document.getElementById('comment')
|
||||||
|
// if (commentElement) {
|
||||||
|
// commentElement?.scrollIntoView({ behavior: 'smooth', block: 'start', inline: 'nearest' })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateHeaderHeight()
|
||||||
|
})
|
||||||
|
|
||||||
return (<div className='flex space-x-1 items-center justify-center transform hover:scale-105 duration-200 w-7 h-7 text-center' onClick={navToComment} >
|
return (<div className='flex space-x-1 items-center justify-center transform hover:scale-105 duration-200 w-7 h-7 text-center' onClick={navToComment} >
|
||||||
<i className='fas fa-comment text-xs' />
|
<i className='fas fa-comment text-xs' />
|
||||||
</div>)
|
</div>)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default JumpToCommentButton
|
export default JumpToCommentButton
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import { getPageTableOfContents } from 'notion-utils'
|
|||||||
|
|
||||||
import LayoutBase from './LayoutBase'
|
import LayoutBase from './LayoutBase'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import mediumZoom from 'medium-zoom'
|
import React from 'react'
|
||||||
import React, { useEffect, useRef } from 'react'
|
|
||||||
import Catalog from './components/Catalog'
|
import Catalog from './components/Catalog'
|
||||||
import { ArticleDetail } from './components/ArticleDetail'
|
import { ArticleDetail } from './components/ArticleDetail'
|
||||||
import { ArticleLock } from './components/ArticleLock'
|
import { ArticleLock } from './components/ArticleLock'
|
||||||
@@ -17,26 +16,6 @@ export const LayoutSlug = props => {
|
|||||||
}
|
}
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
|
|
||||||
const zoom =
|
|
||||||
typeof window !== 'undefined' &&
|
|
||||||
mediumZoom({
|
|
||||||
container: '.notion-viewport',
|
|
||||||
background: 'rgba(0, 0, 0, 0.2)',
|
|
||||||
margin: getMediumZoomMargin()
|
|
||||||
})
|
|
||||||
const zoomRef = useRef(zoom ? zoom.clone() : null)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// 将所有container下的所有图片添加medium-zoom
|
|
||||||
const container = document.getElementById('notion-article')
|
|
||||||
const imgList = container?.getElementsByTagName('img')
|
|
||||||
if (imgList && zoomRef.current) {
|
|
||||||
for (let i = 0; i < imgList.length; i++) {
|
|
||||||
zoomRef.current.attach(imgList[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const slotRight = post?.toc && post?.toc?.length > 3 && (
|
const slotRight = post?.toc && post?.toc?.length > 3 && (
|
||||||
<div key={locale.COMMON.TABLE_OF_CONTENTS} >
|
<div key={locale.COMMON.TABLE_OF_CONTENTS} >
|
||||||
<Catalog toc={post.toc} />
|
<Catalog toc={post.toc} />
|
||||||
@@ -50,27 +29,9 @@ export const LayoutSlug = props => {
|
|||||||
slotRight={slotRight}
|
slotRight={slotRight}
|
||||||
>
|
>
|
||||||
|
|
||||||
{!lock && <ArticleDetail {...props} />}
|
{!lock && <ArticleDetail {...props} />}
|
||||||
|
|
||||||
{lock && <ArticleLock password={post.password} validPassword={validPassword} />}
|
{lock && <ArticleLock password={post.password} validPassword={validPassword} />}
|
||||||
</LayoutBase>
|
</LayoutBase>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMediumZoomMargin () {
|
|
||||||
const width = window.innerWidth
|
|
||||||
|
|
||||||
if (width < 500) {
|
|
||||||
return 8
|
|
||||||
} else if (width < 800) {
|
|
||||||
return 20
|
|
||||||
} else if (width < 1280) {
|
|
||||||
return 30
|
|
||||||
} else if (width < 1600) {
|
|
||||||
return 40
|
|
||||||
} else if (width < 1920) {
|
|
||||||
return 48
|
|
||||||
} else {
|
|
||||||
return 72
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
import {
|
|
||||||
Code,
|
|
||||||
Collection,
|
|
||||||
CollectionRow,
|
|
||||||
Equation,
|
|
||||||
NotionRenderer
|
|
||||||
} from 'react-notion-x'
|
|
||||||
import Comment from '@/components/Comment'
|
import Comment from '@/components/Comment'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
@@ -14,18 +7,8 @@ import TagItemMini from './TagItemMini'
|
|||||||
import CONFIG_MEDIUM from '../config_medium'
|
import CONFIG_MEDIUM from '../config_medium'
|
||||||
import formatDate from '@/lib/formatDate'
|
import formatDate from '@/lib/formatDate'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
|
|
||||||
import 'prismjs'
|
|
||||||
import 'prismjs/components/prism-bash'
|
|
||||||
import 'prismjs/components/prism-javascript'
|
|
||||||
import 'prismjs/components/prism-markup'
|
|
||||||
import 'prismjs/components/prism-python'
|
|
||||||
import 'prismjs/components/prism-typescript'
|
|
||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
|
import NotionPage from '@/components/NotionPage'
|
||||||
const mapPageUrl = id => {
|
|
||||||
return 'https://www.notion.so/' + id.replace(/-/g, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ArticleDetail = props => {
|
export const ArticleDetail = props => {
|
||||||
const { post, prev, next } = props
|
const { post, prev, next } = props
|
||||||
@@ -54,6 +37,9 @@ export const ArticleDetail = props => {
|
|||||||
</>
|
</>
|
||||||
</Link>
|
</Link>
|
||||||
<div className="text-gray-500">{date}</div>
|
<div className="text-gray-500">{date}</div>
|
||||||
|
<div className='mx-2 text-gray-300 dark:text-gray-600'>
|
||||||
|
{locale.COMMON.LAST_EDITED_TIME}: {post.lastEditedTime}
|
||||||
|
</div>
|
||||||
<div className="hidden busuanzi_container_page_pv text-gray-500 font-light mr-2">
|
<div className="hidden busuanzi_container_page_pv text-gray-500 font-light mr-2">
|
||||||
<i className="ml-3 mr-0.5 fas fa-eye" />
|
<i className="ml-3 mr-0.5 fas fa-eye" />
|
||||||
|
|
||||||
@@ -62,18 +48,7 @@ export const ArticleDetail = props => {
|
|||||||
</section>
|
</section>
|
||||||
{/* Notion文章主体 */}
|
{/* Notion文章主体 */}
|
||||||
<section id="notion-article" className="px-1 max-w-5xl">
|
<section id="notion-article" className="px-1 max-w-5xl">
|
||||||
{post.blockMap && (
|
{post.blockMap && (<NotionPage post={post} />)}
|
||||||
<NotionRenderer
|
|
||||||
recordMap={post.blockMap}
|
|
||||||
mapPageUrl={mapPageUrl}
|
|
||||||
components={{
|
|
||||||
equation: Equation,
|
|
||||||
code: Code,
|
|
||||||
collectionRow: CollectionRow,
|
|
||||||
collection: Collection
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="px-1 py-2 my-1 text-sm font-light overflow-auto text-gray-600 dark:text-gray-400">
|
<section className="px-1 py-2 my-1 text-sm font-light overflow-auto text-gray-600 dark:text-gray-400">
|
||||||
@@ -90,10 +65,10 @@ export const ArticleDetail = props => {
|
|||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<div className='flex justify-between'>
|
<div className='flex justify-between'>
|
||||||
{ CONFIG_MEDIUM.POST_DETAIL_CATEGORY && post.category && <CategoryItem category={post.category}/>}
|
{CONFIG_MEDIUM.POST_DETAIL_CATEGORY && post.category && <CategoryItem category={post.category} />}
|
||||||
<div>
|
<div>
|
||||||
{ CONFIG_MEDIUM.POST_DETAIL_TAG && post?.tagItems?.map(tag => <TagItemMini key={tag.name} tag={tag} />)}
|
{CONFIG_MEDIUM.POST_DETAIL_TAG && post?.tagItems?.map(tag => <TagItemMini key={tag.name} tag={tag} />)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ArticleAround prev={prev} next={next} />
|
<ArticleAround prev={prev} next={next} />
|
||||||
<Comment frontMatter={post} />
|
<Comment frontMatter={post} />
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import BLOG from '@/blog.config'
|
import BLOG from '@/blog.config'
|
||||||
|
import NotionPage from '@/components/NotionPage'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Code, Collection, Equation, NotionRenderer } from 'react-notion-x'
|
|
||||||
import CONFIG_MEDIUM from '../config_medium'
|
import CONFIG_MEDIUM from '../config_medium'
|
||||||
import CategoryItem from './CategoryItem'
|
import CategoryItem from './CategoryItem'
|
||||||
import TagItemMini from './TagItemMini'
|
import TagItemMini from './TagItemMini'
|
||||||
@@ -51,16 +51,7 @@ const BlogPostCard = ({ post, showSummary }) => {
|
|||||||
|
|
||||||
{showPreview && (
|
{showPreview && (
|
||||||
<div className="overflow-ellipsis truncate">
|
<div className="overflow-ellipsis truncate">
|
||||||
<NotionRenderer
|
<NotionPage post={post} />
|
||||||
bodyClassName="max-h-full"
|
|
||||||
recordMap={post.blockMap}
|
|
||||||
mapPageUrl={mapPageUrl}
|
|
||||||
components={{
|
|
||||||
equation: Equation,
|
|
||||||
code: Code,
|
|
||||||
collection: Collection
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div className="pointer-events-none border-t pt-8 border-dashed">
|
<div className="pointer-events-none border-t pt-8 border-dashed">
|
||||||
<div className="w-full justify-start flex">
|
<div className="w-full justify-start flex">
|
||||||
<Link href={`${BLOG.SUB_PATH}/article/${post.slug}`} passHref>
|
<Link href={`${BLOG.SUB_PATH}/article/${post.slug}`} passHref>
|
||||||
@@ -78,8 +69,4 @@ const BlogPostCard = ({ post, showSummary }) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapPageUrl = id => {
|
|
||||||
return 'https://www.notion.so/' + id.replace(/-/g, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
export default BlogPostCard
|
export default BlogPostCard
|
||||||
|
|||||||
@@ -6,50 +6,23 @@ import ShareBar from './ShareBar'
|
|||||||
import TagItem from './TagItem'
|
import TagItem from './TagItem'
|
||||||
import formatDate from '@/lib/formatDate'
|
import formatDate from '@/lib/formatDate'
|
||||||
import { useGlobal } from '@/lib/global'
|
import { useGlobal } from '@/lib/global'
|
||||||
import mediumZoom from 'medium-zoom'
|
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import 'prismjs'
|
|
||||||
import 'prismjs/components/prism-bash'
|
|
||||||
import 'prismjs/components/prism-c'
|
|
||||||
import 'prismjs/components/prism-java'
|
|
||||||
import 'prismjs/components/prism-markup'
|
|
||||||
import 'prismjs/components/prism-python'
|
|
||||||
import 'prismjs/components/prism-typescript'
|
|
||||||
import { useEffect, useRef } from 'react'
|
|
||||||
import { Code, Collection, CollectionRow, Equation, NotionRenderer } from 'react-notion-x'
|
|
||||||
import ArticleCopyright from './ArticleCopyright'
|
import ArticleCopyright from './ArticleCopyright'
|
||||||
import WordCount from './WordCount'
|
import WordCount from './WordCount'
|
||||||
|
import NotionPage from '@/components/NotionPage'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {*} param0
|
* @param {*} param0
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export default function ArticleDetail (props) {
|
export default function ArticleDetail(props) {
|
||||||
const { post, recommendPosts, prev, next, showArticleInfo } = props
|
const { post, recommendPosts, prev, next, showArticleInfo } = props
|
||||||
const url = BLOG.LINK + useRouter().asPath
|
const url = BLOG.LINK + useRouter().asPath
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
const date = formatDate(post?.date?.start_date || post.createdTime, locale.LOCALE)
|
const date = formatDate(post?.date?.start_date || post.createdTime, locale.LOCALE)
|
||||||
|
|
||||||
const zoom = typeof window !== 'undefined' && mediumZoom({
|
|
||||||
container: '.notion-viewport',
|
|
||||||
background: 'rgba(0, 0, 0, 0.2)',
|
|
||||||
margin: getMediumZoomMargin()
|
|
||||||
})
|
|
||||||
const zoomRef = useRef(zoom ? zoom.clone() : null)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// 将所有container下的所有图片添加medium-zoom
|
|
||||||
const container = document.getElementById('container')
|
|
||||||
const imgList = container.getElementsByTagName('img')
|
|
||||||
if (imgList && zoomRef.current) {
|
|
||||||
for (let i = 0; i < imgList.length; i++) {
|
|
||||||
(zoomRef.current).attach(imgList[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return (<div id="container" className="shadow md:hover:shadow-2xl overflow-x-auto flex-grow mx-auto w-screen md:w-full ">
|
return (<div id="container" className="shadow md:hover:shadow-2xl overflow-x-auto flex-grow mx-auto w-screen md:w-full ">
|
||||||
<div itemScope itemType="https://schema.org/Movie"
|
<div itemScope itemType="https://schema.org/Movie"
|
||||||
className="subpixel-antialiased py-10 px-5 lg:pt-24 md:px-24 dark:border-gray-700 bg-white dark:bg-gray-800"
|
className="subpixel-antialiased py-10 px-5 lg:pt-24 md:px-24 dark:border-gray-700 bg-white dark:bg-gray-800"
|
||||||
@@ -57,68 +30,61 @@ export default function ArticleDetail (props) {
|
|||||||
|
|
||||||
{showArticleInfo && <header className='animate__slideInDown animate__animated'>
|
{showArticleInfo && <header className='animate__slideInDown animate__animated'>
|
||||||
{post.type && !post.type.includes('Page') && post?.page_cover && (
|
{post.type && !post.type.includes('Page') && post?.page_cover && (
|
||||||
<div className="w-full relative md:flex-shrink-0 overflow-hidden">
|
<div className="w-full relative md:flex-shrink-0 overflow-hidden">
|
||||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||||
<img alt={post.title} src={post?.page_cover} className='object-center w-full' />
|
<img alt={post.title} src={post?.page_cover} className='object-center w-full' />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 文章Title */}
|
{/* 文章Title */}
|
||||||
<div className="font-bold text-3xl text-black dark:text-white font-serif pt-10">
|
<div className="font-bold text-3xl text-black dark:text-white font-serif pt-10">
|
||||||
{post.title}
|
{post.title}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section className="flex-wrap flex mt-2 text-gray-400 dark:text-gray-400 font-light leading-8">
|
<section className="flex-wrap flex mt-2 text-gray-400 dark:text-gray-400 font-light leading-8">
|
||||||
<div>
|
<div className='flex flex-wrap'>
|
||||||
{post.category && <>
|
{post.category && <>
|
||||||
<Link href={`/category/${post.category}`} passHref>
|
<Link href={`/category/${post.category}`} passHref>
|
||||||
<a className="cursor-pointer text-md mr-2 hover:text-black dark:hover:text-white border-b dark:border-gray-500 border-dashed">
|
<a className="cursor-pointer text-md mr-2 hover:text-black dark:hover:text-white border-b dark:border-gray-500 border-dashed">
|
||||||
<i className="mr-1 far fa-folder-open" /> {post.category}
|
<i className="mr-1 far fa-folder-open" /> {post.category}
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
<span className='mr-2'>|</span>
|
<span className='mr-2'>|</span>
|
||||||
</>}
|
</>}
|
||||||
{post.type[0] !== 'Page' && (<>
|
{post.type[0] !== 'Page' && (<>
|
||||||
<Link
|
<Link
|
||||||
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
||||||
passHref
|
passHref
|
||||||
>
|
>
|
||||||
<a className="pl-1 mr-2 cursor-pointer hover:text-gray-700 dark:hover:text-gray-200 border-b dark:border-gray-500 border-dashed">
|
<a className="pl-1 mr-2 cursor-pointer hover:text-gray-700 dark:hover:text-gray-200 border-b dark:border-gray-500 border-dashed">
|
||||||
{date}
|
{date}
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
<span className='mr-2'>|</span>
|
<span className='mr-2'>|</span>
|
||||||
</>)}
|
|
||||||
|
|
||||||
<div className="hidden busuanzi_container_page_pv font-light mr-2">
|
<div className="hidden busuanzi_container_page_pv font-light mr-2">
|
||||||
<i className='mr-1 fas fa-eye'/>
|
<i className='mr-1 fas fa-eye' />
|
||||||
|
|
||||||
<span className="mr-2 busuanzi_value_page_pv"/>
|
<span className="mr-2 busuanzi_value_page_pv" />
|
||||||
<span className='mr-2'>|</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='flex flex-nowrap whitespace-nowrap items-center font-light text-md'>
|
|
||||||
<WordCount/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</section>
|
</>)}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='mr-2'>
|
||||||
|
<i className='far fa-clock mr-2' />{locale.COMMON.LAST_EDITED_TIME} {post.lastEditedTime}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='flex flex-nowrap whitespace-nowrap items-center font-light text-md'>
|
||||||
|
<WordCount />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
</header>}
|
</header>}
|
||||||
|
|
||||||
{/* Notion内容主体 */}
|
{/* Notion内容主体 */}
|
||||||
<article id='notion-article' className='px-1'>
|
<article id='notion-article' className='px-1'>
|
||||||
{post.blockMap && (
|
{post.blockMap && (<NotionPage post={post} />)}
|
||||||
<NotionRenderer
|
|
||||||
recordMap={post.blockMap}
|
|
||||||
mapPageUrl={mapPageUrl}
|
|
||||||
components={{
|
|
||||||
equation: Equation,
|
|
||||||
code: Code,
|
|
||||||
collectionRow: CollectionRow,
|
|
||||||
collection: Collection
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<section className="px-1 py-2 my-1 text-sm font-light overflow-auto text-gray-600 dark:text-gray-400">
|
<section className="px-1 py-2 my-1 text-sm font-light overflow-auto text-gray-600 dark:text-gray-400">
|
||||||
@@ -129,34 +95,34 @@ export default function ArticleDetail (props) {
|
|||||||
data-ad-layout="in-article"
|
data-ad-layout="in-article"
|
||||||
data-ad-format="fluid"
|
data-ad-format="fluid"
|
||||||
data-ad-client="ca-pub-2708419466378217"
|
data-ad-client="ca-pub-2708419466378217"
|
||||||
data-ad-slot="3806269138"/>
|
data-ad-slot="3806269138" />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{showArticleInfo && <>
|
{showArticleInfo && <>
|
||||||
{/* 版权声明 */}
|
{/* 版权声明 */}
|
||||||
<ArticleCopyright author={BLOG.AUTHOR} url={url} />
|
<ArticleCopyright author={BLOG.AUTHOR} url={url} />
|
||||||
|
|
||||||
{/* 推荐文章 */}
|
{/* 推荐文章 */}
|
||||||
<RecommendPosts currentPost={post} recommendPosts={recommendPosts} />
|
<RecommendPosts currentPost={post} recommendPosts={recommendPosts} />
|
||||||
|
|
||||||
{/* 标签列表 */}
|
{/* 标签列表 */}
|
||||||
<section className="md:flex md:justify-between">
|
<section className="md:flex md:justify-between">
|
||||||
{post.tagItems && (
|
{post.tagItems && (
|
||||||
<div className="flex flex-nowrap leading-8 p-1 py-4 overflow-x-auto">
|
<div className="flex flex-nowrap leading-8 p-1 py-4 overflow-x-auto">
|
||||||
<div className="hidden md:block dark:text-gray-300 whitespace-nowrap">
|
<div className="hidden md:block dark:text-gray-300 whitespace-nowrap">
|
||||||
{locale.COMMON.TAGS}:
|
{locale.COMMON.TAGS}:
|
||||||
|
</div>
|
||||||
|
{post.tagItems.map(tag => (
|
||||||
|
<TagItem key={tag.name} tag={tag} />
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
{post.tagItems.map(tag => (
|
)}
|
||||||
<TagItem key={tag.name} tag={tag} />
|
<div>
|
||||||
))}
|
<ShareBar post={post} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
</section>
|
||||||
<div>
|
|
||||||
<ShareBar post={post} />
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<BlogAround prev={prev} next={next} />
|
<BlogAround prev={prev} next={next} />
|
||||||
</>}
|
</>}
|
||||||
|
|
||||||
{/* 评论互动 */}
|
{/* 评论互动 */}
|
||||||
@@ -167,25 +133,3 @@ export default function ArticleDetail (props) {
|
|||||||
|
|
||||||
</div>)
|
</div>)
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapPageUrl = id => {
|
|
||||||
return 'https://www.notion.so/' + id.replace(/-/g, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMediumZoomMargin () {
|
|
||||||
const width = window.innerWidth
|
|
||||||
|
|
||||||
if (width < 500) {
|
|
||||||
return 8
|
|
||||||
} else if (width < 800) {
|
|
||||||
return 20
|
|
||||||
} else if (width < 1280) {
|
|
||||||
return 30
|
|
||||||
} else if (width < 1600) {
|
|
||||||
return 40
|
|
||||||
} else if (width < 1920) {
|
|
||||||
return 48
|
|
||||||
} else {
|
|
||||||
return 72
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,16 +3,10 @@ import { useGlobal } from '@/lib/global'
|
|||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {
|
|
||||||
Code,
|
|
||||||
Collection,
|
|
||||||
CollectionRow,
|
|
||||||
Equation,
|
|
||||||
NotionRenderer
|
|
||||||
} from 'react-notion-x'
|
|
||||||
import Card from './Card'
|
import Card from './Card'
|
||||||
import TagItemMini from './TagItemMini'
|
import TagItemMini from './TagItemMini'
|
||||||
import CONFIG_NEXT from '../config_next'
|
import CONFIG_NEXT from '../config_next'
|
||||||
|
import NotionPage from '@/components/NotionPage'
|
||||||
|
|
||||||
const BlogPostCard = ({ post, showSummary }) => {
|
const BlogPostCard = ({ post, showSummary }) => {
|
||||||
const { locale } = useGlobal()
|
const { locale } = useGlobal()
|
||||||
@@ -26,18 +20,16 @@ const BlogPostCard = ({ post, showSummary }) => {
|
|||||||
<div className="lg:p-8 p-4 flex flex-col w-full">
|
<div className="lg:p-8 p-4 flex flex-col w-full">
|
||||||
<Link href={`${BLOG.SUB_PATH}/article/${post.slug}`} passHref>
|
<Link href={`${BLOG.SUB_PATH}/article/${post.slug}`} passHref>
|
||||||
<a
|
<a
|
||||||
className={`cursor-pointer font-bold hover:underline text-3xl ${
|
className={`cursor-pointer font-bold hover:underline text-3xl ${showPreview ? 'text-center' : ''
|
||||||
showPreview ? 'text-center' : ''
|
} leading-tight text-gray-700 dark:text-gray-100 hover:text-blue-500 dark:hover:text-blue-400`}
|
||||||
} leading-tight text-gray-700 dark:text-gray-100 hover:text-blue-500 dark:hover:text-blue-400`}
|
|
||||||
>
|
>
|
||||||
{post.title}
|
{post.title}
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={`flex mt-2 items-center ${
|
className={`flex mt-2 items-center ${showPreview ? 'justify-center' : 'justify-start'
|
||||||
showPreview ? 'justify-center' : 'justify-start'
|
} flex-wrap dark:text-gray-500 text-gray-400 hover:text-blue-500 dark:hover:text-blue-400 `}
|
||||||
} flex-wrap dark:text-gray-500 text-gray-400 hover:text-blue-500 dark:hover:text-blue-400 `}
|
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
{post.category && (
|
{post.category && (
|
||||||
@@ -87,17 +79,7 @@ const BlogPostCard = ({ post, showSummary }) => {
|
|||||||
|
|
||||||
{showPreview && post?.blockMap && (
|
{showPreview && post?.blockMap && (
|
||||||
<div className="overflow-ellipsis truncate">
|
<div className="overflow-ellipsis truncate">
|
||||||
<NotionRenderer
|
<NotionPage post={post} />
|
||||||
bodyClassName="max-h-full"
|
|
||||||
recordMap={post.blockMap}
|
|
||||||
mapPageUrl={mapPageUrl}
|
|
||||||
components={{
|
|
||||||
equation: Equation,
|
|
||||||
code: Code,
|
|
||||||
collectionRow: CollectionRow,
|
|
||||||
collection: Collection
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -130,8 +112,4 @@ const BlogPostCard = ({ post, showSummary }) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapPageUrl = id => {
|
|
||||||
return 'https://www.notion.so/' + id.replace(/-/g, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
export default BlogPostCard
|
export default BlogPostCard
|
||||||
|
|||||||
Reference in New Issue
Block a user