Commit dc8dce68 authored by tom's avatar tom

basic socket implementation

parent b13780fe
......@@ -61,6 +61,9 @@ function makePolicyMap() {
// client error monitoring
'sentry.io', '*.sentry.io',
// todo_tom pass host from config
'wss://blockscout.com',
],
'script-src': [
......
import React from 'react';
import { SECOND } from 'lib/consts';
const OPEN_STATE = 1;
interface Params {
onOpen?: (event: Event) => void;
onError?: (event: Event) => void;
onClose?: (event: Event) => void;
}
type SocketData = [ null, null, string, string, Record<string, unknown> ];
interface SocketChannelSubscriber {
filters?: Array<string>;
callback: (payload: unknown) => void;
}
export default function useApiSocket({ onOpen, onError, onClose }: Params) {
const socket = React.useRef<WebSocket>();
const onReadyEvents = React.useRef<Array<SocketData>>([]);
const channels = React.useRef<Record<string, Array<SocketChannelSubscriber>>>({});
function startHeartBeat() {
return window.setInterval(() => {
const data: SocketData = [ null, null, 'phoenix', 'heartbeat', {} ];
socket.current?.send(JSON.stringify(data));
}, 30 * SECOND);
}
const joinRoom = React.useCallback((id: string, subscriber: SocketChannelSubscriber) => {
const data: SocketData = [ null, null, id, 'phx_join', {} ];
if (socket.current?.readyState === OPEN_STATE) {
socket.current?.send(JSON.stringify(data));
} else {
onReadyEvents.current.push(data);
}
if (channels.current[id]) {
channels.current[id].push(subscriber);
} else {
channels.current[id] = [ subscriber ];
}
}, []);
const leaveRoom = React.useCallback((id: string) => {
const data: SocketData = [ null, null, id, 'phx_leave', {} ];
if (socket.current?.readyState === OPEN_STATE) {
socket.current?.send(JSON.stringify(data));
} else {
onReadyEvents.current.push(data);
}
channels.current[id] = [];
}, []);
React.useEffect(() => {
if (socket.current) {
socket.current.close();
}
// todo_tom pass host and base path from config
socket.current = new WebSocket('wss://blockscout.com/poa/core/socket/v2/websocket?vsn=2.0.0');
let heartBeatTimeoutId: number | undefined;
socket.current.addEventListener('open', (event: Event) => {
onOpen?.(event);
heartBeatTimeoutId = startHeartBeat();
onReadyEvents.current.forEach((data) => socket.current?.send(JSON.stringify(data)));
onReadyEvents.current = [];
});
socket.current.addEventListener('message', (event) => {
const data: SocketData = JSON.parse(event.data);
const channelId = data[2];
const filterId = data[3];
const payload = data[4];
const subscribers = channels.current[channelId];
subscribers
?.filter((subscriber) => subscriber.filters ? subscriber.filters.includes(filterId) : true)
?.forEach((subscriber) => subscriber.callback(payload));
});
socket.current.addEventListener('error', (event) => {
onError?.(event);
});
socket.current.addEventListener('close', (event) => {
onClose?.(event);
});
return () => {
window.clearInterval(heartBeatTimeoutId);
socket.current?.close();
};
}, [ onClose, onError, onOpen ]);
return { joinRoom, leaveRoom };
}
......@@ -5,6 +5,7 @@ import React from 'react';
import appConfig from 'configs/app/config';
import * as cookies from 'lib/cookies';
import useApiSocket from 'lib/hooks/useApiSocket';
import useToast from 'lib/hooks/useToast';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle';
......@@ -14,11 +15,23 @@ const Home = () => {
const [ isFormVisible, setFormVisibility ] = React.useState(false);
const [ token, setToken ] = React.useState('');
const { joinRoom, leaveRoom } = useApiSocket({});
React.useEffect(() => {
joinRoom('blocks:0xdc4765d9dabf6c6c4908fe97e649ef1f05cb6252', {
filters: [ 'new_block' ],
callback: () => {},
});
return () => {
leaveRoom('blocks:0xdc4765d9dabf6c6c4908fe97e649ef1f05cb6252');
};
}, [ joinRoom, leaveRoom ]);
React.useEffect(() => {
const token = cookies.get(cookies.NAMES.API_TOKEN);
setFormVisibility(Boolean(!token && appConfig.isAccountSupported));
}, []);
}, [ joinRoom ]);
const checkSentry = React.useCallback(() => {
Sentry.captureException(new Error('Test error'), { extra: { foo: 'bar' }, tags: { source: 'test' } });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment