Merge branch 'DRVDOC-1260' into 'main'
[ProtonMail-WebClient.git] / packages / components / containers / addresses / AddressesWithUser.tsx
blob372b3cfc8318753ed70bec681afd77f3dd00b320
1 import { useCallback, useEffect, useState } from 'react';
3 import { c } from 'ttag';
5 import { useAddresses } from '@proton/account/addresses/hooks';
6 import { Href } from '@proton/atoms';
7 import Alert from '@proton/components/components/alert/Alert';
8 import useModalState from '@proton/components/components/modalTwo/useModalState';
9 import OrderableTable from '@proton/components/components/orderableTable/OrderableTable';
10 import OrderableTableBody from '@proton/components/components/orderableTable/OrderableTableBody';
11 import OrderableTableHeader from '@proton/components/components/orderableTable/OrderableTableHeader';
12 import OrderableTableRow from '@proton/components/components/orderableTable/OrderableTableRow';
13 import MailUpsellButton from '@proton/components/components/upsell/MailUpsellButton';
14 import NewUpsellModal from '@proton/components/components/upsell/modal/NewUpsellModal';
15 import UpsellModal from '@proton/components/components/upsell/modal/UpsellModal';
16 import useOneDollarConfig from '@proton/components/components/upsell/useOneDollarPromo';
17 import useUpsellConfig from '@proton/components/components/upsell/useUpsellConfig';
18 import SettingsParagraph from '@proton/components/containers/account/SettingsParagraph';
19 import useApi from '@proton/components/hooks/useApi';
20 import useEventManager from '@proton/components/hooks/useEventManager';
21 import useNotifications from '@proton/components/hooks/useNotifications';
22 import { orderAddress } from '@proton/shared/lib/api/addresses';
23 import { APP_UPSELL_REF_PATH, BRAND_NAME, MAIL_UPSELL_PATHS, UPSELL_COMPONENT } from '@proton/shared/lib/constants';
24 import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
25 import { getUpsellRef, useNewUpsellModalVariant } from '@proton/shared/lib/helpers/upsell';
26 import { getKnowledgeBaseUrl } from '@proton/shared/lib/helpers/url';
27 import type { Address, CachedOrganizationKey, Member, UserModel } from '@proton/shared/lib/interfaces';
28 import { getIsNonDefault, sortAddresses } from '@proton/shared/lib/mail/addresses';
29 import addressesImg from '@proton/styles/assets/img/illustrations/new-upsells-img/addresses.svg';
30 import move from '@proton/utils/move';
32 import AddressActions from './AddressActions';
33 import AddressStatus from './AddressStatus';
34 import { getPermissions, getStatus } from './helper';
36 interface Props {
37     user: UserModel;
38     member?: Member;
39     organizationKey?: CachedOrganizationKey;
40     hasDescription?: boolean;
41     allowAddressDeletion: boolean;
44 const AddressesUser = ({ user, organizationKey, member, hasDescription = true, allowAddressDeletion }: Props) => {
45     const api = useApi();
46     const { createNotification } = useNotifications();
47     const [savingIndex, setSavingIndex] = useState<number | undefined>();
48     const { call } = useEventManager();
49     const [addresses, loadingAddresses] = useAddresses();
50     const [list, setAddresses] = useState<Address[]>(() => sortAddresses(addresses || []));
52     const upsellRef = getUpsellRef({
53         app: APP_UPSELL_REF_PATH.MAIL_UPSELL_REF_PATH,
54         component: UPSELL_COMPONENT.MODAL,
55         feature: MAIL_UPSELL_PATHS.UNLIMITED_ADDRESSES,
56         isSettings: true,
57     });
59     const oneDollarConfig = useOneDollarConfig();
60     const upsellConfig = useUpsellConfig({ upsellRef, ...oneDollarConfig });
62     const displayNewUpsellModalsVariant = useNewUpsellModalVariant();
64     const [upsellModalProps, handleUpsellModalDisplay, renderUpsellModal] = useModalState();
66     useEffect(() => {
67         if (addresses) {
68             setAddresses(sortAddresses(addresses));
69         }
70     }, [addresses]);
72     const handleSortEnd = useCallback(
73         async ({ oldIndex, newIndex }: { oldIndex: number; newIndex: number }) => {
74             try {
75                 const newList = move(list, oldIndex, newIndex);
76                 const { isExternal, isDisabled } = getStatus(newList[0], 0);
78                 if (isDeepEqual(list, newList)) {
79                     return;
80                 }
82                 if (newIndex === 0 && (isDisabled || isExternal)) {
83                     const errorMessage = (() => {
84                         if (isDisabled) {
85                             return c('Notification').t`A disabled address cannot be default`;
86                         }
87                         if (isExternal) {
88                             return c('Notification').t`An external address cannot be default`;
89                         }
90                     })();
91                     if (errorMessage) {
92                         createNotification({
93                             type: 'error',
94                             text: errorMessage,
95                         });
96                     }
97                     setAddresses(sortAddresses(addresses));
98                     return;
99                 }
101                 setAddresses(newList);
102                 setSavingIndex(newIndex);
104                 await api(orderAddress(newList.map(({ ID }) => ID)));
105                 await call();
107                 setSavingIndex(undefined);
108             } catch (e: any) {
109                 setSavingIndex(undefined);
110                 setAddresses(sortAddresses(addresses));
111             }
112         },
113         [list, addresses]
114     );
116     const setDefaultAddress = useCallback(
117         (addressOldIndex: number) => {
118             return async () => {
119                 await handleSortEnd({ oldIndex: addressOldIndex, newIndex: 0 });
120             };
121         },
122         [list, addresses]
123     );
125     if (!loadingAddresses && !addresses?.length) {
126         return <Alert className="mb-4">{c('Info').t`No addresses exist`}</Alert>;
127     }
129     const modal = displayNewUpsellModalsVariant ? (
130         <NewUpsellModal
131             titleModal={c('Title').t`An address for each role`}
132             description={c('Description')
133                 .t`Keep different parts of your life separate and your inbox organized with additional addresses.`}
134             modalProps={upsellModalProps}
135             illustration={addressesImg}
136             sourceEvent="BUTTON_MORE_ADDRESSES"
137             {...upsellConfig}
138         />
139     ) : (
140         <UpsellModal
141             title={c('Title').t`Increase your privacy with more addresses`}
142             description={c('Description')
143                 .t`Separate different aspects of your life with multiple email addresses and unlock more premium features when you upgrade.`}
144             modalProps={upsellModalProps}
145             sourceEvent="BUTTON_MORE_ADDRESSES"
146             features={['more-storage', 'more-email-addresses', 'unlimited-folders-and-labels', 'custom-email-domains']}
147             {...upsellConfig}
148         />
149     );
151     return (
152         <>
153             {hasDescription && (
154                 <SettingsParagraph className="mt-2">
155                     <span>
156                         {c('Info').t`Use the different types of email addresses and aliases offered by ${BRAND_NAME}.`}
157                     </span>
158                     <br />
159                     <Href href={getKnowledgeBaseUrl('/addresses-and-aliases')}>{c('Link').t`Learn more`}</Href>
160                 </SettingsParagraph>
161             )}
163             {!user.hasPaidMail && (
164                 <MailUpsellButton
165                     onClick={() => handleUpsellModalDisplay(true)}
166                     text={c('Action').t`Get more addresses`}
167                 />
168             )}
170             <OrderableTable
171                 onSortEnd={handleSortEnd}
172                 className="simple-table--has-actions mt-4"
173                 helperClassname="simple-table--has-actions"
174             >
175                 <OrderableTableHeader
176                     cells={[
177                         c('Header for addresses table').t`Address`,
178                         c('Header for addresses table').t`Status`,
179                         c('Header for addresses table').t`Actions`,
180                     ]}
181                 />
182                 <OrderableTableBody colSpan={3} loading={loadingAddresses}>
183                     {list &&
184                         list.map((address, i) => {
185                             const addressStatuses = getStatus(address, i);
186                             return (
187                                 <OrderableTableRow
188                                     disableSort={getIsNonDefault(address)}
189                                     key={i}
190                                     index={i}
191                                     cells={[
192                                         <div key={0} className="text-ellipsis" title={address.Email}>
193                                             {address.Email}
194                                         </div>,
195                                         <AddressStatus key={1} {...addressStatuses} />,
196                                         <AddressActions
197                                             key={2}
198                                             address={address}
199                                             member={member}
200                                             user={user}
201                                             onSetDefault={setDefaultAddress(i)}
202                                             savingIndex={savingIndex}
203                                             addressIndex={i}
204                                             permissions={getPermissions({
205                                                 addressIndex: i,
206                                                 member,
207                                                 address,
208                                                 addresses: list,
209                                                 user,
210                                                 organizationKey,
211                                             })}
212                                             allowAddressDeletion={allowAddressDeletion}
213                                         />,
214                                     ]}
215                                 />
216                             );
217                         })}
218                 </OrderableTableBody>
219             </OrderableTable>
221             {renderUpsellModal && modal}
222         </>
223     );
226 export default AddressesUser;