[web] Misc improvements (non functional) (#4654)
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
</>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
23
web/packages/base/components/LoadingOverlay.tsx
Normal file
23
web/packages/base/components/LoadingOverlay.tsx
Normal 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>
|
||||
);
|
||||
21
web/packages/base/components/utils/hooks-i18n.ts
Normal file
21
web/packages/base/components/utils/hooks-i18n.ts
Normal 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;
|
||||
};
|
||||
Reference in New Issue
Block a user