add messaging

Change-Id: I9f7b5c73e25774751a2c55ea6c2575feca5ebfd5
diff --git a/client/src/components/ConversationView.js b/client/src/components/ConversationView.js
index 6217905..5c5a936 100644
--- a/client/src/components/ConversationView.js
+++ b/client/src/components/ConversationView.js
@@ -1,12 +1,14 @@
-import CircularProgress from '@material-ui/core/CircularProgress';
 import React, { useEffect, useState } from 'react';
 import MessageList from './MessageList';
 import SendMessageForm from './SendMessageForm';
 import authManager from '../AuthManager'
 import Conversation from '../../../model/Conversation';
 import LoadingPage from './loading';
+import io from "socket.io-client";
 
 const ConversationView = props => {
+  const [loadingMesages, setLoadingMesages] = useState(false)
+  const [socket, setSocket] = useState(undefined)
   const [state, setState] = useState({
     loaded: false,
     error: false,
@@ -21,7 +23,7 @@
       console.log(result)
       setState({
         loaded: true,
-        conversation: Conversation.from(props.accountId, result)// result.map(account => Account.from(account)),
+        conversation: Conversation.from(props.accountId, result)
       })
     }, error => {
       console.log(`get error ${error}`)
@@ -33,6 +35,52 @@
     return () => controller.abort()
   }, [props.accountId, props.conversationId])
 
+  useEffect(() => {
+    console.log("io.connect")
+    const socket = io()
+    setSocket(socket)
+    return () => {
+      console.log("io.disconnect")
+      socket.disconnect()
+      setSocket(undefined)
+    }
+  }, [])
+
+  useEffect(() => {
+    if (!state.conversation)
+      return
+    console.log(`io set conversation ${state.conversation.getId()} `+ socket)
+    if (socket)
+      socket.emit('conversation', { accountId: state.conversation.getAccountId(), conversationId: state.conversation.getId() })
+    socket.off('newMessage')
+    socket.on('newMessage', (data) => {
+      console.log("newMessage")
+      console.log(data)
+      setState(state => {
+        if (state.conversation)
+          state.conversation.addMessage(data)
+          return {...state}
+      })
+    })
+  }, [state.conversation ? state.conversation.getId() : "", socket])
+
+  useEffect(() => {
+    if (!loadingMesages || !state.conversation)
+      return
+    console.log(`Load more messages`)
+    const controller = new AbortController()
+    authManager.fetch(`/api/accounts/${state.conversation.getAccountId()}/conversations/${state.conversation.getId()}/messages`, {signal: controller.signal})
+      .then(res => res.json())
+      .then(messages => {
+        console.log(messages)
+        setLoadingMesages(false)
+        if (state.conversation)
+            state.conversation.addLoadedMessages(messages)
+        setState(state)
+      }).catch(e => console.log(e))
+      return () => controller.abort()
+  }, [state, loadingMesages])
+
   const sendMessage = (message) => {
     authManager.fetch(`/api/accounts/${props.accountId}/conversations/${props.conversationId}`, {
       headers: {
@@ -44,24 +92,13 @@
     })
   }
 
-  const loadMore = () => {
-    authManager.fetch(`/api/accounts/${props.accountId}/conversations/${props.conversationId}/messages`)
-      .then(res => res.json())
-      .then(messages => {
-        console.log(messages)
-        state.conversation.addLoadedMessages(messages)
-        setState(state)
-      })
-  }
-
-  console.log("ConversationView render " + (state.conversation ? state.conversation.getMessages().length : "no conversation"))
   if (state.loaded === false) {
       return <LoadingPage />
   } else if (state.error === true) {
       return <div>Error loding {props.conversationId}</div>
   } else {
   return <div className="messenger">
-      <MessageList conversation={state.conversation} loadMore={loadMore} messages={state.conversation.getMessages()} />
+      <MessageList conversation={state.conversation} loading={loadingMesages} loadMore={() => setLoadingMesages(true)} messages={state.conversation.getMessages()} />
       <SendMessageForm onSend={sendMessage} />
     </div>
   }
diff --git a/client/src/components/Message.js b/client/src/components/Message.js
index 9054cd7..f4b4f57 100644
--- a/client/src/components/Message.js
+++ b/client/src/components/Message.js
@@ -1,16 +1,27 @@
 import { Typography } from '@material-ui/core'
+import { GroupOutlined } from '@material-ui/icons'
 import React from 'react'
+import ConversationAvatar from './ConversationAvatar'
 
 function Message(props) {
-    console.log("Message render")
-    console.log(props.message)
-
-    return (
-        <div className="message">
-            <div className="message-username">{props.message.author}</div>
-            <Typography className="message-text">{props.message.body}</Typography>
-        </div>
-    )
+    const message = props.message
+    if (message.type == 'text/plain')
+        return (<div className="message">
+            <div className="message-avatar">
+                    <ConversationAvatar name="{message.author}" /></div>
+                    <Typography className="message-text">{message.body}</Typography>
+                </div>)
+    else if (message.type == 'contact')
+        return (<div className="contact-event">
+            <Typography className="message-text">Contact event</Typography>
+        </div>)
+    else if (message.type == 'initial')
+        return (<div className="conversation-event">
+            <Typography variant="h6" className="message-text" color="textSecondary">
+                <div className="inline-avatar"><GroupOutlined color="action" style={{ fontSize: 32 }} /></div>Conversation created
+                </Typography>
+        </div>)
+    else return ''
 }
 
 export default Message
\ No newline at end of file
diff --git a/client/src/components/MessageList.js b/client/src/components/MessageList.js
index b2fca5e..e73af6a 100644
--- a/client/src/components/MessageList.js
+++ b/client/src/components/MessageList.js
@@ -2,15 +2,14 @@
 import React, { useEffect } from 'react'
 import { Box, Divider, Typography } from '@material-ui/core'
 import ConversationAvatar from './ConversationAvatar'
-const reverseMap = (arr, f) => arr.map((_, idx, arr) => f(arr[arr.length - 1 - idx ]));
 
 export default function MessageList(props) {
   const displayName = props.conversation.getDisplayName()
   const messages = props.conversation.getMessages()
-  console.log("MessageList render " + messages.length)
 
   useEffect(() => {
-    props.loadMore()
+    if (!props.loading)
+      props.loadMore()
   }, [props.conversation.getId()])
 
   return (
@@ -26,9 +25,8 @@
         <Divider orientation="horizontal" />
       </Box>
       <div className="message-list">
-      <div className="message-list-inner">
-      {reverseMap(messages, (message) => <Message key={message.id} message={message} />)}
-      </div>
+        {messages.map((message) => <Message key={message.id} message={message} />)}
+        <div style={{ border: "1px solid transparent" }}/>
       </div>
     </React.Fragment>
   )