Skip to content

Commit 06010df

Browse files
authored
Merge branch 'main' into main
2 parents 0847bc2 + a668dc8 commit 06010df

366 files changed

Lines changed: 2162 additions & 1656 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

components/AlgoliaSearchModal.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { siteConfig } from '@/lib/config'
33
import { useGlobal } from '@/lib/global'
44
import algoliasearch from 'algoliasearch'
55
import throttle from 'lodash/throttle'
6-
import Link from 'next/link'
6+
import SmartLink from '@/components/SmartLink'
77
import { useRouter } from 'next/router'
88
import {
99
Fragment,
@@ -104,7 +104,8 @@ export default function AlgoliaSearchModal({ cRef }) {
104104
// 跳转Search结果
105105
const onJumpSearchResult = () => {
106106
if (searchResults.length > 0) {
107-
window.location.href = `${siteConfig('SUB_PATH', '')}/${searchResults[activeIndex].slug}`
107+
const searchResult = searchResults[activeIndex]
108+
window.location.href = `${siteConfig('SUB_PATH', '')}/${searchResult.slug || searchResult.objectID}`
108109
}
109110
}
110111

@@ -356,7 +357,7 @@ function TagGroups() {
356357
<div id='tags-group' className='dark:border-gray-700 space-y-2'>
357358
{firstTenTags?.map((tag, index) => {
358359
return (
359-
<Link
360+
<SmartLink
360361
passHref
361362
key={index}
362363
href={`/tag/${encodeURIComponent(tag.name)}`}
@@ -372,7 +373,7 @@ function TagGroups() {
372373
<></>
373374
)}
374375
</div>
375-
</Link>
376+
</SmartLink>
376377
)
377378
})}
378379
</div>

components/CanvasEmail.js

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { useEffect, useRef, useState } from 'react'
2+
3+
const CanvasEmail = ({ email, className = '' }) => {
4+
const canvasRef = useRef(null)
5+
const textRef = useRef(null)
6+
const [isCopied, setIsCopied] = useState(false)
7+
8+
useEffect(() => {
9+
if (!textRef.current || !canvasRef.current) return
10+
11+
const canvas = canvasRef.current
12+
const ctx = canvas.getContext('2d')
13+
const textElement = textRef.current
14+
15+
// Get computed styles from the hidden text element
16+
const style = window.getComputedStyle(textElement)
17+
const font = style.font
18+
const color = style.color
19+
20+
// Set canvas font and measure text
21+
ctx.font = font
22+
const metrics = ctx.measureText(email)
23+
const fontSize = parseInt(style.fontSize)
24+
const lineHeight = fontSize * 1.2
25+
26+
// Set canvas dimensions
27+
const scale = window.devicePixelRatio || 1
28+
canvas.width = metrics.width * scale
29+
canvas.height = lineHeight * scale
30+
canvas.style.width = `${metrics.width}px`
31+
canvas.style.height = `${lineHeight}px`
32+
33+
// Redraw with high DPI support
34+
ctx.scale(scale, scale)
35+
ctx.font = font
36+
ctx.fillStyle = color
37+
ctx.textBaseline = 'top' // Changed to 'top' for better vertical alignment
38+
ctx.fillText(email, 0, 0)
39+
40+
// Handle copy to clipboard
41+
const handleCopy = e => {
42+
e.preventDefault()
43+
navigator.clipboard.writeText(email).then(() => {
44+
setIsCopied(true)
45+
setTimeout(() => setIsCopied(false), 2000)
46+
})
47+
}
48+
49+
canvas.style.cursor = 'pointer'
50+
canvas.addEventListener('click', handleCopy)
51+
return () => canvas.removeEventListener('click', handleCopy)
52+
}, [email])
53+
54+
return (
55+
<span
56+
className={`relative inline-block align-middle ${className}`}
57+
style={{ lineHeight: 'normal' }}>
58+
{/* Hidden span for measuring text metrics */}
59+
<span
60+
ref={textRef}
61+
style={{
62+
position: 'absolute',
63+
visibility: 'hidden',
64+
whiteSpace: 'nowrap',
65+
font: 'inherit',
66+
pointerEvents: 'none',
67+
userSelect: 'none',
68+
lineHeight: 'normal'
69+
}}></span>
70+
71+
{/* Canvas that displays the text */}
72+
<canvas
73+
ref={canvasRef}
74+
className='inline-block align-middle'
75+
style={{
76+
verticalAlign: 'middle',
77+
backgroundColor: 'transparent',
78+
pointerEvents: 'auto',
79+
font: 'inherit',
80+
lineHeight: 'normal',
81+
display: 'inline-block',
82+
userSelect: 'none',
83+
WebkitUserSelect: 'none',
84+
msUserSelect: 'none',
85+
MozUserSelect: 'none',
86+
KhtmlUserSelect: 'none'
87+
}}
88+
title={isCopied ? 'Copied!' : 'Click to copy'}
89+
aria-label={`Email: ${email}`}
90+
/>
91+
</span>
92+
)
93+
}
94+
95+
export default CanvasEmail

components/CustomContextMenu.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import useWindowSize from '@/hooks/useWindowSize'
22
import { siteConfig } from '@/lib/config'
33
import { useGlobal } from '@/lib/global'
44
import { THEMES, saveDarkModeToLocalStorage } from '@/themes/theme'
5-
import Link from 'next/link'
5+
import SmartLink from '@/components/SmartLink'
66
import { useRouter } from 'next/router'
77
import { useEffect, useLayoutEffect, useRef, useState } from 'react'
88

@@ -198,23 +198,23 @@ export default function CustomContextMenu(props) {
198198
)}
199199

200200
{CUSTOM_RIGHT_CLICK_CONTEXT_MENU_CATEGORY && (
201-
<Link
201+
<SmartLink
202202
href='/category'
203203
title={locale.MENU.CATEGORY}
204204
className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
205205
<i className='fa-solid fa-square-minus mr-2' />
206206
<div className='whitespace-nowrap'>{locale.MENU.CATEGORY}</div>
207-
</Link>
207+
</SmartLink>
208208
)}
209209

210210
{CUSTOM_RIGHT_CLICK_CONTEXT_MENU_TAG && (
211-
<Link
211+
<SmartLink
212212
href='/tag'
213213
title={locale.MENU.TAGS}
214214
className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
215215
<i className='fa-solid fa-tag mr-2' />
216216
<div className='whitespace-nowrap'>{locale.MENU.TAGS}</div>
217-
</Link>
217+
</SmartLink>
218218
)}
219219
</div>
220220

components/Player.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ const Player = () => {
6464
<link
6565
rel='stylesheet'
6666
type='text/css'
67-
href='https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/aplayer/1.10.1/APlayer.min.css'
67+
href='https://cdn.jsdelivr.net/npm/aplayer@1.10.0/dist/APlayer.min.css'
6868
/>
6969
{meting ? (
7070
<meting-js

components/SmartLink.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import Link from 'next/link'
2+
import { siteConfig } from '@/lib/config'
3+
4+
// 过滤 <a> 标签不能识别的 props
5+
const filterDOMProps = props => {
6+
const { passHref, legacyBehavior, ...rest } = props
7+
return rest
8+
}
9+
10+
const SmartLink = ({ href, children, ...rest }) => {
11+
const LINK = siteConfig('LINK')
12+
13+
// 获取 URL 字符串用于判断是否是外链
14+
let urlString = ''
15+
16+
if (typeof href === 'string') {
17+
urlString = href
18+
} else if (
19+
typeof href === 'object' &&
20+
href !== null &&
21+
typeof href.pathname === 'string'
22+
) {
23+
urlString = href.pathname
24+
}
25+
26+
const isExternal = urlString.startsWith('http') && !urlString.startsWith(LINK)
27+
28+
if (isExternal) {
29+
// 对于外部链接,必须是 string 类型
30+
const externalUrl =
31+
typeof href === 'string' ? href : new URL(href.pathname, LINK).toString()
32+
33+
return (
34+
<a
35+
href={externalUrl}
36+
target='_blank'
37+
rel='noopener noreferrer'
38+
{...filterDOMProps(rest)}>
39+
{children}
40+
</a>
41+
)
42+
}
43+
44+
// 内部链接(可为对象形式)
45+
return (
46+
<Link href={href} {...rest}>
47+
{children}
48+
</Link>
49+
)
50+
}
51+
52+
export default SmartLink

components/ui/dashboard/DashboardButton.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { siteConfig } from '@/lib/config'
2-
import Link from 'next/link'
2+
import SmartLink from '@/components/SmartLink'
33
import { useRouter } from 'next/router'
44
/**
55
* 跳转仪表盘的按钮
@@ -24,7 +24,7 @@ export default function DashboardButton({ className }) {
2424
<button
2525
type='button'
2626
className={`${className || ''} text-white bg-gray-800 hover:bg-gray-900 hover:ring-4 hover:ring-gray-300 focus:outline-none focus:ring-4 focus:ring-gray-300 font-medium rounded-lg text-sm px-5 py-2 me-2 dark:bg-gray-800 dark:hover:bg-gray-700 dark:focus:ring-gray-700 dark:border-gray-700`}>
27-
<Link href='/dashboard'>仪表盘</Link>
27+
<SmartLink href='/dashboard'>仪表盘</SmartLink>
2828
</button>
2929
)
3030
}

components/ui/dashboard/DashboardHeader.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import LazyImage from '@/components/LazyImage'
22
import { useGlobal } from '@/lib/global'
33
import formatDate from '@/lib/utils/formatDate'
4-
import Link from 'next/link'
4+
import SmartLink from '@/components/SmartLink'
55
import DashboardSignOutButton from './DashboardSignOutButton'
66

77
/**
@@ -25,11 +25,11 @@ export default function DashboardHeader() {
2525
<div class='font-medium dark:text-white'>
2626
<div className='flex items-center gap-x-2'>
2727
<span>{user?.fullName}</span>
28-
<Link href='/dashboard/membership'>
28+
<SmartLink href='/dashboard/membership'>
2929
<span class='bg-gray-100 text-gray-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-gray-300'>
3030
普通用户
3131
</span>
32-
</Link>
32+
</SmartLink>
3333
</div>
3434
<div className='text-sm text-gray-500 gap-x-2 flex dark:text-gray-400'>
3535
<span>{user?.username}</span>

components/ui/dashboard/DashboardItemAffliate.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import Link from 'next/link'
1+
import SmartLink from '@/components/SmartLink'
22

33
/**
44
* 联盟行销
@@ -140,11 +140,11 @@ export default function DashboardItemAffliate() {
140140
for='remember'
141141
className='ms-2 text-sm font-medium text-gray-900 dark:text-gray-300'>
142142
我以阅读并同意{' '}
143-
<Link
143+
<SmartLink
144144
href='/terms-of-use'
145145
className='text-blue-600 hover:underline dark:text-blue-500'>
146146
服务协议
147-
</Link>
147+
</SmartLink>
148148
.
149149
</label>
150150
</div>

components/ui/dashboard/DashboardMenuList.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import Link from 'next/link'
1+
import SmartLink from '@/components/SmartLink'
22

33
/**
44
* 仪表盘菜单
@@ -43,12 +43,12 @@ export default function DashboardMenuList() {
4343
className={`rounded-lg cursor-pointer block ${
4444
isActive ? 'bg-blue-100 text-blue-600' : 'hover:bg-gray-100'
4545
}`}>
46-
<Link
46+
<SmartLink
4747
href={item.href}
4848
className='block py-2 px-4 w-full items-center justify-center'>
4949
<i className={`${item.icon} w-6 mr-2`}></i>
5050
<span className='whitespace-nowrap'>{item.title}</span>
51-
</Link>
51+
</SmartLink>
5252
</li>
5353
)
5454
})}

conf/contact.config.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@
33
*/
44
module.exports = {
55
// 社交链接,不需要可留空白,例如 CONTACT_WEIBO:''
6-
CONTACT_EMAIL: process.env.NEXT_PUBLIC_CONTACT_EMAIL || '', // 邮箱地址 例如mail@tangly1024.com
6+
CONTACT_EMAIL:
7+
(process.env.NEXT_PUBLIC_CONTACT_EMAIL &&
8+
btoa(
9+
unescape(encodeURIComponent(process.env.NEXT_PUBLIC_CONTACT_EMAIL))
10+
)) ||
11+
'', // 邮箱地址 例如mail@tangly1024.com
712
CONTACT_WEIBO: process.env.NEXT_PUBLIC_CONTACT_WEIBO || '', // 你的微博个人主页
813
CONTACT_TWITTER: process.env.NEXT_PUBLIC_CONTACT_TWITTER || '', // 你的twitter个人主页
914
CONTACT_GITHUB: process.env.NEXT_PUBLIC_CONTACT_GITHUB || '', // 你的github个人主页 例如 https://github.com/tangly1024

0 commit comments

Comments
 (0)