mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-14 06:42:08 +08:00
fix: stop idle mihomo websocket subscriptions
This commit is contained in:
@@ -35,7 +35,6 @@ export const initConnSummaryData: ConnectionSummaryData = {
|
||||
let connectionData: ConnectionMonitorData = initConnData
|
||||
let connectionSummary: ConnectionSummaryData = initConnSummaryData
|
||||
let connectionSocket: MihomoWebSocket | null = null
|
||||
let connectionStarted = false
|
||||
let connectionConnecting = false
|
||||
let reconnectTimer: ReturnType<typeof setTimeout> | null = null
|
||||
let flushTimer: ReturnType<typeof setTimeout> | null = null
|
||||
@@ -53,6 +52,9 @@ const notifySummaryListeners = () => {
|
||||
summaryListeners.forEach((listener) => listener())
|
||||
}
|
||||
|
||||
const hasConnectionSubscribers = () =>
|
||||
connectionListeners.size > 0 || summaryListeners.size > 0
|
||||
|
||||
const sameMetadata = (left: ConnectionMetadata, right: ConnectionMetadata) =>
|
||||
metadataValue(left.network) === metadataValue(right.network) &&
|
||||
metadataValue(left.type) === metadataValue(right.type) &&
|
||||
@@ -215,7 +217,7 @@ const flushPendingMessage = () => {
|
||||
flushTimer = null
|
||||
const messageData = pendingMessageData
|
||||
pendingMessageData = null
|
||||
if (!messageData) return
|
||||
if (!messageData || !hasConnectionSubscribers()) return
|
||||
|
||||
let payload: IConnections
|
||||
try {
|
||||
@@ -270,6 +272,7 @@ const closeConnectionSocket = async () => {
|
||||
}
|
||||
|
||||
const scheduleReconnect = () => {
|
||||
if (!hasConnectionSubscribers()) return
|
||||
if (reconnectTimer) return
|
||||
reconnectTimer = window.setTimeout(() => {
|
||||
reconnectTimer = null
|
||||
@@ -278,20 +281,27 @@ const scheduleReconnect = () => {
|
||||
}
|
||||
|
||||
async function reconnectConnectionSocket() {
|
||||
if (!hasConnectionSubscribers()) return
|
||||
await closeConnectionSocket()
|
||||
scheduleReconnect()
|
||||
}
|
||||
|
||||
async function connectConnectionSocket() {
|
||||
if (connectionSocket || connectionConnecting) return
|
||||
if (!hasConnectionSubscribers()) return
|
||||
|
||||
clearReconnectTimer()
|
||||
connectionConnecting = true
|
||||
|
||||
try {
|
||||
const socket = await MihomoWebSocket.connect_connections()
|
||||
if (!hasConnectionSubscribers()) {
|
||||
await socket.close()
|
||||
return
|
||||
}
|
||||
connectionSocket = socket
|
||||
socket.addListener((message) => {
|
||||
if (connectionSocket !== socket) return
|
||||
if (message.type !== 'Text') return
|
||||
if (message.data.startsWith('Websocket error')) {
|
||||
void reconnectConnectionSocket()
|
||||
@@ -308,27 +318,39 @@ async function connectConnectionSocket() {
|
||||
}
|
||||
|
||||
const startConnectionMonitor = () => {
|
||||
if (connectionStarted) return
|
||||
connectionStarted = true
|
||||
void connectConnectionSocket()
|
||||
}
|
||||
|
||||
const stopConnectionMonitorIfIdle = () => {
|
||||
if (hasConnectionSubscribers()) return
|
||||
|
||||
clearReconnectTimer()
|
||||
pendingMessageData = null
|
||||
if (flushTimer) {
|
||||
window.clearTimeout(flushTimer)
|
||||
flushTimer = null
|
||||
}
|
||||
void closeConnectionSocket()
|
||||
}
|
||||
|
||||
const getConnectionSnapshot = () => connectionData
|
||||
const getConnectionSummarySnapshot = () => connectionSummary
|
||||
|
||||
const subscribeConnectionData = (listener: ConnectionListener) => {
|
||||
startConnectionMonitor()
|
||||
connectionListeners.add(listener)
|
||||
startConnectionMonitor()
|
||||
return () => {
|
||||
connectionListeners.delete(listener)
|
||||
stopConnectionMonitorIfIdle()
|
||||
}
|
||||
}
|
||||
|
||||
const subscribeConnectionSummary = (listener: ConnectionListener) => {
|
||||
startConnectionMonitor()
|
||||
summaryListeners.add(listener)
|
||||
startConnectionMonitor()
|
||||
return () => {
|
||||
summaryListeners.delete(listener)
|
||||
stopConnectionMonitorIfIdle()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,9 +373,15 @@ const clearClosedConnectionData = () => {
|
||||
notifyConnectionListeners()
|
||||
}
|
||||
|
||||
export const useConnectionData = () => {
|
||||
export const useConnectionData = (options?: { enabled?: boolean }) => {
|
||||
const enabled = options?.enabled ?? true
|
||||
const subscribe = useCallback(
|
||||
(listener: ConnectionListener) =>
|
||||
enabled ? subscribeConnectionData(listener) : () => {},
|
||||
[enabled],
|
||||
)
|
||||
const data = useSyncExternalStore(
|
||||
subscribeConnectionData,
|
||||
subscribe,
|
||||
getConnectionSnapshot,
|
||||
getConnectionSnapshot,
|
||||
)
|
||||
@@ -372,9 +400,15 @@ export const useConnectionData = () => {
|
||||
}
|
||||
}
|
||||
|
||||
export const useConnectionSummaryData = () => {
|
||||
export const useConnectionSummaryData = (options?: { enabled?: boolean }) => {
|
||||
const enabled = options?.enabled ?? true
|
||||
const subscribe = useCallback(
|
||||
(listener: ConnectionListener) =>
|
||||
enabled ? subscribeConnectionSummary(listener) : () => {},
|
||||
[enabled],
|
||||
)
|
||||
const data = useSyncExternalStore(
|
||||
subscribeConnectionSummary,
|
||||
subscribe,
|
||||
getConnectionSummarySnapshot,
|
||||
getConnectionSummarySnapshot,
|
||||
)
|
||||
|
||||
@@ -29,10 +29,12 @@ const shouldSkipDuplicateMemory = (memory: IMemoryUsageItem) => {
|
||||
return false
|
||||
}
|
||||
|
||||
export const useMemoryData = () => {
|
||||
export const useMemoryData = (options?: { enabled?: boolean }) => {
|
||||
const enabled = options?.enabled ?? true
|
||||
|
||||
const { response, refresh } = useMihomoWsSubscription<IMemoryUsageItem>({
|
||||
storageKey: 'mihomo_memory_date',
|
||||
buildSubscriptKey: (date) => `getClashMemory-${date}`,
|
||||
buildSubscriptKey: (date) => (enabled ? `getClashMemory-${date}` : null),
|
||||
fallbackData: FALLBACK_MEMORY_USAGE,
|
||||
connect: () => MihomoWebSocket.connect_memory(),
|
||||
throttleMs: 500,
|
||||
|
||||
@@ -33,7 +33,7 @@ export const useTrafficData = (options?: { enabled?: boolean }) => {
|
||||
} = useTrafficMonitorEnhanced({ subscribe: false, enabled })
|
||||
const { response, refresh } = useMihomoWsSubscription<ITrafficItem>({
|
||||
storageKey: 'mihomo_traffic_date',
|
||||
buildSubscriptKey: (date) => `getClashTraffic-${date}`,
|
||||
buildSubscriptKey: (date) => (enabled ? `getClashTraffic-${date}` : null),
|
||||
fallbackData: FALLBACK_TRAFFIC,
|
||||
connect: () => MihomoWebSocket.connect_traffic(),
|
||||
throttleMs: 200,
|
||||
|
||||
+52
-12
@@ -1,28 +1,68 @@
|
||||
import { TauriEvent } from '@tauri-apps/api/event'
|
||||
import { getCurrentWindow } from '@tauri-apps/api/window'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
const isDocumentVisible = () =>
|
||||
typeof document === 'undefined' || document.visibilityState === 'visible'
|
||||
|
||||
export const useVisibility = () => {
|
||||
const [visible, setVisible] = useState(() =>
|
||||
typeof document === 'undefined'
|
||||
? true
|
||||
: document.visibilityState === 'visible',
|
||||
)
|
||||
const [visible, setVisible] = useState(isDocumentVisible)
|
||||
|
||||
useEffect(() => {
|
||||
const handleVisibilityChange = () => {
|
||||
setVisible(document.visibilityState === 'visible')
|
||||
let mounted = true
|
||||
let visibilityTimer: ReturnType<typeof setTimeout> | null = null
|
||||
const appWindow = getCurrentWindow()
|
||||
|
||||
const updateVisible = async () => {
|
||||
const windowVisible = await appWindow.isVisible().catch(() => true)
|
||||
if (mounted) {
|
||||
setVisible(isDocumentVisible() && windowVisible)
|
||||
}
|
||||
}
|
||||
|
||||
const updateVisibleSoon = () => {
|
||||
if (visibilityTimer) {
|
||||
window.clearTimeout(visibilityTimer)
|
||||
}
|
||||
visibilityTimer = window.setTimeout(() => {
|
||||
visibilityTimer = null
|
||||
void updateVisible()
|
||||
}, 50)
|
||||
}
|
||||
|
||||
const handleVisibleEvent = () => {
|
||||
void updateVisible()
|
||||
}
|
||||
|
||||
const handleFocus = () => setVisible(true)
|
||||
const handlePointerDown = () => setVisible(true)
|
||||
|
||||
document.addEventListener('focus', handleFocus)
|
||||
document.addEventListener('focus', handleVisibleEvent)
|
||||
document.addEventListener('pointerdown', handlePointerDown)
|
||||
document.addEventListener('visibilitychange', handleVisibilityChange)
|
||||
document.addEventListener('visibilitychange', handleVisibleEvent)
|
||||
window.addEventListener('focus', handleVisibleEvent)
|
||||
|
||||
const unlistenFocusChanged = appWindow.onFocusChanged(updateVisibleSoon)
|
||||
const unlistenCloseRequested = appWindow.listen(
|
||||
TauriEvent.WINDOW_CLOSE_REQUESTED,
|
||||
() => {
|
||||
setVisible(false)
|
||||
updateVisibleSoon()
|
||||
},
|
||||
)
|
||||
void updateVisible()
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('focus', handleFocus)
|
||||
mounted = false
|
||||
if (visibilityTimer) {
|
||||
window.clearTimeout(visibilityTimer)
|
||||
visibilityTimer = null
|
||||
}
|
||||
document.removeEventListener('focus', handleVisibleEvent)
|
||||
document.removeEventListener('pointerdown', handlePointerDown)
|
||||
document.removeEventListener('visibilitychange', handleVisibilityChange)
|
||||
document.removeEventListener('visibilitychange', handleVisibleEvent)
|
||||
window.removeEventListener('focus', handleVisibleEvent)
|
||||
void unlistenFocusChanged.then((unlisten) => unlisten())
|
||||
void unlistenCloseRequested.then((unlisten) => unlisten())
|
||||
}
|
||||
}, [])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user