soapbox/app/soapbox/stream.ts

129 wiersze
3.2 KiB
TypeScript
Czysty Zwykły widok Historia

2020-03-27 20:59:38 +00:00
'use strict';
import WebSocketClient from '@gamestdio/websocket';
2021-03-24 22:53:09 +00:00
import { getAccessToken } from 'soapbox/utils/auth';
2020-03-27 20:59:38 +00:00
import type { AppDispatch, RootState } from 'soapbox/store';
2020-03-27 20:59:38 +00:00
const randomIntUpTo = (max: number) => Math.floor(Math.random() * Math.floor(max));
2023-04-03 00:54:08 +00:00
interface ConnectStreamCallbacks {
onConnect(): void
onDisconnect(): void
onReceive(websocket: WebSocket, data: unknown): void
}
type PollingRefreshFn = (dispatch: AppDispatch, done?: () => void) => void
export function connectStream(
path: string,
2023-04-03 00:54:08 +00:00
pollingRefresh: PollingRefreshFn | null = null,
callbacks: (dispatch: AppDispatch, getState: () => RootState) => ConnectStreamCallbacks,
) {
return (dispatch: AppDispatch, getState: () => RootState) => {
const streamingAPIBaseURL = getState().instance.urls.get('streaming_api');
2021-03-24 22:53:09 +00:00
const accessToken = getAccessToken(getState());
2020-03-27 20:59:38 +00:00
const { onConnect, onDisconnect, onReceive } = callbacks(dispatch, getState);
let polling: NodeJS.Timeout | null = null;
2020-03-27 20:59:38 +00:00
const setupPolling = () => {
if (pollingRefresh) {
pollingRefresh(dispatch, () => {
polling = setTimeout(() => setupPolling(), 20000 + randomIntUpTo(20000));
});
}
2020-03-27 20:59:38 +00:00
};
const clearPolling = () => {
if (polling) {
clearTimeout(polling);
polling = null;
}
};
2023-04-03 00:54:08 +00:00
let subscription: WebSocket;
2020-03-27 20:59:38 +00:00
// If the WebSocket fails to be created, don't crash the whole page,
// just proceed without a subscription.
try {
subscription = getStream(streamingAPIBaseURL!, accessToken, path, {
connected() {
if (pollingRefresh) {
clearPolling();
}
onConnect();
},
2020-03-27 20:59:38 +00:00
disconnected() {
if (pollingRefresh) {
polling = setTimeout(() => setupPolling(), randomIntUpTo(40000));
}
2020-03-27 20:59:38 +00:00
onDisconnect();
},
2020-03-27 20:59:38 +00:00
received(data) {
2023-04-03 00:54:08 +00:00
onReceive(subscription, data);
},
2020-03-27 20:59:38 +00:00
reconnected() {
if (pollingRefresh) {
clearPolling();
pollingRefresh(dispatch);
}
2020-03-27 20:59:38 +00:00
onConnect();
},
2020-03-27 20:59:38 +00:00
});
} catch (e) {
console.error(e);
}
2020-03-27 20:59:38 +00:00
const disconnect = () => {
if (subscription) {
subscription.close();
}
clearPolling();
};
return disconnect;
};
}
export default function getStream(
streamingAPIBaseURL: string,
accessToken: string,
stream: string,
{ connected, received, disconnected, reconnected }: {
connected: ((this: WebSocket, ev: Event) => any) | null
received: (data: any) => void
disconnected: ((this: WebSocket, ev: Event) => any) | null
reconnected: ((this: WebSocket, ev: Event) => any)
},
) {
2020-03-27 20:59:38 +00:00
const params = [ `stream=${stream}` ];
const ws = new WebSocketClient(`${streamingAPIBaseURL}/api/v1/streaming/?${params.join('&')}`, accessToken as any);
2020-03-27 20:59:38 +00:00
ws.onopen = connected;
ws.onclose = disconnected;
ws.onreconnect = reconnected;
ws.onmessage = (e) => {
if (!e.data) return;
try {
received(JSON.parse(e.data));
} catch (error) {
console.error(e);
2020-04-14 18:44:40 +00:00
console.error(`Could not parse the above streaming event.\n${error}`);
}
2020-04-14 18:44:40 +00:00
};
2020-03-27 20:59:38 +00:00
return ws;
2021-08-03 19:22:51 +00:00
}