зеркало из https://github.com/mozilla/fxa.git
move logic up from index to container, set up storybook
This commit is contained in:
Родитель
8267d2b26d
Коммит
a853a23ce6
|
@ -2,6 +2,7 @@
|
|||
* 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 { FtlMsg } from 'fxa-react/lib/utils';
|
||||
import FormPasswordWithBalloons from '../FormPasswordWithBalloons';
|
||||
import InputText from '../InputText';
|
||||
|
|
|
@ -21,7 +21,7 @@ export type FormSetupAccountProps = {
|
|||
onSubmit: (e?: React.BaseSyntheticEvent) => Promise<void>;
|
||||
loading: boolean;
|
||||
isSync: boolean;
|
||||
offeredSyncEngineConfigs?: typeof syncEngineConfigs;
|
||||
offeredSyncEngineConfigs?: typeof syncEngineConfigs | undefined;
|
||||
setDeclinedSyncEngines: React.Dispatch<React.SetStateAction<string[]>>;
|
||||
isDesktopRelay: boolean;
|
||||
setSelectedNewsletterSlugs?: React.Dispatch<React.SetStateAction<string[]>>;
|
||||
|
|
|
@ -2,24 +2,29 @@
|
|||
* 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 { RouteComponentProps } from '@reach/router';
|
||||
import React from 'react';
|
||||
import { RouteComponentProps, useLocation } from '@reach/router';
|
||||
import SetPassword from '.';
|
||||
import { currentAccount } from '../../../lib/cache';
|
||||
import LoadingSpinner from 'fxa-react/components/LoadingSpinner';
|
||||
import { useNavigateWithQuery as useNavigate } from '../../../lib/hooks/useNavigateWithQuery';
|
||||
import { useAuthClient, useSensitiveDataClient } from '../../../models';
|
||||
import { cache } from '../../../lib/cache';
|
||||
import { useCallback } from 'react';
|
||||
import { CreatePasswordHandler, SetPasswordIntegration } from './interfaces';
|
||||
import { HandledError } from '../../../lib/error-utils';
|
||||
import {
|
||||
AuthUiErrorNos,
|
||||
AuthUiErrors,
|
||||
} from '../../../lib/auth-errors/auth-errors';
|
||||
isOAuthNativeIntegration,
|
||||
useAuthClient,
|
||||
useFtlMsgResolver,
|
||||
useSensitiveDataClient,
|
||||
} from '../../../models';
|
||||
import { cache } from '../../../lib/cache';
|
||||
import { useState } from 'react';
|
||||
import { CreatePasswordHandler, SetPasswordIntegration } from './interfaces';
|
||||
import { getLocalizedErrorMessage } from '../../../lib/error-utils';
|
||||
import {
|
||||
AUTH_DATA_KEY,
|
||||
SensitiveDataClientAuthKeys,
|
||||
} from '../../../lib/sensitive-data-client';
|
||||
import useSyncEngines from '../../../lib/hooks/useSyncEngines';
|
||||
import { getSyncNavigate } from '../../Signin/utils';
|
||||
import firefox from '../../../lib/channels/firefox';
|
||||
|
||||
const SetPasswordContainer = ({
|
||||
integration,
|
||||
|
@ -36,33 +41,19 @@ const SetPasswordContainer = ({
|
|||
const { keyFetchToken, unwrapBKey } =
|
||||
(sensitiveData as SensitiveDataClientAuthKeys) || {};
|
||||
|
||||
const createPasswordHandler: CreatePasswordHandler = useCallback(
|
||||
async (email, sessionToken, newPassword) => {
|
||||
try {
|
||||
const passwordCreated = await authClient.createPassword(
|
||||
sessionToken,
|
||||
email,
|
||||
newPassword
|
||||
);
|
||||
cache.modify({
|
||||
id: cache.identify({ __typename: 'Account' }),
|
||||
fields: {
|
||||
passwordCreated() {
|
||||
return passwordCreated;
|
||||
},
|
||||
},
|
||||
});
|
||||
return { error: null };
|
||||
} catch (error) {
|
||||
const { errno } = error as HandledError;
|
||||
if (errno && AuthUiErrorNos[errno]) {
|
||||
return { error };
|
||||
}
|
||||
return { error: AuthUiErrors.UNEXPECTED_ERROR as HandledError };
|
||||
}
|
||||
},
|
||||
[authClient]
|
||||
);
|
||||
const ftlMsgResolver = useFtlMsgResolver();
|
||||
const location = useLocation();
|
||||
const [createPasswordLoading, setCreatePasswordLoading] =
|
||||
useState<boolean>(false);
|
||||
const [bannerErrorText, setBannerErrorText] = useState<string>('');
|
||||
const {
|
||||
offeredSyncEngines,
|
||||
offeredSyncEngineConfigs,
|
||||
declinedSyncEngines,
|
||||
setDeclinedSyncEngines,
|
||||
// TODO some metrics on this page?
|
||||
} = useSyncEngines(integration);
|
||||
const isOAuthNative = isOAuthNativeIntegration(integration);
|
||||
|
||||
// Users must be already authenticated on this page. This page is currently always
|
||||
// for the Sync flow so keys are always required.
|
||||
|
@ -78,16 +69,87 @@ const SetPasswordContainer = ({
|
|||
return <LoadingSpinner />;
|
||||
}
|
||||
|
||||
const createPasswordHandler: CreatePasswordHandler = async (newPassword) => {
|
||||
try {
|
||||
const passwordCreated = await authClient.createPassword(
|
||||
sessionToken,
|
||||
email,
|
||||
newPassword
|
||||
);
|
||||
cache.modify({
|
||||
id: cache.identify({ __typename: 'Account' }),
|
||||
fields: {
|
||||
passwordCreated() {
|
||||
return passwordCreated;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const syncEngines = {
|
||||
offeredEngines: offeredSyncEngines,
|
||||
declinedEngines: declinedSyncEngines,
|
||||
};
|
||||
|
||||
// GleanMetrics.registration.cwts({ sync: { cwts: syncOptions } });
|
||||
firefox.fxaLogin({
|
||||
email,
|
||||
// Do not send these values if OAuth. Mobile doesn't care about this message, and
|
||||
// sending these values can cause intermittent sync disconnect issues in oauth desktop.
|
||||
...(!isOAuthNative && {
|
||||
keyFetchToken,
|
||||
unwrapBKey,
|
||||
}),
|
||||
sessionToken,
|
||||
uid,
|
||||
verified: true,
|
||||
services: {
|
||||
sync: syncEngines,
|
||||
},
|
||||
});
|
||||
|
||||
// TODO: call finishOAuthFlowHandler here or needs reauth?
|
||||
// if (
|
||||
// isOAuthNative &&
|
||||
// oauthData
|
||||
// ) {
|
||||
// firefox.fxaOAuthLogin({
|
||||
// action: 'signin',
|
||||
// code: oauthData.code,
|
||||
// redirect: oauthData.redirect,
|
||||
// state: oauthData.state,
|
||||
// });
|
||||
// }
|
||||
|
||||
// navigate to inline recovery key setup
|
||||
const { to } = getSyncNavigate(location.search, true);
|
||||
navigate(to, {
|
||||
state: {
|
||||
email,
|
||||
uid,
|
||||
sessionToken,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
const localizedErrorMessage = getLocalizedErrorMessage(
|
||||
ftlMsgResolver,
|
||||
error
|
||||
);
|
||||
setBannerErrorText(localizedErrorMessage);
|
||||
// if the request errored, loading state must be marked as false to reenable submission
|
||||
setCreatePasswordLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<SetPassword
|
||||
{...{
|
||||
email,
|
||||
sessionToken,
|
||||
uid,
|
||||
createPasswordHandler,
|
||||
keyFetchToken,
|
||||
unwrapBKey,
|
||||
integration,
|
||||
setCreatePasswordLoading,
|
||||
createPasswordLoading,
|
||||
bannerErrorText,
|
||||
offeredSyncEngineConfigs,
|
||||
setDeclinedSyncEngines,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
import React from 'react';
|
||||
import { Meta } from '@storybook/react';
|
||||
import { SetPassword } from './index';
|
||||
import { SetPasswordProps } from './interfaces';
|
||||
import { withLocalization } from 'fxa-react/lib/storybooks';
|
||||
import { syncEngineConfigs } from '../../../components/ChooseWhatToSync/sync-engines';
|
||||
|
||||
export default {
|
||||
title: 'Pages/PostVerify/SetPassword',
|
||||
component: SetPassword,
|
||||
decorators: [
|
||||
withLocalization,
|
||||
(Story) => {
|
||||
const [, setDeclinedSyncEngines] = React.useState<string[]>([]);
|
||||
return <Story {...{ setDeclinedSyncEngines }} />;
|
||||
},
|
||||
],
|
||||
} as Meta;
|
||||
|
||||
const defaultArgs: Omit<SetPasswordProps, 'setDeclinedSyncEngines'> = {
|
||||
bannerErrorText: '',
|
||||
email: 'test@example.com',
|
||||
createPasswordHandler: async (password: string) => {
|
||||
console.log('Password created:', password);
|
||||
},
|
||||
setCreatePasswordLoading: (loading: boolean) => {
|
||||
console.log('Loading state:', loading);
|
||||
},
|
||||
createPasswordLoading: false,
|
||||
offeredSyncEngineConfigs: syncEngineConfigs,
|
||||
};
|
||||
|
||||
export const Default = ({
|
||||
setDeclinedSyncEngines,
|
||||
}: Pick<SetPasswordProps, 'setDeclinedSyncEngines'>) => (
|
||||
<SetPassword
|
||||
{...defaultArgs}
|
||||
setDeclinedSyncEngines={setDeclinedSyncEngines}
|
||||
/>
|
||||
);
|
||||
|
||||
export const WithError = ({
|
||||
setDeclinedSyncEngines,
|
||||
}: Pick<SetPasswordProps, 'setDeclinedSyncEngines'>) => (
|
||||
<SetPassword
|
||||
{...defaultArgs}
|
||||
bannerErrorText="An error occurred"
|
||||
setDeclinedSyncEngines={setDeclinedSyncEngines}
|
||||
/>
|
||||
);
|
|
@ -2,125 +2,31 @@
|
|||
* 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 { FtlMsg } from 'fxa-react/lib/utils';
|
||||
import AppLayout from '../../../components/AppLayout';
|
||||
import { FormSetupAccount } from '../../../components/FormSetupAccount';
|
||||
import { SetPasswordFormData, SetPasswordProps } from './interfaces';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useCallback, useState } from 'react';
|
||||
import firefox from '../../../lib/channels/firefox';
|
||||
import { isOAuthNativeIntegration, useFtlMsgResolver } from '../../../models';
|
||||
import { getLocalizedErrorMessage } from '../../../lib/error-utils';
|
||||
import { useCallback } from 'react';
|
||||
import Banner from '../../../components/Banner';
|
||||
import { useLocation, useNavigate } from '@reach/router';
|
||||
import { getSyncNavigate } from '../../Signin/utils';
|
||||
import useSyncEngines from '../../../lib/hooks/useSyncEngines';
|
||||
|
||||
export const SetPassword = ({
|
||||
bannerErrorText,
|
||||
email,
|
||||
sessionToken,
|
||||
uid,
|
||||
createPasswordHandler,
|
||||
keyFetchToken,
|
||||
unwrapBKey,
|
||||
integration,
|
||||
setCreatePasswordLoading,
|
||||
createPasswordLoading,
|
||||
offeredSyncEngineConfigs,
|
||||
setDeclinedSyncEngines,
|
||||
}: SetPasswordProps) => {
|
||||
const ftlMsgResolver = useFtlMsgResolver();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const [createPasswordLoading, setCreatePasswordLoading] =
|
||||
useState<boolean>(false);
|
||||
const [bannerErrorText, setBannerErrorText] = useState<string>('');
|
||||
const {
|
||||
offeredSyncEngines,
|
||||
offeredSyncEngineConfigs,
|
||||
declinedSyncEngines,
|
||||
setDeclinedSyncEngines,
|
||||
// TODO some metrics on this page?
|
||||
selectedEngines,
|
||||
} = useSyncEngines(integration);
|
||||
const isOAuthNative = isOAuthNativeIntegration(integration);
|
||||
|
||||
|
||||
const onSubmit = useCallback(
|
||||
async ({ newPassword }: SetPasswordFormData) => {
|
||||
setCreatePasswordLoading(true);
|
||||
|
||||
const { error } = await createPasswordHandler(
|
||||
email,
|
||||
sessionToken,
|
||||
newPassword
|
||||
);
|
||||
|
||||
if (error) {
|
||||
const localizedErrorMessage = getLocalizedErrorMessage(
|
||||
ftlMsgResolver,
|
||||
error
|
||||
);
|
||||
setBannerErrorText(localizedErrorMessage);
|
||||
// if the request errored, loading state must be marked as false to reenable submission
|
||||
setCreatePasswordLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const syncEngines = {
|
||||
offeredEngines: offeredSyncEngines,
|
||||
declinedEngines: declinedSyncEngines,
|
||||
};
|
||||
|
||||
// GleanMetrics.registration.cwts({ sync: { cwts: syncOptions } });
|
||||
firefox.fxaLogin({
|
||||
email,
|
||||
// Do not send these values if OAuth. Mobile doesn't care about this message, and
|
||||
// sending these values can cause intermittent sync disconnect issues in oauth desktop.
|
||||
...(!isOAuthNative && {
|
||||
keyFetchToken,
|
||||
unwrapBKey,
|
||||
}),
|
||||
sessionToken,
|
||||
uid,
|
||||
verified: true,
|
||||
services: {
|
||||
sync: syncEngines,
|
||||
},
|
||||
});
|
||||
|
||||
// TODO: call finishOAuthFlowHandler here or needs reauth?
|
||||
// if (
|
||||
// isOAuthNative &&
|
||||
// oauthData
|
||||
// ) {
|
||||
// firefox.fxaOAuthLogin({
|
||||
// action: 'signin',
|
||||
// code: oauthData.code,
|
||||
// redirect: oauthData.redirect,
|
||||
// state: oauthData.state,
|
||||
// });
|
||||
// }
|
||||
|
||||
// navigate to inline recovery key setup
|
||||
const { to } = getSyncNavigate(location.search, true);
|
||||
navigate(to, {
|
||||
state: {
|
||||
email,
|
||||
uid,
|
||||
sessionToken,
|
||||
},
|
||||
});
|
||||
await createPasswordHandler(newPassword);
|
||||
},
|
||||
[
|
||||
createPasswordHandler,
|
||||
email,
|
||||
ftlMsgResolver,
|
||||
isOAuthNative,
|
||||
keyFetchToken,
|
||||
location.search,
|
||||
navigate,
|
||||
sessionToken,
|
||||
uid,
|
||||
unwrapBKey,
|
||||
declinedSyncEngines,
|
||||
offeredSyncEngines,
|
||||
]
|
||||
[createPasswordHandler, setCreatePasswordLoading]
|
||||
);
|
||||
|
||||
const { handleSubmit, register, getValues, errors, formState, trigger } =
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* 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 { HandledError } from '../../../lib/error-utils';
|
||||
import { syncEngineConfigs } from '../../../components/ChooseWhatToSync/sync-engines';
|
||||
import { OAuthIntegration } from './../../../models/integrations/oauth-native-integration';
|
||||
|
||||
export type SetPasswordIntegration = Pick<OAuthIntegration, 'type' | 'isSync'>;
|
||||
|
@ -13,22 +13,14 @@ export interface SetPasswordFormData {
|
|||
confirmPassword: string;
|
||||
}
|
||||
|
||||
export interface CreatePasswordHandlerError {
|
||||
error: HandledError | null;
|
||||
}
|
||||
|
||||
export type CreatePasswordHandler = (
|
||||
email: string,
|
||||
sessionToken: string,
|
||||
newPassword: string
|
||||
) => Promise<CreatePasswordHandlerError>;
|
||||
export type CreatePasswordHandler = (newPassword: string) => Promise<void>;
|
||||
|
||||
export interface SetPasswordProps {
|
||||
email: string;
|
||||
sessionToken: string;
|
||||
uid: string;
|
||||
createPasswordHandler: CreatePasswordHandler;
|
||||
keyFetchToken: string;
|
||||
unwrapBKey: string;
|
||||
integration: SetPasswordIntegration;
|
||||
setCreatePasswordLoading: (loading: boolean) => void;
|
||||
createPasswordLoading: boolean;
|
||||
bannerErrorText: string;
|
||||
offeredSyncEngineConfigs: typeof syncEngineConfigs | undefined;
|
||||
setDeclinedSyncEngines: React.Dispatch<React.SetStateAction<string[]>>;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче