blob: fc0ee9bef72cd0414671350dffee8f092ac7483b [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 */
idillonae655dd2022-10-14 18:11:02 -040018import { Divider, Stack, Typography } from '@mui/material';
idillon6847e252022-11-04 11:50:08 -040019import { Account, Conversation, ConversationMember } from 'jami-web-common';
20import { useContext, useEffect, useMemo, useState } from 'react';
idillonae655dd2022-10-14 18:11:02 -040021import { useTranslation } from 'react-i18next';
simon1170c322022-10-31 14:51:31 -040022import { useNavigate } from 'react-router';
simon07b4eb02022-09-29 17:50:26 -040023
simon35378692022-10-02 23:25:57 -040024import { SocketContext } from '../contexts/Socket';
idillon6847e252022-11-04 11:50:08 -040025import ChatInterface from '../pages/ChatInterface';
idillonae655dd2022-10-14 18:11:02 -040026import { useAccountQuery } from '../services/Account';
idillon6847e252022-11-04 11:50:08 -040027import { useConversationQuery } from '../services/Conversation';
idillonae655dd2022-10-14 18:11:02 -040028import { translateEnumeration, TranslateEnumerationOptions } from '../utils/translations';
29import { AddParticipantButton, ShowOptionsMenuButton, StartAudioCallButton, StartVideoCallButton } from './Button';
simon6b9ddfb2022-10-03 00:04:50 -040030import LoadingPage from './Loading';
Adrien Béraud35e7d7c2021-04-13 03:28:39 -040031
simon35378692022-10-02 23:25:57 -040032type ConversationViewProps = {
33 accountId: string;
34 conversationId: string;
35};
idillonae655dd2022-10-14 18:11:02 -040036const ConversationView = ({ accountId, conversationId }: ConversationViewProps) => {
simond47ef9e2022-09-28 22:24:28 -040037 const socket = useContext(SocketContext);
idillonae655dd2022-10-14 18:11:02 -040038 const [account, setAccount] = useState<Account | undefined>();
simon35378692022-10-02 23:25:57 -040039 const [conversation, setConversation] = useState<Conversation | undefined>();
simond47ef9e2022-09-28 22:24:28 -040040 const [isLoading, setIsLoading] = useState(true);
41 const [error, setError] = useState(false);
idillon08f77172022-09-13 19:14:17 -040042
idillonae655dd2022-10-14 18:11:02 -040043 const accountQuery = useAccountQuery(accountId);
simon80b7b3b2022-09-28 17:50:10 -040044 const conversationQuery = useConversationQuery(accountId, conversationId);
Adrien Béraud35e7d7c2021-04-13 03:28:39 -040045
Adrien Béraud5e9e19b2021-04-22 01:38:53 -040046 useEffect(() => {
idillonae655dd2022-10-14 18:11:02 -040047 if (accountQuery.isSuccess) {
48 setAccount(Account.from(accountQuery.data));
49 }
50 }, [accountQuery.isSuccess, accountQuery.data]);
51
52 useEffect(() => {
idillonea465602022-09-13 19:58:51 -040053 if (conversationQuery.isSuccess) {
simon80b7b3b2022-09-28 17:50:10 -040054 const conversation = Conversation.from(accountId, conversationQuery.data);
simond47ef9e2022-09-28 22:24:28 -040055 setConversation(conversation);
idillon08f77172022-09-13 19:14:17 -040056 }
simon80b7b3b2022-09-28 17:50:10 -040057 }, [accountId, conversationQuery.isSuccess, conversationQuery.data]);
idillon08f77172022-09-13 19:14:17 -040058
59 useEffect(() => {
idillon6847e252022-11-04 11:50:08 -040060 setIsLoading(accountQuery.isLoading || conversationQuery.isLoading);
61 }, [accountQuery.isLoading, conversationQuery.isLoading]);
idillon08f77172022-09-13 19:14:17 -040062
63 useEffect(() => {
idillon6847e252022-11-04 11:50:08 -040064 setError(accountQuery.isError || conversationQuery.isError);
65 }, [accountQuery.isError, conversationQuery.isError]);
simond47ef9e2022-09-28 22:24:28 -040066
67 useEffect(() => {
68 if (!conversation) return;
simon80b7b3b2022-09-28 17:50:10 -040069 console.log(`io set conversation ${conversationId} ` + socket);
simon35378692022-10-02 23:25:57 -040070 if (socket) {
71 socket.emit('conversation', {
72 accountId,
73 conversationId,
74 });
simon35378692022-10-02 23:25:57 -040075 }
simon80b7b3b2022-09-28 17:50:10 -040076 }, [accountId, conversation, conversationId, socket]);
Adrien Béraudabba2e52021-04-24 21:39:56 -040077
idillonea465602022-09-13 19:58:51 -040078 if (isLoading) {
simond47ef9e2022-09-28 22:24:28 -040079 return <LoadingPage />;
idillonae655dd2022-10-14 18:11:02 -040080 } else if (error || !account || !conversation) {
simon80b7b3b2022-09-28 17:50:10 -040081 return <div>Error loading {conversationId}</div>;
Adrien Béraud5e9e19b2021-04-22 01:38:53 -040082 }
idillonbef18a52022-09-01 01:51:40 -040083
84 return (
idillonae655dd2022-10-14 18:11:02 -040085 <Stack height="100%">
idillon02f579d2022-11-06 21:26:55 -050086 <ConversationHeader
87 account={account}
88 members={conversation.getMembers()}
89 adminTitle={conversation.infos.title as string}
90 conversationId={conversationId}
91 />
idillonae655dd2022-10-14 18:11:02 -040092 <Divider
93 sx={{
94 borderTop: '1px solid #E5E5E5',
95 }}
96 />
idillon6847e252022-11-04 11:50:08 -040097 <ChatInterface account={account} conversationId={conversationId} members={conversation.getMembers()} />
idillonbef18a52022-09-01 01:51:40 -040098 </Stack>
simond47ef9e2022-09-28 22:24:28 -040099 );
100};
Adrien Béraud35e7d7c2021-04-13 03:28:39 -0400101
idillonae655dd2022-10-14 18:11:02 -0400102type ConversationHeaderProps = {
103 account: Account;
simon1170c322022-10-31 14:51:31 -0400104 conversationId: string;
idillonae655dd2022-10-14 18:11:02 -0400105 members: ConversationMember[];
106 adminTitle: string | undefined;
107};
108
simon1170c322022-10-31 14:51:31 -0400109const ConversationHeader = ({ account, members, adminTitle, conversationId }: ConversationHeaderProps) => {
idillonae655dd2022-10-14 18:11:02 -0400110 const { t } = useTranslation();
simon1170c322022-10-31 14:51:31 -0400111 const navigate = useNavigate();
idillonae655dd2022-10-14 18:11:02 -0400112
113 const title = useMemo(() => {
114 if (adminTitle) {
115 return adminTitle;
116 }
117
118 const options: TranslateEnumerationOptions<ConversationMember> = {
119 elementPartialKey: 'member',
120 getElementValue: (member) => getMemberName(member),
121 translaters: [
122 () =>
123 // The user is chatting with themself
124 t('conversation_title_one', { member0: account?.getDisplayName() }),
125 (interpolations) => t('conversation_title_one', interpolations),
126 (interpolations) => t('conversation_title_two', interpolations),
127 (interpolations) => t('conversation_title_three', interpolations),
128 (interpolations) => t('conversation_title_four', interpolations),
129 (interpolations) => t('conversation_title_more', interpolations),
130 ],
131 };
132
133 return translateEnumeration<ConversationMember>(members, options);
134 }, [account, members, adminTitle, t]);
135
simon1170c322022-10-31 14:51:31 -0400136 const startCall = (withVideo = false) => {
Michelle Sepkap Simeb3dd3122022-11-03 02:12:39 -0400137 let url = `/deprecated-account/${account.getId()}/call/${conversationId}`;
simon1170c322022-10-31 14:51:31 -0400138 if (withVideo) {
139 url += '?video=true';
140 }
141 navigate(url);
142 };
143
idillonae655dd2022-10-14 18:11:02 -0400144 return (
idillon6847e252022-11-04 11:50:08 -0400145 <Stack direction="row" padding="16px" overflow="hidden">
idillonae655dd2022-10-14 18:11:02 -0400146 <Stack flex={1} justifyContent="center" whiteSpace="nowrap" overflow="hidden">
147 <Typography variant="h3" textOverflow="ellipsis">
148 {title}
149 </Typography>
150 </Stack>
151 <Stack direction="row" spacing="20px">
simon1170c322022-10-31 14:51:31 -0400152 <StartAudioCallButton onClick={() => startCall(false)} />
153 <StartVideoCallButton onClick={() => startCall(true)} />
idillonae655dd2022-10-14 18:11:02 -0400154 <AddParticipantButton />
155 <ShowOptionsMenuButton />
156 </Stack>
157 </Stack>
158 );
159};
160
161const getMemberName = (member: ConversationMember) => {
162 const contact = member.contact;
163 return contact.getDisplayName();
164};
165
simond47ef9e2022-09-28 22:24:28 -0400166export default ConversationView;