import { useEffect } from "react"; import ReconnectingWebSocket, { CloseEvent } from "reconnecting-websocket"; import { proxy, ref, useSnapshot } from "valtio"; import { subscribeKey } from "valtio/utils"; import { Select } from "."; import state, { ILog, transactionsState } from "../state"; import { extractJSON } from "../utils/json"; import LogBox from "./LogBox"; interface ISelect { label: string; value: T; } export interface IStreamState { selectedAccount: ISelect | null; status: "idle" | "opened" | "closed"; statusChangeTimestamp?: number; logs: ILog[]; socket?: ReconnectingWebSocket; } export const streamState = proxy({ selectedAccount: null as ISelect | null, status: "idle", logs: [] as ILog[], }); const onOpen = (account: ISelect | null) => { if (!account) { return; } // streamState.logs = []; streamState.status = "opened"; streamState.statusChangeTimestamp = Date.now(); pushLog(`Debug stream opened for account ${account?.value}`, { type: "success", }); }; const onError = () => { pushLog("Something went wrong! Check your connection and try again.", { type: "error", }); }; const onClose = (e: CloseEvent) => { // 999 = closed websocket connection by switching account if (e.code !== 4999) { pushLog(`Connection was closed. [code: ${e.code}]`, { type: "error", }); } streamState.status = "closed"; streamState.statusChangeTimestamp = Date.now(); }; const onMessage = (event: any) => { // Ping returns just account address, if we get that // response we don't need to log anything if (event.data !== streamState.selectedAccount?.value) { pushLog(event.data); } }; let interval: NodeJS.Timer | null = null; const addListeners = (account: ISelect | null) => { if (account?.value && streamState.socket?.url.endsWith(account?.value)) { return; } streamState.logs = []; if (account?.value) { if (interval) { clearInterval(interval); } if (streamState.socket) { streamState.socket?.removeEventListener("open", () => onOpen(account)); streamState.socket?.removeEventListener("close", onClose); streamState.socket?.removeEventListener("error", onError); streamState.socket?.removeEventListener("message", onMessage); } streamState.socket = ref( new ReconnectingWebSocket( `wss://${process.env.NEXT_PUBLIC_DEBUG_STREAM_URL}/${account?.value}` ) ); if (streamState.socket) { interval = setInterval(() => { streamState.socket?.send(""); }, 45000); } streamState.socket.addEventListener("open", () => onOpen(account)); streamState.socket.addEventListener("close", onClose); streamState.socket.addEventListener("error", onError); streamState.socket.addEventListener("message", onMessage); } }; subscribeKey(streamState, "selectedAccount", addListeners); const DebugStream = () => { const { selectedAccount, logs } = useSnapshot(streamState); const { activeHeader: activeTxTab } = useSnapshot(transactionsState); const { accounts } = useSnapshot(state); const accountOptions = accounts.map((acc) => ({ label: acc.name, value: acc.address, })); const renderNav = () => ( <>