set styles for messages

Change-Id: I8b86255a55a745a305f953f77122a98970de0958
diff --git a/client/src/components/MessageList.js b/client/src/components/MessageList.js
index 3ae1b1c..3f16e46 100644
--- a/client/src/components/MessageList.js
+++ b/client/src/components/MessageList.js
@@ -1,33 +1,157 @@
-import Message from './Message'
+import dayjs from "dayjs"
 import React, { useEffect } from 'react'
-import { Box, Divider, Typography } from '@mui/material'
-import ConversationAvatar from './ConversationAvatar'
+import dayOfYear from 'dayjs/plugin/dayOfYear'
+import isBetween from 'dayjs/plugin/isBetween'
+import { Stack } from "@mui/system"
+import { MessageCall, MessageDate, MessageInitial, MessageMember, MessageBubblesGroup, MessageTime, MessageMerge } from "./Message"
+
+dayjs.extend(dayOfYear)
+dayjs.extend(isBetween)
 
 export default function MessageList(props) {
-  const displayName = props.conversation.getDisplayName()
-  const messages = props.conversation.getMessages()
+  const messagesComponents = buildMessagesList(props.messages)
 
   useEffect(() => {
     if (!props.loading)
       props.loadMore()
-  }, [props.conversation.getId()])
+  }, [props.conversationId])
 
   return (
-    <React.Fragment>
-      <Box className="conversation-header">
-        <Box style={{ margin: 16, flexShrink: 0 }}>
-          <ConversationAvatar displayName={props.conversation.getDisplayNameNoFallback()} />
-        </Box>
-        <Box style={{ flex: "1 1 auto", overflow: 'hidden' }}>
-          <Typography className="title" variant="h6">{displayName}</Typography>
-          <Typography className="subtitle" variant="subtitle1" >{props.conversation.getId()}</Typography>
-        </Box>
-        <Divider orientation="horizontal" />
-      </Box>
-      <div className="message-list">
-        {messages.map((message) => <Message key={message.id} message={message} />)}
-        <div style={{ border: "1px solid transparent" }}/>
-      </div>
-    </React.Fragment>
+    <Stack
+      marginLeft="16px"
+      marginRight="16px"
+      direction="column-reverse"
+    >
+      {messagesComponents?.map(
+        ({Component, id, props}) => <Component key={id} {...props}/>
+      )}
+    </Stack>
   )
-}
\ No newline at end of file
+}
+
+const buildMessagesList = (messages) => {
+  if (messages.length == 0) {
+    return null;
+  }
+
+  const components = []
+  let lastTime = dayjs.unix(messages[0].timestamp)
+  let lastAuthor = messages[0].author
+  let messageBubblesGroup = []
+
+  const pushMessageBubblesGroup = () => {
+    if (messageBubblesGroup.length == 0) {
+      return
+    }
+    components.push({
+      id: `group-${messageBubblesGroup[0].id}`,
+      Component: MessageBubblesGroup,
+      props: { messages: messageBubblesGroup },
+    })
+    messageBubblesGroup = []
+  }
+
+  const pushMessageCall = (message) => {
+    components.push({
+      id: `call-${message.id}`,
+      Component: MessageCall,
+      props: { message },
+    })
+  }
+
+  const pushMessageMember = (message) => {
+    components.push({
+      id: `member-${message.id}`,
+      Component: MessageMember,
+      props: { message },
+    })
+  }
+
+  const pushMessageMerge = (message) => {
+    components.push({
+      id: `merge-${message.id}`,
+      Component: MessageMerge,
+      props: { message },
+    })
+  }
+
+  const pushMessageTime = (message, time, hasDateOnTop=false) => {
+    components.push({
+      id: `time-${message.id}`,
+      Component: MessageTime,
+      props: { time, hasDateOnTop },
+    })
+  }
+
+  const pushMessageDate = (message, time) => {
+    components.push({
+      id: `date-${message.id}`,
+      Component: MessageDate,
+      props: { time }
+    })
+  }
+
+  const pushMessageInitial = (message) => {
+    components.push({
+      id: `initial-${message.id}`,
+      Component: MessageInitial,
+      props: { message }
+    })
+  }
+
+  messages.forEach(message => { // most recent messages first
+    switch (message.type) {
+      case "text/plain":
+      case "application/data-transfer+json":
+        if (lastAuthor != message.author) {
+          pushMessageBubblesGroup()
+        }
+        messageBubblesGroup.push(message)
+        break
+      case "application/call-history+json":
+        pushMessageBubblesGroup()
+        pushMessageCall(message)
+        break
+      case "member":
+        pushMessageBubblesGroup()
+        pushMessageMember(message)
+        break
+      case "merge":
+        pushMessageBubblesGroup()
+        pushMessageMerge(message)
+        break
+      case "initial":
+      default:
+        break
+    }
+
+    const time = dayjs.unix(message.timestamp)
+    if (message.type == "initial") {
+      pushMessageBubblesGroup()
+      pushMessageTime(message, time, true)
+      pushMessageDate(message, time)
+      pushMessageInitial(message)
+    }
+    else {
+      if ( // If the date is different
+        lastTime?.year() != time.year()
+        || lastTime?.dayOfYear() != time.dayOfYear()
+      ) { 
+        pushMessageBubblesGroup()
+        pushMessageTime(message, time, true)
+        pushMessageDate(message, time)
+      }
+      else if ( // If more than 5 minutes have passed since the last message
+        !lastTime.isBetween(time, time?.add(5, "minute"))
+      ) {
+        pushMessageBubblesGroup()
+        pushMessageTime(message, time)
+      }
+
+      lastTime = time
+      lastAuthor = message.author
+    }
+  })
+
+  return components
+}