blob: 86f77da5f7b6e29fb6c1ac93f790ce64517aa7d0 [file] [log] [blame]
idillon6847e252022-11-04 11:50:08 -04001/*
2 * Copyright (C) 2022 Savoir-faire Linux Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License as
6 * published by the Free Software Foundation; either version 3 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Affero General Public License for more details.
13 *
14 * You should have received a copy of the GNU Affero General Public
15 * License along with this program. If not, see
16 * <https://www.gnu.org/licenses/>.
17 */
18import { Divider, Stack } from '@mui/material';
19import { Account, ConversationMember, Message } from 'jami-web-common';
20import { useCallback, useContext, useEffect, useState } from 'react';
21
22import LoadingPage from '../components/Loading';
23import MessageList from '../components/MessageList';
24import SendMessageForm from '../components/SendMessageForm';
25import { SocketContext } from '../contexts/Socket';
26import { useMessagesQuery, useSendMessageMutation } from '../services/Conversation';
27
28type ChatInterfaceProps = {
29 account: Account;
30 conversationId: string;
31 members: ConversationMember[];
32};
33const ChatInterface = ({ account, conversationId, members }: ChatInterfaceProps) => {
34 const socket = useContext(SocketContext);
35 const [messages, setMessages] = useState<Message[]>([]);
36 const [isLoading, setIsLoading] = useState(true);
37 const [error, setError] = useState(false);
38
39 const messagesQuery = useMessagesQuery(account.getId(), conversationId);
40 const sendMessageMutation = useSendMessageMutation(account.getId(), conversationId);
41
42 useEffect(() => {
43 if (messagesQuery.isSuccess) {
44 const sortedMessages = sortMessages(messagesQuery.data);
45 setMessages(sortedMessages);
46 }
47 }, [messagesQuery.isSuccess, messagesQuery.data]);
48
49 useEffect(() => {
50 setIsLoading(messagesQuery.isLoading);
51 }, [messagesQuery.isLoading]);
52
53 useEffect(() => {
54 setError(messagesQuery.isError);
55 }, [messagesQuery.isError]);
56
57 const sendMessage = useCallback((message: string) => sendMessageMutation.mutate(message), [sendMessageMutation]);
58
59 useEffect(() => {
60 if (socket) {
61 socket.off('newMessage');
62 socket.on('newMessage', (data) => {
63 console.log('newMessage');
64 setMessages((messages) => addMessage(messages, data));
65 });
66 }
67 }, [conversationId, socket]);
68
69 if (isLoading) {
70 return <LoadingPage />;
71 } else if (error) {
72 return <div>Error loading {conversationId}</div>;
73 }
74
75 return (
76 <Stack flex={1} overflow="hidden">
77 <MessageList account={account} members={members} messages={messages} />
78 <Divider
79 sx={{
80 margin: '30px 16px 0px 16px',
81 borderTop: '1px solid #E5E5E5',
82 }}
83 />
84 <SendMessageForm account={account} members={members} onSend={sendMessage} />
85 </Stack>
86 );
87};
88
89const addMessage = (sortedMessages: Message[], message: Message) => {
90 if (sortedMessages.length === 0) {
91 return [message];
92 } else if (message.id === sortedMessages[sortedMessages.length - 1].linearizedParent) {
93 return [...sortedMessages, message];
94 } else if (message.linearizedParent === sortedMessages[0].id) {
95 return [message, ...sortedMessages];
96 } else {
97 console.error("Can't insert message " + message.id);
98 return sortedMessages;
99 }
100};
101
102const sortMessages = (messages: Message[]) => {
103 let sortedMessages: Message[] = [];
104 messages.forEach((message) => (sortedMessages = addMessage(sortedMessages, message)));
105 return sortedMessages;
106};
107
108export default ChatInterface;