Files
clash-verge-rev/src/hooks/use-profiles.ts
T
Tunglies 6e08a77a84 refactor(backend): specta ipc bindings (#8195)
* 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
2026-10-12 06:16:05 +00:00

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, // 检测是否处于异常状态
}
}