Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Domains: Implement multi-target email forwards #98837

Merged
merged 52 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
7c135cb
Implement multi email forwards
alshakero Jan 23, 2025
3e50f1b
Add most of the needed UI
alshakero Jan 24, 2025
6c0b00d
Switch to `new`
alshakero Jan 25, 2025
81da338
Fix email section in Upgrades > Email section
alshakero Jan 27, 2025
d0896ba
CSS
alshakero Jan 27, 2025
7f4a623
Handle duplicates
alshakero Jan 27, 2025
3537d35
Limit globally
alshakero Jan 28, 2025
24b1c4a
Fix duplication
alshakero Jan 28, 2025
07b7cb1
Fix optimistic UI mutation
alshakero Jan 28, 2025
86a706c
Fix destination encoding
alshakero Jan 28, 2025
b1738ab
Merge branch 'trunk' into implement/mutliple-email-forwards
alshakero Jan 28, 2025
6f307e2
Bring back TS comment
alshakero Jan 28, 2025
7c80149
Address feedback
alshakero Jan 28, 2025
9dc7496
Code comment
alshakero Jan 28, 2025
f97f1a8
Merge branch 'trunk' into implement/mutliple-email-forwards
alshakero Jan 28, 2025
b32f5b6
Fix email context
alshakero Jan 28, 2025
9c5db5c
Unify design amongst all contexts
alshakero Jan 28, 2025
fe82dbf
More unification
alshakero Jan 28, 2025
ac8086a
Implement new input form
alshakero Jan 29, 2025
808bd2c
Add padding top
alshakero Jan 29, 2025
bc22633
Implement forward list
alshakero Jan 30, 2025
cf4ce9b
Visual fixes
alshakero Jan 30, 2025
6e6d407
Merge branch 'trunk' into implement/mutliple-email-forwards
alshakero Jan 30, 2025
1937fb7
Don't allow @
alshakero Jan 30, 2025
77afbbe
UX improvements
alshakero Jan 30, 2025
17b6a29
Handle duplicates
alshakero Jan 30, 2025
881c7d2
Refactor
alshakero Jan 30, 2025
5a22366
Remove unneeded file
alshakero Jan 30, 2025
0492535
Fix typo
alshakero Jan 30, 2025
b9f56ec
Improve code comment
alshakero Jan 30, 2025
129e2b0
Remove unused code
alshakero Jan 30, 2025
da3b5f9
Remove more unused code
alshakero Jan 30, 2025
f35e394
Revert "Remove unneeded file"
alshakero Jan 30, 2025
061fb2f
Refactor
alshakero Jan 30, 2025
70ff428
Fix validation and stop when tired next time
alshakero Jan 31, 2025
a4001c4
Bring back context menus
alshakero Jan 31, 2025
562e9b5
Bring back context meu
alshakero Jan 31, 2025
e182922
Remove `resend` for active addresses
alshakero Jan 31, 2025
cb1934f
Add happy state
alshakero Jan 31, 2025
94631b6
Fix focus issue WP dialog
alshakero Jan 31, 2025
9e60359
Switch to WP dropdown for better acccessibility
alshakero Jan 31, 2025
ad48071
Don't show resend when there are no warnings
alshakero Jan 31, 2025
33bd2b2
Remove unused code
alshakero Feb 1, 2025
b479ac1
Spell check
alshakero Feb 3, 2025
34de258
Fix error handling
alshakero Feb 3, 2025
f618391
Dont show active during loading
alshakero Feb 3, 2025
e3ae16e
Mobile support
alshakero Feb 3, 2025
ecd72c1
Fine tuning the notices
alshakero Feb 3, 2025
bf77b82
Fix margins
alshakero Feb 3, 2025
4dcc99f
Merge branch 'trunk' into implement/mutliple-email-forwards
alshakero Feb 3, 2025
d9eeda2
Add track events
alshakero Feb 4, 2025
e7101c2
Merge branch 'trunk' into implement/mutliple-email-forwards
alshakero Feb 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions client/data/emails/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ export type EmailAccountEmail = {
warnings: Warning[];
};

export type AlterDestinationParams = {
mailbox: string;
destination: string;
domain: string;
};

