Ensure WebSocket client is never undefined

Change-Id: I5f5c34112fa989d6c06697b8a6d46acfbd01008a
diff --git a/client/src/contexts/CallManagerProvider.tsx b/client/src/contexts/CallManagerProvider.tsx
index 33b3055..ec68765 100644
--- a/client/src/contexts/CallManagerProvider.tsx
+++ b/client/src/contexts/CallManagerProvider.tsx
@@ -28,7 +28,7 @@
 import { SetState, WithChildren } from '../utils/utils';
 import { AlertSnackbarContext } from './AlertSnackbarProvider';
 import CallProvider, { CallRole } from './CallProvider';
-import { WebSocketContext } from './WebSocketProvider';
+import { useWebSocketContext } from './WebSocketProvider';
 
 export type CallData = {
   conversationId: string;
@@ -57,8 +57,8 @@
 
 export default ({ children }: WithChildren) => {
   const [callData, setCallData] = useState<CallData>();
-  const webSocket = useContext(WebSocketContext);
   const { setAlertContent } = useContext(AlertSnackbarContext);
+  const webSocket = useWebSocketContext();
   const navigate = useNavigate();
   const { data: conversationInfos } = useConversationInfosQuery(callData?.conversationId);
   const { data: members } = useMembersQuery(callData?.conversationId);
@@ -81,10 +81,6 @@
   }, [callData]);
 
   useEffect(() => {
-    if (!webSocket) {
-      return;
-    }
-
     const callBeginListener = ({ conversationId, withVideoOn }: CallBegin) => {
       if (callData) {
         // TODO: Currently, we display a notification if already in a call.
diff --git a/client/src/contexts/CallProvider.tsx b/client/src/contexts/CallProvider.tsx
index 050ad58..400140c 100644
--- a/client/src/contexts/CallProvider.tsx
+++ b/client/src/contexts/CallProvider.tsx
@@ -26,7 +26,7 @@
 import { useAuthContext } from './AuthProvider';
 import { CallData, CallManagerContext } from './CallManagerProvider';
 import ConditionalContextProvider from './ConditionalContextProvider';
-import { IWebSocketContext, WebSocketContext } from './WebSocketProvider';
+import { IWebSocketContext, useWebSocketContext } from './WebSocketProvider';
 
 export type CallRole = 'caller' | 'receiver';
 
@@ -83,7 +83,7 @@
 export const useCallContext = optionalCallContext.useOptionalContext;
 
 export default ({ children }: WithChildren) => {
-  const webSocket = useContext(WebSocketContext);
+  const webSocket = useWebSocketContext();
   const { callMembers, callData, exitCall } = useContext(CallManagerContext);
 
   const dependencies = useMemo(
diff --git a/client/src/contexts/ConversationProvider.tsx b/client/src/contexts/ConversationProvider.tsx
index d518c64..097d416 100644
--- a/client/src/contexts/ConversationProvider.tsx
+++ b/client/src/contexts/ConversationProvider.tsx
@@ -16,7 +16,7 @@
  * <https://www.gnu.org/licenses/>.
  */
 import { ComposingStatus, ConversationInfos, ConversationView, WebSocketMessageType } from 'jami-web-common';
-import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
+import { useCallback, useEffect, useMemo, useState } from 'react';
 
 import LoadingPage from '../components/Loading';
 import { createOptionalContext } from '../hooks/createOptionalContext';
@@ -27,7 +27,7 @@
 import { useConversationInfosQuery, useMembersQuery } from '../services/conversationQueries';
 import { WithChildren } from '../utils/utils';
 import { useAuthContext } from './AuthProvider';
-import { WebSocketContext } from './WebSocketProvider';
+import { useWebSocketContext } from './WebSocketProvider';
 
 interface IConversationContext {
   conversationId: string;
@@ -46,7 +46,7 @@
     urlParams: { conversationId },
   } = useUrlParams<ConversationRouteParams>();
   const { accountId, account } = useAuthContext();
-  const webSocket = useContext(WebSocketContext);
+  const webSocket = useWebSocketContext();
   const [composingMembers, setComposingMembers] = useState<ConversationMember[]>([]);
 
   const conversationInfosQuery = useConversationInfosQuery(conversationId!);
@@ -96,7 +96,7 @@
   );
 
   useEffect(() => {
-    if (!conversationInfos || !conversationId || !webSocket) {
+    if (!conversationInfos || !conversationId) {
       return;
     }
 
diff --git a/client/src/contexts/MessengerProvider.tsx b/client/src/contexts/MessengerProvider.tsx
index 2f86ad9..fe96468 100644
--- a/client/src/contexts/MessengerProvider.tsx
+++ b/client/src/contexts/MessengerProvider.tsx
@@ -16,10 +16,10 @@
  * <https://www.gnu.org/licenses/>.
  */
 import { ConversationMessage, IConversationRequest, WebSocketMessageType } from 'jami-web-common';
-import { createContext, ReactNode, useContext, useEffect, useMemo } from 'react';
+import { createContext, ReactNode, useEffect, useMemo } from 'react';
 
 import { useAddConversationRequestToCache, useRefreshConversationsSummaries } from '../services/conversationQueries';
-import { WebSocketContext } from './WebSocketProvider';
+import { useWebSocketContext } from './WebSocketProvider';
 
 // It is not sure yet we want this context to have no value
 // eslint-disable-next-line @typescript-eslint/no-empty-interface
@@ -30,16 +30,12 @@
 export const MessengerContext = createContext<IMessengerContext>(defaultMessengerContext);
 
 export default ({ children }: { children: ReactNode }) => {
-  const webSocket = useContext(WebSocketContext);
+  const webSocket = useWebSocketContext();
 
   const refreshConversationsSummaries = useRefreshConversationsSummaries();
   const addConversationRequestToCache = useAddConversationRequestToCache();
 
   useEffect(() => {
-    if (!webSocket) {
-      return;
-    }
-
     const conversationMessageListener = (_data: ConversationMessage) => {
       refreshConversationsSummaries();
     };
diff --git a/client/src/contexts/WebSocketProvider.tsx b/client/src/contexts/WebSocketProvider.tsx
index 44d54c0..1408953 100644
--- a/client/src/contexts/WebSocketProvider.tsx
+++ b/client/src/contexts/WebSocketProvider.tsx
@@ -15,8 +15,9 @@
  * License along with this program.  If not, see
  * <https://www.gnu.org/licenses/>.
  */
-import { createContext, useEffect, useMemo, useRef } from 'react';
+import { useEffect, useMemo, useRef } from 'react';
 
+import { createOptionalContext } from '../hooks/createOptionalContext';
 import { WebSocketClient } from '../services/WebSocketClient';
 import { apiUrl } from '../utils/constants';
 import { WithChildren } from '../utils/utils';
@@ -28,7 +29,8 @@
   send: WebSocketClient['send'];
 }
 
-export const WebSocketContext = createContext<IWebSocketContext | undefined>(undefined);
+const optionalWebSocketContext = createOptionalContext<IWebSocketContext>('WebSocketContext');
+export const useWebSocketContext = optionalWebSocketContext.useOptionalContext;
 
 export default ({ children }: WithChildren) => {
   const webSocketClientRef = useRef<WebSocketClient>(new WebSocketClient());
@@ -39,7 +41,7 @@
     webSocketClientRef.current.connect(apiUrl, accessToken);
   }, [accessToken]);
 
-  const value: IWebSocketContext = useMemo(
+  const value = useMemo(
     () => ({
       bind: webSocketClientRef.current.bind.bind(webSocketClientRef.current),
       unbind: webSocketClientRef.current.unbind.bind(webSocketClientRef.current),
@@ -48,5 +50,7 @@
     []
   );
 
-  return <WebSocketContext.Provider value={value}>{children}</WebSocketContext.Provider>;
+  return (
+    <optionalWebSocketContext.Context.Provider value={value}>{children}</optionalWebSocketContext.Context.Provider>
+  );
 };