From 62bb0a0043b0bd405a798039101d27b0dba073e1 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Mon, 21 Aug 2023 12:53:53 +0530 Subject: [PATCH 01/13] added ExportPendingList UI --- .../src/components/DialogBoxV2/index.tsx | 3 +- apps/photos/src/components/ExportFinished.tsx | 22 ++++- .../src/components/ExportPendingList.tsx | 97 +++++++++++++++++++ apps/photos/src/components/FileList.tsx | 8 +- 4 files changed, 123 insertions(+), 7 deletions(-) create mode 100644 apps/photos/src/components/ExportPendingList.tsx diff --git a/apps/photos/src/components/DialogBoxV2/index.tsx b/apps/photos/src/components/DialogBoxV2/index.tsx index 7c56eeeba..a97074423 100644 --- a/apps/photos/src/components/DialogBoxV2/index.tsx +++ b/apps/photos/src/components/DialogBoxV2/index.tsx @@ -18,7 +18,6 @@ type IProps = React.PropsWithChildren< onClose: () => void; attributes: DialogBoxAttributesV2; size?: Breakpoint; - titleCloseButton?: boolean; } >; @@ -42,12 +41,12 @@ export default function DialogBoxV2({ return ( diff --git a/apps/photos/src/components/ExportFinished.tsx b/apps/photos/src/components/ExportFinished.tsx index 0b096661c..a01d92fbe 100644 --- a/apps/photos/src/components/ExportFinished.tsx +++ b/apps/photos/src/components/ExportFinished.tsx @@ -5,11 +5,13 @@ import { Stack, Typography, } from '@mui/material'; -import React from 'react'; import { t } from 'i18next'; import { formatDateTime } from 'utils/time/format'; import { SpaceBetweenFlex } from './Container'; import { formatNumber } from 'utils/number/format'; +import ExportPendingList from './ExportPendingList'; +import { useState } from 'react'; +import LinkButton from './pages/gallery/LinkButton'; interface Props { pendingFileCount: number; @@ -19,6 +21,16 @@ interface Props { } export default function ExportFinished(props: Props) { + const [pendingFileListView, setPendingFileListView] = + useState(false); + + const openPendingFileList = () => { + setPendingFileListView(true); + }; + + const closePendingFileList = () => { + setPendingFileListView(false); + }; return ( <> @@ -27,9 +39,9 @@ export default function ExportFinished(props: Props) { {t('PENDING_ITEMS')} - + {formatNumber(props.pendingFileCount)} - + @@ -54,6 +66,10 @@ export default function ExportFinished(props: Props) { {t('EXPORT_AGAIN')} + ); } diff --git a/apps/photos/src/components/ExportPendingList.tsx b/apps/photos/src/components/ExportPendingList.tsx new file mode 100644 index 000000000..6107c124b --- /dev/null +++ b/apps/photos/src/components/ExportPendingList.tsx @@ -0,0 +1,97 @@ +import { EnteFile } from 'types/file'; +import { ResultItemContainer } from './Upload/UploadProgress/styledComponents'; +import FileList from 'components/FileList'; +import { useEffect, useState } from 'react'; +import { getCollectionNameMap } from 'utils/collection'; +import { getLocalCollections } from 'services/collectionService'; +import { getLocalFiles, getLocalHiddenFiles } from 'services/fileService'; +import exportService from 'services/export'; +import { getUnExportedFiles } from 'utils/export'; +import { mergeMetadata, getPersonalFiles } from 'utils/file'; +import { LS_KEYS, getData } from 'utils/storage/localStorage'; +import DialogBoxV2 from './DialogBoxV2'; +import { t } from 'i18next'; +import { FlexWrapper } from './Container'; +import CollectionCard from './Collections/CollectionCard'; +import { ResultPreviewTile } from './Collections/styledComponents'; +import { Box } from '@mui/material'; + +interface Iprops { + isOpen: boolean; + onClose: () => void; +} + +const ExportPendingList = (props: Iprops) => { + const [collectionNameMap, setCollectionNameMap] = useState(new Map()); + const [pendingFiles, setPendingFiles] = useState([]); + + useEffect(() => { + const main = async () => { + const collections = await getLocalCollections(); + setCollectionNameMap(getCollectionNameMap(collections)); + + const exportFolder = exportService.getExportSettings()?.folder; + if (!exportFolder) { + return []; + } + const exportRecord = await exportService.getExportRecord( + exportFolder + ); + + const files = mergeMetadata([ + ...(await getLocalFiles()), + ...(await getLocalHiddenFiles()), + ]); + const user = getData(LS_KEYS.USER); + const personalFiles = getPersonalFiles(files, user); + + const filesToExport = getUnExportedFiles( + personalFiles, + exportRecord + ); + + setPendingFiles(filesToExport); + }; + main(); + }, []); + + return ( + + ( + + + + null} + collectionTile={ResultPreviewTile} + /> + + {`${collectionNameMap.get(file.collectionID)} - ${ + file.metadata.title + }`} + + + ))} + /> + + ); +}; + +export default ExportPendingList; diff --git a/apps/photos/src/components/FileList.tsx b/apps/photos/src/components/FileList.tsx index 00269d7d0..dc06685e9 100644 --- a/apps/photos/src/components/FileList.tsx +++ b/apps/photos/src/components/FileList.tsx @@ -4,6 +4,8 @@ import { FixedSizeList as List } from 'react-window'; interface Iprops { fileList: any[]; + maxHeight?: number; + itemSize?: number; } export default function FileList(props: Iprops) { @@ -26,9 +28,11 @@ export default function FileList(props: Iprops) { return ( {Row} From fa659db08945cd73035fd576e48a61f068ea2543 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Mon, 21 Aug 2023 17:59:46 +0530 Subject: [PATCH 02/13] refactored pendingExports and collectionNameMap state --- apps/photos/src/components/ExportFinished.tsx | 8 ++- apps/photos/src/components/ExportModal.tsx | 33 ++++++------ .../src/components/ExportPendingList.tsx | 51 +++---------------- apps/photos/src/pages/gallery/index.tsx | 6 ++- apps/photos/src/services/export/index.ts | 48 +++-------------- apps/photos/src/types/export/index.ts | 8 +-- 6 files changed, 43 insertions(+), 111 deletions(-) diff --git a/apps/photos/src/components/ExportFinished.tsx b/apps/photos/src/components/ExportFinished.tsx index a01d92fbe..76b07a164 100644 --- a/apps/photos/src/components/ExportFinished.tsx +++ b/apps/photos/src/components/ExportFinished.tsx @@ -12,9 +12,11 @@ import { formatNumber } from 'utils/number/format'; import ExportPendingList from './ExportPendingList'; import { useState } from 'react'; import LinkButton from './pages/gallery/LinkButton'; +import { EnteFile } from 'types/file'; interface Props { - pendingFileCount: number; + pendingExports: EnteFile[]; + collectionNameMap: Map; onHide: () => void; lastExportTime: number; startExport: () => void; @@ -40,7 +42,7 @@ export default function ExportFinished(props: Props) { {t('PENDING_ITEMS')} - {formatNumber(props.pendingFileCount)} + {formatNumber(props.pendingExports.length)} @@ -67,6 +69,8 @@ export default function ExportFinished(props: Props) { diff --git a/apps/photos/src/components/ExportModal.tsx b/apps/photos/src/components/ExportModal.tsx index 01f9a357a..968acc7e5 100644 --- a/apps/photos/src/components/ExportModal.tsx +++ b/apps/photos/src/components/ExportModal.tsx @@ -1,7 +1,7 @@ import isElectron from 'is-electron'; import React, { useEffect, useState, useContext } from 'react'; import exportService from 'services/export'; -import { ExportProgress, ExportSettings, FileExportStats } from 'types/export'; +import { ExportProgress, ExportSettings } from 'types/export'; import { Box, Button, @@ -30,6 +30,7 @@ import { t } from 'i18next'; import LinkButton from './pages/gallery/LinkButton'; import { CustomError } from 'utils/error'; import { addLogLine } from 'utils/logging'; +import { EnteFile } from 'types/file'; const ExportFolderPathContainer = styled(LinkButton)` width: 262px; @@ -44,6 +45,7 @@ const ExportFolderPathContainer = styled(LinkButton)` interface Props { show: boolean; onHide: () => void; + collectionNameMap: Map; } export default function ExportModal(props: Props) { const appContext = useContext(AppContext); @@ -55,10 +57,7 @@ export default function ExportModal(props: Props) { failed: 0, total: 0, }); - const [fileExportStats, setFileExportStats] = useState({ - totalCount: 0, - pendingCount: 0, - }); + const [pendingExports, setPendingExports] = useState([]); const [lastExportTime, setLastExportTime] = useState(0); // ==================== @@ -72,8 +71,8 @@ export default function ExportModal(props: Props) { exportService.setUIUpdaters({ setExportStage, setExportProgress, - setFileExportStats, setLastExportTime, + setPendingExports, }); const exportSettings: ExportSettings = exportService.getExportSettings(); @@ -123,20 +122,18 @@ export default function ExportModal(props: Props) { const syncExportRecord = async (exportFolder: string): Promise => { try { if (!exportService.exportFolderExists(exportFolder)) { - const fileExportStats = await exportService.getFileExportStats( + const pendingExports = await exportService.getPendingExports( null ); - setFileExportStats(fileExportStats); + setPendingExports(pendingExports); } const exportRecord = await exportService.getExportRecord( exportFolder ); setExportStage(exportRecord.stage); setLastExportTime(exportRecord.lastAttemptTimestamp); - const fileExportStats = await exportService.getFileExportStats( - exportRecord - ); - setFileExportStats(fileExportStats); + const pendingExports = await exportService.getPendingExports(null); + setPendingExports(pendingExports); } catch (e) { if (e.message !== CustomError.EXPORT_FOLDER_DOES_NOT_EXIST) { logError(e, 'syncExportRecord failed'); @@ -219,8 +216,9 @@ export default function ExportModal(props: Props) { stopExport={stopExport} onHide={props.onHide} lastExportTime={lastExportTime} - pendingFileCount={fileExportStats.pendingCount} exportProgress={exportProgress} + pendingExports={pendingExports} + collectionNameMap={props.collectionNameMap} /> ); @@ -306,16 +304,18 @@ const ExportDynamicContent = ({ stopExport, onHide, lastExportTime, - pendingFileCount, exportProgress, + pendingExports, + collectionNameMap, }: { exportStage: ExportStage; startExport: () => void; stopExport: () => void; onHide: () => void; lastExportTime: number; - pendingFileCount: number; exportProgress: ExportProgress; + pendingExports: EnteFile[]; + collectionNameMap: Map; }) => { switch (exportStage) { case ExportStage.INIT: @@ -336,7 +336,8 @@ const ExportDynamicContent = ({ ); diff --git a/apps/photos/src/components/ExportPendingList.tsx b/apps/photos/src/components/ExportPendingList.tsx index 6107c124b..43f8801e9 100644 --- a/apps/photos/src/components/ExportPendingList.tsx +++ b/apps/photos/src/components/ExportPendingList.tsx @@ -1,14 +1,6 @@ import { EnteFile } from 'types/file'; import { ResultItemContainer } from './Upload/UploadProgress/styledComponents'; import FileList from 'components/FileList'; -import { useEffect, useState } from 'react'; -import { getCollectionNameMap } from 'utils/collection'; -import { getLocalCollections } from 'services/collectionService'; -import { getLocalFiles, getLocalHiddenFiles } from 'services/fileService'; -import exportService from 'services/export'; -import { getUnExportedFiles } from 'utils/export'; -import { mergeMetadata, getPersonalFiles } from 'utils/file'; -import { LS_KEYS, getData } from 'utils/storage/localStorage'; import DialogBoxV2 from './DialogBoxV2'; import { t } from 'i18next'; import { FlexWrapper } from './Container'; @@ -19,42 +11,11 @@ import { Box } from '@mui/material'; interface Iprops { isOpen: boolean; onClose: () => void; + collectionNameMap: Map; + pendingExports: EnteFile[]; } const ExportPendingList = (props: Iprops) => { - const [collectionNameMap, setCollectionNameMap] = useState(new Map()); - const [pendingFiles, setPendingFiles] = useState([]); - - useEffect(() => { - const main = async () => { - const collections = await getLocalCollections(); - setCollectionNameMap(getCollectionNameMap(collections)); - - const exportFolder = exportService.getExportSettings()?.folder; - if (!exportFolder) { - return []; - } - const exportRecord = await exportService.getExportRecord( - exportFolder - ); - - const files = mergeMetadata([ - ...(await getLocalFiles()), - ...(await getLocalHiddenFiles()), - ]); - const user = getData(LS_KEYS.USER); - const personalFiles = getPersonalFiles(files, user); - - const filesToExport = getUnExportedFiles( - personalFiles, - exportRecord - ); - - setPendingFiles(filesToExport); - }; - main(); - }, []); - return ( { ( + fileList={props.pendingExports.map((file) => ( @@ -83,9 +44,9 @@ const ExportPendingList = (props: Iprops) => { collectionTile={ResultPreviewTile} /> - {`${collectionNameMap.get(file.collectionID)} - ${ - file.metadata.title - }`} + {`${props.collectionNameMap.get( + file.collectionID + )} - ${file.metadata.title}`} ))} diff --git a/apps/photos/src/pages/gallery/index.tsx b/apps/photos/src/pages/gallery/index.tsx index ec6f86bd2..9aedd688a 100644 --- a/apps/photos/src/pages/gallery/index.tsx +++ b/apps/photos/src/pages/gallery/index.tsx @@ -1032,7 +1032,11 @@ export default function Gallery() { isInSearchMode={isInSearchMode} /> )} - + {}, setExportStage: () => {}, setLastExportTime: () => {}, - setFileExportStats: () => {}, + setPendingExports: () => {}, }; private currentExportProgress: ExportProgress = { total: 0, @@ -230,9 +229,9 @@ class ExportService { } } - getFileExportStats = async ( + getPendingExports = async ( exportRecord: ExportRecord - ): Promise => { + ): Promise => { try { const user: User = getData(LS_KEYS.USER); const files = [ @@ -241,44 +240,11 @@ class ExportService { ]; const userPersonalFiles = getPersonalFiles(files, user); - const collections = await getLocalCollections(true); - const userNonEmptyPersonalCollections = - getNonEmptyPersonalCollections( - collections, - userPersonalFiles, - user - ); - const unExportedFiles = getUnExportedFiles( userPersonalFiles, exportRecord ); - const deletedExportedFiles = getDeletedExportedFiles( - userPersonalFiles, - exportRecord - ); - const renamedCollections = getRenamedExportedCollections( - userNonEmptyPersonalCollections, - exportRecord - ); - const deletedCollections = getDeletedExportedCollections( - userNonEmptyPersonalCollections, - exportRecord - ); - - addLocalLog( - () => - `personal files:${userPersonalFiles.length} unexported files: ${unExportedFiles.length}, deleted exported files: ${deletedExportedFiles.length}, renamed collections: ${renamedCollections.length}, deleted collections: ${deletedCollections.length}` - ); - - return { - totalCount: userPersonalFiles.length, - pendingCount: - unExportedFiles.length + - deletedExportedFiles.length + - renamedCollections.length + - deletedCollections.length, - }; + return unExportedFiles; } catch (e) { logError(e, 'getUpdateFileLists failed'); throw e; @@ -319,8 +285,8 @@ class ExportService { const exportRecord = await this.getExportRecord(exportFolder); - const fileExportStats = await this.getFileExportStats(exportRecord); - this.uiUpdater.setFileExportStats(fileExportStats); + const pendingExports = await this.getPendingExports(exportRecord); + this.uiUpdater.setPendingExports(pendingExports); } catch (e) { logError(e, 'postExport failed'); } diff --git a/apps/photos/src/types/export/index.ts b/apps/photos/src/types/export/index.ts index e957259ad..b52042bf7 100644 --- a/apps/photos/src/types/export/index.ts +++ b/apps/photos/src/types/export/index.ts @@ -1,4 +1,5 @@ import { ExportStage } from 'constants/export'; +import { EnteFile } from 'types/file'; export interface ExportProgress { success: number; @@ -17,11 +18,6 @@ export interface FileExportNames { [ID: string]: string; } -export interface FileExportStats { - totalCount: number; - pendingCount: number; -} - export interface ExportRecordV0 { stage: ExportStage; lastAttemptTimestamp: number; @@ -66,6 +62,6 @@ export interface ExportSettings { export interface ExportUIUpdaters { setExportStage: (stage: ExportStage) => void; setExportProgress: (progress: ExportProgress) => void; - setFileExportStats: (fileExportStats: FileExportStats) => void; setLastExportTime: (exportTime: number) => void; + setPendingExports: (pendingExports: EnteFile[]) => void; } From 31f016797ce7fbe215659c25a4b701d541db8077 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Mon, 21 Aug 2023 18:31:27 +0530 Subject: [PATCH 03/13] fix flicker issue --- .../src/components/ExportPendingList.tsx | 57 ++++++++----- apps/photos/src/components/FileList.tsx | 41 --------- apps/photos/src/components/ItemList.tsx | 83 +++++++++++++++++++ .../UploadProgress/inProgressSection.tsx | 45 ++++++---- .../Upload/UploadProgress/resultSection.tsx | 32 +++++-- 5 files changed, 175 insertions(+), 83 deletions(-) delete mode 100644 apps/photos/src/components/FileList.tsx create mode 100644 apps/photos/src/components/ItemList.tsx diff --git a/apps/photos/src/components/ExportPendingList.tsx b/apps/photos/src/components/ExportPendingList.tsx index 43f8801e9..09a1c2ac9 100644 --- a/apps/photos/src/components/ExportPendingList.tsx +++ b/apps/photos/src/components/ExportPendingList.tsx @@ -1,6 +1,6 @@ import { EnteFile } from 'types/file'; import { ResultItemContainer } from './Upload/UploadProgress/styledComponents'; -import FileList from 'components/FileList'; +import ItemList from 'components/ItemList'; import DialogBoxV2 from './DialogBoxV2'; import { t } from 'i18next'; import { FlexWrapper } from './Container'; @@ -16,6 +16,36 @@ interface Iprops { } const ExportPendingList = (props: Iprops) => { + const renderListItem = (file: EnteFile) => { + return ( + + + + null} + collectionTile={ResultPreviewTile} + /> + + {`${props.collectionNameMap.get(file.collectionID)} / ${ + file.metadata.title + }`} + + + ); + }; + + const getItemTitle = (file: EnteFile) => { + return `${props.collectionNameMap.get(file.collectionID)} - ${ + file.metadata.title + }`; + }; + + const generateItemKey = (file: EnteFile) => { + return `${file.collectionID}-${file.id}`; + }; + return ( { }, }} size="xs"> - ( - - - - null} - collectionTile={ResultPreviewTile} - /> - - {`${props.collectionNameMap.get( - file.collectionID - )} - ${file.metadata.title}`} - - - ))} + items={props.pendingExports} + renderListItem={renderListItem} + getItemTitle={getItemTitle} + generateItemKey={generateItemKey} /> ); diff --git a/apps/photos/src/components/FileList.tsx b/apps/photos/src/components/FileList.tsx deleted file mode 100644 index dc06685e9..000000000 --- a/apps/photos/src/components/FileList.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { Box, Tooltip } from '@mui/material'; -import React from 'react'; -import { FixedSizeList as List } from 'react-window'; - -interface Iprops { - fileList: any[]; - maxHeight?: number; - itemSize?: number; -} - -export default function FileList(props: Iprops) { - const Row = ({ index, style }) => ( - -
{props.fileList[index]}
-
- ); - - return ( - - - {Row} - - - ); -} diff --git a/apps/photos/src/components/ItemList.tsx b/apps/photos/src/components/ItemList.tsx new file mode 100644 index 000000000..0a3ff59bb --- /dev/null +++ b/apps/photos/src/components/ItemList.tsx @@ -0,0 +1,83 @@ +import { Box, Tooltip } from '@mui/material'; +import React from 'react'; +import { + FixedSizeList as List, + ListChildComponentProps, + areEqual, +} from 'react-window'; +import memoize from 'memoize-one'; + +interface Iprops { + items: any[]; + generateItemKey: (item: any) => string; + getItemTitle: (item: any) => string; + renderListItem: (item: any) => JSX.Element; + maxHeight?: number; + itemSize?: number; +} + +interface ItemData { + renderListItem: (item: any) => JSX.Element; + getItemTitle: (item: any) => string; + items: any[]; +} + +const createItemData = memoize( + ( + renderListItem: (item: any) => JSX.Element, + getItemTitle: (item: any) => string, + items: any[] + ): ItemData => ({ + renderListItem, + getItemTitle, + items, + }) +); + +const Row = React.memo( + ({ index, style, data }: ListChildComponentProps) => { + const { renderListItem, items, getItemTitle } = data; + return ( + +
{renderListItem(items[index])}
+
+ ); + }, + areEqual +); + +export default function ItemList(props: Iprops) { + const itemData = createItemData( + props.renderListItem, + props.getItemTitle, + props.items + ); + return ( + + + {Row} + + + ); +} diff --git a/apps/photos/src/components/Upload/UploadProgress/inProgressSection.tsx b/apps/photos/src/components/Upload/UploadProgress/inProgressSection.tsx index d74142be9..98d6d48cc 100644 --- a/apps/photos/src/components/Upload/UploadProgress/inProgressSection.tsx +++ b/apps/photos/src/components/Upload/UploadProgress/inProgressSection.tsx @@ -1,5 +1,5 @@ import React, { useContext } from 'react'; -import FileList from 'components/FileList'; +import ItemList from 'components/ItemList'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import { InProgressItemContainer } from './styledComponents'; import { @@ -18,6 +18,29 @@ export const InProgressSection = () => { useContext(UploadProgressContext); const fileList = inProgressUploads ?? []; + const renderListItem = ({ localFileID, progress }) => { + return ( + + {uploadFileNames.get(localFileID)} + {uploadStage === UPLOAD_STAGES.UPLOADING && ( + <> + {' '} + {`-`} + {`${progress}%`} + + )} + + ); + }; + + const getItemTitle = ({ localFileID, progress }) => { + return `${uploadFileNames.get(localFileID)} - ${progress}%`; + }; + + const generateItemKey = ({ localFileID, progress }) => { + return `${localFileID}-${progress}`; + }; + return ( }> @@ -29,19 +52,13 @@ export const InProgressSection = () => { {hasLivePhotos && ( {t('LIVE_PHOTOS_DETECTED')} )} - ( - - {uploadFileNames.get(localFileID)} - {uploadStage === UPLOAD_STAGES.UPLOADING && ( - <> - {' '} - {`-`} - {`${progress}%`} - - )} - - ))} + diff --git a/apps/photos/src/components/Upload/UploadProgress/resultSection.tsx b/apps/photos/src/components/Upload/UploadProgress/resultSection.tsx index 052670d27..ca5e0e276 100644 --- a/apps/photos/src/components/Upload/UploadProgress/resultSection.tsx +++ b/apps/photos/src/components/Upload/UploadProgress/resultSection.tsx @@ -1,5 +1,5 @@ import React, { useContext } from 'react'; -import FileList from 'components/FileList'; +import ItemList from 'components/ItemList'; import { Typography } from '@mui/material'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import { ResultItemContainer } from './styledComponents'; @@ -26,6 +26,23 @@ export const ResultSection = (props: ResultSectionProps) => { if (!fileList?.length) { return <>; } + + const renderListItem = (fileID) => { + return ( + + {uploadFileNames.get(fileID)} + + ); + }; + + const getItemTitle = (fileID) => { + return uploadFileNames.get(fileID); + }; + + const generateItemKey = (fileID) => { + return fileID; + }; + return ( }> @@ -35,12 +52,13 @@ export const ResultSection = (props: ResultSectionProps) => { {props.sectionInfo && ( {props.sectionInfo} )} - ( - - {uploadFileNames.get(fileID)} - - ))} + From 551bdad89f9faa66239189698fb93455e23d9ebf Mon Sep 17 00:00:00 2001 From: Abhinav Date: Mon, 21 Aug 2023 19:02:37 +0530 Subject: [PATCH 04/13] fix dialog width issue --- apps/photos/src/components/AuthenticateUserModal.tsx | 3 +-- apps/photos/src/components/DialogBoxV2/index.tsx | 12 +++++++----- apps/photos/src/components/ExportPendingList.tsx | 6 ++++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/apps/photos/src/components/AuthenticateUserModal.tsx b/apps/photos/src/components/AuthenticateUserModal.tsx index 6c5b6921a..159235ba1 100644 --- a/apps/photos/src/components/AuthenticateUserModal.tsx +++ b/apps/photos/src/components/AuthenticateUserModal.tsx @@ -72,8 +72,7 @@ export default function AuthenticateUserModal({ sx={{ position: 'absolute' }} attributes={{ title: t('PASSWORD'), - }} - PaperProps={{ sx: { padding: '8px 12px', maxWidth: '320px' } }}> + }}> & { + Omit & { onClose: () => void; attributes: DialogBoxAttributesV2; - size?: Breakpoint; } >; @@ -39,17 +37,21 @@ export default function DialogBoxV2({ onClose: onClose, }); + const { PaperProps, ...rest } = props; + return ( + {...rest}> {attributes.icon && ( diff --git a/apps/photos/src/components/ExportPendingList.tsx b/apps/photos/src/components/ExportPendingList.tsx index 09a1c2ac9..45b830c55 100644 --- a/apps/photos/src/components/ExportPendingList.tsx +++ b/apps/photos/src/components/ExportPendingList.tsx @@ -50,14 +50,16 @@ const ExportPendingList = (props: Iprops) => { + }}> Date: Mon, 21 Aug 2023 19:22:12 +0530 Subject: [PATCH 05/13] fix ellipsis-ing --- .../src/components/ExportPendingList.tsx | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/apps/photos/src/components/ExportPendingList.tsx b/apps/photos/src/components/ExportPendingList.tsx index 45b830c55..ac061ab0a 100644 --- a/apps/photos/src/components/ExportPendingList.tsx +++ b/apps/photos/src/components/ExportPendingList.tsx @@ -1,12 +1,11 @@ import { EnteFile } from 'types/file'; -import { ResultItemContainer } from './Upload/UploadProgress/styledComponents'; import ItemList from 'components/ItemList'; import DialogBoxV2 from './DialogBoxV2'; import { t } from 'i18next'; import { FlexWrapper } from './Container'; import CollectionCard from './Collections/CollectionCard'; import { ResultPreviewTile } from './Collections/styledComponents'; -import { Box } from '@mui/material'; +import { Box, styled } from '@mui/material'; interface Iprops { isOpen: boolean; @@ -15,24 +14,34 @@ interface Iprops { pendingExports: EnteFile[]; } +export const ItemContainer = styled('div')` + position: relative; + top: 5px; + display: inline-block; + max-width: 394px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +`; + const ExportPendingList = (props: Iprops) => { const renderListItem = (file: EnteFile) => { return ( - - - - null} - collectionTile={ResultPreviewTile} - /> - + + + null} + collectionTile={ResultPreviewTile} + /> + + {`${props.collectionNameMap.get(file.collectionID)} / ${ file.metadata.title }`} - - + + ); }; From 3b2b524ed97631371087b4115e4ef42f6dbb4f0e Mon Sep 17 00:00:00 2001 From: Abhinav Date: Mon, 21 Aug 2023 19:26:04 +0530 Subject: [PATCH 06/13] fix pending item title --- apps/photos/src/components/ExportPendingList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/photos/src/components/ExportPendingList.tsx b/apps/photos/src/components/ExportPendingList.tsx index ac061ab0a..4de889e55 100644 --- a/apps/photos/src/components/ExportPendingList.tsx +++ b/apps/photos/src/components/ExportPendingList.tsx @@ -46,7 +46,7 @@ const ExportPendingList = (props: Iprops) => { }; const getItemTitle = (file: EnteFile) => { - return `${props.collectionNameMap.get(file.collectionID)} - ${ + return `${props.collectionNameMap.get(file.collectionID)} / ${ file.metadata.title }`; }; From 7309f89a58b84cbd279d89e096343722bd2c1824 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Mon, 21 Aug 2023 19:30:07 +0530 Subject: [PATCH 07/13] disable user-select on collection tile and preview card --- apps/photos/src/components/Collections/styledComponents.ts | 1 + apps/photos/src/components/pages/gallery/PreviewCard.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/photos/src/components/Collections/styledComponents.ts b/apps/photos/src/components/Collections/styledComponents.ts index 0719ac62e..1ecfb0dfa 100644 --- a/apps/photos/src/components/Collections/styledComponents.ts +++ b/apps/photos/src/components/Collections/styledComponents.ts @@ -44,6 +44,7 @@ export const CollectionTile = styled('div')` height: 100%; pointer-events: none; } + user-select: none; `; export const ActiveIndicator = styled('div')` diff --git a/apps/photos/src/components/pages/gallery/PreviewCard.tsx b/apps/photos/src/components/pages/gallery/PreviewCard.tsx index b970629ce..1f530c1c3 100644 --- a/apps/photos/src/components/pages/gallery/PreviewCard.tsx +++ b/apps/photos/src/components/pages/gallery/PreviewCard.tsx @@ -197,7 +197,7 @@ const Cont = styled('div')<{ disabled: boolean }>` position: relative; flex: 1; cursor: ${(props) => (props.disabled ? 'not-allowed' : 'pointer')}; - + user-select: none; & > img { object-fit: cover; max-width: 100%; From cc77c1293b78f183df69baa5f919b30fda2dd3e7 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Mon, 21 Aug 2023 19:40:31 +0530 Subject: [PATCH 08/13] fix getPendingExports call --- apps/photos/src/components/ExportModal.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/photos/src/components/ExportModal.tsx b/apps/photos/src/components/ExportModal.tsx index 968acc7e5..f3a23174c 100644 --- a/apps/photos/src/components/ExportModal.tsx +++ b/apps/photos/src/components/ExportModal.tsx @@ -132,7 +132,9 @@ export default function ExportModal(props: Props) { ); setExportStage(exportRecord.stage); setLastExportTime(exportRecord.lastAttemptTimestamp); - const pendingExports = await exportService.getPendingExports(null); + const pendingExports = await exportService.getPendingExports( + exportRecord + ); setPendingExports(pendingExports); } catch (e) { if (e.message !== CustomError.EXPORT_FOLDER_DOES_NOT_EXIST) { From 24238f5e192c390c82de6ee50bedf30a6f8305c4 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Mon, 21 Aug 2023 20:02:48 +0530 Subject: [PATCH 09/13] consider only pending export as pending item and create separate state for trashing and renaming collection and files --- .../photos/public/locales/en/translation.json | 39 +++++++------- .../src/components/ExportInProgress.tsx | 23 ++++++-- apps/photos/src/components/ExportModal.tsx | 6 ++- apps/photos/src/constants/export.ts | 8 ++- apps/photos/src/pages/_app.tsx | 2 +- apps/photos/src/services/export/index.ts | 54 ++++--------------- 6 files changed, 63 insertions(+), 69 deletions(-) diff --git a/apps/photos/public/locales/en/translation.json b/apps/photos/public/locales/en/translation.json index 9be07e5cb..ac91908cf 100644 --- a/apps/photos/public/locales/en/translation.json +++ b/apps/photos/public/locales/en/translation.json @@ -232,14 +232,14 @@ "CAPTION_PLACEHOLDER": "Add a description", "LOCATION": "Location", "SHOW_ON_MAP": "View on OpenStreetMap", - "MAP":"Map", - "MAP_SETTINGS":"Map Settings", - "ENABLE_MAPS":"Enable Maps?", - "ENABLE_MAP":"Enable map", - "DISABLE_MAPS":"Disable Maps?", - "ENABLE_MAP_DESCRIPTION":"

This will show your photos on a world map.

The map is hosted by OpenStreetMap, and the exact locations of your photos are never shared.

You can disable this feature anytime from Settings.

", - "DISABLE_MAP_DESCRIPTION":"

This will disable the display of your photos on a world map.

You can enable this feature anytime from Settings.

", - "DISABLE_MAP":"Disable map", + "MAP": "Map", + "MAP_SETTINGS": "Map Settings", + "ENABLE_MAPS": "Enable Maps?", + "ENABLE_MAP": "Enable map", + "DISABLE_MAPS": "Disable Maps?", + "ENABLE_MAP_DESCRIPTION": "

This will show your photos on a world map.

The map is hosted by OpenStreetMap, and the exact locations of your photos are never shared.

You can disable this feature anytime from Settings.

", + "DISABLE_MAP_DESCRIPTION": "

This will disable the display of your photos on a world map.

You can enable this feature anytime from Settings.

", + "DISABLE_MAP": "Disable map", "DETAILS": "Details", "VIEW_EXIF": "View all EXIF data", "NO_EXIF": "No EXIF data", @@ -357,24 +357,24 @@ "MODIFY_SHARING": "Modify sharing", "ADD_COLLABORATORS": "Add collaborators", "ADD_NEW_EMAIL": "Add a new email", - "shared_with_people_zero" :"Share with specific people", + "shared_with_people_zero": "Share with specific people", "shared_with_people_one": "Shared with 1 person", "shared_with_people_other": "Shared with {{count, number}} people", "participants_zero": "No participants", "participants_one": "1 participant", "participants_other": "{{count, number}} participants", - "ADD_VIEWERS":"Add viewers", + "ADD_VIEWERS": "Add viewers", "PARTICIPANTS": "Participants", "CHANGE_PERMISSIONS_TO_VIEWER": "

{{selectedEmail}} will not be able to add more photos to the album

They will still be able to remove photos added by them

", "CHANGE_PERMISSIONS_TO_COLLABORATOR": "{{selectedEmail}} will be able to add photos to the album", "CONVERT_TO_VIEWER": "Yes, convert to viewer", "CONVERT_TO_COLLABORATOR": "Yes, convert to collaborator", - "CHANGE_PERMISSION":"Change permission?", + "CHANGE_PERMISSION": "Change permission?", "REMOVE_PARTICIPANT": "Remove?", - "CONFIRM_REMOVE":"Yes, remove", - "MANAGE":"Manage", - "ADDED_AS":"Added as", - "COLLABORATOR_RIGHTS":"Collaborators can add photos and videos to the shared album", + "CONFIRM_REMOVE": "Yes, remove", + "MANAGE": "Manage", + "ADDED_AS": "Added as", + "COLLABORATOR_RIGHTS": "Collaborators can add photos and videos to the shared album", "REMOVE_PARTICIPANT_HEAD": "Remove participant", "OWNER": "Owner", "COLLABORATORS": "Collaborators", @@ -528,6 +528,9 @@ "STOP_EXPORT": "Stop", "EXPORT_PROGRESS": "{{progress.success}} / {{progress.total}} items synced", "MIGRATING_EXPORT": "Preparing...", + "RENAMING_COLLECTION_FOLDERS": "Renaming album folders", + "TRASHING_DELETED_FILES": "Trashing deleted files", + "TRASHING_DELETED_COLLECTIONS": "Trashing deleted albums", "EXPORT_NOTIFICATION": { "START": "Export started", "IN_PROGRESS": "Export already in progress", @@ -564,7 +567,7 @@ "NEWEST_FIRST": "Newest first", "OLDEST_FIRST": "Oldest first", "CONVERSION_FAILED_NOTIFICATION_MESSAGE": "This file could not be previewed. Click here to download the original.", - "SELECT_COLLECTION":"Select album", - "PIN_ALBUM":"Pin album", - "UNPIN_ALBUM":"Unpin album" + "SELECT_COLLECTION": "Select album", + "PIN_ALBUM": "Pin album", + "UNPIN_ALBUM": "Unpin album" } diff --git a/apps/photos/src/components/ExportInProgress.tsx b/apps/photos/src/components/ExportInProgress.tsx index 7c5de61ea..cb3fec073 100644 --- a/apps/photos/src/components/ExportInProgress.tsx +++ b/apps/photos/src/components/ExportInProgress.tsx @@ -27,16 +27,33 @@ interface Props { } export default function ExportInProgress(props: Props) { - const isLoading = props.exportProgress.total === 0; + const showIndeterminateProgress = () => { + return ( + props.exportStage === ExportStage.STARTING || + props.exportStage === ExportStage.MIGRATION || + props.exportStage === ExportStage.RENAMING_COLLECTION_FOLDERS || + props.exportStage === ExportStage.TRASHING_DELETED_FILES || + props.exportStage === ExportStage.TRASHING_DELETED_COLLECTIONS + ); + }; return ( <> - {isLoading ? ( + {props.exportStage === ExportStage.STARTING ? ( t('EXPORT_STARTING') ) : props.exportStage === ExportStage.MIGRATION ? ( t('MIGRATING_EXPORT') + ) : props.exportStage === + ExportStage.RENAMING_COLLECTION_FOLDERS ? ( + t('RENAMING_COLLECTION_FOLDERS') + ) : props.exportStage === + ExportStage.TRASHING_DELETED_FILES ? ( + t('TRASHING_DELETED_FILES') + ) : props.exportStage === + ExportStage.TRASHING_DELETED_COLLECTIONS ? ( + t('TRASHING_DELETED_COLLECTIONS') ) : ( ; - case ExportStage.INPROGRESS: case ExportStage.MIGRATION: + case ExportStage.STARTING: + case ExportStage.EXPORTING_FILES: + case ExportStage.RENAMING_COLLECTION_FOLDERS: + case ExportStage.TRASHING_DELETED_FILES: + case ExportStage.TRASHING_DELETED_COLLECTIONS: return ( { this.updateExportProgress({ success: ++success, failed: failed, - total: - removedFileUIDs.length + - filesToExport.length + - deletedExportedCollections.length + - renamedCollections.length, + total: filesToExport.length, }); }; const incrementFailed = () => { this.updateExportProgress({ success: success, failed: ++failed, - total: - removedFileUIDs.length + - filesToExport.length + - deletedExportedCollections.length + - renamedCollections.length, + total: filesToExport.length, }); }; if (renamedCollections?.length > 0) { + this.updateExportStage(ExportStage.RENAMING_COLLECTION_FOLDERS); addLogLine(`renaming ${renamedCollections.length} collections`); await this.collectionRenamer( exportFolder, collectionIDExportNameMap, renamedCollections, - incrementSuccess, - incrementFailed, isCanceled ); } if (removedFileUIDs?.length > 0) { + this.updateExportStage(ExportStage.TRASHING_DELETED_FILES); addLogLine(`trashing ${removedFileUIDs.length} files`); await this.fileTrasher( exportFolder, collectionIDExportNameMap, removedFileUIDs, - incrementSuccess, - incrementFailed, isCanceled ); } if (filesToExport?.length > 0) { + this.updateExportStage(ExportStage.EXPORTING_FILES); addLogLine(`exporting ${filesToExport.length} files`); await this.fileExporter( filesToExport, @@ -472,14 +449,15 @@ class ExportService { ); } if (deletedExportedCollections?.length > 0) { + this.updateExportStage( + ExportStage.TRASHING_DELETED_COLLECTIONS + ); addLogLine( `removing ${deletedExportedCollections.length} collections` ); await this.collectionRemover( deletedExportedCollections, exportFolder, - incrementSuccess, - incrementFailed, isCanceled ); } @@ -498,8 +476,6 @@ class ExportService { exportFolder: string, collectionIDExportNameMap: Map, renamedCollections: Collection[], - incrementSuccess: () => void, - incrementFailed: () => void, isCanceled: CancellationStatus ) { try { @@ -546,9 +522,7 @@ class ExportService { addLogLine( `renaming collection with id ${collection.id} from ${oldCollectionExportName} to ${newCollectionExportName} successful` ); - incrementSuccess(); } catch (e) { - incrementFailed(); logError(e, 'collectionRenamer failed a collection'); if ( e.message === @@ -575,8 +549,6 @@ class ExportService { async collectionRemover( deletedExportedCollectionIDs: number[], exportFolder: string, - incrementSuccess: () => void, - incrementFailed: () => void, isCanceled: CancellationStatus ) { try { @@ -623,9 +595,7 @@ class ExportService { addLogLine( `removing collection with id ${collectionID} from export folder successful` ); - incrementSuccess(); } catch (e) { - incrementFailed(); logError(e, 'collectionRemover failed a collection'); if ( e.message === @@ -745,8 +715,6 @@ class ExportService { exportDir: string, collectionIDExportNameMap: Map, removedFileUIDs: string[], - incrementSuccess: () => void, - incrementFailed: () => void, isCanceled: CancellationStatus ): Promise { try { @@ -844,9 +812,7 @@ class ExportService { } await this.removeFileExportedRecord(exportDir, fileUID); addLogLine(`trashing file with id ${fileUID} successful`); - incrementSuccess(); } catch (e) { - incrementFailed(); logError(e, 'trashing failed for a file'); if ( e.message === From 8eec2ae597ad7fa3d8e4d1ca8fd4d15277a01b6f Mon Sep 17 00:00:00 2001 From: Abhinav Date: Mon, 21 Aug 2023 20:09:31 +0530 Subject: [PATCH 10/13] update string --- apps/photos/public/locales/en/translation.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/photos/public/locales/en/translation.json b/apps/photos/public/locales/en/translation.json index ac91908cf..005e33662 100644 --- a/apps/photos/public/locales/en/translation.json +++ b/apps/photos/public/locales/en/translation.json @@ -528,9 +528,9 @@ "STOP_EXPORT": "Stop", "EXPORT_PROGRESS": "{{progress.success}} / {{progress.total}} items synced", "MIGRATING_EXPORT": "Preparing...", - "RENAMING_COLLECTION_FOLDERS": "Renaming album folders", - "TRASHING_DELETED_FILES": "Trashing deleted files", - "TRASHING_DELETED_COLLECTIONS": "Trashing deleted albums", + "RENAMING_COLLECTION_FOLDERS": "Renaming album folders...", + "TRASHING_DELETED_FILES": "Trashing deleted files...", + "TRASHING_DELETED_COLLECTIONS": "Trashing deleted albums...", "EXPORT_NOTIFICATION": { "START": "Export started", "IN_PROGRESS": "Export already in progress", From e33162c5d71466f22028a67c39e6d624c0463456 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Mon, 21 Aug 2023 20:11:33 +0530 Subject: [PATCH 11/13] fix numbering --- apps/photos/src/constants/export.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/photos/src/constants/export.ts b/apps/photos/src/constants/export.ts index c776ab394..2b0fc8804 100644 --- a/apps/photos/src/constants/export.ts +++ b/apps/photos/src/constants/export.ts @@ -4,8 +4,8 @@ export const ENTE_TRASH_FOLDER = 'Trash'; export enum ExportStage { INIT = 0, - MIGRATION = 2, - STARTING = 1, + MIGRATION = 1, + STARTING = 2, EXPORTING_FILES = 3, TRASHING_DELETED_FILES = 4, RENAMING_COLLECTION_FOLDERS = 5, From 51b7eb5e07ed2f437693f63b53728756f5420c1c Mon Sep 17 00:00:00 2001 From: Abhinav Date: Mon, 21 Aug 2023 20:14:16 +0530 Subject: [PATCH 12/13] fix isExport in progress check --- apps/photos/src/pages/_app.tsx | 4 ++-- apps/photos/src/utils/export/index.ts | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/photos/src/pages/_app.tsx b/apps/photos/src/pages/_app.tsx index 74ed46980..db74da47e 100644 --- a/apps/photos/src/pages/_app.tsx +++ b/apps/photos/src/pages/_app.tsx @@ -75,9 +75,9 @@ import { getAppNameAndTitle, } from 'constants/apps'; import exportService from 'services/export'; -import { ExportStage } from 'constants/export'; import { REDIRECTS } from 'constants/redirects'; import { getLocalMapEnabled, setLocalMapEnabled } from 'utils/storage'; +import { isExportInProgress } from 'utils/export'; const redirectMap = new Map([ [REDIRECTS.ROADMAP, getRoadmapRedirectURL], @@ -275,7 +275,7 @@ export default function App(props) { if (exportSettings.continuousExport) { exportService.enableContinuousExport(); } - if (exportRecord.stage === ExportStage.EXPORTING_FILES) { + if (isExportInProgress(exportRecord.stage)) { addLogLine('export was in progress, resuming'); exportService.scheduleExport(); } diff --git a/apps/photos/src/utils/export/index.ts b/apps/photos/src/utils/export/index.ts index 3c5cb2f19..8266c8149 100644 --- a/apps/photos/src/utils/export/index.ts +++ b/apps/photos/src/utils/export/index.ts @@ -10,7 +10,11 @@ import { EnteFile } from 'types/file'; import { Metadata } from 'types/upload'; import { splitFilenameAndExtension } from 'utils/file'; -import { ENTE_METADATA_FOLDER, ENTE_TRASH_FOLDER } from 'constants/export'; +import { + ENTE_METADATA_FOLDER, + ENTE_TRASH_FOLDER, + ExportStage, +} from 'constants/export'; import sanitize from 'sanitize-filename'; import { formatDateTimeShort } from 'utils/time/format'; import { HIDDEN_COLLECTION_NAME } from 'services/collectionService'; @@ -305,3 +309,6 @@ export const parseLivePhotoExportName = ( const { image, video } = JSON.parse(livePhotoExportName); return { image, video }; }; + +export const isExportInProgress = (exportStage: ExportStage) => + exportStage > ExportStage.INIT && exportStage < ExportStage.FINISHED; From f2bdb6459b0a7bc6b124dacb71cbb822214d9801 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Mon, 21 Aug 2023 20:15:33 +0530 Subject: [PATCH 13/13] only show link button component if has pending items --- apps/photos/src/components/ExportFinished.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/apps/photos/src/components/ExportFinished.tsx b/apps/photos/src/components/ExportFinished.tsx index 76b07a164..40944cf7a 100644 --- a/apps/photos/src/components/ExportFinished.tsx +++ b/apps/photos/src/components/ExportFinished.tsx @@ -41,9 +41,15 @@ export default function ExportFinished(props: Props) { {t('PENDING_ITEMS')} - - {formatNumber(props.pendingExports.length)} - + {props.pendingExports.length ? ( + + {formatNumber(props.pendingExports.length)} + + ) : ( + + {formatNumber(props.pendingExports.length)} + + )}