Add small UX improvements to call

- Notification saying you have missed a call if you are already in a call
- Indication about the call in the conversation list item

GitLab: #197
GitLab: #173
Change-Id: Ibe4ff0d9f3ffd95cd0d83afd2041c5fad513a7e0
diff --git a/client/src/components/ConversationListItem.tsx b/client/src/components/ConversationListItem.tsx
index 3f5dad3..e4be263 100644
--- a/client/src/components/ConversationListItem.tsx
+++ b/client/src/components/ConversationListItem.tsx
@@ -24,6 +24,7 @@
 
 import { useAuthContext } from '../contexts/AuthProvider';
 import { CallManagerContext } from '../contexts/CallManagerProvider';
+import { CallStatus, useCallContext } from '../contexts/CallProvider';
 import { useConversationContext } from '../contexts/ConversationProvider';
 import { MessengerContext } from '../contexts/MessengerProvider';
 import { Conversation } from '../models/conversation';
@@ -52,6 +53,9 @@
   const conversationId = conversationContext?.conversationId;
   const contextMenuHandler = useContextMenuHandler();
   const { newContactId, setNewContactId } = useContext(MessengerContext);
+  const callContext = useCallContext(true);
+  const { callData } = useContext(CallManagerContext);
+  const { t } = useTranslation();
 
   const pathId = conversationId ?? newContactId;
   const isSelected = conversation.getDisplayUri() === pathId;
@@ -68,6 +72,28 @@
     }
   }, [navigate, conversation, userId, setNewContactId]);
 
+  const getSecondaryText = () => {
+    const propsConversationId = conversation.id;
+
+    if (!propsConversationId) {
+      return '';
+    }
+
+    if (!callContext || !callData || callData.conversationId !== propsConversationId) {
+      return conversation.getDisplayUri();
+    }
+
+    if (callContext.callStatus === CallStatus.InCall) {
+      return callContext.isAudioOn ? t('ongoing_call_unmuted') : t('ongoing_call_muted');
+    }
+
+    if (callContext.callStatus === CallStatus.Connecting) {
+      return t('connecting_call');
+    }
+
+    return callContext.callRole === 'caller' ? t('outgoing_call') : t('incoming_call');
+  };
+
   return (
     <Box onContextMenu={contextMenuHandler.handleAnchorPosition}>
       <ConversationMenu
@@ -81,7 +107,7 @@
         <ListItemAvatar>
           <ConversationAvatar displayName={conversation.getDisplayNameNoFallback()} />
         </ListItemAvatar>
-        <ListItemText primary={conversation.getDisplayName()} secondary={conversation.getDisplayUri()} />
+        <ListItemText primary={conversation.getDisplayName()} secondary={getSecondaryText()} />
       </ListItem>
     </Box>
   );
diff --git a/client/src/contexts/CallManagerProvider.tsx b/client/src/contexts/CallManagerProvider.tsx
index 7d8d843..c04010f 100644
--- a/client/src/contexts/CallManagerProvider.tsx
+++ b/client/src/contexts/CallManagerProvider.tsx
@@ -17,8 +17,10 @@
  */
 import { CallBegin, WebSocketMessageType } from 'jami-web-common';
 import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
+import { useTranslation } from 'react-i18next';
 import { useNavigate } from 'react-router-dom';
 
+import { AlertSnackbar } from '../components/AlertSnackbar';
 import { RemoteVideoOverlay } from '../components/VideoOverlay';
 import { useUrlParams } from '../hooks/useUrlParams';
 import { Conversation } from '../models/conversation';
@@ -60,6 +62,8 @@
   const navigate = useNavigate();
   const { conversation } = useConversationQuery(callData?.conversationId);
   const { urlParams } = useUrlParams<ConversationRouteParams>();
+  const [missedCallConversationId, setMissedCallConversationId] = useState<string>();
+  const { t } = useTranslation();
 
   const failStartCall = useCallback(() => {
     throw new Error('Cannot start call: Already in a call');
@@ -77,16 +81,18 @@
   }, [callData]);
 
   useEffect(() => {
-    if (callData) {
-      // TODO: Currently, we simply do not bind the CallBegin listener if already in a call.
-      //       In the future, we should handle receiving a call while already in another.
-      return;
-    }
     if (!webSocket) {
       return;
     }
 
     const callBeginListener = ({ conversationId, withVideoOn }: CallBegin) => {
+      if (callData) {
+        // TODO: Currently, we display a notification if already in a call.
+        //       In the future, we should handle receiving a call while already in another.
+        setMissedCallConversationId(conversationId);
+        return;
+      }
+
       startCall({ conversationId: conversationId, role: 'receiver', withVideoOn });
       navigate(`/conversation/${conversationId}`);
     };
@@ -109,15 +115,24 @@
   );
 
   return (
-    <CallManagerContext.Provider value={value}>
-      <WebRtcProvider>
-        <CallProvider>
-          {callData && callData.conversationId !== urlParams.conversationId && (
-            <RemoteVideoOverlay callConversationId={callData.conversationId} />
-          )}
-          {children}
-        </CallProvider>
-      </WebRtcProvider>
-    </CallManagerContext.Provider>
+    <>
+      <AlertSnackbar
+        severity={'info'}
+        open={missedCallConversationId !== undefined}
+        onClose={() => setMissedCallConversationId(undefined)}
+      >
+        {t('missed_incoming_call', { conversationId: missedCallConversationId })}
+      </AlertSnackbar>
+      <CallManagerContext.Provider value={value}>
+        <WebRtcProvider>
+          <CallProvider>
+            {callData && callData.conversationId !== urlParams.conversationId && (
+              <RemoteVideoOverlay callConversationId={callData.conversationId} />
+            )}
+            {children}
+          </CallProvider>
+        </WebRtcProvider>
+      </CallManagerContext.Provider>
+    </>
   );
 };
