Skip to content

Commit 5476719

Browse files
Add init changes to refactor recover account email to be a separate feature
1 parent ce414be commit 5476719

File tree

7 files changed

+178
-40
lines changed

7 files changed

+178
-40
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved.
2+
*
3+
* This software is licensed under the Apache License, Version 2.0 (the
4+
* "License") as published by the Apache Software Foundation.
5+
*
6+
* You may not use this file except in compliance with the License. You may
7+
* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
* License for the specific language governing permissions and limitations
13+
* under the License.
14+
*/
15+
16+
import * as React from "react";
17+
18+
import { ComponentOverrideContext } from "../../../../../components/componentOverride/componentOverrideContext";
19+
import FeatureWrapper from "../../../../../components/featureWrapper";
20+
import SuperTokens from "../../../../../superTokens";
21+
import SendRecoveryEmailFormTheme from "../../themes/sendRecoveryEmail";
22+
import { defaultTranslationsWebauthn } from "../../themes/translations";
23+
24+
import type { SendRecoveryEmailFormProps } from "../../../types";
25+
26+
export const SendRecoveryEmailForm: React.FC<SendRecoveryEmailFormProps> = (props): JSX.Element => {
27+
let userContext;
28+
if (props.userContext !== undefined) {
29+
userContext = props.userContext;
30+
}
31+
const [error, setError] = React.useState<string>();
32+
33+
const childProps = {
34+
config: props.recipe.config,
35+
error: error,
36+
onError: (error: string) => setError(error),
37+
clearError: () => setError(undefined),
38+
recipeImplementation: props.recipe.webJSRecipe,
39+
useComponentOverride: props.useComponentOverrides,
40+
userContext,
41+
};
42+
const recipeComponentOverrides = props.useComponentOverrides();
43+
44+
return (
45+
<ComponentOverrideContext.Provider value={recipeComponentOverrides}>
46+
<FeatureWrapper
47+
useShadowDom={SuperTokens.getInstanceOrThrow().useShadowDom}
48+
defaultStore={defaultTranslationsWebauthn}>
49+
<React.Fragment>
50+
{/* No custom theme, use default. */}
51+
{props.children === undefined && <SendRecoveryEmailFormTheme {...childProps} />}
52+
{/* Otherwise, custom theme is provided, propagate props. */}
53+
{props.children &&
54+
React.Children.map(props.children, (child) => {
55+
if (React.isValidElement(child)) {
56+
return React.cloneElement(child, childProps);
57+
}
58+
59+
return child;
60+
})}
61+
</React.Fragment>
62+
</FeatureWrapper>
63+
</ComponentOverrideContext.Provider>
64+
);
65+
};

lib/ts/recipe/webauthn/components/themes/signUp/emailSent.tsx renamed to lib/ts/recipe/webauthn/components/themes/sendRecoveryEmail/emailSent.tsx

