[web] Misc improvements (non functional) (#4654)

This commit is contained in:
Manav Rathi
2025-01-09 20:13:00 +05:30
committed by GitHub
11 changed files with 138 additions and 133 deletions

View File

@@ -1,12 +1,11 @@
import { staticAppTitle } from "@/base/app";
import { CustomHead } from "@/base/components/Head";
import { LoadingOverlay } from "@/base/components/LoadingOverlay";
import { AttributedMiniDialog } from "@/base/components/MiniDialog";
import { ActivityIndicator } from "@/base/components/mui/ActivityIndicator";
import { Overlay } from "@/base/components/mui/Container";
import { AppNavbar } from "@/base/components/Navbar";
import { useAttributedMiniDialog } from "@/base/components/utils/dialog";
import { useSetupI18n } from "@/base/components/utils/hooks-i18n";
import { getTheme, THEME_COLOR } from "@/base/components/utils/theme";
import { setupI18n } from "@/base/i18n";
import { disableDiskLogs } from "@/base/log";
import { logUnhandledErrorsAndRejections } from "@/base/log-web";
import { CssBaseline } from "@mui/material";
@@ -20,13 +19,13 @@ import "@fontsource-variable/inter";
import "styles/global.css";
const App: React.FC<AppProps> = ({ Component, pageProps }) => {
const [isI18nReady, setIsI18nReady] = useState<boolean>(false);
const [showNavbar, setShowNavbar] = useState(false);
const isI18nReady = useSetupI18n();
const { showMiniDialog, miniDialogProps } = useAttributedMiniDialog();
useEffect(() => {
disableDiskLogs();
void setupI18n().finally(() => setIsI18nReady(true));
logUnhandledErrorsAndRejections(true);
return () => logUnhandledErrorsAndRejections(false);
}, []);
@@ -50,19 +49,7 @@ const App: React.FC<AppProps> = ({ Component, pageProps }) => {
<AttributedMiniDialog {...miniDialogProps} />
<AppContext.Provider value={appContext}>
{!isI18nReady && (
<Overlay
sx={(theme) => ({
display: "flex",
justifyContent: "center",
alignItems: "center",
zIndex: 2000,
backgroundColor: theme.colors.background.base,
})}
>
<ActivityIndicator />
</Overlay>
)}
{!isI18nReady && <LoadingOverlay />}
{showNavbar && <AppNavbar />}
{isI18nReady && <Component {...pageProps} />}
</AppContext.Provider>

View File

@@ -1,13 +1,12 @@
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 { AttributedMiniDialog } from "@/base/components/MiniDialog";
import { ActivityIndicator } from "@/base/components/mui/ActivityIndicator";
import { Overlay } from "@/base/components/mui/Container";
import { AppNavbar } from "@/base/components/Navbar";
import { useAttributedMiniDialog } from "@/base/components/utils/dialog";
import { useSetupI18n } from "@/base/components/utils/hooks-i18n";
import { THEME_COLOR, getTheme } from "@/base/components/utils/theme";
import { setupI18n } from "@/base/i18n";
import {
logStartupBanner,
logUnhandledErrorsAndRejections,
@@ -33,10 +32,10 @@ import "styles/global.css";
const App: React.FC<AppProps> = ({ Component, pageProps }) => {
const router = useRouter();
const [isI18nReady, setIsI18nReady] = useState<boolean>(false);
const [loading, setLoading] = useState(false);
const [showNavbar, setShowNavBar] = useState(false);
const isI18nReady = useSetupI18n();
const { showMiniDialog, miniDialogProps } = useAttributedMiniDialog();
const [themeColor, setThemeColor] = useLocalState(
LS_KEYS.THEME,
@@ -44,7 +43,6 @@ const App: React.FC<AppProps> = ({ Component, pageProps }) => {
);
useEffect(() => {
void setupI18n().finally(() => setIsI18nReady(true));
const user = getData(LS_KEYS.USER) as User | undefined | null;
void migrateKVToken(user);
logStartupBanner(user?.id);
@@ -94,22 +92,8 @@ const App: React.FC<AppProps> = ({ Component, pageProps }) => {
<AttributedMiniDialog {...miniDialogProps} />
<AppContext.Provider value={appContext}>
{(loading || !isI18nReady) && (
<Overlay
sx={(theme) => ({
display: "flex",
justifyContent: "center",
alignItems: "center",
zIndex: 2000,
backgroundColor: theme.colors.background.base,
})}
>
<ActivityIndicator />
</Overlay>
)}
{isI18nReady && (
<Component setLoading={setLoading} {...pageProps} />
)}
{(loading || !isI18nReady) && <LoadingOverlay />}
{isI18nReady && <Component {...pageProps} />}
</AppContext.Provider>
</ThemeProvider>
</>

View File

@@ -345,12 +345,12 @@ const UnparseableCode: React.FC<UnparseableCodeProps> = ({
<Typography variant="small">{code.issuer}</Typography>
<Typography
variant="small"
sx={(theme) => ({
color: theme.palette.critical.main,
sx={{
color: "critical.main",
flex: 1,
minHeight: "16px",
mb: 2,
})}
}}
>
{errorMessage}
</Typography>

View File

@@ -1,5 +1,6 @@
import { EnteLogo } from "@/base/components/EnteLogo";
import { decryptMetadataJSON_New } from "@/base/crypto";
import { Box, Button, Stack, Typography } from "@mui/material";
import React, { useEffect, useMemo, useState } from "react";
interface SharedCode {
@@ -8,13 +9,7 @@ interface SharedCode {
codes: string;
}
interface CodeDisplay {
currentCode: string;
nextCode: string;
progress: number;
}
const Share: React.FC = () => {
const Page: React.FC = () => {
const [sharedCode, setSharedCode] = useState<SharedCode | null>(null);
const [error, setError] = useState<string | null>(null);
const [timeStatus, setTimeStatus] = useState<number>(-10);
@@ -51,10 +46,10 @@ const Share: React.FC = () => {
try {
const decryptedCode = (await decryptMetadataJSON_New(
{
encryptedData: base64UrlToByteArray(data),
decryptionHeader: base64UrlToByteArray(header),
encryptedData: base64UrlToBytes(data),
decryptionHeader: base64UrlToBytes(header),
},
base64UrlToByteArray(key),
base64UrlToBytes(key),
)) as SharedCode;
setSharedCode(decryptedCode);
} catch (error) {
@@ -83,7 +78,7 @@ const Share: React.FC = () => {
if (status === 0) {
setCodeDisplay(
getCodeDisplay(
parseCodeDisplay(
codes,
sharedCode.startTime,
sharedCode.step,
@@ -101,37 +96,38 @@ const Share: React.FC = () => {
[codeDisplay.progress],
);
const Message: React.FC<{ text: string }> = ({ text }) => (
<p style={{ textAlign: "center", fontSize: "24px" }}>{text}</p>
);
return (
<div
style={{
display: "flex",
flexDirection: "column",
<Stack
sx={{
justifyContent: "space-between",
alignItems: "center",
height: "100vh",
backgroundColor: "#000000",
color: "#FFFFFF",
padding: "20px",
}}
>
<EnteLogo />
<div style={{ width: "100%", maxWidth: "300px" }}>
{error && <p style={{ color: "red" }}>{error}</p>}
<Box sx={{ width: "min(100%, 300px)" }}>
{error && (
<Typography
sx={{ textAlign: "center", color: "critical.main" }}
>
{error}
</Typography>
)}
{timeStatus === -10 && !error && (
<Message text="Decrypting..." />
<Message>{"Decrypting..."}</Message>
)}
{timeStatus === -1 && (
<Message text="Your or the person who shared the code has out of sync time." />
<Message>
Your or the person who shared the code has out of sync
time.
</Message>
)}
{timeStatus === 1 && <Message text="The code has expired." />}
{timeStatus === 1 && <Message>The code has expired.</Message>}
{timeStatus === 0 && (
<div
style={{
<Box
sx={{
backgroundColor: "#1C1C1E",
borderRadius: "10px",
paddingBottom: "20px",
@@ -169,63 +165,60 @@ const Share: React.FC = () => {
position: "absolute",
right: "20px",
bottom: "20px",
fontSize: "12px",
opacity: 0.6,
}}
>
<p style={{ margin: 0 }}>
<Typography
variant="mini"
sx={{ color: "text.faint" }}
>
{codeDisplay.nextCode === ""
? "Last code"
: "next"}
</p>
</Typography>
{codeDisplay.nextCode !== "" && (
<p style={{ margin: 0 }}>
<Typography
variant="mini"
sx={{ color: "text.faint" }}
>
{codeDisplay.nextCode}
</p>
</Typography>
)}
</div>
</div>
</Box>
)}
</div>
</Box>
<a
<Button
color="accent"
sx={{
backgroundColor: "#8E2DE2",
borderRadius: "25px",
padding: "15px 30px",
marginBottom: "42px",
}}
href="https://ente.io/auth"
target="_blank"
rel="noopener noreferrer"
>
<button
style={{
backgroundColor: "#8E2DE2",
color: "#FFFFFF",
border: "none",
borderRadius: "25px",
padding: "15px 30px",
fontSize: "16px",
fontWeight: "bold",
cursor: "pointer",
width: "100%",
maxWidth: "300px",
marginBottom: "42px",
}}
>
Try Ente Auth
</button>
</a>
</div>
Try Ente Auth
</Button>
</Stack>
);
};
export default Share;
export default Page;
const base64UrlToByteArray = (base64Url: string): Uint8Array => {
const base64UrlToBytes = (base64Url: string): Uint8Array => {
const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
};
const formatCode = (code: string): string =>
code.replace(/(.{3})/g, "$1 ").trim();
interface CodeDisplay {
currentCode: string;
nextCode: string;
progress: number;
}
const getCodeDisplay = (
const parseCodeDisplay = (
codes: string[],
startTime: number,
stepDuration: number,
@@ -241,3 +234,11 @@ const getCodeDisplay = (
progress,
};
};
const formatCode = (code: string) => code.replace(/(.{3})/g, "$1 ").trim();
const Message: React.FC<React.PropsWithChildren> = ({ children }) => (
<Typography variant="h4" style={{ textAlign: "center" }}>
{children}
</Typography>
);

View File

@@ -1,5 +1,5 @@
import { styled } from "@mui/material";
export const LoadingOverlay = styled("div")`
export const GalleryLoadingOverlay = styled("div")`
left: 0;
top: 0;
outline: none;

View File

@@ -1,15 +1,14 @@
import { clientPackageName, isDesktop, staticAppTitle } from "@/base/app";
import { CustomHead } from "@/base/components/Head";
import { LoadingOverlay } from "@/base/components/LoadingOverlay";
import { AttributedMiniDialog } from "@/base/components/MiniDialog";
import { ActivityIndicator } from "@/base/components/mui/ActivityIndicator";
import { Overlay } from "@/base/components/mui/Container";
import { AppNavbar } from "@/base/components/Navbar";
import {
genericErrorDialogAttributes,
useAttributedMiniDialog,
} from "@/base/components/utils/dialog";
import { useSetupI18n } from "@/base/components/utils/hooks-i18n";
import { THEME_COLOR, getTheme } from "@/base/components/utils/theme";
import { setupI18n } from "@/base/i18n";
import log from "@/base/log";
import {
logStartupBanner,
@@ -54,7 +53,6 @@ import "styles/global.css";
export default function App({ Component, pageProps }: AppProps) {
const router = useRouter();
const [isI18nReady, setIsI18nReady] = useState<boolean>(false);
const [loading, setLoading] = useState(false);
const [showNavbar, setShowNavBar] = useState(false);
const [watchFolderView, setWatchFolderView] = useState(false);
@@ -65,6 +63,7 @@ export default function App({ Component, pageProps }: AppProps) {
useState<NotificationAttributes>(null);
const isOffline = useIsOffline();
const isI18nReady = useSetupI18n();
const { showMiniDialog, miniDialogProps } = useAttributedMiniDialog();
const { loadingBarRef, showLoadingBar, hideLoadingBar } = useLoadingBar();
const [themeColor, setThemeColor] = useLocalState(
@@ -73,7 +72,6 @@ export default function App({ Component, pageProps }: AppProps) {
);
useEffect(() => {
void setupI18n().finally(() => setIsI18nReady(true));
const user = getData(LS_KEYS.USER) as User | undefined | null;
void migrateKVToken(user);
logStartupBanner(user?.id);
@@ -152,7 +150,7 @@ export default function App({ Component, pageProps }: AppProps) {
});
router.events.on("routeChangeComplete", () => {
log.debug(() => `Route change took ${Date.now() - t}} ms`);
log.debug(() => `Route change took ${Date.now() - t} ms`);
setLoading(false);
});
}, []);
@@ -229,22 +227,8 @@ export default function App({ Component, pageProps }: AppProps) {
/>
<AppContext.Provider value={appContext}>
{(loading || !isI18nReady) && (
<Overlay
sx={(theme) => ({
display: "flex",
justifyContent: "center",
alignItems: "center",
zIndex: 2000,
backgroundColor: theme.colors.background.base,
})}
>
<ActivityIndicator />
</Overlay>
)}
{isI18nReady && (
<Component setLoading={setLoading} {...pageProps} />
)}
{(loading || !isI18nReady) && <LoadingOverlay />}
{isI18nReady && <Component {...pageProps} />}
</AppContext.Provider>
</ThemeProvider>
</>

View File

@@ -97,7 +97,7 @@ import {
import { FixCreationTime } from "components/FixCreationTime";
import FullScreenDropZone from "components/FullScreenDropZone";
import GalleryEmptyState from "components/GalleryEmptyState";
import { LoadingOverlay } from "components/LoadingOverlay";
import { GalleryLoadingOverlay } from "components/GalleryLoadingOverlay";
import PhotoFrame from "components/PhotoFrame";
import { ITEM_TYPE, TimeStampListItem } from "components/PhotoList";
import Sidebar from "components/Sidebar";
@@ -892,9 +892,9 @@ export default function Gallery() {
}}
/>
{blockingLoad && (
<LoadingOverlay>
<GalleryLoadingOverlay>
<ActivityIndicator />
</LoadingOverlay>
</GalleryLoadingOverlay>
)}
{isFirstLoad && (
<CenteredFlex>

View File

@@ -52,7 +52,7 @@ import {
FilesDownloadProgressAttributes,
} from "components/FilesDownloadProgress";
import FullScreenDropZone from "components/FullScreenDropZone";
import { LoadingOverlay } from "components/LoadingOverlay";
import { GalleryLoadingOverlay } from "components/GalleryLoadingOverlay";
import PhotoFrame from "components/PhotoFrame";
import { ITEM_TYPE, TimeStampListItem } from "components/PhotoList";
import Uploader from "components/Upload/Uploader";
@@ -520,9 +520,9 @@ export default function PublicCollectionGallery() {
selectable={downloadEnabled}
/>
{blockingLoad && (
<LoadingOverlay>
<GalleryLoadingOverlay>
<ActivityIndicator />
</LoadingOverlay>
</GalleryLoadingOverlay>
)}
<Uploader
syncWithRemote={syncWithRemote}

View File

@@ -142,6 +142,11 @@ with Next.js.
For more details, see [translations.md](translations.md).
### Font
The app uses Inter as its primary font, via
[@fontsource-variable/inter](https://fontsource.org/fonts/inter/install).
### UI components
- [react-window](https://github.com/bvaughn/react-window) is used for lazy-ily

View File

@@ -0,0 +1,23 @@
import { ActivityIndicator } from "@/base/components/mui/ActivityIndicator";
import { Overlay } from "@/base/components/mui/Container";
import React from "react";
/**
* An opaque overlay that covers the entire viewport and shows an activity
* indicator in its center.
*
* Useful as a top level "blocking" overscreen while the app is being loaded.
*/
export const LoadingOverlay: React.FC = () => (
<Overlay
sx={(theme) => ({
display: "flex",
justifyContent: "center",
alignItems: "center",
zIndex: 2000,
backgroundColor: theme.colors.background.base,
})}
>
<ActivityIndicator />
</Overlay>
);

View File

@@ -0,0 +1,21 @@
import { setupI18n } from "@/base/i18n";
import { useEffect, useState } from "react";
/**
* A hook that initializes the localization library that we use.
*
* This is only meant to be called from the top level `_app.tsx`, as this
* initialization is expected to only happen once for the lifetime of the page.
*
* @returns a boolean which will be set to true when the localized strings have
* been loaded.
*/
export const useSetupI18n = () => {
const [isI18nReady, setIsI18nReady] = useState(false);
useEffect(() => {
void setupI18n().finally(() => setIsI18nReady(true));
}, []);
return isI18nReady;
};