add auth page

This commit is contained in:
Abhinav 2023-11-09 14:04:18 +05:30
parent 720b1d77d1
commit 2454cca617
20 changed files with 54 additions and 196 deletions

View file

@ -106,7 +106,7 @@ export default function App(props: EnteAppProps) {
setupI18n().finally(() => setIsI18nReady(true));
// set client package name in headers
HTTPService.setHeaders({
'X-Client-Package': CLIENT_PACKAGE_NAMES.get(APPS.PHOTOS),
'X-Client-Package': CLIENT_PACKAGE_NAMES.get(APPS.AUTH),
});
// setup logging
clearLogsIfLocalStorageLimitExceeded();
@ -195,8 +195,8 @@ export default function App(props: EnteAppProps) {
<Head>
<title>
{isI18nReady
? t('TITLE', { context: APPS.PHOTOS })
: APP_TITLES.get(APPS.PHOTOS)}
? t('TITLE', { context: APPS.AUTH })
: APP_TITLES.get(APPS.AUTH)}
</title>
<meta
name="viewport"
@ -204,7 +204,7 @@ export default function App(props: EnteAppProps) {
/>
</Head>
<ThemeProvider theme={getTheme(themeColor, APPS.PHOTOS)}>
<ThemeProvider theme={getTheme(themeColor, APPS.AUTH)}>
<CssBaseline enableColorScheme />
{showNavbar && <AppNavbar isMobile={isMobile} />}
<MessageContainer>

View file

@ -11,7 +11,7 @@ export default function ChangeEmail() {
<ChangeEmailPage
appContext={appContext}
router={router}
appName={APPS.PHOTOS}
appName={APPS.AUTH}
/>
);
}

View file

@ -11,7 +11,7 @@ export default function ChangePassword() {
<ChangePasswordPage
appContext={appContext}
router={router}
appName={APPS.PHOTOS}
appName={APPS.AUTH}
/>
);
}

View file

@ -11,7 +11,7 @@ export default function Credential() {
<CredentialPage
appContext={appContext}
router={router}
appName={APPS.PHOTOS}
appName={APPS.AUTH}
/>
);
}

View file

@ -11,7 +11,7 @@ export default function Generate() {
<GeneratePage
appContext={appContext}
router={router}
appName={APPS.PHOTOS}
appName={APPS.AUTH}
/>
);
}

View file

@ -11,7 +11,7 @@ export default function Login() {
<LoginPage
appContext={appContext}
router={router}
appName={APPS.PHOTOS}
appName={APPS.AUTH}
/>
);
}

View file

@ -11,7 +11,7 @@ export default function Recover() {
<RecoverPage
appContext={appContext}
router={router}
appName={APPS.PHOTOS}
appName={APPS.AUTH}
/>
);
}

View file

@ -11,7 +11,7 @@ export default function Sigup() {
<SignupPage
appContext={appContext}
router={router}
appName={APPS.PHOTOS}
appName={APPS.AUTH}
/>
);
}

View file

@ -11,7 +11,7 @@ export default function TwoFactorRecover() {
<TwoFactorRecoverPage
appContext={appContext}
router={router}
appName={APPS.PHOTOS}
appName={APPS.AUTH}
/>
);
}

View file

@ -11,7 +11,7 @@ export default function TwoFactorSetup() {
<TwoFactorSetupPage
appContext={appContext}
router={router}
appName={APPS.PHOTOS}
appName={APPS.AUTH}
/>
);
}

View file

@ -11,7 +11,7 @@ export default function TwoFactorVerify() {
<TwoFactorVerifyPage
appContext={appContext}
router={router}
appName={APPS.PHOTOS}
appName={APPS.AUTH}
/>
);
}

View file

@ -11,7 +11,7 @@ export default function Verify() {
<VerifyPage
appContext={appContext}
router={router}
appName={APPS.PHOTOS}
appName={APPS.AUTH}
/>
);
}

View file

@ -1,11 +1,12 @@
import { HttpStatusCode } from 'axios';
import HTTPService from 'services/HTTPService';
import HTTPService from '@ente/shared/network/HTTPService';
import { AuthEntity, AuthKey } from 'types/authenticator/api';
import { Code } from 'types/authenticator/code';
import ComlinkCryptoWorker from 'utils/comlink/ComlinkCryptoWorker';
import { getEndpoint } from 'utils/common/apiUtil';
import { getActualKey, getToken } from 'utils/common/key';
import { ApiError, CustomError } from 'utils/error';
import ComlinkCryptoWorker from '@ente/shared/crypto';
import { getEndpoint } from '@ente/shared/network/api';
import { getActualKey } from '@ente/shared/user';
import { getToken } from '@ente/shared/storage/localStorage/helpers';
import { ApiError, CustomError } from '@ente/shared/error';
import { logError } from '@ente/shared/sentry';
const ENDPOINT = getEndpoint();

