Remove payments API routing initialization
[ProtonMail-WebClient.git] / packages / components / containers / addresses / AddressActions.tsx
blob7bd3d51ecafb83ad9b868d25b4e50b37e932fb74
1 import { c } from 'ttag';
3 import { disableAllowAddressDeletion } from '@proton/account';
4 import { useOrganizationKey } from '@proton/account/organizationKey/hooks';
5 import DropdownActions from '@proton/components/components/dropdown/DropdownActions';
6 import useModalState from '@proton/components/components/modalTwo/useModalState';
7 import useApi from '@proton/components/hooks/useApi';
8 import useEventManager from '@proton/components/hooks/useEventManager';
9 import useNotifications from '@proton/components/hooks/useNotifications';
10 import { useLoading } from '@proton/hooks';
11 import { useDispatch } from '@proton/redux-shared-store';
12 import { deleteAddress, disableAddress, enableAddress } from '@proton/shared/lib/api/addresses';
13 import { ADDRESS_STATUS, MEMBER_PRIVATE } from '@proton/shared/lib/constants';
14 import type { Address, Member, UserModel } from '@proton/shared/lib/interfaces';
15 import isTruthy from '@proton/utils/isTruthy';
17 import EditExternalAddressModal from '../../containers/account/EditExternalAddressModal';
18 import EditInternalAddressModal from '../../containers/addresses/EditInternalAddressModal';
19 import useAddressFlags from '../../hooks/useAddressFlags';
20 import DeleteAddressPrompt from './DeleteAddressPrompt';
21 import DisableAddressModal from './DisableAddressModal';
22 import type { AddressPermissions } from './helper';
23 import CreateMissingKeysAddressModal from './missingKeys/CreateMissingKeysAddressModal';
25 interface Props {
26     address: Address;
27     member?: Member; // undefined if self
28     user: UserModel;
29     onSetDefault?: () => Promise<unknown>;
30     savingIndex?: number;
31     addressIndex?: number;
32     permissions: AddressPermissions;
33     allowAddressDeletion: boolean;
36 const useAddressFlagsActionsList = (address: Address, user: UserModel, member: Member | undefined) => {
37     const addressFlags = useAddressFlags(address);
38     const isPaidMail = user.hasPaidMail;
39     // Only allow on the user's own settings address list, not in org admin management panel.
40     // This still allows an admin logged in as sub-user to manage the preferences.
41     const isSelf = member === undefined || !!member.Self;
42     if (!addressFlags || !isPaidMail || !isSelf) {
43         return [];
44     }
46     const { allowDisablingEncryption, encryptionDisabled, expectSignatureDisabled, handleSetAddressFlags } =
47         addressFlags;
49     const actions = [];
51     if (!encryptionDisabled && allowDisablingEncryption) {
52         actions.push({
53             // translator: this is in a small space, so the string should be short, max 25 characters
54             text: c('Address action').t`Disable E2EE mail`,
55             onClick: () => handleSetAddressFlags(true, expectSignatureDisabled),
56         });
57     }
59     if (encryptionDisabled) {
60         actions.push({
61             // translator: this is in a small space, so the string should be short, max 25 characters
62             text: c('Address action').t`Enable E2EE mail`,
63             onClick: () => handleSetAddressFlags(false, expectSignatureDisabled),
64         });
65     }
67     if (expectSignatureDisabled) {
68         actions.push({
69             // translator: this is in a small space, so the string should be short, max 25 characters
70             text: c('Address action').t`Disallow unsigned mail`,
71             onClick: () => handleSetAddressFlags(encryptionDisabled, false),
72         });
73     }
75     return actions;
78 const AddressActions = ({
79     address,
80     member,
81     user,
82     onSetDefault,
83     savingIndex,
84     addressIndex,
85     permissions,
86     allowAddressDeletion,
87 }: Props) => {
88     const api = useApi();
89     const { call } = useEventManager();
90     const [loading, withLoading] = useLoading();
91     const { createNotification } = useNotifications();
92     const addressFlagsActionsList = useAddressFlagsActionsList(address, user, member);
93     const dispatch = useDispatch();
94     const [organizationKey] = useOrganizationKey();
96     const [missingKeysProps, setMissingKeysAddressModalOpen, renderMissingKeysModal] = useModalState();
97     const [deleteAddressPromptProps, setDeleteAddressPromptOpen, renderDeleteAddressPrompt] = useModalState();
98     const [deleteAddressModalProps, setDeleteAddressModalOpen, renderDeleteAddressModal] = useModalState();
99     const [disableAddressProps, setDisableAddressModalOpen, renderDisableAddress] = useModalState();
100     const [editInternalAddressProps, setEditInternalAddressOpen, renderEditInternalAddressModal] = useModalState();
101     const [editExternalAddressProps, setEditExternalAddressOpen, renderEditExternalAddressModal] = useModalState();
103     const handleDelete = async () => {
104         if (address.Status === ADDRESS_STATUS.STATUS_ENABLED) {
105             await api(disableAddress(address.ID));
106         }
107         await api(deleteAddress(address.ID));
108         await call();
109         createNotification({ text: c('Success notification').t`Address deleted` });
110     };
112     const handleDeleteOncePerYear = async () => {
113         await handleDelete();
114         dispatch(disableAllowAddressDeletion());
115     };
117     const handleEnable = async () => {
118         await api(enableAddress(address.ID));
119         await call();
120         createNotification({ text: c('Success notification').t`Address enabled` });
121     };
123     const handleDisable = async () => {
124         await api(disableAddress(address.ID));
125         await call();
126         createNotification({ text: c('Success notification').t`Address disabled` });
127     };
129     const mustActivateOrganizationKey = member?.Private === MEMBER_PRIVATE.READABLE && !organizationKey?.privateKey;
131     const list =
132         savingIndex !== undefined
133             ? [savingIndex === addressIndex ? { text: c('Address action').t`Saving` } : null].filter(isTruthy)
134             : [
135                   permissions.canGenerate && {
136                       text: c('Address action').t`Generate missing keys`,
137                       onClick: () => setMissingKeysAddressModalOpen(true),
138                   },
139                   permissions.canEditInternalAddress && {
140                       text: c('Address action').t`Edit`,
141                       onClick: () => setEditInternalAddressOpen(true),
142                   },
143                   permissions.canEditExternalAddress && {
144                       text: c('Address action').t`Edit address`,
145                       onClick: () => setEditExternalAddressOpen(true),
146                   },
147                   permissions.canMakeDefault &&
148                       onSetDefault && {
149                           text: c('Address action').t`Set as default`,
150                           onClick: () => onSetDefault(),
151                       },
152                   permissions.canEnable && {
153                       text: c('Address action').t`Enable`,
154                       onClick: () => withLoading(handleEnable()),
155                   },
156                   permissions.canDisable && {
157                       text: c('Address action').t`Disable`,
158                       onClick: () => setDisableAddressModalOpen(true),
159                   },
160                   permissions.canDeleteAddress &&
161                       ({
162                           text: c('Address action').t`Delete address`,
163                           actionType: 'delete',
164                           onClick: () => setDeleteAddressModalOpen(true),
165                       } as const),
166                   permissions.canDeleteAddressOncePerYear &&
167                       !mustActivateOrganizationKey &&
168                       ({
169                           text: c('Address action').t`Delete address`,
170                           actionType: 'delete',
171                           onClick: () => setDeleteAddressPromptOpen(true),
172                           tooltip: allowAddressDeletion
173                               ? c('Delete address tooltip').t`You can only delete 1 address per year`
174                               : c('Delete address tooltip')
175                                     .t`You've reached the limit of address deletions for this user.`,
176                           disabled: !allowAddressDeletion,
177                       } as const),
178                   ...addressFlagsActionsList,
179               ].filter(isTruthy);
181     return (
182         <>
183             {renderMissingKeysModal && (
184                 <CreateMissingKeysAddressModal {...missingKeysProps} member={member} addressesToGenerate={[address]} />
185             )}
186             {renderEditInternalAddressModal && (
187                 <EditInternalAddressModal address={address} {...editInternalAddressProps} />
188             )}
189             {renderEditExternalAddressModal && (
190                 <EditExternalAddressModal address={address} {...editExternalAddressProps} />
191             )}
192             {renderDeleteAddressPrompt && (
193                 <DeleteAddressPrompt
194                     onDeleteAddress={handleDeleteOncePerYear}
195                     {...deleteAddressPromptProps}
196                     email={address.Email}
197                     type="permanent"
198                 />
199             )}
200             {renderDeleteAddressModal && (
201                 <DeleteAddressPrompt
202                     onDeleteAddress={handleDelete}
203                     {...deleteAddressModalProps}
204                     email={address.Email}
205                 />
206             )}
207             {renderDisableAddress && (
208                 <DisableAddressModal email={address.Email} onDisable={handleDisable} {...disableAddressProps} />
209             )}
211             {list.length ? (
212                 <DropdownActions size="small" list={list} loading={loading || savingIndex !== undefined} />
213             ) : (
214                 <div
215                     // This is a placeholder to avoid height loss when dropdownActions are not rendered
216                     style={{ height: '24px' }}
217                 />
218             )}
219         </>
220     );
223 export default AddressActions;