Files
xrpl-hooks-ide/components/DebugStream.tsx
Valtteri Karesto 89217d2633 Remove console.log
2022-06-28 11:03:56 +03:00

197 lines
5.2 KiB
TypeScript

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<T = string> {
label: string;
value: T;
}
export interface IStreamState {
selectedAccount: ISelect | null;
status: "idle" | "opened" | "closed";
statusChangeTimestamp?: number;
logs: ILog[];
socket?: ReconnectingWebSocket;
}
export const streamState = proxy<IStreamState>({
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("");
}, 10000);
}
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 = () => (
<>
<Select
instanceId="DSAccount"
placeholder="Select account"
options={accountOptions}
hideSelectedOptions
value={selectedAccount}
onChange={(acc) => {
streamState.socket?.close(
4999,
"Old connection closed because user switched account"
);
streamState.selectedAccount = acc as any;
}}
css={{ width: "100%" }}
/>
</>
);
useEffect(() => {
const account = transactionsState.transactions.find(
(tx) => tx.header === activeTxTab
)?.state.selectedAccount;
if (account && account.value !== streamState.selectedAccount?.value)
streamState.selectedAccount = account;
}, [activeTxTab]);
const clearLog = () => {
streamState.logs = [];
streamState.statusChangeTimestamp = Date.now();
};
return (
<LogBox
enhanced
renderNav={renderNav}
title="Debug stream"
logs={logs}
clearLog={clearLog}
/>
);
};
export default DebugStream;
export const pushLog = (
str: any,
opts: Partial<Pick<ILog, "type">> = {}
): ILog | undefined => {
if (!str) return;
if (typeof str !== "string") throw Error("Unrecognized debug log stream!");
const match = str.match(/([\s\S]+(?:UTC|ISO|GMT[+|-]\d+))?\ ?([\s\S]*)/m);
const [_, tm, msg] = match || [];
const timestamp = Date.parse(tm || "") || undefined;
const timestring = !timestamp ? tm : new Date(timestamp).toLocaleTimeString();
const extracted = extractJSON(msg);
const message = !extracted
? msg
: msg.slice(0, extracted.start) + msg.slice(extracted.end + 1);
const jsonData = extracted
? JSON.stringify(extracted.result, null, 2)
: undefined;
if (extracted?.result?.id?._Request?.includes("hooks-builder-req")) {
return;
}
const { type = "log" } = opts;
const log: ILog = {
type,
message,
timestring,
jsonData,
defaultCollapsed: true,
};
if (log) streamState.logs.push(log);
return log;
};