feat(console): 第5按钮·更新公告——dock最右侧新增铃铛按钮(反选切换),大磁片页:当前版本/检查更新/自动检查开关/新版本横幅+忽略此版本/公告正文(changelog.ts);更新检查核心抽到updateCheck.ts(键与设置面板复用)
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import { Fragment, useCallback, useEffect, useLayoutEffect, useRef, useState, type CSSProperties } from 'react'
|
||||
import { parentPathOf } from './pathutil'
|
||||
import { CHANGELOG_V030 } from './changelog'
|
||||
import { LOCAL_VERSION, checkUpdate, getAutoCheck, readCache, setAutoCheck as storeAutoCheck, setSkipVersion, type UpdateInfo, type UpdateStatus } from './updateCheck'
|
||||
import { Terminal } from 'xterm'
|
||||
|
||||
import MarkdownIt from 'markdown-it'
|
||||
@@ -1415,6 +1417,11 @@ function ConsolePane() {
|
||||
const [photoGrid, setPhotoGridState] = useState<boolean>(() => splitEnv?.console?.getPhotoGrid?.() ?? true)
|
||||
const [gridOpacity, setGridOpacityState] = useState<number>(() => splitEnv?.console?.getGridOpacity?.() ?? 8)
|
||||
const [cardBlur, setCardBlurState] = useState<number>(() => splitEnv?.console?.getCardBlur?.() ?? 0)
|
||||
const [annOpen, setAnnOpen] = useState(false)
|
||||
const [updStatus, setUpdStatus] = useState<UpdateStatus>(() => readCache().status)
|
||||
const [updInfo, setUpdInfo] = useState<UpdateInfo | null>(() => readCache().info)
|
||||
const [autoCheckOn, setAutoCheckOn] = useState<boolean>(() => getAutoCheck())
|
||||
const updBusyRef = useRef(false)
|
||||
const photoUrlRef = useRef<string>('')
|
||||
const [openMenu, setOpenMenu] = useState<string | null>(null)
|
||||
const [bgEdit, setBgEdit] = useState<'plain' | 'glow' | 'photo' | null>(null)
|
||||
@@ -1514,6 +1521,27 @@ function ConsolePane() {
|
||||
setCardBlurState(v)
|
||||
splitEnv?.console?.setCardBlur?.(v)
|
||||
}
|
||||
const runUpdateCheck = async (force: boolean) => {
|
||||
if (updBusyRef.current) return
|
||||
updBusyRef.current = true
|
||||
setUpdStatus('checking')
|
||||
try {
|
||||
const r = await checkUpdate(force)
|
||||
setUpdStatus(r.status)
|
||||
setUpdInfo(r.info)
|
||||
} finally {
|
||||
updBusyRef.current = false
|
||||
}
|
||||
}
|
||||
const onSkipVersion = () => {
|
||||
if (updInfo) { setSkipVersion(updInfo.latest); setUpdInfo(null); setUpdStatus('uptodate') }
|
||||
}
|
||||
const onAutoCheckToggle = () => {
|
||||
const next = !autoCheckOn
|
||||
setAutoCheckOn(next)
|
||||
storeAutoCheck(next)
|
||||
if (next) void runUpdateCheck(false)
|
||||
}
|
||||
/** 在媒体库中选中某条 */
|
||||
const selectPhoto = (p: { id: string; kind: 'photo' | 'video'; url: string }) => {
|
||||
setPhotoIdLocal(p.id)
|
||||
@@ -1605,6 +1633,8 @@ function ConsolePane() {
|
||||
useEffect(() => {
|
||||
splitEnv?.console?.refreshPreviews?.()
|
||||
}, [])
|
||||
// 自动检查更新(节流一天;手动按钮始终 force)
|
||||
useEffect(() => { if (getAutoCheck()) void runUpdateCheck(false) }, [])
|
||||
// 背景照片库:IndexedDB 异步加载记录 → 对象 URL;无记录则空
|
||||
useEffect(() => {
|
||||
let alive = true
|
||||
@@ -1684,6 +1714,26 @@ function ConsolePane() {
|
||||
{bg === 'glow' && (<><i className="dsh-wt_blob dsh-wt_blob1" /><i className="dsh-wt_blob dsh-wt_blob2" /><i className="dsh-wt_blob dsh-wt_blob3" /><i className="dsh-wt_blob dsh-wt_blob4" /></>)}</span>
|
||||
{openMenu !== null && <div className="dsh-wt_dropMask" onClick={() => setOpenMenu(null)} />}
|
||||
<div className="dsh-wt_consoleScroll">
|
||||
{annOpen ? (
|
||||
<div className="dsh-wt_announce">
|
||||
<div className="dsh-wt_announceHead">
|
||||
<span className="dsh-wt_announceVer">{T('annot.curVer')} v{LOCAL_VERSION}</span>
|
||||
<button type="button" className="dsh-wt_announceBtn" disabled={updStatus === 'checking'} onClick={() => runUpdateCheck(true)}>{updStatus === 'checking' ? T('annot.checking') : T('annot.checkNow')}</button>
|
||||
<span className="dsh-wt_announceAuto">{T('annot.autoCheck')}</span>
|
||||
<button type="button" className={'dsh-wt_switch' + (autoCheckOn ? ' dsh-wt_switchOn' : '')} aria-pressed={autoCheckOn} aria-label={T('annot.autoCheck')} onClick={onAutoCheckToggle}><span className="dsh-wt_switchKnob" /></button>
|
||||
</div>
|
||||
{updInfo && (
|
||||
<div className="dsh-wt_announceUpdate">
|
||||
<span className="dsh-wt_announceNewVer">{T('annot.newVer')} v{updInfo.latest}</span>
|
||||
<button type="button" className="dsh-wt_gridToggle dsh-wt_gridToggleOn" onClick={() => { try { window.open(updInfo.url, '_blank') } catch {} }}>{T('annot.gotoRelease')}</button>
|
||||
<button type="button" className="dsh-wt_announceSkip" onClick={onSkipVersion}>{T('annot.skipVer')}</button>
|
||||
</div>
|
||||
)}
|
||||
{updStatus === 'failed' && <div className="dsh-wt_announceStatus">{T('annot.checkFail')}</div>}
|
||||
{updStatus === 'uptodate' && !updInfo && <div className="dsh-wt_announceStatus">{T('annot.latest')}</div>}
|
||||
<div className="dsh-wt_announceBody">{CHANGELOG_V030}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div ref={gridRef} className="dsh-wt_consoleGrid" style={{ ['--wt-cols' as any]: cols }}>
|
||||
{cards.map((c) => (
|
||||
<div
|
||||
@@ -1729,7 +1779,8 @@ function ConsolePane() {
|
||||
<span className="dsh-wt_consoleAddLabel">{T('console.addProject')}</span>
|
||||
</div>
|
||||
{cards.length === 0 && <div className="dsh-wt_consoleEmpty">{T('console.empty')}</div>}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="dsh-wt_consoleDockWrap">
|
||||
<div className="dsh-wt_consoleDock">
|
||||
@@ -1737,6 +1788,7 @@ function ConsolePane() {
|
||||
<button type="button" className={'dsh-wt_dockBtn' + (openMenu === 'shape' ? ' dsh-wt_dockBtnOn' : '')} title={T('console.shapeLabel')} aria-label={T('console.shapeLabel')} onClick={() => setOpenMenu(openMenu === 'shape' ? null : 'shape')}><svg width="20" height="20" viewBox="0 0 16 16" aria-hidden><rect x="2.2" y="2.2" width="7" height="7" rx="1.5" fill="none" stroke="currentColor" strokeWidth="0.9" /><circle cx="10.6" cy="10.6" r="3.8" fill="none" stroke="currentColor" strokeWidth="0.9" /></svg></button>
|
||||
<button type="button" className={'dsh-wt_dockBtn' + (openMenu === 'bg' ? ' dsh-wt_dockBtnOn' : '')} title={T('console.bgLabel')} aria-label={T('console.bgLabel')} onClick={() => { setOpenMenu(openMenu === 'bg' ? null : 'bg'); setBgEdit(null) }}><svg width="18" height="18" viewBox="0 0 16 16" aria-hidden><rect x="2.2" y="3.2" width="11.6" height="9.6" rx="1.6" fill="none" stroke="currentColor" strokeWidth="0.9" /><circle cx="5.9" cy="6.7" r="1.05" fill="none" stroke="currentColor" strokeWidth="0.8" /><path d="M3.4 11.4l3-3 2.3 2.3 1.9-1.9 3 2.6" fill="none" stroke="currentColor" strokeWidth="0.9" /></svg></button>
|
||||
<button type="button" className={'dsh-wt_dockBtn' + (openMenu === 'cols' ? ' dsh-wt_dockBtnOn' : '')} title={T('console.colsLabel')} aria-label={T('console.colsLabel')} onClick={() => setOpenMenu(openMenu === 'cols' ? null : 'cols')}><svg width="20" height="20" viewBox="0 0 16 16" aria-hidden><rect x="2.2" y="3.4" width="2.7" height="9.2" rx="0.9" fill="none" stroke="currentColor" strokeWidth="0.9" /><rect x="6.65" y="3.4" width="2.7" height="9.2" rx="0.9" fill="none" stroke="currentColor" strokeWidth="0.9" /><rect x="11.1" y="3.4" width="2.7" height="9.2" rx="0.9" fill="none" stroke="currentColor" strokeWidth="0.9" /></svg></button>
|
||||
<button type="button" className={'dsh-wt_dockBtn' + (annOpen ? ' dsh-wt_dockBtnOn' : '')} title={T('annot.updateTitle')} aria-label={T('annot.updateTitle')} onClick={() => { setOpenMenu(null); setAnnOpen((v) => !v) }}><svg width="18" height="18" viewBox="0 0 16 16" aria-hidden><path d="M2.6 7c0-2.9 2.4-4.9 5.4-4.9s5.4 2 5.4 4.9v2.9l1.2 1.7H1.4l1.2-1.7z" fill="none" stroke="currentColor" strokeWidth="0.9" strokeLinejoin="round" /><path d="M6.2 12.9c.3.7 1 1.2 1.8 1.2s1.5-.5 1.8-1.2" fill="none" stroke="currentColor" strokeWidth="0.9" strokeLinecap="round" /></svg>{updInfo && <span className="dsh-wt_dockBadge" aria-hidden />}</button>
|
||||
</div>
|
||||
{openMenu === 'theme' && (
|
||||
<div className="dsh-wt_drop">
|
||||
|
||||
Reference in New Issue
Block a user