Send received message through WebSocket
- send received message from through WebSocket
- remove SocketIO from client
GitLab: #96
Change-Id: I7a8eec04010f0773428f914792c13decef393ebf
diff --git a/client/src/contexts/WebSocketProvider.tsx b/client/src/contexts/WebSocketProvider.tsx
index 86b794f..38317e2 100644
--- a/client/src/contexts/WebSocketProvider.tsx
+++ b/client/src/contexts/WebSocketProvider.tsx
@@ -15,18 +15,16 @@
* License along with this program. If not, see
* <https://www.gnu.org/licenses/>.
*/
-import { WebSocketMessage, WebSocketMessageType } from 'jami-web-common';
+import { WebSocketCallbacks, WebSocketMessage, WebSocketMessageTable, WebSocketMessageType } from 'jami-web-common';
import { createContext, useCallback, useEffect, useRef, useState } from 'react';
import { apiUrl } from '../utils/constants';
import { WithChildren } from '../utils/utils';
import { useAuthContext } from './AuthProvider';
-export type WebSocketMessageFn = (message: WebSocketMessage) => void;
-
-interface IWebSocketContext {
- bind: (type: WebSocketMessageType, callback: WebSocketMessageFn) => void;
- send: WebSocketMessageFn;
+export interface IWebSocketContext {
+ bind: <T extends WebSocketMessageType>(type: T, callback: (data: WebSocketMessageTable[T]) => void) => void;
+ send: <T extends WebSocketMessageType>(type: T, data: WebSocketMessageTable[T]) => void;
}
export const WebSocketContext = createContext<IWebSocketContext | undefined>(undefined);
@@ -34,71 +32,88 @@
export default ({ children }: WithChildren) => {
const [isConnected, setIsConnected] = useState(false);
const webSocketRef = useRef<WebSocket>();
- const callbacksRef = useRef(new Map<WebSocketMessageType, WebSocketMessageFn[]>());
+ const callbacksRef = useRef<WebSocketCallbacks>({
+ [WebSocketMessageType.ConversationMessage]: [],
+ [WebSocketMessageType.ConversationView]: [],
+ [WebSocketMessageType.WebRTCOffer]: [],
+ [WebSocketMessageType.WebRTCAnswer]: [],
+ [WebSocketMessageType.IceCandidate]: [],
+ });
const { token: accessToken } = useAuthContext();
- const bind = useCallback((type: WebSocketMessageType, messageCallback: WebSocketMessageFn) => {
- const messageCallbacks = callbacksRef.current.get(type);
- if (messageCallbacks) {
- messageCallbacks.push(messageCallback);
- } else {
- callbacksRef.current.set(type, [messageCallback]);
- }
- }, []);
+ const context: IWebSocketContext = {
+ bind: useCallback((type, callback) => {
+ callbacksRef.current[type].push(callback);
+ }, []),
+ send: useCallback(
+ (type, data) => {
+ if (isConnected) {
+ webSocketRef.current?.send(JSON.stringify({ type, data }));
+ }
+ },
+ [isConnected]
+ ),
+ };
- const send = useCallback(
- (message: WebSocketMessage) => {
- if (isConnected) {
- webSocketRef.current?.send(JSON.stringify(message));
- }
- },
- [isConnected]
- );
-
- const handleOnOpen = useCallback(() => setIsConnected(true), []);
-
- const handleOnClose = useCallback(() => {
- setIsConnected(false);
- callbacksRef.current.clear();
- }, []);
-
- const handleOnMessage = useCallback(({ data }: MessageEvent<string>) => {
- const message: WebSocketMessage = JSON.parse(data);
- const messageCallbacks = callbacksRef.current.get(message.type);
- if (messageCallbacks) {
- for (const messageCallback of messageCallbacks) {
- messageCallback(message);
- }
- } else {
- console.warn(`Unhandled message of type ${message.type}`);
- }
- }, []);
-
- const handleOnError = useCallback((event: Event) => {
- console.error('Closing WebSocket due to an error:', event);
- webSocketRef.current?.close();
- }, []);
-
- useEffect(() => {
+ const connect = useCallback(() => {
const url = new URL(apiUrl);
url.protocol = 'ws:';
url.searchParams.set('accessToken', accessToken);
const webSocket = new WebSocket(url);
- webSocket.onopen = handleOnOpen;
- webSocket.onclose = handleOnClose;
- webSocket.onmessage = handleOnMessage;
- webSocket.onerror = handleOnError;
+
+ webSocket.onopen = () => {
+ console.debug('WebSocket connected');
+ setIsConnected(true);
+ };
+
+ webSocket.onclose = () => {
+ console.debug('WebSocket disconnected');
+ setIsConnected(false);
+ for (const callbacks of Object.values(callbacksRef.current)) {
+ callbacks.length = 0;
+ }
+ setTimeout(connect, 1000);
+ };
+
+ webSocket.onmessage = <T extends WebSocketMessageType>({ data }: MessageEvent<string>) => {
+ console.debug('WebSocket received message', data);
+ const message: WebSocketMessage<T> = JSON.parse(data);
+ if (!message.type || !message.data) {
+ console.warn(`Incorrect format (require type and data) ${message}`);
+ return;
+ }
+ if (!Object.values(WebSocketMessageType).includes(message.type)) {
+ console.warn(`Unhandled message of type: ${message.type}`);
+ return;
+ }
+ const callbacks = callbacksRef.current[message.type];
+ for (const callback of callbacks) {
+ callback(message.data);
+ }
+ };
+
+ webSocket.onerror = (event: Event) => {
+ console.error('Closing WebSocket due to an error:', event);
+ webSocketRef.current?.close();
+ };
webSocketRef.current = webSocket;
- return () => webSocket.close();
- }, [accessToken, handleOnOpen, handleOnClose, handleOnMessage, handleOnError]);
+ return () => {
+ switch (webSocket.readyState) {
+ case webSocket.CONNECTING:
+ webSocket.onopen = () => webSocket.close();
+ break;
+ case webSocket.OPEN:
+ webSocket.close();
+ break;
+ }
+ };
+ }, [accessToken]);
- return isConnected ? (
- <WebSocketContext.Provider value={{ bind, send }}>{children}</WebSocketContext.Provider>
- ) : (
- <>{children}</>
- );
+ useEffect(connect, [connect]);
+
+ return <WebSocketContext.Provider value={isConnected ? context : undefined}>{children}</WebSocketContext.Provider>;
};