Implement the Manage Sender Domain component and
The AuthorizeSenderDomainModal MAILPOET-4302
This commit is contained in:
committed by
Veljko V
parent
5c3a9fe9b2
commit
847d199352
187
mailpoet/assets/js/src/common/authorize_sender_domain_modal.tsx
Normal file
187
mailpoet/assets/js/src/common/authorize_sender_domain_modal.tsx
Normal file
@ -0,0 +1,187 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { noop } from 'lodash';
|
||||
import { MailPoet } from 'mailpoet';
|
||||
import { Modal } from 'common/modal/modal';
|
||||
import {
|
||||
ManageSenderDomain,
|
||||
SenderDomainDnsItem,
|
||||
SenderDomainEntity,
|
||||
} from 'common/manage_sender_domain';
|
||||
import { isErrorResponse, Response, ErrorResponse } from 'ajax';
|
||||
|
||||
interface SenderDomainApiResponseType extends Response {
|
||||
data: SenderDomainDnsItem[];
|
||||
}
|
||||
|
||||
type VerifyResponseType = {
|
||||
dns: SenderDomainDnsItem[];
|
||||
ok: boolean;
|
||||
error?: string;
|
||||
};
|
||||
interface SenderDomainApiVerifyResponseType extends Response {
|
||||
data: VerifyResponseType;
|
||||
}
|
||||
|
||||
type ApiActionType = 'fetch' | 'create' | 'verify';
|
||||
|
||||
/**
|
||||
* @param {string} domain - Sender Domain
|
||||
* @param {ApiActionType} type - action type
|
||||
* @returns {Promise}
|
||||
*/
|
||||
const makeApiRequest = (domain: string, type: ApiActionType = 'fetch') => {
|
||||
let requestAction = 'getAuthorizedSenderDomains';
|
||||
|
||||
if (type === 'create') {
|
||||
requestAction = 'createAuthorizedSenderDomain';
|
||||
} else if (type === 'verify') {
|
||||
requestAction = 'verifyAuthorizedSenderDomain';
|
||||
}
|
||||
|
||||
return MailPoet.Ajax.post({
|
||||
api_version: MailPoet.apiVersion,
|
||||
endpoint: 'settings',
|
||||
action: requestAction,
|
||||
data: { domain },
|
||||
});
|
||||
};
|
||||
|
||||
const getApiErrorMessage = (error: { error?: ErrorResponse }): string =>
|
||||
isErrorResponse(error) && error.errors[0] && error.errors[0].message
|
||||
? error.errors[0].message
|
||||
: '';
|
||||
|
||||
const generateRowData = (senderDomain: string, dns: SenderDomainDnsItem[]) => {
|
||||
const row: SenderDomainEntity[] = [
|
||||
{
|
||||
domain: senderDomain,
|
||||
dns,
|
||||
},
|
||||
];
|
||||
return row;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
senderDomain: string;
|
||||
onRequestClose: () => void;
|
||||
setVerifiedSenderDomain?: (senderDomain: string) => void;
|
||||
};
|
||||
|
||||
function AuthorizeSenderDomainModal({
|
||||
senderDomain,
|
||||
onRequestClose,
|
||||
setVerifiedSenderDomain,
|
||||
}: Props): JSX.Element {
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
const [loadingButton, setLoadingButton] = useState(false);
|
||||
const [rowData, setRowData] = useState<SenderDomainEntity[]>([]);
|
||||
const modalIsOpened = useRef<boolean>(false);
|
||||
|
||||
const performStateUpdate = (callback: (param) => void, args) => {
|
||||
if (!modalIsOpened.current) return; // do nothing if modal is not opened
|
||||
callback(args);
|
||||
};
|
||||
|
||||
const verifyDnsButtonClicked = async () => {
|
||||
setLoadingButton(true);
|
||||
|
||||
try {
|
||||
const res: SenderDomainApiVerifyResponseType = await makeApiRequest(
|
||||
senderDomain,
|
||||
'verify',
|
||||
);
|
||||
if (!modalIsOpened.current) return;
|
||||
|
||||
setRowData(generateRowData(senderDomain, res.data.dns));
|
||||
if (res.data.ok) {
|
||||
// record verified, close the modal
|
||||
setErrorMessage('');
|
||||
setVerifiedSenderDomain(senderDomain);
|
||||
onRequestClose();
|
||||
}
|
||||
} catch (e) {
|
||||
const error: { error?: ErrorResponse; meta?: VerifyResponseType } = e;
|
||||
if (!modalIsOpened.current) return;
|
||||
|
||||
setRowData(generateRowData(senderDomain, error?.meta?.dns || []));
|
||||
const apiErrorMessage = getApiErrorMessage(e);
|
||||
setErrorMessage(apiErrorMessage || error?.meta?.error || '');
|
||||
}
|
||||
|
||||
performStateUpdate(setLoadingButton, false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!senderDomain) {
|
||||
return null;
|
||||
}
|
||||
modalIsOpened.current = true;
|
||||
|
||||
const allSenderDomains = window.mailpoet_all_sender_domains || [];
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
if (allSenderDomains.includes(senderDomain)) {
|
||||
// sender domain already exist
|
||||
const res: SenderDomainApiResponseType = await makeApiRequest(
|
||||
senderDomain,
|
||||
);
|
||||
performStateUpdate(
|
||||
setRowData,
|
||||
generateRowData(senderDomain, res.data),
|
||||
);
|
||||
} else {
|
||||
// create new sender domain
|
||||
const res: SenderDomainApiResponseType = await makeApiRequest(
|
||||
senderDomain,
|
||||
'create',
|
||||
);
|
||||
performStateUpdate(
|
||||
setRowData,
|
||||
generateRowData(senderDomain, res.data),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
const apiErrorMessage = getApiErrorMessage(e);
|
||||
|
||||
performStateUpdate(setErrorMessage, apiErrorMessage);
|
||||
}
|
||||
})().catch(() => {
|
||||
// do nothing
|
||||
});
|
||||
|
||||
return () => {
|
||||
modalIsOpened.current = false;
|
||||
};
|
||||
}, [senderDomain]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
onRequestClose={onRequestClose}
|
||||
contentClassName="authorize-sender-domain-modal"
|
||||
>
|
||||
{errorMessage && (
|
||||
<strong className="mailpoet_error_item mailpoet_error">
|
||||
{' '}
|
||||
{errorMessage}{' '}
|
||||
</strong>
|
||||
)}
|
||||
<ManageSenderDomain
|
||||
rows={rowData}
|
||||
verifyDnsButtonClicked={verifyDnsButtonClicked}
|
||||
loadingButton={loadingButton}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
AuthorizeSenderDomainModal.propTypes = {
|
||||
senderDomain: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
AuthorizeSenderDomainModal.defaultProps = {
|
||||
setVerifiedSenderDomain: noop,
|
||||
};
|
||||
|
||||
export { AuthorizeSenderDomainModal };
|
@ -0,0 +1,2 @@
|
||||
export * from './manage_sender_domain_types';
|
||||
export * from './manage_sender_domain';
|
@ -0,0 +1,100 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { noop } from 'lodash';
|
||||
import ReactStringReplace from 'react-string-replace';
|
||||
import { Button, Loader, TypographyHeading as Heading } from 'common';
|
||||
import { MailPoet } from 'mailpoet';
|
||||
import { SenderDomainEntity } from './manage_sender_domain_types';
|
||||
|
||||
type Props = {
|
||||
max_width: string;
|
||||
rows: Array<SenderDomainEntity>;
|
||||
loadingButton: boolean;
|
||||
verifyDnsButtonClicked: () => void;
|
||||
};
|
||||
function ManageSenderDomain({
|
||||
max_width,
|
||||
rows,
|
||||
loadingButton,
|
||||
verifyDnsButtonClicked,
|
||||
}: Props) {
|
||||
if (rows.length === 0) return <Loader size={84} />;
|
||||
|
||||
const { dns, domain } = rows[0];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Heading level={2}>
|
||||
{' '}
|
||||
{MailPoet.I18n.t('manageSenderDomainHeaderTitle')}{' '}
|
||||
</Heading>
|
||||
<p>
|
||||
{ReactStringReplace(
|
||||
MailPoet.I18n.t('manageSenderDomainHeaderSubtitle'),
|
||||
/\[link](.*?)\[\/link]/g,
|
||||
(match) => (
|
||||
<a
|
||||
key={match}
|
||||
className="mailpoet-link"
|
||||
href="https://kb.mailpoet.com/article/188-how-to-set-up-mailpoet-sending-service#dns"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{match}
|
||||
</a>
|
||||
),
|
||||
)}
|
||||
</p>
|
||||
|
||||
<table className="widefat fixed" style={{ maxWidth: max_width }}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th> {MailPoet.I18n.t('manageSenderDomainTableHeaderType')} </th>
|
||||
<th> {MailPoet.I18n.t('manageSenderDomainTableHeaderHost')} </th>
|
||||
<th> {MailPoet.I18n.t('manageSenderDomainTableHeaderValue')} </th>
|
||||
<th> {MailPoet.I18n.t('manageSenderDomainTableHeaderStatus')} </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dns.map((dnsRecord) => (
|
||||
<tr key={`row_${domain}_${dnsRecord.host}`}>
|
||||
<td>{dnsRecord.type}</td>
|
||||
<td>{dnsRecord.host}</td>
|
||||
<td>{dnsRecord.value}</td>
|
||||
<td>{dnsRecord.status}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<Button withSpinner={loadingButton} onClick={verifyDnsButtonClicked}>
|
||||
{' '}
|
||||
{MailPoet.I18n.t('manageSenderDomainVerifyButton')}{' '}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
ManageSenderDomain.propTypes = {
|
||||
max_width: PropTypes.string,
|
||||
rows: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
domain: PropTypes.string.isRequired,
|
||||
dns: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
host: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
status: PropTypes.string,
|
||||
message: PropTypes.string,
|
||||
}),
|
||||
).isRequired,
|
||||
}),
|
||||
).isRequired,
|
||||
verifyDnsButtonClicked: PropTypes.func,
|
||||
};
|
||||
|
||||
ManageSenderDomain.defaultProps = {
|
||||
max_width: 'auto',
|
||||
verifyDnsButtonClicked: noop,
|
||||
};
|
||||
|
||||
export { ManageSenderDomain };
|
@ -0,0 +1,20 @@
|
||||
type SenderDomainVerificationStatus = 'valid' | 'invalid' | 'pending';
|
||||
|
||||
type SenderDomainDnsItem = {
|
||||
host: string;
|
||||
value: string;
|
||||
type: string;
|
||||
status: SenderDomainVerificationStatus;
|
||||
message: string;
|
||||
};
|
||||
|
||||
type SenderDomainEntity = {
|
||||
domain: string;
|
||||
dns: Array<SenderDomainDnsItem>;
|
||||
};
|
||||
|
||||
export {
|
||||
SenderDomainVerificationStatus,
|
||||
SenderDomainDnsItem,
|
||||
SenderDomainEntity,
|
||||
};
|
@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import { MailPoet } from 'mailpoet';
|
||||
import ReactStringReplace from 'react-string-replace';
|
||||
import { AuthorizeSenderEmailModal } from 'common/authorize_sender_email_modal';
|
||||
import { AuthorizeSenderDomainModal } from 'common/authorize_sender_domain_modal';
|
||||
|
||||
const userHostDomain = window.location.hostname.replace('www.', '');
|
||||
const suggestedEmailAddress = `contact@${userHostDomain}`;
|
||||
@ -63,13 +64,11 @@ function SenderEmailAddressWarning({
|
||||
return (
|
||||
<>
|
||||
{showAuthorizedEmailModal && (
|
||||
// TODO: Change me. This should open the sender domain modal
|
||||
<AuthorizeSenderEmailModal
|
||||
senderEmail={emailAddress}
|
||||
<AuthorizeSenderDomainModal
|
||||
senderDomain={emailAddressDomain}
|
||||
onRequestClose={() => {
|
||||
setShowAuthorizedEmailModal(false);
|
||||
}}
|
||||
setAuthorizedAddress={setAuthorizedEmailAddress}
|
||||
/>
|
||||
)}
|
||||
<p className="sender_email_address_warning">
|
||||
@ -80,7 +79,7 @@ function SenderEmailAddressWarning({
|
||||
<a
|
||||
key={match}
|
||||
className="mailpoet-link"
|
||||
href="https://kb.mailpoet.com/article/328-set-up-dkim-for-your-sender-domain"
|
||||
href="https://kb.mailpoet.com/article/295-spf-and-dkim"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={loadModal}
|
||||
|
@ -341,7 +341,7 @@ class Settings extends APIEndpoint {
|
||||
if (!$response['ok']) {
|
||||
// sender domain verification error. probably an improper setup
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => WPFunctions::get()->__('failed sender domain verification', 'mailpoet'),
|
||||
APIError::BAD_REQUEST => WPFunctions::get()->__($response['error'] ?? 'failed sender domain verification', 'mailpoet'),
|
||||
], $response);
|
||||
}
|
||||
|
||||
|
@ -116,6 +116,14 @@ jQuery('#adminmenu #toplevel_page_mailpoet-newsletters')
|
||||
'setFromAddressEmailNotAuthorized': __('Can’t use this email yet! [link]Please authorize it first[/link].', 'mailpoet'),
|
||||
'setFromAddressEmailUnknownError': __('An error occured when saving FROM email address.', 'mailpoet'),
|
||||
|
||||
'manageSenderDomainHeaderTitle': __('Manage Sender Domain ', 'mailpoet'),
|
||||
'manageSenderDomainHeaderSubtitle': __('To help your audience and MailPoet authenticate you as the domain owner, please add the following DNS records to your domain’s DNS and click “Verify the DNS records”. Please note that it may take up to 24 hours for DNS changes to propagate after you make the change. [link]Read the guide[/link].', 'mailpoet'),
|
||||
'manageSenderDomainTableHeaderType': __('Type', 'mailpoet'),
|
||||
'manageSenderDomainTableHeaderHost': __('Host', 'mailpoet'),
|
||||
'manageSenderDomainTableHeaderValue': __('Value', 'mailpoet'),
|
||||
'manageSenderDomainTableHeaderStatus': __('Status', 'mailpoet'),
|
||||
'manageSenderDomainVerifyButton': __('Verify the DNS records', 'mailpoet'),
|
||||
|
||||
'reviewRequestHeading': _x('Thank you! Time to tell the world?', 'After a user gives us positive feedback via the NPS poll, we ask them to review our plugin on WordPress.org.'),
|
||||
'reviewRequestDidYouKnow': __('[username], did you know that hundreds of WordPress users read the reviews on the plugin repository? They’re also a source of inspiration for our team.'),
|
||||
'reviewRequestUsingForDays': _n('You’ve been using MailPoet for [days] day now, and we would love to read your own review.', 'You’ve been using MailPoet for [days] days now, and we would love to read your own review.', installed_days_ago),
|
||||
|
Reference in New Issue
Block a user