diff --git a/client/src/contexts/CallProvider.tsx b/client/src/contexts/CallProvider.tsx
index 8be6dff..7517133 100644
--- a/client/src/contexts/CallProvider.tsx
+++ b/client/src/contexts/CallProvider.tsx
@@ -414,13 +414,10 @@
       mediaDevices,
       currentMediaDeviceIds,
       isAudioOn,
-      setIsAudioOn,
       videoStatus,
       updateVideoStatus,
       isChatShown,
-      setIsChatShown,
       isFullscreen,
-      setIsFullscreen,
       callRole,
       callStatus,
       callStartTime,
diff --git a/client/src/locale/en/translation.json b/client/src/locale/en/translation.json
index 701998a..21fff51 100644
--- a/client/src/locale/en/translation.json
+++ b/client/src/locale/en/translation.json
@@ -1,9 +1,10 @@
 {
   "severity": "",
-  "share_screen": "Share your screen",
-  "share_window": "Share window",
-  "share_screen_area": "Share an area of your screen",
-  "share_file": "Share a file",
+  "ongoing_call_unmuted": "Ongoing call",
+  "ongoing_call_muted": "Ongoing call (muted)",
+  "connecting_call": "Connecting...",
+  "outgoing_call": "Outgoing call",
+  "incoming_call": "Incoming call",
   "conversation_message": "Message",
   "conversation_start_audiocall": "Start audio call",
   "conversation_start_videocall": "Start video call",
@@ -72,6 +73,7 @@
   "message_input_placeholder_three": "Write to {{member0}}, {{member1}} and {{member2}}",
   "message_input_placeholder_four": "Write to {{member0}}, {{member1}}, {{member2}}, +1 other member",
   "message_input_placeholder_more": "Write to {{member0}}, {{member1}}, {{member2}}, +{{excess}} other members",
+  "missed_incoming_call": "Missed incoming call from conversation {{conversationId}}",
   "conversation_add_contact": "Add contact",
   "loading": "Loading...",
   "calling": "Calling {{member0}}",
@@ -111,8 +113,13 @@
   "setup_login_password_placeholder_creation": "New password",
   "setup_login_password_placeholder_repeat": "Repeat password",
   "admin_creation_submit_button": "Create admin account",
+  "share_screen": "Share your screen",
+  "share_window": "Share window",
+  "share_screen_area": "Share an area of your screen",
+  "share_file": "Share a file",
   "incoming_call_audio": "Incoming audio call from {{member0}}",
   "incoming_call_video": "Incoming video call from {{member0}}",
   "severity_error": "Error",
-  "severity_success": "Success"
+  "severity_success": "Success",
+  "severity_info": "Info"
 }
diff --git a/client/src/locale/fr/translation.json b/client/src/locale/fr/translation.json
index d91519f..c397c0f 100644
--- a/client/src/locale/fr/translation.json
+++ b/client/src/locale/fr/translation.json
@@ -1,9 +1,10 @@
 {
   "severity": "",
-  "share_screen": "Partager votre écran",
-  "share_window": "Partager la fenêtre",
-  "share_screen_area": "Partager une partie de l'écran",
-  "share_file": "Partager le fichier",
+  "ongoing_call_unmuted": "Appel en cours",
+  "ongoing_call_muted": "Appel en cours (muet)",
+  "connecting_call": "Connexion...",
+  "outgoing_call": "Appel sortant",
+  "incoming_call": "Appel entrant",
   "conversation_message": "Envoyer un message",
   "conversation_start_audiocall": "Démarrer appel audio",
   "conversation_start_videocall": "Démarrer appel vidéo",
@@ -72,6 +73,7 @@
   "message_input_placeholder_three": "Écrire à {{member0}}, {{member1}} et {{member2}}",
   "message_input_placeholder_four": "Écrire à {{member0}}, {{member1}}, {{member2}}, +1 autre membre",
   "message_input_placeholder_more": "Écrire à {{member0}}, {{member1}}, {{member2}}, +{{excess}} autres membres",
+  "missed_incoming_call": "Appel manqué de la conversation {{conversationId}}",
   "conversation_add_contact": "Ajouter le contact",
   "loading": "Chargement...",
   "calling": "Appel vers {{member0}}",
@@ -111,8 +113,13 @@
   "setup_login_password_placeholder_creation": "Nouveau mot de passe",
   "setup_login_password_placeholder_repeat": "Répéter le mot de passe",
   "admin_creation_submit_button": "Créer un compte admin",
+  "share_screen": "Partager votre écran",
+  "share_window": "Partager la fenêtre",
+  "share_screen_area": "Partager une partie de l'écran",
+  "share_file": "Partager le fichier",
   "incoming_call_audio": "Appel audio entrant de {{member0}}",
   "incoming_call_video": "Appel vidéo entrant de {{member0}}",
   "severity_error": "Erreur",
-  "severity_success": "Succès"
+  "severity_success": "Succès",
+  "severity_info": "Info"
 }