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';
27 member?: Member; // undefined if self
29 onSetDefault?: () => Promise<unknown>;
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) {
46 const { allowDisablingEncryption, encryptionDisabled, expectSignatureDisabled, handleSetAddressFlags } =
51 if (!encryptionDisabled && allowDisablingEncryption) {
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),
59 if (encryptionDisabled) {
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),
67 if (expectSignatureDisabled) {
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),
78 const AddressActions = ({
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));
107 await api(deleteAddress(address.ID));
109 createNotification({ text: c('Success notification').t`Address deleted` });
112 const handleDeleteOncePerYear = async () => {
113 await handleDelete();
114 dispatch(disableAllowAddressDeletion());
117 const handleEnable = async () => {
118 await api(enableAddress(address.ID));
120 createNotification({ text: c('Success notification').t`Address enabled` });
123 const handleDisable = async () => {
124 await api(disableAddress(address.ID));
126 createNotification({ text: c('Success notification').t`Address disabled` });
129 const mustActivateOrganizationKey = member?.Private === MEMBER_PRIVATE.READABLE && !organizationKey?.privateKey;
132 savingIndex !== undefined
133 ? [savingIndex === addressIndex ? { text: c('Address action').t`Saving` } : null].filter(isTruthy)
135 permissions.canGenerate && {
136 text: c('Address action').t`Generate missing keys`,
137 onClick: () => setMissingKeysAddressModalOpen(true),
139 permissions.canEditInternalAddress && {
140 text: c('Address action').t`Edit`,
141 onClick: () => setEditInternalAddressOpen(true),
143 permissions.canEditExternalAddress && {
144 text: c('Address action').t`Edit address`,
145 onClick: () => setEditExternalAddressOpen(true),
147 permissions.canMakeDefault &&
149 text: c('Address action').t`Set as default`,
150 onClick: () => onSetDefault(),
152 permissions.canEnable && {
153 text: c('Address action').t`Enable`,
154 onClick: () => withLoading(handleEnable()),
156 permissions.canDisable && {
157 text: c('Address action').t`Disable`,
158 onClick: () => setDisableAddressModalOpen(true),
160 permissions.canDeleteAddress &&
162 text: c('Address action').t`Delete address`,
163 actionType: 'delete',
164 onClick: () => setDeleteAddressModalOpen(true),
166 permissions.canDeleteAddressOncePerYear &&
167 !mustActivateOrganizationKey &&
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,
178 ...addressFlagsActionsList,
183 {renderMissingKeysModal && (
184 <CreateMissingKeysAddressModal {...missingKeysProps} member={member} addressesToGenerate={[address]} />
186 {renderEditInternalAddressModal && (
187 <EditInternalAddressModal address={address} {...editInternalAddressProps} />
189 {renderEditExternalAddressModal && (
190 <EditExternalAddressModal address={address} {...editExternalAddressProps} />
192 {renderDeleteAddressPrompt && (
194 onDeleteAddress={handleDeleteOncePerYear}
195 {...deleteAddressPromptProps}
196 email={address.Email}
200 {renderDeleteAddressModal && (
202 onDeleteAddress={handleDelete}
203 {...deleteAddressModalProps}
204 email={address.Email}
207 {renderDisableAddress && (
208 <DisableAddressModal email={address.Email} onDisable={handleDisable} {...disableAddressProps} />
212 <DropdownActions size="small" list={list} loading={loading || savingIndex !== undefined} />
215 // This is a placeholder to avoid height loss when dropdownActions are not rendered
216 style={{ height: '24px' }}
223 export default AddressActions;