1 import { c } from 'ttag';
3 import { useGetAddressKeys } from '@proton/account/addressKeys/hooks';
4 import { useGetUser } from '@proton/account/user/hooks';
5 import { useGetUserKeys } from '@proton/account/userKeys/hooks';
6 import DropdownActions from '@proton/components/components/dropdown/DropdownActions';
7 import useKTVerifier from '@proton/components/containers/keyTransparency/useKTVerifier';
8 import useApi from '@proton/components/hooks/useApi';
9 import useEventManager from '@proton/components/hooks/useEventManager';
10 import useNotifications from '@proton/components/hooks/useNotifications';
11 import useLoading from '@proton/hooks/useLoading';
12 import { deleteForwarding, rejectForwarding } from '@proton/shared/lib/api/forwardings';
13 import { replaceAddressTokens } from '@proton/shared/lib/api/keys';
14 import type { Address, IncomingAddressForwarding } from '@proton/shared/lib/interfaces';
15 import { ForwardingState } from '@proton/shared/lib/interfaces';
16 import { getHasMigratedAddressKeys, getReplacedAddressKeyTokens, splitKeys } from '@proton/shared/lib/keys';
17 import isTruthy from '@proton/utils/isTruthy';
19 import useVerifyOutboundPublicKeys from '../keyTransparency/useVerifyOutboundPublicKeys';
20 import { acceptIncomingForwarding } from './helpers';
23 forward: IncomingAddressForwarding;
27 const IncomingForwardActions = ({ forward, addresses }: Props) => {
29 const [loading, withLoading] = useLoading();
30 const { call } = useEventManager();
31 const { createNotification } = useNotifications();
32 const getUserKeys = useGetUserKeys();
33 const getUser = useGetUser();
34 const verifyOutboundPublicKeys = useVerifyOutboundPublicKeys();
35 const silentApi = <T,>(config: any) => api<T>({ ...config, silence: true });
36 const { keyTransparencyVerify, keyTransparencyCommit } = useKTVerifier(silentApi, getUser);
37 const address = addresses.find(({ ID }) => ID === forward.ForwardeeAddressID);
38 const getAddressKeys = useGetAddressKeys();
39 const isPending = forward.State === ForwardingState.Pending;
40 const isActive = forward.State === ForwardingState.Active;
41 const isRejected = forward.State === ForwardingState.Rejected;
42 const hasForwardingKeys = !!forward.ForwardingKeys?.length;
44 const handleAccept = async () => {
46 throw new Error('No address');
49 const userKeys = await getUserKeys();
51 if (getHasMigratedAddressKeys(addresses) && userKeys.length > 1) {
52 // The token is validated with the primary user key, and this is to ensure that the address tokens are encrypted to the primary user key.
53 // NOTE: Reencrypting address token happens automatically when generating a new user key, but there are users who generated user keys before that functionality existed.
54 const primaryUserKey = userKeys[0].privateKey;
55 const splitUserKeys = splitKeys(userKeys);
56 const replacedResult = await getReplacedAddressKeyTokens({
58 privateKeys: splitUserKeys.privateKeys,
59 privateKey: primaryUserKey,
61 if (replacedResult.AddressKeyTokens.length) {
62 await api(replaceAddressTokens(replacedResult));
67 const addressKeys = await getAddressKeys(address.ID);
69 await acceptIncomingForwarding({
75 keyTransparencyVerify,
76 keyTransparencyCommit,
77 verifyOutboundPublicKeys,
80 createNotification({ text: c('email_forwarding_2023: Success').t`Forwarding accepted` });
83 const handleDecline = async () => {
84 await api(rejectForwarding(forward.ID));
86 createNotification({ text: c('email_forwarding_2023: Success').t`Forwarding declined` });
89 const handleDelete = async () => {
90 await api(deleteForwarding(forward.ID));
92 createNotification({ text: c('email_forwarding_2023: Success').t`Forwarding deleted` });
98 hasForwardingKeys && {
99 text: c('email_forwarding_2023: Action').t`Accept`,
101 void withLoading(handleAccept());
105 text: c('email_forwarding_2023: Action').t`Decline`,
106 onClick: async () => {
107 void withLoading(handleDecline());
110 (isActive || isRejected) && {
111 text: c('email_forwarding_2023: Action').t`Delete`,
112 onClick: async () => {
113 void withLoading(handleDelete());
118 return <DropdownActions list={list} size="small" loading={loading} />;
121 export default IncomingForwardActions;