mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-25 13:45:03 +08:00
refactor: replace JSON deep copy with deepClone helper and extract useTauriEvent hook (#3140)
* refactor: replace JSON.parse(JSON.stringify()) with structuredClone and extract useTauriEvent hook Replace all `JSON.parse(JSON.stringify())` deep copy patterns with native `structuredClone()` across production source (9 occurrences), tests (11 occurrences), and a hand-rolled `deepClone` utility in providerConfigUtils.ts. Add "ES2022" to tsconfig lib for type support. Extract a `useTauriEvent` hook to eliminate the repeated Tauri event listener boilerplate (`useEffect` + `active/disposed` flag + async `listen`) that was duplicated across App.tsx (3 listeners) and useUsageCacheBridge.ts. The hook handles async registration, race-condition guards, and cleanup automatically. * fix: add compatible deepClone helper - Add a shared deepClone helper with a structuredClone runtime guard and fallback. - Route clone call sites through the helper. - Preserve universal-provider-synced listener ordering and drop the dead-directory diff. * fix: harden Tauri event handling - Guard WebDAV sync status events against missing payloads. - Preserve settings query invalidation ordering before showing auto-sync errors. - Simplify useTauriEvent subscriptions to avoid dependency-driven re-listens. --------- Co-authored-by: zcb <zhangchongbiao@qiyuanlab.com> Co-authored-by: Jason <farion1231@gmail.com>
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
|
||||
|
||||
/**
|
||||
* 在 useEffect 中监听 Tauri 事件,自动管理异步注册和卸载清理。
|
||||
* 避免每次使用时重复编写 active flag + async setup 样板代码。
|
||||
*/
|
||||
export function useTauriEvent<P>(
|
||||
eventName: string,
|
||||
handler: (payload: P) => void | Promise<void>,
|
||||
): void {
|
||||
const handlerRef = useRef(handler);
|
||||
handlerRef.current = handler;
|
||||
|
||||
useEffect(() => {
|
||||
let disposed = false;
|
||||
let unlisten: UnlistenFn | undefined;
|
||||
|
||||
void (async () => {
|
||||
try {
|
||||
const off = await listen<P>(eventName, (event) => {
|
||||
void handlerRef.current(event.payload);
|
||||
});
|
||||
if (disposed) {
|
||||
off();
|
||||
} else {
|
||||
unlisten = off;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to subscribe ${eventName} event`, error);
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
disposed = true;
|
||||
unlisten?.();
|
||||
};
|
||||
}, [eventName]);
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
import { useEffect } from "react";
|
||||
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import type { AppId } from "@/lib/api/types";
|
||||
import type { UsageResult } from "@/types";
|
||||
import type { SubscriptionQuota } from "@/types/subscription";
|
||||
import { usageKeys } from "@/lib/query/usage";
|
||||
import { subscriptionKeys } from "@/lib/query/subscription";
|
||||
import { useTauriEvent } from "./useTauriEvent";
|
||||
|
||||
type UsageCacheUpdatedPayload =
|
||||
| {
|
||||
@@ -28,39 +27,17 @@ type UsageCacheUpdatedPayload =
|
||||
export function useUsageCacheBridge() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
useEffect(() => {
|
||||
let unlisten: UnlistenFn | undefined;
|
||||
let disposed = false;
|
||||
|
||||
(async () => {
|
||||
const off = await listen<UsageCacheUpdatedPayload>(
|
||||
"usage-cache-updated",
|
||||
(event) => {
|
||||
const payload = event.payload;
|
||||
if (payload.kind === "script") {
|
||||
queryClient.setQueryData<UsageResult>(
|
||||
usageKeys.script(payload.providerId, payload.appType),
|
||||
payload.data,
|
||||
);
|
||||
} else if (payload.kind === "subscription") {
|
||||
queryClient.setQueryData<SubscriptionQuota>(
|
||||
subscriptionKeys.quota(payload.appType),
|
||||
payload.data,
|
||||
);
|
||||
}
|
||||
},
|
||||
useTauriEvent<UsageCacheUpdatedPayload>("usage-cache-updated", (payload) => {
|
||||
if (payload.kind === "script") {
|
||||
queryClient.setQueryData<UsageResult>(
|
||||
usageKeys.script(payload.providerId, payload.appType),
|
||||
payload.data,
|
||||
);
|
||||
|
||||
if (disposed) {
|
||||
off();
|
||||
} else {
|
||||
unlisten = off;
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
disposed = true;
|
||||
unlisten?.();
|
||||
};
|
||||
}, [queryClient]);
|
||||
} else if (payload.kind === "subscription") {
|
||||
queryClient.setQueryData<SubscriptionQuota>(
|
||||
subscriptionKeys.quota(payload.appType),
|
||||
payload.data,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user