diff --git a/src/components/home/system-info-card.tsx b/src/components/home/system-info-card.tsx index 4fd2fb3ed..11fcdf913 100644 --- a/src/components/home/system-info-card.tsx +++ b/src/components/home/system-info-card.tsx @@ -10,14 +10,13 @@ import { useLockFn } from "ahooks"; import { useCallback, useEffect, useMemo, useReducer } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router"; -import useSWR from "swr"; import { useServiceInstaller } from "@/hooks/use-service-installer"; import { useSystemState } from "@/hooks/use-system-state"; +import { useUpdate } from "@/hooks/use-update"; import { useVerge } from "@/hooks/use-verge"; import { getSystemInfo } from "@/services/cmds"; import { showNotice } from "@/services/notice-service"; -import { checkUpdateSafe as checkUpdate } from "@/services/update"; import { version as appVersion } from "@root/package.json"; import { EnhancedCard } from "./enhanced-card"; @@ -52,6 +51,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(), + }); + }, + }); + // 系统信息状态 const [systemState, dispatchSystemState] = useReducer(systemStateReducer, { osInfo: "", @@ -109,7 +120,7 @@ export const SystemInfoCard = () => { timeoutId = window.setTimeout(() => { if (verge?.auto_check_update) { - checkUpdate().catch(console.error); + triggerCheckUpdate().catch(console.error); } }, 5000); } @@ -118,26 +129,7 @@ export const SystemInfoCard = () => { window.clearTimeout(timeoutId); } }; - }, [verge?.auto_check_update, dispatchSystemState]); - - // 自动检查更新逻辑 - useSWR( - verge?.auto_check_update ? "checkUpdate" : null, - async () => { - const now = Date.now(); - localStorage.setItem("last_check_update", now.toString()); - dispatchSystemState({ - type: "set-last-check-update", - payload: new Date(now).toLocaleString(), - }); - return await checkUpdate(); - }, - { - revalidateOnFocus: false, - refreshInterval: 24 * 60 * 60 * 1000, // 每天检查一次 - dedupingInterval: 60 * 60 * 1000, // 1小时内不重复检查 - }, - ); + }, [verge?.auto_check_update, dispatchSystemState, triggerCheckUpdate]); // 导航到设置页面 const goToSettings = useCallback(() => { @@ -164,7 +156,7 @@ export const SystemInfoCard = () => { // 检查更新 const onCheckUpdate = useLockFn(async () => { try { - const info = await checkUpdate(); + const info = await triggerCheckUpdate(); if (!info?.available) { showNotice.success( "settings.components.verge.advanced.notifications.latestVersion", diff --git a/src/components/layout/update-button.tsx b/src/components/layout/update-button.tsx index 21da30351..53dba06f9 100644 --- a/src/components/layout/update-button.tsx +++ b/src/components/layout/update-button.tsx @@ -1,10 +1,8 @@ import { Button } from "@mui/material"; import { useRef } from "react"; -import useSWR from "swr"; import { DialogRef } from "@/components/base"; -import { useVerge } from "@/hooks/use-verge"; -import { checkUpdateSafe } from "@/services/update"; +import { useUpdate } from "@/hooks/use-update"; import { UpdateViewer } from "../setting/mods/update-viewer"; @@ -14,20 +12,9 @@ interface Props { export const UpdateButton = (props: Props) => { const { className } = props; - const { verge } = useVerge(); - const { auto_check_update } = verge || {}; - const viewerRef = useRef(null); - const { data: updateInfo } = useSWR( - auto_check_update || auto_check_update === null ? "checkUpdate" : null, - checkUpdateSafe, - { - errorRetryCount: 2, - revalidateIfStale: false, - focusThrottleInterval: 36e5, // 1 hour - }, - ); + const { updateInfo } = useUpdate(); if (!updateInfo?.available) return null; diff --git a/src/components/proxy/use-render-list.ts b/src/components/proxy/use-render-list.ts index a1333d6d5..21ed3ae97 100644 --- a/src/components/proxy/use-render-list.ts +++ b/src/components/proxy/use-render-list.ts @@ -1,9 +1,8 @@ import { useEffect, useMemo } from "react"; -import useSWR from "swr"; +import { useRuntimeConfig } from "@/hooks/use-clash"; import { useVerge } from "@/hooks/use-verge"; import { useAppData } from "@/providers/app-data-context"; -import { getRuntimeConfig } from "@/services/cmds"; import delayManager from "@/services/delay"; import { debugLog } from "@/utils/debug"; @@ -107,14 +106,7 @@ export const useRenderList = ( const latencyTimeout = verge?.default_latency_timeout; // 获取运行时配置用于链式代理模式 - const { data: runtimeConfig } = useSWR( - isChainMode ? "getRuntimeConfig" : null, - getRuntimeConfig, - { - revalidateOnFocus: false, - revalidateIfStale: true, - }, - ); + const { data: runtimeConfig } = useRuntimeConfig(!!isChainMode); // 计算列数 const col = useMemo( diff --git a/src/components/setting/mods/network-interface-viewer.tsx b/src/components/setting/mods/network-interface-viewer.tsx index 71db18308..7defbdd5d 100644 --- a/src/components/setting/mods/network-interface-viewer.tsx +++ b/src/components/setting/mods/network-interface-viewer.tsx @@ -4,10 +4,9 @@ import { writeText } from "@tauri-apps/plugin-clipboard-manager"; import type { Ref } from "react"; import { useImperativeHandle, useState } from "react"; import { useTranslation } from "react-i18next"; -import useSWR from "swr"; import { BaseDialog, DialogRef } from "@/components/base"; -import { getNetworkInterfacesInfo } from "@/services/cmds"; +import { useNetworkInterfaces } from "@/hooks/use-network"; import { showNotice } from "@/services/notice-service"; export function NetworkInterfaceViewer({ ref }: { ref?: Ref }) { @@ -22,13 +21,7 @@ export function NetworkInterfaceViewer({ ref }: { ref?: Ref }) { close: () => setOpen(false), })); - const { data: networkInterfaces } = useSWR( - "clash-verge-rev-internal://network-interfaces", - getNetworkInterfacesInfo, - { - fallbackData: [], // default data before fetch - }, - ); + const { networkInterfaces } = useNetworkInterfaces(); return ( }) { const { t } = useTranslation(); @@ -23,11 +22,7 @@ export function UpdateViewer({ ref }: { ref?: Ref }) { const updateState = useUpdateState(); const setUpdateState = useSetUpdateState(); - const { data: updateInfo } = useSWR("checkUpdate", checkUpdate, { - errorRetryCount: 2, - revalidateIfStale: false, - focusThrottleInterval: 36e5, // 1 hour - }); + const { updateInfo } = useUpdate(); const [downloaded, setDownloaded] = useState(0); const [total, setTotal] = useState(0); diff --git a/src/hooks/use-clash.ts b/src/hooks/use-clash.ts index 34dfca9fd..4053ad985 100644 --- a/src/hooks/use-clash.ts +++ b/src/hooks/use-clash.ts @@ -51,11 +51,12 @@ const validatePorts = (patch: ClashInfoPatch) => { }); }; +export const useRuntimeConfig = (shouldFetch: boolean = true) => { + return useSWR(shouldFetch ? "getRuntimeConfig" : null, getRuntimeConfig); +}; + export const useClash = () => { - const { data: clash, mutate: mutateClash } = useSWR( - "getRuntimeConfig", - getRuntimeConfig, - ); + const { data: clash, mutate: mutateClash } = useRuntimeConfig(); const { data: versionData, mutate: mutateVersion } = useSWR( "getVersion", diff --git a/src/hooks/use-network.ts b/src/hooks/use-network.ts new file mode 100644 index 000000000..567dfe465 --- /dev/null +++ b/src/hooks/use-network.ts @@ -0,0 +1,22 @@ +import useSWR from "swr"; + +import { getNetworkInterfacesInfo } from "@/services/cmds"; + +export const useNetworkInterfaces = () => { + const { data, error, isLoading, mutate } = useSWR( + "getNetworkInterfacesInfo", + getNetworkInterfacesInfo, + { + revalidateOnFocus: false, + revalidateOnReconnect: false, + fallbackData: [], + }, + ); + + return { + networkInterfaces: data || [], + loading: isLoading, + error, + mutate, + }; +}; diff --git a/src/hooks/use-update.ts b/src/hooks/use-update.ts new file mode 100644 index 000000000..76dc578b8 --- /dev/null +++ b/src/hooks/use-update.ts @@ -0,0 +1,46 @@ +import useSWR, { SWRConfiguration } from "swr"; + +import { checkUpdateSafe } from "@/services/update"; + +import { useVerge } from "./use-verge"; + +export interface UpdateInfo { + version: string; + body: string; + date: string; + available: boolean; + downloadAndInstall: (onEvent?: any) => Promise; +} + +export const useUpdate = ( + enabled: boolean = true, + options?: SWRConfiguration, +) => { + const { verge } = useVerge(); + const { auto_check_update } = verge || {}; + + // Determine if we should check for updates + // If enabled is explicitly false, don't check + // Otherwise, respect the auto_check_update setting (or default to true if null/undefined for manual triggers) + const shouldCheck = enabled && auto_check_update !== false; + + const { + data: updateInfo, + mutate: checkUpdate, + isValidating, + } = useSWR(shouldCheck ? "checkUpdate" : null, checkUpdateSafe, { + errorRetryCount: 2, + revalidateIfStale: false, + revalidateOnFocus: false, + focusThrottleInterval: 36e5, // 1 hour + refreshInterval: 24 * 60 * 60 * 1000, // 24 hours + dedupingInterval: 60 * 60 * 1000, // 1 hour + ...options, + }); + + return { + updateInfo, + checkUpdate, + loading: isValidating, + }; +};