mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-14 07:26:52 +00:00
theme-game
This commit is contained in:
@@ -432,7 +432,8 @@ const BLOG = {
|
||||
category: process.env.NEXT_PUBLIC_NOTION_PROPERTY_CATEGORY || 'category',
|
||||
date: process.env.NEXT_PUBLIC_NOTION_PROPERTY_DATE || 'date',
|
||||
tags: process.env.NEXT_PUBLIC_NOTION_PROPERTY_TAGS || 'tags',
|
||||
icon: process.env.NEXT_PUBLIC_NOTION_PROPERTY_ICON || 'icon'
|
||||
icon: process.env.NEXT_PUBLIC_NOTION_PROPERTY_ICON || 'icon',
|
||||
ext: process.env.NEXT_PUBLIC_NOTION_PROPERTY_EXT || 'ext' // 扩展字段,存放json-string,用于复杂业务
|
||||
},
|
||||
|
||||
// RSS订阅
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useRef, useEffect, useState } from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
|
||||
/**
|
||||
* 可拖拽组件
|
||||
*/
|
||||
|
||||
export const Draggable = (props) => {
|
||||
const { children } = props
|
||||
export const Draggable = props => {
|
||||
const { children, stick } = props
|
||||
const draggableRef = useRef(null)
|
||||
const rafRef = useRef(null)
|
||||
const [moving, setMoving] = useState(false)
|
||||
@@ -14,8 +14,10 @@ export const Draggable = (props) => {
|
||||
const draggableElements = document.getElementsByClassName('draggable')
|
||||
|
||||
// 标准化鼠标事件对象
|
||||
function e(event) { // 定义事件对象标准化函数
|
||||
if (!event) { // 兼容IE浏览器
|
||||
function e(event) {
|
||||
// 定义事件对象标准化函数
|
||||
if (!event) {
|
||||
// 兼容IE浏览器
|
||||
event = window.event
|
||||
event.target = event.srcElement
|
||||
event.layerX = event.offsetX
|
||||
@@ -40,9 +42,10 @@ export const Draggable = (props) => {
|
||||
document.onmousedown = start
|
||||
document.ontouchstart = start
|
||||
|
||||
function start (event) { // 按下鼠标时,初始化处理
|
||||
function start(event) {
|
||||
// 按下鼠标时,初始化处理
|
||||
if (!draggableElements) return
|
||||
event = e(event)// 获取标准事件对象
|
||||
event = e(event) // 获取标准事件对象
|
||||
|
||||
for (const drag of draggableElements) {
|
||||
// 判断鼠标点击的区域是否是拖拽框内
|
||||
@@ -60,19 +63,20 @@ export const Draggable = (props) => {
|
||||
offsetX = event.mx - currentObj.offsetLeft
|
||||
offsetY = event.my - currentObj.offsetTop
|
||||
|
||||
document.onmousemove = move// 注册鼠标移动事件处理函数
|
||||
document.onmousemove = move // 注册鼠标移动事件处理函数
|
||||
document.ontouchmove = move
|
||||
document.onmouseup = stop// 注册松开鼠标事件处理函数
|
||||
document.onmouseup = stop // 注册松开鼠标事件处理函数
|
||||
document.ontouchend = stop
|
||||
}
|
||||
}
|
||||
|
||||
function move(event) { // 鼠标移动处理函数
|
||||
function move(event) {
|
||||
// 鼠标移动处理函数
|
||||
event = e(event)
|
||||
rafRef.current = requestAnimationFrame(() => updatePosition(event))
|
||||
}
|
||||
|
||||
const stop = (event) => {
|
||||
const stop = event => {
|
||||
event = e(event)
|
||||
document.documentElement.style.overflow = 'auto' // 恢复默认的滚动行为
|
||||
cancelAnimationFrame(rafRef.current)
|
||||
@@ -80,7 +84,7 @@ export const Draggable = (props) => {
|
||||
currentObj = document.ontouchmove = document.ontouchend = document.onmousemove = document.onmouseup = null
|
||||
}
|
||||
|
||||
const updatePosition = (event) => {
|
||||
const updatePosition = event => {
|
||||
if (currentObj) {
|
||||
const left = event.mx - offsetX
|
||||
const top = event.my - offsetY
|
||||
@@ -120,15 +124,18 @@ export const Draggable = (props) => {
|
||||
if (offsetTop < 0) {
|
||||
drag.firstElementChild.style.top = 0
|
||||
}
|
||||
if (offsetTop > (clientHeight - offsetHeight)) {
|
||||
if (offsetTop > clientHeight - offsetHeight) {
|
||||
drag.firstElementChild.style.top = clientHeight - offsetHeight + 'px'
|
||||
}
|
||||
if (offsetLeft < 0) {
|
||||
drag.firstElementChild.style.left = 0
|
||||
}
|
||||
if (offsetLeft > (clientWidth - offsetWidth)) {
|
||||
if (offsetLeft > clientWidth - offsetWidth) {
|
||||
drag.firstElementChild.style.left = clientWidth - offsetWidth + 'px'
|
||||
}
|
||||
if (stick === 'left') {
|
||||
drag.firstElementChild.style.left = 0 + 'px'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,9 +149,11 @@ export const Draggable = (props) => {
|
||||
}
|
||||
}, [])
|
||||
|
||||
return <div className={`draggable ${moving ? 'cursor-grabbing' : 'cursor-grab'} select-none`} ref={draggableRef}>
|
||||
{children}
|
||||
</div>
|
||||
return (
|
||||
<div className={`draggable ${moving ? 'cursor-grabbing' : 'cursor-grab'} select-none`} ref={draggableRef}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Draggable.defaultProps = { left: 0, top: 0 }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getTextContent, getDateValue } from 'notion-utils'
|
||||
import { NotionAPI } from 'notion-client'
|
||||
import BLOG from '@/blog.config'
|
||||
import { NotionAPI } from 'notion-client'
|
||||
import { getDateValue, getTextContent } from 'notion-utils'
|
||||
import formatDate from '../utils/formatDate'
|
||||
// import { createHash } from 'crypto'
|
||||
import md5 from 'js-md5'
|
||||
@@ -49,8 +49,7 @@ export default async function getPageProperties(id, value, schema, authToken, ta
|
||||
if (rawUsers[i][0][1]) {
|
||||
const userId = rawUsers[i][0]
|
||||
const res = await api.getUsers(userId)
|
||||
const resValue =
|
||||
res?.recordMapWithRoles?.notion_user?.[userId[1]]?.value
|
||||
const resValue = res?.recordMapWithRoles?.notion_user?.[userId[1]]?.value
|
||||
const user = {
|
||||
id: resValue?.id,
|
||||
first_name: resValue?.given_name,
|
||||
@@ -93,16 +92,17 @@ export default async function getPageProperties(id, value, schema, authToken, ta
|
||||
properties.pageIcon = mapImgUrl(value?.format?.page_icon, value) ?? ''
|
||||
properties.pageCover = mapImgUrl(value?.format?.page_cover, value) ?? ''
|
||||
properties.pageCoverThumbnail = mapImgUrl(value?.format?.page_cover, value, 'block', 'pageCoverThumbnail') ?? ''
|
||||
|
||||
properties.ext = converToJSON(properties?.ext)
|
||||
properties.content = value.content ?? []
|
||||
properties.tagItems = properties?.tags?.map(tag => {
|
||||
return { name: tag, color: tagOptions?.find(t => t.value === tag)?.color || 'gray' }
|
||||
}) || []
|
||||
properties.tagItems =
|
||||
properties?.tags?.map(tag => {
|
||||
return { name: tag, color: tagOptions?.find(t => t.value === tag)?.color || 'gray' }
|
||||
}) || []
|
||||
delete properties.content
|
||||
|
||||
// 处理URL
|
||||
if (properties.type === 'Post') {
|
||||
properties.slug = (BLOG.POST_URL_PREFIX) ? generateCustomizeUrl(properties) : (properties.slug ?? properties.id)
|
||||
properties.slug = BLOG.POST_URL_PREFIX ? generateCustomizeUrl(properties) : properties.slug ?? properties.id
|
||||
} else if (properties.type === 'Page') {
|
||||
properties.slug = properties.slug ?? properties.id
|
||||
} else if (properties.type === 'Menu' || properties.type === 'SubMenu') {
|
||||
@@ -122,6 +122,24 @@ export default async function getPageProperties(id, value, schema, authToken, ta
|
||||
return properties
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串转json
|
||||
* @param {*} str
|
||||
* @returns
|
||||
*/
|
||||
function converToJSON(str) {
|
||||
if (!str) {
|
||||
return {}
|
||||
}
|
||||
// 使用正则表达式去除空格和换行符
|
||||
try {
|
||||
return JSON.parse(str.replace(/\s/g, ''))
|
||||
} catch (error) {
|
||||
console.warn('无效JSON', str)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 映射用户自定义表头
|
||||
*/
|
||||
@@ -164,7 +182,7 @@ function generateCustomizeUrl(postProperties) {
|
||||
const formatPostCreatedDate = new Date(postProperties?.publishDay)
|
||||
fullPrefix += String(formatPostCreatedDate.getUTCDate()).padStart(2, 0)
|
||||
} else if (pattern === '%slug%') {
|
||||
fullPrefix += (postProperties.slug ?? postProperties.id)
|
||||
fullPrefix += postProperties.slug ?? postProperties.id
|
||||
} else if (!pattern.includes('%')) {
|
||||
fullPrefix += pattern
|
||||
} else {
|
||||
@@ -180,5 +198,5 @@ function generateCustomizeUrl(postProperties) {
|
||||
if (fullPrefix.endsWith('/')) {
|
||||
fullPrefix = fullPrefix.substring(0, fullPrefix.length - 1) // 去掉尾部部的"/"
|
||||
}
|
||||
return `${fullPrefix}/${(postProperties.slug ?? postProperties.id)}`
|
||||
return `${fullPrefix}/${postProperties.slug ?? postProperties.id}`
|
||||
}
|
||||
|
||||
48
public/games-external/common/index.htm
Normal file
48
public/games-external/common/index.htm
Normal file
@@ -0,0 +1,48 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Full Screen iFrame</title>
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#myIframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
/* 可选:移除边框 */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- <div style="position: absolute;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
background: white;">
|
||||
<button onclick="toggleFullScreen()">Toggle Full Screen</button>
|
||||
</div> -->
|
||||
|
||||
<iframe id="myIframe" allowfullscreen="allowfullscreen" allow="autoplay" scrolling="no"></iframe>
|
||||
|
||||
|
||||
<!-- https://letsplay247.github.io/cz.html?n=space-wars-battleground -->
|
||||
<script>
|
||||
var myParam = location.search.split('n=')[1]
|
||||
document.getElementById("myIframe").src = myParam;
|
||||
</script>
|
||||
<script src="/js/fullscreen.js" type="text/javascript"></script>
|
||||
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
||||
43
public/games-external/crazy/index.htm
Normal file
43
public/games-external/crazy/index.htm
Normal file
@@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Full Screen iFrame</title>
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#myIframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
/* 可选:移除边框 */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- <div style="display: block;background-color: red;">
|
||||
<button onclick="toggleFullScreen()">Toggle Full Screen</button>
|
||||
</div> -->
|
||||
<iframe id="myIframe" allowfullscreen="allowfullscreen" allow="autoplay" scrolling="no"></iframe>
|
||||
|
||||
|
||||
<!-- https://letsplay247.github.io/cz.html?n=space-wars-battleground -->
|
||||
<script>
|
||||
var myParam = location.search.split('n=')[1]
|
||||
document.getElementById("myIframe").src = "https://games.crazygames.com/en_US/" + myParam + "/index.html";
|
||||
</script>
|
||||
<script src="/js/fullscreen.js" type="text/javascript"></script>
|
||||
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
||||
32
public/js/fullscreen.js
Normal file
32
public/js/fullscreen.js
Normal file
@@ -0,0 +1,32 @@
|
||||
window.toggleFullScreen = toggleFullScreen
|
||||
function toggleFullScreen() {
|
||||
var iframe = document.getElementById('myIframe')
|
||||
|
||||
if (!document.fullscreenElement) {
|
||||
if (iframe.requestFullscreen) {
|
||||
iframe.requestFullscreen()
|
||||
} else if (iframe.mozRequestFullScreen) {
|
||||
/* Firefox */
|
||||
iframe.mozRequestFullScreen()
|
||||
} else if (iframe.webkitRequestFullscreen) {
|
||||
/* Chrome, Safari and Opera */
|
||||
iframe.webkitRequestFullscreen()
|
||||
} else if (iframe.msRequestFullscreen) {
|
||||
/* IE/Edge */
|
||||
iframe.msRequestFullscreen()
|
||||
}
|
||||
} else {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen()
|
||||
} else if (document.mozCancelFullScreen) {
|
||||
/* Firefox */
|
||||
document.mozCancelFullScreen()
|
||||
} else if (document.webkitExitFullscreen) {
|
||||
/* Chrome, Safari and Opera */
|
||||
document.webkitExitFullscreen()
|
||||
} else if (document.msExitFullscreen) {
|
||||
/* IE/Edge */
|
||||
document.msExitFullscreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,15 +2,24 @@ import dynamic from 'next/dynamic'
|
||||
|
||||
const NotionPage = dynamic(() => import('@/components/NotionPage'))
|
||||
|
||||
/**
|
||||
* 公告
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
const Announcement = ({ notice, className }) => {
|
||||
if (notice?.blockMap) {
|
||||
return <div className={className}>
|
||||
<section id='announcement-wrapper' className='mb-10'>
|
||||
{notice && (<div id="announcement-content">
|
||||
<NotionPage post={notice} className='text-center ' />
|
||||
</div>)}
|
||||
</section>
|
||||
</div>
|
||||
return (
|
||||
<div className={className}>
|
||||
<section id='announcement-wrapper' className='mb-10'>
|
||||
{notice && (
|
||||
<div id='announcement-content'>
|
||||
<NotionPage post={notice} />
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { useRouter } from 'next/router'
|
||||
import Link from 'next/link'
|
||||
import BlogPost from './BlogPost'
|
||||
import { useRouter } from 'next/router'
|
||||
import { GameListIndexCombine } from './GameListIndexCombine'
|
||||
|
||||
export const BlogListPage = props => {
|
||||
const { page = 1, posts, postCount } = props
|
||||
@@ -14,37 +13,39 @@ export const BlogListPage = props => {
|
||||
|
||||
const showPrev = currentPage > 1
|
||||
const showNext = currentPage < totalPage && posts?.length > 0
|
||||
const pagePrefix = router.asPath.split('?')[0].replace(/\/page\/[1-9]\d*/, '').replace(/\/$/, '')
|
||||
const pagePrefix = router.asPath
|
||||
.split('?')[0]
|
||||
.replace(/\/page\/[1-9]\d*/, '')
|
||||
.replace(/\/$/, '')
|
||||
|
||||
return (
|
||||
<div className="w-full md:pr-12 my-6">
|
||||
<div className='w-full md:pr-12 my-6'>
|
||||
<div id='posts-wrapper'>
|
||||
<GameListIndexCombine {...props} />
|
||||
</div>
|
||||
|
||||
<div id="posts-wrapper">
|
||||
{posts?.map(post => (
|
||||
<BlogPost key={post.id} post={post}/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between text-xs">
|
||||
<Link
|
||||
href={{ pathname: currentPage - 1 === 1 ? `${pagePrefix}/` : `${pagePrefix}/page/${currentPage - 1}`, query: router.query.s ? { s: router.query.s } : {} }}
|
||||
className={`${showPrev ? ' ' : ' invisible block pointer-events-none '}no-underline py-2 px-3 rounded`}>
|
||||
|
||||
<button rel="prev" className="block cursor-pointer">
|
||||
← {locale.PAGINATION.PREV}
|
||||
</button>
|
||||
|
||||
</Link>
|
||||
<Link
|
||||
href={{ pathname: `${pagePrefix}/page/${currentPage + 1}`, query: router.query.s ? { s: router.query.s } : {} }}
|
||||
className={`${showNext ? ' ' : 'invisible pointer-events-none '} no-underline py-2 px-3 rounded`}>
|
||||
|
||||
<button rel="next" className="block cursor-pointer">
|
||||
{locale.PAGINATION.NEXT} →
|
||||
</button>
|
||||
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex justify-between text-xs'>
|
||||
<Link
|
||||
href={{
|
||||
pathname: currentPage - 1 === 1 ? `${pagePrefix}/` : `${pagePrefix}/page/${currentPage - 1}`,
|
||||
query: router.query.s ? { s: router.query.s } : {}
|
||||
}}
|
||||
className={`${showPrev ? ' ' : ' invisible block pointer-events-none '}no-underline py-2 px-3 rounded`}>
|
||||
<button rel='prev' className='block cursor-pointer'>
|
||||
← {locale.PAGINATION.PREV}
|
||||
</button>
|
||||
</Link>
|
||||
<Link
|
||||
href={{
|
||||
pathname: `${pagePrefix}/page/${currentPage + 1}`,
|
||||
query: router.query.s ? { s: router.query.s } : {}
|
||||
}}
|
||||
className={`${showNext ? ' ' : 'invisible pointer-events-none '} no-underline py-2 px-3 rounded`}>
|
||||
<button rel='next' className='block cursor-pointer'>
|
||||
{locale.PAGINATION.NEXT} →
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
import { useRef, useEffect, useState } from 'react'
|
||||
|
||||
/**
|
||||
* 可拖拽组件
|
||||
*/
|
||||
export const Draggable = (props) => {
|
||||
const { children,stick } = props
|
||||
const draggableRef = useRef(null)
|
||||
const rafRef = useRef(null)
|
||||
const [moving, setMoving] = useState(false)
|
||||
let currentObj, offsetX, offsetY
|
||||
|
||||
useEffect(() => {
|
||||
const draggableElements = document.getElementsByClassName('draggable')
|
||||
|
||||
// 标准化鼠标事件对象
|
||||
function e(event) { // 定义事件对象标准化函数
|
||||
if (!event) { // 兼容IE浏览器
|
||||
event = window.event
|
||||
event.target = event.srcElement
|
||||
event.layerX = event.offsetX
|
||||
event.layerY = event.offsetY
|
||||
}
|
||||
// 移动端
|
||||
if (event.type === 'touchstart' || event.type === 'touchmove') {
|
||||
event.clientX = event.touches[0].clientX
|
||||
event.clientY = event.touches[0].clientY
|
||||
}
|
||||
|
||||
event.mx = event.pageX || event.clientX + document.body.scrollLeft
|
||||
// 计算鼠标指针的x轴距离
|
||||
event.my = event.pageY || event.clientY + document.body.scrollTop
|
||||
// 计算鼠标指针的y轴距离
|
||||
|
||||
return event // 返回标准化的事件对象
|
||||
}
|
||||
|
||||
// 定义鼠标事件处理函数
|
||||
// document.pointerdown = start
|
||||
document.onmousedown = start
|
||||
document.ontouchstart = start
|
||||
|
||||
function start (event) { // 按下鼠标时,初始化处理
|
||||
if (!draggableElements) return
|
||||
event = e(event)// 获取标准事件对象
|
||||
|
||||
for (const drag of draggableElements) {
|
||||
// 判断鼠标点击的区域是否是拖拽框内
|
||||
if (inDragBox(event, drag)) {
|
||||
currentObj = drag.firstElementChild
|
||||
}
|
||||
}
|
||||
if (currentObj) {
|
||||
if (event.type === 'touchstart') {
|
||||
event.preventDefault() // 阻止默认的滚动行为
|
||||
document.documentElement.style.overflow = 'hidden' // 防止页面一起滚动
|
||||
}
|
||||
|
||||
setMoving(true)
|
||||
offsetX = event.mx - currentObj.offsetLeft
|
||||
offsetY = event.my - currentObj.offsetTop
|
||||
|
||||
document.onmousemove = move// 注册鼠标移动事件处理函数
|
||||
document.ontouchmove = move
|
||||
document.onmouseup = stop// 注册松开鼠标事件处理函数
|
||||
document.ontouchend = stop
|
||||
}
|
||||
}
|
||||
|
||||
function move(event) { // 鼠标移动处理函数
|
||||
event = e(event)
|
||||
rafRef.current = requestAnimationFrame(() => updatePosition(event))
|
||||
}
|
||||
|
||||
const stop = (event) => {
|
||||
event = e(event)
|
||||
document.documentElement.style.overflow = 'auto' // 恢复默认的滚动行为
|
||||
cancelAnimationFrame(rafRef.current)
|
||||
setMoving(false)
|
||||
currentObj = document.ontouchmove = document.ontouchend = document.onmousemove = document.onmouseup = null
|
||||
}
|
||||
|
||||
const updatePosition = (event) => {
|
||||
if (currentObj) {
|
||||
const left = event.mx - offsetX
|
||||
const top = event.my - offsetY
|
||||
currentObj.style.left = left + 'px'
|
||||
currentObj.style.top = top + 'px'
|
||||
checkInWindow()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 鼠标是否在可拖拽区域内
|
||||
* @param {*} event
|
||||
* @returns
|
||||
*/
|
||||
function inDragBox(event, drag) {
|
||||
const { clientX, clientY } = event // 鼠标位置
|
||||
const { offsetHeight, offsetWidth, offsetTop, offsetLeft } = drag.firstElementChild // 窗口位置
|
||||
const horizontal = clientX > offsetLeft && clientX < offsetLeft + offsetWidth
|
||||
const vertical = clientY > offsetTop && clientY < offsetTop + offsetHeight
|
||||
|
||||
if (horizontal && vertical) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 若超出窗口则吸附。
|
||||
*/
|
||||
function checkInWindow() {
|
||||
// 检查是否悬浮在窗口内
|
||||
for (const drag of draggableElements) {
|
||||
// 判断鼠标点击的区域是否是拖拽框内
|
||||
const { offsetHeight, offsetWidth, offsetTop, offsetLeft } = drag.firstElementChild
|
||||
const { clientHeight, clientWidth } = document.documentElement
|
||||
if (offsetTop < 0) {
|
||||
drag.firstElementChild.style.top = 0
|
||||
}
|
||||
if (offsetTop > (clientHeight - offsetHeight)) {
|
||||
drag.firstElementChild.style.top = clientHeight - offsetHeight + 'px'
|
||||
}
|
||||
if (offsetLeft < 0) {
|
||||
drag.firstElementChild.style.left = 0
|
||||
}
|
||||
if (offsetLeft > (clientWidth - offsetWidth)) {
|
||||
drag.firstElementChild.style.left = clientWidth - offsetWidth + 'px'
|
||||
}
|
||||
if(stick==='left'){
|
||||
drag.firstElementChild.style.left = 0 + 'px'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('resize', checkInWindow)
|
||||
|
||||
return () => {
|
||||
return () => {
|
||||
window.removeEventListener('resize', checkInWindow)
|
||||
cancelAnimationFrame(rafRef.current)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
return <div className={`draggable ${moving ? 'cursor-grabbing' : 'cursor-grab'} select-none`} ref={draggableRef}>
|
||||
{children}
|
||||
</div>
|
||||
}
|
||||
|
||||
Draggable.defaultProps = { left: 0, top: 0 }
|
||||
@@ -1,8 +1,7 @@
|
||||
import DarkModeButton from '@/components/DarkModeButton'
|
||||
import Vercel from '@/components/Vercel'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
|
||||
export const Footer = (props) => {
|
||||
export const Footer = props => {
|
||||
const d = new Date()
|
||||
const currentYear = d.getFullYear()
|
||||
const { post } = props
|
||||
@@ -10,20 +9,27 @@ export const Footer = (props) => {
|
||||
const since = siteConfig('SINCE')
|
||||
const copyrightDate = parseInt(since) < currentYear ? since + '-' + currentYear : currentYear
|
||||
|
||||
return <footer
|
||||
className={`z-10 relative mt-6 flex-shrink-0 m-auto w-full text-gray-500 dark:text-gray-400 transition-all ${
|
||||
!fullWidth ? 'max-w-2xl px-4' : 'px-4 md:px-24'
|
||||
}`}
|
||||
>
|
||||
<DarkModeButton className='text-center py-4'/>
|
||||
<hr className="border-gray-200 dark:border-gray-600" />
|
||||
<div className="my-4 text-sm leading-6">
|
||||
<div className="flex align-baseline justify-between flex-wrap">
|
||||
<p>
|
||||
© {siteConfig('AUTHOR')} {copyrightDate}
|
||||
</p>
|
||||
<Vercel />
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
return (
|
||||
<footer
|
||||
className={`z-10 relative mt-6 flex-shrink-0 m-auto w-full text-gray-500 dark:text-gray-400 transition-all ${
|
||||
!fullWidth ? 'max-w-2xl px-4' : 'px-4 md:px-24'
|
||||
}`}>
|
||||
<DarkModeButton className='text-center py-4' />
|
||||
<hr className='border-gray-200 dark:border-gray-600' />
|
||||
<div className='my-4 text-sm leading-6'>
|
||||
<div className='flex align-baseline justify-between flex-wrap'>
|
||||
<span className='dark:text-gray-200 no-underline ml-4'>
|
||||
Powered by
|
||||
<a href='https://github.com/tangly1024/NotionNext' className=' hover:underline'>
|
||||
{' '}
|
||||
NotionNext {siteConfig('VERSION')}{' '}
|
||||
</a>
|
||||
</span>
|
||||
<p>
|
||||
© {siteConfig('TITLE')} {copyrightDate}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
/* eslint-disable @next/next/no-img-element */
|
||||
|
||||
import Image from 'next/image'
|
||||
|
||||
/**
|
||||
* 全屏按钮
|
||||
* @returns
|
||||
@@ -14,26 +12,20 @@ export default function FullScreen() {
|
||||
block: 'end',
|
||||
inline: 'nearest'
|
||||
})
|
||||
// console.log(document?.getElementById('game-wrapper')?.contentWindow)
|
||||
document?.getElementById('game-wrapper')?.contentWindow?.toggleFullScreen()
|
||||
document?.getElementById('game-wrapper')?.contentWindow?.toggleFullScreen &&
|
||||
document?.getElementById('game-wrapper')?.contentWindow?.toggleFullScreen()
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="group text-white w-full justify-center items-center flex rounded-lg m-2 md:m-0 p-2 hover:bg-gray-700 bg-[#1F2030] md:rounded-none md:bg-none"
|
||||
onClick={toggleFullScreen}
|
||||
>
|
||||
<Image
|
||||
width={18}
|
||||
height={18}
|
||||
src="/svg/fullscreen-alt.svg"
|
||||
alt="full screen"
|
||||
title="full screen"
|
||||
className="cursor-pointer group-hover:scale-125 transition-all duration-150 "
|
||||
className='group text-white w-full justify-center items-center flex rounded-lg m-2 md:m-0 p-2 hover:bg-gray-700 bg-[#1F2030] md:rounded-none md:bg-none'
|
||||
onClick={toggleFullScreen}>
|
||||
<i
|
||||
alt='full screen'
|
||||
title='full screen'
|
||||
className='cursor-pointer fas fa-expand group-hover:scale-125 transition-all duration-150 '
|
||||
/>
|
||||
<span className="h-full flex mx-2 md:hidden items-center select-none">
|
||||
FullScreen
|
||||
</span>
|
||||
<span className='h-full flex mx-2 md:hidden items-center select-none'>FullScreen</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,27 +1,23 @@
|
||||
/* eslint-disable @next/next/no-img-element */
|
||||
import { AdSlot } from '@/components/GoogleAdsense'
|
||||
import { deepClone } from '@/lib/utils'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { checkContainHttp, deepClone, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import Link from 'next/link'
|
||||
import { useState } from 'react'
|
||||
import CONFIG from '../config'
|
||||
|
||||
/**
|
||||
* 游戏列表
|
||||
* @returns
|
||||
*/
|
||||
export const GameListIndexCombine = ({ games }) => {
|
||||
const gamesClone = deepClone(games)
|
||||
|
||||
gamesClone?.sort((a, b) => {
|
||||
const orderA = a.order || 999
|
||||
const orderB = b.order || 999
|
||||
|
||||
return orderA - orderB
|
||||
})
|
||||
export const GameListIndexCombine = ({ posts }) => {
|
||||
const gamesClone = deepClone(posts)
|
||||
|
||||
// 构造一个List<Component>
|
||||
const components = []
|
||||
|
||||
// 根据序号随机大小;或根据game.recommend 决定
|
||||
const recommend = true
|
||||
const recommend = siteConfig('GAME_INDEX_EXPAND_RECOMMEND', true, CONFIG)
|
||||
|
||||
let index = 0
|
||||
// 无限循环
|
||||
@@ -40,7 +36,7 @@ export const GameListIndexCombine = ({ games }) => {
|
||||
// 试图将4合一卡组塞满
|
||||
while (gamesClone?.length > 0 && groupItems.length < 4) {
|
||||
const item = gamesClone.shift()
|
||||
if (item.recommend) {
|
||||
if (item.tags?.some(t => t === siteConfig('GAME_RECOMMEND_TAG', 'Recommend', CONFIG))) {
|
||||
components.push(<GameItem key={index} item={item} isLargeCard={true} />)
|
||||
break
|
||||
} else {
|
||||
@@ -122,11 +118,14 @@ const GameItemGroup = ({ items }) => {
|
||||
* @returns
|
||||
*/
|
||||
const GameItem = ({ item, isLargeCard }) => {
|
||||
const { id, title, img, video } = item
|
||||
const { title } = item
|
||||
const img = item.pageCoverThumbnail
|
||||
const [showType, setShowType] = useState('img') // img or video
|
||||
const url = checkContainHttp(item.slug) ? sliceUrlFromHttp(item.slug) : `${siteConfig('SUB_PATH', '')}/${item.slug}`
|
||||
const video = item?.ext?.video
|
||||
return (
|
||||
<a
|
||||
href={`/game/${id}`}
|
||||
<Link
|
||||
href={`${url}`}
|
||||
onMouseOver={() => {
|
||||
setShowType('video')
|
||||
}}
|
||||
@@ -159,6 +158,6 @@ const GameItem = ({ item, isLargeCard }) => {
|
||||
src={img}
|
||||
alt={title}
|
||||
/>
|
||||
</a>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/* eslint-disable @next/next/no-img-element */
|
||||
import { deepClone } from '@/lib/utils'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { checkContainHttp, deepClone, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import Link from 'next/link'
|
||||
import { useState } from 'react'
|
||||
|
||||
/**
|
||||
@@ -38,12 +40,14 @@ export const GameListNormal = ({ games, maxCount = 18 }) => {
|
||||
* @returns
|
||||
*/
|
||||
const GameItem = ({ item }) => {
|
||||
const { id, title, img, video } = item
|
||||
const { title, img } = item
|
||||
const [showType, setShowType] = useState('img') // img or video
|
||||
const url = checkContainHttp(item.slug) ? sliceUrlFromHttp(item.slug) : `${siteConfig('SUB_PATH', '')}/${item.slug}`
|
||||
const video = item?.ext?.video
|
||||
|
||||
return (
|
||||
<a
|
||||
href={`/game/${id}`}
|
||||
<Link
|
||||
href={`${url}`}
|
||||
onMouseOver={() => {
|
||||
setShowType('video')
|
||||
}}
|
||||
@@ -64,6 +68,6 @@ const GameItem = ({ item }) => {
|
||||
</video>
|
||||
)}
|
||||
<img className='w-full h-full absolute object-cover' src={img} alt={title} />
|
||||
</a>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
/* eslint-disable @next/next/no-img-element */
|
||||
import { deepClone } from '@/lib/utils'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { checkContainHttp, deepClone, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import Link from 'next/link'
|
||||
import { useState } from 'react'
|
||||
|
||||
/**
|
||||
* 游戏列表- 关联游戏,在详情页展示
|
||||
* @returns
|
||||
*/
|
||||
export const GameListRelate = ({ games }) => {
|
||||
const gamesClone = deepClone(games)
|
||||
export const GameListRelate = ({ posts }) => {
|
||||
const gamesClone = deepClone(posts)
|
||||
|
||||
// 构造一个List<Component>
|
||||
const components = []
|
||||
@@ -24,7 +26,7 @@ export const GameListRelate = ({ games }) => {
|
||||
|
||||
return (
|
||||
<div className='game-list-wrapper w-full max-w-full overflow-x-auto'>
|
||||
<div className='game-grid grid grid-flow-col gap-2'>
|
||||
<div className='game-grid grid grid-flow-col justify-start gap-2'>
|
||||
{components?.map((ItemComponent, index) => {
|
||||
return ItemComponent
|
||||
})}
|
||||
@@ -38,13 +40,17 @@ export const GameListRelate = ({ games }) => {
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
const GameItem = ({ item, isLargeCard }) => {
|
||||
const { id, title, img, video } = item
|
||||
const GameItem = ({ item }) => {
|
||||
const { title } = item
|
||||
const [showType, setShowType] = useState('img') // img or video
|
||||
const url = checkContainHttp(item.slug) ? sliceUrlFromHttp(item.slug) : `${siteConfig('SUB_PATH', '')}/${item.slug}`
|
||||
|
||||
const img = item?.pageCoverThumbnail
|
||||
const video = item?.ext?.video
|
||||
|
||||
return (
|
||||
<a
|
||||
href={`/game/${id}`}
|
||||
<Link
|
||||
href={`${url}`}
|
||||
onMouseOver={() => {
|
||||
setShowType('video')
|
||||
}}
|
||||
@@ -71,6 +77,6 @@ const GameItem = ({ item, isLargeCard }) => {
|
||||
src={img}
|
||||
alt={title}
|
||||
/>
|
||||
</a>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* eslint-disable @next/next/no-img-element */
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import { deepClone } from '@/lib/utils'
|
||||
import { checkContainHttp, deepClone, sliceUrlFromHttp } from '@/lib/utils'
|
||||
import { useState } from 'react'
|
||||
|
||||
/**
|
||||
@@ -48,12 +49,15 @@ export const GameListRecent = ({ maxCount = 14 }) => {
|
||||
* @returns
|
||||
*/
|
||||
const GameItem = ({ item }) => {
|
||||
const { id, title, img, video } = item || {}
|
||||
const { title } = item || {}
|
||||
const [showType, setShowType] = useState('img') // img or video
|
||||
const url = checkContainHttp(item.slug) ? sliceUrlFromHttp(item.slug) : `${siteConfig('SUB_PATH', '')}/${item.slug}`
|
||||
|
||||
const img = item?.pageCoverThumbnail
|
||||
const video = item?.ext?.video
|
||||
return (
|
||||
<a
|
||||
href={`/game/${id}`}
|
||||
href={`${url}`}
|
||||
onMouseOver={() => {
|
||||
setShowType('video')
|
||||
}}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import Image from 'next/image'
|
||||
import Logo from './Logo'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
|
||||
/**
|
||||
* 顶栏
|
||||
@@ -9,22 +9,16 @@ import { useGlobal } from '@/lib/global'
|
||||
export default function Header() {
|
||||
const { setSideBarVisible } = useGlobal()
|
||||
return (
|
||||
<header className="z-20">
|
||||
<div className="w-full h-16 rounded-md bg-[#1F2030] flex justify-between items-center px-4">
|
||||
<header className='z-20'>
|
||||
<div className='w-full h-16 rounded-md bg-white shadow-card dark:bg-[#1F2030] flex justify-between items-center px-4'>
|
||||
<Logo />
|
||||
|
||||
<button
|
||||
className="flex xl:hidden"
|
||||
className='flex xl:hidden'
|
||||
onClick={() => {
|
||||
setSideBarVisible(true)
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src="/svg/search.svg"
|
||||
className="mr-2"
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
}}>
|
||||
<Image src='/svg/search.svg' className='mr-2' width={20} height={20} />
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -6,7 +6,7 @@ export default function Logo() {
|
||||
return (
|
||||
<Link passHref href='/' className='logo rounded cursor-pointer flex flex-col items-center'>
|
||||
<div className='w-full'>
|
||||
<h1 className='text-2xl text-white font-bold font-serif'>{siteConfig('TITLE')}</h1>
|
||||
<h1 className='text-2xl dark:text-white font-bold font-serif'>{siteConfig('TITLE')}</h1>
|
||||
<h2 className='text-xs text-gray-400 whitespace-nowrap'>{siteConfig('BIO')}</h2>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
@@ -4,9 +4,9 @@ import { useGameGlobal } from '..'
|
||||
export const MenuList = () => {
|
||||
const { setSideBarVisible } = useGameGlobal()
|
||||
return (
|
||||
<div>
|
||||
<div className='dark:text-white'>
|
||||
<ul>
|
||||
<li className='text-white py-4 px-2 font-bold hover:underline'>
|
||||
<li className='py-4 px-2 font-bold hover:underline'>
|
||||
<Link href='/' passHref>
|
||||
<span className='flex items-center gap-2'>
|
||||
<i className='fas fa-home' />
|
||||
@@ -14,7 +14,7 @@ export const MenuList = () => {
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
<li className='text-white py-4 px-2 font-bold hover:underline'>
|
||||
<li className='py-4 px-2 font-bold hover:underline'>
|
||||
<button
|
||||
className='flex items-center gap-2'
|
||||
onClick={() => {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
const CONFIG = {
|
||||
GAME_NAV_NOTION_ICON: true, // 是否读取Notion图标作为站点头像 ; 否则默认显示黑色SVG方块
|
||||
|
||||
GAME_RECOMMEND_TAG: 'Recommend', // 打了此Tag,被视为推荐
|
||||
GAME_INDEX_EXPAND_RECOMMEND: true, // 首页列表是否将推荐游戏放大,否则随机放大。
|
||||
|
||||
// 特殊菜单
|
||||
GAME_MENU_RANDOM_POST: true, // 是否显示随机跳转文章按钮
|
||||
GAME_MENU_SEARCH_BUTTON: true, // 是否显示搜索按钮,该按钮支持Algolia搜索
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import Comment from '@/components/Comment'
|
||||
import CusdisComponent from '@/components/CusdisComponent'
|
||||
import { Draggable } from '@/components/Draggable'
|
||||
import { AdSlot } from '@/components/GoogleAdsense'
|
||||
import replaceSearchResult from '@/components/Mark'
|
||||
import NotionPage from '@/components/NotionPage'
|
||||
import ShareBar from '@/components/ShareBar'
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import { deepClone, isBrowser } from '@/lib/utils'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { createContext, useContext, useEffect, useRef, useState } from 'react'
|
||||
import Announcement from './components/Announcement'
|
||||
import { ArticleFooter } from './components/ArticleFooter'
|
||||
import { ArticleInfo } from './components/ArticleInfo'
|
||||
import { ArticleLock } from './components/ArticleLock'
|
||||
import BlogArchiveItem from './components/BlogArchiveItem'
|
||||
import { BlogListPage } from './components/BlogListPage'
|
||||
import { BlogListScroll } from './components/BlogListScroll'
|
||||
import { Footer } from './components/Footer'
|
||||
import FullScreen from './components/FullScreen'
|
||||
import { GameListIndexCombine } from './components/GameListIndexCombine'
|
||||
import { GameListRelate } from './components/GameListRealate'
|
||||
import { GameListRecent } from './components/GameListRecent'
|
||||
import Header from './components/Header'
|
||||
import NavBar from './components/NavBar'
|
||||
import SearchNavBar from './components/SearchNavBar'
|
||||
@@ -45,7 +45,11 @@ const LayoutBase = props => {
|
||||
// 在列表中进行实时过滤
|
||||
const [filterKey, setFilterKey] = useState('')
|
||||
|
||||
const [filterGames, setFilterGames] = useState(deepClone(allNavPages?.filter(item => item.recommend)))
|
||||
const [filterGames, setFilterGames] = useState(
|
||||
deepClone(
|
||||
allNavPages?.filter(item => item.tags?.some(t => t === siteConfig('GAME_RECOMMEND_TAG', 'Recommend', CONFIG)))
|
||||
)
|
||||
)
|
||||
const [recentGames, setRecentGames] = useState([])
|
||||
const [sideBarVisible, setSideBarVisible] = useState(false)
|
||||
|
||||
@@ -68,7 +72,7 @@ const LayoutBase = props => {
|
||||
}}>
|
||||
<div
|
||||
id='theme-game'
|
||||
className={`${siteConfig('FONT_STYLE')} w-full h-full min-h-screen justify-center dark:text-gray-300 scroll-smooth`}>
|
||||
className={`${siteConfig('FONT_STYLE')} w-full h-full min-h-screen justify-center bg-[#83FFE7] dark:bg-black dark:text-gray-300 scroll-smooth`}>
|
||||
<Style />
|
||||
{/* 左右布局 */}
|
||||
<div id='wrapper' className={'relative flex justify-between w-full h-full mx-auto'}>
|
||||
@@ -95,8 +99,9 @@ const LayoutBase = props => {
|
||||
</div>
|
||||
|
||||
{/* 右侧 */}
|
||||
<main className='flex-grow w-full overflow-x-scroll'>
|
||||
<main className='flex-grow w-full overflow-x-auto'>
|
||||
{children}
|
||||
|
||||
<div className='ads w-full justify-center flex p-2'>
|
||||
<AdSlot type='flow' />
|
||||
</div>
|
||||
@@ -122,7 +127,13 @@ const LayoutBase = props => {
|
||||
* @returns
|
||||
*/
|
||||
const LayoutIndex = props => {
|
||||
return <LayoutPostList {...props} topSlot={<Announcement {...props} />} />
|
||||
return (
|
||||
<>
|
||||
<GameListRecent />
|
||||
<LayoutPostList {...props} />
|
||||
<Announcement {...props} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,7 +142,7 @@ const LayoutIndex = props => {
|
||||
* @returns
|
||||
*/
|
||||
const LayoutPostList = props => {
|
||||
const { posts, topSlot, tag } = props
|
||||
const { posts, tag } = props
|
||||
const { filterKey } = useGameGlobal()
|
||||
let filteredBlogPosts = []
|
||||
if (filterKey && posts) {
|
||||
@@ -146,7 +157,6 @@ const LayoutPostList = props => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{topSlot}
|
||||
{tag && <SearchNavBar {...props} />}
|
||||
{siteConfig('POST_LIST_STYLE') === 'page' ? (
|
||||
<BlogListPage {...props} posts={filteredBlogPosts} />
|
||||
@@ -227,26 +237,58 @@ const LayoutArchive = props => {
|
||||
* @returns
|
||||
*/
|
||||
const LayoutSlug = props => {
|
||||
const { post, lock, validPassword } = props
|
||||
const router = useRouter()
|
||||
const { post, allNavPages, lock, validPassword } = props
|
||||
const game = post
|
||||
const [loading, setLoading] = useState(false)
|
||||
// const [url, setUrl] = useState(game?.ext?.href)
|
||||
// 替换成随机推荐和相关游戏
|
||||
const relateGames = allNavPages
|
||||
const randomGames = allNavPages
|
||||
|
||||
// 将当前游戏加入到最近游玩
|
||||
useEffect(() => {
|
||||
// 404
|
||||
if (!post) {
|
||||
setTimeout(
|
||||
() => {
|
||||
if (isBrowser) {
|
||||
const article = document.getElementById('notion-article')
|
||||
if (!article) {
|
||||
router.push('/404').then(() => {
|
||||
console.warn('找不到页面', router.asPath)
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
siteConfig('POST_WAITING_TIME_FOR_404') * 1000
|
||||
)
|
||||
// if (!url || url !== game?.ext?.href) {
|
||||
// // 游戏路径
|
||||
// setUrl(game?.ext?.href)
|
||||
// }
|
||||
|
||||
// 更新最新游戏
|
||||
const recentGames = localStorage.getItem('recent_games') ? JSON.parse(localStorage.getItem('recent_games')) : []
|
||||
|
||||
const existedIndex = recentGames.findIndex(item => item?.id === game?.id)
|
||||
if (existedIndex === -1) {
|
||||
recentGames.unshift(game) // 将游戏插入到数组头部
|
||||
} else {
|
||||
// 如果游戏已存在于数组中,将其移至数组头部
|
||||
const existingGame = recentGames.splice(existedIndex, 1)[0]
|
||||
recentGames.unshift(existingGame)
|
||||
}
|
||||
}, [post])
|
||||
localStorage.setItem('recent_games', JSON.stringify(recentGames))
|
||||
|
||||
const iframe = document.getElementById('game-wrapper')
|
||||
|
||||
// 定义一个函数来处理iframe加载成功事件
|
||||
function iframeLoaded() {
|
||||
if (game) {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定加载事件
|
||||
if (iframe.attachEvent) {
|
||||
iframe.attachEvent('onload', iframeLoaded)
|
||||
} else {
|
||||
iframe.onload = iframeLoaded
|
||||
}
|
||||
|
||||
// 更改iFrame的title
|
||||
if (document?.getElementById('game-wrapper')?.contentDocument.querySelector('title')?.textContent) {
|
||||
document.getElementById('game-wrapper').contentDocument.querySelector('title').textContent = `${
|
||||
game?.title || ''
|
||||
} - Play ${game?.title || ''} on ${siteConfig('TITLE')}`
|
||||
}
|
||||
}, [game])
|
||||
|
||||
return (
|
||||
<>
|
||||
{lock && <ArticleLock validPassword={validPassword} />}
|
||||
@@ -254,11 +296,100 @@ const LayoutSlug = props => {
|
||||
{!lock && (
|
||||
<div id='article-wrapper' className='px-2'>
|
||||
<>
|
||||
<ArticleInfo post={post} />
|
||||
{/* <ArticleInfo post={post} />
|
||||
<NotionPage post={post} />
|
||||
<ShareBar post={post} />
|
||||
<Comment frontMatter={post} />
|
||||
<ArticleFooter />
|
||||
<ArticleFooter /> */}
|
||||
{/* 游戏区域 */}
|
||||
<div className='game-detail-wrapper w-full grow flex md:px-2'>
|
||||
{/* 移动端返回主页按钮 */}
|
||||
<Draggable stick='left'>
|
||||
<div
|
||||
style={{ left: '0px', top: '1rem' }}
|
||||
className='fixed xl:hidden group space-x-1 flex items-center z-20 pr-3 pl-1 bg-[#202030] rounded-r-2xl shadow-lg '>
|
||||
<Link href='/' className='px-1 py-3 hover:scale-125 duration-200 transition-all' passHref>
|
||||
<i className='fas fa-arrow-left' />
|
||||
</Link>{' '}
|
||||
<span
|
||||
className='text-white font-serif'
|
||||
onClick={() => {
|
||||
document.querySelector('.game-info').scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'end',
|
||||
inline: 'nearest'
|
||||
})
|
||||
}}>
|
||||
G
|
||||
</span>
|
||||
</div>
|
||||
</Draggable>
|
||||
|
||||
<div className='w-full py-1 md:py-4'>
|
||||
<div className='bg-black w-full xl:h-[calc(100vh-8rem)] h-screen rounded-md relative'>
|
||||
{/* Loading遮罩 */}
|
||||
{loading && (
|
||||
<div className='absolute z-20 w-full xl:h-[calc(100vh-8rem)] h-screen rounded-md overflow-hidden '>
|
||||
<div className='z-20 absolute bg-black bg-opacity-75 w-full h-full flex flex-col gap-4 justify-center items-center'>
|
||||
<h2 className='text-3xl text-white flex gap-2'>
|
||||
<i className='fas fa-spinner animate-spin'></i>
|
||||
{siteConfig('TITLE')}
|
||||
</h2>
|
||||
<h3 className='text-xl text-white'>{siteConfig('DESCRIPTION')}</h3>
|
||||
</div>
|
||||
|
||||
{/* 游戏封面图 */}
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
{game?.img && <img src={game?.img} className='w-full h-full blur-md absolute top-0 left-0 z-0' />}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<iframe
|
||||
id='game-wrapper'
|
||||
className={`w-full xl:h-[calc(100vh-8rem)] h-screen md:rounded-md overflow-hidden ${game?.ext?.href ? '' : 'hidden'}`}
|
||||
style={{
|
||||
position: 'relative'
|
||||
}}
|
||||
src={game?.ext?.href}></iframe>
|
||||
|
||||
{/* 游戏窗口装饰器 */}
|
||||
{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 />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 游戏资讯 */}
|
||||
<div className='game-info dark:text-white py-4 mt-8 md:mt-0'>
|
||||
<div className='w-full'>
|
||||
<GameListRelate posts={relateGames} />
|
||||
</div>
|
||||
<h1 className='text-2xl px-2 py-2 xl:px-0'>{game?.title}</h1>
|
||||
<h2 className='px-2 py-2 xl:px-0'>
|
||||
Play {game?.title || ''} on {siteConfig('TITLE', '')}
|
||||
</h2>
|
||||
<p className='px-2 py-2 xl:px-0'>{siteConfig('DESCRIPTION')}</p>
|
||||
<AdSlot />
|
||||
|
||||
{game && (
|
||||
<div>
|
||||
<div className='py-2 text-2xl dark:text-white'>Comment</div>
|
||||
<CusdisComponent frontMatter={game} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='xl:hidden py-2'>
|
||||
<Header />
|
||||
</div>
|
||||
{/* 其它游戏列表 */}
|
||||
<div>
|
||||
<GameListIndexCombine posts={randomGames} />
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import BLOG, { LAYOUT_MAPPINGS } from '@/blog.config'
|
||||
import { getQueryParam, getQueryVariable, isBrowser } from '../lib/utils'
|
||||
import dynamic from 'next/dynamic'
|
||||
import getConfig from 'next/config'
|
||||
import * as ThemeComponents from '@theme-components'
|
||||
import getConfig from 'next/config'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { getQueryParam, getQueryVariable, isBrowser } from '../lib/utils'
|
||||
|
||||
// 在next.config.js中扫描所有主题
|
||||
export const { THEMES = [] } = getConfig().publicRuntimeConfig
|
||||
@@ -100,7 +100,6 @@ export const initDarkMode = (updateDarkMode, defaultDarkMode) => {
|
||||
|
||||
// 查看localStorage中用户记录的是否深色模式
|
||||
const userDarkMode = loadDarkModeFromLocalStorage()
|
||||
console.log('深色模式',userDarkMode)
|
||||
if (userDarkMode) {
|
||||
newDarkMode = userDarkMode === 'dark' || userDarkMode === 'true'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user