blob: 7f10e416d238ad18ec5d51240c4abb5ec94a29d9 [file] [log] [blame]
simon26e79f72022-10-05 22:16: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 */
simon9f814a32022-11-22 21:40:53 -050018import { Box, Stack } from '@mui/material';
Misha Krieger-Raynauld20cf1c82022-11-23 20:26:50 -050019import { Contact, Conversation, ConversationMessage, WebSocketMessageType } from 'jami-web-common';
Michelle Sepkap Simedd82cbf2022-11-17 23:31:49 -050020import { useContext, useEffect, useState } from 'react';
simonf929a362022-11-18 16:53:45 -050021import { Outlet } from 'react-router-dom';
Adrien Béraud35e7d7c2021-04-13 03:28:39 -040022
Adrien Béraud6ecaa402021-04-06 17:37:25 -040023//import Sound from 'react-sound';
Adrien Béraud995e8022021-04-08 13:46:51 -040024import ConversationList from '../components/ConversationList';
simon07b4eb02022-09-29 17:50:26 -040025import Header from '../components/Header';
simon6b9ddfb2022-10-03 00:04:50 -040026import LoadingPage from '../components/Loading';
simon07b4eb02022-09-29 17:50:26 -040027import NewContactForm from '../components/NewContactForm';
simon5da8ca62022-11-09 15:21:25 -050028import { useAuthContext } from '../contexts/AuthProvider';
Michelle Sepkap Simedd82cbf2022-11-17 23:31:49 -050029import { WebSocketContext } from '../contexts/WebSocketProvider';
simonf929a362022-11-18 16:53:45 -050030import { useUrlParams } from '../hooks/useUrlParams';
Michelle Sepkap Simedd82cbf2022-11-17 23:31:49 -050031import { setRefreshFromSlice } from '../redux/appSlice';
32import { useAppDispatch, useAppSelector } from '../redux/hooks';
simonf929a362022-11-18 16:53:45 -050033import { AddContactRouteParams } from '../router';
simonfe1de722022-10-02 00:21:43 -040034import AddContactPage from './AddContactPage';
ervinanoh34eb9472022-09-13 04:20:28 -040035
simon5da8ca62022-11-09 15:21:25 -050036const Messenger = () => {
Michelle Sepkap Simee580f422022-10-31 23:27:04 -040037 const { refresh } = useAppSelector((state) => state.userInfo);
Michelle Sepkap Simedd82cbf2022-11-17 23:31:49 -050038 const dispatch = useAppDispatch();
simon94fe53e2022-11-10 12:51:58 -050039 const { account, axiosInstance } = useAuthContext();
Michelle Sepkap Simedd82cbf2022-11-17 23:31:49 -050040 const webSocket = useContext(WebSocketContext);
ervinanoh34eb9472022-09-13 04:20:28 -040041
simonfe1de722022-10-02 00:21:43 -040042 const [conversations, setConversations] = useState<Conversation[] | undefined>(undefined);
simond47ef9e2022-09-28 22:24:28 -040043 const [searchQuery, setSearchQuery] = useState('');
simonfe1de722022-10-02 00:21:43 -040044 const [searchResult, setSearchResults] = useState<Conversation | undefined>(undefined);
Larbi Gharibe9af9732021-03-31 15:08:01 +010045
simonff1cb352022-11-24 15:15:26 -050046 const { urlParams } = useUrlParams<AddContactRouteParams>();
47
48 // TODO: Rework the contact adding logic so that adding a contact does not make the current conversationId undefined.
49 // The newContactId should not come from the route, but from a state.
50 const newContactId = urlParams?.contactId;
Adrien Béraud35e7d7c2021-04-13 03:28:39 -040051
simon5da8ca62022-11-09 15:21:25 -050052 const accountId = account.getId();
simonfe1de722022-10-02 00:21:43 -040053
Adrien Béraud4e287b92021-04-24 16:15:56 -040054 useEffect(() => {
simond47ef9e2022-09-28 22:24:28 -040055 const controller = new AbortController();
simon94fe53e2022-11-10 12:51:58 -050056 axiosInstance
57 .get<Conversation[]>('/conversations', {
58 signal: controller.signal,
59 })
60 .then(({ data }) => {
61 setConversations(Object.values(data).map((c) => Conversation.from(accountId, c)));
simond47ef9e2022-09-28 22:24:28 -040062 });
ervinanoh34eb9472022-09-13 04:20:28 -040063 // return () => controller.abort()
simon94fe53e2022-11-10 12:51:58 -050064 }, [axiosInstance, accountId, refresh]);
Adrien Béraud995e8022021-04-08 13:46:51 -040065
Adrien Béraudabba2e52021-04-24 21:39:56 -040066 useEffect(() => {
Michelle Sepkap Simedd82cbf2022-11-17 23:31:49 -050067 if (!webSocket) {
68 return;
69 }
70
Misha Krieger-Raynauld20cf1c82022-11-23 20:26:50 -050071 const conversationMessageListener = (_data: ConversationMessage) => {
72 dispatch(setRefreshFromSlice());
73 };
74
Michelle Sepkap Simedd82cbf2022-11-17 23:31:49 -050075 webSocket.bind(WebSocketMessageType.ConversationMessage, conversationMessageListener);
Misha Krieger-Raynauld20cf1c82022-11-23 20:26:50 -050076
77 return () => {
78 webSocket.unbind(WebSocketMessageType.ConversationMessage, conversationMessageListener);
79 };
Michelle Sepkap Simedd82cbf2022-11-17 23:31:49 -050080 }, [webSocket, dispatch]);
81
82 useEffect(() => {
simond47ef9e2022-09-28 22:24:28 -040083 if (!searchQuery) return;
84 const controller = new AbortController();
simon94fe53e2022-11-10 12:51:58 -050085 // TODO: Type properly https://git.jami.net/savoirfairelinux/jami-web/-/issues/92
86 axiosInstance
87 .get<{ state: number; address: string; username: string }>(`/ns/username/${searchQuery}`, {
88 signal: controller.signal,
simond47ef9e2022-09-28 22:24:28 -040089 })
simon94fe53e2022-11-10 12:51:58 -050090 .then(({ data }) => {
91 const contact = new Contact(data.address);
92 contact.setRegisteredName(data.username);
simond47ef9e2022-09-28 22:24:28 -040093 setSearchResults(contact ? Conversation.fromSingleContact(accountId, contact) : undefined);
94 })
simon416d0792022-11-03 02:46:18 -040095 .catch(() => {
simond47ef9e2022-09-28 22:24:28 -040096 setSearchResults(undefined);
97 });
98 // return () => controller.abort() // crash on React18
simon94fe53e2022-11-10 12:51:58 -050099 }, [accountId, searchQuery, axiosInstance]);
Adrien Béraud35e7d7c2021-04-13 03:28:39 -0400100
Adrien Béraud4e287b92021-04-24 16:15:56 -0400101 return (
simon9f814a32022-11-22 21:40:53 -0500102 <Box display="flex" height="100%">
simond47ef9e2022-09-28 22:24:28 -0400103 <Stack flexGrow={0} flexShrink={0} overflow="auto">
idillonbef18a52022-09-01 01:51:40 -0400104 <Header />
105 <NewContactForm onChange={setSearchQuery} />
simonff1cb352022-11-24 15:15:26 -0500106 {newContactId && <AddContactPage contactId={newContactId} />}
simond47ef9e2022-09-28 22:24:28 -0400107 {conversations ? (
108 <ConversationList search={searchResult} conversations={conversations} accountId={accountId} />
109 ) : (
110 <div className="rooms-list">
111 <LoadingPage />
112 </div>
113 )}
idillonbef18a52022-09-01 01:51:40 -0400114 </Stack>
simon9f814a32022-11-22 21:40:53 -0500115 <Box flexGrow={1} display="flex" position="relative">
simonf929a362022-11-18 16:53:45 -0500116 <Outlet />
simon9f814a32022-11-22 21:40:53 -0500117 </Box>
118 </Box>
simond47ef9e2022-09-28 22:24:28 -0400119 );
120};
Larbi Gharibe9af9732021-03-31 15:08:01 +0100121
simond47ef9e2022-09-28 22:24:28 -0400122export default Messenger;