Skip to content

Commit 3ed5f16

Browse files
committedMar 15, 2024
movie 404
1 parent ac91695 commit 3ed5f16

11 files changed

+496
-139
lines changed
 

‎lib/db/getSiteData.js

+115-37
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,7 @@ export { getPost } from '../notion/getNotionPost'
2525
* @returns
2626
*
2727
*/
28-
export async function getGlobalData({
29-
pageId = BLOG.NOTION_PAGE_ID,
30-
from
31-
}) {
28+
export async function getGlobalData({ pageId = BLOG.NOTION_PAGE_ID, from }) {
3229
// 从notion获取
3330
const data = await getNotionPageData({ pageId, from })
3431
const db = deepClone(data)
@@ -89,7 +86,9 @@ function cleanBlock(post) {
8986
* @returns
9087
*/
9188
function getLatestPosts({ allPages, from, latestPostCount }) {
92-
const allPosts = allPages?.filter(page => page.type === 'Post' && page.status === 'Published')
89+
const allPosts = allPages?.filter(
90+
page => page.type === 'Post' && page.status === 'Published'
91+
)
9392

9493
const latestPosts = Object.create(allPosts).sort((a, b) => {
9594
const dateA = new Date(a?.lastEditedDate || a?.publishDate)
@@ -140,7 +139,13 @@ function getCustomNav({ allPages }) {
140139
p.to = '/' + p.slug
141140
}
142141
}
143-
customNav.push({ icon: p.icon || null, name: p.title, to: p.slug, target: '_blank', show: true })
142+
customNav.push({
143+
icon: p.icon || null,
144+
name: p.title,
145+
to: p.slug,
146+
target: '_blank',
147+
show: true
148+
})
144149
})
145150
}
146151
return customNav
@@ -152,7 +157,12 @@ function getCustomNav({ allPages }) {
152157
* @returns
153158
*/
154159
function getCustomMenu({ collectionData }) {
155-
const menuPages = collectionData.filter(post => post.status === 'Published' && (post?.type === BLOG.NOTION_PROPERTY_NAME.type_menu || post?.type === BLOG.NOTION_PROPERTY_NAME.type_sub_menu))
160+
const menuPages = collectionData.filter(
161+
post =>
162+
post.status === 'Published' &&
163+
(post?.type === BLOG.NOTION_PROPERTY_NAME.type_menu ||
164+
post?.type === BLOG.NOTION_PROPERTY_NAME.type_sub_menu)
165+
)
156166
const menus = []
157167
if (menuPages && menuPages.length > 0) {
158168
menuPages.forEach(e => {
@@ -190,7 +200,9 @@ function getCustomMenu({ collectionData }) {
190200
*/
191201
function getTagOptions(schema) {
192202
if (!schema) return {}
193-
const tagSchema = Object.values(schema).find(e => e.name === BLOG.NOTION_PROPERTY_NAME.tags)
203+
const tagSchema = Object.values(schema).find(
204+
e => e.name === BLOG.NOTION_PROPERTY_NAME.tags
205+
)
194206
return tagSchema?.options || []
195207
}
196208

@@ -201,7 +213,9 @@ function getTagOptions(schema) {
201213
*/
202214
function getCategoryOptions(schema) {
203215
if (!schema) return {}
204-
const categorySchema = Object.values(schema).find(e => e.name === BLOG.NOTION_PROPERTY_NAME.category)
216+
const categorySchema = Object.values(schema).find(
217+
e => e.name === BLOG.NOTION_PROPERTY_NAME.category
218+
)
205219
return categorySchema?.options || []
206220
}
207221

@@ -213,9 +227,15 @@ function getCategoryOptions(schema) {
213227
*/
214228
function getSiteInfo({ collection, block }) {
215229
const title = collection?.name?.[0][0] || BLOG.TITLE
216-
const description = collection?.description ? Object.assign(collection).description[0][0] : BLOG.DESCRIPTION
217-
const pageCover = collection?.cover ? mapImgUrl(collection?.cover, block[idToUuid(BLOG.NOTION_PAGE_ID)]?.value) : BLOG.HOME_BANNER_IMAGE
218-
let icon = collection?.icon ? mapImgUrl(collection?.icon, collection, 'collection') : BLOG.AVATAR
230+
const description = collection?.description
231+
? Object.assign(collection).description[0][0]
232+
: BLOG.DESCRIPTION
233+
const pageCover = collection?.cover
234+
? mapImgUrl(collection?.cover, block[idToUuid(BLOG.NOTION_PAGE_ID)]?.value)
235+
: BLOG.HOME_BANNER_IMAGE
236+
let icon = collection?.icon
237+
? mapImgUrl(collection?.icon, collection, 'collection')
238+
: BLOG.AVATAR
219239

220240
// 用户头像压缩一下
221241
icon = compressImage(icon)
@@ -236,7 +256,13 @@ function getSiteInfo({ collection, block }) {
236256
*/
237257
export function getNavPages({ allPages }) {
238258
const allNavPages = allPages?.filter(post => {
239-
return post && post?.slug && (!post?.slug?.startsWith('http')) && post?.type === 'Post' && post?.status === 'Published'
259+
return (
260+
post &&
261+
post?.slug &&
262+
!post?.slug?.startsWith('http') &&
263+
post?.type === 'Post' &&
264+
post?.status === 'Published'
265+
)
240266
})
241267

242268
return allNavPages.map(item => ({
@@ -248,7 +274,8 @@ export function getNavPages({ allPages }) {
248274
summary: item.summary || null,
249275
slug: item.slug,
250276
pageIcon: item.pageIcon || '',
251-
lastEditedDate: item.lastEditedDate
277+
lastEditedDate: item.lastEditedDate,
278+
publishDate: item.publishDate
252279
}))
253280
}
254281

@@ -265,19 +292,26 @@ async function getNotice(post) {
265292
}
266293

267294
// 没有数据时返回
268-
const EmptyData = (pageId) => {
295+
const EmptyData = pageId => {
269296
const empty = {
270297
notice: null,
271298
siteInfo: getSiteInfo({}),
272-
allPages: [{
273-
id: 1,
274-
title: `无法获取Notion数据,请检查Notion_ID: \n 当前 ${pageId}`,
275-
summary: '访问文档获取帮助→ https://tangly1024.com/article/vercel-deploy-notion-next',
276-
status: 'Published',
277-
type: 'Post',
278-
slug: '13a171332816461db29d50e9f575b00d',
279-
date: { start_date: '2023-04-24', lastEditedDay: '2023-04-24', tagItems: [] }
280-
}],
299+
allPages: [
300+
{
301+
id: 1,
302+
title: `无法获取Notion数据,请检查Notion_ID: \n 当前 ${pageId}`,
303+
summary:
304+
'访问文档获取帮助→ https://tangly1024.com/article/vercel-deploy-notion-next',
305+
status: 'Published',
306+
type: 'Post',
307+
slug: '13a171332816461db29d50e9f575b00d',
308+
date: {
309+
start_date: '2023-04-24',
310+
lastEditedDay: '2023-04-24',
311+
tagItems: []
312+
}
313+
}
314+
],
281315
allNavPages: [],
282316
collection: [],
283317
collectionQuery: {},
@@ -313,7 +347,8 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) {
313347
const rawMetadata = block[pageId]?.value
314348
// Check Type Page-Database和Inline-Database
315349
if (
316-
rawMetadata?.type !== 'collection_view_page' && rawMetadata?.type !== 'collection_view'
350+
rawMetadata?.type !== 'collection_view_page' &&
351+
rawMetadata?.type !== 'collection_view'
317352
) {
318353
console.error(`pageId "${pageId}" is not a database`)
319354
return EmptyData(pageId)
@@ -328,9 +363,21 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) {
328363
const viewIds = rawMetadata?.view_ids
329364
const collectionData = []
330365

331-
const pageIds = getAllPageIds(collectionQuery, collectionId, collectionView, viewIds)
366+
const pageIds = getAllPageIds(
367+
collectionQuery,
368+
collectionId,
369+
collectionView,
370+
viewIds
371+
)
332372
if (pageIds?.length === 0) {
333-
console.error('获取到的文章列表为空,请检查notion模板', collectionQuery, collection, collectionView, viewIds, pageRecordMap)
373+
console.error(
374+
'获取到的文章列表为空,请检查notion模板',
375+
collectionQuery,
376+
collection,
377+
collectionView,
378+
viewIds,
379+
pageRecordMap
380+
)
334381
} else {
335382
// console.log('有效Page数量', pageIds?.length)
336383
}
@@ -343,15 +390,29 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) {
343390
// 如果找不到文章对应的block,说明发生了溢出,使用pageID再去请求
344391
const pageBlock = await getSingleBlock(id, from)
345392
if (pageBlock.block[id].value) {
346-
const properties = (await getPageProperties(id, pageBlock.block[id].value, schema, null, getTagOptions(schema))) || null
393+
const properties =
394+
(await getPageProperties(
395+
id,
396+
pageBlock.block[id].value,
397+
schema,
398+
null,
399+
getTagOptions(schema)
400+
)) || null
347401
if (properties) {
348402
collectionData.push(properties)
349403
}
350404
}
351405
continue
352406
}
353407

354-
const properties = (await getPageProperties(id, value, schema, null, getTagOptions(schema))) || null
408+
const properties =
409+
(await getPageProperties(
410+
id,
411+
value,
412+
schema,
413+
null,
414+
getTagOptions(schema)
415+
)) || null
355416
if (properties) {
356417
collectionData.push(properties)
357418
}
@@ -365,13 +426,16 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) {
365426
if (post?.type === 'Post' && post.status === 'Published') {
366427
postCount++
367428
}
368-
return post && post?.slug &&
369-
(!post?.slug?.startsWith('http')) &&
429+
return (
430+
post &&
431+
post?.slug &&
432+
!post?.slug?.startsWith('http') &&
370433
(post?.status === 'Invisible' || post?.status === 'Published')
434+
)
371435
})
372436

373437
// 站点配置优先读取配置表格,否则读取blog.config.js 文件
374-
const NOTION_CONFIG = await getConfigMapFromConfigPage(collectionData) || {}
438+
const NOTION_CONFIG = (await getConfigMapFromConfigPage(collectionData)) || {}
375439

376440
// Sort by date
377441
if (BLOG.POSTS_SORT_BY === 'date') {
@@ -380,13 +444,27 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) {
380444
})
381445
}
382446

383-
const notice = await getNotice(collectionData.filter(post => {
384-
return post && post?.type && post?.type === 'Notice' && post.status === 'Published'
385-
})?.[0])
386-
const categoryOptions = getAllCategories({ allPages, categoryOptions: getCategoryOptions(schema) })
447+
const notice = await getNotice(
448+
collectionData.filter(post => {
449+
return (
450+
post &&
451+
post?.type &&
452+
post?.type === 'Notice' &&
453+
post.status === 'Published'
454+
)
455+
})?.[0]
456+
)
457+
const categoryOptions = getAllCategories({
458+
allPages,
459+
categoryOptions: getCategoryOptions(schema)
460+
})
387461
const tagOptions = getAllTags({ allPages, tagOptions: getTagOptions(schema) })
388462
// 旧的菜单
389-
const customNav = getCustomNav({ allPages: collectionData.filter(post => post?.type === 'Page' && post.status === 'Published') })
463+
const customNav = getCustomNav({
464+
allPages: collectionData.filter(
465+
post => post?.type === 'Page' && post.status === 'Published'
466+
)
467+
})
390468
// 新的菜单
391469
const customMenu = await getCustomMenu({ collectionData })
392470
const latestPosts = getLatestPosts({ allPages, from, latestPostCount: 6 })
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { useGlobal } from '@/lib/global'
2+
import { formatDateFmt } from '@/lib/utils/formatDate'
3+
import Link from 'next/link'
4+
5+
export default function ArchiveDateList(props) {
6+
const postsSortByDate = Object.create(props.allNavPages)
7+
const { locale } = useGlobal()
8+
9+
postsSortByDate.sort((a, b) => {
10+
return b?.publishDate - a?.publishDate
11+
})
12+
13+
let dates = []
14+
postsSortByDate.forEach(post => {
15+
const date = formatDateFmt(post.publishDate, 'yyyy-MM')
16+
if (!dates[date]) {
17+
dates.push(date)
18+
}
19+
})
20+
dates = dates.slice(0, 5)
21+
return (
22+
<div>
23+
<div className="text-2xl dark:text-white mb-2">{locale.NAV.ARCHIVE}</div>
24+
{dates?.map((date, index) => {
25+
return (
26+
<div key={index}>
27+
<Link
28+
href={`/archive#${date}`}
29+
className="hover:underline dark:text-green-500"
30+
>
31+
{date}
32+
</Link>
33+
</div>
34+
)
35+
})}
36+
</div>
37+
)
38+
}
+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { siteConfig } from '@/lib/config'
2+
import { useGlobal } from '@/lib/global'
3+
import Link from 'next/link'
4+
5+
const CategoryGroup = props => {
6+
const { currentCategory, categoryOptions } = props
7+
const { locale } = useGlobal()
8+
if (!categoryOptions || categoryOptions.length === 0) return <></>
9+
const categoryCount = siteConfig('PREVIEW_CATEGORY_COUNT')
10+
const categories = categoryOptions.slice(0, categoryCount)
11+
return (
12+
<>
13+
<div>
14+
<h2 className="text-2xl dark:text-white">{locale.COMMON.CATEGORY}</h2>
15+
<div id="category-list" className="dark:border-gray-600 flex flex-col">
16+
{categories.map(category => {
17+
const selected = currentCategory === category.name
18+
return (
19+
<Link
20+
key={category.name}
21+
href={`/category/${category.name}`}
22+
passHref
23+
className={
24+
(selected
25+
? 'hover:text-white dark:hover:text-white bg-gray-600 text-white '
26+
: 'dark:text-green-400 text-gray-500 hover:text-white hover:bg-gray-500 dark:hover:text-white') +
27+
' w-full items-center duration-300 px-2 cursor-pointer py-1 font-light'
28+
}
29+
>
30+
<i
31+
className={`${selected ? 'text-white fa-folder-open ' : 'text-gray-500 fa-folder '} mr-2 fas`}
32+
/>
33+
{category.name}({category.count})
34+
</Link>
35+
)
36+
})}
37+
</div>
38+
</div>
39+
</>
40+
)
41+
}
42+
43+
export default CategoryGroup

‎themes/movie/components/Header.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { MenuListTop } from './MenuListTop'
88
*/
99
export const Header = props => {
1010
return (
11-
<header className="w-full px-6 relative z-10">
11+
<header className="w-full px-8 relative z-20">
1212
<div className="mx-auto md:flex justify-between items-center">
1313
<Link
1414
href="/"
@@ -17,7 +17,7 @@ export const Header = props => {
1717
{siteConfig('TITLE')}
1818
</Link>
1919
<div className="w-full md:w-auto text-center md:text-right">
20-
{/* 右侧文字 */}
20+
{/* 右侧菜单 */}
2121
<MenuListTop {...props} />
2222
</div>
2323
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { siteConfig } from '@/lib/config'
2+
import { useGlobal } from '@/lib/global'
3+
import Link from 'next/link'
4+
import { useRouter } from 'next/router'
5+
import { checkContainHttp, sliceUrlFromHttp } from '@/lib/utils'
6+
7+
/**
8+
* 最新文章列表
9+
* @param posts 所有文章数据
10+
* @param sliceCount 截取展示的数量 默认6
11+
* @constructor
12+
*/
13+
const LatestPostsGroup = ({ latestPosts }) => {
14+
// 获取当前路径
15+
const currentPath = useRouter().asPath
16+
const { locale } = useGlobal()
17+
18+
if (!latestPosts) {
19+
return <></>
20+
}
21+
22+
return (
23+
<div>
24+
<div className="pb-1 px-2 flex flex-nowrap justify-between">
25+
<div className="text-2xl text-gray-600 dark:text-gray-200">
26+
<i className="mr-2 fas fa-history" />
27+
{locale.COMMON.LATEST_POSTS}
28+
</div>
29+
</div>
30+
31+
{latestPosts.map(post => {
32+
const selected =
33+
currentPath === `${siteConfig('SUB_PATH', '')}/${post.slug}`
34+
const url = checkContainHttp(post.slug)
35+
? sliceUrlFromHttp(post.slug)
36+
: `${siteConfig('SUB_PATH', '')}/${post.slug}`
37+
return (
38+
<Link
39+
key={post.id}
40+
title={post.title}
41+
href={url}
42+
passHref
43+
className={'my-1 flex'}
44+
>
45+
<div
46+
className={
47+
(selected
48+
? 'text-white bg-gray-600 '
49+
: 'text-gray-500 dark:text-green-400 ') +
50+
' py-1 flex hover:bg-gray-500 px-2 duration-200 w-full ' +
51+
'hover:text-white dark:hover:text-white cursor-pointer'
52+
}
53+
>
54+
<li className="line-clamp-2">{post.title}</li>
55+
</div>
56+
</Link>
57+
)
58+
})}
59+
</div>
60+
)
61+
}
62+
export default LatestPostsGroup

‎themes/movie/components/MenuItemDrop.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,19 @@ export const MenuItemDrop = ({ link }) => {
4040
{hasSubMenu && (
4141
<ul
4242
style={{ backdropFilter: 'blur(3px)' }}
43-
className={`${show ? 'visible opacity-100 top-12' : 'invisible opacity-0 top-20'} drop-shadow-md overflow-hidden rounded-md text-black dark:text-white bg-white dark:bg-black transition-all duration-300 z-30 absolute block `}
43+
className={`${show ? 'visible opacity-100 top-14' : 'invisible opacity-0 top-20'} drop-shadow-md overflow-hidden rounded-md text-black dark:text-white bg-white dark:bg-black transition-all duration-300 z-30 absolute block `}
4444
>
4545
{link.subMenus.map((sLink, index) => {
4646
return (
4747
<li
4848
key={index}
49-
className="cursor-pointer hover:bg-indigo-300 hover:text-black tracking-widest transition-all duration-200 dark:border-gray-800 py-1 pr-6 pl-3"
49+
className="cursor-pointer dark:bg-hexo-black-gray dark:hover:bg-gray-300 hover:bg-gray-300 hover:text-black tracking-widest transition-all duration-200 dark:border-gray-800 py-1 pr-6 pl-3"
5050
>
5151
<Link
5252
href={sLink.to}
5353
target={link?.to?.indexOf('http') === 0 ? '_blank' : '_self'}
5454
>
55-
<span className="text-sm text-nowrap font-extralight">
55+
<span className="text-sm text-nowrap">
5656
{link?.icon && <i className={sLink?.icon}> &nbsp; </i>}
5757
{sLink.title}
5858
</span>

‎themes/movie/components/MenuListTop.js

+32-9
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,32 @@ import CONFIG from '../config'
33
import { siteConfig } from '@/lib/config'
44
import { MenuItemDrop } from './MenuItemDrop'
55

6-
export const MenuListTop = (props) => {
6+
export const MenuListTop = props => {
77
const { customNav, customMenu } = props
88
const { locale } = useGlobal()
99

1010
let links = [
11-
{ id: 1, icon: 'fa-solid fa-house', name: locale.NAV.INDEX, to: '/', show: siteConfig('HEXO_MENU_INDEX', null, CONFIG) },
12-
{ id: 2, icon: 'fas fa-search', name: locale.NAV.SEARCH, to: '/search', show: siteConfig('HEXO_MENU_SEARCH', null, CONFIG) },
13-
{ id: 3, icon: 'fas fa-archive', name: locale.NAV.ARCHIVE, to: '/archive', show: siteConfig('HEXO_MENU_ARCHIVE', null, CONFIG) }
11+
{
12+
id: 1,
13+
icon: 'fa-solid fa-house',
14+
name: locale.NAV.INDEX,
15+
to: '/',
16+
show: siteConfig('HEXO_MENU_INDEX', null, CONFIG)
17+
},
18+
{
19+
id: 2,
20+
icon: 'fas fa-search',
21+
name: locale.NAV.SEARCH,
22+
to: '/search',
23+
show: siteConfig('HEXO_MENU_SEARCH', null, CONFIG)
24+
},
25+
{
26+
id: 3,
27+
icon: 'fas fa-archive',
28+
name: locale.NAV.ARCHIVE,
29+
to: '/archive',
30+
show: siteConfig('HEXO_MENU_ARCHIVE', null, CONFIG)
31+
}
1432
// { icon: 'fas fa-folder', name: locale.COMMON.CATEGORY, to: '/category', show: siteConfig('MENU_CATEGORY', null, CONFIG) },
1533
// { icon: 'fas fa-tag', name: locale.COMMON.TAGS, to: '/tag', show: siteConfig('MENU_TAG', null, CONFIG) }
1634
]
@@ -34,9 +52,14 @@ export const MenuListTop = (props) => {
3452
return null
3553
}
3654

37-
return (<>
38-
<nav id='nav-mobile' className='leading-8 justify-center font-light w-full flex'>
39-
{links?.map((link, index) => link && link.show && <MenuItemDrop key={index} link={link} />)}
40-
</nav>
41-
</>)
55+
return (
56+
<>
57+
<nav id="nav-mobile" className="leading-8 justify-center w-full flex">
58+
{links?.map(
59+
(link, index) =>
60+
link && link.show && <MenuItemDrop key={index} link={link} />
61+
)}
62+
</nav>
63+
</>
64+
)
4265
}

‎themes/movie/components/TagGroups.js

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { useGlobal } from '@/lib/global'
2+
import Link from 'next/link'
3+
import { useRouter } from 'next/router'
4+
5+
/**
6+
* 标签组
7+
* @param tags
8+
* @param currentTag
9+
* @returns {JSX.Element}
10+
* @constructor
11+
*/
12+
const TagGroups = ({ tagOptions, className }) => {
13+
const router = useRouter()
14+
const { locale } = useGlobal()
15+
const { tag: currentTag } = router.query
16+
if (!tagOptions) return <></>
17+
18+
return (
19+
<div>
20+
<div className="text-2xl dark:text-white mb-2">{locale.COMMON.TAGS}</div>
21+
<div id="tags-group" className="dark:border-gray-700 space-y-2">
22+
{tagOptions.map((tag, index) => {
23+
const selected = currentTag === tag.name
24+
return (
25+
<Link
26+
passHref
27+
key={index}
28+
href={`/tag/${encodeURIComponent(tag.name)}`}
29+
className={'cursor-pointer inline-block whitespace-nowrap'}
30+
>
31+
<div
32+
className={`${className || ''}
33+
${selected ? 'text-white bg-blue-600 dark:bg-yellow-600' : ''}
34+
flex items-center hover:bg-blue-600 dark:hover:bg-yellow-600 hover:scale-110 hover:text-white rounded-lg px-2 py-0.5 duration-150 transition-all`}
35+
>
36+
<div className="text-lg">{tag.name} </div>
37+
{tag.count ? (
38+
<sup className="relative ml-1">{tag.count}</sup>
39+
) : (
40+
<></>
41+
)}
42+
</div>
43+
</Link>
44+
)
45+
})}
46+
</div>
47+
</div>
48+
)
49+
}
50+
51+
export default TagGroups

‎themes/movie/components/TagItemMini.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@ const TagItemMini = ({ tag, selected = false }) => {
66
key={tag}
77
href={selected ? '/' : `/tag/${encodeURIComponent(tag.name)}`}
88
passHref
9-
className={
10-
'inline-block text-md font-extrabold rounded-xl text-shadow py-0.5 mr-2 text-[#2EBF8B]'
11-
}
9+
className={'inline-block rounded-xl py-0.5 mr-2 text-[#2EBF8B]'}
1210
>
13-
<div className="font-light">
11+
<div className="text-md font-bold text-shadow">
1412
{selected && <i className="mr-1 fa-tag" />}{' '}
1513
{tag.name + (tag.count ? `(${tag.count})` : '')}{' '}
1614
</div>

‎themes/movie/index.js

+111-69
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client'
22

33
import CONFIG from './config'
4-
import { useEffect } from 'react'
4+
import { createContext, useContext, useEffect, useRef } from 'react'
55
import { Header } from './components/Header'
66
import { Nav } from './components/Nav'
77
import { Footer } from './components/Footer'
@@ -26,6 +26,16 @@ import { useRouter } from 'next/router'
2626
import { Transition } from '@headlessui/react'
2727
import { Style } from './style'
2828
import { siteConfig } from '@/lib/config'
29+
import AlgoliaSearchModal from '@/components/AlgoliaSearchModal'
30+
import LatestPostsGroup from './components/LatestPostsGroup'
31+
import CategoryGroup from './components/CategoryGroup'
32+
import { formatDateFmt } from '@/lib/utils/formatDate'
33+
import ArchiveDateList from './components/ArchiveDateList'
34+
import TagGroups from './components/TagGroups'
35+
36+
// 主题全局状态
37+
const ThemeGlobalMovie = createContext()
38+
export const useMovieGlobal = () => useContext(ThemeGlobalMovie)
2939

3040
/**
3141
* 基础布局框架
@@ -35,32 +45,11 @@ import { siteConfig } from '@/lib/config'
3545
* @constructor
3646
*/
3747
const LayoutBase = props => {
38-
const { children } = props
48+
const { children, slotTop } = props
3949
const { onLoading, fullWidth } = useGlobal()
4050
const router = useRouter()
4151
const { category, tag } = props
42-
// 顶部如果是按照分类或标签查看文章列表,列表顶部嵌入一个横幅
43-
// 如果是搜索,则列表顶部嵌入 搜索框
44-
let slotTop = null
45-
if (category) {
46-
slotTop = (
47-
<div className="pb-12">
48-
<i className="mr-1 fas fa-folder-open" />
49-
{category}
50-
</div>
51-
)
52-
} else if (tag) {
53-
slotTop = <div className="pb-12">#{tag}</div>
54-
} else if (props.slotTop) {
55-
slotTop = props.slotTop
56-
} else if (router.route === '/search') {
57-
// 嵌入一个搜索框在顶部
58-
slotTop = (
59-
<div className="pb-12">
60-
<SearchInput {...props} />
61-
</div>
62-
)
63-
}
52+
const searchModal = useRef(null)
6453

6554
// 增加一个状态以触发 Transition 组件的动画
6655
// const [showTransition, setShowTransition] = useState(true)
@@ -71,58 +60,63 @@ const LayoutBase = props => {
7160
// }, [onLoading])
7261

7362
return (
74-
<div
75-
id="theme-example"
76-
className={`${siteConfig('FONT_STYLE')} dark:text-gray-300 bg-white dark:bg-[#2A2A2A] scroll-smooth`}
77-
>
78-
<Style />
63+
<ThemeGlobalMovie.Provider value={{ searchModal }}>
64+
<div
65+
id="theme-movie"
66+
className={`${siteConfig('FONT_STYLE')} dark:text-gray-300 bg-white dark:bg-[#2A2A2A] scroll-smooth min-h-screen flex flex-col justify-between`}
67+
>
68+
<Style />
7969

80-
{/* 页头 */}
81-
<Header {...props} />
70+
{/* 页头 */}
71+
<Header {...props} />
8272

83-
{/* 主体 */}
84-
<div id="container-inner" className="w-full relative z-10">
85-
{/* 标题栏 */}
86-
{/* {fullWidth ? null : <Title {...props} />} */}
73+
{/* 主体 */}
74+
<div id="container-inner" className="w-full relative z-10">
75+
{/* 标题栏 */}
76+
{/* {fullWidth ? null : <Title {...props} />} */}
8777

88-
<div
89-
id="container-wrapper"
90-
className={
91-
(JSON.parse(siteConfig('LAYOUT_SIDEBAR_REVERSE'))
92-
? 'flex-row-reverse'
93-
: '') +
94-
'relative mx-auto justify-center md:flex items-start py-8 px-2'
95-
}
96-
>
97-
{/* 内容 */}
98-
<div className={`w-full ${fullWidth ? '' : ''} xl:px-32 lg:px-4`}>
99-
<Transition
100-
show={!onLoading}
101-
appear={true}
102-
enter="transition ease-in-out duration-700 transform order-first"
103-
enterFrom="opacity-0 translate-y-16"
104-
enterTo="opacity-100"
105-
leave="transition ease-in-out duration-300 transform"
106-
leaveFrom="opacity-100 translate-y-0"
107-
leaveTo="opacity-0 -translate-y-16"
108-
unmount={false}
109-
>
110-
{/* 嵌入模块 */}
111-
{slotTop}
112-
{children}
113-
</Transition>
78+
<div
79+
id="container-wrapper"
80+
className={
81+
(JSON.parse(siteConfig('LAYOUT_SIDEBAR_REVERSE'))
82+
? 'flex-row-reverse'
83+
: '') +
84+
'relative mx-auto justify-center md:flex items-start py-8 px-2'
85+
}
86+
>
87+
{/* 内容 */}
88+
<div className={`w-full ${fullWidth ? '' : ''} xl:px-32 lg:px-4`}>
89+
<Transition
90+
show={!onLoading}
91+
appear={true}
92+
enter="transition ease-in-out duration-700 transform order-first"
93+
enterFrom="opacity-0 translate-y-16"
94+
enterTo="opacity-100"
95+
leave="transition ease-in-out duration-300 transform"
96+
leaveFrom="opacity-100 translate-y-0"
97+
leaveTo="opacity-0 -translate-y-16"
98+
unmount={false}
99+
>
100+
{/* 嵌入模块 */}
101+
{slotTop}
102+
{children}
103+
</Transition>
104+
</div>
114105
</div>
115106
</div>
116-
</div>
117107

118-
{/* 页脚 */}
119-
<Footer {...props} />
108+
{/* 页脚 */}
109+
<Footer {...props} />
120110

121-
{/* 回顶按钮 */}
122-
<div className="fixed right-4 bottom-4 z-10">
123-
<JumpToTopButton />
111+
{/* 搜索框 */}
112+
<AlgoliaSearchModal cRef={searchModal} {...props} />
113+
114+
{/* 回顶按钮 */}
115+
<div className="fixed right-4 bottom-4 z-10">
116+
<JumpToTopButton />
117+
</div>
124118
</div>
125-
</div>
119+
</ThemeGlobalMovie.Provider>
126120
)
127121
}
128122

@@ -200,7 +194,55 @@ const LayoutSlug = props => {
200194
* @returns
201195
*/
202196
const Layout404 = props => {
203-
return <>404 Not found.</>
197+
const { locale } = useGlobal()
198+
const { searchModal } = useMovieGlobal()
199+
const router = useRouter()
200+
// 展示搜索框
201+
const toggleShowSearchInput = () => {
202+
if (siteConfig('ALGOLIA_APP_ID')) {
203+
searchModal.current.openSearch()
204+
}
205+
}
206+
207+
const onKeyUp = e => {
208+
if (e.keyCode === 13) {
209+
const search = document.getElementById('search').value
210+
if (search) {
211+
router.push({ pathname: '/search/' + search })
212+
}
213+
}
214+
}
215+
216+
return (
217+
<>
218+
<div className="h-52">
219+
<h2 className="text-4xl">{locale.COMMON.NO_RESULTS_FOUND}</h2>
220+
<hr className="my-4" />
221+
<div className="max-w-md relative">
222+
<input
223+
autoFocus
224+
id="search"
225+
onClick={toggleShowSearchInput}
226+
onKeyUp={onKeyUp}
227+
className="float-left w-full outline-none h-full p-2 rounded dark:bg-[#383838] bg-gray-100"
228+
aria-label="Submit search"
229+
type="search"
230+
name="s"
231+
autoComplete="off"
232+
placeholder="Type then hit enter to search..."
233+
/>
234+
<i className="fas fa-search absolute right-0 my-auto p-2"></i>
235+
</div>
236+
</div>
237+
{/* 底部导航 */}
238+
<div className="h-full flex-grow grid grid-cols-4 gap-4">
239+
<LatestPostsGroup {...props} />
240+
<CategoryGroup {...props} />
241+
<ArchiveDateList {...props} />
242+
<TagGroups {...props} />
243+
</div>
244+
</>
245+
)
204246
}
205247

206248
/**

‎themes/simple/components/NavBar.js

+37-15
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { MenuList } from './MenuList'
99
* @param {*} props
1010
* @returns
1111
*/
12-
export default function NavBar (props) {
12+
export default function NavBar(props) {
1313
const [showSearchInput, changeShowSearchInput] = useState(false)
1414
const router = useRouter()
1515
const { searchModal } = useSimpleGlobal()
@@ -23,7 +23,7 @@ export default function NavBar (props) {
2323
}
2424
}
2525

26-
const onKeyUp = (e) => {
26+
const onKeyUp = e => {
2727
if (e.keyCode === 13) {
2828
const search = document.getElementById('theme-simple-search').value
2929
if (search) {
@@ -33,19 +33,41 @@ export default function NavBar (props) {
3333
}
3434

3535
return (
36-
<nav className="w-full bg-white md:pt-0 relative z-20 shadow border-t border-gray-100 dark:border-hexo-black-gray dark:bg-black">
37-
<div id="nav-bar-inner" className="h-12 mx-auto max-w-9/10 justify-between items-center text-sm md:text-md md:justify-start">
38-
{/* 左侧菜单 */}
39-
<div className="h-full w-full float-left text-center md:text-left flex flex-wrap items-stretch md:justify-start md:items-start space-x-4">
40-
{showSearchInput && <input autoFocus id="theme-simple-search" onKeyUp={onKeyUp} className='float-left w-full outline-none h-full px-4' aria-label="Submit search" type="search" name="s" autoComplete="off" placeholder="Type then hit enter to search..." />}
41-
{!showSearchInput && (<MenuList {...props}/>)}
42-
</div>
36+
<nav className="w-full bg-white md:pt-0 relative z-20 shadow border-t border-gray-100 dark:border-hexo-black-gray dark:bg-black">
37+
<div
38+
id="nav-bar-inner"
39+
className="h-12 mx-auto max-w-9/10 justify-between items-center text-sm md:text-md md:justify-start"
40+
>
41+
{/* 左侧菜单 */}
42+
<div className="h-full w-full float-left text-center md:text-left flex flex-wrap items-stretch md:justify-start md:items-start space-x-4">
43+
{showSearchInput && (
44+
<input
45+
autoFocus
46+
id="theme-simple-search"
47+
onKeyUp={onKeyUp}
48+
className="float-left w-full outline-none h-full px-4"
49+
aria-label="Submit search"
50+
type="search"
51+
name="s"
52+
autoComplete="off"
53+
placeholder="Type then hit enter to search..."
54+
/>
55+
)}
56+
{!showSearchInput && <MenuList {...props} />}
57+
</div>
4358

44-
<div className="absolute right-12 h-full text-center px-2 flex items-center text-blue-400 cursor-pointer">
45-
{/* <!-- extra links --> */}
46-
<i className={showSearchInput ? 'fa-regular fa-circle-xmark' : 'fa-solid fa-magnifying-glass' + ' align-middle'} onClick={toggleShowSearchInput}></i>
47-
</div>
48-
</div>
49-
</nav>
59+
<div className="absolute right-12 h-full text-center px-2 flex items-center text-blue-400 cursor-pointer">
60+
{/* <!-- extra links --> */}
61+
<i
62+
className={
63+
showSearchInput
64+
? 'fa-regular fa-circle-xmark'
65+
: 'fa-solid fa-magnifying-glass' + ' align-middle'
66+
}
67+
onClick={toggleShowSearchInput}
68+
></i>
69+
</div>
70+
</div>
71+
</nav>
5072
)
5173
}

0 commit comments

Comments
 (0)
Please sign in to comment.