File renamed without changes.
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { useState } from "react";
2+
3+
import { redirectToAuth } from "../../../../..";
4+
import { SuperTokensBranding } from "../../../../../components/SuperTokensBranding";
5+
import SuperTokens from "../../../../../superTokens";
6+
import { AuthPageFooter } from "../../../../../ui";
7+
import UserContextWrapper from "../../../../../usercontext/userContextWrapper";
8+
import GeneralError from "../../../../emailpassword/components/library/generalError";
9+
import { ThemeBase } from "../themeBase";
10+
11+
import { PasskeyEmailSent } from "./emailSent";
12+
import { PasskeyRecoverAccountForm } from "./recoverAccountForm";
13+
14+
import type { SendRecoveryEmailFormThemeProps } from "../../../types";
15+
16+
export enum SendRecoveryEmailScreen {
17+
RecoverAccount,
18+
RecoverEmailSent,
19+
}
20+
21+
export const SendRecoveryEmailFormThemeInner = (
22+
props: SendRecoveryEmailFormThemeProps & {
23+
activeScreen: SendRecoveryEmailScreen;
24+
setActiveScreen: React.Dispatch<React.SetStateAction<SendRecoveryEmailScreen>>;
25+
}
26+
): JSX.Element | null => {
27+
const [recoverAccountEmail, setRecoverAccountEmail] = useState<string>("");
28+
const onRecoverAccountFormSuccess = (result: { email: string }) => {
29+
setRecoverAccountEmail(result.email);
30+
props.setActiveScreen(SendRecoveryEmailScreen.RecoverEmailSent);
31+
};
32+
33+
const onRecoverAccountBackClick = async () => {
34+
await redirectToAuth({ show: "signup" });
35+
};
36+
37+
const onEmailChangeClick = () => {
38+
props.setActiveScreen(SendRecoveryEmailScreen.RecoverAccount);
39+
};
40+
41+
return props.activeScreen === SendRecoveryEmailScreen.RecoverAccount ? (
42+
<PasskeyRecoverAccountForm
43+
onSuccess={onRecoverAccountFormSuccess}
44+
onBackClick={onRecoverAccountBackClick}
45+
recipeImplementation={props.recipeImplementation}
46+
setError={(error) => console.error(error)}
47+
/>
48+
) : props.activeScreen === SendRecoveryEmailScreen.RecoverEmailSent ? (
49+
<PasskeyEmailSent email={recoverAccountEmail} onEmailChangeClick={onEmailChangeClick} />
50+
) : null;
51+
};
52+
53+
const SendRecoveryEmailFormTheme = (props: SendRecoveryEmailFormThemeProps): JSX.Element => {
54+
const stInstance = SuperTokens.getInstanceOrThrow();
55+
const rootStyle = stInstance.rootStyle;
56+
const privacyPolicyLink = stInstance.privacyPolicyLink;
57+
const termsOfServiceLink = stInstance.termsOfServiceLink;
58+
59+
const [activeScreen, setActiveScreen] = useState<SendRecoveryEmailScreen>(SendRecoveryEmailScreen.RecoverAccount);
60+
61+
return (
62+
<UserContextWrapper userContext={props.userContext}>
63+
<ThemeBase userStyles={[rootStyle, props.config.recipeRootStyle]}>
64+
<div data-supertokens="container authPage recoverAccountWithEmail">
65+
<div data-supertokens="row">
66+
{props.error !== undefined && <GeneralError error={props.error} />}
67+
<SendRecoveryEmailFormThemeInner
68+
{...props}
69+
activeScreen={activeScreen}
70+
setActiveScreen={setActiveScreen}
71+
/>
72+
{activeScreen !== SendRecoveryEmailScreen.RecoverEmailSent && (
73+
<AuthPageFooter
74+
factorIds={[]}
75+
isSignUp={true}
76+
hasSeparateSignUpView={true}
77+
privacyPolicyLink={privacyPolicyLink}
78+
termsOfServiceLink={termsOfServiceLink}
79+
/>
80+
)}
81+
</div>
82+
<SuperTokensBranding />
83+
</div>
84+
</ThemeBase>
85+
</UserContextWrapper>
86+
);
87+
};
88+
89+
export default SendRecoveryEmailFormTheme;

lib/ts/recipe/webauthn/components/themes/signUp/recoverAccountForm.tsx renamed to lib/ts/recipe/webauthn/components/themes/sendRecoveryEmail/recoverAccountForm.tsx

File renamed without changes.

