diff --git a/src/components/home/system-info-card.tsx b/src/components/home/system-info-card.tsx index 9a4e170fa..6d93a57b8 100644 --- a/src/components/home/system-info-card.tsx +++ b/src/components/home/system-info-card.tsx @@ -7,13 +7,13 @@ import { } from '@mui/icons-material' import { Typography, Stack, Divider, Chip, IconButton } from '@mui/material' import { useLockFn } from 'ahooks' -import { useCallback, useEffect, useMemo, useReducer } from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useNavigate } from 'react-router' import { useServiceInstaller } from '@/hooks/use-service-installer' 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 { getSystemInfo } from '@/services/cmds' import { showNotice } from '@/services/notice-service' @@ -21,29 +21,6 @@ import { version as appVersion } from '@root/package.json' 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 = () => { const { t } = useTranslation() const { verge, patchVerge } = useVerge() @@ -51,28 +28,18 @@ export const SystemInfoCard = () => { const { isAdminMode, isSidecarMode } = useSystemState() const { installServiceAndRestartCore } = useServiceInstaller() - // 自动检查更新逻辑 - const { checkUpdate: triggerCheckUpdate } = 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(), - }) - }, - }) + // 自动检查更新逻辑(lastCheckUpdate 由 useUpdate 统一管理) + const { checkUpdate: triggerCheckUpdate, lastCheckUpdate } = useUpdate(true) - // 系统信息状态 - const [systemState, dispatchSystemState] = useReducer(systemStateReducer, { - osInfo: '', - lastCheckUpdate: '-', - }) + const [osInfo, setOsInfo] = useState('') + + const lastCheckUpdateText = useMemo( + () => (lastCheckUpdate ? new Date(lastCheckUpdate).toLocaleString() : '-'), + [lastCheckUpdate], + ) // 初始化系统信息 useEffect(() => { - let timeoutId: number | undefined - getSystemInfo() .then((info) => { const lines = info.split('\n') @@ -87,49 +54,24 @@ export const SystemInfoCard = () => { sysVersion = sysVersion.substring(sysName.length).trim() } - dispatchSystemState({ - type: 'set-os-info', - payload: `${sysName} ${sysVersion}`, - }) + setOsInfo(`${sysName} ${sysVersion}`) } }) .catch(console.error) + }, []) - // 获取最后检查更新时间 - const lastCheck = localStorage.getItem('last_check_update') - if (lastCheck) { - try { - const timestamp = parseInt(lastCheck, 10) - 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(), - }) + // 如果启用了自动检查更新但没有记录,设置当前时间并延迟检查 + useEffect(() => { + if (!verge?.auto_check_update) return + const stored = localStorage.getItem('last_check_update') + if (stored) return - timeoutId = window.setTimeout(() => { - if (verge?.auto_check_update) { - triggerCheckUpdate().catch(console.error) - } - }, 5000) - } - return () => { - if (timeoutId !== undefined) { - window.clearTimeout(timeoutId) - } - } - }, [verge?.auto_check_update, dispatchSystemState, triggerCheckUpdate]) + updateLastCheckTime() + const timeoutId = window.setTimeout(() => { + triggerCheckUpdate().catch(console.error) + }, 5000) + return () => window.clearTimeout(timeoutId) + }, [verge?.auto_check_update, triggerCheckUpdate]) // 导航到设置页面 const goToSettings = useCallback(() => { @@ -157,12 +99,6 @@ export const SystemInfoCard = () => { const onCheckUpdate = useLockFn(async () => { try { 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) { showNotice.success( 'settings.components.verge.advanced.notifications.latestVersion', @@ -280,7 +216,7 @@ export const SystemInfoCard = () => { {t('home.components.systemInfo.fields.osInfo')} - {systemState.osInfo} + {osInfo} @@ -341,7 +277,7 @@ export const SystemInfoCard = () => { '&:hover': { opacity: 0.7 }, }} > - {systemState.lastCheckUpdate} + {lastCheckUpdateText} diff --git a/src/components/setting/setting-verge-advanced.tsx b/src/components/setting/setting-verge-advanced.tsx index 625bf3bca..119a023db 100644 --- a/src/components/setting/setting-verge-advanced.tsx +++ b/src/components/setting/setting-verge-advanced.tsx @@ -4,6 +4,7 @@ import { useCallback, useRef } from 'react' import { useTranslation } from 'react-i18next' import { DialogRef, TooltipIcon } from '@/components/base' +import { updateLastCheckTime } from '@/hooks/use-update' import { exitApp, exportDiagnosticInfo, @@ -45,6 +46,7 @@ const SettingVergeAdvanced = ({ onError: _ }: Props) => { const onCheckUpdate = async () => { try { const info = await checkUpdate() + updateLastCheckTime() if (!info?.available) { showNotice.success( 'settings.components.verge.advanced.notifications.latestVersion', diff --git a/src/hooks/use-update.ts b/src/hooks/use-update.ts index eb954aee8..16ceed48e 100644 --- a/src/hooks/use-update.ts +++ b/src/hooks/use-update.ts @@ -1,4 +1,4 @@ -import useSWR, { SWRConfiguration } from 'swr' +import useSWR, { mutate as globalMutate, SWRConfiguration } from 'swr' import { checkUpdateSafe } from '@/services/update' @@ -12,6 +12,26 @@ export interface UpdateInfo { downloadAndInstall: (onEvent?: any) => Promise } +// --- 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 = ( enabled: boolean = true, options?: SWRConfiguration, @@ -36,11 +56,26 @@ export const useUpdate = ( refreshInterval: 24 * 60 * 60 * 1000, // 24 hours dedupingInterval: 60 * 60 * 1000, // 1 hour ...options, + onSuccess: (...args) => { + updateLastCheckTime() + options?.onSuccess?.(...args) + }, }) + // Shared last check timestamp + const { data: lastCheckUpdate } = useSWR( + LAST_CHECK_KEY, + readLastCheckFromStorage, + { + revalidateOnFocus: false, + revalidateOnReconnect: false, + }, + ) + return { updateInfo, checkUpdate, loading: isValidating, + lastCheckUpdate: lastCheckUpdate ?? null, } }