mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-13 23:16:47 +00:00
game 主题支持PWA
This commit is contained in:
80
components/PWA.js
Normal file
80
components/PWA.js
Normal file
@@ -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
|
||||
// }
|
||||
// }
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 <Layout {...props} />
|
||||
}
|
||||
|
||||
@@ -25,7 +28,9 @@ export async function getStaticProps() {
|
||||
const from = 'index'
|
||||
const props = await getGlobalData({ from })
|
||||
|
||||
props.posts = props.allPages?.filter(page => page.type === 'Post' && page.status === 'Published')
|
||||
props.posts = props.allPages?.filter(
|
||||
page => page.type === 'Post' && page.status === 'Published'
|
||||
)
|
||||
|
||||
// 处理分页
|
||||
if (siteConfig('POST_LIST_STYLE') === 'scroll') {
|
||||
@@ -41,7 +46,11 @@ export async function getStaticProps() {
|
||||
if (post.password && post.password !== '') {
|
||||
continue
|
||||
}
|
||||
post.blockMap = await getPostBlocks(post.id, 'slug', siteConfig('POST_PREVIEW_LINES'))
|
||||
post.blockMap = await getPostBlocks(
|
||||
post.id,
|
||||
'slug',
|
||||
siteConfig('POST_PREVIEW_LINES')
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* 全屏按钮
|
||||
* @returns
|
||||
*/
|
||||
export default function FullScreen() {
|
||||
export default function FullScreenButton() {
|
||||
function toggleFullScreen() {
|
||||
// window.scrollTo(0, 2)
|
||||
document?.querySelector('#game-wrapper')?.scrollIntoView({
|
||||
@@ -37,6 +37,7 @@ export const GameListIndexCombine = ({ posts }) => {
|
||||
// 试图将4合一卡组塞满
|
||||
while (gamesClone?.length > 0 && groupItems.length < 4) {
|
||||
const item = gamesClone.shift()
|
||||
index++
|
||||
if (
|
||||
item.tags?.some(
|
||||
t => t === siteConfig('GAME_RECOMMEND_TAG', 'Recommend', CONFIG)
|
||||
@@ -59,6 +60,7 @@ export const GameListIndexCombine = ({ posts }) => {
|
||||
// 剩余的4合一不满4个的给他放大卡
|
||||
while (groupItems.length > 0) {
|
||||
const item = groupItems.shift()
|
||||
index++
|
||||
components.push(
|
||||
<GameItem key={index++} item={item} isLargeCard={true} />
|
||||
)
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Draggable } from '@/components/Draggable'
|
||||
import { AdSlot } from '@/components/GoogleAdsense'
|
||||
import replaceSearchResult from '@/components/Mark'
|
||||
import NotionPage from '@/components/NotionPage'
|
||||
import { PWA as initialPWA } from '@/components/PWA'
|
||||
import ShareBar from '@/components/ShareBar'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { loadWowJS } from '@/lib/plugins/wow'
|
||||
@@ -17,7 +18,7 @@ import { BlogListPage } from './components/BlogListPage'
|
||||
import { BlogListScroll } from './components/BlogListScroll'
|
||||
import BlogPostBar from './components/BlogPostBar'
|
||||
import { Footer } from './components/Footer'
|
||||
import FullScreen from './components/FullScreen'
|
||||
import FullScreenButton from './components/FullScreenButton'
|
||||
import { GameListIndexCombine } from './components/GameListIndexCombine'
|
||||
import { GameListRelate } from './components/GameListRealate'
|
||||
import { GameListRecent } from './components/GameListRecent'
|
||||
@@ -45,9 +46,6 @@ export const useGameGlobal = () => useContext(ThemeGlobalGame)
|
||||
*/
|
||||
const LayoutBase = props => {
|
||||
const { allNavPages, children } = props
|
||||
|
||||
// const fullWidth = post?.fullWidth ?? false
|
||||
// const { onLoading } = useGlobal()
|
||||
const searchModal = useRef(null)
|
||||
// 在列表中进行实时过滤
|
||||
const [filterKey, setFilterKey] = useState('')
|
||||
@@ -279,13 +277,17 @@ const LayoutArchive = props => {
|
||||
* @returns
|
||||
*/
|
||||
const LayoutSlug = props => {
|
||||
const { post, allNavPages, recommendPosts, lock, validPassword } = props
|
||||
const { post, siteInfo, allNavPages, recommendPosts, lock, validPassword } =
|
||||
props
|
||||
const game = deepClone(post)
|
||||
const [loading, setLoading] = useState(true)
|
||||
// const [url, setUrl] = useState(game?.ext?.href)
|
||||
const relateGames = recommendPosts
|
||||
const randomGames = shuffleArray(deepClone(allNavPages))
|
||||
|
||||
// 初始化可安装应用
|
||||
initialPWA(game, siteInfo)
|
||||
|
||||
// 将当前游戏加入到最近游玩
|
||||
useEffect(() => {
|
||||
// 更新最新游戏
|
||||
@@ -403,8 +405,8 @@ const LayoutSlug = props => {
|
||||
{/* 游戏窗口装饰器 */}
|
||||
{game && !loading && (
|
||||
<div className='game-decorator bg-[#0B0D14] right-0 bottom-0 flex justify-center h-12 md:w-12 z-10 md:absolute'>
|
||||
{/* 加入全屏按钮 */}
|
||||
<FullScreen />
|
||||
{/* 全屏按钮 */}
|
||||
<FullScreenButton />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user