blob: 80bd73940895cbab4db2374c950362721a4f0e52 [file] [log] [blame]
idillon18283ac2023-01-07 12:06:42 -05001/*
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 */
18
19import { Dialog, DialogProps, List, Stack, Typography } from '@mui/material';
20import { IConversationRequest } from 'jami-web-common';
21import { useCallback, useMemo } from 'react';
22import { useTranslation } from 'react-i18next';
23import { useNavigate } from 'react-router-dom';
24
25import { useConversationDisplayNameShort } from '../hooks/useConversationDisplayName';
26import { Contact } from '../models/contact';
27import {
28 useAcceptConversationRequestMutation,
29 useBlockConversationRequestMutation,
30 useConversationRequestsQuery,
31 useDeclineConversationRequestMutation,
32} from '../services/conversationQueries';
33import { ColoredRoundButton } from './Button';
34import ConversationAvatar from './ConversationAvatar';
35import { CustomListItemButton } from './CustomListItemButton';
36import { useDialogHandler } from './Dialog';
37import LoadingPage from './Loading';
38import { CheckMarkIcon, PersonWithCrossMarkIcon, SaltireIcon } from './SvgIcon';
39
40export const ConversationRequestList = () => {
41 const conversationRequestsQuery = useConversationRequestsQuery();
42 const conversationRequests = conversationRequestsQuery.data;
43
44 return (
45 <List>
46 {conversationRequests?.map((conversationRequest) => (
47 <ConversationRequestListItem
48 key={conversationRequest.conversationId}
49 conversationRequest={conversationRequest}
50 />
51 ))}
52 </List>
53 );
54};
55
56type ConversationRequestListItemProps = {
57 conversationRequest: IConversationRequest;
58};
59
60const ConversationRequestListItem = ({ conversationRequest }: ConversationRequestListItemProps) => {
61 const dialogHandler = useDialogHandler();
62 const infos = conversationRequest.infos;
63
64 const conversationName = useConversationDisplayNameShort(null, infos.title, conversationRequest.membersNames);
65
66 return (
67 <>
68 <HandleConversationRequestDialog {...dialogHandler.props} conversationRequest={conversationRequest} />
69 <CustomListItemButton
70 onClick={dialogHandler.openDialog}
71 icon={<ConversationAvatar displayName={conversationName} src={infos.avatar} />}
72 primaryText={<Typography variant="body1">{conversationName}</Typography>}
73 />
74 </>
75 );
76};
77
78type HandleConversationRequestDialogProps = DialogProps & {
79 conversationRequest: IConversationRequest;
80};
81
82const HandleConversationRequestDialog = ({ conversationRequest, ...props }: HandleConversationRequestDialogProps) => {
83 const { t } = useTranslation();
84 const navigate = useNavigate();
85
86 const {
87 conversationId,
88 infos: { avatar, title },
89 } = conversationRequest;
90
91 const contact = useMemo(() => {
92 return new Contact(conversationRequest.from.uri, conversationRequest.from.registeredName);
93 }, [conversationRequest]);
94
95 const closeDialog = useCallback(
96 () => props.onClose?.({}, 'escapeKeyDown'), // dummy arguments
97 [props]
98 );
99
100 const blockConversationRequestMutation = useBlockConversationRequestMutation();
101 const acceptConversationRequestMutation = useAcceptConversationRequestMutation();
102 const declineConversationRequestMutation = useDeclineConversationRequestMutation();
103
104 const blockConversationRequest = useCallback(() => {
105 blockConversationRequestMutation.mutate(
106 { conversationId },
107 {
108 onSettled: closeDialog,
109 }
110 );
111 }, [blockConversationRequestMutation, conversationId, closeDialog]);
112
113 const acceptConversationRequest = useCallback(() => {
114 acceptConversationRequestMutation.mutate(
115 { conversationId },
116 {
117 onSuccess: () => navigate(`/conversation/${conversationId}`),
118 onSettled: closeDialog,
119 }
120 );
121 }, [acceptConversationRequestMutation, conversationId, closeDialog, navigate]);
122
123 const declineConversationRequest = useCallback(() => {
124 declineConversationRequestMutation.mutate(
125 { conversationId },
126 {
127 onSettled: closeDialog,
128 }
129 );
130 }, [declineConversationRequestMutation, conversationId, closeDialog]);
131
132 return (
133 <Dialog {...props}>
134 <Stack alignItems="center" spacing="40px" position="relative">
135 <Typography variant="caption" visibility={acceptConversationRequestMutation.isLoading ? 'hidden' : 'visible'}>
136 {t('conversation_request_has_sent_request', { contact: contact.getDisplayName() })}
137 </Typography>
138 <ConversationAvatar displayName={title} src={avatar} sx={{ width: '112px', height: '112px' }} />
139 {acceptConversationRequestMutation.isLoading ? (
140 <>
141 <Typography variant="h3" whiteSpace="pre-line" textAlign="center" fontWeight="bold">
142 {t('conversation_request_accepted')}
143 </Typography>
144 <Typography variant="caption" whiteSpace="pre-line" textAlign="center">
145 {t('conversation_request_waiting_for_sync', { contact: contact.getDisplayName() })}
146 </Typography>
147 <LoadingPage />
148 </>
149 ) : (
150 <>
151 <Typography variant="h3" whiteSpace="pre-line" textAlign="center" fontWeight="bold">
152 {t('conversation_request_ask_join')}
153 </Typography>
154 <Stack direction="row" spacing="30px">
155 <ColoredRoundButton
156 aria-label="block conversation"
157 onClick={blockConversationRequest}
158 Icon={PersonWithCrossMarkIcon}
159 paletteColor={(theme) => theme.palette.warning}
160 />
161 <ColoredRoundButton
162 aria-label="decline conversation request"
163 onClick={declineConversationRequest}
164 Icon={SaltireIcon}
165 paletteColor={(theme) => theme.palette.error}
166 />
167 <ColoredRoundButton
168 aria-label="accept conversation request"
169 onClick={acceptConversationRequest}
170 Icon={CheckMarkIcon}
171 paletteColor={(theme) => theme.palette.success}
172 />
173 </Stack>
174 </>
175 )}
176 </Stack>
177 </Dialog>
178 );
179};