add names to conversation view and set header styles

Change-Id: Ic34b2cea754a5a82224a9fbf158b0126c7e44a5e
diff --git a/client/src/components/ConversationView.tsx b/client/src/components/ConversationView.tsx
index 0a8e22d..a6c0d35 100644
--- a/client/src/components/ConversationView.tsx
+++ b/client/src/components/ConversationView.tsx
@@ -15,13 +15,16 @@
  * License along with this program.  If not, see
  * <https://www.gnu.org/licenses/>.
  */
-import { Box, Stack, Typography } from '@mui/material';
-import { Conversation, Message } from 'jami-web-common';
-import { useCallback, useContext, useEffect, useState } from 'react';
+import { Divider, Stack, Typography } from '@mui/material';
+import { Account, Conversation, ConversationMember, Message } from 'jami-web-common';
+import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
+import { useTranslation } from 'react-i18next';
 
 import { SocketContext } from '../contexts/Socket';
+import { useAccountQuery } from '../services/Account';
 import { useConversationQuery, useMessagesQuery, useSendMessageMutation } from '../services/Conversation';
-import ConversationAvatar from './ConversationAvatar';
+import { translateEnumeration, TranslateEnumerationOptions } from '../utils/translations';
+import { AddParticipantButton, ShowOptionsMenuButton, StartAudioCallButton, StartVideoCallButton } from './Button';
 import LoadingPage from './Loading';
 import MessageList from './MessageList';
 import SendMessageForm from './SendMessageForm';
@@ -30,18 +33,26 @@
   accountId: string;
   conversationId: string;
 };
-const ConversationView = ({ accountId, conversationId, ...props }: ConversationViewProps) => {
+const ConversationView = ({ accountId, conversationId }: ConversationViewProps) => {
   const socket = useContext(SocketContext);
+  const [account, setAccount] = useState<Account | undefined>();
   const [conversation, setConversation] = useState<Conversation | undefined>();
   const [messages, setMessages] = useState<Message[]>([]);
   const [isLoading, setIsLoading] = useState(true);
   const [error, setError] = useState(false);
 
+  const accountQuery = useAccountQuery(accountId);
   const conversationQuery = useConversationQuery(accountId, conversationId);
   const messagesQuery = useMessagesQuery(accountId, conversationId);
   const sendMessageMutation = useSendMessageMutation(accountId, conversationId);
 
   useEffect(() => {
+    if (accountQuery.isSuccess) {
+      setAccount(Account.from(accountQuery.data));
+    }
+  }, [accountQuery.isSuccess, accountQuery.data]);
+
+  useEffect(() => {
     if (conversationQuery.isSuccess) {
       const conversation = Conversation.from(accountId, conversationQuery.data);
       setConversation(conversation);
@@ -56,12 +67,12 @@
   }, [messagesQuery.isSuccess, messagesQuery.data]);
 
   useEffect(() => {
-    setIsLoading(conversationQuery.isLoading || messagesQuery.isLoading);
-  }, [conversationQuery.isLoading, messagesQuery.isLoading]);
+    setIsLoading(accountQuery.isLoading || conversationQuery.isLoading || messagesQuery.isLoading);
+  }, [accountQuery.isLoading, conversationQuery.isLoading, messagesQuery.isLoading]);
 
   useEffect(() => {
-    setError(conversationQuery.isError || messagesQuery.isError);
-  }, [conversationQuery.isError, messagesQuery.isError]);
+    setError(accountQuery.isLoading || conversationQuery.isError || messagesQuery.isError);
+  }, [accountQuery.isLoading, conversationQuery.isError, messagesQuery.isError]);
 
   const sendMessage = useCallback((message: string) => sendMessageMutation.mutate(message), [sendMessageMutation]);
 
@@ -83,35 +94,94 @@
 
   if (isLoading) {
     return <LoadingPage />;
-  } else if (error) {
+  } else if (error || !account || !conversation) {
     return <div>Error loading {conversationId}</div>;
   }
 
   return (
-    <Stack flexGrow={1} height="100%">
-      <Stack direction="row" flexGrow={0}>
-        <Box style={{ margin: 16, flexShrink: 0 }}>
-          <ConversationAvatar displayName={conversation?.getDisplayNameNoFallback()} />
-        </Box>
-        <Box style={{ flex: '1 1 auto', overflow: 'hidden' }}>
-          <Typography className="title" variant="h6">
-            {conversation?.getDisplayName()}
-          </Typography>
-          <Typography className="subtitle" variant="subtitle1">
-            {conversationId}
-          </Typography>
-        </Box>
+    <Stack height="100%">
+      <Stack padding="16px">
+        <ConversationHeader
+          account={account}
+          members={conversation.getMembers()}
+          adminTitle={conversation.infos.title as string}
+        />
       </Stack>
-      <Stack flexGrow={1} overflow="auto" direction="column-reverse">
-        <MessageList messages={messages} />
+      <Divider
+        sx={{
+          borderTop: '1px solid #E5E5E5',
+        }}
+      />
+      <Stack flex={1} overflow="auto" direction="column-reverse" padding="0px 16px">
+        <MessageList account={account} members={conversation.getMembers()} messages={messages} />
       </Stack>
-      <Stack flexGrow={0}>
-        <SendMessageForm onSend={sendMessage} />
+      <Divider
+        sx={{
+          margin: '30px 16px 0px 16px',
+          borderTop: '1px solid #E5E5E5',
+        }}
+      />
+      <Stack padding="16px">
+        <SendMessageForm account={account} members={conversation.getMembers()} onSend={sendMessage} />
       </Stack>
     </Stack>
   );
 };
 
+type ConversationHeaderProps = {
+  account: Account;
+  members: ConversationMember[];
+  adminTitle: string | undefined;
+};
+
+const ConversationHeader = ({ account, members, adminTitle }: ConversationHeaderProps) => {
+  const { t } = useTranslation();
+
+  const title = useMemo(() => {
+    if (adminTitle) {
+      return adminTitle;
+    }
+
+    const options: TranslateEnumerationOptions<ConversationMember> = {
+      elementPartialKey: 'member',
+      getElementValue: (member) => getMemberName(member),
+      translaters: [
+        () =>
+          // The user is chatting with themself
+          t('conversation_title_one', { member0: account?.getDisplayName() }),
+        (interpolations) => t('conversation_title_one', interpolations),
+        (interpolations) => t('conversation_title_two', interpolations),
+        (interpolations) => t('conversation_title_three', interpolations),
+        (interpolations) => t('conversation_title_four', interpolations),
+        (interpolations) => t('conversation_title_more', interpolations),
+      ],
+    };
+
+    return translateEnumeration<ConversationMember>(members, options);
+  }, [account, members, adminTitle, t]);
+
+  return (
+    <Stack direction="row">
+      <Stack flex={1} justifyContent="center" whiteSpace="nowrap" overflow="hidden">
+        <Typography variant="h3" textOverflow="ellipsis">
+          {title}
+        </Typography>
+      </Stack>
+      <Stack direction="row" spacing="20px">
+        <StartAudioCallButton />
+        <StartVideoCallButton />
+        <AddParticipantButton />
+        <ShowOptionsMenuButton />
+      </Stack>
+    </Stack>
+  );
+};
+
+const getMemberName = (member: ConversationMember) => {
+  const contact = member.contact;
+  return contact.getDisplayName();
+};
+
 const addMessage = (sortedMessages: Message[], message: Message) => {
   if (sortedMessages.length === 0) {
     return [message];