type EmailAccountDomain = {
domain: string;
is_primary: boolean;
Expand Down Expand Up @@ -45,4 +51,5 @@ export type Mailbox = {
mailbox: string;
warnings?: Warning[];
temporary?: boolean;
target: string;
};
16 changes: 9 additions & 7 deletions client/data/emails/use-add-email-forward-mutation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import { getSelectedSiteId } from 'calypso/state/ui/selectors';
import { getCacheKey as getEmailAccountsQueryKey } from './use-get-email-accounts-query';
import type { UseMutationOptions } from '@tanstack/react-query';

const ArrayOfFive = new Array( 5 );

type AddMailboxFormData = {
destination: string;
destinations: typeof ArrayOfFive;
mailbox: string;
};

Expand Down Expand Up @@ -65,7 +67,7 @@ export default function useAddEmailForwardMutation(
};

mutationOptions.onMutate = async ( variables ) => {
const { mailbox, destination } = variables;
const { mailbox, destinations } = variables;
suppliedOnMutate?.( variables );

await queryClient.cancelQueries( { queryKey: emailAccountsQueryKey } );
Expand All @@ -79,16 +81,16 @@ export default function useAddEmailForwardMutation(
const newEmailForwards = orderBy(
[
...emailForwards,
{
...destinations.map( ( d ) => ( {
domain: domainName,
email_type: 'email_forward',
is_verified: false,
mailbox,
role: 'standard',
target: destination,
target: d,
temporary: true,
warnings: [],
},
} ) ),
],
[ 'mailbox' ],
[ 'asc' ]
Expand Down Expand Up @@ -179,10 +181,10 @@ export default function useAddEmailForwardMutation(
};

return useMutation< any, unknown, AddMailboxFormData, Context >( {
mutationFn: ( { mailbox, destination } ) =>
mutationFn: ( { mailbox, destinations } ) =>
wp.req.post( `/domains/${ encodeURIComponent( domainName ) }/email/new`, {
mailbox,
destination,
destinations,
} ),
...mutationOptions,
} );
Expand Down
21 changes: 13 additions & 8 deletions client/data/emails/use-remove-email-forward-mutation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { useDispatch, useSelector } from 'calypso/state';
import { errorNotice } from 'calypso/state/notices/actions';
import { getSelectedSiteId } from 'calypso/state/ui/selectors';
import { getCacheKey as getEmailAccountsQueryKey } from './use-get-email-accounts-query';
import type { EmailAccountEmail } from './types';
import type { EmailAccountEmail, AlterDestinationParams, Mailbox } from './types';
import type { UseMutationOptions } from '@tanstack/react-query';

type Context = {
Expand All @@ -26,7 +26,7 @@ const MUTATION_KEY = 'removeEmailForward';
export default function useRemoveEmailForwardMutation(
domainName: string,
mutationOptions: Omit<
UseMutationOptions< any, unknown, EmailAccountEmail, Context >,
UseMutationOptions< any, unknown, AlterDestinationParams, Context >,
'mutationFn'
> = {}
) {
Expand Down Expand Up @@ -70,7 +70,11 @@ export default function useRemoveEmailForwardMutation(
{
...previousEmailAccountsQueryData.accounts[ 0 ],
emails: emailForwards.filter(
( forward: EmailAccountEmail ) => forward.mailbox !== emailForward.mailbox
( forward: EmailAccountEmail ) =>
! (
forward.mailbox === emailForward.mailbox &&
forward.target === emailForward.destination
)
),
},
],
Expand Down Expand Up @@ -140,13 +144,14 @@ export default function useRemoveEmailForwardMutation(
dispatch( errorMessage );
};

return useMutation< any, unknown, EmailAccountEmail, Context >( {
mutationFn: ( { mailbox } ) =>
wp.req.post(
return useMutation< Mailbox, unknown, AlterDestinationParams, Context >( {
mutationFn: ( { mailbox, destination } ) => {
return wp.req.post(
`/domains/${ encodeURIComponent( domainName ) }/email/${ encodeURIComponent(
mailbox
) }/delete`
),
) }/${ encodeURIComponent( destination ) }/delete`
);
},
...mutationOptions,
} );
}
16 changes: 9 additions & 7 deletions client/data/emails/use-resend-verify-email-forward-mutation.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { CALYPSO_CONTACT } from '@automattic/urls';
import { useMutation } from '@tanstack/react-query';
import { useTranslate } from 'i18n-calypso';
import { getEmailForwardAddress } from 'calypso/lib/emails';
import wp from 'calypso/lib/wp';
import { useDispatch } from 'calypso/state';
import { errorNotice, successNotice } from 'calypso/state/notices/actions';
import type { EmailAccountEmail } from './types';
import type { AlterDestinationParams } from './types';
import type { UseMutationOptions } from '@tanstack/react-query';

const MUTATION_KEY = 'reverifyEmailForward';
Expand All @@ -18,7 +17,10 @@ const MUTATION_KEY = 'reverifyEmailForward';
*/
export default function useResendVerifyEmailForwardMutation(
domainName: string,
mutationOptions: Omit< UseMutationOptions< any, unknown, EmailAccountEmail >, 'mutationFn' > = {}
mutationOptions: Omit<
UseMutationOptions< any, unknown, AlterDestinationParams >,
'mutationFn'
> = {}
) {
const dispatch = useDispatch();
const translate = useTranslate();
Expand All @@ -31,7 +33,7 @@ export default function useResendVerifyEmailForwardMutation(
mutationOptions.onSuccess = ( data, emailForward, context ) => {
suppliedOnSuccess?.( data, emailForward, context );

const destination = getEmailForwardAddress( emailForward );
const { destination } = emailForward;

const successMessage = translate(
'Successfully sent confirmation email for %(email)s to %(destination)s.',
Expand Down Expand Up @@ -69,12 +71,12 @@ export default function useResendVerifyEmailForwardMutation(
dispatch( errorNotice( failureMessage ) );
};

return useMutation< any, unknown, EmailAccountEmail >( {
mutationFn: ( { mailbox } ) =>
return useMutation< any, unknown, AlterDestinationParams >( {
mutationFn: ( { mailbox, destination } ) =>
wp.req.post(
`/domains/${ encodeURIComponent( domainName ) }/email/${ encodeURIComponent(
mailbox
) }/resend-verification`
) }/${ encodeURIComponent( destination ) }/resend-verification`
),
...mutationOptions,
} );
Expand Down

This file was deleted.

35 changes: 0 additions & 35 deletions client/lib/domains/email-forwarding/index.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,3 @@
import emailValidator from 'email-validator';
import { mapValues } from 'lodash';
import { hasDuplicatedEmailForwards } from 'calypso/lib/domains/email-forwarding/has-duplicated-email-forwards';

function validateAllFields( fieldValues, existingEmailForwards = [] ) {
return mapValues( fieldValues, ( value, fieldName ) => {
const isValid = validateField( {
value,
name: fieldName,
} );

if ( ! isValid ) {
return [ 'Invalid' ];
}

if ( fieldName !== 'mailbox' ) {
return [];
}

return hasDuplicatedEmailForwards( value, existingEmailForwards ) ? [ 'Duplicated' ] : [];
} );
}

function validateField( { name, value } ) {
switch ( name ) {
case 'mailbox':
return /^[a-z0-9._+-]{1,64}$/i.test( value ) && ! /(^\.)|(\.{2,})|(\.$)/.test( value );
case 'destination':
return emailValidator.validate( value );
default:
return true;
}
}

export { getEmailForwardsCount } from './get-email-forwards-count';
export { hasEmailForwards } from './has-email-forwards';
export { getDomainsWithEmailForwards } from './get-domains-with-email-forwards';
export { validateAllFields };
80 changes: 80 additions & 0 deletions client/my-sites/email/email-forwarding/actions-menu/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { ConfirmationDialog } from '@automattic/components';
import {
__experimentalHeading as Heading,
__experimentalText as Text,
__experimentalVStack as VStack,
DropdownMenu,
} from '@wordpress/components';
import { rotateLeft, trash, moreHorizontalMobile } from '@wordpress/icons';
import { useTranslate } from 'i18n-calypso';
import { useState } from 'react';
import { getEmailForwardAddress } from 'calypso/lib/emails';
import { useResend, useRemove } from '../hooks';
import type { Mailbox } from '../../../../data/emails/types';
import './style.scss';

export const ActionsMenu = ( { mailbox }: { mailbox: Mailbox } ) => {
const [ isOpen, setIsOpen ] = useState( false );
const remove = useRemove( { mailbox } );
const resend = useResend( { mailbox } );
const translate = useTranslate();

const handleConfirm = () => {
remove( mailbox.mailbox, mailbox.domain, getEmailForwardAddress( mailbox ) );
setIsOpen( false );
};

const handleCancel = () => {
setIsOpen( false );
};

return (
<>
<ConfirmationDialog
isOpen={ isOpen }
onConfirm={ handleConfirm }
onCancel={ handleCancel }
cancelButtonText={ translate( 'Cancel' ) }
confirmButtonText={ translate( 'Remove' ) }
>
<VStack>
<Heading level={ 3 }>
{ translate( 'Are you sure you want to remove this email forward?' ) }
</Heading>
<Text>
{ translate(
"This will remove it from our records and if it's not used in another forward, it will require reverficiaton if added again."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spelling for reverficiaton to reverification

) }
</Text>
</VStack>
</ConfirmationDialog>
<DropdownMenu
icon={ moreHorizontalMobile }
label={ translate( 'More options' ) }
controls={
mailbox.warnings?.length
? [
{
title: 'Resend',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add translations for the title property. Resend and Remove

icon: rotateLeft,
onClick: () =>
resend( mailbox.mailbox, mailbox.domain, getEmailForwardAddress( mailbox ) ),
},
{
title: 'Remove',
icon: trash,
onClick: () => setIsOpen( true ),
},
]
: [
{
title: 'Remove',
icon: trash,
onClick: () => setIsOpen( true ),
},
]
}
/>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Delete me once https://github.com/Automattic/wp-calypso/pull/98095#discussion_r1937657191 is addressed.
.email-forward-list__actions {
.components-dropdown.components-dropdown-menu {
flex-grow: unset;
}
}
Loading