diff --git a/web/apps/photos/src/components/Collections/AllAlbums.tsx b/web/apps/photos/src/components/Collections/AllAlbums.tsx
index 4eaad44e91..9406779c0b 100644
--- a/web/apps/photos/src/components/Collections/AllAlbums.tsx
+++ b/web/apps/photos/src/components/Collections/AllAlbums.tsx
@@ -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 (
-
+
{collectionRow.map((item: any) => (
))}
-
+
);
},
diff --git a/web/apps/photos/src/components/Collections/CollectionShare.tsx b/web/apps/photos/src/components/Collections/CollectionShare.tsx
index 30320038db..3d3fe1ed87 100644
--- a/web/apps/photos/src/components/Collections/CollectionShare.tsx
+++ b/web/apps/photos/src/components/Collections/CollectionShare.tsx
@@ -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 = ({
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 = ({
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
/>
@@ -571,27 +563,12 @@ interface AddParticipantFormValues {
interface AddParticipantFormProps {
callback: (props: { email?: string; emails?: string[] }) => Promise;
- 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 = (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 = (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 = (props) => {
return (
- initialValues={{
- inputValue: props.initialValue ?? "",
- selectedOptions: [],
- }}
+ initialValues={{ inputValue: "", selectedOptions: [] }}
onSubmit={submitForm}
validationSchema={validationSchema}
validateOnChange={false}
@@ -651,31 +618,25 @@ const AddParticipantForm: React.FC = (props) => {
}) => (
diff --git a/web/packages/gallery/components/Upload.tsx b/web/packages/gallery/components/Upload.tsx
new file mode 100644
index 0000000000..7f723875cf
--- /dev/null
+++ b/web/packages/gallery/components/Upload.tsx
@@ -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";
diff --git a/web/packages/new/photos/components/gallery/index.tsx b/web/packages/new/photos/components/gallery/index.tsx
index ee8aad7040..66edbedb6b 100644
--- a/web/packages/new/photos/components/gallery/index.tsx
+++ b/web/packages/new/photos/components/gallery/index.tsx
@@ -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 = ({
);
-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 (
-
-
-
-
- }}
- />
-
-
- {t("welcome_to_ente_subtitle")}
-
-
-
-
-
-
-
-
-
- );
+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 = ({
+ isUploadInProgress,
+ onUpload,
+}) => (
+
+
+
+ }}
+ />
+
+
+ {t("welcome_to_ente_subtitle")}
+
+
+
+
+ onUpload("upload")}
+ disabled={isUploadInProgress}
+ sx={{ p: 1 }}
+ >
+
+
+ {t("upload_first_photo")}
+
+
+ onUpload("import")}
+ disabled={isUploadInProgress}
+ sx={{ p: 1 }}
+ >
+
+
+ {t("import_your_folders")}
+
+
+
+
+);
/**
* Prevent the image from being selected _and_ dragged, since dragging it
diff --git a/web/packages/shared/components/Container.tsx b/web/packages/shared/components/Container.tsx
index 63b9bf82b5..6d6f935d7e 100644
--- a/web/packages/shared/components/Container.tsx
+++ b/web/packages/shared/components/Container.tsx
@@ -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;
-`;