blob: 2f070764eca9a509d04818a021d9418d2320c801 [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';
simon4e7445c2022-11-16 21:18:46 -050032import { useTranslation } 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();
simon4e7445c2022-11-16 21:18:46 -050098 const { t } = useTranslation();
simon575c9402022-10-25 16:21:40 -040099
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
simon94fe53e2022-11-10 12:51:58 -0500118 const getContactDetails = async () => {
simon575c9402022-10-25 16:21:40 -0400119 const controller = new AbortController();
simon94fe53e2022-11-10 12:51:58 -0500120 try {
Misha Krieger-Raynauld46e9d242022-11-12 18:02:43 -0500121 const { data } = await axiosInstance.get(`/contacts/${userId}`, {
simon94fe53e2022-11-10 12:51:58 -0500122 signal: controller.signal,
123 });
124 console.log('CONTACT LIST - DETAILS: ', data);
125 } catch (e) {
126 console.log('ERROR GET CONTACT DETAILS: ', e);
127 }
simon575c9402022-10-25 16:21:40 -0400128 };
129
simon94fe53e2022-11-10 12:51:58 -0500130 const removeOrBlock = async (block = false) => {
simon575c9402022-10-25 16:21:40 -0400131 setBlockOrRemove(false);
132
simon575c9402022-10-25 16:21:40 -0400133 const controller = new AbortController();
simon5da8ca62022-11-09 15:21:25 -0500134 let url = `/contacts/${userId}`;
135 if (block) {
136 url += '/block';
137 }
simon94fe53e2022-11-10 12:51:58 -0500138 try {
139 await axiosInstance(url, {
140 signal: controller.signal,
141 method: block ? 'POST' : 'DELETE',
simon575c9402022-10-25 16:21:40 -0400142 });
simon94fe53e2022-11-10 12:51:58 -0500143 dispatch(setRefreshFromSlice());
144 } catch (e) {
145 console.error(`Error ${block ? 'blocking' : 'removing'} contact : `, e);
146 dispatch(setRefreshFromSlice());
147 }
simon575c9402022-10-25 16:21:40 -0400148 closeModalDelete();
149 };
150
simon2d3b6532022-11-08 21:01:57 -0500151 const uri = conversation.getId() ? `/conversation/${conversation.getId()}` : `/add-contact/${userId}`;
simon575c9402022-10-25 16:21:40 -0400152 return (
simon1170c322022-10-31 14:51:31 -0400153 <div onContextMenu={openMenu}>
simon575c9402022-10-25 16:21:40 -0400154 <div>
simon1170c322022-10-31 14:51:31 -0400155 <Menu open={!!menuAnchorEl} onClose={closeModal} anchorEl={menuAnchorEl}>
156 <MenuItem
simon575c9402022-10-25 16:21:40 -0400157 onClick={() => {
simon5da8ca62022-11-09 15:21:25 -0500158 navigate(uri);
simon575c9402022-10-25 16:21:40 -0400159 closeModal();
160 }}
simon575c9402022-10-25 16:21:40 -0400161 >
simon1170c322022-10-31 14:51:31 -0400162 <ListItemIcon>
simon575c9402022-10-25 16:21:40 -0400163 <MessageIcon style={{ color: iconColor }} />
simon1170c322022-10-31 14:51:31 -0400164 </ListItemIcon>
simon4e7445c2022-11-16 21:18:46 -0500165 <ListItemText>{t('conversation_message')}</ListItemText>
simon1170c322022-10-31 14:51:31 -0400166 </MenuItem>
167 <MenuItem
simon575c9402022-10-25 16:21:40 -0400168 onClick={() => {
simon5da8ca62022-11-09 15:21:25 -0500169 navigate(`/account/call/${conversation.getId()}`);
simon575c9402022-10-25 16:21:40 -0400170 }}
171 >
simon1170c322022-10-31 14:51:31 -0400172 <ListItemIcon>
173 <AudioCallIcon style={{ color: iconColor }} />
174 </ListItemIcon>
simon4e7445c2022-11-16 21:18:46 -0500175 <ListItemText>{t('conversation_start_audiocall')}</ListItemText>
simon1170c322022-10-31 14:51:31 -0400176 </MenuItem>
simon575c9402022-10-25 16:21:40 -0400177
simon1170c322022-10-31 14:51:31 -0400178 <MenuItem
179 onClick={() => {
simon5da8ca62022-11-09 15:21:25 -0500180 navigate(`call/${conversation.getId()}?video=true`);
simon1170c322022-10-31 14:51:31 -0400181 }}
182 >
183 <ListItemIcon>
184 <VideoCallIcon style={{ color: iconColor }} />
185 </ListItemIcon>
simon4e7445c2022-11-16 21:18:46 -0500186 <ListItemText>{t('conversation_start_videocall')}</ListItemText>
simon1170c322022-10-31 14:51:31 -0400187 </MenuItem>
188
189 {isSelected && (
190 <MenuItem
191 onClick={() => {
simon3f5f3e72022-11-08 21:01:57 -0500192 navigate(`/`);
simon1170c322022-10-31 14:51:31 -0400193 closeModal();
194 }}
195 >
196 <ListItemIcon>
197 <CancelIcon style={{ color: iconColor }} />
198 </ListItemIcon>
simon4e7445c2022-11-16 21:18:46 -0500199 <ListItemText>{t('conversation_close')}</ListItemText>
simon1170c322022-10-31 14:51:31 -0400200 </MenuItem>
201 )}
202
203 <MenuItem
simon575c9402022-10-25 16:21:40 -0400204 onClick={() => {
205 console.log('open details contact for: ');
206 closeModal();
207 openModalDetails();
208 getContactDetails();
209 }}
simon575c9402022-10-25 16:21:40 -0400210 >
simon1170c322022-10-31 14:51:31 -0400211 <ListItemIcon>
simon575c9402022-10-25 16:21:40 -0400212 <ContactDetailsIcon style={{ color: iconColor }} />
simon1170c322022-10-31 14:51:31 -0400213 </ListItemIcon>
simon4e7445c2022-11-16 21:18:46 -0500214 <ListItemText>{t('conversation_details')}</ListItemText>
simon1170c322022-10-31 14:51:31 -0400215 </MenuItem>
simon575c9402022-10-25 16:21:40 -0400216
simon1170c322022-10-31 14:51:31 -0400217 <MenuItem
simon575c9402022-10-25 16:21:40 -0400218 onClick={() => {
219 setBlockOrRemove(true);
220 closeModal();
221 openModalDelete();
222 }}
simon575c9402022-10-25 16:21:40 -0400223 >
simon1170c322022-10-31 14:51:31 -0400224 <ListItemIcon>
simon575c9402022-10-25 16:21:40 -0400225 <BlockContactIcon style={{ color: iconColor }} />
simon1170c322022-10-31 14:51:31 -0400226 </ListItemIcon>
simon4e7445c2022-11-16 21:18:46 -0500227 <ListItemText>{t('conversation_block_contact')}</ListItemText>
simon1170c322022-10-31 14:51:31 -0400228 </MenuItem>
simon575c9402022-10-25 16:21:40 -0400229
simon1170c322022-10-31 14:51:31 -0400230 <MenuItem
simon575c9402022-10-25 16:21:40 -0400231 onClick={() => {
232 setBlockOrRemove(false);
233 closeModal();
234 openModalDelete();
235 }}
simon575c9402022-10-25 16:21:40 -0400236 >
simon1170c322022-10-31 14:51:31 -0400237 <ListItemIcon>
simon575c9402022-10-25 16:21:40 -0400238 <RemoveContactIcon style={{ color: iconColor }} />
simon1170c322022-10-31 14:51:31 -0400239 </ListItemIcon>
simon4e7445c2022-11-16 21:18:46 -0500240 <ListItemText>{t('conversation_delete_contact')}</ListItemText>
simon1170c322022-10-31 14:51:31 -0400241 </MenuItem>
242 </Menu>
simon575c9402022-10-25 16:21:40 -0400243 </div>
244
245 <div>
246 <Modal
247 isOpen={modalDetailsIsOpen}
248 onRequestClose={closeModalDetails}
249 style={contactDetailsStyles}
250 contentLabel="Détails contact"
251 >
252 <Stack direction={'row'} alignContent="flex-end">
253 <Stack direction={'column'}>
254 <div style={{ height: '100px' }}>
255 <ConversationAvatar displayName={conversation.getDisplayNameNoFallback()} />
256 </div>
257
258 <div
259 style={{
260 fontSize: '20px',
261 marginBottom: '20px',
262 height: '20px',
263 }}
264 >
265 Informations
266 </div>
267
268 <Typography variant="caption">Nom d&apos;utilisateur</Typography>
269 <div style={{ height: '20px' }} />
270 <Typography variant="caption">Identifiant </Typography>
271 <div style={{ height: '20px' }} />
272
273 <div
274 style={{
275 flex: 1,
276 height: '150px',
277 flexDirection: 'column',
278 // alignSelf: "flex-end",
279 }}
280 >
281 <Typography variant="caption">Code QR</Typography>
282 </div>
283
284 <Typography variant="caption">est un swarm </Typography>
285 </Stack>
286
287 <Stack direction={'column'}>
288 <div
289 style={{
290 fontWeight: 'bold',
291 fontSize: '20px',
292 height: '100px',
293 }}
294 >
295 {conversation.getDisplayNameNoFallback() + '(resolved name)'}
296 </div>
297
298 <div
299 style={{
300 height: '40px',
301 }}
302 />
303 <Typography variant="caption">
304 <div style={{ fontWeight: 'bold' }}>{conversation.getDisplayNameNoFallback()}</div>
305 </Typography>
306
307 <div style={{ height: '20px' }} />
308
309 <Typography variant="caption">
310 <div style={{ fontWeight: 'bold' }}> {userId}</div>
311 </Typography>
312
313 <div style={{ height: '20px' }} />
314
315 <div>
316 <QRCodeCanvas size={40} value={`${userId}`} />
317 </div>
318
319 <Typography variant="caption">
simon1170c322022-10-31 14:51:31 -0400320 <div style={{ fontWeight: 'bold' }}> {String(isSwarm)}</div>
simon575c9402022-10-25 16:21:40 -0400321 </Typography>
322 </Stack>
323 </Stack>
324 <div
325 onClick={closeModalDetails}
326 style={{
327 width: '100px',
328 borderStyle: 'solid',
329 textAlign: 'center',
330 borderRadius: '5px',
331 marginLeft: '150px',
332 marginTop: '10px',
333 }}
334 >
335 <Typography variant="caption">Fermer</Typography>
336 </div>
337 </Modal>
338 </div>
339
340 <div>
341 <Modal
342 isOpen={modalDeleteIsOpen}
343 onRequestClose={closeModalDelete}
344 style={cancelStyles}
345 contentLabel="Merci de confirmer"
346 >
347 <Typography variant="h4">Merci de confirmer</Typography>
348 <Stack direction={'column'} justifyContent="space-around" spacing={'75px'}>
349 <div style={{ textAlign: 'center', marginTop: '10%' }}>
350 <Typography variant="body2">
351 Voulez vous vraiment {blockOrRemove ? 'bloquer' : 'supprimer'} ce contact?
352 </Typography>
353 </div>
354
355 <Stack direction={'row'} top={'25px'} alignSelf="center" spacing={1}>
356 <Box
357 onClick={() => {
simon5da8ca62022-11-09 15:21:25 -0500358 if (blockOrRemove) removeOrBlock(true);
359 else removeOrBlock(false);
simon575c9402022-10-25 16:21:40 -0400360 }}
361 style={{
362 width: '100px',
363 textAlign: 'center',
364 borderStyle: 'solid',
365 borderColor: 'red',
366 borderRadius: '10px',
367 color: 'red',
368 }}
369 >
370 {blockOrRemove ? 'Bloquer' : 'Supprimer'}
371 </Box>
372 <Box
373 onClick={closeModalDelete}
374 style={{
375 width: '100px',
376 textAlign: 'center',
377 paddingLeft: '12px',
378 paddingRight: '12px',
379 borderStyle: 'solid',
380 borderRadius: '10px',
381 }}
382 >
383 Annuler
384 </Box>
385 </Stack>
386 </Stack>
387 </Modal>
388 </div>
389
simon5da8ca62022-11-09 15:21:25 -0500390 <ListItem button alignItems="flex-start" selected={isSelected} onClick={() => navigate(uri)}>
simon575c9402022-10-25 16:21:40 -0400391 <ListItemAvatar>
392 <ConversationAvatar displayName={conversation.getDisplayNameNoFallback()} />
393 </ListItemAvatar>
394 <ListItemText primary={conversation.getDisplayName()} secondary={conversation.getDisplayUri()} />
395 </ListItem>
396 </div>
397 );
398}