diff --git a/src/hooks/use-connection-data.ts b/src/hooks/use-connection-data.ts index b7e7e818b..b3e7d6909 100644 --- a/src/hooks/use-connection-data.ts +++ b/src/hooks/use-connection-data.ts @@ -81,6 +81,7 @@ export const useConnectionData = () => { buildSubscriptKey: (date) => `getClashConnection-${date}`, fallbackData: initConnData, connect: () => MihomoWebSocket.connect_connections(), + throttleMs: 16, setupHandlers: ({ next, scheduleReconnect }) => ({ handleMessage: (data) => { if (data.startsWith('Websocket error')) { @@ -89,14 +90,9 @@ export const useConnectionData = () => { return } - try { - const parsed = JSON.parse(data) as IConnections - next(null, (old = initConnData) => - mergeConnectionSnapshot(parsed, old), - ) - } catch (error) { - next(error) - } + next(null, (old = initConnData) => + mergeConnectionSnapshot(JSON.parse(data) as IConnections, old), + ) }, }), }) diff --git a/src/hooks/use-memory-data.ts b/src/hooks/use-memory-data.ts index 1ca527c99..d2734fda8 100644 --- a/src/hooks/use-memory-data.ts +++ b/src/hooks/use-memory-data.ts @@ -15,6 +15,7 @@ export const useMemoryData = () => { buildSubscriptKey: (date) => `getClashMemory-${date}`, fallbackData: FALLBACK_MEMORY_USAGE, connect: () => MihomoWebSocket.connect_memory(), + throttleMs: 500, setupHandlers: ({ next, scheduleReconnect }) => ({ handleMessage: (data) => { if (data.startsWith('Websocket error')) { diff --git a/src/hooks/use-mihomo-ws-subscription.ts b/src/hooks/use-mihomo-ws-subscription.ts index 46f617175..74a0d214c 100644 --- a/src/hooks/use-mihomo-ws-subscription.ts +++ b/src/hooks/use-mihomo-ws-subscription.ts @@ -4,7 +4,7 @@ import { mutate, type MutatorCallback } from 'swr' import useSWRSubscription from 'swr/subscription' import { type Message, type MihomoWebSocket } from 'tauri-plugin-mihomo-api' -export const RECONNECT_DELAY_MS = 100 +export const RECONNECT_DELAY_MS = 1000 type NextFn = (error?: any, data?: T | MutatorCallback) => void @@ -26,6 +26,15 @@ interface UseMihomoWsSubscriptionOptions { fallbackData: T connect: () => Promise keepPreviousData?: boolean + /** + * When > 0, coalesce rapid WebSocket messages by wrapping the `next` + * function passed to `setupHandlers`. Only the most recent value is + * flushed, at most once per `throttleMs` milliseconds. + * + * Uses `setTimeout` (not `requestAnimationFrame`) so it keeps working + * when the window is backgrounded or minimized. + */ + throttleMs?: number setupHandlers: (ctx: HandlerContext) => HandlerResult } @@ -38,6 +47,7 @@ export const useMihomoWsSubscription = ( fallbackData, connect, keepPreviousData = true, + throttleMs, setupHandlers, } = options @@ -77,18 +87,59 @@ export const useMihomoWsSubscription = ( timeoutRef.current = setTimeout(connectWs, RECONNECT_DELAY_MS) } + let throttleCleanup: (() => void) | undefined + let wrappedNext: NextFn = next + + if (throttleMs && throttleMs > 0) { + let pendingData: T | MutatorCallback | undefined + let hasPending = false + let timerId: ReturnType | null = null + + const flush = () => { + timerId = null + if (hasPending) { + const data = pendingData + pendingData = undefined + hasPending = false + next(undefined, data) + } + } + + wrappedNext = (error?: any, data?: T | MutatorCallback) => { + if (error !== undefined && error !== null) { + next(error, data) + return + } + if (!timerId) { + next(undefined, data) + timerId = setTimeout(flush, throttleMs) + } else { + pendingData = data + hasPending = true + } + } + + throttleCleanup = () => { + if (timerId) { + clearTimeout(timerId) + timerId = null + } + } + } + const { handleMessage: handleTextMessage, onConnected, cleanup, } = setupHandlers({ - next, + next: wrappedNext, scheduleReconnect, isMounted: () => isMounted, }) const cleanupAll = () => { clearReconnectTimer() + throttleCleanup?.() cleanup?.() void closeSocket() } diff --git a/src/hooks/use-traffic-data.ts b/src/hooks/use-traffic-data.ts index b220c3eae..70926bf39 100644 --- a/src/hooks/use-traffic-data.ts +++ b/src/hooks/use-traffic-data.ts @@ -16,6 +16,7 @@ export const useTrafficData = (options?: { enabled?: boolean }) => { buildSubscriptKey: (date) => `getClashTraffic-${date}`, fallbackData: FALLBACK_TRAFFIC, connect: () => MihomoWebSocket.connect_traffic(), + throttleMs: 200, setupHandlers: ({ next, scheduleReconnect }) => ({ handleMessage: (data) => { if (data.startsWith('Websocket error')) { diff --git a/src/services/config.ts b/src/services/config.ts index 5b8d860b6..693f154e7 100644 --- a/src/services/config.ts +++ b/src/services/config.ts @@ -23,8 +23,8 @@ export const SWR_SLOW_POLL = { export const SWR_MIHOMO = { ...SWR_NOT_SMART, - errorRetryInterval: 500, - errorRetryCount: 15, + errorRetryInterval: 2000, + errorRetryCount: 3, } export const SWR_EXTERNAL_API = {