import { useLocalStorage } from "foxact/use-local-storage"; import { useEffect, useRef } from "react"; import { mutate } from "swr"; import useSWRSubscription from "swr/subscription"; import { MihomoWebSocket } from "tauri-plugin-mihomo-api"; export const initConnData: IConnections = { uploadTotal: 0, downloadTotal: 0, connections: [], }; export const useConnectionData = () => { const [date, setDate] = useLocalStorage("mihomo_connection_date", Date.now()); const subscriptKey = `getClashConnection-${date}`; const ws = useRef(null); const wsFirstConnection = useRef(true); const timeoutRef = useRef>(null); const response = useSWRSubscription( subscriptKey, (_key, { next }) => { const reconnect = async () => { await ws.current?.close(); ws.current = null; timeoutRef.current = setTimeout(async () => await connect(), 500); }; const connect = () => MihomoWebSocket.connect_connections() .then((ws_) => { ws.current = ws_; if (timeoutRef.current) clearTimeout(timeoutRef.current); ws_.addListener(async (msg) => { if (msg.type === "Text") { if (msg.data.startsWith("Websocket error")) { next(msg.data); await reconnect(); } else { const data = JSON.parse(msg.data) as IConnections; next(null, (old = initConnData) => { const oldConn = old.connections; const maxLen = data.connections?.length; const connections: IConnectionsItem[] = []; const rest = (data.connections || []).filter((each) => { const index = oldConn.findIndex((o) => o.id === each.id); if (index >= 0 && index < maxLen) { const old = oldConn[index]; each.curUpload = each.upload - old.upload; each.curDownload = each.download - old.download; connections[index] = each; return false; } return true; }); for (let i = 0; i < maxLen; ++i) { if (!connections[i] && rest.length > 0) { connections[i] = rest.shift()!; connections[i].curUpload = 0; connections[i].curDownload = 0; } } return { ...data, connections }; }); } } }); }) .catch((_) => { if (!ws.current) { timeoutRef.current = setTimeout(async () => await connect(), 500); } }); if ( wsFirstConnection.current || (ws.current && !wsFirstConnection.current) ) { wsFirstConnection.current = false; if (ws.current) { ws.current.close(); ws.current = null; } connect(); } return () => { ws.current?.close(); }; }, { fallbackData: initConnData, keepPreviousData: true, }, ); useEffect(() => { mutate(`$sub$${subscriptKey}`); }, [date, subscriptKey]); const refreshGetClashConnection = () => { setDate(Date.now()); }; return { response, refreshGetClashConnection }; };