This commit is contained in:
Manav Rathi 2024-05-09 09:54:06 +05:30
parent d69ff79a67
commit bedb515dc2
No known key found for this signature in database
4 changed files with 198 additions and 174 deletions

View file

@ -1,9 +1,24 @@
export default function UploadSelectorInputs({
type GetInputProps = () => React.HTMLAttributes<HTMLInputElement>;
interface UploadSelectorInputsProps {
getDragAndDropInputProps: GetInputProps;
getFileSelectorInputProps: GetInputProps;
getFolderSelectorInputProps: GetInputProps;
getZipFileSelectorInputProps?: GetInputProps;
}
/**
* Create a bunch of HTML inputs elements, one each for the given props.
*
* These hidden input element serve as the way for us to show various file /
* folder Selector dialogs and handle drag and drop inputs.
*/
export const UploadSelectorInputs: React.FC<UploadSelectorInputsProps> = ({
getDragAndDropInputProps,
getFileSelectorInputProps,
getFolderSelectorInputProps,
getZipFileSelectorInputProps,
}) {
}) => {
return (
<>
<input {...getDragAndDropInputProps()} />
@ -14,4 +29,4 @@ export default function UploadSelectorInputs({
)}
</>
);
}
};

View file

@ -1,82 +1,36 @@
import {
SESSION_KEYS,
clearKeys,
getKey,
} from "@ente/shared/storage/sessionStorage";
import { Typography, styled } from "@mui/material";
import { t } from "i18next";
import { useRouter } from "next/router";
import {
createContext,
useContext,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import {
constructEmailList,
createAlbum,
getAllLatestCollections,
getAllLocalCollections,
getCollectionSummaries,
getFavItemIds,
getHiddenItemsSummary,
getSectionSummaries,
} from "services/collectionService";
import { getLocalFiles, syncFiles } from "services/fileService";
import { checkSubscriptionPurchase } from "utils/billing";
import log from "@/next/log";
import { APPS } from "@ente/shared/apps/constants";
import { CenteredFlex } from "@ente/shared/components/Container";
import EnteSpinner from "@ente/shared/components/EnteSpinner";
import { PHOTOS_PAGES as PAGES } from "@ente/shared/constants/pages";
import { CustomError } from "@ente/shared/error";
import { useFileInput } from "@ente/shared/hooks/useFileInput";
import useMemoSingleThreaded from "@ente/shared/hooks/useMemoSingleThreaded";
import InMemoryStore, { MS_KEYS } from "@ente/shared/storage/InMemoryStore";
import { LS_KEYS, getData } from "@ente/shared/storage/localStorage";
import {
getToken,
isFirstLogin,
justSignedUp,
setIsFirstLogin,
setJustSignedUp,
} from "@ente/shared/storage/localStorage/helpers";
import CollectionSelector, {
CollectionSelectorAttributes,
} from "components/Collections/CollectionSelector";
import FullScreenDropZone from "components/FullScreenDropZone";
import { LoadingOverlay } from "components/LoadingOverlay";
import PhotoFrame from "components/PhotoFrame";
import Sidebar from "components/Sidebar";
import SelectedFileOptions from "components/pages/gallery/SelectedFileOptions";
import { useDropzone } from "react-dropzone";
import {
isTokenValid,
syncMapEnabled,
validateKey,
} from "services/userService";
import { preloadImage } from "utils/common";
import {
FILE_OPS_TYPE,
constructFileToCollectionMap,
getSelectedFiles,
getUniqueFiles,
handleFileOps,
mergeMetadata,
sortFiles,
} from "utils/file";
import log from "@/next/log";
import { APPS } from "@ente/shared/apps/constants";
import { CenteredFlex } from "@ente/shared/components/Container";
import { PHOTOS_PAGES as PAGES } from "@ente/shared/constants/pages";
import { CustomError } from "@ente/shared/error";
import useFileInput from "@ente/shared/hooks/useFileInput";
import useMemoSingleThreaded from "@ente/shared/hooks/useMemoSingleThreaded";
import InMemoryStore, { MS_KEYS } from "@ente/shared/storage/InMemoryStore";
import { LS_KEYS, getData } from "@ente/shared/storage/localStorage";
import { getToken } from "@ente/shared/storage/localStorage/helpers";
SESSION_KEYS,
clearKeys,
getKey,
} from "@ente/shared/storage/sessionStorage";
import { User } from "@ente/shared/user/types";
import { isPromise } from "@ente/shared/utils";
import { Typography, styled } from "@mui/material";
import AuthenticateUserModal from "components/AuthenticateUserModal";
import Collections from "components/Collections";
import CollectionNamer, {
CollectionNamerAttributes,
} from "components/Collections/CollectionNamer";
import CollectionSelector, {
CollectionSelectorAttributes,
} from "components/Collections/CollectionSelector";
import ExportModal from "components/ExportModal";
import {
FilesDownloadProgress,
@ -85,13 +39,18 @@ import {
import FixCreationTime, {
FixCreationTimeAttributes,
} from "components/FixCreationTime";
import FullScreenDropZone from "components/FullScreenDropZone";
import GalleryEmptyState from "components/GalleryEmptyState";
import { LoadingOverlay } from "components/LoadingOverlay";
import PhotoFrame from "components/PhotoFrame";
import { ITEM_TYPE, TimeStampListItem } from "components/PhotoList";
import SearchResultInfo from "components/Search/SearchResultInfo";
import Sidebar from "components/Sidebar";
import Uploader from "components/Upload/Uploader";
import UploadInputs from "components/UploadSelectorInputs";
import { UploadSelectorInputs } from "components/UploadSelectorInputs";
import { GalleryNavbar } from "components/pages/gallery/Navbar";
import PlanSelector from "components/pages/gallery/PlanSelector";
import SelectedFileOptions from "components/pages/gallery/SelectedFileOptions";
import {
ALL_SECTION,
ARCHIVE_SECTION,
@ -100,15 +59,42 @@ import {
TRASH_SECTION,
} from "constants/collection";
import { SYNC_INTERVAL_IN_MICROSECONDS } from "constants/gallery";
import { t } from "i18next";
import { useRouter } from "next/router";
import { AppContext } from "pages/_app";
import {
createContext,
useContext,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { useDropzone } from "react-dropzone";
import { clipService } from "services/clip-service";
import { constructUserIDToEmailMap } from "services/collectionService";
import {
constructEmailList,
constructUserIDToEmailMap,
createAlbum,
getAllLatestCollections,
getAllLocalCollections,
getCollectionSummaries,
getFavItemIds,
getHiddenItemsSummary,
getSectionSummaries,
} from "services/collectionService";
import downloadManager from "services/download";
import { syncEmbeddings, syncFileEmbeddings } from "services/embeddingService";
import { syncEntities } from "services/entityService";
import { getLocalFiles, syncFiles } from "services/fileService";
import locationSearchService from "services/locationSearchService";
import { getLocalTrashedFiles, syncTrash } from "services/trashService";
import uploadManager from "services/upload/uploadManager";
import {
isTokenValid,
syncMapEnabled,
validateKey,
} from "services/userService";
import { Collection, CollectionSummaries } from "types/collection";
import { EnteFile } from "types/file";
import {
@ -120,6 +106,7 @@ import {
} from "types/gallery";
import { Search, SearchResultSummary, UpdateSearch } from "types/search";
import { FamilyData } from "types/user";
import { checkSubscriptionPurchase } from "utils/billing";
import {
COLLECTION_OPS_TYPE,
constructCollectionNameMap,
@ -131,6 +118,16 @@ import {
splitNormalAndHiddenCollections,
} from "utils/collection";
import ComlinkSearchWorker from "utils/comlink/ComlinkSearchWorker";
import { preloadImage } from "utils/common";
import {
FILE_OPS_TYPE,
constructFileToCollectionMap,
getSelectedFiles,
getUniqueFiles,
handleFileOps,
mergeMetadata,
sortFiles,
} from "utils/file";
import { isArchivedFile } from "utils/magicMetadata";
import { getSessionExpiredMessage } from "utils/ui";
import { getLocalFamilyData } from "utils/user/family";
@ -201,8 +198,11 @@ export default function Gallery() {
const [isPhotoSwipeOpen, setIsPhotoSwipeOpen] = useState(false);
const {
// A function to call to get the props we should apply to the container,
getRootProps: getDragAndDropRootProps,
// ... the props we should apply to the <input> element,
getInputProps: getDragAndDropInputProps,
// ... and the files that we got.
acceptedFiles: dragAndDropFiles,
} = useDropzone({
noClick: true,
@ -210,23 +210,23 @@ export default function Gallery() {
disabled: shouldDisableDropzone,
});
const {
selectedFiles: fileSelectorFiles,
open: openFileSelector,
getInputProps: getFileSelectorInputProps,
openSelector: openFileSelector,
selectedFiles: fileSelectorFiles,
} = useFileInput({
directory: false,
});
const {
selectedFiles: folderSelectorFiles,
open: openFolderSelector,
getInputProps: getFolderSelectorInputProps,
openSelector: openFolderSelector,
selectedFiles: folderSelectorFiles,
} = useFileInput({
directory: true,
});
const {
selectedFiles: fileSelectorZipFiles,
open: openZipFileSelector,
getInputProps: getZipFileSelectorInputProps,
openSelector: openZipFileSelector,
selectedFiles: fileSelectorZipFiles,
} = useFileInput({
directory: false,
accept: ".zip",
@ -1013,14 +1013,14 @@ export default function Gallery() {
setSelectedFiles: setSelected,
}}
>
<FullScreenDropZone
getDragAndDropRootProps={getDragAndDropRootProps}
>
<UploadInputs
getDragAndDropInputProps={getDragAndDropInputProps}
getFileSelectorInputProps={getFileSelectorInputProps}
getFolderSelectorInputProps={getFolderSelectorInputProps}
getZipFileSelectorInputProps={getZipFileSelectorInputProps}
<FullScreenDropZone {...{ getDragAndDropRootProps }}>
<UploadSelectorInputs
{...{
getDragAndDropInputProps,
getFileSelectorInputProps,
getFolderSelectorInputProps,
getZipFileSelectorInputProps,
}}
/>
{blockingLoad && (
<LoadingOverlay>

View file

@ -1,15 +1,51 @@
import log from "@/next/log";
import { logoutUser } from "@ente/accounts/services/user";
import { APPS } from "@ente/shared/apps/constants";
import {
CenteredFlex,
SpaceBetweenFlex,
VerticallyCentered,
} from "@ente/shared/components/Container";
import EnteSpinner from "@ente/shared/components/EnteSpinner";
import FormPaper from "@ente/shared/components/Form/FormPaper";
import FormPaperTitle from "@ente/shared/components/Form/FormPaper/Title";
import OverflowMenu from "@ente/shared/components/OverflowMenu/menu";
import { OverflowMenuOption } from "@ente/shared/components/OverflowMenu/option";
import SingleInputForm, {
SingleInputFormProps,
} from "@ente/shared/components/SingleInputForm";
import { PHOTOS_PAGES as PAGES } from "@ente/shared/constants/pages";
import { ENTE_WEBSITE_LINK } from "@ente/shared/constants/urls";
import ComlinkCryptoWorker from "@ente/shared/crypto";
import { CustomError, parseSharingErrorCodes } from "@ente/shared/error";
import { useFileInput } from "@ente/shared/hooks/useFileInput";
import AddPhotoAlternateOutlined from "@mui/icons-material/AddPhotoAlternateOutlined";
import FileDownloadOutlinedIcon from "@mui/icons-material/FileDownloadOutlined";
import MoreHoriz from "@mui/icons-material/MoreHoriz";
import Typography from "@mui/material/Typography";
import bs58 from "bs58";
import { CollectionInfo } from "components/Collections/CollectionInfo";
import { CollectionInfoBarWrapper } from "components/Collections/styledComponents";
import {
FilesDownloadProgress,
FilesDownloadProgressAttributes,
} from "components/FilesDownloadProgress";
import FullScreenDropZone from "components/FullScreenDropZone";
import { LoadingOverlay } from "components/LoadingOverlay";
import PhotoFrame from "components/PhotoFrame";
import { ITEM_TYPE, TimeStampListItem } from "components/PhotoList";
import UploadButton from "components/Upload/UploadButton";
import Uploader from "components/Upload/Uploader";
import { UploadSelectorInputs } from "components/UploadSelectorInputs";
import SharedAlbumNavbar from "components/pages/sharedAlbum/Navbar";
import SelectedFileOptions from "components/pages/sharedAlbum/SelectedFileOptions";
import { ALL_SECTION } from "constants/collection";
import { t } from "i18next";
import { useRouter } from "next/router";
import { AppContext } from "pages/_app";
import { useContext, useEffect, useMemo, useRef, useState } from "react";
import { useDropzone } from "react-dropzone";
import downloadManager from "services/download";
import {
getLocalPublicCollection,
getLocalPublicCollectionPassword,
@ -25,50 +61,6 @@ import {
} from "services/publicCollectionService";
import { Collection } from "types/collection";
import { EnteFile } from "types/file";
import {
downloadSelectedFiles,
getSelectedFiles,
mergeMetadata,
sortFiles,
} from "utils/file";
import { PublicCollectionGalleryContext } from "utils/publicCollectionGallery";
import { logoutUser } from "@ente/accounts/services/user";
import { APPS } from "@ente/shared/apps/constants";
import EnteSpinner from "@ente/shared/components/EnteSpinner";
import FormPaper from "@ente/shared/components/Form/FormPaper";
import FormPaperTitle from "@ente/shared/components/Form/FormPaper/Title";
import OverflowMenu from "@ente/shared/components/OverflowMenu/menu";
import { OverflowMenuOption } from "@ente/shared/components/OverflowMenu/option";
import SingleInputForm, {
SingleInputFormProps,
} from "@ente/shared/components/SingleInputForm";
import { PHOTOS_PAGES as PAGES } from "@ente/shared/constants/pages";
import { ENTE_WEBSITE_LINK } from "@ente/shared/constants/urls";
import ComlinkCryptoWorker from "@ente/shared/crypto";
import useFileInput from "@ente/shared/hooks/useFileInput";
import AddPhotoAlternateOutlined from "@mui/icons-material/AddPhotoAlternateOutlined";
import FileDownloadOutlinedIcon from "@mui/icons-material/FileDownloadOutlined";
import MoreHoriz from "@mui/icons-material/MoreHoriz";
import Typography from "@mui/material/Typography";
import bs58 from "bs58";
import { CollectionInfo } from "components/Collections/CollectionInfo";
import { CollectionInfoBarWrapper } from "components/Collections/styledComponents";
import {
FilesDownloadProgress,
FilesDownloadProgressAttributes,
} from "components/FilesDownloadProgress";
import FullScreenDropZone from "components/FullScreenDropZone";
import { LoadingOverlay } from "components/LoadingOverlay";
import { ITEM_TYPE, TimeStampListItem } from "components/PhotoList";
import UploadButton from "components/Upload/UploadButton";
import Uploader from "components/Upload/Uploader";
import UploadSelectorInputs from "components/UploadSelectorInputs";
import SharedAlbumNavbar from "components/pages/sharedAlbum/Navbar";
import SelectedFileOptions from "components/pages/sharedAlbum/SelectedFileOptions";
import { useRouter } from "next/router";
import { useDropzone } from "react-dropzone";
import downloadManager from "services/download";
import {
SelectedState,
SetFilesDownloadProgressAttributes,
@ -76,6 +68,13 @@ import {
UploadTypeSelectorIntent,
} from "types/gallery";
import { downloadCollectionFiles, isHiddenCollection } from "utils/collection";
import {
downloadSelectedFiles,
getSelectedFiles,
mergeMetadata,
sortFiles,
} from "utils/file";
import { PublicCollectionGalleryContext } from "utils/publicCollectionGallery";
export default function PublicCollectionGallery() {
const token = useRef<string>(null);
@ -118,16 +117,16 @@ export default function PublicCollectionGallery() {
disabled: shouldDisableDropzone,
});
const {
selectedFiles: fileSelectorFiles,
open: openFileSelector,
getInputProps: getFileSelectorInputProps,
openSelector: openFileSelector,
selectedFiles: fileSelectorFiles,
} = useFileInput({
directory: false,
});
const {
selectedFiles: folderSelectorFiles,
open: openFolderSelector,
getInputProps: getFolderSelectorInputProps,
openSelector: openFolderSelector,
selectedFiles: folderSelectorFiles,
} = useFileInput({
directory: true,
});
@ -543,14 +542,13 @@ export default function PublicCollectionGallery() {
photoListFooter,
}}
>
<FullScreenDropZone
getDragAndDropRootProps={getDragAndDropRootProps}
>
<FullScreenDropZone {...{ getDragAndDropRootProps }}>
<UploadSelectorInputs
getDragAndDropInputProps={getDragAndDropInputProps}
getFileSelectorInputProps={getFileSelectorInputProps}
getFolderSelectorInputProps={getFolderSelectorInputProps}
getZipFileSelectorInputProps={undefined}
{...{
getDragAndDropInputProps,
getFileSelectorInputProps,
getFolderSelectorInputProps,
}}
/>
<SharedAlbumNavbar
showUploadButton={

View file

@ -1,56 +1,71 @@
import { useCallback, useRef, useState } from "react";
interface UseFileInputParams {
/**
* If `true`, the file open dialog will ask the user to select directories.
* Otherwise it'll ask the user to select files (default).
*/
directory?: boolean;
/**
* If specified, it'll restrict the type of files that the user can select
* by setting the "accept" attribute of the underlying HTML input element we
* use to surface the file selector dialog. For value of accept can be an
* extension or a MIME type (See
* https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept).
*/
accept?: string;
}
interface UseFileInputResult {
/**
* A function to call to get the properties that should be passed to a dummy
* `input` element that needs to be created to anchor the select file
* dialog. This input HTML element is not going to be visible, but it needs
* to be part of the DOM for {@link openSelector} to have effect.
*/
getInputProps: () => React.HTMLAttributes<HTMLInputElement>;
/**
* A function that can be called to open the select file / directory dialog.
*/
openSelector: () => void;
/**
* The list of {@link File}s that the user selected.
*
* This will be a list even if the user selected directories - in that case,
* it will be the recursive list of files within this directory.
*/
selectedFiles: File[];
}
/**
* Return three things:
* Wrap a open file selector into an easy to use package.
*
* - A function that can be called to trigger the showing of the select file /
* directory dialog.
* Returns a {@link UseFileInputResult} which contains a function to get the
* props for an input element, a function to open the file selector, and the
* list of selected files.
*
* - The list of properties that should be passed to a dummy `input` element
* that needs to be created to anchor the select file dialog. This input HTML
* element is not going to be visible, but it needs to be part of the DOM fro
* the open trigger to have effect.
*
* - The list of files that the user selected. This will be a list even if the
* user selected directories - in that case, it will be the recursive list of
* files within this directory.
*
* @param param0
*
* - If {@link directory} is true, the file open dialog will ask the user to
* select directories. Otherwise it'll ask the user to select files.
*
* - If {@link accept} is specified, it'll restrict the type of files that the
* user can select by setting the "accept" attribute of the underlying HTML
* input element we use to surface the file selector dialog. For value of
* accept can be an extension or a MIME type (See
* https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept).
* See the documentation of {@link UseFileInputParams} and
* {@link UseFileInputResult} for more details.
*/
export default function useFileInput({
export const useFileInput = ({
directory,
accept,
}: UseFileInputParams) {
}: UseFileInputParams): UseFileInputResult => {
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const inputRef = useRef<HTMLInputElement>();
const openSelectorDialog = useCallback(() => {
const openSelector = useCallback(() => {
if (inputRef.current) {
inputRef.current.value = null;
inputRef.current.click();
}
}, []);
const handleChange: React.ChangeEventHandler<HTMLInputElement> = async (
const handleChange: React.ChangeEventHandler<HTMLInputElement> = (
event,
) => {
if (!!event.target && !!event.target.files) {
setSelectedFiles([...event.target.files]);
}
const files = event.target?.files;
if (files) setSelectedFiles([...files]);
};
// [Note: webkitRelativePath]
@ -78,12 +93,8 @@ export default function useFileInput({
onChange: handleChange,
...(accept ? { accept } : {}),
}),
[],
[directoryOpts, accept],
);
return {
getInputProps,
open: openSelectorDialog,
selectedFiles: selectedFiles,
};
}
return { getInputProps, openSelector, selectedFiles };
};