use React Query to fetch messages

Change-Id: I85d0700efcc947462943ed0379498902b73950b3
diff --git a/client/src/components/ConversationView.js b/client/src/components/ConversationView.js
index dfba0d1..964d5bf 100644
--- a/client/src/components/ConversationView.js
+++ b/client/src/components/ConversationView.js
@@ -7,35 +7,40 @@
 import io from "socket.io-client";
 import { Box, Stack, Typography } from '@mui/material';
 import ConversationAvatar from './ConversationAvatar';
+import { useConversationQuery, useMessagesQuery } from '../services/conversation';
 
 const ConversationView = props => {
   const [loadingMessages, setLoadingMessages] = useState(false)
-  const [socket, setSocket] = useState(undefined)
-  const [state, setState] = useState({
-    loaded: false,
-    error: false,
-    conversation: undefined
-  })
+  const [socket, setSocket] = useState()
+  const [conversation, setConversation] = useState()
+  const [messages, setMessages] = useState([])
+  const [loaded, setLoaded] = useState(true)
+  const [error, setError] = useState(false)
+
+  const conversationQuery = useConversationQuery(props.accountId, props.conversationId)
+  const messagesQuery = useMessagesQuery(props.accountId, props.conversationId)
 
   useEffect(() => {
-    const controller = new AbortController()
-    authManager.fetch(`/api/accounts/${props.accountId}/conversations/${props.conversationId}`, {signal: controller.signal})
-    .then(res => res.json())
-    .then(result => {
-      console.log(result)
-      setState({
-        loaded: true,
-        conversation: Conversation.from(props.accountId, result)
-      })
-    }, error => {
-      console.log(`get error ${error}`)
-      setState({
-        loaded: true,
-        error: true
-      })
-    })
-   // return () => controller.abort() // crash on React18
-  }, [props.accountId, props.conversationId])
+    if (conversationQuery.data) {
+      const conversation = Conversation.from(props.accountId, conversationQuery.data)
+      setConversation(conversation)
+    }
+  }, [conversationQuery.data])
+
+  useEffect(() => {
+    if (messagesQuery.data) {
+      const sortedMessages = sortMessages(messagesQuery.data)
+      setMessages(sortedMessages)
+    }
+  }, [messagesQuery.data])
+
+  useEffect(() => {
+    setLoaded(!(conversationQuery.isLoading || messagesQuery.isLoading))
+  }, [conversationQuery.isLoading, messagesQuery.isLoading])
+  
+  useEffect(() => {
+    setError(conversationQuery.isError || messagesQuery.isError)
+  }, [conversationQuery.isError, messagesQuery.isError])
 
   useEffect(() => {
     console.log("io.connect")
@@ -49,41 +54,18 @@
   }, [])
 
   useEffect(() => {
-    if (!state.conversation)
+    if (!conversation)
       return
-    console.log(`io set conversation ${state.conversation.getId()} `+ socket)
+    console.log(`io set conversation ${props.conversationId} `+ socket)
     if (socket)
-      socket.emit('conversation', { accountId: state.conversation.getAccountId(), conversationId: state.conversation.getId() })
+      socket.emit('conversation', { accountId: props.accountId, conversationId: props.conversationId })
     socket.off('newMessage')
     socket.on('newMessage', (data) => {
       console.log("newMessage")
       console.log(data)
-      setState(state => {
-        if (state.conversation)
-          state.conversation.addMessage(data)
-        return {...state}
-      })
+      setMessages(addMessage(messages, data))
     })
-  }, [state.conversation ? state.conversation.getId() : "", socket])
-
-  useEffect(() => {
-    if (!loadingMessages || !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)
-        setLoadingMessages(false)
-        setState(state => {
-          if (state.conversation)
-            state.conversation.addLoadedMessages(messages)
-            return {...state}
-          })
-      }).catch(e => console.log(e))
-     // return () => controller.abort() // crash on React18
-  }, [state, loadingMessages])
+  }, [conversation ? props.conversationId : "", socket])
 
   const sendMessage = (message) => {
     authManager.fetch(`/api/accounts/${props.accountId}/conversations/${props.conversationId}`, {
@@ -96,10 +78,10 @@
     })
   }
 
-  if (state.loaded === false) {
+  if (!loaded) {
       return <LoadingPage />
-  } else if (state.error === true) {
-      return <div>Error loding {props.conversationId}</div>
+  } else if (error) {
+      return <div>Error loading {props.conversationId}</div>
   }
 
   return (
@@ -109,19 +91,19 @@
     >
       <Stack direction="row" flexGrow={0}>
         <Box style={{ margin: 16, flexShrink: 0 }}>
-          <ConversationAvatar displayName={state.conversation.getDisplayNameNoFallback()} />
+          <ConversationAvatar displayName={conversation?.getDisplayNameNoFallback()} />
         </Box>
         <Box style={{ flex: "1 1 auto", overflow: 'hidden' }}>
-          <Typography className="title" variant="h6">{state.conversation.getDisplayName()}</Typography>
-          <Typography className="subtitle" variant="subtitle1" >{state.conversation.getId()}</Typography>
+          <Typography className="title" variant="h6">{conversation?.getDisplayName()}</Typography>
+          <Typography className="subtitle" variant="subtitle1" >{props.conversationId}</Typography>
         </Box>
       </Stack>
       <Stack flexGrow={1} overflow="auto" direction="column-reverse">
         <MessageList
-          conversationId={state.conversation.getId()}
+          conversationId={props.conversationId}
           loading={loadingMessages} 
           loadMore={() => setLoadingMessages(true)}
-          messages={state.conversation.getMessages()}
+          messages={messages}
         />
       </Stack>
       <Stack flexGrow={0}>
@@ -131,4 +113,22 @@
   )
 }
 
+const addMessage = (sortedMessages, message) => {
+  if (sortedMessages.length === 0) {
+    return [message]
+  } else if (message.id === sortedMessages[sortedMessages.length - 1].linearizedParent) {
+    return [...sortedMessages, message]
+  } else if (message.linearizedParent === sortedMessages[0].id) {
+    return [message, ...sortedMessages]
+  } else {
+    console.log("Can't insert message " + message.id)
+  }
+}
+
+const sortMessages = (messages) => {
+  let sortedMessages = []
+  messages.forEach(message => sortedMessages = addMessage(sortedMessages, message))
+  return sortedMessages
+}
+
 export default ConversationView
\ No newline at end of file