mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-10-28 00:42:08 +08:00
* refactor(ipc): generate bindings with Specta * fix(profile): project IPC inputs to consumed fields * fix(events): remove unused refresh payloads * fix(webdav): return only consumed backup metadata * fix(network): trim unused interface metadata * fix(sysinfo): return only displayed system fields * chore(knip): check generated runtime exports * fix(proxy): confirm runtime chain updates before UI changes * fix(clipboard): propagate proxy environment copy failures * refactor(dns): remove ignored runtime patch * refactor(core): trim unused upgrade response fields * refactor(ipc): avoid redundant response copies * refactor(proxy): clarify automatic proxy query naming * chore(tests): remove added IPC regression tests * refactor(ipc): enforce typed command boundaries * chore(contract): check generated bindings before push * refactor(ipc): remove unused proxy liveness payloads
106 lines
2.9 KiB
TypeScript
106 lines
2.9 KiB
TypeScript
import { useCallback, useEffect, useRef } from 'react'
|
|
|
|
import type { ProfilePatch, ProfileSelection } from '@/services/bindings'
|
|
import { getProfiles, patchProfile, patchProfilesConfig } from '@/services/cmds'
|
|
import { mutate } from '@/services/mutate'
|
|
import { migrateProfileTestUrls } from '@/services/proxy-test-url'
|
|
import { fetchCacheData, setCacheData, useQuery } from '@/services/query-client'
|
|
import { debugLog } from '@/utils/debug'
|
|
|
|
const profilesQueryKey = ['getProfiles'] as const
|
|
|
|
export const fetchProfilesIntoCache = () =>
|
|
fetchCacheData(profilesQueryKey, getProfiles)
|
|
|
|
export const useProfiles = () => {
|
|
const {
|
|
data: profiles,
|
|
refetch,
|
|
error,
|
|
isFetching: isValidating,
|
|
} = useQuery({
|
|
queryKey: profilesQueryKey,
|
|
queryFn: async () => {
|
|
const data = await getProfiles()
|
|
debugLog(
|
|
'[useProfiles] 配置数据更新成功,配置数量:',
|
|
data?.items?.length || 0,
|
|
)
|
|
return data
|
|
},
|
|
refetchOnWindowFocus: false,
|
|
refetchOnReconnect: false,
|
|
staleTime: 500,
|
|
retry: 3,
|
|
retryDelay: 1000,
|
|
refetchInterval: false,
|
|
})
|
|
|
|
useEffect(() => {
|
|
if (profiles) migrateProfileTestUrls(profiles)
|
|
}, [profiles])
|
|
|
|
const refetchRef = useRef(refetch)
|
|
refetchRef.current = refetch
|
|
const mutateProfiles = useCallback(async () => {
|
|
await refetchRef.current()
|
|
}, [])
|
|
|
|
const patchProfiles = useCallback(
|
|
async (value: ProfileSelection) => {
|
|
try {
|
|
const result = await mutate(() => patchProfilesConfig(value), {
|
|
id: 'patch-profiles-config',
|
|
onFulfilled: (outcome) => {
|
|
if (outcome.status === 'valid') {
|
|
void setCacheData<IProfilesConfig>(profilesQueryKey, (current) =>
|
|
current ? { ...current, ...value } : current,
|
|
)
|
|
}
|
|
},
|
|
})
|
|
|
|
if (!result.ok) {
|
|
// Backend Busy keeps local state untouched, as before.
|
|
return result.value
|
|
}
|
|
const outcome = result.value
|
|
if (outcome.status !== 'valid' && outcome.status !== 'busy') {
|
|
await mutateProfiles()
|
|
}
|
|
return outcome
|
|
} catch (error) {
|
|
await mutateProfiles()
|
|
throw error
|
|
}
|
|
},
|
|
[mutateProfiles],
|
|
)
|
|
|
|
const patchCurrent = useCallback(
|
|
async (value: ProfilePatch) => {
|
|
if (profiles?.current) {
|
|
const uid = profiles.current
|
|
await mutate(() => patchProfile(uid, value), {
|
|
id: `patch-profile:${uid}`,
|
|
errorNotice: false,
|
|
})
|
|
void mutateProfiles()
|
|
}
|
|
},
|
|
[mutateProfiles, profiles],
|
|
)
|
|
|
|
return {
|
|
profiles,
|
|
current: profiles?.items?.find((p) => p && p.uid === profiles.current),
|
|
patchProfiles,
|
|
patchCurrent,
|
|
mutateProfiles,
|
|
// 新增故障检测状态
|
|
isLoading: isValidating,
|
|
error,
|
|
isStale: !profiles && !error && !isValidating, // 检测是否处于异常状态
|
|
}
|
|
}
|