blob: c094d710369d3754da01b6537c5c9d7c164097ae [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';
simon5da8ca62022-11-09 15:21:25 -050039import { apiUrl } from '../utils/constants';
simon575c9402022-10-25 16:21:40 -040040import ConversationAvatar from './ConversationAvatar';
simon5da8ca62022-11-09 15:21:25 -050041import {
42 AudioCallIcon,
43 BlockContactIcon,
44 CancelIcon,
45 ContactDetailsIcon,
46 MessageIcon,
47 RemoveContactIcon,
48 VideoCallIcon,
49} from './SvgIcon';
simon575c9402022-10-25 16:21:40 -040050
51const cancelStyles: Modal.Styles = {
52 content: {
53 left: '94px',
54 width: '300px',
55 height: '220px',
56 background: '#FFFFFF 0% 0% no-repeat padding-box',
57 boxShadow: '3px 3px 7px #00000029',
58 borderRadius: '20px',
59 opacity: '1',
60
61 textAlign: 'left',
62 font: 'normal normal normal 12px/26px Ubuntu',
63 letterSpacing: '0px',
64 color: '#000000',
65 },
66};
67
68const contactDetailsStyles: Modal.Styles = {
69 content: {
70 left: '94px',
71 width: '450px',
72 height: '450px',
73 background: '#FFFFFF 0% 0% no-repeat padding-box',
74 boxShadow: '3px 3px 7px #00000029',
75 borderRadius: '20px',
76 opacity: '1',
77
78 textAlign: 'left',
79 font: 'normal normal normal 12px/26px Ubuntu',
80 letterSpacing: '0px',
81 color: '#000000',
82 },
83};
84
simon575c9402022-10-25 16:21:40 -040085const iconColor = '#005699';
86
87type ConversationListItemProps = {
88 conversation: Conversation;
89};
90
91export default function ConversationListItem({ conversation }: ConversationListItemProps) {
simon5da8ca62022-11-09 15:21:25 -050092 const { token } = useAuthContext();
simon575c9402022-10-25 16:21:40 -040093 const { conversationId, contactId } = useParams();
94 const dispatch = useAppDispatch();
95
96 const pathId = conversationId || contactId;
97 const isSelected = conversation.getDisplayUri() === pathId;
98 const navigate = useNavigate();
99
simon1170c322022-10-31 14:51:31 -0400100 const [menuAnchorEl, setMenuAnchorEl] = useState<HTMLElement | null>(null);
simon575c9402022-10-25 16:21:40 -0400101 const [modalDetailsIsOpen, setModalDetailsIsOpen] = useState(false);
102 const [modalDeleteIsOpen, setModalDeleteIsOpen] = useState(false);
103 const [blockOrRemove, setBlockOrRemove] = useState(true);
simon416d0792022-11-03 02:46:18 -0400104 const [userId] = useState(conversation?.getFirstMember()?.contact.getUri());
105 const [isSwarm] = useState(true);
simon575c9402022-10-25 16:21:40 -0400106
simon1170c322022-10-31 14:51:31 -0400107 const openMenu = (e: MouseEvent<HTMLDivElement>) => {
simon575c9402022-10-25 16:21:40 -0400108 e.preventDefault();
109 console.log(e);
simon1170c322022-10-31 14:51:31 -0400110 setMenuAnchorEl(e.currentTarget);
simon575c9402022-10-25 16:21:40 -0400111 };
112 const openModalDetails = () => setModalDetailsIsOpen(true);
113 const openModalDelete = () => setModalDeleteIsOpen(true);
simon1170c322022-10-31 14:51:31 -0400114 const closeModal = () => setMenuAnchorEl(null);
simon575c9402022-10-25 16:21:40 -0400115 const closeModalDetails = () => setModalDetailsIsOpen(false);
116 const closeModalDelete = () => setModalDeleteIsOpen(false);
117
118 const getContactDetails = () => {
119 const controller = new AbortController();
simon5da8ca62022-11-09 15:21:25 -0500120 fetch(new URL(`/contacts/${userId}`, apiUrl), {
121 signal: controller.signal,
122 headers: {
123 Authorization: `Bearer ${token}`,
124 },
125 })
simon575c9402022-10-25 16:21:40 -0400126 .then((res) => res.json())
127 .then((result) => {
128 console.log('CONTACT LIST - DETAILS: ', result);
129 })
130 .catch((e) => console.log('ERROR GET CONTACT DETAILS: ', e));
131 };
132
simon5da8ca62022-11-09 15:21:25 -0500133 const removeOrBlock = (block = false) => {
simon575c9402022-10-25 16:21:40 -0400134 setBlockOrRemove(false);
135
simon5da8ca62022-11-09 15:21:25 -0500136 console.log('EEEH', conversation.getAccountId(), userId);
simon575c9402022-10-25 16:21:40 -0400137
138 const controller = new AbortController();
simon5da8ca62022-11-09 15:21:25 -0500139 let url = `/contacts/${userId}`;
140 if (block) {
141 url += '/block';
142 }
143 fetch(new URL(url, apiUrl), {
144 signal: controller.signal,
145 method: block ? 'POST' : 'DELETE',
146 headers: {
147 Authorization: `Bearer ${token}`,
148 },
149 })
simon575c9402022-10-25 16:21:40 -0400150 .then((res) => res.json())
simon416d0792022-11-03 02:46:18 -0400151 .then(() => {
simon575c9402022-10-25 16:21:40 -0400152 console.log('propre');
153 dispatch(setRefreshFromSlice());
154 })
155 .catch((e) => {
simon5da8ca62022-11-09 15:21:25 -0500156 console.log(`ERROR ${block ? 'blocking' : 'removing'} CONTACT : `, e);
simon575c9402022-10-25 16:21:40 -0400157 dispatch(setRefreshFromSlice());
158 });
159 closeModalDelete();
160 };
161
simon5da8ca62022-11-09 15:21:25 -0500162 const uri = conversation.getId() ? `/account/conversation/${conversation.getId()}` : `/account/addContact/${userId}`;
simon575c9402022-10-25 16:21:40 -0400163 return (
simon1170c322022-10-31 14:51:31 -0400164 <div onContextMenu={openMenu}>
simon575c9402022-10-25 16:21:40 -0400165 <div>
simon1170c322022-10-31 14:51:31 -0400166 <Menu open={!!menuAnchorEl} onClose={closeModal} anchorEl={menuAnchorEl}>
167 <MenuItem
simon575c9402022-10-25 16:21:40 -0400168 onClick={() => {
simon5da8ca62022-11-09 15:21:25 -0500169 navigate(uri);
simon575c9402022-10-25 16:21:40 -0400170 closeModal();
171 }}
simon575c9402022-10-25 16:21:40 -0400172 >
simon1170c322022-10-31 14:51:31 -0400173 <ListItemIcon>
simon575c9402022-10-25 16:21:40 -0400174 <MessageIcon style={{ color: iconColor }} />
simon1170c322022-10-31 14:51:31 -0400175 </ListItemIcon>
176 <ListItemText>
177 <Trans i18nKey="conversation_message" />
178 </ListItemText>
179 </MenuItem>
180 <MenuItem
simon575c9402022-10-25 16:21:40 -0400181 onClick={() => {
simon5da8ca62022-11-09 15:21:25 -0500182 navigate(`/account/call/${conversation.getId()}`);
simon575c9402022-10-25 16:21:40 -0400183 }}
184 >
simon1170c322022-10-31 14:51:31 -0400185 <ListItemIcon>
186 <AudioCallIcon style={{ color: iconColor }} />
187 </ListItemIcon>
188 <ListItemText>
189 <Trans i18nKey="conversation_start_audiocall" />
190 </ListItemText>
191 </MenuItem>
simon575c9402022-10-25 16:21:40 -0400192
simon1170c322022-10-31 14:51:31 -0400193 <MenuItem
194 onClick={() => {
simon5da8ca62022-11-09 15:21:25 -0500195 navigate(`call/${conversation.getId()}?video=true`);
simon1170c322022-10-31 14:51:31 -0400196 }}
197 >
198 <ListItemIcon>
199 <VideoCallIcon style={{ color: iconColor }} />
200 </ListItemIcon>
201 <ListItemText>
202 <Trans i18nKey="conversation_start_videocall" />
203 </ListItemText>
204 </MenuItem>
205
206 {isSelected && (
207 <MenuItem
208 onClick={() => {
simon5da8ca62022-11-09 15:21:25 -0500209 navigate(`/account`);
simon1170c322022-10-31 14:51:31 -0400210 closeModal();
211 }}
212 >
213 <ListItemIcon>
214 <CancelIcon style={{ color: iconColor }} />
215 </ListItemIcon>
216 <ListItemText>
217 <Trans i18nKey="conversation_close" />
218 </ListItemText>
219 </MenuItem>
220 )}
221
222 <MenuItem
simon575c9402022-10-25 16:21:40 -0400223 onClick={() => {
224 console.log('open details contact for: ');
225 closeModal();
226 openModalDetails();
227 getContactDetails();
228 }}
simon575c9402022-10-25 16:21:40 -0400229 >
simon1170c322022-10-31 14:51:31 -0400230 <ListItemIcon>
simon575c9402022-10-25 16:21:40 -0400231 <ContactDetailsIcon style={{ color: iconColor }} />
simon1170c322022-10-31 14:51:31 -0400232 </ListItemIcon>
233 <ListItemText>
234 <Trans i18nKey="conversation_details" />
235 </ListItemText>
236 </MenuItem>
simon575c9402022-10-25 16:21:40 -0400237
simon1170c322022-10-31 14:51:31 -0400238 <MenuItem
simon575c9402022-10-25 16:21:40 -0400239 onClick={() => {
240 setBlockOrRemove(true);
241 closeModal();
242 openModalDelete();
243 }}
simon575c9402022-10-25 16:21:40 -0400244 >
simon1170c322022-10-31 14:51:31 -0400245 <ListItemIcon>
simon575c9402022-10-25 16:21:40 -0400246 <BlockContactIcon style={{ color: iconColor }} />
simon1170c322022-10-31 14:51:31 -0400247 </ListItemIcon>
248 <ListItemText>
249 <Trans i18nKey="conversation_block_contact" />
250 </ListItemText>
251 </MenuItem>
simon575c9402022-10-25 16:21:40 -0400252
simon1170c322022-10-31 14:51:31 -0400253 <MenuItem
simon575c9402022-10-25 16:21:40 -0400254 onClick={() => {
255 setBlockOrRemove(false);
256 closeModal();
257 openModalDelete();
258 }}
simon575c9402022-10-25 16:21:40 -0400259 >
simon1170c322022-10-31 14:51:31 -0400260 <ListItemIcon>
simon575c9402022-10-25 16:21:40 -0400261 <RemoveContactIcon style={{ color: iconColor }} />
simon1170c322022-10-31 14:51:31 -0400262 </ListItemIcon>
263 <ListItemText>
264 <Trans i18nKey="conversation_delete_contact" />
265 </ListItemText>
266 </MenuItem>
267 </Menu>
simon575c9402022-10-25 16:21:40 -0400268 </div>
269
270 <div>
271 <Modal
272 isOpen={modalDetailsIsOpen}
273 onRequestClose={closeModalDetails}
274 style={contactDetailsStyles}
275 contentLabel="Détails contact"
276 >
277 <Stack direction={'row'} alignContent="flex-end">
278 <Stack direction={'column'}>
279 <div style={{ height: '100px' }}>
280 <ConversationAvatar displayName={conversation.getDisplayNameNoFallback()} />
281 </div>
282
283 <div
284 style={{
285 fontSize: '20px',
286 marginBottom: '20px',
287 height: '20px',
288 }}
289 >
290 Informations
291 </div>
292
293 <Typography variant="caption">Nom d&apos;utilisateur</Typography>
294 <div style={{ height: '20px' }} />
295 <Typography variant="caption">Identifiant </Typography>
296 <div style={{ height: '20px' }} />
297
298 <div
299 style={{
300 flex: 1,
301 height: '150px',
302 flexDirection: 'column',
303 // alignSelf: "flex-end",
304 }}
305 >
306 <Typography variant="caption">Code QR</Typography>
307 </div>
308
309 <Typography variant="caption">est un swarm </Typography>
310 </Stack>
311
312 <Stack direction={'column'}>
313 <div
314 style={{
315 fontWeight: 'bold',
316 fontSize: '20px',
317 height: '100px',
318 }}
319 >
320 {conversation.getDisplayNameNoFallback() + '(resolved name)'}
321 </div>
322
323 <div
324 style={{
325 height: '40px',
326 }}
327 />
328 <Typography variant="caption">
329 <div style={{ fontWeight: 'bold' }}>{conversation.getDisplayNameNoFallback()}</div>
330 </Typography>
331
332 <div style={{ height: '20px' }} />
333
334 <Typography variant="caption">
335 <div style={{ fontWeight: 'bold' }}> {userId}</div>
336 </Typography>
337
338 <div style={{ height: '20px' }} />
339
340 <div>
341 <QRCodeCanvas size={40} value={`${userId}`} />
342 </div>
343
344 <Typography variant="caption">
simon1170c322022-10-31 14:51:31 -0400345 <div style={{ fontWeight: 'bold' }}> {String(isSwarm)}</div>
simon575c9402022-10-25 16:21:40 -0400346 </Typography>
347 </Stack>
348 </Stack>
349 <div
350 onClick={closeModalDetails}
351 style={{
352 width: '100px',
353 borderStyle: 'solid',
354 textAlign: 'center',
355 borderRadius: '5px',
356 marginLeft: '150px',
357 marginTop: '10px',
358 }}
359 >
360 <Typography variant="caption">Fermer</Typography>
361 </div>
362 </Modal>
363 </div>
364
365 <div>
366 <Modal
367 isOpen={modalDeleteIsOpen}
368 onRequestClose={closeModalDelete}
369 style={cancelStyles}
370 contentLabel="Merci de confirmer"
371 >
372 <Typography variant="h4">Merci de confirmer</Typography>
373 <Stack direction={'column'} justifyContent="space-around" spacing={'75px'}>
374 <div style={{ textAlign: 'center', marginTop: '10%' }}>
375 <Typography variant="body2">
376 Voulez vous vraiment {blockOrRemove ? 'bloquer' : 'supprimer'} ce contact?
377 </Typography>
378 </div>
379
380 <Stack direction={'row'} top={'25px'} alignSelf="center" spacing={1}>
381 <Box
382 onClick={() => {
simon5da8ca62022-11-09 15:21:25 -0500383 if (blockOrRemove) removeOrBlock(true);
384 else removeOrBlock(false);
simon575c9402022-10-25 16:21:40 -0400385 }}
386 style={{
387 width: '100px',
388 textAlign: 'center',
389 borderStyle: 'solid',
390 borderColor: 'red',
391 borderRadius: '10px',
392 color: 'red',
393 }}
394 >
395 {blockOrRemove ? 'Bloquer' : 'Supprimer'}
396 </Box>
397 <Box
398 onClick={closeModalDelete}
399 style={{
400 width: '100px',
401 textAlign: 'center',
402 paddingLeft: '12px',
403 paddingRight: '12px',
404 borderStyle: 'solid',
405 borderRadius: '10px',
406 }}
407 >
408 Annuler
409 </Box>
410 </Stack>
411 </Stack>
412 </Modal>
413 </div>
414
simon5da8ca62022-11-09 15:21:25 -0500415 <ListItem button alignItems="flex-start" selected={isSelected} onClick={() => navigate(uri)}>
simon575c9402022-10-25 16:21:40 -0400416 <ListItemAvatar>
417 <ConversationAvatar displayName={conversation.getDisplayNameNoFallback()} />
418 </ListItemAvatar>
419 <ListItemText primary={conversation.getDisplayName()} secondary={conversation.getDisplayUri()} />
420 </ListItem>
421 </div>
422 );
423}