This commit is contained in:
Manav Rathi
2025-01-10 13:51:03 +05:30
parent 642c9e611b
commit 6a263e2176
7 changed files with 121 additions and 147 deletions

View File

@@ -1,6 +1,6 @@
import { staticAppTitle } from "@/base/app";
import { CustomHead } from "@/base/components/Head";
import { LoadingOverlay } from "@/base/components/LoadingOverlay";
import { LoadingOverlay } from "@/base/components/loaders";
import { AttributedMiniDialog } from "@/base/components/MiniDialog";
import { useAttributedMiniDialog } from "@/base/components/utils/dialog";
import { useSetupI18n, useSetupLogs } from "@/base/components/utils/hooks-app";

View File

@@ -1,7 +1,7 @@
import { accountLogout } from "@/accounts/services/logout";
import { clientPackageName, staticAppTitle } from "@/base/app";
import { CustomHead } from "@/base/components/Head";
import { LoadingOverlay } from "@/base/components/LoadingOverlay";
import { LoadingOverlay } from "@/base/components/loaders";
import { AttributedMiniDialog } from "@/base/components/MiniDialog";
import { AppNavbar } from "@/base/components/Navbar";
import { useAttributedMiniDialog } from "@/base/components/utils/dialog";

View File

@@ -1,6 +1,6 @@
import { clientPackageName, isDesktop, staticAppTitle } from "@/base/app";
import { CustomHead } from "@/base/components/Head";
import { LoadingOverlay } from "@/base/components/LoadingOverlay";
import { LoadingOverlay } from "@/base/components/loaders";
import { AttributedMiniDialog } from "@/base/components/MiniDialog";
import { AppNavbar } from "@/base/components/Navbar";
import {

View File

@@ -4,18 +4,20 @@ import {
passkeySessionExpiredErrorMessage,
saveCredentialsAndNavigateTo,
} from "@/accounts/services/passkey";
import { FormPaper, FormPaperFooter } from "@/base/components/FormPaper";
import type { MiniDialogAttributes } from "@/base/components/MiniDialog";
import { FocusVisibleButton } from "@/base/components/mui/FocusVisibleButton";
import { genericErrorDialogAttributes } from "@/base/components/utils/dialog";
import log from "@/base/log";
import { customAPIHost } from "@/base/origins";
import { VerticallyCentered } from "@ente/shared/components/Container";
import LinkButton from "@ente/shared/components/LinkButton";
import { CircularProgress, Stack, Typography, styled } from "@mui/material";
import { t } from "i18next";
import { useRouter } from "next/router";
import React, { useEffect, useState } from "react";
import {
AccountsPageContents,
AccountsPageFooter,
} from "./layouts/centered-paper";
export const PasswordHeader: React.FC<React.PropsWithChildren> = ({
children,
@@ -44,7 +46,7 @@ const Header_ = styled("div")`
gap: 8px;
`;
export const LoginFlowFormFooter: React.FC<React.PropsWithChildren> = ({
export const AccountsPageFooterWithHost: React.FC<React.PropsWithChildren> = ({
children,
}) => {
const [host, setHost] = useState<string | undefined>();
@@ -52,16 +54,14 @@ export const LoginFlowFormFooter: React.FC<React.PropsWithChildren> = ({
useEffect(() => void customAPIHost().then(setHost), []);
return (
<FormPaperFooter>
<Stack sx={{ gap: "16px", width: "100%", textAlign: "start" }}>
{children}
{host && (
<Typography variant="small" sx={{ color: "text.faint" }}>
{host}
</Typography>
)}
</Stack>
</FormPaperFooter>
<Stack sx={{ gap: 2 }}>
<AccountsPageFooter>{children}</AccountsPageFooter>
{host && (
<Typography variant="small" sx={{ color: "text.faint" }}>
{host}
</Typography>
)}
</Stack>
);
};
@@ -119,59 +119,50 @@ export const VerifyingPasskey: React.FC<VerifyingPasskeyProps> = ({
};
return (
<VerticallyCentered>
<FormPaper style={{ minWidth: "320px" }}>
<PasskeyHeader>{email ?? ""}</PasskeyHeader>
<AccountsPageContents>
<PasskeyHeader>{email ?? ""}</PasskeyHeader>
<VerifyingPasskeyMiddle>
<VerifyingPasskeyStatus>
{verificationStatus == "checking" ? (
<Typography>
<CircularProgress color="accent" size="1.5em" />
</Typography>
) : (
<Typography sx={{ color: "text.muted" }}>
{verificationStatus == "waiting"
? t("waiting_for_verification")
: t("verification_still_pending")}
</Typography>
)}
</VerifyingPasskeyStatus>
<VerifyingPasskeyMiddle>
<VerifyingPasskeyStatus>
{verificationStatus == "checking" ? (
<Typography>
<CircularProgress color="accent" size="1.5em" />
</Typography>
) : (
<Typography sx={{ color: "text.muted" }}>
{verificationStatus == "waiting"
? t("waiting_for_verification")
: t("verification_still_pending")}
</Typography>
)}
</VerifyingPasskeyStatus>
<ButtonStack>
<FocusVisibleButton
onClick={handleRetry}
fullWidth
color="secondary"
>
{t("try_again")}
</FocusVisibleButton>
<FocusVisibleButton
onClick={handleCheckStatus}
fullWidth
color="accent"
>
{t("check_status")}
</FocusVisibleButton>
</ButtonStack>
</VerifyingPasskeyMiddle>
<LoginFlowFormFooter>
<Stack
direction="row"
sx={{ justifyContent: "space-between" }}
<ButtonStack>
<FocusVisibleButton
onClick={handleRetry}
fullWidth
color="secondary"
>
<LinkButton onClick={handleRecover}>
{t("recover_account")}
</LinkButton>
<LinkButton onClick={logout}>
{t("change_email")}
</LinkButton>
</Stack>
</LoginFlowFormFooter>
</FormPaper>
</VerticallyCentered>
{t("try_again")}
</FocusVisibleButton>
<FocusVisibleButton
onClick={handleCheckStatus}
fullWidth
color="accent"
>
{t("check_status")}
</FocusVisibleButton>
</ButtonStack>
</VerifyingPasskeyMiddle>
<AccountsPageFooterWithHost>
<LinkButton onClick={handleRecover}>
{t("recover_account")}
</LinkButton>
<LinkButton onClick={logout}>{t("change_email")}</LinkButton>
</AccountsPageFooterWithHost>
</AccountsPageContents>
);
};

View File

@@ -1,11 +1,36 @@
import { AccountsPageContents } from "@/accounts/components/layouts/centered-paper";
import {
AccountsPageFooterWithHost,
PasswordHeader,
VerifyingPasskey,
} from "@/accounts/components/LoginComponents";
import { SecondFactorChoice } from "@/accounts/components/SecondFactorChoice";
import { sessionExpiredDialogAttributes } from "@/accounts/components/utils/dialog";
import { FormPaper } from "@/base/components/FormPaper";
import { ActivityIndicator } from "@/base/components/mui/ActivityIndicator";
import { useSecondFactorChoiceIfNeeded } from "@/accounts/components/utils/second-factor-choice";
import { PAGES } from "@/accounts/constants/pages";
import {
openPasskeyVerificationURL,
passkeyVerificationRedirectURL,
} from "@/accounts/services/passkey";
import {
appHomeRoute,
stashRedirect,
unstashRedirect,
} from "@/accounts/services/redirect";
import { checkSessionValidity } from "@/accounts/services/session";
import {
configureSRP,
generateSRPSetupAttributes,
loginViaSRP,
} from "@/accounts/services/srp";
import type { SRPAttributes } from "@/accounts/services/srp-remote";
import { getSRPAttributes } from "@/accounts/services/srp-remote";
import type { PageProps } from "@/accounts/types/page";
import { LoadingIndicator } from "@/base/components/loaders";
import { sharedCryptoWorker } from "@/base/crypto";
import type { B64EncryptionResult } from "@/base/crypto/libsodium";
import { clearLocalStorage } from "@/base/local-storage";
import log from "@/base/log";
import { VerticallyCentered } from "@ente/shared/components/Container";
import LinkButton from "@ente/shared/components/LinkButton";
import VerifyMasterPasswordForm, {
type VerifyMasterPasswordFormProps,
@@ -35,39 +60,12 @@ import {
setKey,
} from "@ente/shared/storage/sessionStorage";
import type { KeyAttributes, User } from "@ente/shared/user/types";
import { Stack } from "@mui/material";
import { t } from "i18next";
import { useRouter } from "next/router";
import { useCallback, useEffect, useState } from "react";
import {
LoginFlowFormFooter,
PasswordHeader,
VerifyingPasskey,
} from "../components/LoginComponents";
import { SecondFactorChoice } from "../components/SecondFactorChoice";
import { useSecondFactorChoiceIfNeeded } from "../components/utils/second-factor-choice";
import { PAGES } from "../constants/pages";
import {
openPasskeyVerificationURL,
passkeyVerificationRedirectURL,
} from "../services/passkey";
import {
appHomeRoute,
stashRedirect,
unstashRedirect,
} from "../services/redirect";
import { checkSessionValidity } from "../services/session";
import {
configureSRP,
generateSRPSetupAttributes,
loginViaSRP,
} from "../services/srp";
import type { SRPAttributes } from "../services/srp-remote";
import { getSRPAttributes } from "../services/srp-remote";
import type { PageProps } from "../types/page";
const Page: React.FC<PageProps> = ({ appContext }) => {
const { logout, showNavBar, showMiniDialog } = appContext;
const { logout, showMiniDialog } = appContext;
const [srpAttributes, setSrpAttributes] = useState<SRPAttributes>();
const [keyAttributes, setKeyAttributes] = useState<KeyAttributes>();
@@ -201,7 +199,6 @@ const Page: React.FC<PageProps> = ({ appContext }) => {
}
};
void main();
showNavBar(true);
}, []);
// TODO: ^ validateSession is a dependency, but add that only after we've
// wrapped items from the callback (like logout) in useCallback too.
@@ -336,11 +333,7 @@ const Page: React.FC<PageProps> = ({ appContext }) => {
};
if (!keyAttributes && !srpAttributes) {
return (
<VerticallyCentered>
<ActivityIndicator />
</VerticallyCentered>
);
return <LoadingIndicator />;
}
if (passkeyVerificationData) {
@@ -354,11 +347,7 @@ const Page: React.FC<PageProps> = ({ appContext }) => {
// See: [Note: Passkey verification in the desktop app]
if (!globalThis.electron) {
return (
<VerticallyCentered>
<ActivityIndicator />
</VerticallyCentered>
);
return <LoadingIndicator />;
}
return (
@@ -376,35 +365,26 @@ const Page: React.FC<PageProps> = ({ appContext }) => {
// TODO: Handle the case when user is not present, or exclude that
// possibility using types.
return (
<VerticallyCentered>
<FormPaper style={{ minWidth: "320px" }}>
<PasswordHeader>{user?.email ?? ""}</PasswordHeader>
<AccountsPageContents>
<PasswordHeader>{user?.email ?? ""}</PasswordHeader>
<VerifyMasterPasswordForm
buttonText={t("sign_in")}
callback={useMasterPassword}
user={user}
keyAttributes={keyAttributes}
getKeyAttributes={getKeyAttributes}
srpAttributes={srpAttributes}
/>
<VerifyMasterPasswordForm
buttonText={t("sign_in")}
callback={useMasterPassword}
user={user}
keyAttributes={keyAttributes}
getKeyAttributes={getKeyAttributes}
srpAttributes={srpAttributes}
/>
<LoginFlowFormFooter>
<Stack
direction="row"
sx={{ justifyContent: "space-between" }}
>
<LinkButton onClick={() => router.push(PAGES.RECOVER)}>
{t("forgot_password")}
</LinkButton>
<LinkButton onClick={logout}>
{t("change_email")}
</LinkButton>
</Stack>
</LoginFlowFormFooter>
</FormPaper>
<AccountsPageFooterWithHost>
<LinkButton onClick={() => router.push(PAGES.RECOVER)}>
{t("forgot_password")}
</LinkButton>
<LinkButton onClick={logout}>{t("change_email")}</LinkButton>
</AccountsPageFooterWithHost>
<SecondFactorChoice {...secondFactorChoiceProps} />
</VerticallyCentered>
</AccountsPageContents>
);
};

View File

@@ -20,9 +20,8 @@ import type {
import { getSRPAttributes } from "@/accounts/services/srp-remote";
import { putAttributes, sendOTT, verifyEmail } from "@/accounts/services/user";
import type { PageProps } from "@/accounts/types/page";
import { ActivityIndicator } from "@/base/components/mui/ActivityIndicator";
import { LoadingIndicator } from "@/base/components/loaders";
import log from "@/base/log";
import { VerticallyCentered } from "@ente/shared/components/Container";
import LinkButton from "@ente/shared/components/LinkButton";
import SingleInputForm, {
type SingleInputFormProps,
@@ -184,11 +183,7 @@ const Page: React.FC<PageProps> = ({ appContext }) => {
};
if (!email) {
return (
<VerticallyCentered>
<ActivityIndicator />
</VerticallyCentered>
);
return <LoadingIndicator />;
}
if (passkeyVerificationData) {
@@ -202,11 +197,7 @@ const Page: React.FC<PageProps> = ({ appContext }) => {
// See: [Note: Passkey verification in the desktop app]
if (!globalThis.electron) {
return (
<VerticallyCentered>
<ActivityIndicator />
</VerticallyCentered>
);
return <LoadingIndicator />;
}
return (

View File

@@ -1,7 +1,19 @@
import { Overlay } from "@/base/components/containers";
import { Overlay, Stack100vhCenter } from "@/base/components/containers";
import { ActivityIndicator } from "@/base/components/mui/ActivityIndicator";
import React from "react";
/**
* A centered activity indicator shown in a container that fills up the entire
* width and height of the viewport.
*
* This is meant as a root component of a page, e.g., during initial load.
*/
export const LoadingIndicator: React.FC = () => (
<Stack100vhCenter>
<ActivityIndicator />
</Stack100vhCenter>
);
/**
* An opaque overlay that covers the entire viewport and shows an activity
* indicator in its center.