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 { NewUpsellModal, useUpsellConfig } from '@proton/components';
8 import Alert from '@proton/components/components/alert/Alert';
9 import useModalState from '@proton/components/components/modalTwo/useModalState';
10 import OrderableTable from '@proton/components/components/orderableTable/OrderableTable';
11 import OrderableTableBody from '@proton/components/components/orderableTable/OrderableTableBody';
12 import OrderableTableHeader from '@proton/components/components/orderableTable/OrderableTableHeader';
13 import OrderableTableRow from '@proton/components/components/orderableTable/OrderableTableRow';
14 import MailUpsellButton from '@proton/components/components/upsell/MailUpsellButton';
15 import UpsellModal from '@proton/components/components/upsell/modal/UpsellModal';
16 import useOneDollarConfig from '@proton/components/components/upsell/useOneDollarPromo';
17 import SettingsParagraph from '@proton/components/containers/account/SettingsParagraph';
18 import useApi from '@proton/components/hooks/useApi';
19 import useEventManager from '@proton/components/hooks/useEventManager';
20 import useNotifications from '@proton/components/hooks/useNotifications';
21 import { orderAddress } from '@proton/shared/lib/api/addresses';
22 import { APP_UPSELL_REF_PATH, BRAND_NAME, MAIL_UPSELL_PATHS, UPSELL_COMPONENT } from '@proton/shared/lib/constants';
23 import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
24 import { getUpsellRef, useNewUpsellModalVariant } from '@proton/shared/lib/helpers/upsell';
25 import { getKnowledgeBaseUrl } from '@proton/shared/lib/helpers/url';
26 import type { Address, CachedOrganizationKey, Member, UserModel } from '@proton/shared/lib/interfaces';
27 import { getIsNonDefault, sortAddresses } from '@proton/shared/lib/mail/addresses';
28 import addressesImg from '@proton/styles/assets/img/illustrations/new-upsells-img/addresses.svg';
29 import move from '@proton/utils/move';
31 import AddressActions from './AddressActions';
32 import AddressStatus from './AddressStatus';
33 import { getPermissions, getStatus } from './helper';
38 organizationKey?: CachedOrganizationKey;
39 hasDescription?: boolean;
40 allowAddressDeletion: boolean;
43 const AddressesUser = ({ user, organizationKey, member, hasDescription = true, allowAddressDeletion }: Props) => {
45 const { createNotification } = useNotifications();
46 const [savingIndex, setSavingIndex] = useState<number | undefined>();
47 const { call } = useEventManager();
48 const [addresses, loadingAddresses] = useAddresses();
49 const [list, setAddresses] = useState<Address[]>(() => sortAddresses(addresses || []));
51 const upsellRef = getUpsellRef({
52 app: APP_UPSELL_REF_PATH.MAIL_UPSELL_REF_PATH,
53 component: UPSELL_COMPONENT.MODAL,
54 feature: MAIL_UPSELL_PATHS.UNLIMITED_ADDRESSES,
58 const oneDollarConfig = useOneDollarConfig();
59 const upsellConfig = useUpsellConfig({ upsellRef, ...oneDollarConfig });
61 const displayNewUpsellModalsVariant = useNewUpsellModalVariant();
63 const [upsellModalProps, handleUpsellModalDisplay, renderUpsellModal] = useModalState();
67 setAddresses(sortAddresses(addresses));
71 const handleSortEnd = useCallback(
72 async ({ oldIndex, newIndex }: { oldIndex: number; newIndex: number }) => {
74 const newList = move(list, oldIndex, newIndex);
75 const { isExternal, isDisabled } = getStatus(newList[0], 0);
77 if (isDeepEqual(list, newList)) {
81 if (newIndex === 0 && (isDisabled || isExternal)) {
82 const errorMessage = (() => {
84 return c('Notification').t`A disabled address cannot be default`;
87 return c('Notification').t`An external address cannot be default`;
96 setAddresses(sortAddresses(addresses));
100 setAddresses(newList);
101 setSavingIndex(newIndex);
103 await api(orderAddress(newList.map(({ ID }) => ID)));
106 setSavingIndex(undefined);
108 setSavingIndex(undefined);
109 setAddresses(sortAddresses(addresses));
115 const setDefaultAddress = useCallback(
116 (addressOldIndex: number) => {
118 await handleSortEnd({ oldIndex: addressOldIndex, newIndex: 0 });
124 if (!loadingAddresses && !addresses?.length) {
125 return <Alert className="mb-4">{c('Info').t`No addresses exist`}</Alert>;
128 const modal = displayNewUpsellModalsVariant ? (
130 titleModal={c('Title').t`An address for each role`}
131 description={c('Description')
132 .t`Keep different parts of your life separate and your inbox organized with additional addresses.`}
133 modalProps={upsellModalProps}
134 illustration={addressesImg}
135 sourceEvent="BUTTON_MORE_ADDRESSES"
140 title={c('Title').t`Increase your privacy with more addresses`}
141 description={c('Description')
142 .t`Separate different aspects of your life with multiple email addresses and unlock more premium features when you upgrade.`}
143 modalProps={upsellModalProps}
144 sourceEvent="BUTTON_MORE_ADDRESSES"
145 features={['more-storage', 'more-email-addresses', 'unlimited-folders-and-labels', 'custom-email-domains']}
153 <SettingsParagraph className="mt-2">
155 {c('Info').t`Use the different types of email addresses and aliases offered by ${BRAND_NAME}.`}
158 <Href href={getKnowledgeBaseUrl('/addresses-and-aliases')}>{c('Link').t`Learn more`}</Href>
162 {!user.hasPaidMail && (
164 onClick={() => handleUpsellModalDisplay(true)}
165 text={c('Action').t`Get more addresses`}
170 onSortEnd={handleSortEnd}
171 className="simple-table--has-actions mt-4"
172 helperClassname="simple-table--has-actions"
174 <OrderableTableHeader
176 c('Header for addresses table').t`Address`,
177 c('Header for addresses table').t`Status`,
178 c('Header for addresses table').t`Actions`,
181 <OrderableTableBody colSpan={3} loading={loadingAddresses}>
183 list.map((address, i) => {
184 const addressStatuses = getStatus(address, i);
187 disableSort={getIsNonDefault(address)}
191 <div key={0} className="text-ellipsis" title={address.Email}>
194 <AddressStatus key={1} {...addressStatuses} />,
200 onSetDefault={setDefaultAddress(i)}
201 savingIndex={savingIndex}
203 permissions={getPermissions({
211 allowAddressDeletion={allowAddressDeletion}
217 </OrderableTableBody>
220 {renderUpsellModal && modal}
225 export default AddressesUser;