diff --git a/frontend/src/api/queries/useAllSettings.ts b/frontend/src/api/queries/useAllSettings.ts new file mode 100644 index 00000000..cd2566d9 --- /dev/null +++ b/frontend/src/api/queries/useAllSettings.ts @@ -0,0 +1,67 @@ +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; + +import { HttpUtil } from '@/utils'; +import { AllSetting } from '@/models/setting'; +import { keys } from '@/api/queryKeys'; + +interface ApiMsg { + success?: boolean; + obj?: T; + msg?: string; +} + +async function fetchAllSetting(): Promise { + const msg = await HttpUtil.post('/panel/setting/all', undefined, { silent: true }) as ApiMsg; + if (!msg?.success) throw new Error(msg?.msg || 'Failed to fetch settings'); + return msg.obj; +} + +export function useAllSettings() { + const queryClient = useQueryClient(); + const [draft, setDraft] = useState(() => new AllSetting()); + const [extraSpinning, setExtraSpinning] = useState(false); + + const query = useQuery({ + queryKey: keys.settings.all(), + queryFn: fetchAllSetting, + staleTime: Infinity, + }); + + const server = useMemo(() => new AllSetting(query.data), [query.data]); + + useEffect(() => { + if (query.data !== undefined) { + setDraft(new AllSetting(query.data)); + } + }, [query.data]); + + const updateSetting = useCallback((patch: Partial) => { + setDraft((prev) => { + const next = new AllSetting(prev); + Object.assign(next, patch); + return next; + }); + }, []); + + const saveMut = useMutation({ + mutationFn: async (next: AllSetting) => + HttpUtil.post('/panel/setting/update', next) as Promise, + onSuccess: (msg) => { + if (msg?.success) queryClient.invalidateQueries({ queryKey: keys.settings.all() }); + }, + }); + + const saveAll = useCallback(() => saveMut.mutateAsync(draft), [saveMut, draft]); + const saveDisabled = useMemo(() => server.equals(draft), [server, draft]); + + return { + allSetting: draft, + updateSetting, + fetched: query.data !== undefined, + spinning: extraSpinning || saveMut.isPending, + setSpinning: setExtraSpinning, + saveDisabled, + saveAll, + }; +} diff --git a/frontend/src/api/queryKeys.ts b/frontend/src/api/queryKeys.ts index 3d4a852d..8f431ef6 100644 --- a/frontend/src/api/queryKeys.ts +++ b/frontend/src/api/queryKeys.ts @@ -6,4 +6,8 @@ export const keys = { root: () => ['nodes'] as const, list: () => ['nodes', 'list'] as const, }, + settings: { + root: () => ['settings'] as const, + all: () => ['settings', 'all'] as const, + }, } as const; diff --git a/frontend/src/hooks/useAllSetting.ts b/frontend/src/hooks/useAllSetting.ts deleted file mode 100644 index 5775f554..00000000 --- a/frontend/src/hooks/useAllSetting.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { HttpUtil } from '@/utils'; -import { AllSetting } from '@/models/setting'; - -interface ApiMsg { - success?: boolean; - obj?: T; -} - -export function useAllSetting() { - const [allSetting, setAllSetting] = useState(() => new AllSetting()); - const [oldAllSetting, setOldAllSetting] = useState(() => new AllSetting()); - const [fetched, setFetched] = useState(false); - const [spinning, setSpinning] = useState(false); - const fetchedRef = useRef(false); - - const applyServerState = useCallback((obj: unknown) => { - setAllSetting(new AllSetting(obj)); - setOldAllSetting(new AllSetting(obj)); - }, []); - - const fetchAll = useCallback(async () => { - const msg = await HttpUtil.post('/panel/setting/all') as ApiMsg; - if (msg?.success) { - applyServerState(msg.obj); - fetchedRef.current = true; - setFetched(true); - } - }, [applyServerState]); - - const saveAll = useCallback(async () => { - setSpinning(true); - try { - const msg = await HttpUtil.post('/panel/setting/update', allSetting) as ApiMsg; - if (msg?.success) await fetchAll(); - } finally { - setSpinning(false); - } - }, [allSetting, fetchAll]); - - const updateSetting = useCallback((patch: Partial) => { - setAllSetting((prev) => { - const next = new AllSetting(prev); - Object.assign(next, patch); - return next; - }); - }, []); - - const saveDisabled = useMemo( - () => allSetting.equals(oldAllSetting), - [allSetting, oldAllSetting], - ); - - useEffect(() => { - - fetchAll(); - }, [fetchAll]); - - return { - allSetting, - updateSetting, - fetched, - spinning, - setSpinning, - saveDisabled, - fetchAll, - saveAll, - }; -} diff --git a/frontend/src/pages/settings/SettingsPage.tsx b/frontend/src/pages/settings/SettingsPage.tsx index 695b1c73..8442f27b 100644 --- a/frontend/src/pages/settings/SettingsPage.tsx +++ b/frontend/src/pages/settings/SettingsPage.tsx @@ -28,7 +28,7 @@ import { HttpUtil, PromiseUtil } from '@/utils'; import { setMessageInstance } from '@/utils/messageBus'; import { useTheme } from '@/hooks/useTheme'; import { useMediaQuery } from '@/hooks/useMediaQuery'; -import { useAllSetting } from '@/hooks/useAllSetting'; +import { useAllSettings } from '@/api/queries/useAllSettings'; import AppSidebar from '@/components/AppSidebar'; import GeneralTab from './GeneralTab'; import SecurityTab from './SecurityTab'; @@ -92,7 +92,7 @@ export default function SettingsPage() { setSpinning, saveDisabled, saveAll, - } = useAllSetting(); + } = useAllSettings(); const [entryHost, setEntryHost] = useState(''); const [entryPort, setEntryPort] = useState('');