[web] General refactoring (#6072)
This commit is contained in:
@@ -20,7 +20,6 @@ import {
|
||||
} from "ente-new/photos/components/Tiles";
|
||||
import type { CollectionSummary } from "ente-new/photos/services/collection/ui";
|
||||
import { CollectionsSortBy } from "ente-new/photos/services/collection/ui";
|
||||
import { FlexWrapper } from "ente-shared/components/Container";
|
||||
import { t } from "i18next";
|
||||
import memoize from "memoize-one";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
@@ -168,7 +167,7 @@ const AlbumsRow = React.memo(
|
||||
const collectionRow = collectionRowList[index];
|
||||
return (
|
||||
<div style={style}>
|
||||
<FlexWrapper gap={"4px"} padding={"16px"}>
|
||||
<Stack direction="row" sx={{ p: 2, gap: 0.5 }}>
|
||||
{collectionRow.map((item: any) => (
|
||||
<AlbumCard
|
||||
isScrolling={isScrolling}
|
||||
@@ -177,7 +176,7 @@ const AlbumsRow = React.memo(
|
||||
key={item.id}
|
||||
/>
|
||||
))}
|
||||
</FlexWrapper>
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -12,18 +12,10 @@ import Photo, { default as PhotoIcon } from "@mui/icons-material/Photo";
|
||||
import PublicIcon from "@mui/icons-material/Public";
|
||||
import RemoveCircleOutlineIcon from "@mui/icons-material/RemoveCircleOutline";
|
||||
import WorkspacesIcon from "@mui/icons-material/Workspaces";
|
||||
import {
|
||||
Dialog,
|
||||
DialogProps,
|
||||
FormHelperText,
|
||||
Stack,
|
||||
styled,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import { Dialog, DialogProps, Stack, styled, Typography } from "@mui/material";
|
||||
import NumberAvatar from "@mui/material/Avatar";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import Avatar from "components/pages/gallery/Avatar";
|
||||
import { FocusVisibleButton } from "ente-base/components/mui/FocusVisibleButton";
|
||||
import { LoadingButton } from "ente-base/components/mui/LoadingButton";
|
||||
import {
|
||||
NestedSidebarDrawer,
|
||||
@@ -43,6 +35,7 @@ import { useClipboardCopy } from "ente-base/components/utils/hooks";
|
||||
import { useModalVisibility } from "ente-base/components/utils/modal";
|
||||
import { useBaseContext } from "ente-base/context";
|
||||
import { sharedCryptoWorker } from "ente-base/crypto";
|
||||
import { isHTTP4xxError } from "ente-base/http";
|
||||
import { formattedDateTime } from "ente-base/i18n-date";
|
||||
import log from "ente-base/log";
|
||||
import { appendCollectionKeyToShareURL } from "ente-gallery/services/share";
|
||||
@@ -522,6 +515,9 @@ const AddParticipant: React.FC<AddParticipantProps> = ({
|
||||
await shareCollection(collection, email, type);
|
||||
await syncWithRemote(false, true);
|
||||
} catch (e) {
|
||||
if (isHTTP4xxError(e)) {
|
||||
throw new Error(t("sharing_user_does_not_exist"));
|
||||
}
|
||||
const errorMessage = handleSharingErrors(e);
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
@@ -549,15 +545,11 @@ const AddParticipant: React.FC<AddParticipantProps> = ({
|
||||
onClose={onClose}
|
||||
callback={collectionShare}
|
||||
optionsList={nonSharedEmails}
|
||||
placeholder={t("enter_email")}
|
||||
fieldType="email"
|
||||
buttonText={
|
||||
type == "VIEWER"
|
||||
? t("add_viewers")
|
||||
: t("add_collaborators")
|
||||
}
|
||||
submitButtonProps={{ size: "large", sx: { mt: 1, mb: 2 } }}
|
||||
disableAutoFocus
|
||||
/>
|
||||
</Stack>
|
||||
</NestedSidebarDrawer>
|
||||
@@ -571,27 +563,12 @@ interface AddParticipantFormValues {
|
||||
|
||||
interface AddParticipantFormProps {
|
||||
callback: (props: { email?: string; emails?: string[] }) => Promise<void>;
|
||||
fieldType: "text" | "email" | "password";
|
||||
placeholder: string;
|
||||
buttonText: string;
|
||||
submitButtonProps?: any;
|
||||
initialValue?: string;
|
||||
secondaryButtonAction?: () => void;
|
||||
disableAutoFocus?: boolean;
|
||||
hiddenPreInput?: any;
|
||||
caption?: any;
|
||||
hiddenPostInput?: any;
|
||||
autoComplete?: string;
|
||||
blockButton?: boolean;
|
||||
hiddenLabel?: boolean;
|
||||
onClose?: () => void;
|
||||
optionsList?: string[];
|
||||
}
|
||||
|
||||
const AddParticipantForm: React.FC<AddParticipantFormProps> = (props) => {
|
||||
const { submitButtonProps } = props;
|
||||
const { sx: buttonSx, ...restSubmitButtonProps } = submitButtonProps ?? {};
|
||||
|
||||
const [loading, SetLoading] = useState(false);
|
||||
|
||||
const submitForm = async (
|
||||
@@ -615,17 +592,10 @@ const AddParticipantForm: React.FC<AddParticipantFormProps> = (props) => {
|
||||
};
|
||||
|
||||
const validationSchema = useMemo(() => {
|
||||
switch (props.fieldType) {
|
||||
case "text":
|
||||
return Yup.object().shape({
|
||||
inputValue: Yup.string().required(t("required")),
|
||||
});
|
||||
case "email":
|
||||
return Yup.object().shape({
|
||||
inputValue: Yup.string().email(t("invalid_email_error")),
|
||||
});
|
||||
}
|
||||
}, [props.fieldType]);
|
||||
return Yup.object().shape({
|
||||
inputValue: Yup.string().email(t("invalid_email_error")),
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleInputFieldClick = (setFieldValue) => {
|
||||
setFieldValue("selectedOptions", []);
|
||||
@@ -633,10 +603,7 @@ const AddParticipantForm: React.FC<AddParticipantFormProps> = (props) => {
|
||||
|
||||
return (
|
||||
<Formik<AddParticipantFormValues>
|
||||
initialValues={{
|
||||
inputValue: props.initialValue ?? "",
|
||||
selectedOptions: [],
|
||||
}}
|
||||
initialValues={{ inputValue: "", selectedOptions: [] }}
|
||||
onSubmit={submitForm}
|
||||
validationSchema={validationSchema}
|
||||
validateOnChange={false}
|
||||
@@ -651,31 +618,25 @@ const AddParticipantForm: React.FC<AddParticipantFormProps> = (props) => {
|
||||
}) => (
|
||||
<form noValidate onSubmit={handleSubmit}>
|
||||
<Stack sx={{ gap: "24px", py: "20px", px: "12px" }}>
|
||||
{props.hiddenPreInput}
|
||||
<Stack>
|
||||
<RowButtonGroupTitle>
|
||||
{t("add_new_email")}
|
||||
</RowButtonGroupTitle>
|
||||
<TextField
|
||||
sx={{ marginTop: 0 }}
|
||||
hiddenLabel={props.hiddenLabel}
|
||||
fullWidth
|
||||
type={props.fieldType}
|
||||
id={props.fieldType}
|
||||
id={"email"}
|
||||
name={"email"}
|
||||
type={"email"}
|
||||
label={t("enter_email")}
|
||||
sx={{ mt: 0 }}
|
||||
disabled={loading}
|
||||
onChange={handleChange("inputValue")}
|
||||
onClick={() =>
|
||||
handleInputFieldClick(setFieldValue)
|
||||
}
|
||||
name={props.fieldType}
|
||||
{...(props.hiddenLabel
|
||||
? { placeholder: props.placeholder }
|
||||
: { label: props.placeholder })}
|
||||
error={Boolean(errors.inputValue)}
|
||||
helperText={errors.inputValue}
|
||||
value={values.inputValue}
|
||||
disabled={loading}
|
||||
autoFocus={!props.disableAutoFocus}
|
||||
autoComplete={props.autoComplete}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
@@ -735,51 +696,19 @@ const AddParticipantForm: React.FC<AddParticipantFormProps> = (props) => {
|
||||
</RowButtonGroup>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
<FormHelperText
|
||||
sx={{
|
||||
position: "relative",
|
||||
top: errors.inputValue ? "-22px" : "0",
|
||||
float: "right",
|
||||
padding: "0 8px",
|
||||
}}
|
||||
>
|
||||
{props.caption}
|
||||
</FormHelperText>
|
||||
{props.hiddenPostInput}
|
||||
</Stack>
|
||||
<FlexWrapper
|
||||
px={"8px"}
|
||||
justifyContent={"center"}
|
||||
flexWrap={props.blockButton ? "wrap-reverse" : "nowrap"}
|
||||
flexWrap={"nowrap"}
|
||||
>
|
||||
<Stack sx={{ px: "8px", width: "100%" }}>
|
||||
{props.secondaryButtonAction && (
|
||||
<FocusVisibleButton
|
||||
onClick={props.secondaryButtonAction}
|
||||
fullWidth
|
||||
color="secondary"
|
||||
sx={{
|
||||
"&&&": {
|
||||
mt: !props.blockButton ? 2 : 0.5,
|
||||
mb: !props.blockButton ? 4 : 0,
|
||||
mr: !props.blockButton ? 1 : 0,
|
||||
...buttonSx,
|
||||
},
|
||||
}}
|
||||
{...restSubmitButtonProps}
|
||||
>
|
||||
{t("cancel")}
|
||||
</FocusVisibleButton>
|
||||
)}
|
||||
|
||||
<LoadingButton
|
||||
type="submit"
|
||||
color="accent"
|
||||
fullWidth
|
||||
loading={loading}
|
||||
sx={{ mt: 2, mb: 4 }}
|
||||
{...restSubmitButtonProps}
|
||||
sx={{ mt: 4, mb: 4 }}
|
||||
>
|
||||
{props.buttonText}
|
||||
</LoadingButton>
|
||||
|
||||
@@ -27,6 +27,7 @@ import { useBaseContext } from "ente-base/context";
|
||||
import { basename, dirname, joinPath } from "ente-base/file-name";
|
||||
import log from "ente-base/log";
|
||||
import type { CollectionMapping, Electron, ZipItem } from "ente-base/types/ipc";
|
||||
import { type UploadTypeSelectorIntent } from "ente-gallery/components/Upload";
|
||||
import { useFileInput } from "ente-gallery/components/utils/use-file-input";
|
||||
import {
|
||||
groupItemsBasedOnParentFolder,
|
||||
@@ -77,8 +78,6 @@ import { PublicCollectionGalleryContext } from "utils/publicCollectionGallery";
|
||||
import { SetCollectionNamerAttributes } from "./Collections/CollectionNamer";
|
||||
import { UploadProgress } from "./UploadProgress";
|
||||
|
||||
export type UploadTypeSelectorIntent = "upload" | "import" | "collect";
|
||||
|
||||
interface UploadProps {
|
||||
syncWithRemote: (force?: boolean, silent?: boolean) => Promise<void>;
|
||||
closeUploadTypeSelector: () => void;
|
||||
@@ -551,7 +550,7 @@ export const Upload: React.FC<UploadProps> = ({
|
||||
|
||||
const preCollectionCreationAction = async () => {
|
||||
props.onCloseCollectionSelector?.();
|
||||
props.setShouldDisableDropzone(!uploadManager.shouldAllowNewUpload());
|
||||
props.setShouldDisableDropzone(uploadManager.isUploadInProgress());
|
||||
setUploadPhase("preparing");
|
||||
setUploadProgressView(true);
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
} from "components/FilesDownloadProgress";
|
||||
import { FixCreationTime } from "components/FixCreationTime";
|
||||
import { Sidebar } from "components/Sidebar";
|
||||
import { Upload, type UploadTypeSelectorIntent } from "components/Upload";
|
||||
import { Upload } from "components/Upload";
|
||||
import SelectedFileOptions from "components/pages/gallery/SelectedFileOptions";
|
||||
import { sessionExpiredDialogAttributes } from "ente-accounts/components/utils/dialog";
|
||||
import { stashRedirect } from "ente-accounts/services/redirect";
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
masterKeyFromSessionIfLoggedIn,
|
||||
} from "ente-base/session";
|
||||
import { FullScreenDropZone } from "ente-gallery/components/FullScreenDropZone";
|
||||
import { type UploadTypeSelectorIntent } from "ente-gallery/components/Upload";
|
||||
import { type Collection } from "ente-media/collection";
|
||||
import { type EnteFile } from "ente-media/file";
|
||||
import {
|
||||
@@ -785,9 +786,7 @@ const Page: React.FC = () => {
|
||||
};
|
||||
|
||||
const openUploader = (intent?: UploadTypeSelectorIntent) => {
|
||||
if (!uploadManager.shouldAllowNewUpload()) {
|
||||
return;
|
||||
}
|
||||
if (uploadManager.isUploadInProgress()) return;
|
||||
setUploadTypeSelectorView(true);
|
||||
setUploadTypeSelectorIntent(intent ?? "upload");
|
||||
};
|
||||
@@ -1095,8 +1094,8 @@ const Page: React.FC = () => {
|
||||
!hiddenFiles?.length &&
|
||||
activeCollectionID === ALL_SECTION ? (
|
||||
<GalleryEmptyState
|
||||
openUploader={openUploader}
|
||||
shouldAllowNewUpload={uploadManager.shouldAllowNewUpload()}
|
||||
isUploadInProgress={uploadManager.isUploadInProgress()}
|
||||
onUpload={openUploader}
|
||||
/>
|
||||
) : !isInSearchMode &&
|
||||
!isFirstLoad &&
|
||||
@@ -1218,7 +1217,7 @@ const SidebarButton: React.FC<ButtonishProps> = ({ onClick }) => (
|
||||
);
|
||||
|
||||
const UploadButton: React.FC<ButtonishProps> = ({ onClick }) => {
|
||||
const disabled = !uploadManager.shouldAllowNewUpload();
|
||||
const disabled = uploadManager.isUploadInProgress();
|
||||
const isSmallWidth = useIsSmallWidth();
|
||||
|
||||
const icon = <FileUploadOutlinedIcon />;
|
||||
|
||||
@@ -558,7 +558,7 @@ const EnteLogoLink = styled("a")(({ theme }) => ({
|
||||
}));
|
||||
|
||||
const AddPhotosButton: React.FC<ButtonishProps> = ({ onClick }) => {
|
||||
const disabled = !uploadManager.shouldAllowNewUpload();
|
||||
const disabled = uploadManager.isUploadInProgress();
|
||||
const isSmallWidth = useIsSmallWidth();
|
||||
|
||||
const icon = <AddPhotoAlternateOutlinedIcon />;
|
||||
@@ -585,7 +585,7 @@ const AddPhotosButton: React.FC<ButtonishProps> = ({ onClick }) => {
|
||||
* shrink on mobile sized screens.
|
||||
*/
|
||||
const AddMorePhotosButton: React.FC<ButtonishProps> = ({ onClick }) => {
|
||||
const disabled = !uploadManager.shouldAllowNewUpload();
|
||||
const disabled = uploadManager.isUploadInProgress();
|
||||
|
||||
return (
|
||||
<FocusVisibleButton
|
||||
|
||||
@@ -682,8 +682,13 @@ class UploadManager {
|
||||
this.onUploadFile(decryptedFile);
|
||||
}
|
||||
|
||||
public shouldAllowNewUpload = () => {
|
||||
return !this.uploadInProgress || watcher.isUploadRunning();
|
||||
/**
|
||||
* `true` if an upload is currently in-progress (either a bunch of files
|
||||
* directly uploaded by the user, or files being uploaded by the folder
|
||||
* watch functionality).
|
||||
*/
|
||||
public isUploadInProgress = () => {
|
||||
return this.uploadInProgress || watcher.isUploadRunning();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ import { ShowHidePasswordInputAdornment } from "ente-base/components/mui/Passwor
|
||||
import { isMuseumHTTPError } from "ente-base/http";
|
||||
import log from "ente-base/log";
|
||||
import { setLSUser } from "ente-shared//storage/localStorage";
|
||||
import { VerticallyCentered } from "ente-shared/components/Container";
|
||||
import {
|
||||
generateAndSaveIntermediateKeyAttributes,
|
||||
saveKeyInSessionStore,
|
||||
@@ -149,7 +148,7 @@ export const SignUpContents: React.FC<SignUpContentsProps> = ({
|
||||
handleSubmit,
|
||||
}): React.JSX.Element => (
|
||||
<form noValidate onSubmit={handleSubmit}>
|
||||
<VerticallyCentered sx={{ mb: 2 }}>
|
||||
<Stack sx={{ mb: 2 }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
id="email"
|
||||
@@ -290,7 +289,7 @@ export const SignUpContents: React.FC<SignUpContentsProps> = ({
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
</VerticallyCentered>
|
||||
</Stack>
|
||||
<Box sx={{ mb: 1 }}>
|
||||
<LoadingButton
|
||||
fullWidth
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Alert, Box, TextField } from "@mui/material";
|
||||
import { Alert, Box, Stack, TextField } from "@mui/material";
|
||||
import {
|
||||
AccountsPageContents,
|
||||
AccountsPageFooter,
|
||||
@@ -10,7 +10,6 @@ import { LinkButton } from "ente-base/components/LinkButton";
|
||||
import { LoadingButton } from "ente-base/components/mui/LoadingButton";
|
||||
import { isHTTPErrorWithStatus } from "ente-base/http";
|
||||
import log from "ente-base/log";
|
||||
import { VerticallyCentered } from "ente-shared/components/Container";
|
||||
import { getData, setLSUser } from "ente-shared/storage/localStorage";
|
||||
import { Formik, type FormikHelpers } from "formik";
|
||||
import { t } from "i18next";
|
||||
@@ -141,7 +140,7 @@ const ChangeEmailForm: React.FC = () => {
|
||||
</Alert>
|
||||
)}
|
||||
<form noValidate onSubmit={handleSubmit}>
|
||||
<VerticallyCentered>
|
||||
<Stack>
|
||||
<TextField
|
||||
fullWidth
|
||||
type="email"
|
||||
@@ -177,7 +176,7 @@ const ChangeEmailForm: React.FC = () => {
|
||||
>
|
||||
{!ottInputVisible ? t("send_otp") : t("verify")}
|
||||
</LoadingButton>
|
||||
</VerticallyCentered>
|
||||
</Stack>
|
||||
</form>
|
||||
|
||||
<AccountsPageFooter>
|
||||
|
||||
7
web/packages/gallery/components/Upload.tsx
Normal file
7
web/packages/gallery/components/Upload.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* The upload can be triggered by different buttons and flows in the UI, each of
|
||||
* which is referred to as an "intent".
|
||||
*
|
||||
* The "intent" does not change the eventual upload outcome, only the UX flow.
|
||||
*/
|
||||
export type UploadTypeSelectorIntent = "upload" | "import" | "collect";
|
||||
@@ -7,11 +7,17 @@
|
||||
* there.
|
||||
*/
|
||||
|
||||
import { Paper, Stack, Typography } from "@mui/material";
|
||||
import AddPhotoAlternateIcon from "@mui/icons-material/AddPhotoAlternateOutlined";
|
||||
import FolderIcon from "@mui/icons-material/FolderOutlined";
|
||||
import { Paper, Stack, styled, Typography } from "@mui/material";
|
||||
import { CenteredFill } from "ente-base/components/containers";
|
||||
import { EnteLogo } from "ente-base/components/EnteLogo";
|
||||
import { FocusVisibleButton } from "ente-base/components/mui/FocusVisibleButton";
|
||||
import { type UploadTypeSelectorIntent } from "ente-gallery/components/Upload";
|
||||
import type { SearchSuggestion } from "ente-new/photos/services/search/types";
|
||||
import { t } from "i18next";
|
||||
import React, { useState } from "react";
|
||||
import { Trans } from "react-i18next";
|
||||
import { enableML } from "../../services/ml";
|
||||
import { EnableML, FaceConsent } from "../sidebar/MLSettings";
|
||||
import { useMLStatusSnapshot } from "../utils/use-snapshot";
|
||||
@@ -51,93 +57,84 @@ export const SearchResultsHeader: React.FC<SearchResultsHeaderProps> = ({
|
||||
</GalleryItemsHeaderAdapter>
|
||||
);
|
||||
|
||||
import AddPhotoAlternateIcon from "@mui/icons-material/AddPhotoAlternateOutlined";
|
||||
import FolderIcon from "@mui/icons-material/FolderOutlined";
|
||||
import { Button, styled } from "@mui/material";
|
||||
import { EnteLogo } from "ente-base/components/EnteLogo";
|
||||
import {
|
||||
FlexWrapper,
|
||||
VerticallyCentered,
|
||||
} from "ente-shared/components/Container";
|
||||
import { Trans } from "react-i18next";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
export function GalleryEmptyState({ openUploader, shouldAllowNewUpload }) {
|
||||
return (
|
||||
<Wrapper>
|
||||
<Stack sx={{ flex: "none", paddingBlock: "12px 32px" }}>
|
||||
<VerticallyCentered sx={{ flex: "none" }}>
|
||||
<Typography
|
||||
variant="h3"
|
||||
sx={{
|
||||
color: "text.muted",
|
||||
userSelect: "none",
|
||||
marginBlockEnd: 1,
|
||||
svg: {
|
||||
color: "text.base",
|
||||
verticalAlign: "middle",
|
||||
marginBlockEnd: "2px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Trans
|
||||
i18nKey="welcome_to_ente_title"
|
||||
components={{ a: <EnteLogo /> }}
|
||||
/>
|
||||
</Typography>
|
||||
<Typography variant="h2">
|
||||
{t("welcome_to_ente_subtitle")}
|
||||
</Typography>
|
||||
</VerticallyCentered>
|
||||
</Stack>
|
||||
<NonDraggableImage
|
||||
height={287.57}
|
||||
alt=""
|
||||
src="/images/empty-state/ente_duck.png"
|
||||
srcSet="/images/empty-state/ente_duck@2x.png, /images/empty-state/ente_duck@3x.png"
|
||||
/>
|
||||
<VerticallyCentered paddingTop={1.5} paddingBottom={1.5}>
|
||||
<Button
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
style={{ cursor: !shouldAllowNewUpload && "not-allowed" }}
|
||||
color="accent"
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call
|
||||
onClick={() => openUploader("upload")}
|
||||
disabled={!shouldAllowNewUpload}
|
||||
sx={{ mt: 1.5, p: 1, width: 320, borderRadius: 0.5 }}
|
||||
>
|
||||
<FlexWrapper sx={{ gap: 1 }} justifyContent="center">
|
||||
<AddPhotoAlternateIcon />
|
||||
{t("upload_first_photo")}
|
||||
</FlexWrapper>
|
||||
</Button>
|
||||
<Button
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
style={{ cursor: !shouldAllowNewUpload && "not-allowed" }}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call
|
||||
onClick={() => openUploader("import")}
|
||||
disabled={!shouldAllowNewUpload}
|
||||
sx={{ mt: 1.5, p: 1, width: 320, borderRadius: 0.5 }}
|
||||
>
|
||||
<FlexWrapper sx={{ gap: 1 }} justifyContent="center">
|
||||
<FolderIcon />
|
||||
{t("import_your_folders")}
|
||||
</FlexWrapper>
|
||||
</Button>
|
||||
</VerticallyCentered>
|
||||
</Wrapper>
|
||||
);
|
||||
interface GalleryEmptyStateProps {
|
||||
/**
|
||||
* If `true`, then an upload is already in progress (the empty state will
|
||||
* then disable the prompts for uploads).
|
||||
*/
|
||||
isUploadInProgress: boolean;
|
||||
/**
|
||||
* Called when the user selects one of the upload buttons. It is passed the
|
||||
* "intent" of the user.
|
||||
*/
|
||||
onUpload: (intent: UploadTypeSelectorIntent) => void;
|
||||
}
|
||||
|
||||
const Wrapper = styled("div")`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
`;
|
||||
export const GalleryEmptyState: React.FC<GalleryEmptyStateProps> = ({
|
||||
isUploadInProgress,
|
||||
onUpload,
|
||||
}) => (
|
||||
<Stack sx={{ alignItems: "center" }}>
|
||||
<Stack
|
||||
sx={{
|
||||
alignItems: "center",
|
||||
textAlign: "center",
|
||||
paddingBlock: "12px 32px",
|
||||
userSelect: "none",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h3"
|
||||
sx={{
|
||||
color: "text.muted",
|
||||
mb: 1,
|
||||
svg: {
|
||||
color: "text.base",
|
||||
verticalAlign: "middle",
|
||||
mb: "2px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Trans
|
||||
i18nKey="welcome_to_ente_title"
|
||||
components={{ a: <EnteLogo /> }}
|
||||
/>
|
||||
</Typography>
|
||||
<Typography variant="h2">
|
||||
{t("welcome_to_ente_subtitle")}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<NonDraggableImage
|
||||
height={287.57}
|
||||
alt=""
|
||||
src="/images/empty-state/ente_duck.png"
|
||||
srcSet="/images/empty-state/ente_duck@2x.png, /images/empty-state/ente_duck@3x.png"
|
||||
/>
|
||||
<Stack sx={{ py: 3, width: 320, gap: 1 }}>
|
||||
<FocusVisibleButton
|
||||
color="accent"
|
||||
onClick={() => onUpload("upload")}
|
||||
disabled={isUploadInProgress}
|
||||
sx={{ p: 1 }}
|
||||
>
|
||||
<Stack direction="row" sx={{ gap: 1, alignItems: "center" }}>
|
||||
<AddPhotoAlternateIcon />
|
||||
{t("upload_first_photo")}
|
||||
</Stack>
|
||||
</FocusVisibleButton>
|
||||
<FocusVisibleButton
|
||||
onClick={() => onUpload("import")}
|
||||
disabled={isUploadInProgress}
|
||||
sx={{ p: 1 }}
|
||||
>
|
||||
<Stack direction="row" sx={{ gap: 1, alignItems: "center" }}>
|
||||
<FolderIcon />
|
||||
{t("import_your_folders")}
|
||||
</Stack>
|
||||
</FocusVisibleButton>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
/**
|
||||
* Prevent the image from being selected _and_ dragged, since dragging it
|
||||
|
||||
@@ -1,25 +1,7 @@
|
||||
import { Box, styled } from "@mui/material";
|
||||
|
||||
export const VerticallyCentered = styled(Box)`
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
overflow: auto;
|
||||
`;
|
||||
|
||||
export const FlexWrapper = styled(Box)`
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
/**
|
||||
* Deprecated, use {@link SpacedRow} from ente-base/components/mui/container
|
||||
* instead
|
||||
*/
|
||||
export const SpaceBetweenFlex = styled(FlexWrapper)`
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user