diff --git a/components/PWA.js b/components/PWA.js
new file mode 100644
index 00000000..118a7c18
--- /dev/null
+++ b/components/PWA.js
@@ -0,0 +1,80 @@
+import { compressImage } from '@/lib/notion/mapImage'
+import { isBrowser } from '../lib/utils'
+
+/**
+ * 初始化PWA
+ * @see https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps
+ * @param {*} props
+ * @returns
+ */
+export function PWA(post, siteInfo) {
+ if (!isBrowser || !post) {
+ return
+ }
+ // 将 manifest 数据转换为 JSON 字符串
+ const manifestData = {
+ id: post?.id,
+ name: post?.title + ' | ' + siteInfo.title,
+ short_name: post?.title,
+ description: post?.summary || siteInfo.description,
+ icons: [
+ {
+ src: compressImage(post?.pageCoverThumbnail, 192),
+ type: 'image/png',
+ sizes: '192x192'
+ }
+ ],
+ form_factor: 'phone',
+ start_url: window.location.href,
+ scope: window.location.href,
+ display: 'standalone',
+ background_color: '#181818',
+ theme_color: '#181818'
+ }
+
+ // 删除已有的 manifest link 元素(如果存在)
+ const existingManifest = document.querySelector('link[rel="manifest"]')
+ if (existingManifest) {
+ existingManifest.parentNode.removeChild(existingManifest)
+ }
+
+ // 创建 manifest 元素
+ const manifest = document.createElement('link')
+ manifest.rel = 'manifest'
+
+ // 设置 manifest 的 href 为一个 Blob URL
+ const blobUrl = URL.createObjectURL(
+ new Blob([JSON.stringify(manifestData)], {
+ type: 'application/json'
+ })
+ )
+ // 这里会报错,因为前端收到的事一个转义了双引号的字符串,无法解析成json,不知道怎么解决
+ manifest.href = blobUrl
+
+ // 将 manifest 添加到 head 中
+ document.head.appendChild(manifest)
+
+ // 不要忘记在适当的时候释放 Blob URL,避免内存泄漏
+ // 例如,在页面卸载或不再需要该 Blob URL 时
+ window.addEventListener('unload', () => {
+ URL.revokeObjectURL(blobUrl)
+ })
+}
+
+/**
+ * 截去url结尾的 / , 便于和slug拼接
+ * @param {*} str
+ * @returns
+ */
+// function getRootPath() {
+// const protocol = window.location.protocol
+// const hostname = window.location.hostname
+// const port = window.location.port
+
+// // 如果端口号存在且不是默认的80或443,则包含端口号
+// if (port && port !== '80' && port !== '443') {
+// return protocol + '//' + hostname + ':' + port
+// } else {
+// return protocol + '//' + hostname
+// }
+// }
diff --git a/lib/db/getSiteData.js b/lib/db/getSiteData.js
index 0abe35ef..e7771025 100755
--- a/lib/db/getSiteData.js
+++ b/lib/db/getSiteData.js
@@ -225,7 +225,7 @@ function getCategoryOptions(schema) {
* @param from
* @returns {Promise<{title,description,pageCover,icon}>}
*/
-function getSiteInfo({ collection, block }) {
+function getSiteInfo({ collection, block, NOTION_CONFIG }) {
const title = collection?.name?.[0][0] || BLOG.TITLE
const description = collection?.description
? Object.assign(collection).description[0][0]
@@ -233,19 +233,21 @@ function getSiteInfo({ collection, block }) {
const pageCover = collection?.cover
? mapImgUrl(collection?.cover, block[idToUuid(BLOG.NOTION_PAGE_ID)]?.value)
: BLOG.HOME_BANNER_IMAGE
- let icon = collection?.icon
- ? mapImgUrl(collection?.icon, collection, 'collection')
- : BLOG.AVATAR
-
// 用户头像压缩一下
- icon = compressImage(icon)
+ let icon = compressImage(
+ collection?.icon
+ ? mapImgUrl(collection?.icon, collection, 'collection')
+ : BLOG.AVATAR
+ )
+ // 站点网址
+ const link = NOTION_CONFIG?.LINK || BLOG.LINK
- // 站点图标不能是emoji情
+ // 站点图标不能是emoji
const emojiPattern = /\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F]/g
if (!icon || emojiPattern.test(icon)) {
icon = BLOG.AVATAR
}
- return { title, description, pageCover, icon }
+ return { title, description, pageCover, icon, link }
}
/**
@@ -355,7 +357,6 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) {
return EmptyData(pageId)
}
const collection = Object.values(pageRecordMap.collection)[0]?.value || {}
- const siteInfo = getSiteInfo({ collection, block })
const collectionId = rawMetadata?.collection_id
const collectionQuery = pageRecordMap.collection_query
const collectionView = pageRecordMap.collection_view
@@ -422,6 +423,11 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) {
// 文章计数
let postCount = 0
+ // 站点配置优先读取配置表格,否则读取blog.config.js 文件
+ const NOTION_CONFIG = (await getConfigMapFromConfigPage(collectionData)) || {}
+
+ const siteInfo = getSiteInfo({ collection, block })
+
// 查找所有的Post和Page
const allPages = collectionData.filter(post => {
if (post?.type === 'Post' && post.status === 'Published') {
@@ -435,9 +441,6 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) {
)
})
- // 站点配置优先读取配置表格,否则读取blog.config.js 文件
- const NOTION_CONFIG = (await getConfigMapFromConfigPage(collectionData)) || {}
-
// Sort by date
if (BLOG.POSTS_SORT_BY === 'date') {
allPages.sort((a, b) => {
diff --git a/lib/utils/index.js b/lib/utils/index.js
index 48592fc1..b0652494 100644
--- a/lib/utils/index.js
+++ b/lib/utils/index.js
@@ -1,5 +1,5 @@
// 封装异步加载资源的方法
-import { memo } from 'react';
+import { memo } from 'react'
/**
* 判断是否客户端
@@ -9,18 +9,18 @@ export const isBrowser = typeof window !== 'undefined'
/**
* 打乱数组
- * @param {*} array
- * @returns
+ * @param {*} array
+ * @returns
*/
-export const shuffleArray = (array) => {
- if (!array) {
- return []
- }
- for (let i = array.length - 1; i > 0; i--) {
- const j = Math.floor(Math.random() * (i + 1));
- [array[i], array[j]] = [array[j], array[i]];
- }
- return array;
+export const shuffleArray = array => {
+ if (!array) {
+ return []
+ }
+ for (let i = array.length - 1; i > 0; i--) {
+ const j = Math.floor(Math.random() * (i + 1))
+ ;[array[i], array[j]] = [array[j], array[i]]
+ }
+ return array
}
/**
diff --git a/next.config.js b/next.config.js
index fd0130ec..c4e9d8e4 100644
--- a/next.config.js
+++ b/next.config.js
@@ -96,19 +96,27 @@ module.exports = withBundleAnalyzer({
if (!isServer) {
console.log('[加载主题]', path.resolve(__dirname, 'themes', THEME))
}
- config.resolve.alias['@theme-components'] = path.resolve(__dirname, 'themes', THEME)
+ config.resolve.alias['@theme-components'] = path.resolve(
+ __dirname,
+ 'themes',
+ THEME
+ )
return config
},
experimental: {
scrollRestoration: true
},
- exportPathMap: async function (defaultPathMap, { dev, dir, outDir, distDir, buildId }) {
- // 导出时 忽略/pages/sitemap.xml.js , 否则报错getServerSideProps
+ exportPathMap: async function (
+ defaultPathMap,
+ { dev, dir, outDir, distDir, buildId }
+ ) {
+ // export 静态导出时 忽略/pages/sitemap.xml.js , 否则和getServerSideProps这个动态文件冲突
const pages = { ...defaultPathMap }
delete pages['/sitemap.xml']
return pages
},
- publicRuntimeConfig: { // 这里的配置既可以服务端获取到,也可以在浏览器端获取到
+ publicRuntimeConfig: {
+ // 这里的配置既可以服务端获取到,也可以在浏览器端获取到
NODE_ENV_API: process.env.NODE_ENV_API || 'prod',
THEMES: themes
}
diff --git a/pages/index.js b/pages/index.js
index 286b1b12..9f2dfb91 100644
--- a/pages/index.js
+++ b/pages/index.js
@@ -13,7 +13,10 @@ import { useRouter } from 'next/router'
*/
const Index = props => {
// 根据页面路径加载不同Layout文件
- const Layout = getLayoutByTheme({ theme: siteConfig('THEME'), router: useRouter() })
+ const Layout = getLayoutByTheme({
+ theme: siteConfig('THEME'),
+ router: useRouter()
+ })
return