Update
This commit is contained in:
@@ -35,7 +35,7 @@ import {
|
||||
setJustSignedUp,
|
||||
setLocalReferralSource,
|
||||
} from "ente-shared/storage/localStorage/helpers";
|
||||
import { useFormik, type FormikHelpers } from "formik";
|
||||
import { useFormik } from "formik";
|
||||
import { t } from "i18next";
|
||||
import type { NextRouter } from "next/router";
|
||||
import React, { useCallback, useState } from "react";
|
||||
@@ -47,13 +47,6 @@ import {
|
||||
AccountsPageTitle,
|
||||
} from "./layouts/centered-paper";
|
||||
|
||||
interface FormValues {
|
||||
email: string;
|
||||
passphrase: string;
|
||||
confirm: string;
|
||||
referral: string;
|
||||
}
|
||||
|
||||
interface SignUpContentsProps {
|
||||
router: NextRouter;
|
||||
/** Called when the user clicks the login option instead. */
|
||||
@@ -76,104 +69,108 @@ export const SignUpContents: React.FC<SignUpContentsProps> = ({
|
||||
[],
|
||||
);
|
||||
|
||||
const registerUser = async (
|
||||
{ email, passphrase, confirm, referral }: FormValues,
|
||||
{ setFieldError }: FormikHelpers<FormValues>,
|
||||
) => {
|
||||
try {
|
||||
if (passphrase !== confirm) {
|
||||
setFieldError("confirm", t("password_mismatch_error"));
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
try {
|
||||
setLocalReferralSource(referral);
|
||||
await sendOTT(email, "signup");
|
||||
await setLSUser({ email });
|
||||
} catch (e) {
|
||||
log.error("Signup failed", e);
|
||||
if (
|
||||
await isMuseumHTTPError(e, 409, "USER_ALREADY_REGISTERED")
|
||||
) {
|
||||
setFieldError("email", t("email_already_registered"));
|
||||
} else {
|
||||
setFieldError("email", t("generic_error"));
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
try {
|
||||
const { masterKey, kek, keyAttributes } =
|
||||
await generateKeysAndAttributes(passphrase);
|
||||
|
||||
const srpSetupAttributes = await generateSRPSetupAttributes(
|
||||
await deriveSRPPassword(kek),
|
||||
);
|
||||
|
||||
setData("originalKeyAttributes", keyAttributes);
|
||||
setData("srpSetupAttributes", srpSetupAttributes);
|
||||
await generateAndSaveInteractiveKeyAttributes(
|
||||
passphrase,
|
||||
keyAttributes,
|
||||
masterKey,
|
||||
);
|
||||
|
||||
await saveMasterKeyInSessionAndSafeStore(masterKey);
|
||||
setJustSignedUp(true);
|
||||
void router.push("/verify");
|
||||
} catch (e) {
|
||||
setFieldError("confirm", t("password_generation_failed"));
|
||||
throw e;
|
||||
}
|
||||
} catch (e) {
|
||||
log.error("signup failed", e);
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const { values, errors, handleChange, handleSubmit } = useFormik({
|
||||
initialValues: { email: "", passphrase: "", confirm: "", referral: "" },
|
||||
const formik = useFormik({
|
||||
initialValues: {
|
||||
email: "",
|
||||
password: "",
|
||||
confirmPassword: "",
|
||||
referral: "",
|
||||
},
|
||||
validationSchema: Yup.object().shape({
|
||||
email: Yup.string()
|
||||
.email(t("invalid_email_error"))
|
||||
.required(t("required")),
|
||||
passphrase: Yup.string().required(t("required")),
|
||||
confirm: Yup.string().required(t("required")),
|
||||
password: Yup.string().required(t("required")),
|
||||
confirmPassword: Yup.string().required(t("required")),
|
||||
}),
|
||||
validateOnChange: false,
|
||||
validateOnBlur: false,
|
||||
onSubmit: registerUser,
|
||||
onSubmit: async (
|
||||
{ email, password, confirmPassword, referral },
|
||||
{ setFieldError },
|
||||
) => {
|
||||
try {
|
||||
if (password != confirmPassword) {
|
||||
setFieldError("confirm", t("password_mismatch_error"));
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
try {
|
||||
setLocalReferralSource(referral);
|
||||
await sendOTT(email, "signup");
|
||||
await setLSUser({ email });
|
||||
} catch (e) {
|
||||
log.error("Signup failed", e);
|
||||
if (
|
||||
await isMuseumHTTPError(
|
||||
e,
|
||||
409,
|
||||
"USER_ALREADY_REGISTERED",
|
||||
)
|
||||
) {
|
||||
setFieldError("email", t("email_already_registered"));
|
||||
} else {
|
||||
setFieldError("email", t("generic_error"));
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
try {
|
||||
const { masterKey, kek, keyAttributes } =
|
||||
await generateKeysAndAttributes(password);
|
||||
|
||||
const srpSetupAttributes = await generateSRPSetupAttributes(
|
||||
await deriveSRPPassword(kek),
|
||||
);
|
||||
|
||||
setData("originalKeyAttributes", keyAttributes);
|
||||
setData("srpSetupAttributes", srpSetupAttributes);
|
||||
await generateAndSaveInteractiveKeyAttributes(
|
||||
password,
|
||||
keyAttributes,
|
||||
masterKey,
|
||||
);
|
||||
|
||||
await saveMasterKeyInSessionAndSafeStore(masterKey);
|
||||
setJustSignedUp(true);
|
||||
void router.push("/verify");
|
||||
} catch (e) {
|
||||
setFieldError("confirm", t("password_generation_failed"));
|
||||
throw e;
|
||||
}
|
||||
} catch (e) {
|
||||
log.error("signup failed", e);
|
||||
}
|
||||
setLoading(false);
|
||||
},
|
||||
});
|
||||
|
||||
const form = (
|
||||
<form noValidate onSubmit={handleSubmit}>
|
||||
<form noValidate onSubmit={formik.handleSubmit}>
|
||||
<Stack sx={{ mb: 2 }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
id="email"
|
||||
name="email"
|
||||
autoComplete="username"
|
||||
type="email"
|
||||
autoComplete="username"
|
||||
label={t("enter_email")}
|
||||
value={values.email}
|
||||
onChange={handleChange("email")}
|
||||
error={Boolean(errors.email)}
|
||||
helperText={errors.email}
|
||||
autoFocus
|
||||
value={formik.values.email}
|
||||
onChange={formik.handleChange}
|
||||
error={!!formik.errors.email}
|
||||
helperText={formik.errors.email}
|
||||
disabled={loading}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
fullWidth
|
||||
id="password"
|
||||
autoFocus
|
||||
/>
|
||||
<TextField
|
||||
name="password"
|
||||
autoComplete="new-password"
|
||||
type={showPassword ? "text" : "password"}
|
||||
label={t("password")}
|
||||
value={values.passphrase}
|
||||
onChange={handleChange("passphrase")}
|
||||
error={Boolean(errors.passphrase)}
|
||||
helperText={errors.passphrase}
|
||||
value={formik.values.password}
|
||||
onChange={formik.handleChange}
|
||||
error={!!formik.errors.password}
|
||||
helperText={formik.errors.password}
|
||||
disabled={loading}
|
||||
fullWidth
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
@@ -185,22 +182,19 @@ export const SignUpContents: React.FC<SignUpContentsProps> = ({
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
fullWidth
|
||||
id="confirm-password"
|
||||
name="confirm-password"
|
||||
name="confirmPassword"
|
||||
autoComplete="new-password"
|
||||
type="password"
|
||||
label={t("confirm_password")}
|
||||
value={values.confirm}
|
||||
onChange={handleChange("confirm")}
|
||||
error={Boolean(errors.confirm)}
|
||||
helperText={errors.confirm}
|
||||
value={formik.values.confirmPassword}
|
||||
onChange={formik.handleChange}
|
||||
error={!!formik.errors.confirmPassword}
|
||||
helperText={formik.errors.confirmPassword}
|
||||
disabled={loading}
|
||||
fullWidth
|
||||
/>
|
||||
<PasswordStrengthHint password={values.passphrase} />
|
||||
|
||||
<PasswordStrengthHint password={formik.values.password} />
|
||||
<Box sx={{ width: "100%" }}>
|
||||
<Typography
|
||||
sx={{
|
||||
@@ -213,13 +207,13 @@ export const SignUpContents: React.FC<SignUpContentsProps> = ({
|
||||
</Typography>
|
||||
<TextField
|
||||
hiddenLabel
|
||||
fullWidth
|
||||
name="referral"
|
||||
type="text"
|
||||
value={values.referral}
|
||||
onChange={handleChange("referral")}
|
||||
error={Boolean(errors.referral)}
|
||||
value={formik.values.referral}
|
||||
onChange={formik.handleChange}
|
||||
error={!!formik.errors.referral}
|
||||
disabled={loading}
|
||||
fullWidth
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
@@ -285,7 +279,9 @@ export const SignUpContents: React.FC<SignUpContentsProps> = ({
|
||||
color="accent"
|
||||
type="submit"
|
||||
loading={loading}
|
||||
disabled={!acceptTerms || isWeakPassword(values.passphrase)}
|
||||
disabled={
|
||||
!acceptTerms || isWeakPassword(formik.values.password)
|
||||
}
|
||||
>
|
||||
{t("create_account")}
|
||||
</LoadingButton>
|
||||
|
||||
@@ -56,7 +56,7 @@ const DeleteAccountDialogContents: React.FC<
|
||||
const [acceptDataDeletion, setAcceptDataDeletion] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const { values, touched, errors, handleChange, handleSubmit } = useFormik({
|
||||
const formik = useFormik({
|
||||
initialValues: { reason: "", feedback: "" },
|
||||
validate: ({ reason, feedback }) => {
|
||||
if (!reason) return { reason: t("required") };
|
||||
@@ -144,30 +144,32 @@ const DeleteAccountDialogContents: React.FC<
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<form onSubmit={formik.handleSubmit}>
|
||||
<Stack sx={{ gap: "24px" }}>
|
||||
<Stack sx={{ gap: "4px" }}>
|
||||
<Typography>{t("delete_account_reason_label")}</Typography>
|
||||
<DropdownInput
|
||||
options={deleteReasonOptions()}
|
||||
placeholder={t("delete_account_reason_placeholder")}
|
||||
selected={values.reason}
|
||||
onSelect={handleChange("reason")}
|
||||
selected={formik.values.reason}
|
||||
onSelect={formik.handleChange}
|
||||
/>
|
||||
{touched.reason && errors.reason && (
|
||||
{formik.touched.reason && formik.errors.reason && (
|
||||
<Typography
|
||||
variant="small"
|
||||
sx={{ px: 1, color: "critical.main" }}
|
||||
>
|
||||
{errors.reason}
|
||||
{formik.errors.reason}
|
||||
</Typography>
|
||||
)}
|
||||
</Stack>
|
||||
<FeedbackInput
|
||||
value={values.feedback}
|
||||
onChange={handleChange("feedback")}
|
||||
value={formik.values.feedback}
|
||||
onChange={formik.handleChange}
|
||||
errorMessage={
|
||||
touched.feedback ? errors.feedback : undefined
|
||||
formik.touched.feedback
|
||||
? formik.errors.feedback
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
<ConfirmationCheckboxInput
|
||||
|
||||
@@ -87,7 +87,7 @@ type FormProps = ContentsProps & {
|
||||
};
|
||||
|
||||
const Form: React.FC<FormProps> = ({ initialAPIOrigin, onClose }) => {
|
||||
const form = useFormik({
|
||||
const formik = useFormik({
|
||||
initialValues: { apiOrigin: initialAPIOrigin },
|
||||
validate: ({ apiOrigin }) => {
|
||||
try {
|
||||
@@ -121,12 +121,12 @@ const Form: React.FC<FormProps> = ({ initialAPIOrigin, onClose }) => {
|
||||
// touched state of apiOrigin gets set too early, perhaps because of the
|
||||
// autoFocus).
|
||||
const hasError =
|
||||
form.submitCount > 0 &&
|
||||
form.touched.apiOrigin &&
|
||||
!!form.errors.apiOrigin;
|
||||
formik.submitCount > 0 &&
|
||||
formik.touched.apiOrigin &&
|
||||
!!formik.errors.apiOrigin;
|
||||
|
||||
return (
|
||||
<form onSubmit={form.handleSubmit}>
|
||||
<form onSubmit={formik.handleSubmit}>
|
||||
<DialogTitle sx={{ "&&": { padding: "24px 24px 12px 24px" } }}>
|
||||
{t("developer_settings")}
|
||||
</DialogTitle>
|
||||
@@ -134,17 +134,16 @@ const Form: React.FC<FormProps> = ({ initialAPIOrigin, onClose }) => {
|
||||
<TextField
|
||||
fullWidth
|
||||
autoFocus
|
||||
id="apiOrigin"
|
||||
name="apiOrigin"
|
||||
label={t("server_endpoint")}
|
||||
placeholder="http://localhost:8080"
|
||||
value={form.values.apiOrigin}
|
||||
onChange={form.handleChange}
|
||||
onBlur={form.handleBlur}
|
||||
value={formik.values.apiOrigin}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
error={hasError}
|
||||
helperText={
|
||||
hasError
|
||||
? form.errors.apiOrigin
|
||||
? formik.errors.apiOrigin
|
||||
: " " /* always show an empty string to prevent a layout shift */
|
||||
}
|
||||
slotProps={{
|
||||
@@ -175,7 +174,7 @@ const Form: React.FC<FormProps> = ({ initialAPIOrigin, onClose }) => {
|
||||
type="submit"
|
||||
color="accent"
|
||||
fullWidth
|
||||
disabled={form.isSubmitting}
|
||||
disabled={formik.isSubmitting}
|
||||
>
|
||||
{t("save")}
|
||||
</FocusVisibleButton>
|
||||
|
||||
Reference in New Issue
Block a user