View file

@ -1,164 +0,0 @@
import { Formik, FormikHelpers } from 'formik';
import React, { useRef, useState } from 'react';
import * as Yup from 'yup';
import SubmitButton from '@ente/shared/components/SubmitButton';
import router from 'next/router';
import { changeEmail, sendOTTForEmailChange } from 'services/userService';
import { getData, LS_KEYS, setData } from 'utils/storage/localStorage';
import { PHOTOS_PAGES as PAGES } from '@ente/shared/constants/pages';
import { Alert, Box, TextField } from '@mui/material';
import { VerticallyCentered } from './Container';
import LinkButton from './pages/gallery/LinkButton';
import FormPaperFooter from '@ente/shared/components/Form/FormPaper/Footer';
import { sleep } from 'utils/common';
import { Trans } from 'react-i18next';
import { t } from 'i18next';
interface formValues {
email: string;
ott?: string;
}
function ChangeEmailForm() {
const [loading, setLoading] = useState(false);
const [ottInputVisible, setShowOttInputVisibility] = useState(false);
const ottInputRef = useRef(null);
const [email, setEmail] = useState(null);
const [showMessage, setShowMessage] = useState(false);
const [success, setSuccess] = useState(false);
const requestOTT = async (
{ email }: formValues,
{ setFieldError }: FormikHelpers<formValues>
) => {
try {
setLoading(true);
await sendOTTForEmailChange(email);
setEmail(email);
setShowOttInputVisibility(true);
setShowMessage(true);
setTimeout(() => {
ottInputRef.current?.focus();
}, 250);
} catch (e) {
setFieldError('email', t('EMAIl_ALREADY_OWNED'));
}
setLoading(false);
};
const requestEmailChange = async (
{ email, ott }: formValues,
{ setFieldError }: FormikHelpers<formValues>
) => {
try {
setLoading(true);
await changeEmail(email, ott);
setData(LS_KEYS.USER, { ...getData(LS_KEYS.USER), email });
setLoading(false);
setSuccess(true);
await sleep(1000);
router.push(PAGES.GALLERY);
} catch (e) {
setLoading(false);
setFieldError('ott', t('INCORRECT_CODE'));
}
};
const goToGallery = () => router.push(PAGES.GALLERY);
return (
<Formik<formValues>
initialValues={{ email: '' }}
validationSchema={Yup.object().shape({
email: Yup.string()
.email(t('EMAIL_ERROR'))
.required(t('REQUIRED')),
ott: ottInputVisible && Yup.string().required(t('REQUIRED')),
})}
validateOnChange={false}
validateOnBlur={false}
onSubmit={!ottInputVisible ? requestOTT : requestEmailChange}>
{({ values, errors, handleChange, handleSubmit }) => (
<>
{showMessage && (
<Alert
color="success"
onClose={() => setShowMessage(false)}>
<Trans
i18nKey="EMAIL_SENT"
components={{
a: (
<Box
color="text.muted"
component={'span'}
/>
),
}}
values={{ email }}
/>
</Alert>
)}
<form noValidate onSubmit={handleSubmit}>
<VerticallyCentered>
<TextField
fullWidth
InputProps={{
readOnly: ottInputVisible,
}}
type="email"
label={t('ENTER_EMAIL')}
value={values.email}
onChange={handleChange('email')}
error={Boolean(errors.email)}
helperText={errors.email}
autoFocus
disabled={loading}
/>
{ottInputVisible && (
<TextField
fullWidth
type="text"
label={t('ENTER_OTT')}
value={values.ott}
onChange={handleChange('ott')}
error={Boolean(errors.ott)}
helperText={errors.ott}
disabled={loading}
/>
)}
<SubmitButton
success={success}
sx={{ mt: 2 }}
loading={loading}
buttonText={
!ottInputVisible
? t('SEND_OTT')
: t('VERIFY')
}
/>
</VerticallyCentered>
</form>
<FormPaperFooter
style={{
justifyContent: ottInputVisible && 'space-between',
}}>
{ottInputVisible && (
<LinkButton
onClick={() =>
setShowOttInputVisibility(false)
}>
{t('CHANGE_EMAIL')}?
</LinkButton>
)}
<LinkButton onClick={goToGallery}>
{t('GO_BACK')}
</LinkButton>
</FormPaperFooter>
</>
)}
</Formik>
);
}
export default ChangeEmailForm;

View file