lib/ts/recipe/webauthn/components/themes/signUp/index.tsx

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,7 @@ function PasskeySignUpTheme(props: SignUpThemeProps): JSX.Element {
4949
props.factorIds.length > 1 ? "multiFactor" : "singleFactor"
5050
}`}>
5151
<div data-supertokens="row">
52-
{![SignUpScreen.Error, SignUpScreen.RecoverAccount, SignUpScreen.RecoverEmailSent].includes(
53-
activeScreen
54-
) && (
52+
{![SignUpScreen.Error].includes(activeScreen) && (
5553
<AuthPageHeader
5654
factorIds={props.factorIds}
5755
isSignUp={true}
@@ -75,15 +73,13 @@ function PasskeySignUpTheme(props: SignUpThemeProps): JSX.Element {
7573
activeScreen={activeScreen}
7674
setActiveScreen={setActiveScreen}
7775
/>
78-
{activeScreen !== SignUpScreen.RecoverEmailSent && (
79-
<AuthPageFooter
80-
factorIds={props.factorIds}
81-
isSignUp={true}
82-
hasSeparateSignUpView={true}
83-
privacyPolicyLink={privacyPolicyLink}
84-
termsOfServiceLink={termsOfServiceLink}
85-
/>
86-
)}
76+
<AuthPageFooter
77+
factorIds={props.factorIds}
78+
isSignUp={true}
79+
hasSeparateSignUpView={true}
80+
privacyPolicyLink={privacyPolicyLink}
81+
termsOfServiceLink={termsOfServiceLink}
82+
/>
8783
</div>
8884
<SuperTokensBranding />
8985
</div>

lib/ts/recipe/webauthn/components/themes/signUp/signUpForm.tsx

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ import { defaultEmailValidator } from "../../../../emailpassword/validators";
2626

2727
import { PasskeyConfirmation } from "./confirmation";
2828
import { ContinueWithoutPasskey } from "./continueWithoutPasskey";
29-
import { PasskeyEmailSent } from "./emailSent";
30-
import { PasskeyRecoverAccountForm } from "./recoverAccountForm";
3129
import { SignUpSomethingWentWrong } from "./somethingWentWrong";
3230

3331
import type { APIFormField } from "../../../../../types";
@@ -38,8 +36,6 @@ export enum SignUpScreen {
3836
SignUpForm,
3937
PasskeyConfirmation,
4038
Error,
41-
RecoverAccount,
42-
RecoverEmailSent,
4339
}
4440

4541
export const SignUpFormInner = withOverride(
@@ -79,7 +75,7 @@ export const SignUpFormInner = withOverride(
7975
<div data-supertokens="formLabelWithLinkWrapper">
8076
<Label value={"WEBAUTHN_SIGN_UP_LABEL"} data-supertokens="emailInputLabel" />
8177
<a
82-
onClick={() => props.setActiveScreen(SignUpScreen.RecoverAccount)}
78+
onClick={() => console.error("Recover account link: to be defined")}
8379
data-supertokens="link linkButton formLabelLinkBtn recoverAccountTrigger">
8480
{t("WEBAUTHN_RECOVER_ACCOUNT_LABEL")}
8581
</a>
@@ -130,7 +126,6 @@ export const SignUpForm = (
130126
const userContext = useUserContext();
131127
const [showPasskeyConfirmationError, setShowPasskeyConfirmationError] = useState(false);
132128
const [isLoading, setIsLoading] = useState(false);
133-
const [recoverAccountEmail, setRecoverAccountEmail] = useState<string>("");
134129

135130
const onContinueClickCallback = useCallback(
136131
(params: ContinueOnSuccessParams) => {
@@ -200,19 +195,6 @@ export const SignUpForm = (
200195
}
201196
}, [callAPI, props]);
202197

203-
const onRecoverAccountFormSuccess = (result: { email: string }) => {
204-
setRecoverAccountEmail(result.email);
205-
props.setActiveScreen(SignUpScreen.RecoverEmailSent);
206-
};
207-
208-
const onRecoverAccountBackClick = () => {
209-
props.setActiveScreen(SignUpScreen.SignUpForm);
210-
};
211-
212-
const onEmailChangeClick = () => {
213-
props.setActiveScreen(SignUpScreen.RecoverAccount);
214-
};
215-
216198
return props.activeScreen === SignUpScreen.SignUpForm ? (
217199
<SignUpFormInner {...props} onContinueClick={onContinueClickCallback} />
218200
) : props.activeScreen === SignUpScreen.PasskeyConfirmation ? (
@@ -225,14 +207,5 @@ export const SignUpForm = (
225207
/>
226208
) : props.activeScreen === SignUpScreen.Error ? (
227209
<SignUpSomethingWentWrong onClick={() => props.setActiveScreen(SignUpScreen.SignUpForm)} />
228-
) : props.activeScreen === SignUpScreen.RecoverAccount ? (
229-
<PasskeyRecoverAccountForm
230-
onSuccess={onRecoverAccountFormSuccess}
231-
onBackClick={onRecoverAccountBackClick}
232-
recipeImplementation={props.recipeImplementation}
233-
setError={(error) => setShowPasskeyConfirmationError(Boolean(error))}
234-
/>
235-
) : props.activeScreen === SignUpScreen.RecoverEmailSent ? (
236-
<PasskeyEmailSent email={recoverAccountEmail} onEmailChangeClick={onEmailChangeClick} />
237210
) : null;
238211
};

lib/ts/recipe/webauthn/types.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,21 @@ export type RecoverAccountWithTokenThemeProps = {
160160
token: string | null;
161161
};
162162

163+
export type SendRecoveryEmailFormProps = {
164+
userContext?: UserContext | undefined;
165+
recipe: Recipe;
166+
useComponentOverrides: () => ComponentOverrideMap;
167+
};
168+
169+
export type SendRecoveryEmailFormThemeProps = {
170+
config: NormalisedConfig;
171+
userContext?: UserContext;
172+
recipeImplementation: RecipeImplementation;
173+
error: string | undefined;
174+
clearError: () => void;
175+
onError: (error: string) => void;
176+
};
177+
163178
export type ContinueOnSuccessParams = {
164179
email: string;
165180
};

0 commit comments

Comments
 (0)