blob: aeef7deb4ff686aba60904c41a10ee54fd35b915 [file] [log] [blame]
simon575c9402022-10-25 16:21:40 -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 */
simon1170c322022-10-31 14:51:31 -040018import {
19 Box,
20 ListItem,
21 ListItemAvatar,
22 ListItemIcon,
23 ListItemText,
24 Menu,
25 MenuItem,
26 Stack,
27 Typography,
28} from '@mui/material';
simon575c9402022-10-25 16:21:40 -040029import { Conversation } from 'jami-web-common';
30import { QRCodeCanvas } from 'qrcode.react';
31import { MouseEvent, useState } from 'react';
simon1170c322022-10-31 14:51:31 -040032import { Trans } from 'react-i18next';
simon575c9402022-10-25 16:21:40 -040033import Modal from 'react-modal';
34import { useNavigate, useParams } from 'react-router-dom';
35
simon5da8ca62022-11-09 15:21:25 -050036import { useAuthContext } from '../contexts/AuthProvider';
simon575c9402022-10-25 16:21:40 -040037import { setRefreshFromSlice } from '../redux/appSlice';
38import { useAppDispatch } from '../redux/hooks';
39import ConversationAvatar from './ConversationAvatar';
simon5da8ca62022-11-09 15:21:25 -050040import {
41 AudioCallIcon,
42 BlockContactIcon,
43 CancelIcon,
44 ContactDetailsIcon,
45 MessageIcon,
46 RemoveContactIcon,
47 VideoCallIcon,
48} from './SvgIcon';
simon575c9402022-10-25 16:21:40 -040049
50const cancelStyles: Modal.Styles = {
51 content: {
52 left: '94px',
53 width: '300px',
54 height: '220px',
55 background: '#FFFFFF 0% 0% no-repeat padding-box',
56 boxShadow: '3px 3px 7px #00000029',
57 borderRadius: '20px',
58 opacity: '1',
59
60 textAlign: 'left',
61 font: 'normal normal normal 12px/26px Ubuntu',
62 letterSpacing: '0px',
63 color: '#000000',
64 },
65};
66
67const contactDetailsStyles: Modal.Styles = {
68 content: {
69 left: '94px',
70 width: '450px',
71 height: '450px',
72 background: '#FFFFFF 0% 0% no-repeat padding-box',
73 boxShadow: '3px 3px 7px #00000029',
74 borderRadius: '20px',
75 opacity: '1',
76
77 textAlign: 'left',
78 font: 'normal normal normal 12px/26px Ubuntu',
79 letterSpacing: '0px',
80 color: '#000000',
81 },
82};
83
simon575c9402022-10-25 16:21:40 -040084const iconColor = '#005699';
85
86type ConversationListItemProps = {
87 conversation: Conversation;
88};
89
90export default function ConversationListItem({ conversation }: ConversationListItemProps) {
simon94fe53e2022-11-10 12:51:58 -050091 const { axiosInstance } = useAuthContext();
simon575c9402022-10-25 16:21:40 -040092 const { conversationId, contactId } = useParams();
93 const dispatch = useAppDispatch();
94
95 const pathId = conversationId || contactId;
96 const isSelected = conversation.getDisplayUri() === pathId;
97 const navigate = useNavigate();
98
simon1170c322022-10-31 14:51:31 -040099 const [menuAnchorEl, setMenuAnchorEl] = useState<HTMLElement | null>(null);
simon575c9402022-10-25 16:21:40 -0400100 const [modalDetailsIsOpen, setModalDetailsIsOpen] = useState(false);
101 const [modalDeleteIsOpen, setModalDeleteIsOpen] = useState(false);
102 const [blockOrRemove, setBlockOrRemove] = useState(true);
simon416d0792022-11-03 02:46:18 -0400103 const [userId] = useState(conversation?.getFirstMember()?.contact.getUri());
104 const [isSwarm] = useState(true);
simon575c9402022-10-25 16:21:40 -0400105
simon1170c322022-10-31 14:51:31 -0400106 const openMenu = (e: MouseEvent<HTMLDivElement>) => {
simon575c9402022-10-25 16:21:40 -0400107 e.preventDefault();
108 console.log(e);
simon1170c322022-10-31 14:51:31 -0400109 setMenuAnchorEl(e.currentTarget);
simon575c9402022-10-25 16:21:40 -0400110 };
111 const openModalDetails = () => setModalDetailsIsOpen(true);
112 const openModalDelete = () => setModalDeleteIsOpen(true);
simon1170c322022-10-31 14:51:31 -0400113 const closeModal = () => setMenuAnchorEl(null);
simon575c9402022-10-25 16:21:40 -0400114 const closeModalDetails = () => setModalDetailsIsOpen(false);
115 const closeModalDelete = () => setModalDeleteIsOpen(false);
116
simon94fe53e2022-11-10 12:51:58 -0500117 const getContactDetails = async () => {
simon575c9402022-10-25 16:21:40 -0400118 const controller = new AbortController();
simon94fe53e2022-11-10 12:51:58 -0500119 try {
120 const data = await axiosInstance.get(`/contacts/${userId}`, {
121 signal: controller.signal,
122 });
123 console.log('CONTACT LIST - DETAILS: ', data);
124 } catch (e) {
125 console.log('ERROR GET CONTACT DETAILS: ', e);
126 }
simon575c9402022-10-25 16:21:40 -0400127 };
128
simon94fe53e2022-11-10 12:51:58 -0500129 const removeOrBlock = async (block = false) => {
simon575c9402022-10-25 16:21:40 -0400130 setBlockOrRemove(false);
131
simon575c9402022-10-25 16:21:40 -0400132 const controller = new AbortController();
simon5da8ca62022-11-09 15:21:25 -0500133 let url = `/contacts/${userId}`;
134 if (block) {
135 url += '/block';
136 }
simon94fe53e2022-11-10 12:51:58 -0500137 try {
138 await axiosInstance(url, {
139 signal: controller.signal,
140 method: block ? 'POST' : 'DELETE',
simon575c9402022-10-25 16:21:40 -0400141 });
simon94fe53e2022-11-10 12:51:58 -0500142 dispatch(setRefreshFromSlice());
143 } catch (e) {
144 console.error(`Error ${block ? 'blocking' : 'removing'} contact : `, e);
145 dispatch(setRefreshFromSlice());
146 }
simon575c9402022-10-25 16:21:40 -0400147 closeModalDelete();
148 };
149
simon3f5f3e72022-11-08 21:01:57 -0500150 const uri = conversation.getId() ? `/conversation/${conversation.getId()}` : `/addContact/${userId}`;
simon575c9402022-10-25 16:21:40 -0400151 return (
simon1170c322022-10-31 14:51:31 -0400152 <div onContextMenu={openMenu}>
simon575c9402022-10-25 16:21:40 -0400153 <div>
simon1170c322022-10-31 14:51:31 -0400154 <Menu open={!!menuAnchorEl} onClose={closeModal} anchorEl={menuAnchorEl}>
155 <MenuItem
simon575c9402022-10-25 16:21:40 -0400156 onClick={() => {
simon5da8ca62022-11-09 15:21:25 -0500157 navigate(uri);
simon575c9402022-10-25 16:21:40 -0400158 closeModal();
159 }}
simon575c9402022-10-25 16:21:40 -0400160 >
simon1170c322022-10-31 14:51:31 -0400161 <ListItemIcon>
simon575c9402022-10-25 16:21:40 -0400162 <MessageIcon style={{ color: iconColor }} />
simon1170c322022-10-31 14:51:31 -0400163 </ListItemIcon>
164 <ListItemText>
165 <Trans i18nKey="conversation_message" />
166 </ListItemText>
167 </MenuItem>
168 <MenuItem
simon575c9402022-10-25 16:21:40 -0400169 onClick={() => {
simon5da8ca62022-11-09 15:21:25 -0500170 navigate(`/account/call/${conversation.getId()}`);
simon575c9402022-10-25 16:21:40 -0400171 }}
172 >
simon1170c322022-10-31 14:51:31 -0400173 <ListItemIcon>
174 <AudioCallIcon style={{ color: iconColor }} />
175 </ListItemIcon>
176 <ListItemText>
177 <Trans i18nKey="conversation_start_audiocall" />
178 </ListItemText>
179 </MenuItem>
simon575c9402022-10-25 16:21:40 -0400180
simon1170c322022-10-31 14:51:31 -0400181 <MenuItem
182 onClick={() => {
simon5da8ca62022-11-09 15:21:25 -0500183 navigate(`call/${conversation.getId()}?video=true`);
simon1170c322022-10-31 14:51:31 -0400184 }}
185 >
186 <ListItemIcon>
187 <VideoCallIcon style={{ color: iconColor }} />
188 </ListItemIcon>
189 <ListItemText>
190 <Trans i18nKey="conversation_start_videocall" />
191 </ListItemText>
192 </MenuItem>
193
194 {isSelected && (
195 <MenuItem
196 onClick={() => {
simon3f5f3e72022-11-08 21:01:57 -0500197 navigate(`/`);
simon1170c322022-10-31 14:51:31 -0400198 closeModal();
199 }}
200 >
201 <ListItemIcon>
202 <CancelIcon style={{ color: iconColor }} />
203 </ListItemIcon>
204 <ListItemText>
205 <Trans i18nKey="conversation_close" />
206 </ListItemText>
207 </MenuItem>
208 )}
209
210 <MenuItem
simon575c9402022-10-25 16:21:40 -0400211 onClick={() => {
212 console.log('open details contact for: ');
213 closeModal();
214 openModalDetails();
215 getContactDetails();
216 }}
simon575c9402022-10-25 16:21:40 -0400217 >
simon1170c322022-10-31 14:51:31 -0400218 <ListItemIcon>
simon575c9402022-10-25 16:21:40 -0400219 <ContactDetailsIcon style={{ color: iconColor }} />
simon1170c322022-10-31 14:51:31 -0400220 </ListItemIcon>
221 <ListItemText>
222 <Trans i18nKey="conversation_details" />
223 </ListItemText>
224 </MenuItem>
simon575c9402022-10-25 16:21:40 -0400225
simon1170c322022-10-31 14:51:31 -0400226 <MenuItem
simon575c9402022-10-25 16:21:40 -0400227 onClick={() => {
228 setBlockOrRemove(true);
229 closeModal();
230 openModalDelete();
231 }}
simon575c9402022-10-25 16:21:40 -0400232 >
simon1170c322022-10-31 14:51:31 -0400233 <ListItemIcon>
simon575c9402022-10-25 16:21:40 -0400234 <BlockContactIcon style={{ color: iconColor }} />
simon1170c322022-10-31 14:51:31 -0400235 </ListItemIcon>
236 <ListItemText>
237 <Trans i18nKey="conversation_block_contact" />
238 </ListItemText>
239 </MenuItem>
simon575c9402022-10-25 16:21:40 -0400240
simon1170c322022-10-31 14:51:31 -0400241 <MenuItem
simon575c9402022-10-25 16:21:40 -0400242 onClick={() => {
243 setBlockOrRemove(false);
244 closeModal();
245 openModalDelete();
246 }}
simon575c9402022-10-25 16:21:40 -0400247 >
simon1170c322022-10-31 14:51:31 -0400248 <ListItemIcon>
simon575c9402022-10-25 16:21:40 -0400249 <RemoveContactIcon style={{ color: iconColor }} />
simon1170c322022-10-31 14:51:31 -0400250 </ListItemIcon>
251 <ListItemText>
252 <Trans i18nKey="conversation_delete_contact" />
253 </ListItemText>
254 </MenuItem>
255 </Menu>
simon575c9402022-10-25 16:21:40 -0400256 </div>
257
258 <div>
259 <Modal
260 isOpen={modalDetailsIsOpen}
261 onRequestClose={closeModalDetails}
262 style={contactDetailsStyles}
263 contentLabel="Détails contact"
264 >
265 <Stack direction={'row'} alignContent="flex-end">
266 <Stack direction={'column'}>
267 <div style={{ height: '100px' }}>
268 <ConversationAvatar displayName={conversation.getDisplayNameNoFallback()} />
269 </div>
270
271 <div
272 style={{
273 fontSize: '20px',
274 marginBottom: '20px',
275 height: '20px',
276 }}
277 >
278 Informations
279 </div>
280
281 <Typography variant="caption">Nom d&apos;utilisateur</Typography>
282 <div style={{ height: '20px' }} />
283 <Typography variant="caption">Identifiant </Typography>
284 <div style={{ height: '20px' }} />
285
286 <div
287 style={{
288 flex: 1,
289 height: '150px',
290 flexDirection: 'column',
291 // alignSelf: "flex-end",
292 }}
293 >
294 <Typography variant="caption">Code QR</Typography>
295 </div>
296
297 <Typography variant="caption">est un swarm </Typography>
298 </Stack>
299
300 <Stack direction={'column'}>
301 <div
302 style={{
303 fontWeight: 'bold',
304 fontSize: '20px',
305 height: '100px',
306 }}
307 >
308 {conversation.getDisplayNameNoFallback() + '(resolved name)'}
309 </div>
310
311 <div
312 style={{
313 height: '40px',
314 }}
315 />
316 <Typography variant="caption">
317 <div style={{ fontWeight: 'bold' }}>{conversation.getDisplayNameNoFallback()}</div>
318 </Typography>
319
320 <div style={{ height: '20px' }} />
321
322 <Typography variant="caption">
323 <div style={{ fontWeight: 'bold' }}> {userId}</div>
324 </Typography>
325
326 <div style={{ height: '20px' }} />
327
328 <div>
329 <QRCodeCanvas size={40} value={`${userId}`} />
330 </div>
331
332 <Typography variant="caption">
simon1170c322022-10-31 14:51:31 -0400333 <div style={{ fontWeight: 'bold' }}> {String(isSwarm)}</div>
simon575c9402022-10-25 16:21:40 -0400334 </Typography>
335 </Stack>
336 </Stack>
337 <div
338 onClick={closeModalDetails}
339 style={{
340 width: '100px',
341 borderStyle: 'solid',
342 textAlign: 'center',
343 borderRadius: '5px',
344 marginLeft: '150px',
345 marginTop: '10px',
346 }}
347 >
348 <Typography variant="caption">Fermer</Typography>
349 </div>
350 </Modal>
351 </div>
352
353 <div>
354 <Modal
355 isOpen={modalDeleteIsOpen}
356 onRequestClose={closeModalDelete}
357 style={cancelStyles}
358 contentLabel="Merci de confirmer"
359 >
360 <Typography variant="h4">Merci de confirmer</Typography>
361 <Stack direction={'column'} justifyContent="space-around" spacing={'75px'}>
362 <div style={{ textAlign: 'center', marginTop: '10%' }}>
363 <Typography variant="body2">
364 Voulez vous vraiment {blockOrRemove ? 'bloquer' : 'supprimer'} ce contact?
365 </Typography>
366 </div>
367
368 <Stack direction={'row'} top={'25px'} alignSelf="center" spacing={1}>
369 <Box
370 onClick={() => {
simon5da8ca62022-11-09 15:21:25 -0500371 if (blockOrRemove) removeOrBlock(true);
372 else removeOrBlock(false);
simon575c9402022-10-25 16:21:40 -0400373 }}
374 style={{
375 width: '100px',
376 textAlign: 'center',
377 borderStyle: 'solid',
378 borderColor: 'red',
379 borderRadius: '10px',
380 color: 'red',
381 }}
382 >
383 {blockOrRemove ? 'Bloquer' : 'Supprimer'}
384 </Box>
385 <Box
386 onClick={closeModalDelete}
387 style={{
388 width: '100px',
389 textAlign: 'center',
390 paddingLeft: '12px',
391 paddingRight: '12px',
392 borderStyle: 'solid',
393 borderRadius: '10px',
394 }}
395 >
396 Annuler
397 </Box>
398 </Stack>
399 </Stack>
400 </Modal>
401 </div>
402
simon5da8ca62022-11-09 15:21:25 -0500403 <ListItem button alignItems="flex-start" selected={isSelected} onClick={() => navigate(uri)}>
simon575c9402022-10-25 16:21:40 -0400404 <ListItemAvatar>
405 <ConversationAvatar displayName={conversation.getDisplayNameNoFallback()} />
406 </ListItemAvatar>
407 <ListItemText primary={conversation.getDisplayName()} secondary={conversation.getDisplayUri()} />
408 </ListItem>
409 </div>
410 );
411}