Remove payments API routing initialization
[ProtonMail-WebClient.git] / packages / components / containers / addresses / helper.ts
blobb6936ec4ffedc2354a297bb24992cf0572e78862
1 import { c } from 'ttag';
3 import {
4     ADDRESS_FLAGS,
5     ADDRESS_PERMISSIONS,
6     ADDRESS_PERMISSION_TYPE,
7     ADDRESS_RECEIVE,
8     ADDRESS_STATUS,
9     ADDRESS_TYPE,
10     MEMBER_PRIVATE,
11     MEMBER_TYPE,
12 } from '@proton/shared/lib/constants';
13 import { hasBit } from '@proton/shared/lib/helpers/bitset';
14 import type {
15     Address,
16     CachedOrganizationKey,
17     Member,
18     PartialMemberAddress,
19     UserModel,
20 } from '@proton/shared/lib/interfaces';
21 import { AddressConfirmationState } from '@proton/shared/lib/interfaces';
22 import { getCanGenerateMemberAddressKeys } from '@proton/shared/lib/keys/memberKeys';
23 import { getIsNonDefault } from '@proton/shared/lib/mail/addresses';
25 const { TYPE_ORIGINAL, TYPE_CUSTOM_DOMAIN, TYPE_PREMIUM } = ADDRESS_TYPE;
27 export const getStatus = (address: Address, i: number) => {
28     const { Type, Status, Receive, DomainID, HasKeys, Flags } = address;
30     const isActive = Status === ADDRESS_STATUS.STATUS_ENABLED && Receive === ADDRESS_RECEIVE.RECEIVE_YES;
31     const isDisabled = Status === ADDRESS_STATUS.STATUS_DISABLED;
32     const isExternal = Type === ADDRESS_TYPE.TYPE_EXTERNAL;
33     const isOrphan = DomainID === null && !isExternal;
34     const isMissingKeys = !HasKeys;
35     const isNotEncrypted = hasBit(Flags, ADDRESS_FLAGS.FLAG_DISABLE_E2EE);
36     const isSignatureNotExpected = hasBit(Flags, ADDRESS_FLAGS.FLAG_DISABLE_EXPECTED_SIGNED);
38     return {
39         isDefault: i === 0,
40         isActive,
41         isExternal,
42         isDisabled,
43         isOrphan,
44         isMissingKeys,
45         isNotEncrypted,
46         isSignatureNotExpected,
47     };
50 export const getPermissions = ({
51     member,
52     address,
53     addresses,
54     user,
55     organizationKey,
56     addressIndex,
57 }: {
58     addressIndex: number;
59     member?: Member;
60     address: Address;
61     addresses: PartialMemberAddress[];
62     user: UserModel;
63     organizationKey?: CachedOrganizationKey;
64 }) => {
65     const { isAdmin, canPay } = user;
66     const { ID, Status, Type, Priority, ConfirmationState } = address;
68     const isSpecialAddress = Type === TYPE_ORIGINAL || Type === TYPE_PREMIUM;
70     const isSelf = !member || !!member.Self;
71     const isDefault = addressIndex === 0;
72     const isEnabled = Status === ADDRESS_STATUS.STATUS_ENABLED;
73     const isExternal = Type === ADDRESS_TYPE.TYPE_EXTERNAL;
75     const canMakeDefault = !isDefault && !getIsNonDefault(address);
77     const canGenerateMemberAddressKeys = getCanGenerateMemberAddressKeys({ user, member, organizationKey, address });
78     /*
79      * Even though the user in question regarding the permissions here might be
80      * the currently logged in user itself (isSelf), it's possible that they don't
81      * have the necessary permission to generate their own missing keys. This is
82      * the case if the currently logged in user is a member of an org of which they
83      * are not an admin of.
84      */
85     const canGenerateSelfAddressKeys = isSelf && user.Private === MEMBER_PRIVATE.UNREADABLE && !address.HasKeys;
86     const canGenerate = canGenerateMemberAddressKeys || canGenerateSelfAddressKeys;
88     let canDisable = isEnabled && isAdmin && !isSpecialAddress && !isExternal;
90     const isManagedUser = member?.Type === MEMBER_TYPE.MANAGED;
91     if (isManagedUser) {
92         const hasOtherEnabledAddress = addresses.some(
93             (otherAddress) => otherAddress.ID !== ID && otherAddress.Status === ADDRESS_STATUS.STATUS_ENABLED
94         );
95         // Accounts for: 'Cannot disable your only enabled address. Please add another address first'
96         if (!hasOtherEnabledAddress && isSelf) {
97             canDisable = false;
98         }
99         // Accounts for: 'Cannot disable your default address. Please make another address default first'
100         if (hasOtherEnabledAddress && Priority === 1) {
101             canDisable = false;
102         }
103     }
105     const canEditInternalAddress = Type !== ADDRESS_TYPE.TYPE_EXTERNAL && isSelf;
106     const canEditExternalAddress =
107         Type === ADDRESS_TYPE.TYPE_EXTERNAL &&
108         ConfirmationState !== AddressConfirmationState.CONFIRMATION_CONFIRMED &&
109         isSelf;
111     // Takes into account disabling permissions since it does that automatically. canPay to simulate the "payments" scope for delete route.
112     const adminCanDeleteCustom = ((isEnabled && canDisable) || !isEnabled) && Type === TYPE_CUSTOM_DOMAIN && canPay;
114     return {
115         canMakeDefault,
116         canGenerate,
117         canEditInternalAddress,
118         canEditExternalAddress,
119         canDisable,
120         canEnable: Status === ADDRESS_STATUS.STATUS_DISABLED && isAdmin && !isSpecialAddress,
121         canDeleteAddress: adminCanDeleteCustom,
122         canDeleteAddressOncePerYear: !adminCanDeleteCustom && isAdmin && !isSpecialAddress && !isExternal && !isDefault,
123         canEdit: isSelf,
124     };
127 export type AddressPermissions = ReturnType<typeof getPermissions>;
128 export type AddressStatuses = ReturnType<typeof getStatus>;
130 export interface AddressWithMemberID extends PartialMemberAddress {
131     MemberID: string;
132     MemberName?: string;
135 export interface MembersMap {
136     [MemberID: string]: Member;
139 export interface SwitchAddressPermissionMultiResponses {
140     Responses: SwitchAddressPermissionResponse[];
143 export interface SwitchAddressPermissionResponse {
144     AddressID: string;
145     Response: {
146         Code: number;
147     };
150 export interface PermissionOption {
151     text: string;
152     value: ADDRESS_PERMISSIONS;
153     testid: string;
156 export const canReceive = (permissions: number): boolean => {
157     return (
158         hasBit(permissions, ADDRESS_PERMISSIONS.PERMISSIONS_RECEIVE_ALL) ||
159         hasBit(permissions, ADDRESS_PERMISSIONS.PERMISSIONS_RECEIVE_ORG)
160     );
163 export const canSend = (permissions: number): boolean => {
164     return (
165         hasBit(permissions, ADDRESS_PERMISSIONS.PERMISSIONS_SEND_ALL) ||
166         hasBit(permissions, ADDRESS_PERMISSIONS.PERMISSIONS_SEND_ORG)
167     );
170 export const hasIncompleteSetup = (permissions: number): boolean => {
171     return !canReceive(permissions) && !canSend(permissions);
174 export const noPermissionMap = (): PermissionOption[] => {
175     return [
176         {
177             text: c('Option').t`No permission`,
178             value: ADDRESS_PERMISSIONS.NO_PERMISSION,
179             testid: 'permission-map:none',
180         },
181     ];
184 export const setupIncompletePermissionMap = (): PermissionOption[] => {
185     return [
186         {
187             text: c('Option').t`Setup incomplete`,
188             value: ADDRESS_PERMISSIONS.NO_PERMISSION,
189             testid: 'permission-map:setup-incomplete',
190         },
191     ];
194 export const permissionsReceiveMap = (): PermissionOption[] => {
195     return [
196         {
197             text: c('Option').t`Receive from all`,
198             value: ADDRESS_PERMISSIONS.PERMISSIONS_RECEIVE_ALL,
199             testid: 'permission-dropdown:receive-all',
200         },
201         {
202             text: c('Option').t`Organization only`,
203             value: ADDRESS_PERMISSIONS.PERMISSIONS_RECEIVE_ORG,
204             testid: 'permission-dropdown:receive-org',
205         },
206     ];
209 export const permissionsSendMap = (): PermissionOption[] => {
210     return [
211         {
212             text: c('Option').t`Send to all`,
213             value: ADDRESS_PERMISSIONS.PERMISSIONS_SEND_ALL,
214             testid: 'permission-dropdown:send-all',
215         },
216         {
217             text: c('Option').t`Organization only`,
218             value: ADDRESS_PERMISSIONS.PERMISSIONS_SEND_ORG,
219             testid: 'permission-dropdown:send-org',
220         },
221     ];
224 export const getReceivePermission = (permissions: number) => {
225     if (hasBit(permissions, ADDRESS_PERMISSIONS.PERMISSIONS_RECEIVE_ALL)) {
226         return ADDRESS_PERMISSIONS.PERMISSIONS_RECEIVE_ALL;
227     }
228     if (hasBit(permissions, ADDRESS_PERMISSIONS.PERMISSIONS_RECEIVE_ORG)) {
229         return ADDRESS_PERMISSIONS.PERMISSIONS_RECEIVE_ORG;
230     }
231     return ADDRESS_PERMISSIONS.NO_PERMISSION;
234 export const getSendPermission = (permissions: number) => {
235     if (hasBit(permissions, ADDRESS_PERMISSIONS.PERMISSIONS_SEND_ALL)) {
236         return ADDRESS_PERMISSIONS.PERMISSIONS_SEND_ALL;
237     }
238     if (hasBit(permissions, ADDRESS_PERMISSIONS.PERMISSIONS_SEND_ORG)) {
239         return ADDRESS_PERMISSIONS.PERMISSIONS_SEND_ORG;
240     }
241     return ADDRESS_PERMISSIONS.NO_PERMISSION;
244 export const permissionsMap = (addressPermissions: number | undefined, type: ADDRESS_PERMISSION_TYPE) => {
245     if (addressPermissions && !hasIncompleteSetup(addressPermissions)) {
246         if (
247             type === ADDRESS_PERMISSION_TYPE.RECEIVE &&
248             getReceivePermission(addressPermissions) !== ADDRESS_PERMISSIONS.NO_PERMISSION
249         ) {
250             return permissionsReceiveMap();
251         }
252         if (
253             type === ADDRESS_PERMISSION_TYPE.SEND &&
254             getSendPermission(addressPermissions) !== ADDRESS_PERMISSIONS.NO_PERMISSION
255         ) {
256             return permissionsSendMap();
257         }
258     }
259     if (!addressPermissions || hasIncompleteSetup(addressPermissions)) {
260         return setupIncompletePermissionMap();
261     }
262     return noPermissionMap();
265 export const getPermission = (permissions: number, type: ADDRESS_PERMISSION_TYPE): string => {
266     if (type === ADDRESS_PERMISSION_TYPE.RECEIVE) {
267         if (hasBit(permissions, ADDRESS_PERMISSIONS.PERMISSIONS_RECEIVE_ALL)) {
268             return c('Permission').t`Receive from all`;
269         }
270         if (hasBit(permissions, ADDRESS_PERMISSIONS.PERMISSIONS_RECEIVE_ORG)) {
271             return c('Permission').t`Organization only`;
272         }
273         if (hasIncompleteSetup(permissions)) {
274             return c('Permission').t`Setup incomplete`;
275         }
276     } else {
277         if (hasBit(permissions, ADDRESS_PERMISSIONS.PERMISSIONS_SEND_ALL)) {
278             return c('Permission').t`Send to all`;
279         }
280         if (hasBit(permissions, ADDRESS_PERMISSIONS.PERMISSIONS_SEND_ORG)) {
281             return c('Permission').t`Organization only`;
282         }
283         if (hasIncompleteSetup(permissions)) {
284             return c('Permission').t`Setup incomplete`;
285         }
286     }
288     return c('Permission').t`No permission`;