@ -13,13 +13,14 @@ import FormPaperFooter from '@ente/shared/components/Form/FormPaper/Footer';
import { sleep } from '@ente/shared/utils';
import { Trans } from 'react-i18next';
import { t } from 'i18next';
import { APPS } from '@ente/shared/apps/constants';
interface formValues {
email: string;
ott?: string;
}
function ChangeEmailForm() {
function ChangeEmailForm({ appName }: { appName: string }) {
const [loading, setLoading] = useState(false);
const [ottInputVisible, setShowOttInputVisibility] = useState(false);
const ottInputRef = useRef(null);
@ -57,14 +58,20 @@ function ChangeEmailForm() {
setLoading(false);
setSuccess(true);
await sleep(1000);
router.push(PAGES.GALLERY);
goToApp();
} catch (e) {
setLoading(false);
setFieldError('ott', t('INCORRECT_CODE'));
}
};
const goToGallery = () => router.push(PAGES.GALLERY);
const goToApp = () => {
if (appName === APPS.AUTH) {
router.push(PAGES.AUTH);
} else {
router.push(PAGES.GALLERY);
}
};
return (
<Formik<formValues>
@ -151,7 +158,7 @@ function ChangeEmailForm() {
{t('CHANGE_EMAIL')}?
</LinkButton>
)}
<LinkButton onClick={goToGallery}>
<LinkButton onClick={goToApp}>
{t('GO_BACK')}
</LinkButton>
</FormPaperFooter>

View file

@ -9,7 +9,7 @@ import FormPaper from '@ente/shared/components/Form/FormPaper';
import FormPaperTitle from '@ente/shared/components/Form/FormPaper/Title';
import { PageProps } from '@ente/shared/apps/types';
function ChangeEmailPage({ router }: PageProps) {
function ChangeEmailPage({ router, appName }: PageProps) {
useEffect(() => {
const user = getData(LS_KEYS.USER);
if (!user?.token) {
@ -21,7 +21,7 @@ function ChangeEmailPage({ router }: PageProps) {
<VerticallyCentered>
<FormPaper>
<FormPaperTitle>{t('CHANGE_EMAIL')}</FormPaperTitle>
<ChangeEmailForm />
<ChangeEmailForm appName={appName} />
</FormPaper>
</VerticallyCentered>
);

View file

@ -90,7 +90,11 @@ export default function Credentials({
}
}
if (key) {
router.push(PAGES.GALLERY);
if (appName === APPS.AUTH) {
router.push(PAGES.AUTH);
} else {
router.push(PAGES.GALLERY);
}
return;
}
const kekEncryptedAttributes: B64EncryptionResult = getKey(

View file

@ -22,6 +22,7 @@ import { sendOtt } from '@ente/accounts/api/user';
import InMemoryStore, { MS_KEYS } from '@ente/shared/storage/InMemoryStore';
import { PageProps } from '@ente/shared/apps/types';
import { logError } from '@ente/shared/sentry';
import { APPS } from '@ente/shared/apps/constants';
const bip39 = require('bip39');
// mobile client library only supports english.
bip39.setDefaultWordlist('english');
@ -47,7 +48,11 @@ export default function Recover({ appContext, router, appName }: PageProps) {
if (!keyAttributes) {
router.push(PAGES.GENERATE);
} else if (key) {
router.push(PAGES.GALLERY);
if (appName === APPS.AUTH) {
router.push(PAGES.AUTH);
} else {
router.push(PAGES.GALLERY);
}
} else {
setKeyAttributes(keyAttributes);
}

View file

@ -16,13 +16,14 @@ import { TwoFactorSetup } from '@ente/accounts/components/two-factor/setup';
import LinkButton from '@ente/shared/components/LinkButton';
import { PageProps } from '@ente/shared/apps/types';
import { logError } from '@ente/shared/sentry';
import { APPS } from '@ente/shared/apps/constants';
export enum SetupMode {
QR_CODE,
MANUAL_CODE,
}
export default function SetupTwoFactor({ router }: PageProps) {
export default function SetupTwoFactor({ router, appName }: PageProps) {
const [twoFactorSecret, setTwoFactorSecret] =
useState<TwoFactorSecret>(null);
@ -55,7 +56,11 @@ export default function SetupTwoFactor({ router }: PageProps) {
...getData(LS_KEYS.USER),
isTwoFactorEnabled: true,
});
router.push(PAGES.GALLERY);
if (appName === APPS.AUTH) {
router.push(PAGES.AUTH);
} else {
router.push(PAGES.GALLERY);
}
};
return (

View file

@ -1,6 +1,6 @@
import { MenuItem, ButtonProps, Typography, Box } from '@mui/material';
import { FluidContainer } from 'components/Container';
import { OverflowMenuContext } from 'contexts/overflowMenu';
import { FluidContainer } from '@ente/shared/components/Container';
import { OverflowMenuContext } from './context';
import React, { useContext } from 'react';
interface Iprops {