Flavien modal two
[ProtonMail-WebClient.git] / packages / components / containers / calendar / shareURL / ShareLinkModal.tsx
blob7059ca6deaf4bcd35b6c228d6b3490376c1f7be2
1 import { useEffect, useState } from 'react';
3 import { c } from 'ttag';
5 import { Button } from '@proton/atoms';
6 import Form from '@proton/components/components/form/Form';
7 import BasicModal from '@proton/components/components/modalTwo/BasicModal';
8 import type { BasicModalProps } from '@proton/components/components/modalTwo/BasicModal';
9 import Option from '@proton/components/components/option/Option';
10 import SelectTwo from '@proton/components/components/selectTwo/SelectTwo';
11 import { useApi, useGetCalendarInfo, useNotifications } from '@proton/components/hooks';
12 import { createPublicLink } from '@proton/shared/lib/api/calendars';
13 import { MAX_CHARS_CLEARTEXT } from '@proton/shared/lib/calendar/constants';
14 import { getPrimaryCalendarKey } from '@proton/shared/lib/calendar/crypto/keys/helpers';
15 import {
16     buildLink,
17     generateEncryptedPurpose,
18     getCreatePublicLinkPayload,
19     transformLinksFromAPI,
20 } from '@proton/shared/lib/calendar/sharing/shareUrl/shareUrl';
21 import type { CalendarLink, CalendarUrlResponse } from '@proton/shared/lib/interfaces/calendar';
22 import { ACCESS_LEVEL } from '@proton/shared/lib/interfaces/calendar';
23 import { splitKeys } from '@proton/shared/lib/keys';
25 import { InputFieldTwo } from '../../../components';
27 interface Props extends Omit<BasicModalProps, 'children' | 'footer'> {
28     calendarID: string;
29     calendarName: string;
30     onSubmit: ({
31         link,
32         transformedLink,
33         accessLevel,
34     }: {
35         link: string;
36         transformedLink: CalendarLink[];
37         accessLevel: ACCESS_LEVEL;
38     }) => Promise<void>;
39     onClose: () => void;
40     isOpen: boolean;
43 const defaultAccessLevel = ACCESS_LEVEL.LIMITED;
45 const ShareLinkModal = ({ calendarID, calendarName, onSubmit, onClose, isOpen, ...rest }: Props) => {
46     const getCalendarInfo = useGetCalendarInfo();
47     const api = useApi();
48     const { createNotification } = useNotifications();
50     const [isLoading, setIsLoading] = useState(false);
51     const [accessLevel, setAccessLevel] = useState<ACCESS_LEVEL>(defaultAccessLevel);
52     const [purpose, setPurpose] = useState('');
54     const handleError = ({ message }: Error) => createNotification({ type: 'error', text: message });
56     const handleSubmit = async () => {
57         try {
58             setIsLoading(true);
59             const { calendarKeys, passphrase, passphraseID } = await getCalendarInfo(calendarID);
60             const { publicKey } = getPrimaryCalendarKey(calendarKeys);
61             const { publicKeys } = splitKeys(calendarKeys);
63             const encryptedPurpose = purpose ? await generateEncryptedPurpose({ purpose, publicKey }) : null;
65             const { payload, passphraseKey, cacheKey } = await getCreatePublicLinkPayload({
66                 accessLevel,
67                 publicKeys,
68                 passphrase,
69                 passphraseID,
70                 encryptedPurpose,
71             });
73             const { CalendarUrl } = await api<CalendarUrlResponse>(createPublicLink(calendarID, payload));
74             const { CalendarUrlID } = CalendarUrl;
75             const link = buildLink({
76                 urlID: CalendarUrlID,
77                 accessLevel,
78                 passphraseKey,
79                 cacheKey,
80             });
81             const { privateKeys } = splitKeys(calendarKeys);
82             const transformedLink = await transformLinksFromAPI({
83                 calendarUrls: [CalendarUrl],
84                 privateKeys,
85                 calendarPassphrase: passphrase,
86                 onError: handleError,
87             });
89             createNotification({ type: 'success', text: c('Info').t`Link created` });
90             setIsLoading(false);
92             await onSubmit({ link, transformedLink, accessLevel });
93             onClose();
94         } catch (e: any) {
95             setIsLoading(false);
96             const error = e instanceof Error ? e : new Error('Failed to generate link');
97             throw error;
98         }
99     };
101     useEffect(() => {
102         if (!isOpen) {
103             setPurpose('');
104             setAccessLevel(defaultAccessLevel);
105         }
106     }, [isOpen]);
108     return (
109         <BasicModal
110             {...rest}
111             isOpen={isOpen}
112             title={c('Title').t`Create public link`}
113             subline={calendarName}
114             footer={
115                 <>
116                     <Button onClick={onClose}>{c('Action').t`Cancel`}</Button>
117                     <Button
118                         type="submit"
119                         loading={isLoading}
120                         className="ml-auto"
121                         onClick={handleSubmit}
122                         color="norm"
123                     >{c('Action').t`Create`}</Button>
124                 </>
125             }
126             onClose={onClose}
127             size="medium"
128             as={Form}
129         >
130             <InputFieldTwo
131                 label={c('Link label (optional)').t`Access`}
132                 as={SelectTwo}
133                 value={accessLevel}
134                 onValue={(value: any) => setAccessLevel(value)}
135             >
136                 <Option value={0} title={c('Access level').t`Limited view (see only free/busy)`} />
137                 <Option value={1} title={c('Access level').t`Full view (see all event details)`} />
138             </InputFieldTwo>
139             <InputFieldTwo
140                 value={purpose}
141                 onValue={setPurpose}
142                 maxLength={MAX_CHARS_CLEARTEXT.PURPOSE}
143                 label={c('Input label').t`Link label (optional)`}
144                 assistiveText={c('Calendar link sharing label input assistive text').t`Only you can see the label`}
145                 placeholder={c('Input placeholder').t`Add label`}
146             />
147         </BasicModal>
148     );
151 export default ShareLinkModal;