From aea2a747f0c4c20b3682a7e255cbf64883907932 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Mon, 24 Jul 2023 17:25:47 +0530 Subject: [PATCH] refactored selectedFileOptions utils to file util --- .../pages/gallery/SelectedFileOptions.tsx | 88 +++++----- apps/photos/src/pages/gallery/index.tsx | 163 +++--------------- apps/photos/src/utils/file/index.ts | 106 ++++++++++++ 3 files changed, 171 insertions(+), 186 deletions(-) diff --git a/apps/photos/src/components/pages/gallery/SelectedFileOptions.tsx b/apps/photos/src/components/pages/gallery/SelectedFileOptions.tsx index b0d227e57..ad484b791 100644 --- a/apps/photos/src/components/pages/gallery/SelectedFileOptions.tsx +++ b/apps/photos/src/components/pages/gallery/SelectedFileOptions.tsx @@ -11,7 +11,6 @@ import { HIDDEN_SECTION, TRASH_SECTION, } from 'constants/collection'; -import { Collection } from 'types/collection'; import { SelectionBar } from '../../Navbar/SelectionBar'; import { AppContext } from 'pages/_app'; import { Box, IconButton, Stack, Tooltip } from '@mui/material'; @@ -30,24 +29,18 @@ import { t } from 'i18next'; import { formatNumber } from 'utils/number/format'; import VisibilityOffOutlined from '@mui/icons-material/VisibilityOffOutlined'; import VisibilityOutlined from '@mui/icons-material/VisibilityOutlined'; +import { FILE_OPS_TYPE } from 'utils/file'; interface Props { - addToCollectionHelper: (collection: Collection) => void; - moveToCollectionHelper: (collection: Collection) => void; - restoreToCollectionHelper: (collection: Collection) => void; - unhideToCollectionHelper: (collection: Collection) => void; + handleCollectionOps: ( + opsType: COLLECTION_OPS_TYPE + ) => (...args: any[]) => void; + handleFileOps: (opsType: FILE_OPS_TYPE) => (...args: any[]) => void; showCreateCollectionModal: (opsType: COLLECTION_OPS_TYPE) => () => void; setCollectionSelectorAttributes: SetCollectionSelectorAttributes; - deleteFileHelper: (permanent?: boolean) => void; - hideFilesHelper: () => void; - removeFromCollectionHelper: () => void; - fixTimeHelper: () => void; - downloadHelper: () => void; count: number; ownCount: number; clearSelection: () => void; - archiveFilesHelper: () => void; - unArchiveFilesHelper: () => void; activeCollection: number; isFavoriteCollection: boolean; isUncategorizedCollection: boolean; @@ -56,22 +49,13 @@ interface Props { } const SelectedFileOptions = ({ - addToCollectionHelper, - moveToCollectionHelper, - restoreToCollectionHelper, showCreateCollectionModal, - removeFromCollectionHelper, - unhideToCollectionHelper, - fixTimeHelper, setCollectionSelectorAttributes, - deleteFileHelper, - hideFilesHelper, - downloadHelper, + handleCollectionOps, + handleFileOps, count, ownCount, clearSelection, - archiveFilesHelper, - unArchiveFilesHelper, activeCollection, isFavoriteCollection, isUncategorizedCollection, @@ -81,21 +65,23 @@ const SelectedFileOptions = ({ const { setDialogMessage } = useContext(AppContext); const addToCollection = () => setCollectionSelectorAttributes({ - callback: addToCollectionHelper, + callback: handleCollectionOps(COLLECTION_OPS_TYPE.ADD), showNextModal: showCreateCollectionModal(COLLECTION_OPS_TYPE.ADD), intent: CollectionSelectorIntent.add, fromCollection: !isInSearchMode ? activeCollection : undefined, }); const trashHandler = () => - setDialogMessage(getTrashFilesMessage(deleteFileHelper)); + setDialogMessage( + getTrashFilesMessage(handleFileOps(FILE_OPS_TYPE.TRASH)) + ); const permanentlyDeleteHandler = () => setDialogMessage({ title: t('DELETE_FILES_TITLE'), content: t('DELETE_FILES_MESSAGE'), proceed: { - action: () => deleteFileHelper(true), + action: handleFileOps(FILE_OPS_TYPE.DELETE_PERMANENTLY), text: t('DELETE'), variant: 'critical', }, @@ -104,7 +90,7 @@ const SelectedFileOptions = ({ const restoreHandler = () => setCollectionSelectorAttributes({ - callback: restoreToCollectionHelper, + callback: handleCollectionOps(COLLECTION_OPS_TYPE.RESTORE), showNextModal: showCreateCollectionModal( COLLECTION_OPS_TYPE.RESTORE ), @@ -118,7 +104,7 @@ const SelectedFileOptions = ({ content: t('CONFIRM_SELF_REMOVE_MESSAGE'), proceed: { - action: removeFromCollectionHelper, + action: handleCollectionOps(COLLECTION_OPS_TYPE.REMOVE), text: t('YES_REMOVE'), variant: 'primary', }, @@ -130,7 +116,7 @@ const SelectedFileOptions = ({ content: t('CONFIRM_SELF_AND_OTHER_REMOVE_MESSAGE'), proceed: { - action: removeFromCollectionHelper, + action: handleCollectionOps(COLLECTION_OPS_TYPE.REMOVE), text: t('YES_REMOVE'), variant: 'critical', }, @@ -141,7 +127,7 @@ const SelectedFileOptions = ({ const moveToCollection = () => { setCollectionSelectorAttributes({ - callback: moveToCollectionHelper, + callback: handleCollectionOps(COLLECTION_OPS_TYPE.MOVE), showNextModal: showCreateCollectionModal(COLLECTION_OPS_TYPE.MOVE), intent: CollectionSelectorIntent.move, fromCollection: !isInSearchMode ? activeCollection : undefined, @@ -150,7 +136,7 @@ const SelectedFileOptions = ({ const unhideToCollection = () => { setCollectionSelectorAttributes({ - callback: unhideToCollectionHelper, + callback: handleCollectionOps(COLLECTION_OPS_TYPE.UNHIDE), showNextModal: showCreateCollectionModal( COLLECTION_OPS_TYPE.UNHIDE ), @@ -174,12 +160,14 @@ const SelectedFileOptions = ({ {isInSearchMode ? ( <> - + - + @@ -189,12 +177,14 @@ const SelectedFileOptions = ({ - + - + @@ -215,7 +205,8 @@ const SelectedFileOptions = ({ ) : isUncategorizedCollection ? ( <> - + @@ -232,7 +223,8 @@ const SelectedFileOptions = ({ ) : isIncomingSharedCollection ? ( - + @@ -244,7 +236,8 @@ const SelectedFileOptions = ({ - + @@ -258,12 +251,14 @@ const SelectedFileOptions = ({ ) : ( <> - + - + @@ -274,14 +269,20 @@ const SelectedFileOptions = ({ {activeCollection === ARCHIVE_SECTION && ( - + )} {activeCollection === ALL_SECTION && ( - + @@ -307,7 +308,8 @@ const SelectedFileOptions = ({ )} - + diff --git a/apps/photos/src/pages/gallery/index.tsx b/apps/photos/src/pages/gallery/index.tsx index da9fb7a00..2995635ad 100644 --- a/apps/photos/src/pages/gallery/index.tsx +++ b/apps/photos/src/pages/gallery/index.tsx @@ -11,8 +11,6 @@ import { clearKeys, getKey, SESSION_KEYS } from 'utils/storage/sessionStorage'; import { getLocalFiles, syncFiles, - trashFiles, - deleteFromTrash, getLocalHiddenFiles, syncHiddenFiles, } from 'services/fileService'; @@ -23,7 +21,6 @@ import { getLocalCollections, createAlbum, getCollectionSummaries, - moveToHiddenCollection, constructEmailList, } from 'services/collectionService'; import { t } from 'i18next'; @@ -49,11 +46,11 @@ import EnteSpinner from 'components/EnteSpinner'; import { LoadingOverlay } from 'components/LoadingOverlay'; import PhotoFrame from 'components/PhotoFrame'; import { - changeFilesVisibility, + FILE_OPS_TYPE, constructFileToCollectionMap, - downloadFiles, getSelectedFiles, getUniqueFiles, + handleFileOps, mergeMetadata, sortFiles, } from 'utils/file'; @@ -81,7 +78,6 @@ import { PAGES } from 'constants/pages'; import { COLLECTION_OPS_TYPE, handleCollectionOps, - getSelectedCollection, getArchivedCollections, hasNonSystemCollections, splitNormalAndHiddenCollections, @@ -100,7 +96,6 @@ import { SelectedState, UploadTypeSelectorIntent, } from 'types/gallery'; -import { VISIBILITY_STATE } from 'types/magicMetadata'; import Collections from 'components/Collections'; import { GalleryNavbar } from 'components/pages/gallery/Navbar'; import { Search, SearchResultSummary, UpdateSearch } from 'types/search'; @@ -721,26 +716,26 @@ export default function Gallery() { } }; - const changeFilesVisibilityHelper = async ( - visibility: VISIBILITY_STATE - ) => { + const fileOpsHelper = (ops: FILE_OPS_TYPE) => async () => { startLoading(); try { const selectedFiles = getSelectedFiles(selected, filteredData); - await changeFilesVisibility(selectedFiles, visibility); - clearSelection(); - } catch (e) { - logError(e, 'change file visibility failed'); - switch (e.status?.toString()) { - case ServerErrorCodes.FORBIDDEN: - setDialogMessage({ - title: t('ERROR'), - - close: { variant: 'critical' }, - content: t('NOT_FILE_OWNER'), - }); - return; + const toProcessFiles = selectedFiles.filter( + (file) => file.ownerID === user.id + ); + if (toProcessFiles.length > 0) { + await handleFileOps( + ops, + toProcessFiles, + setDeletedFileIds, + setHiddenFileIds, + setFixCreationTimeAttributes + ); } + clearSelection(); + await syncWithRemote(false, true); + } catch (e) { + logError(e, 'file ops failed', { ops }); setDialogMessage({ title: t('ERROR'), @@ -748,7 +743,6 @@ export default function Gallery() { content: t('UNKNOWN_ERROR'), }); } finally { - await syncWithRemote(false, true); finishLoading(); } }; @@ -780,77 +774,6 @@ export default function Gallery() { }); }; - const deleteFileHelper = async (permanent?: boolean) => { - startLoading(); - try { - const selectedFiles = getSelectedFiles(selected, filteredData); - setDeletedFileIds((deletedFileIds) => { - selectedFiles.forEach((file) => deletedFileIds.add(file.id)); - return new Set(deletedFileIds); - }); - if (permanent) { - await deleteFromTrash(selectedFiles.map((file) => file.id)); - } else { - await trashFiles(selectedFiles); - } - clearSelection(); - } catch (e) { - setDeletedFileIds(new Set()); - switch (e.status?.toString()) { - case ServerErrorCodes.FORBIDDEN: - setDialogMessage({ - title: t('ERROR'), - - close: { variant: 'critical' }, - content: t('NOT_FILE_OWNER'), - }); - } - setDialogMessage({ - title: t('ERROR'), - - close: { variant: 'critical' }, - content: t('UNKNOWN_ERROR'), - }); - } finally { - await syncWithRemote(false, true); - finishLoading(); - } - }; - - const hideFilesHelper = async () => { - startLoading(); - try { - // passing files here instead of filteredData because we want to move all files copies to hidden collection - const selectedFiles = getSelectedFiles(selected, files); - setHiddenFileIds((hiddenFileIds) => { - selectedFiles.forEach((file) => hiddenFileIds.add(file.id)); - return new Set(hiddenFileIds); - }); - await moveToHiddenCollection(selectedFiles); - clearSelection(); - } catch (e) { - setHiddenFileIds(new Set()); - switch (e.status?.toString()) { - case ServerErrorCodes.FORBIDDEN: - setDialogMessage({ - title: t('ERROR'), - - close: { variant: 'critical' }, - content: t('NOT_FILE_OWNER'), - }); - } - setDialogMessage({ - title: t('ERROR'), - - close: { variant: 'critical' }, - content: t('UNKNOWN_ERROR'), - }); - } finally { - await syncWithRemote(false, true); - finishLoading(); - } - }; - const updateSearch: UpdateSearch = (newSearch, summary) => { if (newSearch?.collection) { setActiveCollection(newSearch?.collection); @@ -865,20 +788,6 @@ export default function Gallery() { } }; - const fixTimeHelper = async () => { - const selectedFiles = getSelectedFiles(selected, filteredData); - setFixCreationTimeAttributes({ files: selectedFiles }); - clearSelection(); - }; - - const downloadHelper = async () => { - const selectedFiles = getSelectedFiles(selected, filteredData); - clearSelection(); - startLoading(); - await downloadFiles(selectedFiles); - finishLoading(); - }; - const openUploader = (intent = UploadTypeSelectorIntent.normalUpload) => { if (!uploadManager.shouldAllowNewUpload()) { return; @@ -1044,46 +953,14 @@ export default function Gallery() { {selected.count > 0 && selected.collectionID === activeCollection && ( - changeFilesVisibilityHelper( - VISIBILITY_STATE.ARCHIVED - ) - } - unArchiveFilesHelper={() => - changeFilesVisibilityHelper( - VISIBILITY_STATE.VISIBLE - ) - } - moveToCollectionHelper={collectionOpsHelper( - COLLECTION_OPS_TYPE.MOVE - )} - restoreToCollectionHelper={collectionOpsHelper( - COLLECTION_OPS_TYPE.RESTORE - )} - unhideToCollectionHelper={collectionOpsHelper( - COLLECTION_OPS_TYPE.UNHIDE - )} + handleCollectionOps={collectionOpsHelper} + handleFileOps={fileOpsHelper} showCreateCollectionModal={ showCreateCollectionModal } setCollectionSelectorAttributes={ setCollectionSelectorAttributes } - deleteFileHelper={deleteFileHelper} - hideFilesHelper={hideFilesHelper} - removeFromCollectionHelper={() => - collectionOpsHelper(COLLECTION_OPS_TYPE.REMOVE)( - getSelectedCollection( - activeCollection, - collections - ) - ) - } - fixTimeHelper={fixTimeHelper} - downloadHelper={downloadHelper} count={selected.count} ownCount={selected.ownCount} clearSelection={clearSelection} diff --git a/apps/photos/src/utils/file/index.ts b/apps/photos/src/utils/file/index.ts index 9dbfadfe3..fd9960279 100644 --- a/apps/photos/src/utils/file/index.ts +++ b/apps/photos/src/utils/file/index.ts @@ -35,6 +35,8 @@ import { CustomError } from 'utils/error'; import { convertBytesToHumanReadable } from './size'; import ComlinkCryptoWorker from 'utils/comlink/ComlinkCryptoWorker'; import { + deleteFromTrash, + trashFiles, updateFileMagicMetadata, updateFilePublicMagicMetadata, } from 'services/fileService'; @@ -42,9 +44,20 @@ import isElectron from 'is-electron'; import imageProcessor from 'services/electron/imageProcessor'; import { isPlaybackPossible } from 'utils/photoFrame'; import { FileTypeInfo } from 'types/upload'; +import { moveToHiddenCollection } from 'services/collectionService'; const WAIT_TIME_IMAGE_CONVERSION = 30 * 1000; +export enum FILE_OPS_TYPE { + DOWNLOAD, + FIX_TIME, + ARCHIVE, + UNARCHIVE, + HIDE, + TRASH, + DELETE_PERMANENTLY, +} + export function downloadAsFile(filename: string, content: string) { const file = new Blob([content], { type: 'text/plain', @@ -691,3 +704,96 @@ export const shouldShowAvatar = (file: EnteFile, user: User) => { return false; } }; + +export const handleFileOps = async ( + ops: FILE_OPS_TYPE, + files: EnteFile[], + setDeletedFileIds: ( + deletedFileIds: Set | ((prev: Set) => Set) + ) => void, + setHiddenFileIds: ( + hiddenFileIds: Set | ((prev: Set) => Set) + ) => void, + setFixCreationTimeAttributes: ( + fixCreationTimeAttributes: + | { + files: EnteFile[]; + } + | ((prev: { files: EnteFile[] }) => { files: EnteFile[] }) + ) => void +) => { + switch (ops) { + case FILE_OPS_TYPE.TRASH: + await deleteFileHelper(files, false, setDeletedFileIds); + break; + case FILE_OPS_TYPE.DELETE_PERMANENTLY: + await deleteFileHelper(files, true, setDeletedFileIds); + break; + case FILE_OPS_TYPE.HIDE: + await hideFilesHelper(files, setHiddenFileIds); + break; + case FILE_OPS_TYPE.DOWNLOAD: + await downloadFiles(files); + break; + case FILE_OPS_TYPE.FIX_TIME: + fixTimeHelper(files, setFixCreationTimeAttributes); + break; + case FILE_OPS_TYPE.ARCHIVE: + await changeFilesVisibility(files, VISIBILITY_STATE.ARCHIVED); + break; + case FILE_OPS_TYPE.UNARCHIVE: + await changeFilesVisibility(files, VISIBILITY_STATE.VISIBLE); + break; + } +}; + +const deleteFileHelper = async ( + selectedFiles: EnteFile[], + permanent: boolean, + setDeletedFileIds: ( + deletedFileIds: Set | ((prev: Set) => Set) + ) => void +) => { + try { + setDeletedFileIds((deletedFileIds) => { + selectedFiles.forEach((file) => deletedFileIds.add(file.id)); + return new Set(deletedFileIds); + }); + if (permanent) { + await deleteFromTrash(selectedFiles.map((file) => file.id)); + } else { + await trashFiles(selectedFiles); + } + } catch (e) { + setDeletedFileIds(new Set()); + throw e; + } +}; + +const hideFilesHelper = async ( + selectedFiles: EnteFile[], + setHiddenFileIds: ( + hiddenFileIds: Set | ((prev: Set) => Set) + ) => void +) => { + try { + // passing files here instead of filteredData because we want to move all files copies to hidden collection + setHiddenFileIds((hiddenFileIds) => { + selectedFiles.forEach((file) => hiddenFileIds.add(file.id)); + return new Set(hiddenFileIds); + }); + await moveToHiddenCollection(selectedFiles); + } catch (e) { + setHiddenFileIds(new Set()); + throw e; + } +}; + +const fixTimeHelper = async ( + selectedFiles: EnteFile[], + setFixCreationTimeAttributes: (fixCreationTimeAttributes: { + files: EnteFile[]; + }) => void +) => { + setFixCreationTimeAttributes({ files: selectedFiles }); +};