Add unbind function to WebSocketContext

Change WebSocketCallbacks to have sets of callbacks instead of arrays.

Change-Id: I48439d0ff9b8188f47e0530d7d91f78c6fae8868
diff --git a/client/src/contexts/WebSocketProvider.tsx b/client/src/contexts/WebSocketProvider.tsx
index e6e246b..b90f609 100644
--- a/client/src/contexts/WebSocketProvider.tsx
+++ b/client/src/contexts/WebSocketProvider.tsx
@@ -15,16 +15,29 @@
  * License along with this program.  If not, see
  * <https://www.gnu.org/licenses/>.
  */
-import { WebSocketCallbacks, WebSocketMessage, WebSocketMessageTable, WebSocketMessageType } from 'jami-web-common';
+import {
+  buildWebSocketCallbacks,
+  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';
 
+type BindFunction = <T extends WebSocketMessageType>(
+  type: T,
+  callback: (data: WebSocketMessageTable[T]) => void
+) => void;
+type SendFunction = <T extends WebSocketMessageType>(type: T, data: WebSocketMessageTable[T]) => void;
+
 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;
+  bind: BindFunction;
+  unbind: BindFunction;
+  send: SendFunction;
 }
 
 export const WebSocketContext = createContext<IWebSocketContext | undefined>(undefined);
@@ -32,29 +45,28 @@
 export default ({ children }: WithChildren) => {
   const [isConnected, setIsConnected] = useState(false);
   const webSocketRef = useRef<WebSocket>();
-  const callbacksRef = useRef<WebSocketCallbacks>({
-    [WebSocketMessageType.ConversationMessage]: [],
-    [WebSocketMessageType.ConversationView]: [],
-    [WebSocketMessageType.WebRTCOffer]: [],
-    [WebSocketMessageType.WebRTCAnswer]: [],
-    [WebSocketMessageType.IceCandidate]: [],
-  });
+  const callbacksRef = useRef<WebSocketCallbacks>(buildWebSocketCallbacks());
 
   const { token: accessToken } = useAuthContext();
 
-  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 bind: BindFunction = useCallback((type, callback) => {
+    const callbacks = callbacksRef.current[type];
+    callbacks.add(callback);
+  }, []);
+
+  const unbind: BindFunction = useCallback((type, callback) => {
+    const callbacks = callbacksRef.current[type];
+    callbacks.delete(callback);
+  }, []);
+
+  const send: SendFunction = useCallback(
+    (type, data) => {
+      if (isConnected) {
+        webSocketRef.current?.send(JSON.stringify({ type, data }));
+      }
+    },
+    [isConnected]
+  );
 
   const connect = useCallback(() => {
     const url = new URL(apiUrl);
@@ -72,7 +84,7 @@
       console.debug('WebSocket disconnected');
       setIsConnected(false);
       for (const callbacks of Object.values(callbacksRef.current)) {
-        callbacks.length = 0;
+        callbacks.clear();
       }
       setTimeout(connect, 1000);
     };
@@ -119,5 +131,19 @@
 
   useEffect(connect, [connect]);
 
-  return <WebSocketContext.Provider value={isConnected ? context : undefined}>{children}</WebSocketContext.Provider>;
+  return (
+    <WebSocketContext.Provider
+      value={
+        isConnected
+          ? {
+              bind,
+              unbind,
+              send,
+            }
+          : undefined
+      }
+    >
+      {children}
+    </WebSocketContext.Provider>
+  );
 };