Skip confirmation step on email subscription (#1646)
* Email subscription params in request body * Email subscription params in request body fix tests * Skip confirm step on email sub When user logged in with the same email he tries to subscribe * Skip confirm step on email sub set autoConfirm param to make it work * Update size-limit * Handle 409: already subscribed * refactor: prevStep to justSubscribed prevStep is not used anywhere else and because of it influences output text (haveSubscribed), have changed it to more intuitive justSubscribed variable * Test case for http error 409
This commit is contained in:
@@ -1,6 +1,16 @@
|
||||
import { siteId, url } from './settings';
|
||||
import { BASE_URL, API_BASE } from './constants';
|
||||
import { Config, Comment, Tree, User, BlockedUser, Sorting, BlockTTL, Image } from './types';
|
||||
import {
|
||||
Config,
|
||||
Comment,
|
||||
Tree,
|
||||
User,
|
||||
BlockedUser,
|
||||
Sorting,
|
||||
BlockTTL,
|
||||
Image,
|
||||
EmailSubVerificationStatus,
|
||||
} from './types';
|
||||
import { apiFetcher, adminFetcher } from './fetcher';
|
||||
|
||||
/* API methods */
|
||||
@@ -72,8 +82,8 @@ export const uploadImage = (image: File): Promise<Image> => {
|
||||
* Start process of email subscription to updates
|
||||
* @param emailAddress email for subscription
|
||||
*/
|
||||
export const emailVerificationForSubscribe = (emailAddress: string) =>
|
||||
apiFetcher.post('/email/subscribe', {}, { address: emailAddress });
|
||||
export const emailVerificationForSubscribe = (emailAddress: string): Promise<EmailSubVerificationStatus> =>
|
||||
apiFetcher.post('/email/subscribe', {}, { address: emailAddress, autoConfirm: true });
|
||||
|
||||
/**
|
||||
* Confirmation of email subscription to updates
|
||||
|
||||
@@ -156,3 +156,8 @@ export interface ApiError {
|
||||
/** in-depth explanation */
|
||||
error: string;
|
||||
}
|
||||
|
||||
export interface EmailSubVerificationStatus {
|
||||
updated: boolean;
|
||||
address: string;
|
||||
}
|
||||
|
||||
+45
-1
@@ -18,6 +18,7 @@ import { persistEmail } from 'components/auth/auth.utils';
|
||||
import enMessages from 'locales/en.json';
|
||||
|
||||
import { SubscribeByEmail, SubscribeByEmailForm } from '.';
|
||||
import { RequestError } from '../../../utils/errorUtils';
|
||||
|
||||
const emailVerificationForSubscribeMock = emailVerificationForSubscribe as unknown as jest.Mock<
|
||||
ReturnType<typeof emailVerificationForSubscribe>
|
||||
@@ -29,6 +30,8 @@ const unsubscribeFromEmailUpdatesMock = unsubscribeFromEmailUpdates as unknown a
|
||||
ReturnType<typeof unsubscribeFromEmailUpdates>
|
||||
>;
|
||||
|
||||
emailVerificationForSubscribeMock.mockImplementation((email) => Promise.resolve({ address: email, updated: false }));
|
||||
|
||||
const initialStore = {
|
||||
user,
|
||||
theme: 'light',
|
||||
@@ -104,7 +107,7 @@ describe('<SubscribeByEmailForm/>', () => {
|
||||
expect(wrapper.text().startsWith('You are subscribed on updates by email')).toBe(true);
|
||||
});
|
||||
|
||||
it('should pass throw subscribe process', async () => {
|
||||
it('should pass through subscribe process', async () => {
|
||||
const wrapper = createWrapper();
|
||||
|
||||
const input = wrapper.find('input');
|
||||
@@ -138,6 +141,47 @@ describe('<SubscribeByEmailForm/>', () => {
|
||||
expect(wrapper.find(Button).text()).toEqual('Unsubscribe');
|
||||
});
|
||||
|
||||
it('should handle http error 409: already subscribed', async () => {
|
||||
emailVerificationForSubscribeMock.mockImplementationOnce(() => Promise.reject(new RequestError('', 409)));
|
||||
|
||||
const wrapper = createWrapper();
|
||||
|
||||
const input = wrapper.find('input');
|
||||
const form = wrapper.find('form');
|
||||
|
||||
input.getDOMNode<HTMLInputElement>().value = 'some@email.com';
|
||||
input.simulate('input');
|
||||
form.simulate('submit');
|
||||
|
||||
await sleep();
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.text().startsWith('You are subscribed on updates by email')).toBe(true);
|
||||
});
|
||||
|
||||
it('should pass through subscribe process without confirmation', async () => {
|
||||
emailVerificationForSubscribeMock.mockImplementationOnce((email) =>
|
||||
Promise.resolve({ address: email, updated: true })
|
||||
);
|
||||
|
||||
const wrapper = createWrapper();
|
||||
|
||||
const input = wrapper.find('input');
|
||||
const form = wrapper.find('form');
|
||||
|
||||
input.getDOMNode<HTMLInputElement>().value = 'some@email.com';
|
||||
input.simulate('input');
|
||||
form.simulate('submit');
|
||||
|
||||
expect(emailVerificationForSubscribeMock).toHaveBeenCalledWith('some@email.com');
|
||||
|
||||
await sleep();
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.text().startsWith('You have been subscribed on updates by email')).toBe(true);
|
||||
expect(wrapper.find(Button).text()).toEqual('Unsubscribe');
|
||||
});
|
||||
|
||||
it('should fill in email from local storage', async () => {
|
||||
const expected = 'someone@email.com';
|
||||
persistEmail(expected);
|
||||
|
||||
+22
-9
@@ -8,7 +8,7 @@ import { User } from 'common/types';
|
||||
import { StoreState } from 'store';
|
||||
import { setUserSubscribed } from 'store/user/actions';
|
||||
import { sleep } from 'utils/sleep';
|
||||
import { extractErrorMessageFromResponse } from 'utils/errorUtils';
|
||||
import { extractErrorMessageFromResponse, RequestError } from 'utils/errorUtils';
|
||||
import { useTheme } from 'hooks/useTheme';
|
||||
import { getHandleClickProps } from 'common/accessibility';
|
||||
import { emailVerificationForSubscribe, emailConfirmationForSubscribe, unsubscribeFromEmailUpdates } from 'common/api';
|
||||
@@ -120,7 +120,7 @@ export const SubscribeByEmailForm: FunctionComponent = () => {
|
||||
const subscribed = useSelector<StoreState, boolean>(({ user }) =>
|
||||
user === null ? false : Boolean(user.email_subscription)
|
||||
);
|
||||
const previousStep = useRef<Step | null>(null);
|
||||
const justSubscribed = useRef<Boolean>(false);
|
||||
|
||||
const [step, setStep] = useState(subscribed ? Step.Subscribed : Step.Email);
|
||||
|
||||
@@ -136,16 +136,31 @@ export const SubscribeByEmailForm: FunctionComponent = () => {
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
let emailVerificationResponse;
|
||||
|
||||
switch (step) {
|
||||
case Step.Email:
|
||||
await emailVerificationForSubscribe(emailAddress);
|
||||
try {
|
||||
emailVerificationResponse = await emailVerificationForSubscribe(emailAddress);
|
||||
justSubscribed.current = true;
|
||||
} catch (e) {
|
||||
if ((e as RequestError).code !== 409) {
|
||||
throw e;
|
||||
}
|
||||
emailVerificationResponse = { address: emailAddress, updated: true };
|
||||
}
|
||||
if (emailVerificationResponse.updated) {
|
||||
dispatch(setUserSubscribed(true));
|
||||
setStep(Step.Subscribed);
|
||||
break;
|
||||
}
|
||||
setToken('');
|
||||
setStep(Step.Token);
|
||||
break;
|
||||
case Step.Token:
|
||||
await emailConfirmationForSubscribe(currentToken);
|
||||
dispatch(setUserSubscribed(true));
|
||||
previousStep.current = Step.Token;
|
||||
justSubscribed.current = true;
|
||||
setStep(Step.Subscribed);
|
||||
break;
|
||||
default:
|
||||
@@ -210,7 +225,6 @@ export const SubscribeByEmailForm: FunctionComponent = () => {
|
||||
try {
|
||||
await unsubscribeFromEmailUpdates();
|
||||
dispatch(setUserSubscribed(false));
|
||||
previousStep.current = Step.Subscribed;
|
||||
setStep(Step.Unsubscribed);
|
||||
} catch (e) {
|
||||
// @ts-ignore
|
||||
@@ -229,10 +243,9 @@ export const SubscribeByEmailForm: FunctionComponent = () => {
|
||||
}
|
||||
|
||||
if (step === Step.Subscribed) {
|
||||
const text =
|
||||
previousStep.current === Step.Token
|
||||
? intl.formatMessage(messages.haveSubscribed)
|
||||
: intl.formatMessage(messages.subscribed);
|
||||
const text = justSubscribed.current
|
||||
? intl.formatMessage(messages.haveSubscribed)
|
||||
: intl.formatMessage(messages.subscribed);
|
||||
|
||||
return (
|
||||
<div className={b('comment-form__subscribe-by-email', { mods: { subscribed: true } })}>
|
||||
|
||||
@@ -97,6 +97,10 @@ export const errorMessages = defineMessages<string | number>({
|
||||
id: 'errors.forbidden',
|
||||
defaultMessage: 'Forbidden.',
|
||||
},
|
||||
409: {
|
||||
id: 'errors.conflict',
|
||||
defaultMessage: 'Conflict.',
|
||||
},
|
||||
429: {
|
||||
id: 'errors.to-many-request',
|
||||
defaultMessage: 'You have reached maximum request limit.',
|
||||
|
||||
Reference in New Issue
Block a user