Custom File download location (#1565)
This commit is contained in:
commit
f5a0393c7c
14 changed files with 514 additions and 332 deletions
|
@ -1,160 +0,0 @@
|
|||
import Notification from 'components/Notification';
|
||||
import { t } from 'i18next';
|
||||
import isElectron from 'is-electron';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { GalleryContext } from 'pages/gallery';
|
||||
import { useContext } from 'react';
|
||||
import ElectronAPIs from '@ente/shared/electron';
|
||||
|
||||
export interface CollectionDownloadProgressAttributes {
|
||||
success: number;
|
||||
failed: number;
|
||||
total: number;
|
||||
collectionName: string;
|
||||
collectionID: number;
|
||||
isHidden: boolean;
|
||||
downloadDirPath: string;
|
||||
canceller: AbortController;
|
||||
}
|
||||
|
||||
interface CollectionDownloadProgressProps {
|
||||
attributesList: CollectionDownloadProgressAttributes[];
|
||||
setAttributesList: (value: CollectionDownloadProgressAttributes[]) => void;
|
||||
}
|
||||
|
||||
export const isCollectionDownloadCompleted = (
|
||||
attributes: CollectionDownloadProgressAttributes
|
||||
) => {
|
||||
return (
|
||||
attributes &&
|
||||
attributes.success + attributes.failed === attributes.total
|
||||
);
|
||||
};
|
||||
|
||||
export const isCollectionDownloadCompletedWithErrors = (
|
||||
attributes: CollectionDownloadProgressAttributes
|
||||
) => {
|
||||
return (
|
||||
attributes &&
|
||||
attributes.failed > 0 &&
|
||||
isCollectionDownloadCompleted(attributes)
|
||||
);
|
||||
};
|
||||
|
||||
export const isCollectionDownloadCancelled = (
|
||||
attributes: CollectionDownloadProgressAttributes
|
||||
) => {
|
||||
return attributes && attributes.canceller?.signal?.aborted;
|
||||
};
|
||||
|
||||
export const CollectionDownloadProgress: React.FC<CollectionDownloadProgressProps> =
|
||||
({ attributesList, setAttributesList }) => {
|
||||
const appContext = useContext(AppContext);
|
||||
const galleryContext = useContext(GalleryContext);
|
||||
|
||||
if (!attributesList) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const onClose = (collectionID: number) => {
|
||||
setAttributesList(
|
||||
attributesList.filter(
|
||||
(attr) => attr.collectionID !== collectionID
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const confirmCancelUpload = (
|
||||
attributes: CollectionDownloadProgressAttributes
|
||||
) => {
|
||||
appContext.setDialogMessage({
|
||||
title: t('STOP_DOWNLOADS_HEADER'),
|
||||
content: t('STOP_ALL_DOWNLOADS_MESSAGE'),
|
||||
proceed: {
|
||||
text: t('YES_STOP_DOWNLOADS'),
|
||||
variant: 'critical',
|
||||
action: () => {
|
||||
attributes?.canceller.abort();
|
||||
onClose(attributes.collectionID);
|
||||
},
|
||||
},
|
||||
close: {
|
||||
text: t('NO'),
|
||||
variant: 'secondary',
|
||||
action: () => {},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleClose =
|
||||
(attributes: CollectionDownloadProgressAttributes) => () => {
|
||||
if (isCollectionDownloadCompleted(attributes)) {
|
||||
onClose(attributes.collectionID);
|
||||
} else {
|
||||
confirmCancelUpload(attributes);
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnClick = (collectionID: number) => () => {
|
||||
const attributes = attributesList.find(
|
||||
(attr) => attr.collectionID === collectionID
|
||||
);
|
||||
if (isElectron()) {
|
||||
ElectronAPIs.openDirectory(attributes.downloadDirPath);
|
||||
} else {
|
||||
if (attributes.isHidden) {
|
||||
galleryContext.openHiddenSection(() => {
|
||||
galleryContext.setActiveCollectionID(
|
||||
attributes.collectionID
|
||||
);
|
||||
});
|
||||
} else {
|
||||
galleryContext.setActiveCollectionID(
|
||||
attributes.collectionID
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{attributesList.map((attributes, index) => (
|
||||
<Notification
|
||||
key={attributes.collectionID}
|
||||
horizontal="left"
|
||||
sx={{ '&&': { bottom: `${index * 80 + 20}px` } }}
|
||||
open
|
||||
onClose={handleClose(attributes)}
|
||||
keepOpenOnClick
|
||||
attributes={{
|
||||
variant: isCollectionDownloadCompletedWithErrors(
|
||||
attributes
|
||||
)
|
||||
? 'critical'
|
||||
: 'secondary',
|
||||
title: isCollectionDownloadCompletedWithErrors(
|
||||
attributes
|
||||
)
|
||||
? t('DOWNLOAD_FAILED')
|
||||
: isCollectionDownloadCompleted(attributes)
|
||||
? t(`DOWNLOAD_COMPLETE`)
|
||||
: t('DOWNLOADING_COLLECTION', {
|
||||
name: attributes.collectionName,
|
||||
}),
|
||||
caption: isCollectionDownloadCompleted(attributes)
|
||||
? attributes.collectionName
|
||||
: t('DOWNLOAD_PROGRESS', {
|
||||
progress: {
|
||||
current:
|
||||
attributes.success +
|
||||
attributes.failed,
|
||||
total: attributes.total,
|
||||
},
|
||||
}),
|
||||
onClick: handleOnClick(attributes.collectionID),
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1,5 +1,4 @@
|
|||
import { CollectionInfo } from './CollectionInfo';
|
||||
import React from 'react';
|
||||
import { Collection, CollectionSummary } from 'types/collection';
|
||||
import CollectionOptions from 'components/Collections/CollectionOptions';
|
||||
import { SetCollectionNamerAttributes } from 'components/Collections/CollectionNamer';
|
||||
|
@ -11,16 +10,14 @@ import Favorite from '@mui/icons-material/FavoriteRounded';
|
|||
import ArchiveOutlined from '@mui/icons-material/ArchiveOutlined';
|
||||
import PeopleIcon from '@mui/icons-material/People';
|
||||
import LinkIcon from '@mui/icons-material/Link';
|
||||
import { SetCollectionDownloadProgressAttributes } from 'types/gallery';
|
||||
import { SetFilesDownloadProgressAttributesCreator } from 'types/gallery';
|
||||
|
||||
interface Iprops {
|
||||
activeCollection: Collection;
|
||||
collectionSummary: CollectionSummary;
|
||||
setCollectionNamerAttributes: SetCollectionNamerAttributes;
|
||||
showCollectionShareModal: () => void;
|
||||
setCollectionDownloadProgressAttributesCreator: (
|
||||
collectionID: number
|
||||
) => SetCollectionDownloadProgressAttributes;
|
||||
setFilesDownloadProgressAttributesCreator: SetFilesDownloadProgressAttributesCreator;
|
||||
isActiveCollectionDownloadInProgress: () => boolean;
|
||||
setActiveCollectionID: (collectionID: number) => void;
|
||||
}
|
||||
|
|
|
@ -33,13 +33,11 @@ import { Trans } from 'react-i18next';
|
|||
import { t } from 'i18next';
|
||||
import { Box } from '@mui/material';
|
||||
import CollectionSortOrderMenu from './CollectionSortOrderMenu';
|
||||
import { SetCollectionDownloadProgressAttributes } from 'types/gallery';
|
||||
import { SetFilesDownloadProgressAttributesCreator } from 'types/gallery';
|
||||
|
||||
interface CollectionOptionsProps {
|
||||
setCollectionNamerAttributes: SetCollectionNamerAttributes;
|
||||
setCollectionDownloadProgressAttributesCreator: (
|
||||
collectionID: number
|
||||
) => SetCollectionDownloadProgressAttributes;
|
||||
setFilesDownloadProgressAttributesCreator: SetFilesDownloadProgressAttributesCreator;
|
||||
isActiveCollectionDownloadInProgress: () => boolean;
|
||||
activeCollection: Collection;
|
||||
collectionSummaryType: CollectionSummaryType;
|
||||
|
@ -76,7 +74,7 @@ const CollectionOptions = (props: CollectionOptionsProps) => {
|
|||
setActiveCollectionID,
|
||||
setCollectionNamerAttributes,
|
||||
showCollectionShareModal,
|
||||
setCollectionDownloadProgressAttributesCreator,
|
||||
setFilesDownloadProgressAttributesCreator,
|
||||
isActiveCollectionDownloadInProgress,
|
||||
} = props;
|
||||
|
||||
|
@ -219,21 +217,25 @@ const CollectionOptions = (props: CollectionOptionsProps) => {
|
|||
return;
|
||||
}
|
||||
if (collectionSummaryType === CollectionSummaryType.hiddenItems) {
|
||||
const setCollectionDownloadProgressAttributes =
|
||||
setCollectionDownloadProgressAttributesCreator(
|
||||
HIDDEN_ITEMS_SECTION
|
||||
const setFilesDownloadProgressAttributes =
|
||||
setFilesDownloadProgressAttributesCreator(
|
||||
activeCollection.name,
|
||||
HIDDEN_ITEMS_SECTION,
|
||||
true
|
||||
);
|
||||
downloadDefaultHiddenCollectionHelper(
|
||||
setCollectionDownloadProgressAttributes
|
||||
setFilesDownloadProgressAttributes
|
||||
);
|
||||
} else {
|
||||
const setCollectionDownloadProgressAttributes =
|
||||
setCollectionDownloadProgressAttributesCreator(
|
||||
activeCollection.id
|
||||
const setFilesDownloadProgressAttributes =
|
||||
setFilesDownloadProgressAttributesCreator(
|
||||
activeCollection.name,
|
||||
activeCollection.id,
|
||||
isHiddenCollection(activeCollection)
|
||||
);
|
||||
downloadCollectionHelper(
|
||||
activeCollection.id,
|
||||
setCollectionDownloadProgressAttributes
|
||||
setFilesDownloadProgressAttributes
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -16,12 +16,11 @@ import { useLocalState } from '@ente/shared/hooks/useLocalState';
|
|||
import { sortCollectionSummaries } from 'services/collectionService';
|
||||
import { LS_KEYS } from '@ente/shared/storage/localStorage';
|
||||
import {
|
||||
CollectionDownloadProgress,
|
||||
CollectionDownloadProgressAttributes,
|
||||
isCollectionDownloadCancelled,
|
||||
isCollectionDownloadCompleted,
|
||||
} from './CollectionDownloadProgress';
|
||||
import { SetCollectionDownloadProgressAttributes } from 'types/gallery';
|
||||
FilesDownloadProgressAttributes,
|
||||
isFilesDownloadCancelled,
|
||||
isFilesDownloadCompleted,
|
||||
} from '../FilesDownloadProgress';
|
||||
import { SetFilesDownloadProgressAttributesCreator } from 'types/gallery';
|
||||
|
||||
interface Iprops {
|
||||
activeCollection: Collection;
|
||||
|
@ -33,6 +32,8 @@ interface Iprops {
|
|||
hiddenCollectionSummaries: CollectionSummaries;
|
||||
setCollectionNamerAttributes: SetCollectionNamerAttributes;
|
||||
setPhotoListHeader: (value: TimeStampListItem) => void;
|
||||
filesDownloadProgressAttributesList: FilesDownloadProgressAttributes[];
|
||||
setFilesDownloadProgressAttributesCreator: SetFilesDownloadProgressAttributesCreator;
|
||||
}
|
||||
|
||||
export default function Collections(props: Iprops) {
|
||||
|
@ -46,17 +47,14 @@ export default function Collections(props: Iprops) {
|
|||
hiddenCollectionSummaries,
|
||||
setCollectionNamerAttributes,
|
||||
setPhotoListHeader,
|
||||
filesDownloadProgressAttributesList,
|
||||
setFilesDownloadProgressAttributesCreator,
|
||||
} = props;
|
||||
|
||||
const [allCollectionView, setAllCollectionView] = useState(false);
|
||||
const [collectionShareModalView, setCollectionShareModalView] =
|
||||
useState(false);
|
||||
|
||||
const [
|
||||
collectionDownloadProgressAttributesList,
|
||||
setCollectionDownloadProgressAttributesList,
|
||||
] = useState<CollectionDownloadProgressAttributes[]>([]);
|
||||
|
||||
const [collectionListSortBy, setCollectionListSortBy] =
|
||||
useLocalState<COLLECTION_LIST_SORT_BY>(
|
||||
LS_KEYS.COLLECTION_SORT_BY,
|
||||
|
@ -86,38 +84,16 @@ export default function Collections(props: Iprops) {
|
|||
[collectionListSortBy, toShowCollectionSummaries]
|
||||
);
|
||||
|
||||
const setCollectionDownloadProgressAttributesCreator =
|
||||
(collectionID: number): SetCollectionDownloadProgressAttributes =>
|
||||
(value) => {
|
||||
setCollectionDownloadProgressAttributesList((prev) => {
|
||||
const attributes = prev?.find(
|
||||
(attr) => attr.collectionID === collectionID
|
||||
);
|
||||
const updatedAttributes =
|
||||
typeof value === 'function' ? value(attributes) : value;
|
||||
|
||||
const updatedAttributesList = attributes
|
||||
? prev.map((attr) =>
|
||||
attr.collectionID === collectionID
|
||||
? updatedAttributes
|
||||
: attr
|
||||
)
|
||||
: [...prev, updatedAttributes];
|
||||
|
||||
return updatedAttributesList;
|
||||
});
|
||||
};
|
||||
|
||||
const isActiveCollectionDownloadInProgress = useCallback(() => {
|
||||
const attributes = collectionDownloadProgressAttributesList.find(
|
||||
const attributes = filesDownloadProgressAttributesList.find(
|
||||
(attr) => attr.collectionID === activeCollectionID
|
||||
);
|
||||
return (
|
||||
attributes &&
|
||||
!isCollectionDownloadCancelled(attributes) &&
|
||||
!isCollectionDownloadCompleted(attributes)
|
||||
!isFilesDownloadCancelled(attributes) &&
|
||||
!isFilesDownloadCompleted(attributes)
|
||||
);
|
||||
}, [activeCollectionID, collectionDownloadProgressAttributesList]);
|
||||
}, [activeCollectionID, filesDownloadProgressAttributesList]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInSearchMode) {
|
||||
|
@ -134,8 +110,8 @@ export default function Collections(props: Iprops) {
|
|||
showCollectionShareModal={() =>
|
||||
setCollectionShareModalView(true)
|
||||
}
|
||||
setCollectionDownloadProgressAttributesCreator={
|
||||
setCollectionDownloadProgressAttributesCreator
|
||||
setFilesDownloadProgressAttributesCreator={
|
||||
setFilesDownloadProgressAttributesCreator
|
||||
}
|
||||
isActiveCollectionDownloadInProgress={
|
||||
isActiveCollectionDownloadInProgress
|
||||
|
@ -195,10 +171,6 @@ export default function Collections(props: Iprops) {
|
|||
onClose={closeCollectionShare}
|
||||
collection={activeCollection}
|
||||
/>
|
||||
<CollectionDownloadProgress
|
||||
attributesList={collectionDownloadProgressAttributesList}
|
||||
setAttributesList={setCollectionDownloadProgressAttributesList}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
159
apps/photos/src/components/FilesDownloadProgress.tsx
Normal file
159
apps/photos/src/components/FilesDownloadProgress.tsx
Normal file
|
@ -0,0 +1,159 @@
|
|||
import Notification from 'components/Notification';
|
||||
import { t } from 'i18next';
|
||||
import isElectron from 'is-electron';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { GalleryContext } from 'pages/gallery';
|
||||
import { useContext } from 'react';
|
||||
import ElectronAPIs from '@ente/shared/electron';
|
||||
|
||||
export interface FilesDownloadProgressAttributes {
|
||||
id: number;
|
||||
success: number;
|
||||
failed: number;
|
||||
total: number;
|
||||
folderName: string;
|
||||
collectionID: number;
|
||||
isHidden: boolean;
|
||||
downloadDirPath: string;
|
||||
canceller: AbortController;
|
||||
}
|
||||
|
||||
interface FilesDownloadProgressProps {
|
||||
attributesList: FilesDownloadProgressAttributes[];
|
||||
setAttributesList: (value: FilesDownloadProgressAttributes[]) => void;
|
||||
}
|
||||
|
||||
export const isFilesDownloadStarted = (
|
||||
attributes: FilesDownloadProgressAttributes
|
||||
) => {
|
||||
return attributes && attributes.total > 0;
|
||||
};
|
||||
|
||||
export const isFilesDownloadCompleted = (
|
||||
attributes: FilesDownloadProgressAttributes
|
||||
) => {
|
||||
return (
|
||||
attributes &&
|
||||
attributes.success + attributes.failed === attributes.total
|
||||
);
|
||||
};
|
||||
|
||||
export const isFilesDownloadCompletedWithErrors = (
|
||||
attributes: FilesDownloadProgressAttributes
|
||||
) => {
|
||||
return (
|
||||
attributes &&
|
||||
attributes.failed > 0 &&
|
||||
isFilesDownloadCompleted(attributes)
|
||||
);
|
||||
};
|
||||
|
||||
export const isFilesDownloadCancelled = (
|
||||
attributes: FilesDownloadProgressAttributes
|
||||
) => {
|
||||
return attributes && attributes.canceller?.signal?.aborted;
|
||||
};
|
||||
|
||||
export const FilesDownloadProgress: React.FC<FilesDownloadProgressProps> = ({
|
||||
attributesList,
|
||||
setAttributesList,
|
||||
}) => {
|
||||
const appContext = useContext(AppContext);
|
||||
const galleryContext = useContext(GalleryContext);
|
||||
|
||||
if (!attributesList) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const onClose = (id: number) => {
|
||||
setAttributesList(attributesList.filter((attr) => attr.id !== id));
|
||||
};
|
||||
|
||||
const confirmCancelUpload = (
|
||||
attributes: FilesDownloadProgressAttributes
|
||||
) => {
|
||||
appContext.setDialogMessage({
|
||||
title: t('STOP_DOWNLOADS_HEADER'),
|
||||
content: t('STOP_ALL_DOWNLOADS_MESSAGE'),
|
||||
proceed: {
|
||||
text: t('YES_STOP_DOWNLOADS'),
|
||||
variant: 'critical',
|
||||
action: () => {
|
||||
attributes?.canceller.abort();
|
||||
onClose(attributes.id);
|
||||
},
|
||||
},
|
||||
close: {
|
||||
text: t('NO'),
|
||||
variant: 'secondary',
|
||||
action: () => {},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleClose = (attributes: FilesDownloadProgressAttributes) => () => {
|
||||
if (isFilesDownloadCompleted(attributes)) {
|
||||
onClose(attributes.id);
|
||||
} else {
|
||||
confirmCancelUpload(attributes);
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnClick = (id: number) => () => {
|
||||
const attributes = attributesList.find((attr) => attr.id === id);
|
||||
if (isElectron()) {
|
||||
ElectronAPIs.openDirectory(attributes.downloadDirPath);
|
||||
} else {
|
||||
if (attributes.isHidden) {
|
||||
galleryContext.openHiddenSection(() => {
|
||||
galleryContext.setActiveCollectionID(
|
||||
attributes.collectionID
|
||||
);
|
||||
});
|
||||
} else {
|
||||
galleryContext.setActiveCollectionID(attributes.collectionID);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{attributesList.map((attributes, index) => (
|
||||
<Notification
|
||||
key={attributes.id}
|
||||
horizontal="left"
|
||||
sx={{
|
||||
'&&': { bottom: `${index * 80 + 20}px` },
|
||||
zIndex: 1600,
|
||||
}}
|
||||
open={isFilesDownloadStarted(attributes)}
|
||||
onClose={handleClose(attributes)}
|
||||
keepOpenOnClick
|
||||
attributes={{
|
||||
variant: isFilesDownloadCompletedWithErrors(attributes)
|
||||
? 'critical'
|
||||
: 'secondary',
|
||||
title: isFilesDownloadCompletedWithErrors(attributes)
|
||||
? t('DOWNLOAD_FAILED')
|
||||
: isFilesDownloadCompleted(attributes)
|
||||
? t(`DOWNLOAD_COMPLETE`)
|
||||
: t('DOWNLOADING_COLLECTION', {
|
||||
name: attributes.folderName,
|
||||
}),
|
||||
caption: isFilesDownloadCompleted(attributes)
|
||||
? attributes.folderName
|
||||
: t('DOWNLOAD_PROGRESS', {
|
||||
progress: {
|
||||
current:
|
||||
attributes.success +
|
||||
attributes.failed,
|
||||
total: attributes.total,
|
||||
},
|
||||
}),
|
||||
onClick: handleOnClick(attributes.id),
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -11,7 +11,10 @@ import AutoSizer from 'react-virtualized-auto-sizer';
|
|||
import PhotoViewer from 'components/PhotoViewer';
|
||||
import { TRASH_SECTION } from 'constants/collection';
|
||||
import { updateFileMsrcProps, updateFileSrcProps } from 'utils/photoFrame';
|
||||
import { SelectedState } from 'types/gallery';
|
||||
import {
|
||||
SelectedState,
|
||||
SetFilesDownloadProgressAttributesCreator,
|
||||
} from 'types/gallery';
|
||||
import { useRouter } from 'next/router';
|
||||
import { logError } from '@ente/shared/sentry';
|
||||
import { addLogLine } from '@ente/shared/logging';
|
||||
|
@ -61,6 +64,7 @@ interface Props {
|
|||
showAppDownloadBanner?: boolean;
|
||||
setIsPhotoSwipeOpen?: (value: boolean) => void;
|
||||
isInHiddenSection?: boolean;
|
||||
setFilesDownloadProgressAttributesCreator?: SetFilesDownloadProgressAttributesCreator;
|
||||
}
|
||||
|
||||
const PhotoFrame = ({
|
||||
|
@ -80,6 +84,7 @@ const PhotoFrame = ({
|
|||
showAppDownloadBanner,
|
||||
setIsPhotoSwipeOpen,
|
||||
isInHiddenSection,
|
||||
setFilesDownloadProgressAttributesCreator,
|
||||
}: Props) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [currentIndex, setCurrentIndex] = useState<number>(0);
|
||||
|
@ -601,6 +606,9 @@ const PhotoFrame = ({
|
|||
enableDownload={enableDownload}
|
||||
fileToCollectionsMap={fileToCollectionsMap}
|
||||
collectionNameMap={collectionNameMap}
|
||||
setFilesDownloadProgressAttributesCreator={
|
||||
setFilesDownloadProgressAttributesCreator
|
||||
}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
|
|
|
@ -8,12 +8,12 @@ import {
|
|||
} from 'services/collectionService';
|
||||
import { EnteFile } from 'types/file';
|
||||
import {
|
||||
downloadFile,
|
||||
copyFileToClipboard,
|
||||
getFileExtension,
|
||||
getFileFromURL,
|
||||
isSupportedRawFormat,
|
||||
isRawFile,
|
||||
downloadSingleFile,
|
||||
} from 'utils/file';
|
||||
import { logError } from '@ente/shared/sentry';
|
||||
|
||||
|
@ -58,6 +58,7 @@ import isElectron from 'is-electron';
|
|||
import ReplayIcon from '@mui/icons-material/Replay';
|
||||
import ImageEditorOverlay from './ImageEditorOverlay';
|
||||
import EditIcon from '@mui/icons-material/Edit';
|
||||
import { SetFilesDownloadProgressAttributesCreator } from 'types/gallery';
|
||||
|
||||
interface PhotoswipeFullscreenAPI {
|
||||
enter: () => void;
|
||||
|
@ -92,6 +93,7 @@ interface Iprops {
|
|||
enableDownload: boolean;
|
||||
fileToCollectionsMap: Map<number, number[]>;
|
||||
collectionNameMap: Map<number, string>;
|
||||
setFilesDownloadProgressAttributesCreator: SetFilesDownloadProgressAttributesCreator;
|
||||
}
|
||||
|
||||
function PhotoViewer(props: Iprops) {
|
||||
|
@ -265,7 +267,7 @@ function PhotoViewer(props: Iprops) {
|
|||
`download-btn-${item.id}`
|
||||
) as HTMLButtonElement;
|
||||
const downloadFile = () => {
|
||||
downloadFileHelper(photoSwipe.currItem);
|
||||
downloadFileHelper(photoSwipe.currItem as unknown as EnteFile);
|
||||
};
|
||||
|
||||
if (downloadLivePhotoBtn) {
|
||||
|
@ -624,15 +626,21 @@ function PhotoViewer(props: Iprops) {
|
|||
setShowImageEditorOverlay(false);
|
||||
};
|
||||
|
||||
const downloadFileHelper = async (file) => {
|
||||
if (file && props.enableDownload) {
|
||||
appContext.startLoading();
|
||||
const downloadFileHelper = async (file: EnteFile) => {
|
||||
if (
|
||||
file &&
|
||||
props.enableDownload &&
|
||||
props.setFilesDownloadProgressAttributesCreator
|
||||
) {
|
||||
try {
|
||||
await downloadFile(file);
|
||||
const setSingleFileDownloadProgress =
|
||||
props.setFilesDownloadProgressAttributesCreator(
|
||||
file.metadata.title
|
||||
);
|
||||
await downloadSingleFile(file, setSingleFileDownloadProgress);
|
||||
} catch (e) {
|
||||
// do nothing
|
||||
}
|
||||
appContext.finishLoading();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -727,7 +735,9 @@ function PhotoViewer(props: Iprops) {
|
|||
onClose={() =>
|
||||
setConversionFailedNotificationOpen(false)
|
||||
}
|
||||
onClick={() => downloadFileHelper(photoSwipe.currItem)}
|
||||
onClick={() =>
|
||||
downloadFileHelper(photoSwipe.currItem as EnteFile)
|
||||
}
|
||||
/>
|
||||
|
||||
<Box
|
||||
|
@ -771,7 +781,9 @@ function PhotoViewer(props: Iprops) {
|
|||
className="pswp__button pswp__button--custom"
|
||||
title={t('DOWNLOAD_OPTION')}
|
||||
onClick={() =>
|
||||
downloadFileHelper(photoSwipe.currItem)
|
||||
downloadFileHelper(
|
||||
photoSwipe.currItem as EnteFile
|
||||
)
|
||||
}>
|
||||
<DownloadIcon />
|
||||
</button>
|
||||
|
|
|
@ -218,6 +218,12 @@ export default function PreviewCard(props: IProps) {
|
|||
const galleryContext = useContext(GalleryContext);
|
||||
const deduplicateContext = useContext(DeduplicateContext);
|
||||
|
||||
const longPressCallback = () => {
|
||||
onSelect(!selected);
|
||||
};
|
||||
|
||||
const longPress = useLongPress(longPressCallback, 500);
|
||||
|
||||
const {
|
||||
file,
|
||||
onClick,
|
||||
|
@ -289,9 +295,6 @@ export default function PreviewCard(props: IProps) {
|
|||
}
|
||||
};
|
||||
|
||||
const longPressCallback = () => {
|
||||
onSelect(!selected);
|
||||
};
|
||||
const handleHover = () => {
|
||||
if (isRangeSelectActive) {
|
||||
onHover();
|
||||
|
@ -304,7 +307,7 @@ export default function PreviewCard(props: IProps) {
|
|||
onClick={handleClick}
|
||||
onMouseEnter={handleHover}
|
||||
disabled={!file?.msrc && !imgSrc}
|
||||
{...(selectable ? useLongPress(longPressCallback, 500) : {})}>
|
||||
{...(selectable ? longPress : {})}>
|
||||
{selectable && (
|
||||
<Check
|
||||
type="checkbox"
|
||||
|
|
|
@ -10,7 +10,6 @@ import { formatNumber } from 'utils/number/format';
|
|||
|
||||
interface Props {
|
||||
count: number;
|
||||
ownCount: number;
|
||||
clearSelection: () => void;
|
||||
downloadFilesHelper: () => void;
|
||||
}
|
||||
|
@ -18,7 +17,6 @@ interface Props {
|
|||
const SelectedFileOptions = ({
|
||||
downloadFilesHelper,
|
||||
count,
|
||||
ownCount,
|
||||
clearSelection,
|
||||
}: Props) => {
|
||||
const { isMobile } = useContext(AppContext);
|
||||
|
@ -31,8 +29,6 @@ const SelectedFileOptions = ({
|
|||
</IconButton>
|
||||
<Box ml={1.5}>
|
||||
{formatNumber(count)} {t('SELECTED')}{' '}
|
||||
{ownCount !== count &&
|
||||
`(${formatNumber(ownCount)} ${t('YOURS')})`}
|
||||
</Box>
|
||||
</FluidContainer>
|
||||
<Stack spacing={2} direction="row" mr={2}>
|
||||
|
|
|
@ -97,6 +97,8 @@ import { EnteFile } from 'types/file';
|
|||
import {
|
||||
GalleryContextType,
|
||||
SelectedState,
|
||||
SetFilesDownloadProgressAttributes,
|
||||
SetFilesDownloadProgressAttributesCreator,
|
||||
UploadTypeSelectorIntent,
|
||||
} from 'types/gallery';
|
||||
import Collections from 'components/Collections';
|
||||
|
@ -130,6 +132,10 @@ import { ClipService } from 'services/clipService';
|
|||
import isElectron from 'is-electron';
|
||||
import downloadManager from 'services/download';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import {
|
||||
FilesDownloadProgress,
|
||||
FilesDownloadProgressAttributes,
|
||||
} from 'components/FilesDownloadProgress';
|
||||
import locationSearchService from 'services/locationSearchService';
|
||||
import ComlinkSearchWorker from 'utils/comlink/ComlinkSearchWorker';
|
||||
import useEffectSingleThreaded from '@ente/shared/hooks/useEffectSingleThreaded';
|
||||
|
@ -286,6 +292,11 @@ export default function Gallery() {
|
|||
|
||||
const [isInHiddenSection, setIsInHiddenSection] = useState(false);
|
||||
|
||||
const [
|
||||
filesDownloadProgressAttributesList,
|
||||
setFilesDownloadProgressAttributesList,
|
||||
] = useState<FilesDownloadProgressAttributes[]>([]);
|
||||
|
||||
const openHiddenSection: GalleryContextType['openHiddenSection'] = (
|
||||
callback
|
||||
) => {
|
||||
|
@ -793,6 +804,40 @@ export default function Gallery() {
|
|||
return <div />;
|
||||
}
|
||||
|
||||
const setFilesDownloadProgressAttributesCreator: SetFilesDownloadProgressAttributesCreator =
|
||||
(folderName, collectionID, isHidden) => {
|
||||
const id = filesDownloadProgressAttributesList?.length ?? 0;
|
||||
const updater: SetFilesDownloadProgressAttributes = (value) => {
|
||||
setFilesDownloadProgressAttributesList((prev) => {
|
||||
const attributes = prev?.find((attr) => attr.id === id);
|
||||
const updatedAttributes =
|
||||
typeof value === 'function'
|
||||
? value(attributes)
|
||||
: { ...attributes, ...value };
|
||||
console.log('value', attributes, updatedAttributes);
|
||||
const updatedAttributesList = attributes
|
||||
? prev.map((attr) =>
|
||||
attr.id === id ? updatedAttributes : attr
|
||||
)
|
||||
: [...prev, updatedAttributes];
|
||||
|
||||
return updatedAttributesList;
|
||||
});
|
||||
};
|
||||
updater({
|
||||
id,
|
||||
folderName,
|
||||
collectionID,
|
||||
isHidden,
|
||||
canceller: null,
|
||||
total: 0,
|
||||
success: 0,
|
||||
failed: 0,
|
||||
downloadDirPath: null,
|
||||
});
|
||||
return updater;
|
||||
};
|
||||
|
||||
const collectionOpsHelper =
|
||||
(ops: COLLECTION_OPS_TYPE) => async (collection: Collection) => {
|
||||
startLoading();
|
||||
|
@ -858,7 +903,8 @@ export default function Gallery() {
|
|||
toProcessFiles,
|
||||
setTempDeletedFileIds,
|
||||
setTempHiddenFileIds,
|
||||
setFixCreationTimeAttributes
|
||||
setFixCreationTimeAttributes,
|
||||
setFilesDownloadProgressAttributesCreator
|
||||
);
|
||||
}
|
||||
if (
|
||||
|
@ -1005,6 +1051,10 @@ export default function Gallery() {
|
|||
attributes={collectionSelectorAttributes}
|
||||
collections={collections}
|
||||
/>
|
||||
<FilesDownloadProgress
|
||||
attributesList={filesDownloadProgressAttributesList}
|
||||
setAttributesList={setFilesDownloadProgressAttributesList}
|
||||
/>
|
||||
<FixCreationTime
|
||||
isOpen={fixCreationTimeView}
|
||||
hide={() => setFixCreationTimeView(false)}
|
||||
|
@ -1034,6 +1084,12 @@ export default function Gallery() {
|
|||
hiddenCollectionSummaries={hiddenCollectionSummaries}
|
||||
setCollectionNamerAttributes={setCollectionNamerAttributes}
|
||||
setPhotoListHeader={setPhotoListHeader}
|
||||
setFilesDownloadProgressAttributesCreator={
|
||||
setFilesDownloadProgressAttributesCreator
|
||||
}
|
||||
filesDownloadProgressAttributesList={
|
||||
filesDownloadProgressAttributesList
|
||||
}
|
||||
/>
|
||||
|
||||
<Uploader
|
||||
|
@ -1101,6 +1157,9 @@ export default function Gallery() {
|
|||
files.length < 30 && !isInSearchMode
|
||||
}
|
||||
isInHiddenSection={isInHiddenSection}
|
||||
setFilesDownloadProgressAttributesCreator={
|
||||
setFilesDownloadProgressAttributesCreator
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{selected.count > 0 &&
|
||||
|
|
|
@ -17,8 +17,7 @@ import {
|
|||
import { Collection } from 'types/collection';
|
||||
import { EnteFile } from 'types/file';
|
||||
import {
|
||||
downloadFile,
|
||||
downloadFiles,
|
||||
downloadSelectedFiles,
|
||||
getSelectedFiles,
|
||||
mergeMetadata,
|
||||
sortFiles,
|
||||
|
@ -58,7 +57,12 @@ import UploadButton from 'components/Upload/UploadButton';
|
|||
import bs58 from 'bs58';
|
||||
import AddPhotoAlternateOutlined from '@mui/icons-material/AddPhotoAlternateOutlined';
|
||||
import ComlinkCryptoWorker from '@ente/shared/crypto';
|
||||
import { SelectedState, UploadTypeSelectorIntent } from 'types/gallery';
|
||||
import {
|
||||
SelectedState,
|
||||
SetFilesDownloadProgressAttributes,
|
||||
SetFilesDownloadProgressAttributesCreator,
|
||||
UploadTypeSelectorIntent,
|
||||
} from 'types/gallery';
|
||||
import FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined';
|
||||
import MoreHoriz from '@mui/icons-material/MoreHoriz';
|
||||
import OverflowMenu from '@ente/shared/components/OverflowMenu/menu';
|
||||
|
@ -66,6 +70,11 @@ import { OverflowMenuOption } from '@ente/shared/components/OverflowMenu/option'
|
|||
import { ENTE_WEBSITE_LINK } from '@ente/shared/constants/urls';
|
||||
import { APPS } from '@ente/shared/apps/constants';
|
||||
import downloadManager from 'services/download';
|
||||
import {
|
||||
FilesDownloadProgress,
|
||||
FilesDownloadProgressAttributes,
|
||||
} from 'components/FilesDownloadProgress';
|
||||
import { downloadCollectionFiles, isHiddenCollection } from 'utils/collection';
|
||||
import SelectedFileOptions from 'components/pages/sharedAlbum/SelectedFileOptions';
|
||||
|
||||
export default function PublicCollectionGallery() {
|
||||
|
@ -93,7 +102,6 @@ export default function PublicCollectionGallery() {
|
|||
const [uploadTypeSelectorView, setUploadTypeSelectorView] = useState(false);
|
||||
const [blockingLoad, setBlockingLoad] = useState(false);
|
||||
const [shouldDisableDropzone, setShouldDisableDropzone] = useState(false);
|
||||
|
||||
const [selected, setSelected] = useState<SelectedState>({
|
||||
ownCount: 0,
|
||||
count: 0,
|
||||
|
@ -124,6 +132,45 @@ export default function PublicCollectionGallery() {
|
|||
directory: true,
|
||||
});
|
||||
|
||||
const [
|
||||
filesDownloadProgressAttributesList,
|
||||
setFilesDownloadProgressAttributesList,
|
||||
] = useState<FilesDownloadProgressAttributes[]>([]);
|
||||
|
||||
const setFilesDownloadProgressAttributesCreator: SetFilesDownloadProgressAttributesCreator =
|
||||
(folderName, collectionID, isHidden) => {
|
||||
const id = filesDownloadProgressAttributesList?.length ?? 0;
|
||||
const updater: SetFilesDownloadProgressAttributes = (value) => {
|
||||
setFilesDownloadProgressAttributesList((prev) => {
|
||||
const attributes = prev?.find((attr) => attr.id === id);
|
||||
const updatedAttributes =
|
||||
typeof value === 'function'
|
||||
? value(attributes)
|
||||
: { ...attributes, ...value };
|
||||
console.log('value', attributes, updatedAttributes);
|
||||
const updatedAttributesList = attributes
|
||||
? prev.map((attr) =>
|
||||
attr.id === id ? updatedAttributes : attr
|
||||
)
|
||||
: [...prev, updatedAttributes];
|
||||
|
||||
return updatedAttributesList;
|
||||
});
|
||||
};
|
||||
updater({
|
||||
id,
|
||||
folderName,
|
||||
collectionID,
|
||||
isHidden,
|
||||
canceller: null,
|
||||
total: 0,
|
||||
success: 0,
|
||||
failed: 0,
|
||||
downloadDirPath: null,
|
||||
});
|
||||
return updater;
|
||||
};
|
||||
|
||||
const openUploader = () => {
|
||||
setUploadTypeSelectorView(true);
|
||||
};
|
||||
|
@ -230,18 +277,24 @@ export default function PublicCollectionGallery() {
|
|||
);
|
||||
|
||||
const downloadAllFiles = async () => {
|
||||
if (!downloadEnabled) {
|
||||
return;
|
||||
}
|
||||
appContext.startLoading();
|
||||
for (const file of publicFiles) {
|
||||
try {
|
||||
await downloadFile(file);
|
||||
} catch (e) {
|
||||
// do nothing
|
||||
try {
|
||||
if (!downloadEnabled) {
|
||||
return;
|
||||
}
|
||||
const setFilesDownloadProgressAttributes =
|
||||
setFilesDownloadProgressAttributesCreator(
|
||||
publicCollection.name,
|
||||
publicCollection.id,
|
||||
isHiddenCollection(publicCollection)
|
||||
);
|
||||
await downloadCollectionFiles(
|
||||
publicCollection.name,
|
||||
publicFiles,
|
||||
setFilesDownloadProgressAttributes
|
||||
);
|
||||
} catch (e) {
|
||||
logError(e, 'failed to downloads shared album all files');
|
||||
}
|
||||
appContext.finishLoading();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -454,15 +507,6 @@ export default function PublicCollectionGallery() {
|
|||
}
|
||||
}
|
||||
|
||||
const downloadFilesHelper = async () => {
|
||||
try {
|
||||
const selectedFiles = getSelectedFiles(selected, publicFiles);
|
||||
await downloadFiles(selectedFiles);
|
||||
} catch (e) {
|
||||
logError(e, 'failed to download selected files');
|
||||
}
|
||||
};
|
||||
|
||||
const clearSelection = () => {
|
||||
if (!selected?.count) {
|
||||
return;
|
||||
|
@ -470,6 +514,23 @@ export default function PublicCollectionGallery() {
|
|||
setSelected({ ownCount: 0, count: 0, collectionID: 0 });
|
||||
};
|
||||
|
||||
const downloadFilesHelper = async () => {
|
||||
try {
|
||||
const selectedFiles = getSelectedFiles(selected, publicFiles);
|
||||
const setFilesDownloadProgressAttributes =
|
||||
setFilesDownloadProgressAttributesCreator(
|
||||
`${selectedFiles.length} ${t('FILES')}`
|
||||
);
|
||||
await downloadSelectedFiles(
|
||||
selectedFiles,
|
||||
setFilesDownloadProgressAttributes
|
||||
);
|
||||
clearSelection();
|
||||
} catch (e) {
|
||||
logError(e, 'failed to download selected files');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<PublicCollectionGalleryContext.Provider
|
||||
value={{
|
||||
|
@ -503,6 +564,9 @@ export default function PublicCollectionGallery() {
|
|||
enableDownload={downloadEnabled}
|
||||
fileToCollectionsMap={null}
|
||||
collectionNameMap={null}
|
||||
setFilesDownloadProgressAttributesCreator={
|
||||
setFilesDownloadProgressAttributesCreator
|
||||
}
|
||||
/>
|
||||
{blockingLoad && (
|
||||
<LoadingOverlay>
|
||||
|
@ -527,12 +591,15 @@ export default function PublicCollectionGallery() {
|
|||
UploadTypeSelectorIntent.collectPhotos
|
||||
}
|
||||
/>
|
||||
<FilesDownloadProgress
|
||||
attributesList={filesDownloadProgressAttributesList}
|
||||
setAttributesList={setFilesDownloadProgressAttributesList}
|
||||
/>
|
||||
{selected.count > 0 && (
|
||||
<SelectedFileOptions
|
||||
downloadFilesHelper={downloadFilesHelper}
|
||||
clearSelection={clearSelection}
|
||||
count={selected.count}
|
||||
ownCount={selected.ownCount}
|
||||
/>
|
||||
)}
|
||||
</FullScreenDropZone>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { CollectionDownloadProgressAttributes } from 'components/Collections/CollectionDownloadProgress';
|
||||
import { FilesDownloadProgressAttributes } from 'components/FilesDownloadProgress';
|
||||
import { CollectionSelectorAttributes } from 'components/Collections/CollectionSelector';
|
||||
import { TimeStampListItem } from 'components/PhotoList';
|
||||
import { Collection } from 'types/collection';
|
||||
|
@ -17,9 +17,19 @@ export type SetLoading = React.Dispatch<React.SetStateAction<boolean>>;
|
|||
export type SetCollectionSelectorAttributes = React.Dispatch<
|
||||
React.SetStateAction<CollectionSelectorAttributes>
|
||||
>;
|
||||
export type SetCollectionDownloadProgressAttributes = React.Dispatch<
|
||||
React.SetStateAction<CollectionDownloadProgressAttributes>
|
||||
>;
|
||||
export type SetFilesDownloadProgressAttributes = (
|
||||
value:
|
||||
| Partial<FilesDownloadProgressAttributes>
|
||||
| ((
|
||||
prev: FilesDownloadProgressAttributes
|
||||
) => FilesDownloadProgressAttributes)
|
||||
) => void;
|
||||
|
||||
export type SetFilesDownloadProgressAttributesCreator = (
|
||||
folderName: string,
|
||||
collectionID?: number,
|
||||
isHidden?: boolean
|
||||
) => SetFilesDownloadProgressAttributes;
|
||||
|
||||
export type MergedSourceURL = {
|
||||
original: string;
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
updatePublicCollectionMagicMetadata,
|
||||
updateSharedCollectionMagicMetadata,
|
||||
} from 'services/collectionService';
|
||||
import { downloadFiles, downloadFilesDesktop } from 'utils/file';
|
||||
import { downloadFilesWithProgress } from 'utils/file';
|
||||
import { getAllLocalFiles, getLocalFiles } from 'services/fileService';
|
||||
import { EnteFile } from 'types/file';
|
||||
import { CustomError } from '@ente/shared/error';
|
||||
|
@ -34,7 +34,6 @@ import {
|
|||
SYSTEM_COLLECTION_TYPES,
|
||||
MOVE_TO_NOT_ALLOWED_COLLECTION,
|
||||
ADD_TO_NOT_ALLOWED_COLLECTION,
|
||||
HIDDEN_ITEMS_SECTION,
|
||||
DEFAULT_HIDDEN_COLLECTION_USER_FACING_NAME,
|
||||
} from 'constants/collection';
|
||||
import { getUnixTimeInMicroSecondsWithDelta } from '@ente/shared/time';
|
||||
|
@ -44,14 +43,13 @@ import { getAlbumsURL } from '@ente/shared/network/api';
|
|||
import bs58 from 'bs58';
|
||||
import { t } from 'i18next';
|
||||
import isElectron from 'is-electron';
|
||||
import { SetCollectionDownloadProgressAttributes } from 'types/gallery';
|
||||
import { SetFilesDownloadProgressAttributes } from 'types/gallery';
|
||||
import ElectronAPIs from '@ente/shared/electron';
|
||||
import {
|
||||
getCollectionExportPath,
|
||||
getUniqueCollectionExportName,
|
||||
} from 'utils/export';
|
||||
import exportService from 'services/export';
|
||||
import { CollectionDownloadProgressAttributes } from 'components/Collections/CollectionDownloadProgress';
|
||||
import { addLogLine } from '@ente/shared/logging';
|
||||
|
||||
export enum COLLECTION_OPS_TYPE {
|
||||
|
@ -101,7 +99,7 @@ export function getSelectedCollection(
|
|||
|
||||
export async function downloadCollectionHelper(
|
||||
collectionID: number,
|
||||
setCollectionDownloadProgressAttributes: SetCollectionDownloadProgressAttributes
|
||||
setFilesDownloadProgressAttributes: SetFilesDownloadProgressAttributes
|
||||
) {
|
||||
try {
|
||||
const allFiles = await getAllLocalFiles();
|
||||
|
@ -117,10 +115,8 @@ export async function downloadCollectionHelper(
|
|||
}
|
||||
await downloadCollectionFiles(
|
||||
collection.name,
|
||||
collection.id,
|
||||
isHiddenCollection(collection),
|
||||
collectionFiles,
|
||||
setCollectionDownloadProgressAttributes
|
||||
setFilesDownloadProgressAttributes
|
||||
);
|
||||
} catch (e) {
|
||||
logError(e, 'download collection failed ');
|
||||
|
@ -128,7 +124,7 @@ export async function downloadCollectionHelper(
|
|||
}
|
||||
|
||||
export async function downloadDefaultHiddenCollectionHelper(
|
||||
setCollectionDownloadProgressAttributes: SetCollectionDownloadProgressAttributes
|
||||
setFilesDownloadProgressAttributes: SetFilesDownloadProgressAttributes
|
||||
) {
|
||||
try {
|
||||
const hiddenCollections = await getLocalCollections('hidden');
|
||||
|
@ -140,78 +136,38 @@ export async function downloadDefaultHiddenCollectionHelper(
|
|||
);
|
||||
await downloadCollectionFiles(
|
||||
DEFAULT_HIDDEN_COLLECTION_USER_FACING_NAME,
|
||||
HIDDEN_ITEMS_SECTION,
|
||||
true,
|
||||
defaultHiddenCollectionFiles,
|
||||
setCollectionDownloadProgressAttributes
|
||||
setFilesDownloadProgressAttributes
|
||||
);
|
||||
} catch (e) {
|
||||
logError(e, 'download hidden files failed ');
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadCollectionFiles(
|
||||
export async function downloadCollectionFiles(
|
||||
collectionName: string,
|
||||
collectionID: number,
|
||||
isHidden: boolean,
|
||||
collectionFiles: EnteFile[],
|
||||
setCollectionDownloadProgressAttributes: SetCollectionDownloadProgressAttributes
|
||||
setFilesDownloadProgressAttributes: SetFilesDownloadProgressAttributes
|
||||
) {
|
||||
if (!collectionFiles.length) {
|
||||
return;
|
||||
}
|
||||
const canceller = new AbortController();
|
||||
const increaseSuccess = () => {
|
||||
if (canceller.signal.aborted) return;
|
||||
setCollectionDownloadProgressAttributes((prev) => ({
|
||||
...prev,
|
||||
success: prev.success + 1,
|
||||
}));
|
||||
};
|
||||
const increaseFailed = () => {
|
||||
if (canceller.signal.aborted) return;
|
||||
setCollectionDownloadProgressAttributes((prev) => ({
|
||||
...prev,
|
||||
failed: prev.failed + 1,
|
||||
}));
|
||||
};
|
||||
const isCancelled = () => canceller.signal.aborted;
|
||||
const initialProgressAttributes: CollectionDownloadProgressAttributes = {
|
||||
collectionName,
|
||||
collectionID,
|
||||
isHidden,
|
||||
canceller,
|
||||
total: collectionFiles.length,
|
||||
success: 0,
|
||||
failed: 0,
|
||||
downloadDirPath: null,
|
||||
};
|
||||
let downloadDirPath: string;
|
||||
if (isElectron()) {
|
||||
const selectedDir = await ElectronAPIs.selectDirectory();
|
||||
if (!selectedDir) {
|
||||
return;
|
||||
}
|
||||
const downloadDirPath = await createCollectionDownloadFolder(
|
||||
downloadDirPath = await createCollectionDownloadFolder(
|
||||
selectedDir,
|
||||
collectionName
|
||||
);
|
||||
setCollectionDownloadProgressAttributes({
|
||||
...initialProgressAttributes,
|
||||
downloadDirPath,
|
||||
});
|
||||
await downloadFilesDesktop(
|
||||
collectionFiles,
|
||||
{ increaseSuccess, increaseFailed, isCancelled },
|
||||
downloadDirPath
|
||||
);
|
||||
} else {
|
||||
setCollectionDownloadProgressAttributes(initialProgressAttributes);
|
||||
await downloadFiles(collectionFiles, {
|
||||
increaseSuccess,
|
||||
increaseFailed,
|
||||
isCancelled,
|
||||
});
|
||||
}
|
||||
await downloadFilesWithProgress(
|
||||
collectionFiles,
|
||||
downloadDirPath,
|
||||
setFilesDownloadProgressAttributes
|
||||
);
|
||||
}
|
||||
|
||||
async function createCollectionDownloadFolder(
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
import { SelectedState } from 'types/gallery';
|
||||
import {
|
||||
SelectedState,
|
||||
SetFilesDownloadProgressAttributes,
|
||||
SetFilesDownloadProgressAttributesCreator,
|
||||
} from 'types/gallery';
|
||||
import {
|
||||
EnteFile,
|
||||
EncryptedEnteFile,
|
||||
|
@ -52,6 +56,7 @@ import { getFileExportPath, getUniqueFileExportName } from 'utils/export';
|
|||
import imageProcessor from 'services/imageProcessor';
|
||||
import ElectronAPIs from '@ente/shared/electron';
|
||||
import { downloadUsingAnchor } from '@ente/shared/utils';
|
||||
import { t } from 'i18next';
|
||||
|
||||
const WAIT_TIME_IMAGE_CONVERSION = 30 * 1000;
|
||||
|
||||
|
@ -625,9 +630,96 @@ export function getUniqueFiles(files: EnteFile[]) {
|
|||
return uniqueFiles;
|
||||
}
|
||||
|
||||
export async function downloadFilesWithProgress(
|
||||
files: EnteFile[],
|
||||
downloadDirPath: string,
|
||||
setFilesDownloadProgressAttributes: SetFilesDownloadProgressAttributes
|
||||
) {
|
||||
if (!files.length) {
|
||||
return;
|
||||
}
|
||||
const canceller = new AbortController();
|
||||
const increaseSuccess = () => {
|
||||
if (canceller.signal.aborted) return;
|
||||
setFilesDownloadProgressAttributes((prev) => ({
|
||||
...prev,
|
||||
success: prev.success + 1,
|
||||
}));
|
||||
};
|
||||
const increaseFailed = () => {
|
||||
if (canceller.signal.aborted) return;
|
||||
setFilesDownloadProgressAttributes((prev) => ({
|
||||
...prev,
|
||||
failed: prev.failed + 1,
|
||||
}));
|
||||
};
|
||||
const isCancelled = () => canceller.signal.aborted;
|
||||
|
||||
setFilesDownloadProgressAttributes({
|
||||
downloadDirPath,
|
||||
success: 0,
|
||||
failed: 0,
|
||||
total: files.length,
|
||||
canceller,
|
||||
});
|
||||
|
||||
if (isElectron()) {
|
||||
await downloadFilesDesktop(
|
||||
files,
|
||||
{ increaseSuccess, increaseFailed, isCancelled },
|
||||
downloadDirPath
|
||||
);
|
||||
} else {
|
||||
await downloadFiles(files, {
|
||||
increaseSuccess,
|
||||
increaseFailed,
|
||||
isCancelled,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function downloadSelectedFiles(
|
||||
files: EnteFile[],
|
||||
setFilesDownloadProgressAttributes: SetFilesDownloadProgressAttributes
|
||||
) {
|
||||
if (!files.length) {
|
||||
return;
|
||||
}
|
||||
let downloadDirPath: string;
|
||||
if (isElectron()) {
|
||||
downloadDirPath = await ElectronAPIs.selectDirectory();
|
||||
if (!downloadDirPath) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
await downloadFilesWithProgress(
|
||||
files,
|
||||
downloadDirPath,
|
||||
setFilesDownloadProgressAttributes
|
||||
);
|
||||
}
|
||||
|
||||
export async function downloadSingleFile(
|
||||
file: EnteFile,
|
||||
setFilesDownloadProgressAttributes: SetFilesDownloadProgressAttributes
|
||||
) {
|
||||
let downloadDirPath: string;
|
||||
if (isElectron()) {
|
||||
downloadDirPath = await ElectronAPIs.selectDirectory();
|
||||
if (!downloadDirPath) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
await downloadFilesWithProgress(
|
||||
[file],
|
||||
downloadDirPath,
|
||||
setFilesDownloadProgressAttributes
|
||||
);
|
||||
}
|
||||
|
||||
export async function downloadFiles(
|
||||
files: EnteFile[],
|
||||
progressBarUpdater?: {
|
||||
progressBarUpdater: {
|
||||
increaseSuccess: () => void;
|
||||
increaseFailed: () => void;
|
||||
isCancelled: () => boolean;
|
||||
|
@ -869,7 +961,8 @@ export const handleFileOps = async (
|
|||
files: EnteFile[];
|
||||
}
|
||||
| ((prev: { files: EnteFile[] }) => { files: EnteFile[] })
|
||||
) => void
|
||||
) => void,
|
||||
setFilesDownloadProgressAttributesCreator: SetFilesDownloadProgressAttributesCreator
|
||||
) => {
|
||||
switch (ops) {
|
||||
case FILE_OPS_TYPE.TRASH:
|
||||
|
@ -881,9 +974,17 @@ export const handleFileOps = async (
|
|||
case FILE_OPS_TYPE.HIDE:
|
||||
await hideFilesHelper(files, setTempHiddenFileIds);
|
||||
break;
|
||||
case FILE_OPS_TYPE.DOWNLOAD:
|
||||
await downloadFiles(files);
|
||||
case FILE_OPS_TYPE.DOWNLOAD: {
|
||||
const setSelectedFileDownloadProgressAttributes =
|
||||
setFilesDownloadProgressAttributesCreator(
|
||||
`${files.length} ${t('FILES')}`
|
||||
);
|
||||
await downloadSelectedFiles(
|
||||
files,
|
||||
setSelectedFileDownloadProgressAttributes
|
||||
);
|
||||
break;
|
||||
}
|
||||
case FILE_OPS_TYPE.FIX_TIME:
|
||||
fixTimeHelper(files, setFixCreationTimeAttributes);
|
||||
break;
|
||||
|
|
Loading…
Add table
Reference in a new issue