blob: 9dab62ebe37a060c0e1adfe082a27a859176874a [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
36import authManager from '../AuthManager';
37import { setRefreshFromSlice } from '../redux/appSlice';
38import { useAppDispatch } from '../redux/hooks';
39import ConversationAvatar from './ConversationAvatar';
simon1170c322022-10-31 14:51:31 -040040import { CancelIcon, RemoveContactIcon, VideoCallIcon } from './SvgIcon';
41import { AudioCallIcon, BlockContactIcon, ContactDetailsIcon, MessageIcon } from './SvgIcon';
simon575c9402022-10-25 16:21:40 -040042
43const cancelStyles: Modal.Styles = {
44 content: {
45 left: '94px',
46 width: '300px',
47 height: '220px',
48 background: '#FFFFFF 0% 0% no-repeat padding-box',
49 boxShadow: '3px 3px 7px #00000029',
50 borderRadius: '20px',
51 opacity: '1',
52
53 textAlign: 'left',
54 font: 'normal normal normal 12px/26px Ubuntu',
55 letterSpacing: '0px',
56 color: '#000000',
57 },
58};
59
60const contactDetailsStyles: Modal.Styles = {
61 content: {
62 left: '94px',
63 width: '450px',
64 height: '450px',
65 background: '#FFFFFF 0% 0% no-repeat padding-box',
66 boxShadow: '3px 3px 7px #00000029',
67 borderRadius: '20px',
68 opacity: '1',
69
70 textAlign: 'left',
71 font: 'normal normal normal 12px/26px Ubuntu',
72 letterSpacing: '0px',
73 color: '#000000',
74 },
75};
76
simon575c9402022-10-25 16:21:40 -040077const iconColor = '#005699';
78
79type ConversationListItemProps = {
80 conversation: Conversation;
81};
82
83export default function ConversationListItem({ conversation }: ConversationListItemProps) {
84 const { conversationId, contactId } = useParams();
85 const dispatch = useAppDispatch();
86
87 const pathId = conversationId || contactId;
88 const isSelected = conversation.getDisplayUri() === pathId;
89 const navigate = useNavigate();
90
simon1170c322022-10-31 14:51:31 -040091 const [menuAnchorEl, setMenuAnchorEl] = useState<HTMLElement | null>(null);
simon575c9402022-10-25 16:21:40 -040092 const [modalDetailsIsOpen, setModalDetailsIsOpen] = useState(false);
93 const [modalDeleteIsOpen, setModalDeleteIsOpen] = useState(false);
94 const [blockOrRemove, setBlockOrRemove] = useState(true);
95 const [userId, setUserId] = useState(conversation?.getFirstMember()?.contact.getUri());
simon1170c322022-10-31 14:51:31 -040096 const [isSwarm, setIsSwarm] = useState(true);
simon575c9402022-10-25 16:21:40 -040097
simon1170c322022-10-31 14:51:31 -040098 const openMenu = (e: MouseEvent<HTMLDivElement>) => {
simon575c9402022-10-25 16:21:40 -040099 e.preventDefault();
100 console.log(e);
simon1170c322022-10-31 14:51:31 -0400101 setMenuAnchorEl(e.currentTarget);
simon575c9402022-10-25 16:21:40 -0400102 };
103 const openModalDetails = () => setModalDetailsIsOpen(true);
104 const openModalDelete = () => setModalDeleteIsOpen(true);
simon1170c322022-10-31 14:51:31 -0400105 const closeModal = () => setMenuAnchorEl(null);
simon575c9402022-10-25 16:21:40 -0400106 const closeModalDetails = () => setModalDetailsIsOpen(false);
107 const closeModalDelete = () => setModalDeleteIsOpen(false);
108
109 const getContactDetails = () => {
110 const controller = new AbortController();
111 authManager
112 .fetch(`/api/accounts/${conversation.getAccountId()}/contacts/details/${userId}`, {
113 signal: controller.signal,
114 })
115 .then((res) => res.json())
116 .then((result) => {
117 console.log('CONTACT LIST - DETAILS: ', result);
118 })
119 .catch((e) => console.log('ERROR GET CONTACT DETAILS: ', e));
120 };
121
122 const removeOrBlock = (typeOfRemove: 'block' | 'remove') => {
123 console.log(typeOfRemove);
124 setBlockOrRemove(false);
125
126 console.log('EEEH', typeOfRemove, conversation.getAccountId(), userId);
127
128 const controller = new AbortController();
129 authManager
130 .fetch(`/api/accounts/${conversation.getAccountId()}/contacts/${typeOfRemove}/${userId}`, {
131 signal: controller.signal,
132 method: 'DELETE',
133 })
134 .then((res) => res.json())
135 .then((result) => {
136 console.log('propre');
137 dispatch(setRefreshFromSlice());
138 })
139 .catch((e) => {
140 console.log(`ERROR ${typeOfRemove}ing CONTACT : `, e);
141 dispatch(setRefreshFromSlice());
142 });
143 closeModalDelete();
144 };
145
146 const uri = conversation.getId() ? `conversation/${conversation.getId()}` : `addContact/${userId}`;
147 return (
simon1170c322022-10-31 14:51:31 -0400148 <div onContextMenu={openMenu}>
simon575c9402022-10-25 16:21:40 -0400149 <div>
simon1170c322022-10-31 14:51:31 -0400150 <Menu open={!!menuAnchorEl} onClose={closeModal} anchorEl={menuAnchorEl}>
151 <MenuItem
simon575c9402022-10-25 16:21:40 -0400152 onClick={() => {
153 navigate(`/account/${conversation.getAccountId()}/${uri}`);
154 closeModal();
155 }}
simon575c9402022-10-25 16:21:40 -0400156 >
simon1170c322022-10-31 14:51:31 -0400157 <ListItemIcon>
simon575c9402022-10-25 16:21:40 -0400158 <MessageIcon style={{ color: iconColor }} />
simon1170c322022-10-31 14:51:31 -0400159 </ListItemIcon>
160 <ListItemText>
161 <Trans i18nKey="conversation_message" />
162 </ListItemText>
163 </MenuItem>
164 <MenuItem
simon575c9402022-10-25 16:21:40 -0400165 onClick={() => {
simon1170c322022-10-31 14:51:31 -0400166 navigate(`/account/${conversation.getAccountId()}/call/${conversation.getId()}`);
simon575c9402022-10-25 16:21:40 -0400167 }}
168 >
simon1170c322022-10-31 14:51:31 -0400169 <ListItemIcon>
170 <AudioCallIcon style={{ color: iconColor }} />
171 </ListItemIcon>
172 <ListItemText>
173 <Trans i18nKey="conversation_start_audiocall" />
174 </ListItemText>
175 </MenuItem>
simon575c9402022-10-25 16:21:40 -0400176
simon1170c322022-10-31 14:51:31 -0400177 <MenuItem
178 onClick={() => {
179 navigate(`/account/${conversation.getAccountId()}/call/${conversation.getId()}?video=true`);
180 }}
181 >
182 <ListItemIcon>
183 <VideoCallIcon style={{ color: iconColor }} />
184 </ListItemIcon>
185 <ListItemText>
186 <Trans i18nKey="conversation_start_videocall" />
187 </ListItemText>
188 </MenuItem>
189
190 {isSelected && (
191 <MenuItem
192 onClick={() => {
193 navigate(`/account/${conversation.getAccountId()}/`);
194 closeModal();
195 }}
196 >
197 <ListItemIcon>
198 <CancelIcon style={{ color: iconColor }} />
199 </ListItemIcon>
200 <ListItemText>
201 <Trans i18nKey="conversation_close" />
202 </ListItemText>
203 </MenuItem>
204 )}
205
206 <MenuItem
simon575c9402022-10-25 16:21:40 -0400207 onClick={() => {
208 console.log('open details contact for: ');
209 closeModal();
210 openModalDetails();
211 getContactDetails();
212 }}
simon575c9402022-10-25 16:21:40 -0400213 >
simon1170c322022-10-31 14:51:31 -0400214 <ListItemIcon>
simon575c9402022-10-25 16:21:40 -0400215 <ContactDetailsIcon style={{ color: iconColor }} />
simon1170c322022-10-31 14:51:31 -0400216 </ListItemIcon>
217 <ListItemText>
218 <Trans i18nKey="conversation_details" />
219 </ListItemText>
220 </MenuItem>
simon575c9402022-10-25 16:21:40 -0400221
simon1170c322022-10-31 14:51:31 -0400222 <MenuItem
simon575c9402022-10-25 16:21:40 -0400223 onClick={() => {
224 setBlockOrRemove(true);
225 closeModal();
226 openModalDelete();
227 }}
simon575c9402022-10-25 16:21:40 -0400228 >
simon1170c322022-10-31 14:51:31 -0400229 <ListItemIcon>
simon575c9402022-10-25 16:21:40 -0400230 <BlockContactIcon style={{ color: iconColor }} />
simon1170c322022-10-31 14:51:31 -0400231 </ListItemIcon>
232 <ListItemText>
233 <Trans i18nKey="conversation_block_contact" />
234 </ListItemText>
235 </MenuItem>
simon575c9402022-10-25 16:21:40 -0400236
simon1170c322022-10-31 14:51:31 -0400237 <MenuItem
simon575c9402022-10-25 16:21:40 -0400238 onClick={() => {
239 setBlockOrRemove(false);
240 closeModal();
241 openModalDelete();
242 }}
simon575c9402022-10-25 16:21:40 -0400243 >
simon1170c322022-10-31 14:51:31 -0400244 <ListItemIcon>
simon575c9402022-10-25 16:21:40 -0400245 <RemoveContactIcon style={{ color: iconColor }} />
simon1170c322022-10-31 14:51:31 -0400246 </ListItemIcon>
247 <ListItemText>
248 <Trans i18nKey="conversation_delete_contact" />
249 </ListItemText>
250 </MenuItem>
251 </Menu>
simon575c9402022-10-25 16:21:40 -0400252 </div>
253
254 <div>
255 <Modal
256 isOpen={modalDetailsIsOpen}
257 onRequestClose={closeModalDetails}
258 style={contactDetailsStyles}
259 contentLabel="Détails contact"
260 >
261 <Stack direction={'row'} alignContent="flex-end">
262 <Stack direction={'column'}>
263 <div style={{ height: '100px' }}>
264 <ConversationAvatar displayName={conversation.getDisplayNameNoFallback()} />
265 </div>
266
267 <div
268 style={{
269 fontSize: '20px',
270 marginBottom: '20px',
271 height: '20px',
272 }}
273 >
274 Informations
275 </div>
276
277 <Typography variant="caption">Nom d&apos;utilisateur</Typography>
278 <div style={{ height: '20px' }} />
279 <Typography variant="caption">Identifiant </Typography>
280 <div style={{ height: '20px' }} />
281
282 <div
283 style={{
284 flex: 1,
285 height: '150px',
286 flexDirection: 'column',
287 // alignSelf: "flex-end",
288 }}
289 >
290 <Typography variant="caption">Code QR</Typography>
291 </div>
292
293 <Typography variant="caption">est un swarm </Typography>
294 </Stack>
295
296 <Stack direction={'column'}>
297 <div
298 style={{
299 fontWeight: 'bold',
300 fontSize: '20px',
301 height: '100px',
302 }}
303 >
304 {conversation.getDisplayNameNoFallback() + '(resolved name)'}
305 </div>
306
307 <div
308 style={{
309 height: '40px',
310 }}
311 />
312 <Typography variant="caption">
313 <div style={{ fontWeight: 'bold' }}>{conversation.getDisplayNameNoFallback()}</div>
314 </Typography>
315
316 <div style={{ height: '20px' }} />
317
318 <Typography variant="caption">
319 <div style={{ fontWeight: 'bold' }}> {userId}</div>
320 </Typography>
321
322 <div style={{ height: '20px' }} />
323
324 <div>
325 <QRCodeCanvas size={40} value={`${userId}`} />
326 </div>
327
328 <Typography variant="caption">
simon1170c322022-10-31 14:51:31 -0400329 <div style={{ fontWeight: 'bold' }}> {String(isSwarm)}</div>
simon575c9402022-10-25 16:21:40 -0400330 </Typography>
331 </Stack>
332 </Stack>
333 <div
334 onClick={closeModalDetails}
335 style={{
336 width: '100px',
337 borderStyle: 'solid',
338 textAlign: 'center',
339 borderRadius: '5px',
340 marginLeft: '150px',
341 marginTop: '10px',
342 }}
343 >
344 <Typography variant="caption">Fermer</Typography>
345 </div>
346 </Modal>
347 </div>
348
349 <div>
350 <Modal
351 isOpen={modalDeleteIsOpen}
352 onRequestClose={closeModalDelete}
353 style={cancelStyles}
354 contentLabel="Merci de confirmer"
355 >
356 <Typography variant="h4">Merci de confirmer</Typography>
357 <Stack direction={'column'} justifyContent="space-around" spacing={'75px'}>
358 <div style={{ textAlign: 'center', marginTop: '10%' }}>
359 <Typography variant="body2">
360 Voulez vous vraiment {blockOrRemove ? 'bloquer' : 'supprimer'} ce contact?
361 </Typography>
362 </div>
363
364 <Stack direction={'row'} top={'25px'} alignSelf="center" spacing={1}>
365 <Box
366 onClick={() => {
367 if (blockOrRemove) removeOrBlock('block');
368 else removeOrBlock('remove');
369 }}
370 style={{
371 width: '100px',
372 textAlign: 'center',
373 borderStyle: 'solid',
374 borderColor: 'red',
375 borderRadius: '10px',
376 color: 'red',
377 }}
378 >
379 {blockOrRemove ? 'Bloquer' : 'Supprimer'}
380 </Box>
381 <Box
382 onClick={closeModalDelete}
383 style={{
384 width: '100px',
385 textAlign: 'center',
386 paddingLeft: '12px',
387 paddingRight: '12px',
388 borderStyle: 'solid',
389 borderRadius: '10px',
390 }}
391 >
392 Annuler
393 </Box>
394 </Stack>
395 </Stack>
396 </Modal>
397 </div>
398
399 <ListItem
400 button
401 alignItems="flex-start"
402 selected={isSelected}
403 onClick={() => navigate(`/account/${conversation.getAccountId()}/${uri}`)}
404 >
405 <ListItemAvatar>
406 <ConversationAvatar displayName={conversation.getDisplayNameNoFallback()} />
407 </ListItemAvatar>
408 <ListItemText primary={conversation.getDisplayName()} secondary={conversation.getDisplayUri()} />
409 </ListItem>
410 </div>
411 );
412}