зеркало из https://github.com/mozilla/fxa.git
Merge pull request #16392 from mozilla/FXA-9054
cleanup(settings): Remove confirm and confirm_signin in react
This commit is contained in:
Коммит
a0a8fba744
|
@ -105,6 +105,7 @@ const BaseAuthenticationBroker = Backbone.Model.extend({
|
|||
afterResetPasswordConfirmationPoll: new NullBehavior(),
|
||||
afterSignIn: new NavigateBehavior('signin_confirmed'),
|
||||
afterSignInConfirmationPoll: new NavigateBehavior('signin_confirmed'),
|
||||
// with React conversion, we are deprecating the confirm view in favor of 'confirm_signup_code'
|
||||
afterSignUp: new NavigateBehavior('confirm'),
|
||||
afterSignUpConfirmationPoll: new NavigateOrRedirectBehavior(
|
||||
'signup_confirmed'
|
||||
|
|
|
@ -172,6 +172,7 @@ export default {
|
|||
const verificationMethod = account.get('verificationMethod');
|
||||
const verificationReason = account.get('verificationReason');
|
||||
|
||||
// with React conversion, we are deprecating the confirm_signin view in favor of 'signin_token_code'
|
||||
if (
|
||||
(verificationReason === VerificationReasons.SIGN_IN &&
|
||||
verificationMethod === VerificationMethods.EMAIL) ||
|
||||
|
@ -221,7 +222,7 @@ export default {
|
|||
this.navigate('confirm_signup_code', { account });
|
||||
});
|
||||
}
|
||||
|
||||
// with React conversion, we are deprecating the default confirm_signin view in favor of 'signin_token_code'
|
||||
return this.navigate('confirm', { account });
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import { RouteComponentProps, Router, useLocation } from '@reach/router';
|
|||
|
||||
import { QueryParams } from '../..';
|
||||
|
||||
import { currentAccount, sessionToken } from '../../lib/cache';
|
||||
import { currentAccount } from '../../lib/cache';
|
||||
import { firefox } from '../../lib/channels/firefox';
|
||||
import GleanMetrics from '../../lib/glean';
|
||||
import * as Metrics from '../../lib/metrics';
|
||||
|
@ -43,7 +43,6 @@ import Clear from '../../pages/Clear';
|
|||
import CookiesDisabled from '../../pages/CookiesDisabled';
|
||||
import CompleteResetPasswordContainer from '../../pages/ResetPassword/CompleteResetPassword/container';
|
||||
import CompleteSigninContainer from '../../pages/Signin/CompleteSignin/container';
|
||||
import Confirm from 'fxa-settings/src/pages/Signup/Confirm';
|
||||
import ConfirmResetPassword from '../../pages/ResetPassword/ConfirmResetPassword';
|
||||
import ConfirmSignupCodeContainer from '../../pages/Signup/ConfirmSignupCode/container';
|
||||
import Legal from '../../pages/Legal';
|
||||
|
@ -223,7 +222,6 @@ const AuthAndAccountSetupRoutes = ({
|
|||
isSignedIn,
|
||||
integration,
|
||||
}: { isSignedIn: boolean; integration: Integration } & RouteComponentProps) => {
|
||||
const sessionTokenId = sessionToken();
|
||||
const localAccount = currentAccount();
|
||||
// TODO: MozServices / string discrepancy, FXA-6802
|
||||
const serviceName = integration.getServiceName() as MozServices;
|
||||
|
@ -304,7 +302,6 @@ const AuthAndAccountSetupRoutes = ({
|
|||
|
||||
{/* Signup */}
|
||||
<CannotCreateAccount path="/cannot_create_account/*" />
|
||||
<Confirm path="/confirm/*" {...{ sessionTokenId }} />
|
||||
<ConfirmSignupCodeContainer
|
||||
path="/confirm_signup_code/*"
|
||||
{...{ integration }}
|
||||
|
|
|
@ -33,10 +33,6 @@ const ConfirmWithLink = ({
|
|||
resendStatus,
|
||||
errorMessage,
|
||||
}: ConfirmWithLinkProps) => {
|
||||
// (temporarily?) removed `Open With Webmail` functionality
|
||||
// Not currently functional in content-server/prod so requires fix/new utility
|
||||
// File follow-up to check with Product to see if we want to recreate this functionality
|
||||
|
||||
return (
|
||||
<>
|
||||
<CardHeader
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
## ConfirmSignin component
|
||||
|
||||
confirm-signin-header = Confirm this sign-in
|
||||
# { $email } is the email entered by the user and where the signin confirmation link was sent
|
||||
confirm-signin-message = Check your email for the sign-in confirmation link sent to { $email }
|
|
@ -1,32 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import React from 'react';
|
||||
import ConfirmSignin, { ConfirmSigninProps } from '.';
|
||||
import AppLayout from '../../../components/AppLayout';
|
||||
import { LocationProvider } from '@reach/router';
|
||||
import { Meta } from '@storybook/react';
|
||||
import { MOCK_ACCOUNT } from '../../../models/mocks';
|
||||
import { withLocalization } from 'fxa-react/lib/storybooks';
|
||||
|
||||
export default {
|
||||
title: 'Pages/Signin/ConfirmSignin',
|
||||
component: ConfirmSignin,
|
||||
decorators: [withLocalization],
|
||||
} as Meta;
|
||||
|
||||
const storyWithProps = (props: ConfirmSigninProps) => {
|
||||
const story = () => (
|
||||
<LocationProvider>
|
||||
<AppLayout>
|
||||
<ConfirmSignin {...props} />
|
||||
</AppLayout>
|
||||
</LocationProvider>
|
||||
);
|
||||
return story;
|
||||
};
|
||||
|
||||
export const Default = storyWithProps({
|
||||
email: MOCK_ACCOUNT.primaryEmail.email,
|
||||
});
|
|
@ -1,61 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import React from 'react';
|
||||
import { fireEvent, screen } from '@testing-library/react';
|
||||
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider'; // import { getFtlBundle, testAllL10n } from 'fxa-react/lib/test-utils';
|
||||
// import { FluentBundle } from '@fluent/bundle';
|
||||
import { usePageViewEvent } from '../../../lib/metrics';
|
||||
import ConfirmSignin, { viewName } from '.';
|
||||
import { MOCK_ACCOUNT } from '../../../models/mocks';
|
||||
import { REACT_ENTRYPOINT } from '../../../constants';
|
||||
|
||||
jest.mock('../../../lib/metrics', () => ({
|
||||
usePageViewEvent: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('ConfirmSignin', () => {
|
||||
// TODO: enable l10n tests when they've been updated to handle embedded tags in ftl strings
|
||||
// TODO: in FXA-6461
|
||||
// let bundle: FluentBundle;
|
||||
// beforeAll(async () => {
|
||||
// bundle = await getFtlBundle('settings');
|
||||
// });
|
||||
// TODO: add tests for all metrics as they are added
|
||||
|
||||
it("renders default view as expected with user's email", () => {
|
||||
renderWithLocalizationProvider(
|
||||
<ConfirmSignin email={MOCK_ACCOUNT.primaryEmail.email} />
|
||||
);
|
||||
// testAllL10n(screen, bundle);
|
||||
|
||||
const headingEl = screen.getByRole('heading', { level: 1 });
|
||||
expect(headingEl).toHaveTextContent('Confirm this sign-in');
|
||||
screen.getByText(
|
||||
`Check your email for the sign-in confirmation link sent to ${MOCK_ACCOUNT.primaryEmail.email}`
|
||||
);
|
||||
screen.getByRole('button', { name: 'Not in inbox or spam folder? Resend' });
|
||||
});
|
||||
|
||||
it('resends the email when the user clicks the resend button', () => {
|
||||
renderWithLocalizationProvider(
|
||||
<ConfirmSignin email={MOCK_ACCOUNT.primaryEmail.email} />
|
||||
);
|
||||
const headingEl = screen.getByRole('heading', { level: 1 });
|
||||
expect(headingEl).toHaveTextContent('Confirm this sign-in');
|
||||
const resendEmailButton = screen.getByRole('button', {
|
||||
name: 'Not in inbox or spam folder? Resend',
|
||||
});
|
||||
fireEvent.click(resendEmailButton);
|
||||
// TO-DO: Once we know where this functionality is coming from, we'll be able to test it.
|
||||
// Add in a test to verify that it's called.
|
||||
});
|
||||
|
||||
it('emits a metrics event on render', () => {
|
||||
renderWithLocalizationProvider(
|
||||
<ConfirmSignin email={MOCK_ACCOUNT.primaryEmail.email} />
|
||||
);
|
||||
expect(usePageViewEvent).toHaveBeenCalledWith(viewName, REACT_ENTRYPOINT);
|
||||
});
|
||||
});
|
|
@ -1,74 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { logViewEvent, usePageViewEvent } from '../../../lib/metrics';
|
||||
import { RouteComponentProps } from '@reach/router';
|
||||
import ConfirmWithLink, {
|
||||
ConfirmWithLinkPageStrings,
|
||||
} from '../../../components/ConfirmWithLink';
|
||||
import { REACT_ENTRYPOINT } from '../../../constants';
|
||||
import { ResendStatus } from '../../../lib/types';
|
||||
import { useFtlMsgResolver } from '../../../models';
|
||||
import { getLocalizedErrorMessage } from '../../../lib/auth-errors/auth-errors';
|
||||
|
||||
export type ConfirmSigninProps = {
|
||||
email: string;
|
||||
goBackCallback?: () => void;
|
||||
};
|
||||
|
||||
export const viewName = 'confirm-signin';
|
||||
|
||||
const ConfirmSignin = ({
|
||||
email,
|
||||
goBackCallback,
|
||||
}: RouteComponentProps & ConfirmSigninProps) => {
|
||||
usePageViewEvent(viewName, REACT_ENTRYPOINT);
|
||||
|
||||
const ftlMsgResolver = useFtlMsgResolver();
|
||||
|
||||
const [resendStatus, setResendStatus] = useState<ResendStatus>(
|
||||
ResendStatus['not sent']
|
||||
);
|
||||
const [errorMessage, setErrorMessage] = useState<string>();
|
||||
|
||||
const confirmSigninPageText: ConfirmWithLinkPageStrings = {
|
||||
headingFtlId: 'confirm-signin-heading',
|
||||
headingText: 'Confirm this sign-in',
|
||||
instructionFtlId: 'confirm-signin-instruction',
|
||||
instructionText: `Check your email for the sign-in confirmation link sent to ${email}`,
|
||||
};
|
||||
|
||||
const resendEmailHandler = async () => {
|
||||
try {
|
||||
// TO-DO: signin confirmation email to user.
|
||||
logViewEvent(viewName, 'resend', REACT_ENTRYPOINT);
|
||||
setErrorMessage('');
|
||||
setResendStatus(ResendStatus['sent']);
|
||||
} catch (err) {
|
||||
const localizedErrorMessage = getLocalizedErrorMessage(
|
||||
ftlMsgResolver,
|
||||
err
|
||||
);
|
||||
setResendStatus(ResendStatus['error']);
|
||||
setErrorMessage(localizedErrorMessage);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ConfirmWithLink
|
||||
{...{
|
||||
email,
|
||||
resendEmailHandler,
|
||||
resendStatus,
|
||||
errorMessage,
|
||||
}}
|
||||
confirmWithLinkPageStrings={confirmSigninPageText}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfirmSignin;
|
|
@ -1,4 +0,0 @@
|
|||
export const EXAMPLE_EMAIL = 'example@domain.com';
|
||||
export const mockGoBackCallback = () => {
|
||||
console.log('Navigating back!');
|
||||
};
|
|
@ -1,7 +0,0 @@
|
|||
## Confirm page
|
||||
## Users will see this page if a verification link was sent to their email address
|
||||
## when setting up a new account
|
||||
|
||||
confirm-signup-heading = Confirm your account
|
||||
# { $email } is the email entered by the user and where the signup confirmation link was sent
|
||||
confirm-signup-instruction = Check your email for the confirmation link sent to { $email }
|
|
@ -1,44 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import React from 'react';
|
||||
import Confirm from '.';
|
||||
import { LocationProvider } from '@reach/router';
|
||||
import { Meta } from '@storybook/react';
|
||||
import { mockAppContext } from '../../../models/mocks';
|
||||
import { withLocalization } from 'fxa-react/lib/storybooks';
|
||||
import { Account, AppContext } from 'fxa-settings/src/models';
|
||||
import {
|
||||
MOCK_PROFILE_WITH_RESEND_ERROR,
|
||||
MOCK_PROFILE_WITH_RESEND_SUCCESS,
|
||||
MOCK_SESSION_TOKEN,
|
||||
MOCK_UNVERIFIED_SESSION,
|
||||
} from './mocks';
|
||||
|
||||
export default {
|
||||
title: 'Pages/Signup/Confirm',
|
||||
component: Confirm,
|
||||
decorators: [withLocalization],
|
||||
} as Meta;
|
||||
|
||||
const storyWithContext = (account: Account) => {
|
||||
const story = () => (
|
||||
<LocationProvider>
|
||||
<AppContext.Provider
|
||||
value={mockAppContext({ account, session: MOCK_UNVERIFIED_SESSION })}
|
||||
>
|
||||
<Confirm sessionTokenId={MOCK_SESSION_TOKEN} />
|
||||
</AppContext.Provider>
|
||||
</LocationProvider>
|
||||
);
|
||||
return story;
|
||||
};
|
||||
|
||||
export const WithSuccessOnResend = storyWithContext(
|
||||
MOCK_PROFILE_WITH_RESEND_SUCCESS
|
||||
);
|
||||
|
||||
export const WithErrorOnResend = storyWithContext(
|
||||
MOCK_PROFILE_WITH_RESEND_ERROR
|
||||
);
|
|
@ -1,147 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import React from 'react';
|
||||
import { fireEvent, screen, waitFor } from '@testing-library/react';
|
||||
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
|
||||
import { LocationProvider } from '@reach/router';
|
||||
// import { FluentBundle } from '@fluent/bundle';
|
||||
// import { getFtlBundle, testAllL10n } from 'fxa-react/lib/test-utils';
|
||||
import { FIREFOX_NOREPLY_EMAIL, REACT_ENTRYPOINT } from '../../../constants';
|
||||
import { usePageViewEvent } from '../../../lib/metrics';
|
||||
import { Account, AppContext, Session } from '../../../models';
|
||||
import {
|
||||
mockAppContext,
|
||||
mockEmail,
|
||||
MOCK_ACCOUNT,
|
||||
MOCK_PROFILE_INFO,
|
||||
mockSession,
|
||||
} from '../../../models/mocks';
|
||||
import Confirm, { viewName } from '.';
|
||||
import { MOCK_SESSION_TOKEN, MOCK_UNVERIFIED_SESSION } from './mocks';
|
||||
|
||||
jest.mock('../../../lib/metrics', () => ({
|
||||
usePageViewEvent: jest.fn(),
|
||||
logViewEvent: jest.fn(),
|
||||
}));
|
||||
jest.mock('@reach/router', () => ({
|
||||
...jest.requireActual('@reach/router'),
|
||||
navigate: jest.fn(),
|
||||
}));
|
||||
|
||||
const MOCK_ACCOUNT_WITH_SUCCESS = {
|
||||
getProfileInfo: jest.fn().mockResolvedValue(MOCK_PROFILE_INFO),
|
||||
sendVerificationCode: jest.fn().mockResolvedValue(true),
|
||||
primaryEmail: mockEmail(MOCK_ACCOUNT.primaryEmail.email, true, false),
|
||||
} as unknown as Account;
|
||||
|
||||
const MOCK_ACCOUNT_WITH_ERROR = {
|
||||
getProfileInfo: jest.fn().mockResolvedValue(MOCK_PROFILE_INFO),
|
||||
sendVerificationCode: jest.fn().mockRejectedValue(Error),
|
||||
primaryEmail: mockEmail(MOCK_ACCOUNT.primaryEmail.email, true, false),
|
||||
} as unknown as Account;
|
||||
|
||||
function renderWithContext(
|
||||
account: Account | undefined,
|
||||
session: Session,
|
||||
sessionTokenId: string | null
|
||||
) {
|
||||
renderWithLocalizationProvider(
|
||||
<AppContext.Provider
|
||||
value={mockAppContext({
|
||||
account,
|
||||
session,
|
||||
})}
|
||||
>
|
||||
<LocationProvider>
|
||||
<Confirm {...{ sessionTokenId }} />
|
||||
</LocationProvider>
|
||||
</AppContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
describe('Confirm page', () => {
|
||||
// TODO : l10n test currently failing on image l10n
|
||||
// - enabling these tests will require attributes handling
|
||||
// let bundle: FluentBundle;
|
||||
// beforeAll(async () => {
|
||||
// bundle = await getFtlBundle('settings');
|
||||
// });
|
||||
afterEach(() => jest.clearAllMocks());
|
||||
|
||||
it('renders a loading spinner until account data is available', async () => {
|
||||
renderWithContext(
|
||||
MOCK_ACCOUNT_WITH_SUCCESS,
|
||||
MOCK_UNVERIFIED_SESSION,
|
||||
MOCK_SESSION_TOKEN
|
||||
);
|
||||
expect(screen.getByRole('img', { name: 'Loading…' })).toBeInTheDocument();
|
||||
await waitFor(() => {
|
||||
const headingEl = screen.getByRole('heading', { level: 1 });
|
||||
expect(headingEl).toHaveTextContent('Confirm your account');
|
||||
});
|
||||
});
|
||||
|
||||
it('renders as expected with unverified session, session token and profile info', async () => {
|
||||
renderWithContext(
|
||||
MOCK_ACCOUNT_WITH_SUCCESS,
|
||||
MOCK_UNVERIFIED_SESSION,
|
||||
MOCK_SESSION_TOKEN
|
||||
);
|
||||
// wait for setEmail to update with email from account model
|
||||
await waitFor(() => {
|
||||
const headingEl = screen.getByRole('heading', { level: 1 });
|
||||
expect(headingEl).toHaveTextContent('Confirm your account');
|
||||
screen.getByText(
|
||||
`Check your email for the confirmation link sent to ${MOCK_ACCOUNT.primaryEmail.email}`
|
||||
);
|
||||
screen.getByRole('button', {
|
||||
name: 'Not in inbox or spam folder? Resend',
|
||||
});
|
||||
// testAllL10n(screen, bundle, { email: MOCK_ACCOUNT.primaryEmail.email });
|
||||
});
|
||||
});
|
||||
|
||||
it('resends the email when the user clicks the resend button', async () => {
|
||||
const account: Account = MOCK_ACCOUNT_WITH_SUCCESS;
|
||||
const session = mockSession(false, false);
|
||||
renderWithContext(account, session, MOCK_SESSION_TOKEN);
|
||||
await waitFor(() => {
|
||||
const resendEmailButton = screen.getByRole('button', {
|
||||
name: 'Not in inbox or spam folder? Resend',
|
||||
});
|
||||
fireEvent.click(resendEmailButton);
|
||||
expect(session.sendVerificationCode).toBeCalled();
|
||||
const successBannerText = `Email resent. Add ${FIREFOX_NOREPLY_EMAIL} to your contacts to ensure a smooth delivery.`;
|
||||
expect(screen.getByText(successBannerText)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders an error banner when resending an email fails', async () => {
|
||||
const account: Account = MOCK_ACCOUNT_WITH_ERROR;
|
||||
const session = mockSession(false, true);
|
||||
renderWithContext(account, session, MOCK_SESSION_TOKEN);
|
||||
await waitFor(() => {
|
||||
const resendEmailButton = screen.getByRole('button', {
|
||||
name: 'Not in inbox or spam folder? Resend',
|
||||
});
|
||||
fireEvent.click(resendEmailButton);
|
||||
expect(session.sendVerificationCode).toBeCalled();
|
||||
const bannerText = `Unexpected error`;
|
||||
expect(screen.getByText(bannerText)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
// only page view event added, could not hit route on prod to test which other metrics should be emitted
|
||||
it('emits a metrics event on render', async () => {
|
||||
renderWithContext(
|
||||
MOCK_ACCOUNT_WITH_SUCCESS,
|
||||
MOCK_UNVERIFIED_SESSION,
|
||||
MOCK_SESSION_TOKEN
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(usePageViewEvent).toHaveBeenCalledWith(viewName, REACT_ENTRYPOINT);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,206 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import {
|
||||
logErrorEvent,
|
||||
logViewEvent,
|
||||
usePageViewEvent,
|
||||
} from '../../../lib/metrics';
|
||||
import { RouteComponentProps, useNavigate } from '@reach/router';
|
||||
import ConfirmWithLink, {
|
||||
ConfirmWithLinkPageStrings,
|
||||
} from '../../../components/ConfirmWithLink';
|
||||
import {
|
||||
NAVIGATION_TIMEOUT_MS,
|
||||
POLLING_INTERVAL_MS,
|
||||
REACT_ENTRYPOINT,
|
||||
} from '../../../constants';
|
||||
import { ResendStatus } from '../../../lib/types';
|
||||
import AppLayout from 'fxa-settings/src/components/AppLayout';
|
||||
import {
|
||||
useAccount,
|
||||
useFtlMsgResolver,
|
||||
useInterval,
|
||||
useSession,
|
||||
} from 'fxa-settings/src/models';
|
||||
import {
|
||||
AuthUiErrors,
|
||||
getLocalizedErrorMessage,
|
||||
} from 'fxa-settings/src/lib/auth-errors/auth-errors';
|
||||
import LoadingSpinner from 'fxa-react/components/LoadingSpinner';
|
||||
import { hardNavigateToContentServer } from 'fxa-react/lib/utils';
|
||||
|
||||
// This page is no longer part of the typical/expected signup flow, but has been preserverd during
|
||||
// the conversion from backbone to react as we were still seeing some traffic to this route.
|
||||
// TODO in FXA-7185: Check metrics once this React page is rolled out to determine if this page can be entirely replaced by ConfirmSignupCode.
|
||||
|
||||
export const viewName = 'confirm';
|
||||
|
||||
export type ConfirmProps = {
|
||||
sessionTokenId?: string | null;
|
||||
};
|
||||
|
||||
export const Confirm = ({
|
||||
sessionTokenId,
|
||||
}: ConfirmProps & RouteComponentProps) => {
|
||||
usePageViewEvent(viewName, REACT_ENTRYPOINT);
|
||||
|
||||
const ftlMsgResolver = useFtlMsgResolver();
|
||||
|
||||
// Show a loading spinner until all checks complete. Without this,
|
||||
// users without a session will experience some jank due to an
|
||||
// immediate redirect or rerender of this page.
|
||||
const [showLoadingSpinner, setShowLoadingSpinner] = useState(true);
|
||||
const [resendStatus, setResendStatus] = useState<ResendStatus>(
|
||||
ResendStatus['not sent']
|
||||
);
|
||||
const [errorMessage, setErrorMessage] = useState<string>();
|
||||
const [email, setEmail] = useState('');
|
||||
|
||||
const account = useAccount();
|
||||
const session = useSession();
|
||||
const navigate = useNavigate();
|
||||
const [isPolling, setIsPolling] = useState<number | null>(
|
||||
POLLING_INTERVAL_MS
|
||||
);
|
||||
|
||||
const getEmail = useCallback(async () => {
|
||||
const response = await account.getProfileInfo();
|
||||
setEmail(response.primaryEmail.email);
|
||||
}, [account]);
|
||||
|
||||
const redirectToSignup = useCallback(() => {
|
||||
hardNavigateToContentServer('/signup');
|
||||
}, []);
|
||||
|
||||
const confirmSignupPageText: ConfirmWithLinkPageStrings = {
|
||||
headingFtlId: 'confirm-signup-heading',
|
||||
headingText: 'Confirm your account',
|
||||
instructionFtlId: 'confirm-signup-instruction',
|
||||
instructionText: `Check your email for the confirmation link sent to ${email}`,
|
||||
};
|
||||
|
||||
// Verifies if we have access to profile info and session token.
|
||||
// If we do, set the email and render the page. If not, we don't know
|
||||
// who the user is and can't confirm if we've sent a confirmation
|
||||
// link, and we can't send a new one without an email address => redirect
|
||||
// the user to retry signup.
|
||||
useEffect(() => {
|
||||
try {
|
||||
if (sessionTokenId) {
|
||||
getEmail();
|
||||
} else {
|
||||
redirectToSignup();
|
||||
}
|
||||
} catch (e) {
|
||||
redirectToSignup();
|
||||
}
|
||||
}, [getEmail, redirectToSignup, sessionTokenId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (email) {
|
||||
setShowLoadingSpinner(false);
|
||||
}
|
||||
}, [email, setShowLoadingSpinner]);
|
||||
|
||||
const navigateToConfirmCode = useCallback(
|
||||
() =>
|
||||
navigate('/confirm_signup_code?showReactApp=true', {
|
||||
state: { email },
|
||||
replace: true,
|
||||
}),
|
||||
[email, navigate]
|
||||
);
|
||||
|
||||
// TODO implement broker to determine the the next screen
|
||||
const navigateToNextScreen = () => {
|
||||
logViewEvent(viewName, 'verification.success');
|
||||
// TODO check if isForcePasswordChange
|
||||
// navigate('/post_verify/password/force_password_change', {account})
|
||||
|
||||
// default behaviour: '/signup_confirmed'
|
||||
// if Sync: '/connect_another_device'
|
||||
// if Web: '/settings'
|
||||
navigate('/settings', { replace: true });
|
||||
};
|
||||
|
||||
// Adds a timeout before navigating to the /confirm_signup_code page
|
||||
// when the user clicks on the resend email link.
|
||||
useEffect(() => {
|
||||
function onTimeout() {
|
||||
navigateToConfirmCode();
|
||||
}
|
||||
let navigateTimeoutId: NodeJS.Timeout;
|
||||
if (resendStatus === ResendStatus.sent) {
|
||||
navigateTimeoutId = setTimeout(onTimeout, NAVIGATION_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
return () => {
|
||||
clearTimeout(navigateTimeoutId);
|
||||
};
|
||||
}, [navigateToConfirmCode, resendStatus]);
|
||||
|
||||
// Subscribe to account updates to find out if the session becomes verified.
|
||||
// Stay on this page until the session is verified, then automatically
|
||||
// navigate to the next screen.
|
||||
useInterval(async () => {
|
||||
try {
|
||||
const sessionVerified = await session.isSessionVerified();
|
||||
if (sessionVerified) {
|
||||
navigateToNextScreen();
|
||||
setIsPolling(null);
|
||||
}
|
||||
} catch (e) {
|
||||
setIsPolling(null);
|
||||
}
|
||||
}, isPolling);
|
||||
|
||||
const resendEmailHandler = async () => {
|
||||
try {
|
||||
// The logic here differs from content-server. Since we are moving away
|
||||
// from confirmation link, this resend function sends a verification code
|
||||
// instead of a verification link and navigates to /signup_confirm_code.
|
||||
// This avoids adding a (to be discontinued) method to the React Account model.
|
||||
await session.sendVerificationCode();
|
||||
setResendStatus(ResendStatus.sent);
|
||||
} catch (err) {
|
||||
const localizedErrorMessage = getLocalizedErrorMessage(
|
||||
ftlMsgResolver,
|
||||
err
|
||||
);
|
||||
setResendStatus(ResendStatus['error']);
|
||||
setErrorMessage(localizedErrorMessage);
|
||||
if (AuthUiErrors['INVALID_TOKEN'].errno === err.errno) {
|
||||
logErrorEvent({ viewName, ...err });
|
||||
// TODO: When redirectToSignup is changed to use navigate,
|
||||
// pass in the error so it can be displayed in an banner
|
||||
// on the /signup page
|
||||
redirectToSignup();
|
||||
}
|
||||
return;
|
||||
}
|
||||
setResendStatus(ResendStatus['sent']);
|
||||
};
|
||||
|
||||
if (showLoadingSpinner) {
|
||||
return <LoadingSpinner fullScreen />;
|
||||
}
|
||||
|
||||
return (
|
||||
<AppLayout>
|
||||
<ConfirmWithLink
|
||||
confirmWithLinkPageStrings={confirmSignupPageText}
|
||||
{...{
|
||||
email,
|
||||
resendEmailHandler,
|
||||
resendStatus,
|
||||
errorMessage,
|
||||
}}
|
||||
/>
|
||||
</AppLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Confirm;
|
|
@ -1,28 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import { Account } from 'fxa-settings/src/models';
|
||||
import {
|
||||
mockEmail,
|
||||
mockSession,
|
||||
MOCK_PROFILE_INFO,
|
||||
} from 'fxa-settings/src/models/mocks';
|
||||
|
||||
export const MOCK_PROFILE_WITH_RESEND_SUCCESS = {
|
||||
getProfileInfo: () => Promise.resolve(MOCK_PROFILE_INFO),
|
||||
sendVerificationCode: () => Promise.resolve({}),
|
||||
primaryEmail: mockEmail('blabidi@blabidiboop.com', true, false),
|
||||
} as unknown as Account;
|
||||
|
||||
export const MOCK_PROFILE_WITH_RESEND_ERROR = {
|
||||
getProfileInfo: () => Promise.resolve(MOCK_PROFILE_INFO),
|
||||
sendVerificationCode: () => Promise.reject(Error),
|
||||
primaryEmail: mockEmail('blabidi@blabidiboop.com', true, false),
|
||||
} as unknown as Account;
|
||||
|
||||
export const MOCK_UNVERIFIED_SESSION = mockSession(false);
|
||||
|
||||
export const MOCK_VERIFIED_SESSION = mockSession(true);
|
||||
|
||||
export const MOCK_SESSION_TOKEN = 'abc123';
|
Загрузка…
Ссылка в новой задаче