Redirect user from call page when not in call

When a user tries to access a call page while not in a call, redirect the user to the home page.

Misc changes:

- Add route state to the call route that includes the CallStatus.
- CallProvider redirects to home if the callStatus isn't set (meaning
  the user isn't in a call).
- Remove `beginCall` function in `ConversationProvider`. Added `useStartCall` hook that redirects the user to the call page. The `CallProvider` automatically sends the `BeginCall` message when the user reaches the page for the first time.
- Reorder functions in CallProvider to have `useEffect` functions at the top

GitLab: #164
Change-Id: I6cec1b9f31cb308d92a69112f5b38d1bdf79e05f
diff --git a/client/src/components/ConversationListItem.tsx b/client/src/components/ConversationListItem.tsx
index a1e6693..48e9004 100644
--- a/client/src/components/ConversationListItem.tsx
+++ b/client/src/components/ConversationListItem.tsx
@@ -23,6 +23,7 @@
 import { useNavigate, useParams } from 'react-router-dom';
 
 import { useAuthContext } from '../contexts/AuthProvider';
+import { useStartCall } from '../hooks/useStartCall';
 import { setRefreshFromSlice } from '../redux/appSlice';
 import { useAppDispatch } from '../redux/hooks';
 import ContextMenu, { ContextMenuHandler, useContextMenuHandler } from './ContextMenu';
@@ -51,7 +52,12 @@
   const isSelected = conversation.getDisplayUri() === pathId;
   const navigate = useNavigate();
   const userId = conversation?.getFirstMember()?.contact.getUri();
-  const uri = conversation.getId() ? `/conversation/${conversation.getId()}` : `/conversation/add-contact/${userId}`;
+
+  // TODO: Improve this component. conversationId should never be undefined.
+  //       (https://git.jami.net/savoirfairelinux/jami-web/-/issues/171)
+  const uri = conversation.getId()
+    ? `/conversation/${conversation.getId()}`
+    : `/conversation/add-contact?newContactId=${userId}`;
   return (
     <Box onContextMenu={contextMenuHandler.handleAnchorPosition}>
       <ConversationMenu
@@ -90,6 +96,8 @@
 
   const navigate = useNavigate();
 
+  const startCall = useStartCall();
+
   const getContactDetails = useCallback(async () => {
     const controller = new AbortController();
     try {
@@ -102,6 +110,8 @@
     }
   }, [axiosInstance, userId]);
 
+  const conversationId = conversation.getId();
+
   const menuOptions: PopoverListItemData[] = useMemo(
     () => [
       {
@@ -115,14 +125,20 @@
         label: t('conversation_start_audiocall'),
         Icon: AudioCallIcon,
         onClick: () => {
-          navigate(`/account/call/${conversation.getId()}`);
+          if (conversationId) {
+            startCall(conversationId);
+          }
         },
       },
       {
         label: t('conversation_start_videocall'),
         Icon: VideoCallIcon,
         onClick: () => {
-          navigate(`call/${conversation.getId()}?video=true`);
+          if (conversationId) {
+            startCall(conversationId, {
+              isVideoOn: true,
+            });
+          }
         },
       },
       ...(isSelected
@@ -160,7 +176,6 @@
       },
     ],
     [
-      conversation,
       navigate,
       uri,
       isSelected,
@@ -169,6 +184,8 @@
       blockContactDialogHandler,
       removeContactDialogHandler,
       t,
+      startCall,
+      conversationId,
     ]
   );
 
diff --git a/client/src/components/ConversationView.tsx b/client/src/components/ConversationView.tsx
index c638295..52215e8 100644
--- a/client/src/components/ConversationView.tsx
+++ b/client/src/components/ConversationView.tsx
@@ -22,6 +22,7 @@
 
 import { useAuthContext } from '../contexts/AuthProvider';
 import { ConversationContext } from '../contexts/ConversationProvider';
+import { useStartCall } from '../hooks/useStartCall';
 import ChatInterface from '../pages/ChatInterface';
 import { translateEnumeration, TranslateEnumerationOptions } from '../utils/translations';
 import { AddParticipantButton, ShowOptionsMenuButton, StartAudioCallButton, StartVideoCallButton } from './Button';
@@ -57,7 +58,7 @@
 
 const ConversationHeader = ({ account, members, adminTitle }: ConversationHeaderProps) => {
   const { t } = useTranslation();
-  const { beginCall } = useContext(ConversationContext);
+  const { conversationId } = useContext(ConversationContext);
 
   const title = useMemo(() => {
     if (adminTitle) {
@@ -82,6 +83,8 @@
     return translateEnumeration<ConversationMember>(members, options);
   }, [account, members, adminTitle, t]);
 
+  const startCall = useStartCall();
+
   return (
     <Stack direction="row" padding="16px" overflow="hidden">
       <Stack flex={1} justifyContent="center" whiteSpace="nowrap" overflow="hidden">
@@ -90,8 +93,14 @@
         </Typography>
       </Stack>
       <Stack direction="row" spacing="20px">
-        <StartAudioCallButton onClick={() => beginCall()} />
-        <StartVideoCallButton onClick={() => beginCall()} />
+        <StartAudioCallButton onClick={() => startCall(conversationId)} />
+        <StartVideoCallButton
+          onClick={() =>
+            startCall(conversationId, {
+              isVideoOn: true,
+            })
+          }
+        />
         <AddParticipantButton />
         <ShowOptionsMenuButton />
       </Stack>