mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-04-13 05:20:28 +08:00
fix(home): unify last check update timestamp across settings and home page (#6605)
The settings page "Check for updates" did not update the homepage "last check update time" because each page managed the timestamp independently. Centralizes the timestamp in the useUpdate hook via SWR + localStorage so both pages share a single data source. Closes https://github.com/clash-verge-rev/clash-verge-rev/issues/6605#issuecomment-4147144987
This commit is contained in:
parent
c3aba3fc79
commit
99bbd7ee5a
@ -7,13 +7,13 @@ import {
|
|||||||
} from '@mui/icons-material'
|
} from '@mui/icons-material'
|
||||||
import { Typography, Stack, Divider, Chip, IconButton } from '@mui/material'
|
import { Typography, Stack, Divider, Chip, IconButton } from '@mui/material'
|
||||||
import { useLockFn } from 'ahooks'
|
import { useLockFn } from 'ahooks'
|
||||||
import { useCallback, useEffect, useMemo, useReducer } from 'react'
|
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useNavigate } from 'react-router'
|
import { useNavigate } from 'react-router'
|
||||||
|
|
||||||
import { useServiceInstaller } from '@/hooks/use-service-installer'
|
import { useServiceInstaller } from '@/hooks/use-service-installer'
|
||||||
import { useSystemState } from '@/hooks/use-system-state'
|
import { useSystemState } from '@/hooks/use-system-state'
|
||||||
import { useUpdate } from '@/hooks/use-update'
|
import { useUpdate, updateLastCheckTime } from '@/hooks/use-update'
|
||||||
import { useVerge } from '@/hooks/use-verge'
|
import { useVerge } from '@/hooks/use-verge'
|
||||||
import { getSystemInfo } from '@/services/cmds'
|
import { getSystemInfo } from '@/services/cmds'
|
||||||
import { showNotice } from '@/services/notice-service'
|
import { showNotice } from '@/services/notice-service'
|
||||||
@ -21,29 +21,6 @@ import { version as appVersion } from '@root/package.json'
|
|||||||
|
|
||||||
import { EnhancedCard } from './enhanced-card'
|
import { EnhancedCard } from './enhanced-card'
|
||||||
|
|
||||||
interface SystemState {
|
|
||||||
osInfo: string
|
|
||||||
lastCheckUpdate: string
|
|
||||||
}
|
|
||||||
|
|
||||||
type SystemStateAction =
|
|
||||||
| { type: 'set-os-info'; payload: string }
|
|
||||||
| { type: 'set-last-check-update'; payload: string }
|
|
||||||
|
|
||||||
const systemStateReducer = (
|
|
||||||
state: SystemState,
|
|
||||||
action: SystemStateAction,
|
|
||||||
): SystemState => {
|
|
||||||
switch (action.type) {
|
|
||||||
case 'set-os-info':
|
|
||||||
return { ...state, osInfo: action.payload }
|
|
||||||
case 'set-last-check-update':
|
|
||||||
return { ...state, lastCheckUpdate: action.payload }
|
|
||||||
default:
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SystemInfoCard = () => {
|
export const SystemInfoCard = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { verge, patchVerge } = useVerge()
|
const { verge, patchVerge } = useVerge()
|
||||||
@ -51,28 +28,18 @@ export const SystemInfoCard = () => {
|
|||||||
const { isAdminMode, isSidecarMode } = useSystemState()
|
const { isAdminMode, isSidecarMode } = useSystemState()
|
||||||
const { installServiceAndRestartCore } = useServiceInstaller()
|
const { installServiceAndRestartCore } = useServiceInstaller()
|
||||||
|
|
||||||
// 自动检查更新逻辑
|
// 自动检查更新逻辑(lastCheckUpdate 由 useUpdate 统一管理)
|
||||||
const { checkUpdate: triggerCheckUpdate } = useUpdate(true, {
|
const { checkUpdate: triggerCheckUpdate, lastCheckUpdate } = useUpdate(true)
|
||||||
onSuccess: () => {
|
|
||||||
const now = Date.now()
|
|
||||||
localStorage.setItem('last_check_update', now.toString())
|
|
||||||
dispatchSystemState({
|
|
||||||
type: 'set-last-check-update',
|
|
||||||
payload: new Date(now).toLocaleString(),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// 系统信息状态
|
const [osInfo, setOsInfo] = useState('')
|
||||||
const [systemState, dispatchSystemState] = useReducer(systemStateReducer, {
|
|
||||||
osInfo: '',
|
const lastCheckUpdateText = useMemo(
|
||||||
lastCheckUpdate: '-',
|
() => (lastCheckUpdate ? new Date(lastCheckUpdate).toLocaleString() : '-'),
|
||||||
})
|
[lastCheckUpdate],
|
||||||
|
)
|
||||||
|
|
||||||
// 初始化系统信息
|
// 初始化系统信息
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let timeoutId: number | undefined
|
|
||||||
|
|
||||||
getSystemInfo()
|
getSystemInfo()
|
||||||
.then((info) => {
|
.then((info) => {
|
||||||
const lines = info.split('\n')
|
const lines = info.split('\n')
|
||||||
@ -87,49 +54,24 @@ export const SystemInfoCard = () => {
|
|||||||
sysVersion = sysVersion.substring(sysName.length).trim()
|
sysVersion = sysVersion.substring(sysName.length).trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatchSystemState({
|
setOsInfo(`${sysName} ${sysVersion}`)
|
||||||
type: 'set-os-info',
|
|
||||||
payload: `${sysName} ${sysVersion}`,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
}, [])
|
||||||
|
|
||||||
// 获取最后检查更新时间
|
// 如果启用了自动检查更新但没有记录,设置当前时间并延迟检查
|
||||||
const lastCheck = localStorage.getItem('last_check_update')
|
useEffect(() => {
|
||||||
if (lastCheck) {
|
if (!verge?.auto_check_update) return
|
||||||
try {
|
const stored = localStorage.getItem('last_check_update')
|
||||||
const timestamp = parseInt(lastCheck, 10)
|
if (stored) return
|
||||||
if (!isNaN(timestamp)) {
|
|
||||||
dispatchSystemState({
|
|
||||||
type: 'set-last-check-update',
|
|
||||||
payload: new Date(timestamp).toLocaleString(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Error parsing last check update time', e)
|
|
||||||
}
|
|
||||||
} else if (verge?.auto_check_update) {
|
|
||||||
// 如果启用了自动检查更新但没有记录,设置当前时间并延迟检查
|
|
||||||
const now = Date.now()
|
|
||||||
localStorage.setItem('last_check_update', now.toString())
|
|
||||||
dispatchSystemState({
|
|
||||||
type: 'set-last-check-update',
|
|
||||||
payload: new Date(now).toLocaleString(),
|
|
||||||
})
|
|
||||||
|
|
||||||
timeoutId = window.setTimeout(() => {
|
updateLastCheckTime()
|
||||||
if (verge?.auto_check_update) {
|
const timeoutId = window.setTimeout(() => {
|
||||||
triggerCheckUpdate().catch(console.error)
|
triggerCheckUpdate().catch(console.error)
|
||||||
}
|
}, 5000)
|
||||||
}, 5000)
|
return () => window.clearTimeout(timeoutId)
|
||||||
}
|
}, [verge?.auto_check_update, triggerCheckUpdate])
|
||||||
return () => {
|
|
||||||
if (timeoutId !== undefined) {
|
|
||||||
window.clearTimeout(timeoutId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [verge?.auto_check_update, dispatchSystemState, triggerCheckUpdate])
|
|
||||||
|
|
||||||
// 导航到设置页面
|
// 导航到设置页面
|
||||||
const goToSettings = useCallback(() => {
|
const goToSettings = useCallback(() => {
|
||||||
@ -157,12 +99,6 @@ export const SystemInfoCard = () => {
|
|||||||
const onCheckUpdate = useLockFn(async () => {
|
const onCheckUpdate = useLockFn(async () => {
|
||||||
try {
|
try {
|
||||||
const info = await triggerCheckUpdate()
|
const info = await triggerCheckUpdate()
|
||||||
const now = Date.now()
|
|
||||||
localStorage.setItem('last_check_update', now.toString())
|
|
||||||
dispatchSystemState({
|
|
||||||
type: 'set-last-check-update',
|
|
||||||
payload: new Date(now).toLocaleString(),
|
|
||||||
})
|
|
||||||
if (!info?.available) {
|
if (!info?.available) {
|
||||||
showNotice.success(
|
showNotice.success(
|
||||||
'settings.components.verge.advanced.notifications.latestVersion',
|
'settings.components.verge.advanced.notifications.latestVersion',
|
||||||
@ -280,7 +216,7 @@ export const SystemInfoCard = () => {
|
|||||||
{t('home.components.systemInfo.fields.osInfo')}
|
{t('home.components.systemInfo.fields.osInfo')}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2" fontWeight="medium">
|
<Typography variant="body2" fontWeight="medium">
|
||||||
{systemState.osInfo}
|
{osInfo}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Divider />
|
<Divider />
|
||||||
@ -341,7 +277,7 @@ export const SystemInfoCard = () => {
|
|||||||
'&:hover': { opacity: 0.7 },
|
'&:hover': { opacity: 0.7 },
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{systemState.lastCheckUpdate}
|
{lastCheckUpdateText}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { useCallback, useRef } from 'react'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
import { DialogRef, TooltipIcon } from '@/components/base'
|
import { DialogRef, TooltipIcon } from '@/components/base'
|
||||||
|
import { updateLastCheckTime } from '@/hooks/use-update'
|
||||||
import {
|
import {
|
||||||
exitApp,
|
exitApp,
|
||||||
exportDiagnosticInfo,
|
exportDiagnosticInfo,
|
||||||
@ -45,6 +46,7 @@ const SettingVergeAdvanced = ({ onError: _ }: Props) => {
|
|||||||
const onCheckUpdate = async () => {
|
const onCheckUpdate = async () => {
|
||||||
try {
|
try {
|
||||||
const info = await checkUpdate()
|
const info = await checkUpdate()
|
||||||
|
updateLastCheckTime()
|
||||||
if (!info?.available) {
|
if (!info?.available) {
|
||||||
showNotice.success(
|
showNotice.success(
|
||||||
'settings.components.verge.advanced.notifications.latestVersion',
|
'settings.components.verge.advanced.notifications.latestVersion',
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import useSWR, { SWRConfiguration } from 'swr'
|
import useSWR, { mutate as globalMutate, SWRConfiguration } from 'swr'
|
||||||
|
|
||||||
import { checkUpdateSafe } from '@/services/update'
|
import { checkUpdateSafe } from '@/services/update'
|
||||||
|
|
||||||
@ -12,6 +12,26 @@ export interface UpdateInfo {
|
|||||||
downloadAndInstall: (onEvent?: any) => Promise<void>
|
downloadAndInstall: (onEvent?: any) => Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Last check timestamp (shared via SWR + localStorage) ---
|
||||||
|
|
||||||
|
const LAST_CHECK_KEY = 'last_check_update'
|
||||||
|
|
||||||
|
const readLastCheckFromStorage = (): number | null => {
|
||||||
|
const stored = localStorage.getItem(LAST_CHECK_KEY)
|
||||||
|
if (!stored) return null
|
||||||
|
const ts = parseInt(stored, 10)
|
||||||
|
return isNaN(ts) ? null : ts
|
||||||
|
}
|
||||||
|
|
||||||
|
export const updateLastCheckTime = (timestamp?: number): number => {
|
||||||
|
const now = timestamp ?? Date.now()
|
||||||
|
localStorage.setItem(LAST_CHECK_KEY, now.toString())
|
||||||
|
globalMutate(LAST_CHECK_KEY, now, false)
|
||||||
|
return now
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- useUpdate hook ---
|
||||||
|
|
||||||
export const useUpdate = (
|
export const useUpdate = (
|
||||||
enabled: boolean = true,
|
enabled: boolean = true,
|
||||||
options?: SWRConfiguration,
|
options?: SWRConfiguration,
|
||||||
@ -36,11 +56,26 @@ export const useUpdate = (
|
|||||||
refreshInterval: 24 * 60 * 60 * 1000, // 24 hours
|
refreshInterval: 24 * 60 * 60 * 1000, // 24 hours
|
||||||
dedupingInterval: 60 * 60 * 1000, // 1 hour
|
dedupingInterval: 60 * 60 * 1000, // 1 hour
|
||||||
...options,
|
...options,
|
||||||
|
onSuccess: (...args) => {
|
||||||
|
updateLastCheckTime()
|
||||||
|
options?.onSuccess?.(...args)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Shared last check timestamp
|
||||||
|
const { data: lastCheckUpdate } = useSWR(
|
||||||
|
LAST_CHECK_KEY,
|
||||||
|
readLastCheckFromStorage,
|
||||||
|
{
|
||||||
|
revalidateOnFocus: false,
|
||||||
|
revalidateOnReconnect: false,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
updateInfo,
|
updateInfo,
|
||||||
checkUpdate,
|
checkUpdate,
|
||||||
loading: isValidating,
|
loading: isValidating,
|
||||||
|
lastCheckUpdate: lastCheckUpdate ?? null,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user