diff --git a/web/apps/photos/src/pages/index.tsx b/web/apps/photos/src/pages/index.tsx index 492785c91..907f37d3d 100644 --- a/web/apps/photos/src/pages/index.tsx +++ b/web/apps/photos/src/pages/index.tsx @@ -1,6 +1,6 @@ import log from "@/next/log"; -import Login from "@ente/accounts/components/Login"; -import SignUp from "@ente/accounts/components/SignUp"; +import { Login } from "@ente/accounts/components/Login"; +import { SignUp } from "@ente/accounts/components/SignUp"; import { APPS } from "@ente/shared/apps/constants"; import { EnteLogo } from "@ente/shared/components/EnteLogo"; import EnteSpinner from "@ente/shared/components/EnteSpinner"; diff --git a/web/packages/accounts/components/Login.tsx b/web/packages/accounts/components/Login.tsx new file mode 100644 index 000000000..6ddbe1491 --- /dev/null +++ b/web/packages/accounts/components/Login.tsx @@ -0,0 +1,75 @@ +import log from "@/next/log"; +import { appNameToAppNameOld } from "@ente/shared/apps/constants"; +import FormPaperFooter from "@ente/shared/components/Form/FormPaper/Footer"; +import FormPaperTitle from "@ente/shared/components/Form/FormPaper/Title"; +import LinkButton from "@ente/shared/components/LinkButton"; +import SingleInputForm, { + type SingleInputFormProps, +} from "@ente/shared/components/SingleInputForm"; +import { LS_KEYS, setData } from "@ente/shared/storage/localStorage"; +import { Input } from "@mui/material"; +import { t } from "i18next"; +import { useRouter } from "next/router"; +import type { AppName } from "packages/next/types/app"; +import { getSRPAttributes } from "../api/srp"; +import { sendOtt } from "../api/user"; +import { PAGES } from "../constants/pages"; + +interface LoginProps { + signUp: () => void; + appName: AppName; +} + +export function Login(props: LoginProps) { + const appNameOld = appNameToAppNameOld(props.appName); + + const router = useRouter(); + + const loginUser: SingleInputFormProps["callback"] = async ( + email, + setFieldError, + ) => { + try { + setData(LS_KEYS.USER, { email }); + const srpAttributes = await getSRPAttributes(email); + log.debug(() => ` srpAttributes: ${JSON.stringify(srpAttributes)}`); + if (!srpAttributes || srpAttributes.isEmailMFAEnabled) { + await sendOtt(appNameOld, email); + router.push(PAGES.VERIFY); + } else { + setData(LS_KEYS.SRP_ATTRIBUTES, srpAttributes); + router.push(PAGES.CREDENTIALS); + } + } catch (e) { + if (e instanceof Error) { + setFieldError(`${t("UNKNOWN_ERROR")} (reason:${e.message})`); + } else { + setFieldError( + `${t("UNKNOWN_ERROR")} (reason:${JSON.stringify(e)})`, + ); + } + } + }; + + return ( + <> + {t("LOGIN")} + + } + /> + + + + {t("NO_ACCOUNT")} + + + + ); +} diff --git a/web/packages/accounts/components/SignUp.tsx b/web/packages/accounts/components/SignUp.tsx new file mode 100644 index 000000000..6c89eb8fb --- /dev/null +++ b/web/packages/accounts/components/SignUp.tsx @@ -0,0 +1,320 @@ +import log from "@/next/log"; +import { sendOtt } from "@ente/accounts/api/user"; +import { PasswordStrengthHint } from "@ente/accounts/components/PasswordStrength"; +import { PAGES } from "@ente/accounts/constants/pages"; +import { isWeakPassword } from "@ente/accounts/utils"; +import { generateKeyAndSRPAttributes } from "@ente/accounts/utils/srp"; +import { LS_KEYS } from "@ente/shared//storage/localStorage"; +import { appNameToAppNameOld } from "@ente/shared/apps/constants"; +import { VerticallyCentered } from "@ente/shared/components/Container"; +import FormPaperFooter from "@ente/shared/components/Form/FormPaper/Footer"; +import FormPaperTitle from "@ente/shared/components/Form/FormPaper/Title"; +import ShowHidePassword from "@ente/shared/components/Form/ShowHidePassword"; +import LinkButton from "@ente/shared/components/LinkButton"; +import SubmitButton from "@ente/shared/components/SubmitButton"; +import { + generateAndSaveIntermediateKeyAttributes, + saveKeyInSessionStore, +} from "@ente/shared/crypto/helpers"; +import { setData } from "@ente/shared/storage/localStorage"; +import { + setJustSignedUp, + setLocalReferralSource, +} from "@ente/shared/storage/localStorage/helpers"; +import { SESSION_KEYS } from "@ente/shared/storage/sessionStorage"; +import InfoOutlined from "@mui/icons-material/InfoOutlined"; +import { + Box, + Checkbox, + FormControlLabel, + FormGroup, + IconButton, + InputAdornment, + Link, + TextField, + Tooltip, + Typography, +} from "@mui/material"; +import { Formik, type FormikHelpers } from "formik"; +import { t } from "i18next"; +import type { NextRouter } from "next/router"; +import type { AppName } from "packages/next/types/app"; +import React, { useState } from "react"; +import { Trans } from "react-i18next"; +import * as Yup from "yup"; + +interface FormValues { + email: string; + passphrase: string; + confirm: string; + referral: string; +} + +interface SignUpProps { + router: NextRouter; + login: () => void; + appName: AppName; +} + +export function SignUp({ router, appName, login }: SignUpProps) { + const appNameOld = appNameToAppNameOld(appName); + + const [acceptTerms, setAcceptTerms] = useState(false); + const [loading, setLoading] = useState(false); + const [showPassword, setShowPassword] = useState(false); + + const handleClickShowPassword = () => { + setShowPassword(!showPassword); + }; + + const handleMouseDownPassword = ( + event: React.MouseEvent, + ) => { + event.preventDefault(); + }; + + const registerUser = async ( + { email, passphrase, confirm, referral }: FormValues, + { setFieldError }: FormikHelpers, + ) => { + try { + if (passphrase !== confirm) { + setFieldError("confirm", t("PASSPHRASE_MATCH_ERROR")); + return; + } + setLoading(true); + try { + setData(LS_KEYS.USER, { email }); + setLocalReferralSource(referral); + await sendOtt(appNameOld, email); + } catch (e) { + const message = e instanceof Error ? e.message : ""; + setFieldError("confirm", `${t("UNKNOWN_ERROR")} ${message}`); + throw e; + } + try { + const { keyAttributes, masterKey, srpSetupAttributes } = + await generateKeyAndSRPAttributes(passphrase); + + setData(LS_KEYS.ORIGINAL_KEY_ATTRIBUTES, keyAttributes); + setData(LS_KEYS.SRP_SETUP_ATTRIBUTES, srpSetupAttributes); + await generateAndSaveIntermediateKeyAttributes( + passphrase, + keyAttributes, + masterKey, + ); + + await saveKeyInSessionStore( + SESSION_KEYS.ENCRYPTION_KEY, + masterKey, + ); + setJustSignedUp(true); + router.push(PAGES.VERIFY); + } catch (e) { + setFieldError("confirm", t("PASSWORD_GENERATION_FAILED")); + throw e; + } + } catch (e) { + log.error("signup failed", e); + } + setLoading(false); + }; + + return ( + <> + {t("SIGN_UP")} + + initialValues={{ + email: "", + passphrase: "", + confirm: "", + referral: "", + }} + validationSchema={Yup.object().shape({ + email: Yup.string() + .email(t("EMAIL_ERROR")) + .required(t("REQUIRED")), + passphrase: Yup.string().required(t("REQUIRED")), + confirm: Yup.string().required(t("REQUIRED")), + })} + validateOnChange={false} + validateOnBlur={false} + onSubmit={registerUser} + > + {({ + values, + errors, + handleChange, + handleSubmit, + }): JSX.Element => ( +
+ + + + + ), + }} + /> + + + + + + + {t("REFERRAL_CODE_HINT")} + + + + + + + + + ), + }} + fullWidth + name="referral" + type="text" + value={values.referral} + onChange={handleChange("referral")} + error={Boolean(errors.referral)} + disabled={loading} + /> + + + + setAcceptTerms(e.target.checked) + } + color="accent" + /> + } + label={ + + + ), + b: ( + + ), + }} + /> + + } + /> + + + + + {loading && ( + + {t("KEY_GENERATION_IN_PROGRESS_MESSAGE")} + + )} + +
+ )} + + + + {t("ACCOUNT_EXISTS")} + + + ); +} diff --git a/web/packages/accounts/pages/login.tsx b/web/packages/accounts/pages/login.tsx index 506cc81bc..57ea023ff 100644 --- a/web/packages/accounts/pages/login.tsx +++ b/web/packages/accounts/pages/login.tsx @@ -1,23 +1,11 @@ -import log from "@/next/log"; -import { appNameToAppNameOld } from "@ente/shared/apps/constants"; import type { PageProps } from "@ente/shared/apps/types"; import { VerticallyCentered } from "@ente/shared/components/Container"; import EnteSpinner from "@ente/shared/components/EnteSpinner"; import FormPaper from "@ente/shared/components/Form/FormPaper"; -import FormPaperFooter from "@ente/shared/components/Form/FormPaper/Footer"; -import FormPaperTitle from "@ente/shared/components/Form/FormPaper/Title"; -import LinkButton from "@ente/shared/components/LinkButton"; -import SingleInputForm, { - type SingleInputFormProps, -} from "@ente/shared/components/SingleInputForm"; -import { LS_KEYS, getData, setData } from "@ente/shared/storage/localStorage"; -import { Input } from "@mui/material"; -import { t } from "i18next"; +import { LS_KEYS, getData } from "@ente/shared/storage/localStorage"; import { useRouter } from "next/router"; -import type { AppName } from "packages/next/types/app"; import React, { useEffect, useState } from "react"; -import { getSRPAttributes } from "../api/srp"; -import { sendOtt } from "../api/user"; +import { Login } from "../components/Login"; import { PAGES } from "../constants/pages"; const Page: React.FC = ({ appContext }) => { @@ -54,62 +42,3 @@ const Page: React.FC = ({ appContext }) => { }; export default Page; - -interface LoginProps { - signUp: () => void; - appName: AppName; -} - -function Login(props: LoginProps) { - const appNameOld = appNameToAppNameOld(props.appName); - - const router = useRouter(); - - const loginUser: SingleInputFormProps["callback"] = async ( - email, - setFieldError, - ) => { - try { - setData(LS_KEYS.USER, { email }); - const srpAttributes = await getSRPAttributes(email); - log.debug(() => ` srpAttributes: ${JSON.stringify(srpAttributes)}`); - if (!srpAttributes || srpAttributes.isEmailMFAEnabled) { - await sendOtt(appNameOld, email); - router.push(PAGES.VERIFY); - } else { - setData(LS_KEYS.SRP_ATTRIBUTES, srpAttributes); - router.push(PAGES.CREDENTIALS); - } - } catch (e) { - if (e instanceof Error) { - setFieldError(`${t("UNKNOWN_ERROR")} (reason:${e.message})`); - } else { - setFieldError( - `${t("UNKNOWN_ERROR")} (reason:${JSON.stringify(e)})`, - ); - } - } - }; - - return ( - <> - {t("LOGIN")} - - } - /> - - - - {t("NO_ACCOUNT")} - - - - ); -} diff --git a/web/packages/accounts/pages/signup.tsx b/web/packages/accounts/pages/signup.tsx index 02f810250..affc0099b 100644 --- a/web/packages/accounts/pages/signup.tsx +++ b/web/packages/accounts/pages/signup.tsx @@ -1,51 +1,12 @@ -import log from "@/next/log"; -import { sendOtt } from "@ente/accounts/api/user"; -import { PasswordStrengthHint } from "@ente/accounts/components/PasswordStrength"; import { PAGES } from "@ente/accounts/constants/pages"; -import { isWeakPassword } from "@ente/accounts/utils"; -import { generateKeyAndSRPAttributes } from "@ente/accounts/utils/srp"; import { LS_KEYS, getData } from "@ente/shared//storage/localStorage"; -import { appNameToAppNameOld } from "@ente/shared/apps/constants"; import type { PageProps } from "@ente/shared/apps/types"; import { VerticallyCentered } from "@ente/shared/components/Container"; import EnteSpinner from "@ente/shared/components/EnteSpinner"; import FormPaper from "@ente/shared/components/Form/FormPaper"; -import FormPaperFooter from "@ente/shared/components/Form/FormPaper/Footer"; -import FormPaperTitle from "@ente/shared/components/Form/FormPaper/Title"; -import ShowHidePassword from "@ente/shared/components/Form/ShowHidePassword"; -import LinkButton from "@ente/shared/components/LinkButton"; -import SubmitButton from "@ente/shared/components/SubmitButton"; -import { - generateAndSaveIntermediateKeyAttributes, - saveKeyInSessionStore, -} from "@ente/shared/crypto/helpers"; -import { setData } from "@ente/shared/storage/localStorage"; -import { - setJustSignedUp, - setLocalReferralSource, -} from "@ente/shared/storage/localStorage/helpers"; -import { SESSION_KEYS } from "@ente/shared/storage/sessionStorage"; -import InfoOutlined from "@mui/icons-material/InfoOutlined"; -import { - Box, - Checkbox, - FormControlLabel, - FormGroup, - IconButton, - InputAdornment, - Link, - TextField, - Tooltip, - Typography, -} from "@mui/material"; -import { Formik, type FormikHelpers } from "formik"; -import { t } from "i18next"; -import type { NextRouter } from "next/router"; import { useRouter } from "next/router"; -import type { AppName } from "packages/next/types/app"; import React, { useEffect, useState } from "react"; -import { Trans } from "react-i18next"; -import * as Yup from "yup"; +import { SignUp } from "../components/SignUp"; const Page: React.FC = ({ appContext }) => { const { appName } = appContext; @@ -81,279 +42,3 @@ const Page: React.FC = ({ appContext }) => { }; export default Page; - -interface FormValues { - email: string; - passphrase: string; - confirm: string; - referral: string; -} - -interface SignUpProps { - router: NextRouter; - login: () => void; - appName: AppName; -} - -function SignUp({ router, appName, login }: SignUpProps) { - const appNameOld = appNameToAppNameOld(appName); - - const [acceptTerms, setAcceptTerms] = useState(false); - const [loading, setLoading] = useState(false); - const [showPassword, setShowPassword] = useState(false); - - const handleClickShowPassword = () => { - setShowPassword(!showPassword); - }; - - const handleMouseDownPassword = ( - event: React.MouseEvent, - ) => { - event.preventDefault(); - }; - - const registerUser = async ( - { email, passphrase, confirm, referral }: FormValues, - { setFieldError }: FormikHelpers, - ) => { - try { - if (passphrase !== confirm) { - setFieldError("confirm", t("PASSPHRASE_MATCH_ERROR")); - return; - } - setLoading(true); - try { - setData(LS_KEYS.USER, { email }); - setLocalReferralSource(referral); - await sendOtt(appNameOld, email); - } catch (e) { - const message = e instanceof Error ? e.message : ""; - setFieldError("confirm", `${t("UNKNOWN_ERROR")} ${message}`); - throw e; - } - try { - const { keyAttributes, masterKey, srpSetupAttributes } = - await generateKeyAndSRPAttributes(passphrase); - - setData(LS_KEYS.ORIGINAL_KEY_ATTRIBUTES, keyAttributes); - setData(LS_KEYS.SRP_SETUP_ATTRIBUTES, srpSetupAttributes); - await generateAndSaveIntermediateKeyAttributes( - passphrase, - keyAttributes, - masterKey, - ); - - await saveKeyInSessionStore( - SESSION_KEYS.ENCRYPTION_KEY, - masterKey, - ); - setJustSignedUp(true); - router.push(PAGES.VERIFY); - } catch (e) { - setFieldError("confirm", t("PASSWORD_GENERATION_FAILED")); - throw e; - } - } catch (e) { - log.error("signup failed", e); - } - setLoading(false); - }; - - return ( - <> - {t("SIGN_UP")} - - initialValues={{ - email: "", - passphrase: "", - confirm: "", - referral: "", - }} - validationSchema={Yup.object().shape({ - email: Yup.string() - .email(t("EMAIL_ERROR")) - .required(t("REQUIRED")), - passphrase: Yup.string().required(t("REQUIRED")), - confirm: Yup.string().required(t("REQUIRED")), - })} - validateOnChange={false} - validateOnBlur={false} - onSubmit={registerUser} - > - {({ - values, - errors, - handleChange, - handleSubmit, - }): JSX.Element => ( -
- - - - - ), - }} - /> - - - - - - - {t("REFERRAL_CODE_HINT")} - - - - - - - - - ), - }} - fullWidth - name="referral" - type="text" - value={values.referral} - onChange={handleChange("referral")} - error={Boolean(errors.referral)} - disabled={loading} - /> - - - - setAcceptTerms(e.target.checked) - } - color="accent" - /> - } - label={ - - - ), - b: ( - - ), - }} - /> - - } - /> - - - - - {loading && ( - - {t("KEY_GENERATION_IN_PROGRESS_MESSAGE")} - - )} - -
- )} - - - - {t("ACCOUNT_EXISTS")} - - - ); -}