mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-10-14 21:20:48 +08:00
refactor(frontend): consolidate query state ownership
Consolidate ordinary query snapshots on SWR and make Profiles own its query key, cache mutation, and invalidation policy. Impact: - remove the generic synchronous query Map while keeping dedicated Mihomo WebSocket snapshots - eliminate duplicate Profiles refreshes while preserving retries and the 800 ms event throttle - preserve preload-backed Verge updates and system proxy rollback behavior - scope uptime polling to the Home Clash info card instead of the app lifetime - leave RunState, Connections, Traffic, backend, and IPC contracts unchanged - reduce frontend code by 72 net lines without adding dependencies
This commit is contained in:
@@ -5,11 +5,11 @@ import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { useClash } from '@/hooks/use-clash'
|
||||
import { useDisplayedMixedPort } from '@/hooks/use-displayed-mixed-port'
|
||||
import { useAppUptime } from '@/hooks/use-system-state'
|
||||
import {
|
||||
useClashConfigData,
|
||||
useRulesData,
|
||||
useSystemData,
|
||||
useUptimeData,
|
||||
} from '@/providers/app-data-context'
|
||||
|
||||
import { EnhancedCard } from './enhanced-card'
|
||||
@@ -28,7 +28,7 @@ export const ClashInfoCard = () => {
|
||||
const { clashConfig } = useClashConfigData()
|
||||
const displayedMixedPort = useDisplayedMixedPort()
|
||||
const { rules } = useRulesData()
|
||||
const { uptime } = useUptimeData()
|
||||
const uptime = useAppUptime()
|
||||
const { systemProxyAddress } = useSystemData()
|
||||
|
||||
// 使用useMemo缓存格式化后的uptime,避免频繁计算
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
type RunState,
|
||||
} from '@/services/cmds'
|
||||
import { showNotice } from '@/services/notice-service'
|
||||
import { setCacheDataAsync, useQuery } from '@/services/query-client'
|
||||
import { setCacheData, useQuery } from '@/services/query-client'
|
||||
|
||||
export const ServiceMigrationDialog = () => {
|
||||
const { t } = useTranslation()
|
||||
@@ -50,7 +50,7 @@ export const ServiceMigrationDialog = () => {
|
||||
const refreshRunState = async () => {
|
||||
try {
|
||||
const data = await getRuntimeState()
|
||||
await setCacheDataAsync<RunState>(runStateQueryKey, data)
|
||||
await setCacheData<RunState>(runStateQueryKey, data)
|
||||
setStateRefreshFailed(false)
|
||||
return data
|
||||
} catch (error) {
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
patchClashConfig,
|
||||
} from '@/services/cmds'
|
||||
import {
|
||||
getCacheData,
|
||||
revalidateQuery,
|
||||
setCacheData,
|
||||
useQuery,
|
||||
@@ -97,11 +96,7 @@ export const useClash = () => {
|
||||
if (updater === undefined) {
|
||||
return refetch()
|
||||
}
|
||||
const next =
|
||||
typeof updater === 'function'
|
||||
? updater(getCacheData<IConfigData>(['getRuntimeConfig']))
|
||||
: updater
|
||||
setCacheData(['getRuntimeConfig'], next)
|
||||
void setCacheData<IConfigData>(['getRuntimeConfig'], updater)
|
||||
if (revalidate !== false) {
|
||||
return refetch()
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { useEffect, useRef } from 'react'
|
||||
import { MihomoWebSocket, type LogLevel } from 'tauri-plugin-mihomo-api'
|
||||
|
||||
import { getClashLogs } from '@/services/cmds'
|
||||
import { setCacheData } from '@/services/query-client'
|
||||
|
||||
import { useClashLog } from './use-clash-log'
|
||||
import { useMihomoWsSubscription } from './use-mihomo-ws-subscription'
|
||||
@@ -54,9 +53,7 @@ export const useLogData = () => {
|
||||
const allowedTypes = LOG_LEVEL_FILTERS[logLevel] ?? DEFAULT_LOG_TYPES
|
||||
const hasLoadedInitialLogsRef = useRef(false)
|
||||
|
||||
const { response, refresh, subscriptionCacheKey } = useMihomoWsSubscription<
|
||||
ILogItem[]
|
||||
>({
|
||||
const { response, refresh, setData } = useMihomoWsSubscription<ILogItem[]>({
|
||||
storageKey: 'mihomo_logs_date',
|
||||
buildSubscriptKey: (date) => (enableLog ? `getClashLog-${date}` : null),
|
||||
fallbackData: [],
|
||||
@@ -154,9 +151,7 @@ export const useLogData = () => {
|
||||
|
||||
const refreshGetClashLog = (clear = false) => {
|
||||
if (clear) {
|
||||
if (subscriptionCacheKey) {
|
||||
setCacheData<ILogItem[]>([subscriptionCacheKey], [])
|
||||
}
|
||||
setData([])
|
||||
} else {
|
||||
hasLoadedInitialLogsRef.current = false
|
||||
refresh()
|
||||
|
||||
@@ -3,7 +3,6 @@ import { type MutableRefObject, useCallback, useEffect, useRef } from 'react'
|
||||
import { type Message, type MihomoWebSocket } from 'tauri-plugin-mihomo-api'
|
||||
|
||||
import {
|
||||
getCacheData,
|
||||
removeCacheData,
|
||||
setCacheData,
|
||||
useQuery,
|
||||
@@ -32,8 +31,17 @@ interface SharedSubscriptionEntry {
|
||||
}
|
||||
|
||||
const sharedSubscriptions = new Map<string, SharedSubscriptionEntry>()
|
||||
const subscriptionSnapshots = new Map<string, unknown>()
|
||||
const initialSubscriptionDate = Date.now()
|
||||
|
||||
const getSubscriptionSnapshot = <T>(key: string) =>
|
||||
subscriptionSnapshots.get(key) as T | undefined
|
||||
|
||||
const writeSubscriptionSnapshot = <T>(key: string, data: T) => {
|
||||
subscriptionSnapshots.set(key, data)
|
||||
void setCacheData<T>([key], data)
|
||||
}
|
||||
|
||||
const syncSharedWsRefs = (entry: SharedSubscriptionEntry) => {
|
||||
entry.refHolders.forEach((ref) => {
|
||||
ref.current = entry.ws
|
||||
@@ -141,7 +149,7 @@ const createSharedSubscriptionEntry = (
|
||||
/**
|
||||
* Mirrors SWR's MutatorCallback: consumers can pass either a plain value or a
|
||||
* functional updater `(current?: T) => T`. The functional form is resolved
|
||||
* against the current cache entry before calling `setCacheData`.
|
||||
* against the current subscription snapshot before updating SWR.
|
||||
*/
|
||||
type NextFn<T> = (
|
||||
error?: any,
|
||||
@@ -208,7 +216,7 @@ export const useMihomoWsSubscription = <T>(
|
||||
): T => {
|
||||
if (typeof data === 'function') {
|
||||
const updater = data as (current?: T) => T | undefined
|
||||
const current = getCacheData<T>([cacheKey])
|
||||
const current = getSubscriptionSnapshot<T>(cacheKey)
|
||||
return updater(current) ?? fallbackData
|
||||
}
|
||||
return data ?? fallbackData
|
||||
@@ -218,9 +226,10 @@ export const useMihomoWsSubscription = <T>(
|
||||
|
||||
const response = useQuery<T>({
|
||||
queryKey: responseCacheKey ? [responseCacheKey] : ['$sub$__disabled__'],
|
||||
queryFn: () => getCacheData<T>([responseCacheKey!]) ?? fallbackData,
|
||||
queryFn: () =>
|
||||
getSubscriptionSnapshot<T>(responseCacheKey!) ?? fallbackData,
|
||||
initialData: () =>
|
||||
getCacheData<T>([responseCacheKey ?? '$sub$__disabled__']) ??
|
||||
getSubscriptionSnapshot<T>(responseCacheKey ?? '$sub$__disabled__') ??
|
||||
fallbackData,
|
||||
staleTime: Infinity,
|
||||
enabled: subscriptionCacheKey !== null,
|
||||
@@ -249,7 +258,7 @@ export const useMihomoWsSubscription = <T>(
|
||||
}
|
||||
if (data === undefined) return
|
||||
const resolved = resolveNextData(data, subscriptionCacheKey)
|
||||
setCacheData<T>([subscriptionCacheKey], resolved)
|
||||
writeSubscriptionSnapshot(subscriptionCacheKey, resolved)
|
||||
}
|
||||
|
||||
if (throttleMs && throttleMs > 0) {
|
||||
@@ -352,10 +361,20 @@ export const useMihomoWsSubscription = <T>(
|
||||
|
||||
const refresh = useCallback(() => {
|
||||
if (subscriptionCacheKey) {
|
||||
removeCacheData([subscriptionCacheKey])
|
||||
subscriptionSnapshots.delete(subscriptionCacheKey)
|
||||
void removeCacheData([subscriptionCacheKey])
|
||||
}
|
||||
setDate(Date.now())
|
||||
}, [subscriptionCacheKey, setDate])
|
||||
|
||||
return { response, refresh, subscriptionCacheKey: responseCacheKey, wsRef }
|
||||
const setData = useCallback(
|
||||
(data: T) => {
|
||||
if (responseCacheKey) {
|
||||
writeSubscriptionSnapshot(responseCacheKey, data)
|
||||
}
|
||||
},
|
||||
[responseCacheKey],
|
||||
)
|
||||
|
||||
return { response, refresh, setData, wsRef }
|
||||
}
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
import { useCallback, useRef } from 'react'
|
||||
|
||||
import { getProfiles, patchProfile, patchProfilesConfig } from '@/services/cmds'
|
||||
import { setCacheDataAsync, useQuery } from '@/services/query-client'
|
||||
import {
|
||||
fetchCacheData,
|
||||
revalidateQuery,
|
||||
setCacheData,
|
||||
useQuery,
|
||||
} from '@/services/query-client'
|
||||
import { debugLog } from '@/utils/debug'
|
||||
|
||||
const profilesQueryKey = ['getProfiles'] as const
|
||||
|
||||
export const revalidateProfiles = () => revalidateQuery(profilesQueryKey)
|
||||
|
||||
export const fetchProfilesIntoCache = () =>
|
||||
fetchCacheData(profilesQueryKey, getProfiles)
|
||||
|
||||
export const useProfiles = () => {
|
||||
const {
|
||||
data: profiles,
|
||||
@@ -11,7 +23,7 @@ export const useProfiles = () => {
|
||||
error,
|
||||
isFetching: isValidating,
|
||||
} = useQuery({
|
||||
queryKey: ['getProfiles'],
|
||||
queryKey: profilesQueryKey,
|
||||
queryFn: async () => {
|
||||
const data = await getProfiles()
|
||||
debugLog(
|
||||
@@ -40,9 +52,8 @@ export const useProfiles = () => {
|
||||
const outcome = await patchProfilesConfig(value)
|
||||
|
||||
if (outcome.status === 'valid') {
|
||||
await setCacheDataAsync<IProfilesConfig>(
|
||||
['getProfiles'],
|
||||
(current) => (current ? { ...current, ...value } : current),
|
||||
await setCacheData<IProfilesConfig>(profilesQueryKey, (current) =>
|
||||
current ? { ...current, ...value } : current,
|
||||
)
|
||||
} else if (outcome.status !== 'busy') {
|
||||
await mutateProfiles()
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
patchVergeConfig,
|
||||
} from '@/services/cmds'
|
||||
import {
|
||||
getCacheData,
|
||||
removeCacheData,
|
||||
revalidateQueries,
|
||||
useQuery,
|
||||
@@ -51,9 +50,7 @@ export const useSystemProxyState = () => {
|
||||
|
||||
const toggleSystemProxy = async (enabled: boolean) => {
|
||||
// Roll failed optimistic writes back to the latest confirmed state.
|
||||
let confirmed =
|
||||
getCacheData<IVergeConfig>(['getVergeConfig'])?.enable_system_proxy ??
|
||||
false
|
||||
let confirmed = verge?.enable_system_proxy ?? false
|
||||
mutateVerge(
|
||||
(prev) => (prev ? { ...prev, enable_system_proxy: enabled } : prev),
|
||||
false,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
getAppUptime,
|
||||
getRuntimeState,
|
||||
type RunState,
|
||||
type RunningMode,
|
||||
@@ -8,6 +9,7 @@ import { useQuery } from '@/services/query-client'
|
||||
import { useVisibility } from './use-visibility'
|
||||
|
||||
export const runStateQueryKey = ['getRuntimeState'] as const
|
||||
const appUptimeQueryKey = ['appUptime'] as const
|
||||
|
||||
/** Fail closed until the first snapshot so TUN never flashes as available. */
|
||||
const unknownRunState: RunState = {
|
||||
@@ -52,3 +54,17 @@ export function useSystemState() {
|
||||
isLoading,
|
||||
}
|
||||
}
|
||||
|
||||
export function useAppUptime() {
|
||||
const { data: uptime = 0 } = useQuery({
|
||||
queryKey: appUptimeQueryKey,
|
||||
queryFn: getAppUptime,
|
||||
staleTime: 5000,
|
||||
refetchInterval: 3000,
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
retry: 1,
|
||||
})
|
||||
|
||||
return uptime
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useCallback } from 'react'
|
||||
|
||||
import { getVergeConfig, patchVergeConfig } from '@/services/cmds'
|
||||
import { getPreloadConfig, setPreloadConfig } from '@/services/preload'
|
||||
import { getCacheData, setCacheData, useQuery } from '@/services/query-client'
|
||||
import { setCacheData, useQuery } from '@/services/query-client'
|
||||
|
||||
export const useVerge = () => {
|
||||
const initialVergeConfig = getPreloadConfig()
|
||||
@@ -30,13 +30,12 @@ export const useVerge = () => {
|
||||
void refetch()
|
||||
return
|
||||
}
|
||||
if (typeof updaterOrData === 'function') {
|
||||
const prev = getCacheData<IVergeConfig>(['getVergeConfig'])
|
||||
const next = updaterOrData(prev)
|
||||
setCacheData(['getVergeConfig'], next)
|
||||
} else {
|
||||
setCacheData(['getVergeConfig'], updaterOrData)
|
||||
}
|
||||
void setCacheData<IVergeConfig>(
|
||||
['getVergeConfig'],
|
||||
typeof updaterOrData === 'function'
|
||||
? (current) => updaterOrData(current ?? verge)
|
||||
: updaterOrData,
|
||||
)
|
||||
}
|
||||
|
||||
const patchVerge = useCallback(
|
||||
|
||||
@@ -1,20 +1,40 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
import { revalidateProfiles } from '@/hooks/use-profiles'
|
||||
import { runStateQueryKey } from '@/hooks/use-system-state'
|
||||
import type { RunState } from '@/services/cmds'
|
||||
import { subscribeVergeEvents } from '@/services/events'
|
||||
import { revalidateQueries, setCacheDataAsync } from '@/services/query-client'
|
||||
import { revalidateQueries, setCacheData } from '@/services/query-client'
|
||||
|
||||
export const useLayoutEvents = (
|
||||
handleNotice: (payload: [string, string]) => void,
|
||||
) => {
|
||||
useEffect(() => {
|
||||
let lastProfileId: string | null = null
|
||||
let lastProfileUpdateTime = 0
|
||||
const refreshThrottle = 800
|
||||
|
||||
const revalidateKeys = (keys: readonly string[]) => {
|
||||
void revalidateQueries(keys.map((key) => [key]))
|
||||
}
|
||||
|
||||
const handleProfileChanged = (newProfileId: string) => {
|
||||
const now = Date.now()
|
||||
if (
|
||||
lastProfileId === newProfileId &&
|
||||
now - lastProfileUpdateTime < refreshThrottle
|
||||
) {
|
||||
return
|
||||
}
|
||||
lastProfileId = newProfileId
|
||||
lastProfileUpdateTime = now
|
||||
void revalidateProfiles()
|
||||
}
|
||||
|
||||
return subscribeVergeEvents(
|
||||
{
|
||||
'profile-changed': handleProfileChanged,
|
||||
'verge://refresh-profiles': () => void revalidateProfiles(),
|
||||
'verge://refresh-clash-config': () => {
|
||||
revalidateKeys([
|
||||
'getProxyView',
|
||||
@@ -36,7 +56,7 @@ export const useLayoutEvents = (
|
||||
},
|
||||
// Transitions carry the full run-state snapshot, so write it directly to cache.
|
||||
'verge://run-state-changed': (payload) => {
|
||||
void setCacheDataAsync<RunState>(runStateQueryKey, payload)
|
||||
void setCacheData<RunState>(runStateQueryKey, payload)
|
||||
},
|
||||
'verge://notice-message': handleNotice,
|
||||
},
|
||||
|
||||
+4
-11
@@ -40,12 +40,11 @@ import {
|
||||
} from '@/components/profile/profile-viewer'
|
||||
import { ConfigViewer } from '@/components/setting/mods/config-viewer'
|
||||
import { useListen } from '@/hooks/use-listen'
|
||||
import { useProfiles } from '@/hooks/use-profiles'
|
||||
import { fetchProfilesIntoCache, useProfiles } from '@/hooks/use-profiles'
|
||||
import {
|
||||
createProfile,
|
||||
deleteProfile,
|
||||
enhanceProfiles,
|
||||
getProfiles,
|
||||
getRuntimeLogs,
|
||||
importProfile,
|
||||
reorderProfile,
|
||||
@@ -53,11 +52,7 @@ import {
|
||||
} from '@/services/cmds'
|
||||
import { subscribeVergeEvents } from '@/services/events'
|
||||
import { errorDetail, showNotice } from '@/services/notice-service'
|
||||
import {
|
||||
fetchCacheData,
|
||||
revalidateQueries,
|
||||
useQuery,
|
||||
} from '@/services/query-client'
|
||||
import { revalidateQuery, useQuery } from '@/services/query-client'
|
||||
import {
|
||||
useLoadingCache,
|
||||
useSetLoadingCache,
|
||||
@@ -175,9 +170,7 @@ const ProfilePage = () => {
|
||||
debugLog('[紧急刷新] 开始强制刷新所有数据')
|
||||
|
||||
try {
|
||||
await revalidateQueries([['getProfiles'], ['getRuntimeLogs']])
|
||||
|
||||
await mutateProfiles()
|
||||
await Promise.all([revalidateQuery(['getRuntimeLogs']), mutateProfiles()])
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||
await onEnhance(false)
|
||||
@@ -294,7 +287,7 @@ const ProfilePage = () => {
|
||||
|
||||
console.warn(`[导入刷新] 常规刷新失败,尝试清除缓存重新获取`)
|
||||
try {
|
||||
await fetchCacheData(['getProfiles'], getProfiles)
|
||||
await fetchProfilesIntoCache()
|
||||
await onEnhance(false)
|
||||
showNotice.error(
|
||||
'profiles.page.feedback.notifications.importNeedsRefresh',
|
||||
|
||||
@@ -26,10 +26,6 @@ export interface SystemContextType {
|
||||
systemProxyAddress: string
|
||||
}
|
||||
|
||||
export interface UptimeContextType {
|
||||
uptime: number
|
||||
}
|
||||
|
||||
export interface CoreDataStatusContextType {
|
||||
isCoreDataPending: boolean
|
||||
}
|
||||
@@ -49,7 +45,6 @@ export const ClashConfigContext = createContext<ClashConfigContextType | null>(
|
||||
null,
|
||||
)
|
||||
export const SystemContext = createContext<SystemContextType | null>(null)
|
||||
export const UptimeContext = createContext<UptimeContextType | null>(null)
|
||||
export const CoreDataStatusContext =
|
||||
createContext<CoreDataStatusContextType | null>(null)
|
||||
export const RefreshersContext = createContext<RefreshersContextType | null>(
|
||||
@@ -80,9 +75,6 @@ export const useClashConfigData = (): ClashConfigContextType =>
|
||||
export const useSystemData = (): SystemContextType =>
|
||||
useCtx(SystemContext, 'useSystemData')
|
||||
|
||||
export const useUptimeData = (): UptimeContextType =>
|
||||
useCtx(UptimeContext, 'useUptimeData')
|
||||
|
||||
export const useAppRefreshers = (): RefreshersContextType =>
|
||||
useCtx(RefreshersContext, 'useAppRefreshers')
|
||||
|
||||
|
||||
@@ -8,14 +8,9 @@ import {
|
||||
import { useClashInfo, useRuntimeConfig } from '@/hooks/use-clash'
|
||||
import { runStateQueryKey } from '@/hooks/use-system-state'
|
||||
import { useVerge } from '@/hooks/use-verge'
|
||||
import {
|
||||
getAppUptime,
|
||||
getProxyView,
|
||||
getRuntimeState,
|
||||
getSystemProxy,
|
||||
} from '@/services/cmds'
|
||||
import { getProxyView, getRuntimeState, getSystemProxy } from '@/services/cmds'
|
||||
import { subscribeVergeEvents } from '@/services/events'
|
||||
import { revalidateQueries, useQuery } from '@/services/query-client'
|
||||
import { useQuery } from '@/services/query-client'
|
||||
import { resolveDisplayedMixedPort } from '@/utils/mixed-port'
|
||||
|
||||
import {
|
||||
@@ -25,7 +20,6 @@ import {
|
||||
RefreshersContext,
|
||||
RulesContext,
|
||||
SystemContext,
|
||||
UptimeContext,
|
||||
} from './app-data-context'
|
||||
|
||||
const TQ_MIHOMO = {
|
||||
@@ -109,14 +103,6 @@ export const AppDataProvider = ({
|
||||
})
|
||||
const runningMode = runState?.mode
|
||||
|
||||
const { data: uptimeData } = useQuery({
|
||||
queryKey: ['appUptime'],
|
||||
queryFn: getAppUptime,
|
||||
...TQ_DEFAULTS,
|
||||
refetchInterval: 3000,
|
||||
retry: 1,
|
||||
})
|
||||
|
||||
const refreshProxy = useStableFn(_refetchProxyView)
|
||||
const refreshClashConfig = useStableFn(_refetchClashConfig)
|
||||
const refreshRules = useStableFn(_refetchRules)
|
||||
@@ -124,22 +110,8 @@ export const AppDataProvider = ({
|
||||
const refreshRuleProviders = useStableFn(_refetchRuleProviders)
|
||||
|
||||
useEffect(() => {
|
||||
let lastProfileId: string | null = null
|
||||
let lastProfileUpdateTime = 0
|
||||
let lastProxyUpdateTime = 0
|
||||
const refreshThrottle = 800
|
||||
const handleProfileChanged = (newProfileId: string) => {
|
||||
const now = Date.now()
|
||||
if (
|
||||
lastProfileId === newProfileId &&
|
||||
now - lastProfileUpdateTime < refreshThrottle
|
||||
) {
|
||||
return
|
||||
}
|
||||
lastProfileId = newProfileId
|
||||
lastProfileUpdateTime = now
|
||||
void revalidateQueries([['getProfiles']])
|
||||
}
|
||||
|
||||
const handleRefreshProxy = () => {
|
||||
const now = Date.now()
|
||||
@@ -148,13 +120,7 @@ export const AppDataProvider = ({
|
||||
refreshProxy().catch(() => {})
|
||||
}
|
||||
|
||||
const handleRefreshProfiles = () => {
|
||||
void revalidateQueries([['getProfiles']])
|
||||
}
|
||||
|
||||
return subscribeVergeEvents({
|
||||
'profile-changed': handleProfileChanged,
|
||||
'verge://refresh-profiles': handleRefreshProfiles,
|
||||
'verge://refresh-proxy-config': handleRefreshProxy,
|
||||
})
|
||||
}, [refreshProxy])
|
||||
@@ -244,8 +210,6 @@ export const AppDataProvider = ({
|
||||
}
|
||||
}, [sysproxy, runningMode, isRunningModePending, verge, displayedMixedPort])
|
||||
|
||||
const uptimeValue = useMemo(() => ({ uptime: uptimeData || 0 }), [uptimeData])
|
||||
|
||||
const coreDataStatusValue = useMemo(
|
||||
() => ({
|
||||
isCoreDataPending: isProxyViewPending || isClashConfigPending,
|
||||
@@ -277,13 +241,11 @@ export const AppDataProvider = ({
|
||||
<RulesContext value={rulesValue}>
|
||||
<ClashConfigContext value={clashConfigValue}>
|
||||
<SystemContext value={systemValue}>
|
||||
<UptimeContext value={uptimeValue}>
|
||||
<CoreDataStatusContext value={coreDataStatusValue}>
|
||||
<RefreshersContext value={refreshersValue}>
|
||||
{children}
|
||||
</RefreshersContext>
|
||||
</CoreDataStatusContext>
|
||||
</UptimeContext>
|
||||
</SystemContext>
|
||||
</ClashConfigContext>
|
||||
</RulesContext>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { unstable_serialize } from 'swr'
|
||||
import useSWR, {
|
||||
type SWRConfiguration,
|
||||
type SWRResponse,
|
||||
@@ -33,19 +32,6 @@ type QueryResult<T> = SWRResponse<T> & {
|
||||
refetch: () => Promise<{ data: T | undefined }>
|
||||
}
|
||||
|
||||
const serializeQueryKey = (queryKey: QueryKey) => unstable_serialize(queryKey)
|
||||
|
||||
const queryCache = new Map<string, unknown>()
|
||||
|
||||
const setCachedData = <T>(queryKey: QueryKey, data: T | undefined) => {
|
||||
const cacheKey = serializeQueryKey(queryKey)
|
||||
if (data === undefined) {
|
||||
queryCache.delete(cacheKey)
|
||||
} else {
|
||||
queryCache.set(cacheKey, data)
|
||||
}
|
||||
}
|
||||
|
||||
export const swrConfig: SWRConfiguration = {
|
||||
dedupingInterval: 2000,
|
||||
errorRetryCount: 3,
|
||||
@@ -53,72 +39,29 @@ export const swrConfig: SWRConfiguration = {
|
||||
revalidateOnFocus: false,
|
||||
}
|
||||
|
||||
export const getCacheData = <T>(queryKey: QueryKey): T | undefined => {
|
||||
return queryCache.get(serializeQueryKey(queryKey)) as T | undefined
|
||||
}
|
||||
|
||||
const updateCachedData = <T>(
|
||||
queryKey: QueryKey,
|
||||
updaterOrData: QueryDataUpdater<T>,
|
||||
) => {
|
||||
const current = getCacheData<T>(queryKey)
|
||||
const next =
|
||||
typeof updaterOrData === 'function'
|
||||
? (updaterOrData as (current: T | undefined) => T | undefined)(current)
|
||||
: updaterOrData
|
||||
setCachedData(queryKey, next)
|
||||
return next
|
||||
}
|
||||
|
||||
export const setCacheData = <T>(
|
||||
queryKey: QueryKey,
|
||||
updaterOrData: QueryDataUpdater<T>,
|
||||
) => {
|
||||
const next = updateCachedData(queryKey, updaterOrData)
|
||||
void swrMutate(queryKey, next, {
|
||||
) =>
|
||||
swrMutate<T>(queryKey, updaterOrData, {
|
||||
populateCache: true,
|
||||
revalidate: false,
|
||||
})
|
||||
return next
|
||||
}
|
||||
|
||||
export const setCacheDataAsync = async <T>(
|
||||
queryKey: QueryKey,
|
||||
updaterOrData: QueryDataUpdater<T>,
|
||||
) => {
|
||||
const next = updateCachedData(queryKey, updaterOrData)
|
||||
await swrMutate(queryKey, next, {
|
||||
populateCache: true,
|
||||
revalidate: false,
|
||||
})
|
||||
return next
|
||||
}
|
||||
|
||||
export const revalidateQuery = async (queryKey: QueryKey) => {
|
||||
const data = await swrMutate(queryKey)
|
||||
if (data !== undefined) {
|
||||
setCachedData(queryKey, data)
|
||||
}
|
||||
return data
|
||||
}
|
||||
export const revalidateQuery = (queryKey: QueryKey) => swrMutate(queryKey)
|
||||
|
||||
export const revalidateQueries = (queryKeys: readonly QueryKey[]) =>
|
||||
Promise.all(queryKeys.map(revalidateQuery))
|
||||
|
||||
export const removeCacheData = (queryKey: QueryKey) => {
|
||||
setCachedData(queryKey, undefined)
|
||||
return swrMutate(queryKey, undefined, {
|
||||
populateCache: true,
|
||||
revalidate: false,
|
||||
})
|
||||
}
|
||||
export const removeCacheData = (queryKey: QueryKey) =>
|
||||
setCacheData(queryKey, undefined)
|
||||
|
||||
export const fetchCacheData = async <T>(
|
||||
queryKey: QueryKey,
|
||||
queryFn: () => Promise<T> | T,
|
||||
) => {
|
||||
const data = await queryFn()
|
||||
setCacheData(queryKey, data)
|
||||
await setCacheData(queryKey, data)
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -144,11 +87,6 @@ export function useQuery<T>(options: QueryOptions<T>): QueryResult<T> {
|
||||
typeof fallbackDataSource === 'function'
|
||||
? (fallbackDataSource as () => T | undefined)()
|
||||
: fallbackDataSource
|
||||
const serializedKey = serializeQueryKey(queryKey)
|
||||
if (enabled && fallbackData !== undefined && !queryCache.has(serializedKey)) {
|
||||
setCachedData(queryKey, fallbackData)
|
||||
}
|
||||
|
||||
const swr = useSWR<T>(enabled ? queryKey : null, queryFn, {
|
||||
dedupingInterval: staleTime,
|
||||
errorRetryCount: retry === false ? 0 : retry,
|
||||
@@ -176,21 +114,12 @@ export function useQuery<T>(options: QueryOptions<T>): QueryResult<T> {
|
||||
revalidateOnReconnect: refetchOnReconnect,
|
||||
refreshInterval: refetchInterval || 0,
|
||||
refreshWhenHidden: refetchIntervalInBackground ?? false,
|
||||
onSuccess: (data) => {
|
||||
setCachedData(queryKey, data)
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
...swr,
|
||||
isFetching: swr.isValidating,
|
||||
isPending: swr.isLoading,
|
||||
refetch: async () => {
|
||||
const data = await swr.mutate()
|
||||
if (data !== undefined) {
|
||||
setCachedData(queryKey, data)
|
||||
}
|
||||
return { data }
|
||||
},
|
||||
refetch: async () => ({ data: await swr.mutate() }),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user