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 EnrichLog from './EnrichLog' 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 clearLog = () => { streamState.logs = [] streamState.statusChangeTimestamp = Date.now() } 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 = () => ( <>