Add toggleable chat drawer in call

During a call, the chat button is enabled and lets the user open the
chat drawer.

GitLab: #177
Change-Id: I7a2ae09fca5af904c3280bc948a2d36135c1c63d
diff --git a/client/src/components/CallButtons.tsx b/client/src/components/CallButtons.tsx
index 7ccdd91..b127a77 100644
--- a/client/src/components/CallButtons.tsx
+++ b/client/src/components/CallButtons.tsx
@@ -84,7 +84,17 @@
 });
 
 export const CallingChatButton = (props: ExpandableButtonProps) => {
-  return <CallButton aria-label="chat" Icon={ChatBubbleIcon} {...props} />;
+  const { setIsChatShown } = useContext(CallContext);
+  return (
+    <CallButton
+      aria-label="chat"
+      Icon={ChatBubbleIcon}
+      onClick={() => {
+        setIsChatShown((v) => !v);
+      }}
+      {...props}
+    />
+  );
 };
 
 export const CallingEndButton = (props: ExpandableButtonProps) => {
diff --git a/client/src/components/CallChatDrawer.tsx b/client/src/components/CallChatDrawer.tsx
new file mode 100644
index 0000000..d2040d7
--- /dev/null
+++ b/client/src/components/CallChatDrawer.tsx
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 Savoir-faire Linux Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this program.  If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+import { Divider, Stack, Typography } from '@mui/material';
+import { useContext } from 'react';
+
+import { CallContext } from '../contexts/CallProvider';
+import { ConversationContext } from '../contexts/ConversationProvider';
+import ChatInterface from '../pages/ChatInterface';
+import { CloseButton } from './Button';
+
+export default () => {
+  return (
+    <Stack
+      width="33%"
+      height="100%"
+      sx={{
+        backgroundColor: 'white',
+      }}
+    >
+      <CallChatDrawerHeader />
+      <Divider
+        sx={{
+          borderTop: '1px solid #E5E5E5',
+        }}
+      />
+      <ChatInterface />
+    </Stack>
+  );
+};
+
+const CallChatDrawerHeader = () => {
+  const { setIsChatShown } = useContext(CallContext);
+  const { conversation } = useContext(ConversationContext);
+
+  // TODO: Improve this to support multiple members
+  const contact = conversation.getFirstMember().contact;
+
+  return (
+    <Stack direction="row" padding={2} spacing={2} alignItems="center">
+      <CloseButton
+        onClick={() => {
+          setIsChatShown(false);
+        }}
+      />
+      <Stack direction="column">
+        <Typography variant="h3" textOverflow="ellipsis">
+          {contact.getDisplayName()}
+        </Typography>
+      </Stack>
+    </Stack>
+  );
+};
diff --git a/client/src/components/ConversationView.tsx b/client/src/components/ConversationView.tsx
index 52215e8..064efcf 100644
--- a/client/src/components/ConversationView.tsx
+++ b/client/src/components/ConversationView.tsx
@@ -16,7 +16,7 @@
  * <https://www.gnu.org/licenses/>.
  */
 import { Divider, Stack, Typography } from '@mui/material';
-import { Account, ConversationMember } from 'jami-web-common';
+import { ConversationMember } from 'jami-web-common';
 import { useContext, useMemo } from 'react';
 import { useTranslation } from 'react-i18next';
 
@@ -28,38 +28,28 @@
 import { AddParticipantButton, ShowOptionsMenuButton, StartAudioCallButton, StartVideoCallButton } from './Button';
 
 const ConversationView = () => {
-  const { account } = useAuthContext();
-  const { conversationId, conversation } = useContext(ConversationContext);
-
   return (
     <Stack flexGrow={1} height="100%">
-      <ConversationHeader
-        account={account}
-        members={conversation.getMembers()}
-        adminTitle={conversation.infos.title as string}
-        conversationId={conversationId}
-      />
+      <ConversationHeader />
       <Divider
         sx={{
           borderTop: '1px solid #E5E5E5',
         }}
       />
-      <ChatInterface conversationId={conversationId} members={conversation.getMembers()} />
+      <ChatInterface />
     </Stack>
   );
 };
 
-type ConversationHeaderProps = {
-  account: Account;
-  conversationId: string;
-  members: ConversationMember[];
-  adminTitle: string | undefined;
-};
-
-const ConversationHeader = ({ account, members, adminTitle }: ConversationHeaderProps) => {
+const ConversationHeader = () => {
+  const { account } = useAuthContext();
+  const { conversation } = useContext(ConversationContext);
   const { t } = useTranslation();
   const { conversationId } = useContext(ConversationContext);
 
+  const members = conversation.getMembers();
+  const adminTitle = conversation.infos.title as string;
+
   const title = useMemo(() => {
     if (adminTitle) {
       return adminTitle;
diff --git a/client/src/contexts/CallProvider.tsx b/client/src/contexts/CallProvider.tsx
index de2a6d7..5595000 100644
--- a/client/src/contexts/CallProvider.tsx
+++ b/client/src/contexts/CallProvider.tsx
@@ -21,7 +21,7 @@
 
 import { useUrlParams } from '../hooks/useUrlParams';
 import { CallRouteParams } from '../router';
-import { WithChildren } from '../utils/utils';
+import { SetState, WithChildren } from '../utils/utils';
 import { ConversationContext } from './ConversationProvider';
 import { WebRtcContext } from './WebRtcProvider';
 import { WebSocketContext } from './WebSocketProvider';
@@ -45,6 +45,8 @@
   setAudioStatus: (isOn: boolean) => void;
   isVideoOn: boolean;
   setVideoStatus: (isOn: boolean) => void;
+  isChatShown: boolean;
+  setIsChatShown: SetState<boolean>;
   callRole: CallRole;
   callStatus: CallStatus;
 
@@ -65,6 +67,8 @@
   setAudioStatus: () => {},
   isVideoOn: false,
   setVideoStatus: () => {},
+  isChatShown: false,
+  setIsChatShown: () => {},
   callRole: 'caller',
   callStatus: CallStatus.Default,
 
@@ -89,6 +93,7 @@
 
   const [isAudioOn, setIsAudioOn] = useState(false);
   const [isVideoOn, setIsVideoOn] = useState(false);
+  const [isChatShown, setIsChatShown] = useState(false);
   const [callStatus, setCallStatus] = useState(routeState?.callStatus);
 
   // TODO: This logic will have to change to support multiple people in a call. Could we move this logic to the server?
@@ -256,6 +261,8 @@
         setAudioStatus,
         isVideoOn,
         setVideoStatus,
+        isChatShown,
+        setIsChatShown,
         callRole,
         callStatus,
         acceptCall,
diff --git a/client/src/pages/CallInterface.tsx b/client/src/pages/CallInterface.tsx
index b6266ac..29dd4e0 100644
--- a/client/src/pages/CallInterface.tsx
+++ b/client/src/pages/CallInterface.tsx
@@ -44,11 +44,12 @@
   CallingVideoCameraButton,
   CallingVolumeButton,
 } from '../components/CallButtons';
+import CallChatDrawer from '../components/CallChatDrawer';
 import { CallContext, CallStatus } from '../contexts/CallProvider';
 import { CallPending } from './CallPending';
 
 export default () => {
-  const { callRole, callStatus } = useContext(CallContext);
+  const { callRole, callStatus, isChatShown } = useContext(CallContext);
 
   if (callStatus !== CallStatus.InCall) {
     return (
@@ -60,7 +61,12 @@
     );
   }
 
-  return <CallInterface />;
+  return (
+    <Box flexGrow={1} display="flex">
+      <CallInterface />
+      {isChatShown && <CallChatDrawer />}
+    </Box>
+  );
 };
 
 interface Props {
@@ -86,7 +92,7 @@
   }, [remoteStream]);
 
   return (
-    <>
+    <Box display="flex" flexGrow={1}>
       <video
         ref={remoteVideoRef}
         autoPlay
@@ -123,7 +129,7 @@
           </Grid>
         </Grid>
       </Box>
-    </>
+    </Box>
   );
 };
 
diff --git a/client/src/pages/ChatInterface.tsx b/client/src/pages/ChatInterface.tsx
index f30c15a..5bcd8a2 100644
--- a/client/src/pages/ChatInterface.tsx
+++ b/client/src/pages/ChatInterface.tsx
@@ -16,7 +16,7 @@
  * <https://www.gnu.org/licenses/>.
  */
 import { Box, Divider, Stack } from '@mui/material';
-import { ConversationMember, ConversationMessage, Message, WebSocketMessageType } from 'jami-web-common';
+import { ConversationMessage, Message, WebSocketMessageType } from 'jami-web-common';
 import { useCallback, useContext, useEffect, useState } from 'react';
 import { useDropzone } from 'react-dropzone';
 
@@ -24,16 +24,14 @@
 import LoadingPage from '../components/Loading';
 import MessageList from '../components/MessageList';
 import SendMessageForm from '../components/SendMessageForm';
+import { ConversationContext } from '../contexts/ConversationProvider';
 import { WebSocketContext } from '../contexts/WebSocketProvider';
 import { useMessagesQuery, useSendMessageMutation } from '../services/Conversation';
 import { FileHandler } from '../utils/files';
 
-type ChatInterfaceProps = {
-  conversationId: string;
-  members: ConversationMember[];
-};
-const ChatInterface = ({ conversationId, members }: ChatInterfaceProps) => {
+const ChatInterface = () => {
   const webSocket = useContext(WebSocketContext);
+  const { conversationId, conversation } = useContext(ConversationContext);
   const [messages, setMessages] = useState<Message[]>([]);
   const [isLoading, setIsLoading] = useState(true);
   const [error, setError] = useState(false);
@@ -107,6 +105,8 @@
     return <div>Error loading {conversationId}</div>;
   }
 
+  const members = conversation.getMembers();
+
   return (
     <Stack flex={1} overflow="hidden" {...getRootProps()} paddingBottom="16px">
       {isDragActive && (