From 1d310babeeb2581d248a53cb8a3ab7d8a2b5c1b2 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Wed, 25 May 2022 15:35:48 +0530 Subject: [PATCH 001/295] init watch --- src/components/pages/gallery/Upload.tsx | 11 + src/services/upload/uploadManager.ts | 13 + src/services/watchService.ts | 319 ++++++++++++++++++++++++ 3 files changed, 343 insertions(+) create mode 100644 src/services/watchService.ts diff --git a/src/components/pages/gallery/Upload.tsx b/src/components/pages/gallery/Upload.tsx index cccaa9556..20841eac4 100644 --- a/src/components/pages/gallery/Upload.tsx +++ b/src/components/pages/gallery/Upload.tsx @@ -25,6 +25,7 @@ import UploadTypeSelector from '../../UploadTypeSelector'; import Router from 'next/router'; import { isCanvasBlocked } from 'utils/upload/isCanvasBlocked'; import { downloadApp } from 'utils/common'; +import watchService from 'services/watchService'; const FIRST_ALBUM_NAME = 'My First Album'; @@ -116,6 +117,12 @@ export default function Upload(props: Props) { resumeDesktopUpload(type, electronFiles, collectionName); } ); + watchService.setElectronFiles = props.setElectronFiles; + watchService.setCollectionName = (collectionName: string) => { + isPendingDesktopUpload.current = true; + pendingDesktopUploadCollectionName.current = collectionName; + }; + watchService.init(); } }, []); @@ -349,6 +356,10 @@ export default function Upload(props: Props) { filesWithCollectionToUpload, collections ); + await watchService.allUploadsDone( + filesWithCollectionToUpload, + collections + ); } catch (err) { const message = getUserFacingErrorMessage( err.message, diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index 8fbc6fd36..802220ece 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -39,6 +39,7 @@ import uiService from './uiService'; import { logUploadInfo } from 'utils/upload'; import isElectron from 'is-electron'; import ImportService from 'services/importService'; +import watchService from 'services/watchService'; const MAX_CONCURRENT_UPLOADS = 4; const FILE_UPLOAD_COMPLETED = 100; @@ -399,6 +400,18 @@ class UploadManager { !areFileWithCollectionsSame(file, fileWithCollection) ); ImportService.updatePendingUploads(this.remainingFiles); + + if ( + fileUploadResult === FileUploadResults.UPLOADED || + fileUploadResult === + FileUploadResults.UPLOADED_WITH_STATIC_THUMBNAIL || + fileUploadResult === FileUploadResults.ALREADY_UPLOADED + ) { + await watchService.fileUploadDone( + fileWithCollection, + uploadedFile + ); + } } } catch (e) { logError(e, 'failed to do post file upload action'); diff --git a/src/services/watchService.ts b/src/services/watchService.ts new file mode 100644 index 000000000..d9fe8b20b --- /dev/null +++ b/src/services/watchService.ts @@ -0,0 +1,319 @@ +import { Collection } from 'types/collection'; +import { EnteFile } from 'types/file'; +import { ElectronFile, FileWithCollection } from 'types/upload'; +import { runningInBrowser } from 'utils/common'; +import { syncCollections } from './collectionService'; +import { syncFiles, trashFiles } from './fileService'; + +interface Mapping { + collectionName: string; + folderPath: string; + files: { + path: string; + id: number; + }[]; +} + +interface UploadQueueType { + collectionName: string; + paths: string[]; +} + +class WatchService { + ElectronAPIs: any; + allElectronAPIsExist: boolean = false; + uploadQueue: UploadQueueType[] = []; + isUploadRunning: boolean = false; + pathToIDMap = new Map(); + setElectronFiles: (files: ElectronFile[]) => void; + setCollectionName: (collectionName: string) => void; + + constructor() { + this.ElectronAPIs = runningInBrowser() && window['ElectronAPIs']; + this.allElectronAPIsExist = !!this.ElectronAPIs?.getWatchMappings; + } + + async init() { + if (this.allElectronAPIsExist) { + const mappings: Mapping[] = + await this.ElectronAPIs.getWatchMappings(); + + console.log('mappings', mappings); + + if (!mappings) { + return; + } + + for (const mapping of mappings) { + const filePathsOnDisk: string[] = + await this.ElectronAPIs.getFilePathsFromDir( + mapping.folderPath + ); + + const filesToUpload = filePathsOnDisk.filter((filePath) => { + return !mapping.files.find( + (file) => file.path === filePath + ); + }); + + const filesToRemove = mapping.files.filter((file) => { + return !filePathsOnDisk.find( + (filePath) => filePath === file.path + ); + }); + + if (filesToUpload.length > 0) { + const event: UploadQueueType = { + collectionName: mapping.collectionName, + paths: filesToUpload, + }; + this.uploadQueue.push(event); + } + + if (filesToRemove.length > 0) { + await this.trashByIDs( + filesToRemove, + mapping.collectionName + ); + mapping.files = mapping.files.filter( + (file) => + !filesToRemove.find( + (fileToRemove) => + file.path === fileToRemove.path + ) + ); + } + + this.runNextUpload(); + } + + this.ElectronAPIs.setWatchMappings(mappings); + this.setWatchFunctions(); + } + } + + async addWatchMapping(collectionName: string, folderPath: string) { + await this.ElectronAPIs.addWatchMapping(collectionName, folderPath); + } + + async removeWatchMapping(collectionName: string) { + await this.ElectronAPIs.removeWatchMapping(collectionName); + } + + async runNextUpload() { + if (this.uploadQueue.length === 0 || this.isUploadRunning) { + return; + } + + this.setCollectionName(this.uploadQueue[0].collectionName); + this.setElectronFiles( + await Promise.all( + this.uploadQueue[0].paths.map(async (path) => { + return await this.ElectronAPIs.getElectronFile(path); + }) + ) + ); + + this.isUploadRunning = true; + } + + async fileUploadDone( + fileWithCollection: FileWithCollection, + file: EnteFile + ) { + if (fileWithCollection.isLivePhoto) { + this.pathToIDMap.set( + (fileWithCollection.livePhotoAssets.image as ElectronFile).path, + file.id + ); + this.pathToIDMap.set( + (fileWithCollection.livePhotoAssets.video as ElectronFile).path, + file.id + ); + } else { + this.pathToIDMap.set( + (fileWithCollection.file as ElectronFile).path, + file.id + ); + } + } + + async allUploadsDone( + filesWithCollection: FileWithCollection[], + collections: Collection[] + ) { + if (this.allElectronAPIsExist) { + const collection = collections.find( + (collection) => + collection.id === filesWithCollection[0].collectionID + ); + if ( + !this.isUploadRunning || + this.uploadQueue.length === 0 || + this.uploadQueue[0].collectionName !== collection?.name + ) { + return; + } + + const uploadedFiles: Mapping['files'] = []; + for (const fileWithCollection of filesWithCollection) { + if (fileWithCollection.isLivePhoto) { + const imagePath = ( + fileWithCollection.livePhotoAssets.image as ElectronFile + ).path; + const videoPath = ( + fileWithCollection.livePhotoAssets.video as ElectronFile + ).path; + if ( + this.pathToIDMap.has(imagePath) && + this.pathToIDMap.has(videoPath) + ) { + uploadedFiles.push({ + path: imagePath, + id: this.pathToIDMap.get(imagePath), + }); + uploadedFiles.push({ + path: videoPath, + id: this.pathToIDMap.get(videoPath), + }); + + this.pathToIDMap.delete(imagePath); + this.pathToIDMap.delete(videoPath); + } + } else { + const filePath = (fileWithCollection.file as ElectronFile) + .path; + if (this.pathToIDMap.has(filePath)) { + uploadedFiles.push({ + path: filePath, + id: this.pathToIDMap.get(filePath), + }); + + this.pathToIDMap.delete(filePath); + } + } + } + + console.log('uploadedFiles', uploadedFiles); + + if (uploadedFiles.length > 0) { + const mappings: Mapping[] = + await this.ElectronAPIs.getWatchMappings(); + const mapping = mappings.find( + (mapping) => + mapping.collectionName === + this.uploadQueue[0].collectionName + ); + mapping.files = [...mapping.files, ...uploadedFiles]; + + console.log('new mappings', mappings); + + await this.ElectronAPIs.setWatchMappings(mappings); + + console.log( + 'now mappings', + await this.ElectronAPIs.getWatchMappings() + ); + } + + this.uploadQueue.shift(); + this.isUploadRunning = false; + this.runNextUpload(); + } + } + + setWatchFunctions() { + if (this.allElectronAPIsExist) { + this.ElectronAPIs.registerWatcherFunctions( + this, + diskFileAddedCallback, + diskFileRemovedCallback + ); + } + } + + async trashByIDs(toTrashFiles: Mapping['files'], collectionName: string) { + if (this.allElectronAPIsExist) { + const collections = await syncCollections(); + const collectionID = collections.find( + (collection) => collection.name === collectionName + )?.id; + if (!collectionID) { + return; + } + const files = await syncFiles(collections, () => {}); + + const idSet = new Set(); + for (const file of toTrashFiles) { + idSet.add(file.id); + } + + const filesToTrash = files.filter((file) => { + return idSet.has(file.id) && file.collectionID === collectionID; + }); + + await trashFiles(filesToTrash); + } + } + + async getCollectionName(filePath: string) { + const mappings: Mapping[] = await this.ElectronAPIs.getWatchMappings(); + + console.log('mappings', mappings, filePath); + + const collectionName = mappings.find((mapping) => + filePath.startsWith(mapping.folderPath) + )?.collectionName; + + if (!collectionName) { + return null; + } + + return collectionName; + } +} + +async function diskFileAddedCallback(w: WatchService, filePath: string) { + console.log('diskFileAddedCallback', w, filePath); + const collectionName = await w.getCollectionName(filePath); + + const event: UploadQueueType = { + collectionName, + paths: [filePath], + }; + w.uploadQueue.push(event); + w.runNextUpload(); +} + +async function diskFileRemovedCallback(w: WatchService, filePath: string) { + const collectionName = await w.getCollectionName(filePath); + + console.log('collection', collectionName); + + if (!collectionName) { + return; + } + + const mappings: Mapping[] = await w.ElectronAPIs.getWatchMappings(); + + const mapping = mappings.find( + (mapping) => mapping.collectionName === collectionName + ); + if (!mapping) { + return; + } + + const file = mapping.files.find((file) => file.path === filePath); + if (!file) { + return; + } + + await w.trashByIDs([file], collectionName); + + mapping.files = mapping.files.filter((file) => file.path !== filePath); + await w.ElectronAPIs.setWatchMappings(mappings); + + console.log('after trash', w.ElectronAPIs.getWatchMappings()); +} + +export default new WatchService(); From 440c5bf4f9b972119fae164e5006fe33cd7ba8ac Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Thu, 2 Jun 2022 18:48:43 +0530 Subject: [PATCH 002/295] return the file when upload is skipped since the file id is needed for watch service --- src/services/upload/uploader.ts | 16 +++++++++++++--- src/utils/upload/index.ts | 6 +++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/services/upload/uploader.ts b/src/services/upload/uploader.ts index b1cffab53..166a6e454 100644 --- a/src/services/upload/uploader.ts +++ b/src/services/upload/uploader.ts @@ -50,9 +50,16 @@ export default async function uploader( throw Error(CustomError.NO_METADATA); } - if (fileAlreadyInCollection(existingFilesInCollection, metadata)) { + const sameFileInSameCollection = fileAlreadyInCollection( + existingFilesInCollection, + metadata + ); + if (sameFileInSameCollection) { logUploadInfo(`skipped upload for ${fileNameSize}`); - return { fileUploadResult: FileUploadResults.ALREADY_UPLOADED }; + return { + fileUploadResult: FileUploadResults.ALREADY_UPLOADED, + uploadedFile: sameFileInSameCollection, + }; } const sameFileInOtherCollection = findSameFileInOtherCollection( @@ -82,7 +89,10 @@ export default async function uploader( fileAlreadyInCollection(existingFiles, metadata) ) { logUploadInfo(`deduped upload for ${fileNameSize}`); - return { fileUploadResult: FileUploadResults.ALREADY_UPLOADED }; + return { + fileUploadResult: FileUploadResults.ALREADY_UPLOADED, + uploadedFile: fileAlreadyInCollection(existingFiles, metadata), + }; } logUploadInfo(`reading asset ${fileNameSize}`); diff --git a/src/utils/upload/index.ts b/src/utils/upload/index.ts index cf8d8c228..126684840 100644 --- a/src/utils/upload/index.ts +++ b/src/utils/upload/index.ts @@ -12,13 +12,13 @@ const DEDUPE_COLLECTION = new Set(['icloud library', 'icloudlibrary']); export function fileAlreadyInCollection( existingFilesInCollection: EnteFile[], newFileMetadata: Metadata -): boolean { +): EnteFile { for (const existingFile of existingFilesInCollection) { if (areFilesSame(existingFile.metadata, newFileMetadata)) { - return true; + return existingFile; } } - return false; + return null; } export function findSameFileInOtherCollection( From df5ecdf04665dc5e9a7c27f4c8e46a3c14da8972 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Thu, 2 Jun 2022 20:08:54 +0530 Subject: [PATCH 003/295] add watch modal --- src/components/WatchModal.tsx | 8 ++++ src/components/pages/gallery/Upload.tsx | 1 + src/services/watchService.ts | 52 ++++++++++++++----------- 3 files changed, 39 insertions(+), 22 deletions(-) create mode 100644 src/components/WatchModal.tsx diff --git a/src/components/WatchModal.tsx b/src/components/WatchModal.tsx new file mode 100644 index 000000000..1926da746 --- /dev/null +++ b/src/components/WatchModal.tsx @@ -0,0 +1,8 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import React from 'react'; + +function WatchModal({ watchModalView, setWatchModalView }) { + return
WatchModal
; +} + +export default WatchModal; diff --git a/src/components/pages/gallery/Upload.tsx b/src/components/pages/gallery/Upload.tsx index 20841eac4..12c635d6e 100644 --- a/src/components/pages/gallery/Upload.tsx +++ b/src/components/pages/gallery/Upload.tsx @@ -122,6 +122,7 @@ export default function Upload(props: Props) { isPendingDesktopUpload.current = true; pendingDesktopUploadCollectionName.current = collectionName; }; + watchService.syncWithRemote = props.syncWithRemote; watchService.init(); } }, []); diff --git a/src/services/watchService.ts b/src/services/watchService.ts index d9fe8b20b..fda2ea429 100644 --- a/src/services/watchService.ts +++ b/src/services/watchService.ts @@ -27,6 +27,7 @@ class WatchService { pathToIDMap = new Map(); setElectronFiles: (files: ElectronFile[]) => void; setCollectionName: (collectionName: string) => void; + syncWithRemote: () => void; constructor() { this.ElectronAPIs = runningInBrowser() && window['ElectronAPIs']; @@ -89,6 +90,17 @@ class WatchService { this.ElectronAPIs.setWatchMappings(mappings); this.setWatchFunctions(); + this.syncWithRemote(); + } + } + + setWatchFunctions() { + if (this.allElectronAPIsExist) { + this.ElectronAPIs.registerWatcherFunctions( + this, + diskFileAddedCallback, + diskFileRemovedCallback + ); } } @@ -208,7 +220,8 @@ class WatchService { console.log('new mappings', mappings); - await this.ElectronAPIs.setWatchMappings(mappings); + this.ElectronAPIs.setWatchMappings(mappings); + this.syncWithRemote(); console.log( 'now mappings', @@ -222,16 +235,6 @@ class WatchService { } } - setWatchFunctions() { - if (this.allElectronAPIsExist) { - this.ElectronAPIs.registerWatcherFunctions( - this, - diskFileAddedCallback, - diskFileRemovedCallback - ); - } - } - async trashByIDs(toTrashFiles: Mapping['files'], collectionName: string) { if (this.allElectronAPIsExist) { const collections = await syncCollections(); @@ -273,20 +276,23 @@ class WatchService { } } -async function diskFileAddedCallback(w: WatchService, filePath: string) { - console.log('diskFileAddedCallback', w, filePath); - const collectionName = await w.getCollectionName(filePath); +async function diskFileAddedCallback(instance: WatchService, filePath: string) { + console.log('diskFileAddedCallback', instance, filePath); + const collectionName = await instance.getCollectionName(filePath); const event: UploadQueueType = { collectionName, paths: [filePath], }; - w.uploadQueue.push(event); - w.runNextUpload(); + instance.uploadQueue.push(event); + instance.runNextUpload(); } -async function diskFileRemovedCallback(w: WatchService, filePath: string) { - const collectionName = await w.getCollectionName(filePath); +async function diskFileRemovedCallback( + instance: WatchService, + filePath: string +) { + const collectionName = await instance.getCollectionName(filePath); console.log('collection', collectionName); @@ -294,7 +300,7 @@ async function diskFileRemovedCallback(w: WatchService, filePath: string) { return; } - const mappings: Mapping[] = await w.ElectronAPIs.getWatchMappings(); + let mappings: Mapping[] = await instance.ElectronAPIs.getWatchMappings(); const mapping = mappings.find( (mapping) => mapping.collectionName === collectionName @@ -308,12 +314,14 @@ async function diskFileRemovedCallback(w: WatchService, filePath: string) { return; } - await w.trashByIDs([file], collectionName); + await instance.trashByIDs([file], collectionName); + mappings = await instance.ElectronAPIs.getWatchMappings(); mapping.files = mapping.files.filter((file) => file.path !== filePath); - await w.ElectronAPIs.setWatchMappings(mappings); + instance.ElectronAPIs.setWatchMappings(mappings); + instance.syncWithRemote(); - console.log('after trash', w.ElectronAPIs.getWatchMappings()); + console.log('after trash', instance.ElectronAPIs.getWatchMappings()); } export default new WatchService(); From 5361dca3e211d8d1b71193cf47cc9196e8be5e04 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Fri, 3 Jun 2022 16:32:49 +0530 Subject: [PATCH 004/295] added debounce for uploads --- src/services/watchService.ts | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/services/watchService.ts b/src/services/watchService.ts index fda2ea429..a212bdbe2 100644 --- a/src/services/watchService.ts +++ b/src/services/watchService.ts @@ -4,6 +4,7 @@ import { ElectronFile, FileWithCollection } from 'types/upload'; import { runningInBrowser } from 'utils/common'; import { syncCollections } from './collectionService'; import { syncFiles, trashFiles } from './fileService'; +import debounce from 'debounce-promise'; interface Mapping { collectionName: string; @@ -28,6 +29,7 @@ class WatchService { setElectronFiles: (files: ElectronFile[]) => void; setCollectionName: (collectionName: string) => void; syncWithRemote: () => void; + promise: Promise; constructor() { this.ElectronAPIs = runningInBrowser() && window['ElectronAPIs']; @@ -117,6 +119,23 @@ class WatchService { return; } + this.isUploadRunning = true; + + const newUploadQueue = [this.uploadQueue[0]]; + const len = this.uploadQueue.length; + for (let i = 1; i < len; i++) { + if ( + this.uploadQueue[i].collectionName === + newUploadQueue[0].collectionName + ) { + newUploadQueue[0].paths.push(...this.uploadQueue[i].paths); + } else { + newUploadQueue.push(this.uploadQueue[i]); + } + } + newUploadQueue.push(...this.uploadQueue.slice(len)); + this.uploadQueue = newUploadQueue; + this.setCollectionName(this.uploadQueue[0].collectionName); this.setElectronFiles( await Promise.all( @@ -125,8 +144,6 @@ class WatchService { }) ) ); - - this.isUploadRunning = true; } async fileUploadDone( @@ -285,7 +302,8 @@ async function diskFileAddedCallback(instance: WatchService, filePath: string) { paths: [filePath], }; instance.uploadQueue.push(event); - instance.runNextUpload(); + // instance.runNextUpload(); + await debounce(runNextUploadByInstance, 300)(instance); } async function diskFileRemovedCallback( @@ -324,4 +342,8 @@ async function diskFileRemovedCallback( console.log('after trash', instance.ElectronAPIs.getWatchMappings()); } +const runNextUploadByInstance = async (w: WatchService) => { + await w.runNextUpload(); +}; + export default new WatchService(); From c11e9d48784cda715a30e4530bc4a90ef551f496 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Sat, 4 Jun 2022 10:50:34 +0530 Subject: [PATCH 005/295] add watch modal and refactor --- src/components/WatchModal.tsx | 172 +++++++++++++++++++++++++++++++++- src/services/watchService.ts | 34 ++++--- 2 files changed, 192 insertions(+), 14 deletions(-) diff --git a/src/components/WatchModal.tsx b/src/components/WatchModal.tsx index 1926da746..6f6fba78a 100644 --- a/src/components/WatchModal.tsx +++ b/src/components/WatchModal.tsx @@ -1,8 +1,174 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -import React from 'react'; +import React, { useEffect, useState } from 'react'; +import { Button, Modal } from 'react-bootstrap'; +import watchService, { WatchMapping } from 'services/watchService'; +import { MdDelete } from 'react-icons/md'; +import { HiArrowNarrowRight } from 'react-icons/hi'; function WatchModal({ watchModalView, setWatchModalView }) { - return
WatchModal
; + const [mappings, setMappings] = useState([]); + const [shouldUpdateMappings, setShouldUpdateMappings] = useState(true); + const [inputFolderPath, setInputFolderPath] = useState(''); + const [inputCollectionName, setInputCollectionName] = useState(''); + + useEffect(() => { + if (shouldUpdateMappings) { + setMappings(watchService.ElectronAPIs.getWatchMappings()); + setShouldUpdateMappings(false); + } + }, [shouldUpdateMappings]); + + const handleFolderSelection = async () => { + const folderPath = await watchService.selectFolder(); + setInputFolderPath(folderPath); + }; + + const handleCollectionNameChange = ( + e: React.ChangeEvent + ) => { + setInputCollectionName(e.target.value); + }; + + const handleAddWatchMapping = () => { + if (inputFolderPath.length > 0 && inputCollectionName.length > 0) { + watchService.addWatchMapping(inputCollectionName, inputFolderPath); + setInputCollectionName(''); + setInputFolderPath(''); + setShouldUpdateMappings(true); + } + }; + + const handleRemoveWatchMapping = (mapping: WatchMapping) => { + watchService.removeWatchMapping(mapping.collectionName); + setShouldUpdateMappings(true); + }; + + return ( + setWatchModalView(false)}> + + Watch Folders + + +
+ +
+ +
+ {inputFolderPath} +
+
+
+ +
+
+
+ Current Watch Mappings +
+
+ {mappings.map((mapping) => ( +
+
+ + {mapping.folderPath}{' '} + + + + {mapping.collectionName} + +
+
+ + handleRemoveWatchMapping(mapping) + } + /> +
+
+ ))} +
+
+
+ ); } export default WatchModal; diff --git a/src/services/watchService.ts b/src/services/watchService.ts index a212bdbe2..936d14046 100644 --- a/src/services/watchService.ts +++ b/src/services/watchService.ts @@ -6,7 +6,7 @@ import { syncCollections } from './collectionService'; import { syncFiles, trashFiles } from './fileService'; import debounce from 'debounce-promise'; -interface Mapping { +export interface WatchMapping { collectionName: string; folderPath: string; files: { @@ -38,8 +38,7 @@ class WatchService { async init() { if (this.allElectronAPIsExist) { - const mappings: Mapping[] = - await this.ElectronAPIs.getWatchMappings(); + const mappings = this.getWatchMappings(); console.log('mappings', mappings); @@ -49,7 +48,7 @@ class WatchService { for (const mapping of mappings) { const filePathsOnDisk: string[] = - await this.ElectronAPIs.getFilePathsFromDir( + await this.ElectronAPIs.getPosixFilePathsFromDir( mapping.folderPath ); @@ -114,6 +113,12 @@ class WatchService { await this.ElectronAPIs.removeWatchMapping(collectionName); } + getWatchMappings(): WatchMapping[] { + if (this.allElectronAPIsExist) { + return this.ElectronAPIs.getWatchMappings() ?? []; + } + } + async runNextUpload() { if (this.uploadQueue.length === 0 || this.isUploadRunning) { return; @@ -184,7 +189,7 @@ class WatchService { return; } - const uploadedFiles: Mapping['files'] = []; + const uploadedFiles: WatchMapping['files'] = []; for (const fileWithCollection of filesWithCollection) { if (fileWithCollection.isLivePhoto) { const imagePath = ( @@ -226,8 +231,7 @@ class WatchService { console.log('uploadedFiles', uploadedFiles); if (uploadedFiles.length > 0) { - const mappings: Mapping[] = - await this.ElectronAPIs.getWatchMappings(); + const mappings = this.getWatchMappings(); const mapping = mappings.find( (mapping) => mapping.collectionName === @@ -252,7 +256,10 @@ class WatchService { } } - async trashByIDs(toTrashFiles: Mapping['files'], collectionName: string) { + async trashByIDs( + toTrashFiles: WatchMapping['files'], + collectionName: string + ) { if (this.allElectronAPIsExist) { const collections = await syncCollections(); const collectionID = collections.find( @@ -277,7 +284,7 @@ class WatchService { } async getCollectionName(filePath: string) { - const mappings: Mapping[] = await this.ElectronAPIs.getWatchMappings(); + const mappings = this.getWatchMappings(); console.log('mappings', mappings, filePath); @@ -291,6 +298,11 @@ class WatchService { return collectionName; } + + async selectFolder(): Promise { + const folderPath = await this.ElectronAPIs.selectFolder(); + return folderPath; + } } async function diskFileAddedCallback(instance: WatchService, filePath: string) { @@ -318,7 +330,7 @@ async function diskFileRemovedCallback( return; } - let mappings: Mapping[] = await instance.ElectronAPIs.getWatchMappings(); + let mappings = instance.getWatchMappings(); const mapping = mappings.find( (mapping) => mapping.collectionName === collectionName @@ -334,7 +346,7 @@ async function diskFileRemovedCallback( await instance.trashByIDs([file], collectionName); - mappings = await instance.ElectronAPIs.getWatchMappings(); + mappings = instance.getWatchMappings(); mapping.files = mapping.files.filter((file) => file.path !== filePath); instance.ElectronAPIs.setWatchMappings(mappings); instance.syncWithRemote(); From e85c16accbd306ee40faa55e935b8f26566adf75 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Sat, 4 Jun 2022 11:04:50 +0530 Subject: [PATCH 006/295] fix functions --- src/components/WatchModal.tsx | 2 +- src/services/watchService.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/WatchModal.tsx b/src/components/WatchModal.tsx index 6f6fba78a..4e4219e9d 100644 --- a/src/components/WatchModal.tsx +++ b/src/components/WatchModal.tsx @@ -12,7 +12,7 @@ function WatchModal({ watchModalView, setWatchModalView }) { useEffect(() => { if (shouldUpdateMappings) { - setMappings(watchService.ElectronAPIs.getWatchMappings()); + setMappings(watchService.getWatchMappings()); setShouldUpdateMappings(false); } }, [shouldUpdateMappings]); diff --git a/src/services/watchService.ts b/src/services/watchService.ts index 936d14046..842f5223e 100644 --- a/src/services/watchService.ts +++ b/src/services/watchService.ts @@ -105,12 +105,12 @@ class WatchService { } } - async addWatchMapping(collectionName: string, folderPath: string) { - await this.ElectronAPIs.addWatchMapping(collectionName, folderPath); + addWatchMapping(collectionName: string, folderPath: string) { + this.ElectronAPIs.addWatchMapping(collectionName, folderPath); } - async removeWatchMapping(collectionName: string) { - await this.ElectronAPIs.removeWatchMapping(collectionName); + removeWatchMapping(collectionName: string) { + this.ElectronAPIs.removeWatchMapping(collectionName); } getWatchMappings(): WatchMapping[] { From 02f6c353aad9b57cbad00b58407afd1aa9f05729 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Sat, 4 Jun 2022 12:40:14 +0530 Subject: [PATCH 007/295] remove from collection instead of trashing --- src/components/WatchModal.tsx | 11 +++++--- src/services/watchService.ts | 53 ++++++++++++++++++++--------------- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/src/components/WatchModal.tsx b/src/components/WatchModal.tsx index 4e4219e9d..4ff4f9f1e 100644 --- a/src/components/WatchModal.tsx +++ b/src/components/WatchModal.tsx @@ -28,17 +28,20 @@ function WatchModal({ watchModalView, setWatchModalView }) { setInputCollectionName(e.target.value); }; - const handleAddWatchMapping = () => { + const handleAddWatchMapping = async () => { if (inputFolderPath.length > 0 && inputCollectionName.length > 0) { - watchService.addWatchMapping(inputCollectionName, inputFolderPath); + await watchService.addWatchMapping( + inputCollectionName, + inputFolderPath + ); setInputCollectionName(''); setInputFolderPath(''); setShouldUpdateMappings(true); } }; - const handleRemoveWatchMapping = (mapping: WatchMapping) => { - watchService.removeWatchMapping(mapping.collectionName); + const handleRemoveWatchMapping = async (mapping: WatchMapping) => { + await watchService.removeWatchMapping(mapping.collectionName); setShouldUpdateMappings(true); }; diff --git a/src/services/watchService.ts b/src/services/watchService.ts index 842f5223e..9be3e11e2 100644 --- a/src/services/watchService.ts +++ b/src/services/watchService.ts @@ -2,8 +2,8 @@ import { Collection } from 'types/collection'; import { EnteFile } from 'types/file'; import { ElectronFile, FileWithCollection } from 'types/upload'; import { runningInBrowser } from 'utils/common'; -import { syncCollections } from './collectionService'; -import { syncFiles, trashFiles } from './fileService'; +import { removeFromCollection, syncCollections } from './collectionService'; +import { syncFiles } from './fileService'; import debounce from 'debounce-promise'; export interface WatchMapping { @@ -29,7 +29,6 @@ class WatchService { setElectronFiles: (files: ElectronFile[]) => void; setCollectionName: (collectionName: string) => void; syncWithRemote: () => void; - promise: Promise; constructor() { this.ElectronAPIs = runningInBrowser() && window['ElectronAPIs']; @@ -105,12 +104,12 @@ class WatchService { } } - addWatchMapping(collectionName: string, folderPath: string) { - this.ElectronAPIs.addWatchMapping(collectionName, folderPath); + async addWatchMapping(collectionName: string, folderPath: string) { + await this.ElectronAPIs.addWatchMapping(collectionName, folderPath); } - removeWatchMapping(collectionName: string) { - this.ElectronAPIs.removeWatchMapping(collectionName); + async removeWatchMapping(collectionName: string) { + await this.ElectronAPIs.removeWatchMapping(collectionName); } getWatchMappings(): WatchMapping[] { @@ -190,6 +189,7 @@ class WatchService { } const uploadedFiles: WatchMapping['files'] = []; + for (const fileWithCollection of filesWithCollection) { if (fileWithCollection.isLivePhoto) { const imagePath = ( @@ -262,10 +262,10 @@ class WatchService { ) { if (this.allElectronAPIsExist) { const collections = await syncCollections(); - const collectionID = collections.find( + const collection = collections.find( (collection) => collection.name === collectionName - )?.id; - if (!collectionID) { + ); + if (!collection) { return; } const files = await syncFiles(collections, () => {}); @@ -276,10 +276,12 @@ class WatchService { } const filesToTrash = files.filter((file) => { - return idSet.has(file.id) && file.collectionID === collectionID; + return ( + idSet.has(file.id) && file.collectionID === collection.id + ); }); - await trashFiles(filesToTrash); + await removeFromCollection(collection, filesToTrash); } } @@ -306,15 +308,19 @@ class WatchService { } async function diskFileAddedCallback(instance: WatchService, filePath: string) { - console.log('diskFileAddedCallback', instance, filePath); const collectionName = await instance.getCollectionName(filePath); + if (!collectionName) { + return; + } + + console.log('adding', collectionName, filePath); + const event: UploadQueueType = { collectionName, paths: [filePath], }; instance.uploadQueue.push(event); - // instance.runNextUpload(); await debounce(runNextUploadByInstance, 300)(instance); } @@ -324,34 +330,37 @@ async function diskFileRemovedCallback( ) { const collectionName = await instance.getCollectionName(filePath); - console.log('collection', collectionName); + console.log('removing', collectionName, filePath); if (!collectionName) { return; } - let mappings = instance.getWatchMappings(); + const mappings = instance.getWatchMappings(); - const mapping = mappings.find( + const mappingIdx = mappings.findIndex( (mapping) => mapping.collectionName === collectionName ); - if (!mapping) { + if (mappingIdx === -1) { return; } - const file = mapping.files.find((file) => file.path === filePath); + const file = mappings[mappingIdx].files.find( + (file) => file.path === filePath + ); if (!file) { return; } await instance.trashByIDs([file], collectionName); - mappings = instance.getWatchMappings(); - mapping.files = mapping.files.filter((file) => file.path !== filePath); + mappings[mappingIdx].files = mappings[mappingIdx].files.filter( + (file) => file.path !== filePath + ); instance.ElectronAPIs.setWatchMappings(mappings); instance.syncWithRemote(); - console.log('after trash', instance.ElectronAPIs.getWatchMappings()); + console.log('after trash', instance.getWatchMappings()); } const runNextUploadByInstance = async (w: WatchService) => { From 54f914453e8a0aa0abb0d97875f98a2a1e699c56 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Sun, 5 Jun 2022 22:20:56 +0530 Subject: [PATCH 008/295] rename --- src/components/pages/gallery/Upload.tsx | 2 +- src/services/upload/uploadManager.ts | 2 +- src/services/watchService.ts | 9 ++------- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/components/pages/gallery/Upload.tsx b/src/components/pages/gallery/Upload.tsx index 12c635d6e..61fd7b96f 100644 --- a/src/components/pages/gallery/Upload.tsx +++ b/src/components/pages/gallery/Upload.tsx @@ -357,7 +357,7 @@ export default function Upload(props: Props) { filesWithCollectionToUpload, collections ); - await watchService.allUploadsDone( + await watchService.allFileUploadsDone( filesWithCollectionToUpload, collections ); diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index 802220ece..e4c7c144f 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -407,7 +407,7 @@ class UploadManager { FileUploadResults.UPLOADED_WITH_STATIC_THUMBNAIL || fileUploadResult === FileUploadResults.ALREADY_UPLOADED ) { - await watchService.fileUploadDone( + await watchService.fileUploaded( fileWithCollection, uploadedFile ); diff --git a/src/services/watchService.ts b/src/services/watchService.ts index 9be3e11e2..cf59fabb9 100644 --- a/src/services/watchService.ts +++ b/src/services/watchService.ts @@ -150,10 +150,7 @@ class WatchService { ); } - async fileUploadDone( - fileWithCollection: FileWithCollection, - file: EnteFile - ) { + async fileUploaded(fileWithCollection: FileWithCollection, file: EnteFile) { if (fileWithCollection.isLivePhoto) { this.pathToIDMap.set( (fileWithCollection.livePhotoAssets.image as ElectronFile).path, @@ -171,7 +168,7 @@ class WatchService { } } - async allUploadsDone( + async allFileUploadsDone( filesWithCollection: FileWithCollection[], collections: Collection[] ) { @@ -239,8 +236,6 @@ class WatchService { ); mapping.files = [...mapping.files, ...uploadedFiles]; - console.log('new mappings', mappings); - this.ElectronAPIs.setWatchMappings(mappings); this.syncWithRemote(); From a0bcdc53292a71920308301c0644e1139f30bb88 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Sun, 5 Jun 2022 23:29:44 +0530 Subject: [PATCH 009/295] added trash type to event queue --- src/services/watchService.ts | 179 +++++++++++++++++++---------------- 1 file changed, 99 insertions(+), 80 deletions(-) diff --git a/src/services/watchService.ts b/src/services/watchService.ts index cf59fabb9..11feb96ba 100644 --- a/src/services/watchService.ts +++ b/src/services/watchService.ts @@ -15,7 +15,8 @@ export interface WatchMapping { }[]; } -interface UploadQueueType { +interface EventQueueType { + type: 'upload' | 'trash'; collectionName: string; paths: string[]; } @@ -23,8 +24,8 @@ interface UploadQueueType { class WatchService { ElectronAPIs: any; allElectronAPIsExist: boolean = false; - uploadQueue: UploadQueueType[] = []; - isUploadRunning: boolean = false; + eventQueue: EventQueueType[] = []; + isEventRunning: boolean = false; pathToIDMap = new Map(); setElectronFiles: (files: ElectronFile[]) => void; setCollectionName: (collectionName: string) => void; @@ -64,33 +65,27 @@ class WatchService { }); if (filesToUpload.length > 0) { - const event: UploadQueueType = { + const event: EventQueueType = { + type: 'upload', collectionName: mapping.collectionName, paths: filesToUpload, }; - this.uploadQueue.push(event); + this.eventQueue.push(event); } if (filesToRemove.length > 0) { - await this.trashByIDs( - filesToRemove, - mapping.collectionName - ); - mapping.files = mapping.files.filter( - (file) => - !filesToRemove.find( - (fileToRemove) => - file.path === fileToRemove.path - ) - ); + const event: EventQueueType = { + type: 'trash', + collectionName: mapping.collectionName, + paths: filesToRemove.map((file) => file.path), + }; + this.eventQueue.push(event); } - - this.runNextUpload(); } - this.ElectronAPIs.setWatchMappings(mappings); this.setWatchFunctions(); this.syncWithRemote(); + await this.runNextEvent(); } } @@ -118,32 +113,47 @@ class WatchService { } } - async runNextUpload() { - if (this.uploadQueue.length === 0 || this.isUploadRunning) { + async runNextEvent() { + console.log('runNextEvent mappings', this.getWatchMappings()); + + if (this.eventQueue.length === 0) { return; } - this.isUploadRunning = true; + if (this.eventQueue[0].type === 'upload') { + this.runNextUpload(); + } else { + this.runNextTrash(); + } + } - const newUploadQueue = [this.uploadQueue[0]]; - const len = this.uploadQueue.length; + async runNextUpload() { + if (this.eventQueue.length === 0 || this.isEventRunning) { + return; + } + + this.isEventRunning = true; + + const newUploadQueue = [this.eventQueue[0]]; + const len = this.eventQueue.length; for (let i = 1; i < len; i++) { if ( - this.uploadQueue[i].collectionName === - newUploadQueue[0].collectionName + this.eventQueue[i].collectionName === + newUploadQueue[0].collectionName && + this.eventQueue[i].type === newUploadQueue[0].type ) { - newUploadQueue[0].paths.push(...this.uploadQueue[i].paths); + newUploadQueue[0].paths.push(...this.eventQueue[i].paths); } else { - newUploadQueue.push(this.uploadQueue[i]); + newUploadQueue.push(this.eventQueue[i]); } } - newUploadQueue.push(...this.uploadQueue.slice(len)); - this.uploadQueue = newUploadQueue; + newUploadQueue.push(...this.eventQueue.slice(len)); + this.eventQueue = newUploadQueue; - this.setCollectionName(this.uploadQueue[0].collectionName); + this.setCollectionName(this.eventQueue[0].collectionName); this.setElectronFiles( await Promise.all( - this.uploadQueue[0].paths.map(async (path) => { + this.eventQueue[0].paths.map(async (path) => { return await this.ElectronAPIs.getElectronFile(path); }) ) @@ -178,9 +188,9 @@ class WatchService { collection.id === filesWithCollection[0].collectionID ); if ( - !this.isUploadRunning || - this.uploadQueue.length === 0 || - this.uploadQueue[0].collectionName !== collection?.name + !this.isEventRunning || + this.eventQueue.length === 0 || + this.eventQueue[0].collectionName !== collection?.name ) { return; } @@ -225,32 +235,60 @@ class WatchService { } } - console.log('uploadedFiles', uploadedFiles); - if (uploadedFiles.length > 0) { const mappings = this.getWatchMappings(); const mapping = mappings.find( (mapping) => mapping.collectionName === - this.uploadQueue[0].collectionName + this.eventQueue[0].collectionName ); mapping.files = [...mapping.files, ...uploadedFiles]; this.ElectronAPIs.setWatchMappings(mappings); this.syncWithRemote(); - - console.log( - 'now mappings', - await this.ElectronAPIs.getWatchMappings() - ); } - this.uploadQueue.shift(); - this.isUploadRunning = false; - this.runNextUpload(); + this.eventQueue.shift(); + this.isEventRunning = false; + this.runNextEvent(); } } + async runNextTrash() { + if (this.eventQueue.length === 0 || this.isEventRunning) { + return; + } + + this.isEventRunning = true; + + const { collectionName, paths } = this.eventQueue[0]; + const filePathsToRemove = new Set(paths); + + const mappings = this.getWatchMappings(); + const mappingIdx = mappings.findIndex( + (mapping) => mapping.collectionName === collectionName + ); + if (mappingIdx === -1) { + return; + } + + const files = mappings[mappingIdx].files.filter((file) => + filePathsToRemove.has(file.path) + ); + + await this.trashByIDs(files, collectionName); + + mappings[mappingIdx].files = mappings[mappingIdx].files.filter( + (file) => !filePathsToRemove.has(file.path) + ); + this.ElectronAPIs.setWatchMappings(mappings); + this.syncWithRemote(); + + this.eventQueue.shift(); + this.isEventRunning = false; + this.runNextEvent(); + } + async trashByIDs( toTrashFiles: WatchMapping['files'], collectionName: string @@ -283,8 +321,6 @@ class WatchService { async getCollectionName(filePath: string) { const mappings = this.getWatchMappings(); - console.log('mappings', mappings, filePath); - const collectionName = mappings.find((mapping) => filePath.startsWith(mapping.folderPath) )?.collectionName; @@ -309,14 +345,15 @@ async function diskFileAddedCallback(instance: WatchService, filePath: string) { return; } - console.log('adding', collectionName, filePath); + console.log('added (upload) to event queue', collectionName, filePath); - const event: UploadQueueType = { + const event: EventQueueType = { + type: 'upload', collectionName, paths: [filePath], }; - instance.uploadQueue.push(event); - await debounce(runNextUploadByInstance, 300)(instance); + instance.eventQueue.push(event); + await debounce(runNextEventByInstance, 300)(instance); } async function diskFileRemovedCallback( @@ -325,41 +362,23 @@ async function diskFileRemovedCallback( ) { const collectionName = await instance.getCollectionName(filePath); - console.log('removing', collectionName, filePath); + console.log('added (trash) to event queue', collectionName, filePath); if (!collectionName) { return; } - const mappings = instance.getWatchMappings(); - - const mappingIdx = mappings.findIndex( - (mapping) => mapping.collectionName === collectionName - ); - if (mappingIdx === -1) { - return; - } - - const file = mappings[mappingIdx].files.find( - (file) => file.path === filePath - ); - if (!file) { - return; - } - - await instance.trashByIDs([file], collectionName); - - mappings[mappingIdx].files = mappings[mappingIdx].files.filter( - (file) => file.path !== filePath - ); - instance.ElectronAPIs.setWatchMappings(mappings); - instance.syncWithRemote(); - - console.log('after trash', instance.getWatchMappings()); + const event: EventQueueType = { + type: 'trash', + collectionName, + paths: [filePath], + }; + instance.eventQueue.push(event); + await debounce(runNextEventByInstance, 300)(instance); } -const runNextUploadByInstance = async (w: WatchService) => { - await w.runNextUpload(); +const runNextEventByInstance = async (w: WatchService) => { + await w.runNextEvent(); }; export default new WatchService(); From 71b9555fa634d3b4941c165efd1776978a7e2e43 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Mon, 6 Jun 2022 10:35:04 +0530 Subject: [PATCH 010/295] batch events whenever possible --- src/services/watchService.ts | 37 +++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/services/watchService.ts b/src/services/watchService.ts index 11feb96ba..5f12f1f7d 100644 --- a/src/services/watchService.ts +++ b/src/services/watchService.ts @@ -134,21 +134,7 @@ class WatchService { this.isEventRunning = true; - const newUploadQueue = [this.eventQueue[0]]; - const len = this.eventQueue.length; - for (let i = 1; i < len; i++) { - if ( - this.eventQueue[i].collectionName === - newUploadQueue[0].collectionName && - this.eventQueue[i].type === newUploadQueue[0].type - ) { - newUploadQueue[0].paths.push(...this.eventQueue[i].paths); - } else { - newUploadQueue.push(this.eventQueue[i]); - } - } - newUploadQueue.push(...this.eventQueue.slice(len)); - this.eventQueue = newUploadQueue; + this.batchNextEvent(); this.setCollectionName(this.eventQueue[0].collectionName); this.setElectronFiles( @@ -261,6 +247,8 @@ class WatchService { this.isEventRunning = true; + this.batchNextEvent(); + const { collectionName, paths } = this.eventQueue[0]; const filePathsToRemove = new Set(paths); @@ -336,6 +324,25 @@ class WatchService { const folderPath = await this.ElectronAPIs.selectFolder(); return folderPath; } + + // Batches all the files to be uploaded (or trashed) of same collection as the next event + batchNextEvent() { + const newEventQueue = [this.eventQueue[0]]; + const len = this.eventQueue.length; + for (let i = 1; i < len; i++) { + if ( + this.eventQueue[i].collectionName === + newEventQueue[0].collectionName && + this.eventQueue[i].type === newEventQueue[0].type + ) { + newEventQueue[0].paths.push(...this.eventQueue[i].paths); + } else { + newEventQueue.push(this.eventQueue[i]); + } + } + newEventQueue.push(...this.eventQueue.slice(len)); + this.eventQueue = newEventQueue; + } } async function diskFileAddedCallback(instance: WatchService, filePath: string) { From 2bd43cdabf28d6939ac33aff71c7ab1bac422ffe Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Mon, 6 Jun 2022 10:56:51 +0530 Subject: [PATCH 011/295] refactor --- src/components/pages/gallery/Upload.tsx | 26 +++++++++++++--------- src/services/watchService.ts | 29 +++++++++++++++++++------ 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/src/components/pages/gallery/Upload.tsx b/src/components/pages/gallery/Upload.tsx index 61fd7b96f..1ea393be4 100644 --- a/src/components/pages/gallery/Upload.tsx +++ b/src/components/pages/gallery/Upload.tsx @@ -117,16 +117,20 @@ export default function Upload(props: Props) { resumeDesktopUpload(type, electronFiles, collectionName); } ); - watchService.setElectronFiles = props.setElectronFiles; - watchService.setCollectionName = (collectionName: string) => { - isPendingDesktopUpload.current = true; - pendingDesktopUploadCollectionName.current = collectionName; - }; - watchService.syncWithRemote = props.syncWithRemote; + watchService.setWatchServiceFunctions( + props.setElectronFiles, + setCollectionName, + props.syncWithRemote + ); watchService.init(); } }, []); + const setCollectionName = (collectionName: string) => { + isPendingDesktopUpload.current = true; + pendingDesktopUploadCollectionName.current = collectionName; + }; + useEffect(() => { if ( props.electronFiles?.length > 0 || @@ -357,10 +361,6 @@ export default function Upload(props: Props) { filesWithCollectionToUpload, collections ); - await watchService.allFileUploadsDone( - filesWithCollectionToUpload, - collections - ); } catch (err) { const message = getUserFacingErrorMessage( err.message, @@ -372,6 +372,12 @@ export default function Upload(props: Props) { } finally { props.setUploadInProgress(false); props.syncWithRemote(); + if (isElectron()) { + await watchService.allFileUploadsDone( + filesWithCollectionToUpload, + collections + ); + } } }; const retryFailed = async () => { diff --git a/src/services/watchService.ts b/src/services/watchService.ts index 5f12f1f7d..b92978634 100644 --- a/src/services/watchService.ts +++ b/src/services/watchService.ts @@ -36,6 +36,16 @@ class WatchService { this.allElectronAPIsExist = !!this.ElectronAPIs?.getWatchMappings; } + setWatchServiceFunctions( + setElectronFiles: (files: ElectronFile[]) => void, + setCollectionName: (collectionName: string) => void, + syncWithRemote: () => void + ) { + this.setElectronFiles = setElectronFiles; + this.setCollectionName = setCollectionName; + this.syncWithRemote = syncWithRemote; + } + async init() { if (this.allElectronAPIsExist) { const mappings = this.getWatchMappings(); @@ -100,23 +110,28 @@ class WatchService { } async addWatchMapping(collectionName: string, folderPath: string) { - await this.ElectronAPIs.addWatchMapping(collectionName, folderPath); + if (this.allElectronAPIsExist) { + await this.ElectronAPIs.addWatchMapping(collectionName, folderPath); + } } async removeWatchMapping(collectionName: string) { - await this.ElectronAPIs.removeWatchMapping(collectionName); + if (this.allElectronAPIsExist) { + await this.ElectronAPIs.removeWatchMapping(collectionName); + } } getWatchMappings(): WatchMapping[] { if (this.allElectronAPIsExist) { return this.ElectronAPIs.getWatchMappings() ?? []; } + return []; } async runNextEvent() { console.log('runNextEvent mappings', this.getWatchMappings()); - if (this.eventQueue.length === 0) { + if (this.eventQueue.length === 0 || this.isEventRunning) { return; } @@ -127,7 +142,7 @@ class WatchService { } } - async runNextUpload() { + private async runNextUpload() { if (this.eventQueue.length === 0 || this.isEventRunning) { return; } @@ -240,7 +255,7 @@ class WatchService { } } - async runNextTrash() { + private async runNextTrash() { if (this.eventQueue.length === 0 || this.isEventRunning) { return; } @@ -277,7 +292,7 @@ class WatchService { this.runNextEvent(); } - async trashByIDs( + private async trashByIDs( toTrashFiles: WatchMapping['files'], collectionName: string ) { @@ -326,7 +341,7 @@ class WatchService { } // Batches all the files to be uploaded (or trashed) of same collection as the next event - batchNextEvent() { + private batchNextEvent() { const newEventQueue = [this.eventQueue[0]]; const len = this.eventQueue.length; for (let i = 1; i < len; i++) { From f1532a7aa1f361a9ddf0c802afe6d434127f1a79 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Mon, 6 Jun 2022 11:09:24 +0530 Subject: [PATCH 012/295] refactor: added error logging --- src/services/watchService.ts | 441 ++++++++++++++++++++--------------- 1 file changed, 249 insertions(+), 192 deletions(-) diff --git a/src/services/watchService.ts b/src/services/watchService.ts index b92978634..9928e891c 100644 --- a/src/services/watchService.ts +++ b/src/services/watchService.ts @@ -5,6 +5,7 @@ import { runningInBrowser } from 'utils/common'; import { removeFromCollection, syncCollections } from './collectionService'; import { syncFiles } from './fileService'; import debounce from 'debounce-promise'; +import { logError } from 'utils/sentry'; export interface WatchMapping { collectionName: string; @@ -48,54 +49,58 @@ class WatchService { async init() { if (this.allElectronAPIsExist) { - const mappings = this.getWatchMappings(); + try { + const mappings = this.getWatchMappings(); - console.log('mappings', mappings); + console.log('mappings', mappings); - if (!mappings) { - return; - } - - for (const mapping of mappings) { - const filePathsOnDisk: string[] = - await this.ElectronAPIs.getPosixFilePathsFromDir( - mapping.folderPath - ); - - const filesToUpload = filePathsOnDisk.filter((filePath) => { - return !mapping.files.find( - (file) => file.path === filePath - ); - }); - - const filesToRemove = mapping.files.filter((file) => { - return !filePathsOnDisk.find( - (filePath) => filePath === file.path - ); - }); - - if (filesToUpload.length > 0) { - const event: EventQueueType = { - type: 'upload', - collectionName: mapping.collectionName, - paths: filesToUpload, - }; - this.eventQueue.push(event); + if (!mappings) { + return; } - if (filesToRemove.length > 0) { - const event: EventQueueType = { - type: 'trash', - collectionName: mapping.collectionName, - paths: filesToRemove.map((file) => file.path), - }; - this.eventQueue.push(event); - } - } + for (const mapping of mappings) { + const filePathsOnDisk: string[] = + await this.ElectronAPIs.getPosixFilePathsFromDir( + mapping.folderPath + ); - this.setWatchFunctions(); - this.syncWithRemote(); - await this.runNextEvent(); + const filesToUpload = filePathsOnDisk.filter((filePath) => { + return !mapping.files.find( + (file) => file.path === filePath + ); + }); + + const filesToRemove = mapping.files.filter((file) => { + return !filePathsOnDisk.find( + (filePath) => filePath === file.path + ); + }); + + if (filesToUpload.length > 0) { + const event: EventQueueType = { + type: 'upload', + collectionName: mapping.collectionName, + paths: filesToUpload, + }; + this.eventQueue.push(event); + } + + if (filesToRemove.length > 0) { + const event: EventQueueType = { + type: 'trash', + collectionName: mapping.collectionName, + paths: filesToRemove.map((file) => file.path), + }; + this.eventQueue.push(event); + } + } + + this.setWatchFunctions(); + this.syncWithRemote(); + await this.runNextEvent(); + } catch (e) { + logError(e, 'error while initializing watch service'); + } } } @@ -111,19 +116,35 @@ class WatchService { async addWatchMapping(collectionName: string, folderPath: string) { if (this.allElectronAPIsExist) { - await this.ElectronAPIs.addWatchMapping(collectionName, folderPath); + try { + await this.ElectronAPIs.addWatchMapping( + collectionName, + folderPath + ); + } catch (e) { + logError(e, 'error while adding watch mapping'); + } } } async removeWatchMapping(collectionName: string) { if (this.allElectronAPIsExist) { - await this.ElectronAPIs.removeWatchMapping(collectionName); + try { + await this.ElectronAPIs.removeWatchMapping(collectionName); + } catch (e) { + logError(e, 'error while removing watch mapping'); + } } } getWatchMappings(): WatchMapping[] { if (this.allElectronAPIsExist) { - return this.ElectronAPIs.getWatchMappings() ?? []; + try { + return this.ElectronAPIs.getWatchMappings() ?? []; + } catch (e) { + logError(e, 'error while getting watch mappings'); + return []; + } } return []; } @@ -143,22 +164,26 @@ class WatchService { } private async runNextUpload() { - if (this.eventQueue.length === 0 || this.isEventRunning) { - return; + try { + if (this.eventQueue.length === 0 || this.isEventRunning) { + return; + } + + this.isEventRunning = true; + + this.batchNextEvent(); + + this.setCollectionName(this.eventQueue[0].collectionName); + this.setElectronFiles( + await Promise.all( + this.eventQueue[0].paths.map(async (path) => { + return await this.ElectronAPIs.getElectronFile(path); + }) + ) + ); + } catch (e) { + logError(e, 'error while running next upload'); } - - this.isEventRunning = true; - - this.batchNextEvent(); - - this.setCollectionName(this.eventQueue[0].collectionName); - this.setElectronFiles( - await Promise.all( - this.eventQueue[0].paths.map(async (path) => { - return await this.ElectronAPIs.getElectronFile(path); - }) - ) - ); } async fileUploaded(fileWithCollection: FileWithCollection, file: EnteFile) { @@ -184,119 +209,132 @@ class WatchService { collections: Collection[] ) { if (this.allElectronAPIsExist) { - const collection = collections.find( - (collection) => - collection.id === filesWithCollection[0].collectionID - ); - if ( - !this.isEventRunning || - this.eventQueue.length === 0 || - this.eventQueue[0].collectionName !== collection?.name - ) { - return; - } + try { + const collection = collections.find( + (collection) => + collection.id === filesWithCollection[0].collectionID + ); + if ( + !this.isEventRunning || + this.eventQueue.length === 0 || + this.eventQueue[0].collectionName !== collection?.name + ) { + return; + } - const uploadedFiles: WatchMapping['files'] = []; + const uploadedFiles: WatchMapping['files'] = []; - for (const fileWithCollection of filesWithCollection) { - if (fileWithCollection.isLivePhoto) { - const imagePath = ( - fileWithCollection.livePhotoAssets.image as ElectronFile - ).path; - const videoPath = ( - fileWithCollection.livePhotoAssets.video as ElectronFile - ).path; - if ( - this.pathToIDMap.has(imagePath) && - this.pathToIDMap.has(videoPath) - ) { - uploadedFiles.push({ - path: imagePath, - id: this.pathToIDMap.get(imagePath), - }); - uploadedFiles.push({ - path: videoPath, - id: this.pathToIDMap.get(videoPath), - }); + for (const fileWithCollection of filesWithCollection) { + if (fileWithCollection.isLivePhoto) { + const imagePath = ( + fileWithCollection.livePhotoAssets + .image as ElectronFile + ).path; + const videoPath = ( + fileWithCollection.livePhotoAssets + .video as ElectronFile + ).path; - this.pathToIDMap.delete(imagePath); - this.pathToIDMap.delete(videoPath); - } - } else { - const filePath = (fileWithCollection.file as ElectronFile) - .path; - if (this.pathToIDMap.has(filePath)) { - uploadedFiles.push({ - path: filePath, - id: this.pathToIDMap.get(filePath), - }); + if ( + this.pathToIDMap.has(imagePath) && + this.pathToIDMap.has(videoPath) + ) { + uploadedFiles.push({ + path: imagePath, + id: this.pathToIDMap.get(imagePath), + }); + uploadedFiles.push({ + path: videoPath, + id: this.pathToIDMap.get(videoPath), + }); - this.pathToIDMap.delete(filePath); + this.pathToIDMap.delete(imagePath); + this.pathToIDMap.delete(videoPath); + } + } else { + const filePath = ( + fileWithCollection.file as ElectronFile + ).path; + + if (this.pathToIDMap.has(filePath)) { + uploadedFiles.push({ + path: filePath, + id: this.pathToIDMap.get(filePath), + }); + + this.pathToIDMap.delete(filePath); + } } } + + if (uploadedFiles.length > 0) { + const mappings = this.getWatchMappings(); + const mapping = mappings.find( + (mapping) => + mapping.collectionName === + this.eventQueue[0].collectionName + ); + mapping.files = [...mapping.files, ...uploadedFiles]; + + this.ElectronAPIs.setWatchMappings(mappings); + this.syncWithRemote(); + } + + this.eventQueue.shift(); + this.isEventRunning = false; + this.runNextEvent(); + } catch (e) { + logError(e, 'error while running all file uploads done'); } - - if (uploadedFiles.length > 0) { - const mappings = this.getWatchMappings(); - const mapping = mappings.find( - (mapping) => - mapping.collectionName === - this.eventQueue[0].collectionName - ); - mapping.files = [...mapping.files, ...uploadedFiles]; - - this.ElectronAPIs.setWatchMappings(mappings); - this.syncWithRemote(); - } - - this.eventQueue.shift(); - this.isEventRunning = false; - this.runNextEvent(); } } private async runNextTrash() { - if (this.eventQueue.length === 0 || this.isEventRunning) { - return; + try { + if (this.eventQueue.length === 0 || this.isEventRunning) { + return; + } + + this.isEventRunning = true; + + this.batchNextEvent(); + + const { collectionName, paths } = this.eventQueue[0]; + const filePathsToRemove = new Set(paths); + + const mappings = this.getWatchMappings(); + const mappingIdx = mappings.findIndex( + (mapping) => mapping.collectionName === collectionName + ); + if (mappingIdx === -1) { + return; + } + + const files = mappings[mappingIdx].files.filter((file) => + filePathsToRemove.has(file.path) + ); + + await this.trashByIDs(files, collectionName); + + mappings[mappingIdx].files = mappings[mappingIdx].files.filter( + (file) => !filePathsToRemove.has(file.path) + ); + this.ElectronAPIs.setWatchMappings(mappings); + this.syncWithRemote(); + + this.eventQueue.shift(); + this.isEventRunning = false; + this.runNextEvent(); + } catch (e) { + logError(e, 'error while running next trash'); } - - this.isEventRunning = true; - - this.batchNextEvent(); - - const { collectionName, paths } = this.eventQueue[0]; - const filePathsToRemove = new Set(paths); - - const mappings = this.getWatchMappings(); - const mappingIdx = mappings.findIndex( - (mapping) => mapping.collectionName === collectionName - ); - if (mappingIdx === -1) { - return; - } - - const files = mappings[mappingIdx].files.filter((file) => - filePathsToRemove.has(file.path) - ); - - await this.trashByIDs(files, collectionName); - - mappings[mappingIdx].files = mappings[mappingIdx].files.filter( - (file) => !filePathsToRemove.has(file.path) - ); - this.ElectronAPIs.setWatchMappings(mappings); - this.syncWithRemote(); - - this.eventQueue.shift(); - this.isEventRunning = false; - this.runNextEvent(); } private async trashByIDs( toTrashFiles: WatchMapping['files'], collectionName: string ) { - if (this.allElectronAPIsExist) { + try { const collections = await syncCollections(); const collection = collections.find( (collection) => collection.name === collectionName @@ -318,29 +356,40 @@ class WatchService { }); await removeFromCollection(collection, filesToTrash); + } catch (e) { + logError(e, 'error while trashing by IDs'); } } async getCollectionName(filePath: string) { - const mappings = this.getWatchMappings(); + try { + const mappings = this.getWatchMappings(); - const collectionName = mappings.find((mapping) => - filePath.startsWith(mapping.folderPath) - )?.collectionName; + const collectionName = mappings.find((mapping) => + filePath.startsWith(mapping.folderPath) + )?.collectionName; - if (!collectionName) { - return null; + if (!collectionName) { + return null; + } + + return collectionName; + } catch (e) { + logError(e, 'error while getting collection name'); } - - return collectionName; } async selectFolder(): Promise { - const folderPath = await this.ElectronAPIs.selectFolder(); - return folderPath; + try { + const folderPath = await this.ElectronAPIs.selectFolder(); + return folderPath; + } catch (e) { + logError(e, 'error while selecting folder'); + } } - // Batches all the files to be uploaded (or trashed) of same collection as the next event + // Batches all the files to be uploaded (or trashed) from the + // event queue of same collection as the next event private batchNextEvent() { const newEventQueue = [this.eventQueue[0]]; const len = this.eventQueue.length; @@ -361,42 +410,50 @@ class WatchService { } async function diskFileAddedCallback(instance: WatchService, filePath: string) { - const collectionName = await instance.getCollectionName(filePath); + try { + const collectionName = await instance.getCollectionName(filePath); - if (!collectionName) { - return; + if (!collectionName) { + return; + } + + console.log('added (upload) to event queue', collectionName, filePath); + + const event: EventQueueType = { + type: 'upload', + collectionName, + paths: [filePath], + }; + instance.eventQueue.push(event); + await debounce(runNextEventByInstance, 300)(instance); + } catch (e) { + logError(e, 'error while calling diskFileAddedCallback'); } - - console.log('added (upload) to event queue', collectionName, filePath); - - const event: EventQueueType = { - type: 'upload', - collectionName, - paths: [filePath], - }; - instance.eventQueue.push(event); - await debounce(runNextEventByInstance, 300)(instance); } async function diskFileRemovedCallback( instance: WatchService, filePath: string ) { - const collectionName = await instance.getCollectionName(filePath); + try { + const collectionName = await instance.getCollectionName(filePath); - console.log('added (trash) to event queue', collectionName, filePath); + console.log('added (trash) to event queue', collectionName, filePath); - if (!collectionName) { - return; + if (!collectionName) { + return; + } + + const event: EventQueueType = { + type: 'trash', + collectionName, + paths: [filePath], + }; + instance.eventQueue.push(event); + await debounce(runNextEventByInstance, 300)(instance); + } catch (e) { + logError(e, 'error while calling diskFileRemovedCallback'); } - - const event: EventQueueType = { - type: 'trash', - collectionName, - paths: [filePath], - }; - instance.eventQueue.push(event); - await debounce(runNextEventByInstance, 300)(instance); } const runNextEventByInstance = async (w: WatchService) => { From 6a0ca2b69552e1c8fb2e124bf254492235db96a0 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Mon, 6 Jun 2022 15:20:45 +0530 Subject: [PATCH 013/295] add modal to sidebar --- src/components/Sidebar/UtilitySection.tsx | 11 +++++++++++ src/components/{ => Sidebar}/WatchModal.tsx | 0 src/utils/strings/englishConstants.tsx | 1 + 3 files changed, 12 insertions(+) rename src/components/{ => Sidebar}/WatchModal.tsx (100%) diff --git a/src/components/Sidebar/UtilitySection.tsx b/src/components/Sidebar/UtilitySection.tsx index 55618f3d5..1cddddc19 100644 --- a/src/components/Sidebar/UtilitySection.tsx +++ b/src/components/Sidebar/UtilitySection.tsx @@ -7,6 +7,7 @@ import TwoFactorModal from 'components/TwoFactor/Modal'; import { PAGES } from 'constants/pages'; import { useRouter } from 'next/router'; import { AppContext } from 'pages/_app'; +import WatchModal from './WatchModal'; export default function UtilitySection({ closeSidebar }) { const router = useRouter(); @@ -14,6 +15,7 @@ export default function UtilitySection({ closeSidebar }) { const [recoverModalView, setRecoveryModalView] = useState(false); const [twoFactorModalView, setTwoFactorModalView] = useState(false); + const [watchModalView, setWatchModalView] = useState(false); // const [fixLargeThumbsView, setFixLargeThumbsView] = useState(false); const openRecoveryKeyModal = () => setRecoveryModalView(true); @@ -22,6 +24,8 @@ export default function UtilitySection({ closeSidebar }) { const openTwoFactorModalView = () => setTwoFactorModalView(true); const closeTwoFactorModalView = () => setTwoFactorModalView(false); + const openWatchModalView = () => setWatchModalView(true); + const redirectToChangePasswordPage = () => { closeSidebar(); router.push(PAGES.CHANGE_PASSWORD); @@ -60,6 +64,9 @@ export default function UtilitySection({ closeSidebar }) { {constants.DEDUPLICATE_FILES} + + {constants.WATCH_FOLDERS} + {/* {constants.COMPRESS_THUMBNAILS} @@ -76,6 +83,10 @@ export default function UtilitySection({ closeSidebar }) { closeSidebar={closeSidebar} setLoading={startLoading} /> + {/* ), + WATCH_FOLDERS: 'Watch Folders', }; export default englishConstants; From 44033eb4fd51939c14e034fedd27c4b7272cd101 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Tue, 7 Jun 2022 11:11:14 +0530 Subject: [PATCH 014/295] convert modal to mui --- src/components/Sidebar/WatchModal.tsx | 78 +++++++++++++-------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/src/components/Sidebar/WatchModal.tsx b/src/components/Sidebar/WatchModal.tsx index 4ff4f9f1e..dca115aa4 100644 --- a/src/components/Sidebar/WatchModal.tsx +++ b/src/components/Sidebar/WatchModal.tsx @@ -1,14 +1,15 @@ import React, { useEffect, useState } from 'react'; -import { Button, Modal } from 'react-bootstrap'; +import { Button, Dialog, IconButton } from '@mui/material'; import watchService, { WatchMapping } from 'services/watchService'; import { MdDelete } from 'react-icons/md'; -import { HiArrowNarrowRight } from 'react-icons/hi'; +import ArrowForwardIcon from '@mui/icons-material/ArrowForward'; +import Close from '@mui/icons-material/Close'; +import { SpaceBetweenFlex } from 'components/Container'; function WatchModal({ watchModalView, setWatchModalView }) { const [mappings, setMappings] = useState([]); const [shouldUpdateMappings, setShouldUpdateMappings] = useState(true); const [inputFolderPath, setInputFolderPath] = useState(''); - const [inputCollectionName, setInputCollectionName] = useState(''); useEffect(() => { if (shouldUpdateMappings) { @@ -22,19 +23,12 @@ function WatchModal({ watchModalView, setWatchModalView }) { setInputFolderPath(folderPath); }; - const handleCollectionNameChange = ( - e: React.ChangeEvent - ) => { - setInputCollectionName(e.target.value); - }; - const handleAddWatchMapping = async () => { - if (inputFolderPath.length > 0 && inputCollectionName.length > 0) { + if (inputFolderPath.length > 0) { await watchService.addWatchMapping( - inputCollectionName, + inputFolderPath.substring(inputFolderPath.lastIndexOf('/') + 1), inputFolderPath ); - setInputCollectionName(''); setInputFolderPath(''); setShouldUpdateMappings(true); } @@ -45,37 +39,43 @@ function WatchModal({ watchModalView, setWatchModalView }) { setShouldUpdateMappings(true); }; + const handleClose = () => { + setWatchModalView(false); + }; + return ( - setWatchModalView(false)}> - - Watch Folders - - + +
+ +
+ Watch Folders +
+ + + +
-
@@ -105,7 +106,7 @@ function WatchModal({ watchModalView, setWatchModalView }) {
{mapping.folderPath}{' '} - ))}
- - +
+
); } From cd996a0b49be0da4cd9e16623e0f50abfac69324 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Tue, 7 Jun 2022 12:24:38 +0530 Subject: [PATCH 015/295] sync status button --- src/components/Sidebar/WatchModal.tsx | 51 +++++++++++++++++++++------ src/services/watchService.ts | 4 +++ 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/src/components/Sidebar/WatchModal.tsx b/src/components/Sidebar/WatchModal.tsx index dca115aa4..2f195f97d 100644 --- a/src/components/Sidebar/WatchModal.tsx +++ b/src/components/Sidebar/WatchModal.tsx @@ -1,15 +1,31 @@ import React, { useEffect, useState } from 'react'; -import { Button, Dialog, IconButton } from '@mui/material'; +import { Button, CircularProgress, Dialog, IconButton } from '@mui/material'; import watchService, { WatchMapping } from 'services/watchService'; import { MdDelete } from 'react-icons/md'; import ArrowForwardIcon from '@mui/icons-material/ArrowForward'; import Close from '@mui/icons-material/Close'; import { SpaceBetweenFlex } from 'components/Container'; -function WatchModal({ watchModalView, setWatchModalView }) { +function WatchModal({ + watchModalView, + setWatchModalView, +}: { + watchModalView: boolean; + setWatchModalView: (watchModalView: boolean) => void; +}) { const [mappings, setMappings] = useState([]); const [shouldUpdateMappings, setShouldUpdateMappings] = useState(true); const [inputFolderPath, setInputFolderPath] = useState(''); + const [isSyncing, setIsSyncing] = useState(false); + + useEffect(() => { + if (watchModalView) { + const interval = setInterval(() => { + setIsSyncing(watchService.isEventRunning); + }, 1000); + return () => clearInterval(interval); + } + }, [watchModalView]); useEffect(() => { if (shouldUpdateMappings) { @@ -39,6 +55,12 @@ function WatchModal({ watchModalView, setWatchModalView }) { setShouldUpdateMappings(true); }; + const handleSyncProgressClick = () => { + if (watchService.isUploadRunning) { + // show progress view + } + }; + const handleClose = () => { setWatchModalView(false); }; @@ -103,17 +125,26 @@ function WatchModal({ watchModalView, setWatchModalView }) { -
- Current Watch Mappings -
+
+ Current Watch Mappings +
+ {isSyncing && ( + + + + )} +
(); setElectronFiles: (files: ElectronFile[]) => void; setCollectionName: (collectionName: string) => void; syncWithRemote: () => void; + showProgressView: () => void; constructor() { this.ElectronAPIs = runningInBrowser() && window['ElectronAPIs']; @@ -170,6 +172,7 @@ class WatchService { } this.isEventRunning = true; + this.isUploadRunning = true; this.batchNextEvent(); @@ -282,6 +285,7 @@ class WatchService { this.eventQueue.shift(); this.isEventRunning = false; + this.isUploadRunning = false; this.runNextEvent(); } catch (e) { logError(e, 'error while running all file uploads done'); From 1d9bc49909a88d887af650eec5306232b39a21c9 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Tue, 7 Jun 2022 12:25:49 +0530 Subject: [PATCH 016/295] move watch modal to components --- src/components/Sidebar/UtilitySection.tsx | 2 +- src/components/{Sidebar => }/WatchModal.tsx | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/components/{Sidebar => }/WatchModal.tsx (100%) diff --git a/src/components/Sidebar/UtilitySection.tsx b/src/components/Sidebar/UtilitySection.tsx index 1cddddc19..ee9c2b590 100644 --- a/src/components/Sidebar/UtilitySection.tsx +++ b/src/components/Sidebar/UtilitySection.tsx @@ -7,7 +7,7 @@ import TwoFactorModal from 'components/TwoFactor/Modal'; import { PAGES } from 'constants/pages'; import { useRouter } from 'next/router'; import { AppContext } from 'pages/_app'; -import WatchModal from './WatchModal'; +import WatchModal from '../WatchModal'; export default function UtilitySection({ closeSidebar }) { const router = useRouter(); diff --git a/src/components/Sidebar/WatchModal.tsx b/src/components/WatchModal.tsx similarity index 100% rename from src/components/Sidebar/WatchModal.tsx rename to src/components/WatchModal.tsx From fd51318ce6be7057dfb6655ef32fb3958bd38602 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Tue, 7 Jun 2022 15:58:20 +0530 Subject: [PATCH 017/295] show progress view when user clicks on watch modal spinner --- src/components/WatchModal.tsx | 2 +- src/components/pages/gallery/Upload.tsx | 9 +++++++-- src/services/watchService.ts | 4 +++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/components/WatchModal.tsx b/src/components/WatchModal.tsx index 2f195f97d..4fd7b4cc9 100644 --- a/src/components/WatchModal.tsx +++ b/src/components/WatchModal.tsx @@ -57,7 +57,7 @@ function WatchModal({ const handleSyncProgressClick = () => { if (watchService.isUploadRunning) { - // show progress view + watchService.showProgressView(); } }; diff --git a/src/components/pages/gallery/Upload.tsx b/src/components/pages/gallery/Upload.tsx index 1ea393be4..0a1753be3 100644 --- a/src/components/pages/gallery/Upload.tsx +++ b/src/components/pages/gallery/Upload.tsx @@ -120,7 +120,8 @@ export default function Upload(props: Props) { watchService.setWatchServiceFunctions( props.setElectronFiles, setCollectionName, - props.syncWithRemote + props.syncWithRemote, + showProgressView ); watchService.init(); } @@ -131,6 +132,10 @@ export default function Upload(props: Props) { pendingDesktopUploadCollectionName.current = collectionName; }; + const showProgressView = () => { + setProgressView(true); + }; + useEffect(() => { if ( props.electronFiles?.length > 0 || @@ -185,7 +190,7 @@ export default function Upload(props: Props) { setUploadResult(new Map()); setPercentComplete(0); props.closeCollectionSelector(); - setProgressView(true); + !watchService.isUploadRunning && setProgressView(true); // don't show progress view if upload triggered by watch service }; const resumeDesktopUpload = async ( diff --git a/src/services/watchService.ts b/src/services/watchService.ts index 94ad79510..f12633bbc 100644 --- a/src/services/watchService.ts +++ b/src/services/watchService.ts @@ -42,11 +42,13 @@ class WatchService { setWatchServiceFunctions( setElectronFiles: (files: ElectronFile[]) => void, setCollectionName: (collectionName: string) => void, - syncWithRemote: () => void + syncWithRemote: () => void, + showProgressView: () => void ) { this.setElectronFiles = setElectronFiles; this.setCollectionName = setCollectionName; this.syncWithRemote = syncWithRemote; + this.showProgressView = showProgressView; } async init() { From 6182249b5fa2c92568b5a689559265e98ab4a920 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Tue, 7 Jun 2022 16:50:25 +0530 Subject: [PATCH 018/295] ignore trashing files if root mapping directory is deleted --- src/services/watchService.ts | 38 +++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/services/watchService.ts b/src/services/watchService.ts index f12633bbc..38ea9d284 100644 --- a/src/services/watchService.ts +++ b/src/services/watchService.ts @@ -26,6 +26,7 @@ class WatchService { ElectronAPIs: any; allElectronAPIsExist: boolean = false; eventQueue: EventQueueType[] = []; + trashingDirQueue: string[] = []; isEventRunning: boolean = false; isUploadRunning: boolean = false; pathToIDMap = new Map(); @@ -303,6 +304,14 @@ class WatchService { this.isEventRunning = true; + if (this.trashingDirQueue.length !== 0) { + this.removeFilesMatchingTrashingDir(this.trashingDirQueue[0]); + this.trashingDirQueue.shift(); + this.isEventRunning = false; + this.runNextEvent(); + return; + } + this.batchNextEvent(); const { collectionName, paths } = this.eventQueue[0]; @@ -367,6 +376,12 @@ class WatchService { } } + removeFilesMatchingTrashingDir(trashingDir: string) { + this.eventQueue = this.eventQueue.filter((event) => + event.paths.every((path) => !path.startsWith(trashingDir)) + ); + } + async getCollectionName(filePath: string) { try { const mappings = this.getWatchMappings(); @@ -439,7 +454,8 @@ async function diskFileAddedCallback(instance: WatchService, filePath: string) { async function diskFileRemovedCallback( instance: WatchService, - filePath: string + filePath: string, + isDir?: boolean ) { try { const collectionName = await instance.getCollectionName(filePath); @@ -450,6 +466,14 @@ async function diskFileRemovedCallback( return; } + if ( + isDir && + hasMappingSameFolderPath(instance, collectionName, filePath) + ) { + instance.trashingDirQueue.push(filePath); + return; + } + const event: EventQueueType = { type: 'trash', collectionName, @@ -466,4 +490,16 @@ const runNextEventByInstance = async (w: WatchService) => { await w.runNextEvent(); }; +const hasMappingSameFolderPath = ( + w: WatchService, + collectionName: string, + folderPath: string +) => { + const mappings = w.getWatchMappings(); + const mapping = mappings.find( + (mapping) => mapping.collectionName === collectionName + ); + return mapping.folderPath === folderPath; +}; + export default new WatchService(); From 07fea0bc556244c4857fcdb5b9e9de6f3d6847d8 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Tue, 7 Jun 2022 17:13:57 +0530 Subject: [PATCH 019/295] remove mappings whose folders don't exist --- src/services/watchService.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/services/watchService.ts b/src/services/watchService.ts index 38ea9d284..c1228c89a 100644 --- a/src/services/watchService.ts +++ b/src/services/watchService.ts @@ -55,7 +55,7 @@ class WatchService { async init() { if (this.allElectronAPIsExist) { try { - const mappings = this.getWatchMappings(); + let mappings = this.getWatchMappings(); console.log('mappings', mappings); @@ -63,6 +63,19 @@ class WatchService { return; } + const existingMappings = []; + for (const mapping of mappings) { + const mappingExists = + await this.ElectronAPIs.isFolderExists( + mapping.folderPath + ); + if (mappingExists) { + existingMappings.push(mapping); + } + } + this.ElectronAPIs.setWatchMappings(existingMappings); + mappings = existingMappings; + for (const mapping of mappings) { const filePathsOnDisk: string[] = await this.ElectronAPIs.getPosixFilePathsFromDir( From ae24b70c4b652d581aefcec3c6ecd598f6081159 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Wed, 8 Jun 2022 23:10:15 +0530 Subject: [PATCH 020/295] refactor --- src/components/WatchModal.tsx | 31 ++--- src/components/pages/gallery/Upload.tsx | 8 +- src/pages/_app.tsx | 5 + src/services/upload/uploadManager.ts | 2 +- src/services/watchService.ts | 159 +++++++++++------------- src/types/watch/index.ts | 14 +++ 6 files changed, 104 insertions(+), 115 deletions(-) create mode 100644 src/types/watch/index.ts diff --git a/src/components/WatchModal.tsx b/src/components/WatchModal.tsx index 4fd7b4cc9..362175f81 100644 --- a/src/components/WatchModal.tsx +++ b/src/components/WatchModal.tsx @@ -1,10 +1,12 @@ import React, { useEffect, useState } from 'react'; import { Button, CircularProgress, Dialog, IconButton } from '@mui/material'; -import watchService, { WatchMapping } from 'services/watchService'; +import watchService from 'services/watchService'; import { MdDelete } from 'react-icons/md'; import ArrowForwardIcon from '@mui/icons-material/ArrowForward'; import Close from '@mui/icons-material/Close'; import { SpaceBetweenFlex } from 'components/Container'; +import { WatchMapping } from 'types/watch'; +import { AppContext } from 'pages/_app'; function WatchModal({ watchModalView, @@ -14,25 +16,12 @@ function WatchModal({ setWatchModalView: (watchModalView: boolean) => void; }) { const [mappings, setMappings] = useState([]); - const [shouldUpdateMappings, setShouldUpdateMappings] = useState(true); const [inputFolderPath, setInputFolderPath] = useState(''); - const [isSyncing, setIsSyncing] = useState(false); + const appContext = React.useContext(AppContext); useEffect(() => { - if (watchModalView) { - const interval = setInterval(() => { - setIsSyncing(watchService.isEventRunning); - }, 1000); - return () => clearInterval(interval); - } - }, [watchModalView]); - - useEffect(() => { - if (shouldUpdateMappings) { - setMappings(watchService.getWatchMappings()); - setShouldUpdateMappings(false); - } - }, [shouldUpdateMappings]); + setMappings(watchService.getWatchMappings()); + }, []); const handleFolderSelection = async () => { const folderPath = await watchService.selectFolder(); @@ -46,17 +35,17 @@ function WatchModal({ inputFolderPath ); setInputFolderPath(''); - setShouldUpdateMappings(true); + setMappings(watchService.getWatchMappings()); } }; const handleRemoveWatchMapping = async (mapping: WatchMapping) => { await watchService.removeWatchMapping(mapping.collectionName); - setShouldUpdateMappings(true); + setMappings(watchService.getWatchMappings()); }; const handleSyncProgressClick = () => { - if (watchService.isUploadRunning) { + if (watchService.isUploadRunning()) { watchService.showProgressView(); } }; @@ -139,7 +128,7 @@ function WatchModal({ }}> Current Watch Mappings
- {isSyncing && ( + {appContext.watchServiceIsRunning && ( diff --git a/src/components/pages/gallery/Upload.tsx b/src/components/pages/gallery/Upload.tsx index 0a1753be3..d177fbec7 100644 --- a/src/components/pages/gallery/Upload.tsx +++ b/src/components/pages/gallery/Upload.tsx @@ -117,13 +117,13 @@ export default function Upload(props: Props) { resumeDesktopUpload(type, electronFiles, collectionName); } ); - watchService.setWatchServiceFunctions( + watchService.init( props.setElectronFiles, setCollectionName, props.syncWithRemote, - showProgressView + showProgressView, + appContext.setWatchServiceIsRunning ); - watchService.init(); } }, []); @@ -190,7 +190,7 @@ export default function Upload(props: Props) { setUploadResult(new Map()); setPercentComplete(0); props.closeCollectionSelector(); - !watchService.isUploadRunning && setProgressView(true); // don't show progress view if upload triggered by watch service + !watchService.isUploadRunning() && setProgressView(true); // don't show progress view if upload triggered by watch service }; const resumeDesktopUpload = async ( diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 04ed9d210..6d541a6f3 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -58,6 +58,8 @@ type AppContextType = { finishLoading: () => void; closeMessageDialog: () => void; setDialogMessage: SetDialogBoxAttributes; + watchServiceIsRunning: boolean; + setWatchServiceIsRunning: (isRunning: boolean) => void; }; export enum FLASH_MESSAGE_TYPE { @@ -92,6 +94,7 @@ export default function App({ Component, err }) { const loadingBar = useRef(null); const [dialogMessage, setDialogMessage] = useState(); const [messageDialogView, setMessageDialogView] = useState(false); + const [watchServiceIsRunning, setWatchServiceIsRunning] = useState(false); useEffect(() => { if ( @@ -284,6 +287,8 @@ export default function App({ Component, err }) { finishLoading, closeMessageDialog, setDialogMessage, + watchServiceIsRunning, + setWatchServiceIsRunning, }}> {loading ? ( diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index e4c7c144f..378cc35f6 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -407,7 +407,7 @@ class UploadManager { FileUploadResults.UPLOADED_WITH_STATIC_THUMBNAIL || fileUploadResult === FileUploadResults.ALREADY_UPLOADED ) { - await watchService.fileUploaded( + await watchService.onFileUpload( fileWithCollection, uploadedFile ); diff --git a/src/services/watchService.ts b/src/services/watchService.ts index c1228c89a..7cb9c757b 100644 --- a/src/services/watchService.ts +++ b/src/services/watchService.ts @@ -6,55 +6,47 @@ import { removeFromCollection, syncCollections } from './collectionService'; import { syncFiles } from './fileService'; import debounce from 'debounce-promise'; import { logError } from 'utils/sentry'; - -export interface WatchMapping { - collectionName: string; - folderPath: string; - files: { - path: string; - id: number; - }[]; -} - -interface EventQueueType { - type: 'upload' | 'trash'; - collectionName: string; - paths: string[]; -} +import { EventQueueType, WatchMapping } from 'types/watch'; class WatchService { ElectronAPIs: any; allElectronAPIsExist: boolean = false; eventQueue: EventQueueType[] = []; + currentEvent: EventQueueType; trashingDirQueue: string[] = []; isEventRunning: boolean = false; - isUploadRunning: boolean = false; + uploadRunning: boolean = false; pathToIDMap = new Map(); setElectronFiles: (files: ElectronFile[]) => void; setCollectionName: (collectionName: string) => void; syncWithRemote: () => void; showProgressView: () => void; + setWatchServiceIsRunning: (isRunning: boolean) => void; constructor() { this.ElectronAPIs = runningInBrowser() && window['ElectronAPIs']; this.allElectronAPIsExist = !!this.ElectronAPIs?.getWatchMappings; } - setWatchServiceFunctions( + isUploadRunning() { + return this.uploadRunning; + } + + async init( setElectronFiles: (files: ElectronFile[]) => void, setCollectionName: (collectionName: string) => void, syncWithRemote: () => void, - showProgressView: () => void + showProgressView: () => void, + setWatchServiceIsRunning: (isRunning: boolean) => void ) { - this.setElectronFiles = setElectronFiles; - this.setCollectionName = setCollectionName; - this.syncWithRemote = syncWithRemote; - this.showProgressView = showProgressView; - } - - async init() { if (this.allElectronAPIsExist) { try { + this.setElectronFiles = setElectronFiles; + this.setCollectionName = setCollectionName; + this.syncWithRemote = syncWithRemote; + this.showProgressView = showProgressView; + this.setWatchServiceIsRunning = setWatchServiceIsRunning; + let mappings = this.getWatchMappings(); console.log('mappings', mappings); @@ -63,18 +55,7 @@ class WatchService { return; } - const existingMappings = []; - for (const mapping of mappings) { - const mappingExists = - await this.ElectronAPIs.isFolderExists( - mapping.folderPath - ); - if (mappingExists) { - existingMappings.push(mapping); - } - } - this.ElectronAPIs.setWatchMappings(existingMappings); - mappings = existingMappings; + mappings = await this.filterOutDeletedMappings(mappings); for (const mapping of mappings) { const filePathsOnDisk: string[] = @@ -114,7 +95,6 @@ class WatchService { } this.setWatchFunctions(); - this.syncWithRemote(); await this.runNextEvent(); } catch (e) { logError(e, 'error while initializing watch service'); @@ -122,6 +102,22 @@ class WatchService { } } + async filterOutDeletedMappings( + mappings: WatchMapping[] + ): Promise { + const notDeletedMappings = []; + for (const mapping of mappings) { + const mappingExists = await this.ElectronAPIs.isFolderExists( + mapping.folderPath + ); + if (mappingExists) { + notDeletedMappings.push(mapping); + } + } + this.ElectronAPIs.setWatchMappings(notDeletedMappings); + return notDeletedMappings; + } + setWatchFunctions() { if (this.allElectronAPIsExist) { this.ElectronAPIs.registerWatcherFunctions( @@ -167,6 +163,11 @@ class WatchService { return []; } + setIsEventRunning(isEventRunning: boolean) { + this.isEventRunning = isEventRunning; + this.setWatchServiceIsRunning(isEventRunning); + } + async runNextEvent() { console.log('runNextEvent mappings', this.getWatchMappings()); @@ -174,28 +175,24 @@ class WatchService { return; } - if (this.eventQueue[0].type === 'upload') { - this.runNextUpload(); + this.setIsEventRunning(true); + const event = this.clubSameCollectionEvents(); + this.currentEvent = event; + if (event.type === 'upload') { + this.processUploadEvent(); } else { - this.runNextTrash(); + this.processTrashEvent(); } } - private async runNextUpload() { + private async processUploadEvent() { try { - if (this.eventQueue.length === 0 || this.isEventRunning) { - return; - } + this.uploadRunning = true; - this.isEventRunning = true; - this.isUploadRunning = true; - - this.batchNextEvent(); - - this.setCollectionName(this.eventQueue[0].collectionName); + this.setCollectionName(this.currentEvent.collectionName); this.setElectronFiles( await Promise.all( - this.eventQueue[0].paths.map(async (path) => { + this.currentEvent.paths.map(async (path) => { return await this.ElectronAPIs.getElectronFile(path); }) ) @@ -205,7 +202,7 @@ class WatchService { } } - async fileUploaded(fileWithCollection: FileWithCollection, file: EnteFile) { + async onFileUpload(fileWithCollection: FileWithCollection, file: EnteFile) { if (fileWithCollection.isLivePhoto) { this.pathToIDMap.set( (fileWithCollection.livePhotoAssets.image as ElectronFile).path, @@ -235,8 +232,7 @@ class WatchService { ); if ( !this.isEventRunning || - this.eventQueue.length === 0 || - this.eventQueue[0].collectionName !== collection?.name + this.currentEvent.collectionName !== collection?.name ) { return; } @@ -291,7 +287,7 @@ class WatchService { const mapping = mappings.find( (mapping) => mapping.collectionName === - this.eventQueue[0].collectionName + this.currentEvent.collectionName ); mapping.files = [...mapping.files, ...uploadedFiles]; @@ -299,9 +295,8 @@ class WatchService { this.syncWithRemote(); } - this.eventQueue.shift(); - this.isEventRunning = false; - this.isUploadRunning = false; + this.setIsEventRunning(false); + this.uploadRunning = false; this.runNextEvent(); } catch (e) { logError(e, 'error while running all file uploads done'); @@ -309,25 +304,17 @@ class WatchService { } } - private async runNextTrash() { + private async processTrashEvent() { try { - if (this.eventQueue.length === 0 || this.isEventRunning) { - return; - } - - this.isEventRunning = true; - if (this.trashingDirQueue.length !== 0) { - this.removeFilesMatchingTrashingDir(this.trashingDirQueue[0]); + this.ignoreFileEventsFromTrashedDir(this.trashingDirQueue[0]); this.trashingDirQueue.shift(); - this.isEventRunning = false; + this.setIsEventRunning(false); this.runNextEvent(); return; } - this.batchNextEvent(); - - const { collectionName, paths } = this.eventQueue[0]; + const { collectionName, paths } = this.currentEvent; const filePathsToRemove = new Set(paths); const mappings = this.getWatchMappings(); @@ -350,8 +337,7 @@ class WatchService { this.ElectronAPIs.setWatchMappings(mappings); this.syncWithRemote(); - this.eventQueue.shift(); - this.isEventRunning = false; + this.setIsEventRunning(false); this.runNextEvent(); } catch (e) { logError(e, 'error while running next trash'); @@ -389,7 +375,7 @@ class WatchService { } } - removeFilesMatchingTrashingDir(trashingDir: string) { + ignoreFileEventsFromTrashedDir(trashingDir: string) { this.eventQueue = this.eventQueue.filter((event) => event.paths.every((path) => !path.startsWith(trashingDir)) ); @@ -424,22 +410,17 @@ class WatchService { // Batches all the files to be uploaded (or trashed) from the // event queue of same collection as the next event - private batchNextEvent() { - const newEventQueue = [this.eventQueue[0]]; - const len = this.eventQueue.length; - for (let i = 1; i < len; i++) { - if ( - this.eventQueue[i].collectionName === - newEventQueue[0].collectionName && - this.eventQueue[i].type === newEventQueue[0].type - ) { - newEventQueue[0].paths.push(...this.eventQueue[i].paths); - } else { - newEventQueue.push(this.eventQueue[i]); - } + private clubSameCollectionEvents(): EventQueueType { + const event = this.eventQueue.shift(); + while ( + this.eventQueue.length > 0 && + event.collectionName === this.eventQueue[0].collectionName && + event.type === this.eventQueue[0].type + ) { + event.paths = [...event.paths, ...this.eventQueue[0].paths]; + this.eventQueue.shift(); } - newEventQueue.push(...this.eventQueue.slice(len)); - this.eventQueue = newEventQueue; + return event; } } diff --git a/src/types/watch/index.ts b/src/types/watch/index.ts new file mode 100644 index 000000000..a8481a6a6 --- /dev/null +++ b/src/types/watch/index.ts @@ -0,0 +1,14 @@ +export interface WatchMapping { + collectionName: string; + folderPath: string; + files: { + path: string; + id: number; + }[]; +} + +export interface EventQueueType { + type: 'upload' | 'trash'; + collectionName: string; + paths: string[]; +} From aa8483d4da878f98862d1694d413fc9618b41896 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Wed, 8 Jun 2022 23:23:18 +0530 Subject: [PATCH 021/295] rename and refactor --- src/services/upload/uploader.ts | 23 +++++++++++++---------- src/services/watchService.ts | 15 +++++++++++---- src/utils/upload/index.ts | 2 +- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/services/upload/uploader.ts b/src/services/upload/uploader.ts index 166a6e454..0d76a23af 100644 --- a/src/services/upload/uploader.ts +++ b/src/services/upload/uploader.ts @@ -2,7 +2,7 @@ import { EnteFile } from 'types/file'; import { handleUploadError, CustomError } from 'utils/error'; import { logError } from 'utils/sentry'; import { - fileAlreadyInCollection, + findSameFileInCollection, findSameFileInOtherCollection, shouldDedupeAcrossCollection, } from 'utils/upload'; @@ -50,7 +50,7 @@ export default async function uploader( throw Error(CustomError.NO_METADATA); } - const sameFileInSameCollection = fileAlreadyInCollection( + const sameFileInSameCollection = findSameFileInCollection( existingFilesInCollection, metadata ); @@ -84,15 +84,18 @@ export default async function uploader( // iOS exports via album doesn't export files without collection and if user exports all photos, album info is not preserved. // This change allow users to export by albums, upload to ente. And export all photos -> upload files which are not already uploaded // as part of the albums - if ( - shouldDedupeAcrossCollection(fileWithCollection.collection.name) && - fileAlreadyInCollection(existingFiles, metadata) - ) { + if (shouldDedupeAcrossCollection(fileWithCollection.collection.name)) { logUploadInfo(`deduped upload for ${fileNameSize}`); - return { - fileUploadResult: FileUploadResults.ALREADY_UPLOADED, - uploadedFile: fileAlreadyInCollection(existingFiles, metadata), - }; + const sameFileInOtherCollection = findSameFileInCollection( + existingFiles, + metadata + ); + if (sameFileInOtherCollection) { + return { + fileUploadResult: FileUploadResults.ALREADY_UPLOADED, + uploadedFile: sameFileInOtherCollection, + }; + } } logUploadInfo(`reading asset ${fileNameSize}`); diff --git a/src/services/watchService.ts b/src/services/watchService.ts index 7cb9c757b..056fef7b9 100644 --- a/src/services/watchService.ts +++ b/src/services/watchService.ts @@ -306,10 +306,7 @@ class WatchService { private async processTrashEvent() { try { - if (this.trashingDirQueue.length !== 0) { - this.ignoreFileEventsFromTrashedDir(this.trashingDirQueue[0]); - this.trashingDirQueue.shift(); - this.setIsEventRunning(false); + if (this.checkAndRemoveIfFileEventsFromTrashedDir()) { this.runNextEvent(); return; } @@ -375,6 +372,16 @@ class WatchService { } } + checkAndRemoveIfFileEventsFromTrashedDir() { + if (this.trashingDirQueue.length !== 0) { + this.ignoreFileEventsFromTrashedDir(this.trashingDirQueue[0]); + this.trashingDirQueue.shift(); + this.setIsEventRunning(false); + return true; + } + return false; + } + ignoreFileEventsFromTrashedDir(trashingDir: string) { this.eventQueue = this.eventQueue.filter((event) => event.paths.every((path) => !path.startsWith(trashingDir)) diff --git a/src/utils/upload/index.ts b/src/utils/upload/index.ts index 126684840..71a27f47b 100644 --- a/src/utils/upload/index.ts +++ b/src/utils/upload/index.ts @@ -9,7 +9,7 @@ import { FILE_TYPE } from 'constants/file'; const TYPE_JSON = 'json'; const DEDUPE_COLLECTION = new Set(['icloud library', 'icloudlibrary']); -export function fileAlreadyInCollection( +export function findSameFileInCollection( existingFilesInCollection: EnteFile[], newFileMetadata: Metadata ): EnteFile { From ac5c8069f37c306779a5e39cd310bdf94180f3f3 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Thu, 9 Jun 2022 11:06:40 +0530 Subject: [PATCH 022/295] refactor to directly use electron files for upload --- src/services/watchService.ts | 184 +++++++++++++++++++---------------- src/types/watch/index.ts | 5 +- 2 files changed, 104 insertions(+), 85 deletions(-) diff --git a/src/services/watchService.ts b/src/services/watchService.ts index 056fef7b9..cc3be14eb 100644 --- a/src/services/watchService.ts +++ b/src/services/watchService.ts @@ -58,40 +58,13 @@ class WatchService { mappings = await this.filterOutDeletedMappings(mappings); for (const mapping of mappings) { - const filePathsOnDisk: string[] = - await this.ElectronAPIs.getPosixFilePathsFromDir( + const filesOnDisk: ElectronFile[] = + await this.ElectronAPIs.getAllFilesFromDir( mapping.folderPath ); - const filesToUpload = filePathsOnDisk.filter((filePath) => { - return !mapping.files.find( - (file) => file.path === filePath - ); - }); - - const filesToRemove = mapping.files.filter((file) => { - return !filePathsOnDisk.find( - (filePath) => filePath === file.path - ); - }); - - if (filesToUpload.length > 0) { - const event: EventQueueType = { - type: 'upload', - collectionName: mapping.collectionName, - paths: filesToUpload, - }; - this.eventQueue.push(event); - } - - if (filesToRemove.length > 0) { - const event: EventQueueType = { - type: 'trash', - collectionName: mapping.collectionName, - paths: filesToRemove.map((file) => file.path), - }; - this.eventQueue.push(event); - } + this.uploadDiffOfFiles(mapping, filesOnDisk); + this.trashDiffOfFiles(mapping, filesOnDisk); } this.setWatchFunctions(); @@ -102,6 +75,46 @@ class WatchService { } } + private uploadDiffOfFiles( + mapping: WatchMapping, + filesOnDisk: ElectronFile[] + ) { + const filesToUpload = filesOnDisk.filter((electronFile) => { + return !mapping.files.find( + (file) => file.path === electronFile.path + ); + }); + + if (filesToUpload.length > 0) { + const event: EventQueueType = { + type: 'upload', + collectionName: mapping.collectionName, + files: filesToUpload, + }; + this.eventQueue.push(event); + } + } + + private trashDiffOfFiles( + mapping: WatchMapping, + filesOnDisk: ElectronFile[] + ) { + const filesToRemove = mapping.files.filter((file) => { + return !filesOnDisk.find( + (electronFile) => electronFile.path === file.path + ); + }); + + if (filesToRemove.length > 0) { + const event: EventQueueType = { + type: 'trash', + collectionName: mapping.collectionName, + paths: filesToRemove.map((file) => file.path), + }; + this.eventQueue.push(event); + } + } + async filterOutDeletedMappings( mappings: WatchMapping[] ): Promise { @@ -190,19 +203,16 @@ class WatchService { this.uploadRunning = true; this.setCollectionName(this.currentEvent.collectionName); - this.setElectronFiles( - await Promise.all( - this.currentEvent.paths.map(async (path) => { - return await this.ElectronAPIs.getElectronFile(path); - }) - ) - ); + this.setElectronFiles(this.currentEvent.files); } catch (e) { logError(e, 'error while running next upload'); } } async onFileUpload(fileWithCollection: FileWithCollection, file: EnteFile) { + if (!this.isUploadRunning) { + return; + } if (fileWithCollection.isLivePhoto) { this.pathToIDMap.set( (fileWithCollection.livePhotoAssets.image as ElectronFile).path, @@ -240,46 +250,7 @@ class WatchService { const uploadedFiles: WatchMapping['files'] = []; for (const fileWithCollection of filesWithCollection) { - if (fileWithCollection.isLivePhoto) { - const imagePath = ( - fileWithCollection.livePhotoAssets - .image as ElectronFile - ).path; - const videoPath = ( - fileWithCollection.livePhotoAssets - .video as ElectronFile - ).path; - - if ( - this.pathToIDMap.has(imagePath) && - this.pathToIDMap.has(videoPath) - ) { - uploadedFiles.push({ - path: imagePath, - id: this.pathToIDMap.get(imagePath), - }); - uploadedFiles.push({ - path: videoPath, - id: this.pathToIDMap.get(videoPath), - }); - - this.pathToIDMap.delete(imagePath); - this.pathToIDMap.delete(videoPath); - } - } else { - const filePath = ( - fileWithCollection.file as ElectronFile - ).path; - - if (this.pathToIDMap.has(filePath)) { - uploadedFiles.push({ - path: filePath, - id: this.pathToIDMap.get(filePath), - }); - - this.pathToIDMap.delete(filePath); - } - } + this.handleUploadedFile(fileWithCollection, uploadedFiles); } if (uploadedFiles.length > 0) { @@ -304,9 +275,51 @@ class WatchService { } } + private handleUploadedFile( + fileWithCollection: FileWithCollection, + uploadedFiles: { path: string; id: number }[] + ) { + if (fileWithCollection.isLivePhoto) { + const imagePath = ( + fileWithCollection.livePhotoAssets.image as ElectronFile + ).path; + const videoPath = ( + fileWithCollection.livePhotoAssets.video as ElectronFile + ).path; + + if ( + this.pathToIDMap.has(imagePath) && + this.pathToIDMap.has(videoPath) + ) { + uploadedFiles.push({ + path: imagePath, + id: this.pathToIDMap.get(imagePath), + }); + uploadedFiles.push({ + path: videoPath, + id: this.pathToIDMap.get(videoPath), + }); + + this.pathToIDMap.delete(imagePath); + this.pathToIDMap.delete(videoPath); + } + } else { + const filePath = (fileWithCollection.file as ElectronFile).path; + + if (this.pathToIDMap.has(filePath)) { + uploadedFiles.push({ + path: filePath, + id: this.pathToIDMap.get(filePath), + }); + + this.pathToIDMap.delete(filePath); + } + } + } + private async processTrashEvent() { try { - if (this.checkAndRemoveIfFileEventsFromTrashedDir()) { + if (this.checkAndIgnoreIfFileEventsFromTrashedDir()) { this.runNextEvent(); return; } @@ -372,7 +385,7 @@ class WatchService { } } - checkAndRemoveIfFileEventsFromTrashedDir() { + checkAndIgnoreIfFileEventsFromTrashedDir() { if (this.trashingDirQueue.length !== 0) { this.ignoreFileEventsFromTrashedDir(this.trashingDirQueue[0]); this.trashingDirQueue.shift(); @@ -431,20 +444,23 @@ class WatchService { } } -async function diskFileAddedCallback(instance: WatchService, filePath: string) { +async function diskFileAddedCallback( + instance: WatchService, + file: ElectronFile +) { try { - const collectionName = await instance.getCollectionName(filePath); + const collectionName = await instance.getCollectionName(file.path); if (!collectionName) { return; } - console.log('added (upload) to event queue', collectionName, filePath); + console.log('added (upload) to event queue', collectionName, file); const event: EventQueueType = { type: 'upload', collectionName, - paths: [filePath], + files: [file], }; instance.eventQueue.push(event); await debounce(runNextEventByInstance, 300)(instance); diff --git a/src/types/watch/index.ts b/src/types/watch/index.ts index a8481a6a6..b34594e35 100644 --- a/src/types/watch/index.ts +++ b/src/types/watch/index.ts @@ -1,3 +1,5 @@ +import { ElectronFile } from 'types/upload'; + export interface WatchMapping { collectionName: string; folderPath: string; @@ -10,5 +12,6 @@ export interface WatchMapping { export interface EventQueueType { type: 'upload' | 'trash'; collectionName: string; - paths: string[]; + paths?: string[]; + files?: ElectronFile[]; } From 6ba67f1c02d04055fa6e8a76092d75493f245ecc Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Thu, 9 Jun 2022 11:26:33 +0530 Subject: [PATCH 023/295] refactor --- src/services/watchService.ts | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/services/watchService.ts b/src/services/watchService.ts index cc3be14eb..52b2dc5a5 100644 --- a/src/services/watchService.ts +++ b/src/services/watchService.ts @@ -120,7 +120,7 @@ class WatchService { ): Promise { const notDeletedMappings = []; for (const mapping of mappings) { - const mappingExists = await this.ElectronAPIs.isFolderExists( + const mappingExists = await this.ElectronAPIs.doesFolderExists( mapping.folderPath ); if (mappingExists) { @@ -136,7 +136,8 @@ class WatchService { this.ElectronAPIs.registerWatcherFunctions( this, diskFileAddedCallback, - diskFileRemovedCallback + diskFileRemovedCallback, + diskFolderRemovedCallback ); } } @@ -471,8 +472,7 @@ async function diskFileAddedCallback( async function diskFileRemovedCallback( instance: WatchService, - filePath: string, - isDir?: boolean + filePath: string ) { try { const collectionName = await instance.getCollectionName(filePath); @@ -483,14 +483,6 @@ async function diskFileRemovedCallback( return; } - if ( - isDir && - hasMappingSameFolderPath(instance, collectionName, filePath) - ) { - instance.trashingDirQueue.push(filePath); - return; - } - const event: EventQueueType = { type: 'trash', collectionName, @@ -503,6 +495,24 @@ async function diskFileRemovedCallback( } } +async function diskFolderRemovedCallback( + instance: WatchService, + folderPath: string +) { + try { + const collectionName = await instance.getCollectionName(folderPath); + if (!collectionName) { + return; + } + + if (hasMappingSameFolderPath(instance, collectionName, folderPath)) { + instance.trashingDirQueue.push(folderPath); + } + } catch (e) { + logError(e, 'error while calling diskFolderRemovedCallback'); + } +} + const runNextEventByInstance = async (w: WatchService) => { await w.runNextEvent(); }; From ae6251968c346596be0e0a37c3db36d807b286ed Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Thu, 9 Jun 2022 16:07:17 +0530 Subject: [PATCH 024/295] rename API func --- src/services/watchService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/watchService.ts b/src/services/watchService.ts index 52b2dc5a5..339976753 100644 --- a/src/services/watchService.ts +++ b/src/services/watchService.ts @@ -422,7 +422,7 @@ class WatchService { async selectFolder(): Promise { try { - const folderPath = await this.ElectronAPIs.selectFolder(); + const folderPath = await this.ElectronAPIs.selectRootDirectory(); return folderPath; } catch (e) { logError(e, 'error while selecting folder'); From 8b83869e42e1f07b6e2aab94f825bfb8222eb876 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Thu, 9 Jun 2022 16:25:48 +0530 Subject: [PATCH 025/295] added types for ElectronAPIs --- src/services/exportService.ts | 3 +- src/services/importService.ts | 3 +- src/services/watchService.ts | 8 +++-- src/types/electron/index.ts | 65 +++++++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 src/types/electron/index.ts diff --git a/src/services/exportService.ts b/src/services/exportService.ts index 3e413576a..d7c9aa0e5 100644 --- a/src/services/exportService.ts +++ b/src/services/exportService.ts @@ -50,12 +50,13 @@ import { import { User } from 'types/user'; import { FILE_TYPE, TYPE_JPEG, TYPE_JPG } from 'constants/file'; import { ExportType, ExportNotification, RecordType } from 'constants/export'; +import { ElectronAPIsInterface } from 'types/electron'; const LATEST_EXPORT_VERSION = 1; const EXPORT_RECORD_FILE_NAME = 'export_status.json'; class ExportService { - ElectronAPIs: any; + ElectronAPIs: ElectronAPIsInterface; private exportInProgress: Promise<{ paused: boolean }> = null; private exportRecordUpdater = new QueueProcessor(1); diff --git a/src/services/importService.ts b/src/services/importService.ts index 5feb85b81..437024d4b 100644 --- a/src/services/importService.ts +++ b/src/services/importService.ts @@ -1,5 +1,6 @@ import { DESKTOP_UPLOAD_TYPE } from 'components/pages/gallery/Upload'; import { Collection } from 'types/collection'; +import { ElectronAPIsInterface } from 'types/electron'; import { ElectronFile, FileWithCollection } from 'types/upload'; import { runningInBrowser } from 'utils/common'; import { logError } from 'utils/sentry'; @@ -15,7 +16,7 @@ interface selectZipResult { zipPaths: string[]; } class ImportService { - ElectronAPIs: any; + ElectronAPIs: ElectronAPIsInterface; private allElectronAPIsExist: boolean = false; constructor() { diff --git a/src/services/watchService.ts b/src/services/watchService.ts index 339976753..7b5cd5775 100644 --- a/src/services/watchService.ts +++ b/src/services/watchService.ts @@ -7,9 +7,10 @@ import { syncFiles } from './fileService'; import debounce from 'debounce-promise'; import { logError } from 'utils/sentry'; import { EventQueueType, WatchMapping } from 'types/watch'; +import { ElectronAPIsInterface } from 'types/electron'; -class WatchService { - ElectronAPIs: any; +export class WatchService { + ElectronAPIs: ElectronAPIsInterface; allElectronAPIsExist: boolean = false; eventQueue: EventQueueType[] = []; currentEvent: EventQueueType; @@ -24,7 +25,8 @@ class WatchService { setWatchServiceIsRunning: (isRunning: boolean) => void; constructor() { - this.ElectronAPIs = runningInBrowser() && window['ElectronAPIs']; + this.ElectronAPIs = (runningInBrowser() && + window['ElectronAPIs']) as ElectronAPIsInterface; this.allElectronAPIsExist = !!this.ElectronAPIs?.getWatchMappings; } diff --git a/src/types/electron/index.ts b/src/types/electron/index.ts new file mode 100644 index 000000000..c5705b5b5 --- /dev/null +++ b/src/types/electron/index.ts @@ -0,0 +1,65 @@ +import { WatchService } from 'services/watchService'; +import { ElectronFile } from 'types/upload'; +import { WatchMapping } from 'types/watch'; + +export interface ElectronAPIsInterface { + exists: (path: string) => boolean; + checkExistsAndCreateCollectionDir: (dirPath: string) => Promise; + checkExistsAndRename: ( + oldDirPath: string, + newDirPath: string + ) => Promise; + saveStreamToDisk: (path: string, fileStream: ReadableStream) => void; + saveFileToDisk: (path: string, file: any) => Promise; + selectRootDirectory: () => Promise; + sendNotification: (content: string) => void; + showOnTray: (content?: any) => void; + reloadWindow: () => void; + registerResumeExportListener: (resumeExport: () => void) => void; + registerStopExportListener: (abortExport: () => void) => void; + registerPauseExportListener: (pauseExport: () => void) => void; + registerRetryFailedExportListener: (retryFailedExport: () => void) => void; + getExportRecord: (filePath: string) => Promise; + setExportRecord: (filePath: string, data: string) => Promise; + getElectronFile: (filePath: string) => Promise; + showUploadFilesDialog: () => Promise; + showUploadDirsDialog: () => Promise; + getPendingUploads: () => Promise<{ + files: ElectronFile[]; + collectionName: string; + type: string; + }>; + setToUploadFiles: (type: string, filePaths: string[]) => void; + showUploadZipDialog: () => Promise<{ + zipPaths: string[]; + files: ElectronFile[]; + }>; + getElectronFilesFromGoogleZip: ( + filePath: string + ) => Promise; + setToUploadCollection: (collectionName: string) => void; + getAllFilesFromDir: (dirPath: string) => Promise; + getWatchMappings: () => WatchMapping[]; + setWatchMappings: (watchMappings: WatchMapping[]) => void; + addWatchMapping: ( + collectionName: string, + folderPath: string + ) => Promise; + removeWatchMapping: (collectionName: string) => Promise; + registerWatcherFunctions: ( + WatchServiceInstance: WatchService, + addFile: ( + WatchServiceInstance: WatchService, + file: ElectronFile + ) => Promise, + removeFile: ( + WatchServiceInstance: WatchService, + path: string + ) => Promise, + removeFolder: ( + WatchServiceInstance: WatchService, + folderPath: string + ) => Promise + ) => void; + doesFolderExists: (dirPath: string) => Promise; +} From 1c2491dcbb6241f42a466a4ba970a371a49ae199 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Thu, 9 Jun 2022 20:13:37 +0530 Subject: [PATCH 026/295] redesign modal --- src/components/WatchModal.tsx | 242 ++++++++++++++++------------------ 1 file changed, 110 insertions(+), 132 deletions(-) diff --git a/src/components/WatchModal.tsx b/src/components/WatchModal.tsx index 362175f81..ebf8a06a9 100644 --- a/src/components/WatchModal.tsx +++ b/src/components/WatchModal.tsx @@ -1,12 +1,75 @@ +/* eslint-disable */ import React, { useEffect, useState } from 'react'; -import { Button, CircularProgress, Dialog, IconButton } from '@mui/material'; +import { + Button, + CircularProgress, + Dialog, + Icon, + IconButton, +} from '@mui/material'; import watchService from 'services/watchService'; import { MdDelete } from 'react-icons/md'; import ArrowForwardIcon from '@mui/icons-material/ArrowForward'; import Close from '@mui/icons-material/Close'; -import { SpaceBetweenFlex } from 'components/Container'; +import { CenteredFlex, SpaceBetweenFlex } from 'components/Container'; import { WatchMapping } from 'types/watch'; import { AppContext } from 'pages/_app'; +import CheckIcon from '@mui/icons-material/Check'; +import FolderIcon from '@mui/icons-material/Folder'; +import { default as MuiStyled } from '@mui/styled-engine'; +import { Box } from '@mui/system'; + +const ModalHeading = MuiStyled('h3')({ + fontSize: '28px', + marginBottom: '24px', + fontWeight: 'bold', +}); + +const FullWidthButton = MuiStyled(Button)({ + width: '100%', + borderRadius: '4px', +}); + +const PaddedContainer = MuiStyled(Box)({ + padding: '24px', +}); + +const FixedHeightContainer = MuiStyled(Box)({ + height: '450px', + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'space-between', +}); + +const VerticallyCentered = MuiStyled(Box)({ + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', +}); + +const NoFoldersTitleText = MuiStyled('h4')({ + fontSize: '24px', + marginBottom: '16px', + fontWeight: 'bold', +}); + +const BottomMarginSpacer = MuiStyled(Box)({ + marginBottom: '10px', +}); + +function CheckmarkIcon() { + return ( + theme.palette.grey.A200, + }}> + + + ); +} function WatchModal({ watchModalView, @@ -55,141 +118,56 @@ function WatchModal({ }; return ( - -
- -
- Watch Folders -
- - - -
-
-
- -
+ + + + Watched folders + - {inputFolderPath} -
-
-
- -
-
- -
- Current Watch Mappings -
- {appContext.watchServiceIsRunning && ( - - + - )} -
-
- {mappings.map((mapping) => ( -
+ + {mappings.length === 0 ? ( + -
- - {mapping.folderPath}{' '} - - - - {mapping.collectionName} - -
-
+ No folders added yet! + + The folders you add here will monitored to + automatically + + + Upload new files to ente + + + Remove deleted files from ente + + + ) : null} + + + + + + - - handleRemoveWatchMapping(mapping) - } - /> -
-
- ))} -
-
+ marginLeft: '8px', + }}> + Add folder + + + +
); } From 3c7eb0797b1a8a5a804d5af436df1389a15630e1 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Fri, 10 Jun 2022 16:01:59 +0530 Subject: [PATCH 027/295] redesigned watch modal --- src/components/WatchModal.tsx | 244 ++++++++++++++++++++++++++++---- src/themes/darkThemeOptions.tsx | 1 + 2 files changed, 217 insertions(+), 28 deletions(-) diff --git a/src/components/WatchModal.tsx b/src/components/WatchModal.tsx index ebf8a06a9..fd3c4f3e1 100644 --- a/src/components/WatchModal.tsx +++ b/src/components/WatchModal.tsx @@ -1,4 +1,3 @@ -/* eslint-disable */ import React, { useEffect, useState } from 'react'; import { Button, @@ -6,26 +5,30 @@ import { Dialog, Icon, IconButton, + Menu, + MenuItem, } from '@mui/material'; import watchService from 'services/watchService'; -import { MdDelete } from 'react-icons/md'; -import ArrowForwardIcon from '@mui/icons-material/ArrowForward'; import Close from '@mui/icons-material/Close'; import { CenteredFlex, SpaceBetweenFlex } from 'components/Container'; import { WatchMapping } from 'types/watch'; import { AppContext } from 'pages/_app'; import CheckIcon from '@mui/icons-material/Check'; -import FolderIcon from '@mui/icons-material/Folder'; +import FolderOpenIcon from '@mui/icons-material/FolderOpen'; +import MoreHorizIcon from '@mui/icons-material/MoreHoriz'; +import DoNotDisturbOutlinedIcon from '@mui/icons-material/DoNotDisturbOutlined'; import { default as MuiStyled } from '@mui/styled-engine'; import { Box } from '@mui/system'; +import DialogBox from './DialogBox'; const ModalHeading = MuiStyled('h3')({ fontSize: '28px', marginBottom: '24px', - fontWeight: 'bold', + fontWeight: 600, }); -const FullWidthButton = MuiStyled(Button)({ +const FullWidthButtonWithTopMargin = MuiStyled(Button)({ + marginTop: '16px', width: '100%', borderRadius: '4px', }); @@ -42,22 +45,74 @@ const FixedHeightContainer = MuiStyled(Box)({ alignItems: 'space-between', }); -const VerticallyCentered = MuiStyled(Box)({ +const FullHeightVerticallyCentered = MuiStyled(Box)({ display: 'flex', flexDirection: 'column', - justifyContent: 'center', + height: '100%', + overflowY: 'auto', + margin: 0, + padding: 0, + listStyle: 'none', + '&::-webkit-scrollbar': { + width: '6px', + }, + '&::-webkit-scrollbar-thumb': { + backgroundColor: 'slategrey', + }, }); const NoFoldersTitleText = MuiStyled('h4')({ fontSize: '24px', marginBottom: '16px', - fontWeight: 'bold', + fontWeight: 600, }); const BottomMarginSpacer = MuiStyled(Box)({ marginBottom: '10px', }); +const HorizontalFlex = MuiStyled(Box)({ + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', +}); + +const VerticalFlex = MuiStyled(Box)({ + display: 'flex', + flexDirection: 'column', +}); + +const MappingEntryTitle = MuiStyled(Box)({ + fontSize: '16px', + fontWeight: 500, + marginLeft: '12px', + marginRight: '6px', +}); + +const MappingEntryFolder = MuiStyled(Box)({ + fontSize: '14px', + fontWeight: 500, + marginTop: '2px', + marginLeft: '12px', + marginRight: '6px', + marginBottom: '6px', + lineHeight: '18px', +}); + +const DialogBoxHeading = MuiStyled('h4')({ + fontSize: '24px', + marginBottom: '16px', + fontWeight: 600, +}); + +const DialogBoxText = MuiStyled('p')({ + fontWeight: 500, +}); + +const DialogBoxButton = MuiStyled(Button)({ + width: '140px', +}); + function CheckmarkIcon() { return ( void; }) { const [mappings, setMappings] = useState([]); - const [inputFolderPath, setInputFolderPath] = useState(''); - const appContext = React.useContext(AppContext); useEffect(() => { setMappings(watchService.getWatchMappings()); }, []); - const handleFolderSelection = async () => { - const folderPath = await watchService.selectFolder(); - setInputFolderPath(folderPath); + const handleAddFolderClick = async () => { + await handleFolderSelection(); }; - const handleAddWatchMapping = async () => { - if (inputFolderPath.length > 0) { + const handleFolderSelection = async () => { + const folderPath = await watchService.selectFolder(); + await handleAddWatchMapping(folderPath); + }; + + const handleAddWatchMapping = async (inputFolderPath: string) => { + if (inputFolderPath?.length > 0) { await watchService.addWatchMapping( inputFolderPath.substring(inputFolderPath.lastIndexOf('/') + 1), inputFolderPath ); - setInputFolderPath(''); setMappings(watchService.getWatchMappings()); } }; @@ -107,11 +163,11 @@ function WatchModal({ setMappings(watchService.getWatchMappings()); }; - const handleSyncProgressClick = () => { - if (watchService.isUploadRunning()) { - watchService.showProgressView(); - } - }; + // const handleSyncProgressClick = () => { + // if (watchService.isUploadRunning()) { + // watchService.showProgressView(); + // } + // }; const handleClose = () => { setWatchModalView(false); @@ -137,9 +193,9 @@ function WatchModal({ {mappings.length === 0 ? ( - No folders added yet! @@ -153,18 +209,34 @@ function WatchModal({ Remove deleted files from ente - - ) : null} + + ) : ( + + {mappings.map((mapping: WatchMapping) => { + return ( + + ); + })} + + )} - + + Add folder - + @@ -172,4 +244,120 @@ function WatchModal({ ); } +function MappingEntry({ + mapping, + handleRemoveMapping, +}: { + mapping: WatchMapping; + handleRemoveMapping: (mapping: WatchMapping) => void; +}) { + const appContext = React.useContext(AppContext); + + useEffect(() => { + console.log(appContext.watchServiceIsRunning); + }, [appContext.watchServiceIsRunning]); + + const [anchorEl, setAnchorEl] = useState(null); + const [dialogBoxOpen, setDialogBoxOpen] = useState(false); + const open = Boolean(anchorEl); + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + + return ( + <> + + + + + + {mapping.collectionName} + {appContext.watchServiceIsRunning && + watchService.currentEvent?.collectionName === + mapping.collectionName && ( + + )} + + theme.palette.grey[500], + }}> + {mapping.folderPath} + + + + + + + + + setDialogBoxOpen(true)} + sx={{ + fontWeight: 600, + color: (theme) => theme.palette.danger.main, + }}> + + + {' '} + Stop watching + + + setDialogBoxOpen(false)} + attributes={{}}> + Stop watching folder? + + Your existing files will not be deleted, but ente will stop + automatically updating the linked ente album on changes in + this folder. + + + setDialogBoxOpen(false)}> + Cancel + + handleRemoveMapping(mapping)}> + Yes, stop + + + + + ); +} + export default WatchModal; diff --git a/src/themes/darkThemeOptions.tsx b/src/themes/darkThemeOptions.tsx index 7b279cbad..0689a7252 100644 --- a/src/themes/darkThemeOptions.tsx +++ b/src/themes/darkThemeOptions.tsx @@ -149,6 +149,7 @@ const darkThemeOptions = createTheme({ A100: '#ccc', A200: 'rgba(256, 256, 256, 0.24)', A400: '#434343', + 500: 'rgba(256, 256, 256, 0.5)', }, divider: 'rgba(256, 256, 256, 0.12)', }, From 64988d8fafef4c26c2113cbb6c126fb979f85739 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Sun, 12 Jun 2022 17:28:46 +0530 Subject: [PATCH 028/295] minimize upload progress modal when triggered by watch service --- src/components/UploadProgress/index.tsx | 9 ++++++++- src/components/UploadProgress/minimized.tsx | 6 ++++-- src/components/WatchModal.tsx | 6 ------ src/components/pages/gallery/Upload.tsx | 3 --- src/services/watchService.ts | 4 +--- 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/components/UploadProgress/index.tsx b/src/components/UploadProgress/index.tsx index 8996b0c30..7ff18daf4 100644 --- a/src/components/UploadProgress/index.tsx +++ b/src/components/UploadProgress/index.tsx @@ -1,6 +1,6 @@ import { UploadProgressDialog } from './dialog'; import { MinimizedUploadProgress } from './minimized'; -import React, { useContext, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import constants from 'utils/strings/constants'; import { UPLOAD_STAGES } from 'constants/upload'; @@ -13,6 +13,7 @@ import { InProgressUpload, } from 'types/upload/ui'; import UploadProgressContext from 'contexts/uploadProgress'; +import watchService from 'services/watchService'; interface Props { open: boolean; @@ -43,6 +44,12 @@ export default function UploadProgress({ const appContext = useContext(AppContext); const [expanded, setExpanded] = useState(true); + useEffect(() => { + if (appContext.watchServiceIsRunning && watchService.isUploadRunning) { + setExpanded(false); + } + }, [appContext.watchServiceIsRunning]); + function confirmCancelUpload() { appContext.setDialogMessage({ title: constants.STOP_UPLOADS_HEADER, diff --git a/src/components/UploadProgress/minimized.tsx b/src/components/UploadProgress/minimized.tsx index c314dcd98..c40995b53 100644 --- a/src/components/UploadProgress/minimized.tsx +++ b/src/components/UploadProgress/minimized.tsx @@ -1,10 +1,12 @@ import { Snackbar, Paper } from '@mui/material'; -import React from 'react'; +import UploadProgressContext from 'contexts/uploadProgress'; +import React, { useContext } from 'react'; import { UploadProgressHeader } from './header'; export function MinimizedUploadProgress(props) { + const { open } = useContext(UploadProgressContext); return ( { - // if (watchService.isUploadRunning()) { - // watchService.showProgressView(); - // } - // }; - const handleClose = () => { setWatchModalView(false); }; diff --git a/src/components/pages/gallery/Upload.tsx b/src/components/pages/gallery/Upload.tsx index 1f4b13172..ca1b7661c 100644 --- a/src/components/pages/gallery/Upload.tsx +++ b/src/components/pages/gallery/Upload.tsx @@ -132,7 +132,6 @@ export default function Upload(props: Props) { props.setElectronFiles, setCollectionName, props.syncWithRemote, - showProgressView, appContext.setWatchServiceIsRunning ); } @@ -143,8 +142,6 @@ export default function Upload(props: Props) { pendingDesktopUploadCollectionName.current = collectionName; }; - const showProgressView = () => {}; - useEffect(() => { if ( props.electronFiles?.length > 0 || diff --git a/src/services/watchService.ts b/src/services/watchService.ts index 7b5cd5775..e108b245e 100644 --- a/src/services/watchService.ts +++ b/src/services/watchService.ts @@ -38,7 +38,6 @@ export class WatchService { setElectronFiles: (files: ElectronFile[]) => void, setCollectionName: (collectionName: string) => void, syncWithRemote: () => void, - showProgressView: () => void, setWatchServiceIsRunning: (isRunning: boolean) => void ) { if (this.allElectronAPIsExist) { @@ -46,7 +45,6 @@ export class WatchService { this.setElectronFiles = setElectronFiles; this.setCollectionName = setCollectionName; this.syncWithRemote = syncWithRemote; - this.showProgressView = showProgressView; this.setWatchServiceIsRunning = setWatchServiceIsRunning; let mappings = this.getWatchMappings(); @@ -409,7 +407,7 @@ export class WatchService { const mappings = this.getWatchMappings(); const collectionName = mappings.find((mapping) => - filePath.startsWith(mapping.folderPath) + filePath.startsWith(mapping.folderPath + '/') )?.collectionName; if (!collectionName) { From 2ed3b457862cb68c1aa92ab101f884a79e8ee896 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Sun, 12 Jun 2022 18:49:29 +0530 Subject: [PATCH 029/295] added functionality to drag and drop folder to create watch mapping --- src/components/WatchModal.tsx | 45 ++++++++++++++++++++++++++++++++++- src/pages/gallery/index.tsx | 10 +++++++- src/services/watchService.ts | 11 +++++++++ src/types/gallery/index.ts | 1 + 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/components/WatchModal.tsx b/src/components/WatchModal.tsx index a6d993567..7666f041e 100644 --- a/src/components/WatchModal.tsx +++ b/src/components/WatchModal.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import { Button, CircularProgress, @@ -20,6 +20,7 @@ import DoNotDisturbOutlinedIcon from '@mui/icons-material/DoNotDisturbOutlined'; import { default as MuiStyled } from '@mui/styled-engine'; import { Box } from '@mui/system'; import DialogBox from './DialogBox'; +import { GalleryContext } from 'pages/gallery'; const ModalHeading = MuiStyled('h3')({ fontSize: '28px', @@ -134,11 +135,53 @@ function WatchModal({ setWatchModalView: (watchModalView: boolean) => void; }) { const [mappings, setMappings] = useState([]); + const { setDropZoneActive } = useContext(GalleryContext); useEffect(() => { setMappings(watchService.getWatchMappings()); }, []); + useEffect(() => { + if (watchModalView) { + setDropZoneActive(false); + + const handleDrag = (e: DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + }; + const handleDrop = (e: DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + + const files = e.dataTransfer.files; + if (files.length > 0) { + handleFolderDrop(files); + } + }; + addEventListener('dragover', handleDrag); + addEventListener('drop', handleDrop); + + return () => { + setDropZoneActive(true); + removeEventListener('dragover', handleDrag); + removeEventListener('drop', handleDrop); + }; + } + }, [watchModalView]); + + const handleFolderDrop = async (folders: FileList) => { + if (folders.length === 0) { + return; + } + for (let i = 0; i < folders.length; i++) { + const folder: any = folders[i]; + const path = (folder.path as string).replace(/\\/g, '/'); + if (await watchService.isFolder(path)) { + await handleAddWatchMapping(path); + } + } + }; + const handleAddFolderClick = async () => { await handleFolderSelection(); }; diff --git a/src/pages/gallery/index.tsx b/src/pages/gallery/index.tsx index 07807d5c6..e8270f90b 100644 --- a/src/pages/gallery/index.tsx +++ b/src/pages/gallery/index.tsx @@ -126,6 +126,7 @@ const defaultGalleryContext: GalleryContextType = { syncWithRemote: () => null, setNotificationAttributes: () => null, setBlockingLoad: () => null, + setDropZoneActive: () => null, }; export const GalleryContext = createContext( @@ -155,6 +156,8 @@ export default function Gallery() { const [collectionNamerView, setCollectionNamerView] = useState(false); const [search, setSearch] = useState(null); const [uploadInProgress, setUploadInProgress] = useState(false); + const [dropZoneDisabled, setDropZoneDisabled] = useState(false); + const { getRootProps, getInputProps, @@ -164,7 +167,7 @@ export default function Gallery() { } = useDropzone({ noClick: true, noKeyboard: true, - disabled: uploadInProgress, + disabled: uploadInProgress || dropZoneDisabled, }); const [isInSearchMode, setIsInSearchMode] = useState(false); @@ -561,6 +564,10 @@ export default function Gallery() { finishLoading(); }; + const setDropZoneActive = (active: boolean) => { + setDropZoneDisabled(!active); + }; + const openUploader = () => { if (importService.checkAllElectronAPIsExists()) { setUploadTypeSelectorView(true); @@ -578,6 +585,7 @@ export default function Gallery() { syncWithRemote, setNotificationAttributes, setBlockingLoad, + setDropZoneActive, }}> Promise; setNotificationAttributes: (attributes: NotificationAttributes) => void; setBlockingLoad: (value: boolean) => void; + setDropZoneActive: (value: boolean) => void; }; From e14aba2d6f9aff542c1e509984a838261281b5d6 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Sun, 12 Jun 2022 23:52:37 +0530 Subject: [PATCH 030/295] moved strings to string constants --- src/components/WatchModal.tsx | 30 ++++++++++++++------------ src/utils/strings/englishConstants.tsx | 12 +++++++++++ 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/components/WatchModal.tsx b/src/components/WatchModal.tsx index 7666f041e..7b067b2f3 100644 --- a/src/components/WatchModal.tsx +++ b/src/components/WatchModal.tsx @@ -21,6 +21,7 @@ import { default as MuiStyled } from '@mui/styled-engine'; import { Box } from '@mui/system'; import DialogBox from './DialogBox'; import { GalleryContext } from 'pages/gallery'; +import constants from 'utils/strings/constants'; const ModalHeading = MuiStyled('h3')({ fontSize: '28px', @@ -219,7 +220,7 @@ function WatchModal({ - Watched folders + {constants.WATCHED_FOLDERS} - No folders added yet! + {constants.NO_FOLDERS_ADDED} - The folders you add here will monitored to - automatically + {constants.FOLDERS_AUTOMATICALLY_MONITORED} - Upload new files to ente + {' '} + {constants.UPLOAD_NEW_FILES_TO_ENTE} - Remove deleted files from ente + {' '} + {constants.REMOVE_DELETED_FILES_FROM_ENTE} ) : ( @@ -272,7 +274,7 @@ function WatchModal({ style={{ marginLeft: '8px', }}> - Add folder + {constants.ADD_FOLDER} @@ -363,7 +365,7 @@ function MappingEntry({ }}> {' '} - Stop watching + {constants.STOP_WATCHING} setDialogBoxOpen(false)} attributes={{}}> - Stop watching folder? + + {constants.STOP_WATCHING_FOLDER} + - Your existing files will not be deleted, but ente will stop - automatically updating the linked ente album on changes in - this folder. + {constants.STOP_WATCHING_DIALOG_MESSAGE} setDialogBoxOpen(false)}> - Cancel + {constants.CANCEL} handleRemoveMapping(mapping)}> - Yes, stop + {constants.YES_STOP} diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index 5b0038bf8..797591b29 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -772,6 +772,18 @@ const englishConstants = { FAMILY: 'Family', FREE: 'free', OF: 'of', + WATCHED_FOLDERS: 'Watched Folders', + NO_FOLDERS_ADDED: 'No folders added yet!', + FOLDERS_AUTOMATICALLY_MONITORED: + 'The folders you add here will monitored to automatically', + UPLOAD_NEW_FILES_TO_ENTE: 'Upload new files to ente', + REMOVE_DELETED_FILES_FROM_ENTE: 'Remove deleted files from ente', + ADD_FOLDER: 'Add folder', + STOP_WATCHING: 'Stop watching', + STOP_WATCHING_FOLDER: 'Stop watching folder?', + STOP_WATCHING_DIALOG_MESSAGE: + 'Your existing files will not be deleted, but ente will stop automatically updating the linked ente album on changes in this folder.', + YES_STOP: 'Yes, stop', }; export default englishConstants; From accf97c38b566985d2d023fdafeb0b045f8924bd Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Mon, 13 Jun 2022 12:05:34 +0530 Subject: [PATCH 031/295] redundant check --- src/components/WatchModal.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/WatchModal.tsx b/src/components/WatchModal.tsx index 7b067b2f3..4e167b1fb 100644 --- a/src/components/WatchModal.tsx +++ b/src/components/WatchModal.tsx @@ -171,9 +171,6 @@ function WatchModal({ }, [watchModalView]); const handleFolderDrop = async (folders: FileList) => { - if (folders.length === 0) { - return; - } for (let i = 0; i < folders.length; i++) { const folder: any = folders[i]; const path = (folder.path as string).replace(/\\/g, '/'); From 21c5af67525ca7ae295809514420879fcc3206a0 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Mon, 13 Jun 2022 12:23:15 +0530 Subject: [PATCH 032/295] check if on the web --- src/components/Sidebar/UtilitySection.tsx | 22 +++++++++++++++++++++- src/utils/strings/englishConstants.tsx | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/components/Sidebar/UtilitySection.tsx b/src/components/Sidebar/UtilitySection.tsx index ee9c2b590..180b054a6 100644 --- a/src/components/Sidebar/UtilitySection.tsx +++ b/src/components/Sidebar/UtilitySection.tsx @@ -8,6 +8,8 @@ import { PAGES } from 'constants/pages'; import { useRouter } from 'next/router'; import { AppContext } from 'pages/_app'; import WatchModal from '../WatchModal'; +import isElectron from 'is-electron'; +import { downloadApp } from 'utils/common'; export default function UtilitySection({ closeSidebar }) { const router = useRouter(); @@ -24,7 +26,25 @@ export default function UtilitySection({ closeSidebar }) { const openTwoFactorModalView = () => setTwoFactorModalView(true); const closeTwoFactorModalView = () => setTwoFactorModalView(false); - const openWatchModalView = () => setWatchModalView(true); + const openWatchModalView = () => { + if (isElectron()) { + setWatchModalView(true); + } else { + setDialogMessage({ + title: constants.DOWNLOAD_APP, + content: constants.DOWNLOAD_APP_MESSAGE(), + + proceed: { + text: constants.DOWNLOAD, + action: downloadApp, + variant: 'success', + }, + close: { + text: constants.CLOSE, + }, + }); + } + }; const redirectToChangePasswordPage = () => { closeSidebar(); diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index 797591b29..5d4d85f90 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -237,7 +237,7 @@ const englishConstants = { DOWNLOAD_APP_MESSAGE: () => ( <>

- Sorry, this operation is currently only supported on our desktop + Sorry, this feature is currently only supported on our desktop app

From 88a1039419fbf42ca01b68e97405fe938844c188 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 15 Jun 2022 13:58:07 +0530 Subject: [PATCH 033/295] use accent instead of success --- src/components/Sidebar/UtilitySection.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Sidebar/UtilitySection.tsx b/src/components/Sidebar/UtilitySection.tsx index 180b054a6..ba184e4f2 100644 --- a/src/components/Sidebar/UtilitySection.tsx +++ b/src/components/Sidebar/UtilitySection.tsx @@ -37,7 +37,7 @@ export default function UtilitySection({ closeSidebar }) { proceed: { text: constants.DOWNLOAD, action: downloadApp, - variant: 'success', + variant: 'accent', }, close: { text: constants.CLOSE, From 31cccabaccb5c5b11582e7eefa4059e9ca272b6f Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 15 Jun 2022 14:00:49 +0530 Subject: [PATCH 034/295] fix using invalid prop --- src/components/UploadProgress/minimized.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/UploadProgress/minimized.tsx b/src/components/UploadProgress/minimized.tsx index c40995b53..db3398677 100644 --- a/src/components/UploadProgress/minimized.tsx +++ b/src/components/UploadProgress/minimized.tsx @@ -2,11 +2,11 @@ import { Snackbar, Paper } from '@mui/material'; import UploadProgressContext from 'contexts/uploadProgress'; import React, { useContext } from 'react'; import { UploadProgressHeader } from './header'; -export function MinimizedUploadProgress(props) { +export function MinimizedUploadProgress() { const { open } = useContext(UploadProgressContext); return ( Date: Wed, 15 Jun 2022 14:26:32 +0530 Subject: [PATCH 035/295] refactor watchModalComponent --- src/components/Sidebar/UtilitySection.tsx | 13 +- src/components/WatchFolder/checkmarkIcon.tsx | 16 + src/components/WatchFolder/index.tsx | 162 +++++++ src/components/WatchFolder/mappingEntry.tsx | 136 ++++++ .../WatchFolder/styledComponents.tsx | 81 ++++ src/components/WatchModal.tsx | 399 ------------------ 6 files changed, 402 insertions(+), 405 deletions(-) create mode 100644 src/components/WatchFolder/checkmarkIcon.tsx create mode 100644 src/components/WatchFolder/index.tsx create mode 100644 src/components/WatchFolder/mappingEntry.tsx create mode 100644 src/components/WatchFolder/styledComponents.tsx delete mode 100644 src/components/WatchModal.tsx diff --git a/src/components/Sidebar/UtilitySection.tsx b/src/components/Sidebar/UtilitySection.tsx index ba184e4f2..30487da6b 100644 --- a/src/components/Sidebar/UtilitySection.tsx +++ b/src/components/Sidebar/UtilitySection.tsx @@ -7,9 +7,9 @@ import TwoFactorModal from 'components/TwoFactor/Modal'; import { PAGES } from 'constants/pages'; import { useRouter } from 'next/router'; import { AppContext } from 'pages/_app'; -import WatchModal from '../WatchModal'; import isElectron from 'is-electron'; import { downloadApp } from 'utils/common'; +import WatchFolderModal from 'components/WatchFolder'; export default function UtilitySection({ closeSidebar }) { const router = useRouter(); @@ -17,7 +17,7 @@ export default function UtilitySection({ closeSidebar }) { const [recoverModalView, setRecoveryModalView] = useState(false); const [twoFactorModalView, setTwoFactorModalView] = useState(false); - const [watchModalView, setWatchModalView] = useState(false); + const [watchFolderModalView, setWatchFolderModalView] = useState(false); // const [fixLargeThumbsView, setFixLargeThumbsView] = useState(false); const openRecoveryKeyModal = () => setRecoveryModalView(true); @@ -28,7 +28,7 @@ export default function UtilitySection({ closeSidebar }) { const openWatchModalView = () => { if (isElectron()) { - setWatchModalView(true); + setWatchFolderModalView(true); } else { setDialogMessage({ title: constants.DOWNLOAD_APP, @@ -67,6 +67,7 @@ export default function UtilitySection({ closeSidebar }) { close: { variant: 'danger' }, }); + const closeWatchFolderModal = () => setWatchFolderModalView(false); return ( <> @@ -103,9 +104,9 @@ export default function UtilitySection({ closeSidebar }) { closeSidebar={closeSidebar} setLoading={startLoading} /> - {/* theme.palette.grey.A200, + }}> + +
+ ); +} diff --git a/src/components/WatchFolder/index.tsx b/src/components/WatchFolder/index.tsx new file mode 100644 index 000000000..2007a20a2 --- /dev/null +++ b/src/components/WatchFolder/index.tsx @@ -0,0 +1,162 @@ +import { + BottomMarginSpacer, + FixedHeightContainer, + FullHeightVerticallyCentered, + FullWidthButtonWithTopMargin, + ModalHeading, + NoFoldersTitleText, + PaddedContainer, +} from './styledComponents'; +import React, { useContext, useEffect, useState } from 'react'; +import { Dialog, IconButton } from '@mui/material'; +import watchService from 'services/watchService'; +import Close from '@mui/icons-material/Close'; +import { CenteredFlex, SpaceBetweenFlex } from 'components/Container'; +import { WatchMapping } from 'types/watch'; +import { GalleryContext } from 'pages/gallery'; +import constants from 'utils/strings/constants'; +import { CheckmarkIcon } from './checkmarkIcon'; +import { MappingEntry } from './mappingEntry'; + +interface NewType { + open: boolean; + onClose: () => void; +} + +export default function WatchFolderModal({ open, onClose }: NewType) { + const [mappings, setMappings] = useState([]); + const { setDropZoneActive } = useContext(GalleryContext); + + useEffect(() => { + setMappings(watchService.getWatchMappings()); + }, []); + + useEffect(() => { + if (open) { + setDropZoneActive(false); + + const handleDrag = (e: DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + }; + const handleDrop = (e: DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + + const files = e.dataTransfer.files; + if (files.length > 0) { + handleFolderDrop(files); + } + }; + addEventListener('dragover', handleDrag); + addEventListener('drop', handleDrop); + + return () => { + setDropZoneActive(true); + removeEventListener('dragover', handleDrag); + removeEventListener('drop', handleDrop); + }; + } + }, [open]); + + const handleFolderDrop = async (folders: FileList) => { + for (let i = 0; i < folders.length; i++) { + const folder: any = folders[i]; + const path = (folder.path as string).replace(/\\/g, '/'); + if (await watchService.isFolder(path)) { + await handleAddWatchMapping(path); + } + } + }; + + const handleAddFolderClick = async () => { + await handleFolderSelection(); + }; + + const handleFolderSelection = async () => { + const folderPath = await watchService.selectFolder(); + await handleAddWatchMapping(folderPath); + }; + + const handleAddWatchMapping = async (inputFolderPath: string) => { + if (inputFolderPath?.length > 0) { + await watchService.addWatchMapping( + inputFolderPath.substring(inputFolderPath.lastIndexOf('/') + 1), + inputFolderPath + ); + setMappings(watchService.getWatchMappings()); + } + }; + + const handleRemoveWatchMapping = async (mapping: WatchMapping) => { + await watchService.removeWatchMapping(mapping.collectionName); + setMappings(watchService.getWatchMappings()); + }; + + return ( + + + + + {constants.WATCHED_FOLDERS} + + + + + + {mappings.length === 0 ? ( + + + {constants.NO_FOLDERS_ADDED} + + {constants.FOLDERS_AUTOMATICALLY_MONITORED} + + + {' '} + {constants.UPLOAD_NEW_FILES_TO_ENTE} + + + {' '} + {constants.REMOVE_DELETED_FILES_FROM_ENTE} + + + ) : ( + + {mappings.map((mapping: WatchMapping) => { + return ( + + ); + })} + + )} + + + + + + + {constants.ADD_FOLDER} + + + + + + ); +} diff --git a/src/components/WatchFolder/mappingEntry.tsx b/src/components/WatchFolder/mappingEntry.tsx new file mode 100644 index 000000000..976e6009b --- /dev/null +++ b/src/components/WatchFolder/mappingEntry.tsx @@ -0,0 +1,136 @@ +import { + DialogBoxButton, + DialogBoxHeading, + DialogBoxText, + HorizontalFlex, + MappingEntryFolder, + MappingEntryTitle, + VerticalFlex, +} from './styledComponents'; +import React, { useEffect, useState } from 'react'; +import { CircularProgress, IconButton, Menu, MenuItem } from '@mui/material'; +import watchService from 'services/watchService'; +import { SpaceBetweenFlex } from 'components/Container'; +import { WatchMapping } from 'types/watch'; +import { AppContext } from 'pages/_app'; +import FolderOpenIcon from '@mui/icons-material/FolderOpen'; +import MoreHorizIcon from '@mui/icons-material/MoreHoriz'; +import DoNotDisturbOutlinedIcon from '@mui/icons-material/DoNotDisturbOutlined'; +import DialogBox from '../DialogBox'; +import constants from 'utils/strings/constants'; + +export function MappingEntry({ + mapping, + handleRemoveMapping, +}: { + mapping: WatchMapping; + handleRemoveMapping: (mapping: WatchMapping) => void; +}) { + const appContext = React.useContext(AppContext); + + useEffect(() => { + console.log(appContext.watchServiceIsRunning); + }, [appContext.watchServiceIsRunning]); + + const [anchorEl, setAnchorEl] = useState(null); + const [dialogBoxOpen, setDialogBoxOpen] = useState(false); + const open = Boolean(anchorEl); + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + + return ( + <> + + + + + + {mapping.collectionName} + {appContext.watchServiceIsRunning && + watchService.currentEvent?.collectionName === + mapping.collectionName && ( + + )} + + theme.palette.grey[500], + }}> + {mapping.folderPath} + + + + + + + + + setDialogBoxOpen(true)} + sx={{ + fontWeight: 600, + color: (theme) => theme.palette.danger.main, + }}> + + + {' '} + {constants.STOP_WATCHING} + + + setDialogBoxOpen(false)} + attributes={{}}> + + {constants.STOP_WATCHING_FOLDER} + + + {constants.STOP_WATCHING_DIALOG_MESSAGE} + + + setDialogBoxOpen(false)}> + {constants.CANCEL} + + handleRemoveMapping(mapping)}> + {constants.YES_STOP} + + + + + ); +} diff --git a/src/components/WatchFolder/styledComponents.tsx b/src/components/WatchFolder/styledComponents.tsx new file mode 100644 index 000000000..20ca60e1f --- /dev/null +++ b/src/components/WatchFolder/styledComponents.tsx @@ -0,0 +1,81 @@ +import { Box, Button } from '@mui/material'; +import { styled } from '@mui/material/styles'; + +export const ModalHeading = styled('h3')({ + fontSize: '28px', + marginBottom: '24px', + fontWeight: 600, +}); +export const FullWidthButtonWithTopMargin = styled(Button)({ + marginTop: '16px', + width: '100%', + borderRadius: '4px', +}); +export const PaddedContainer = styled(Box)({ + padding: '24px', +}); +export const FixedHeightContainer = styled(Box)({ + height: '450px', + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'space-between', +}); +export const FullHeightVerticallyCentered = styled(Box)({ + display: 'flex', + flexDirection: 'column', + height: '100%', + overflowY: 'auto', + margin: 0, + padding: 0, + listStyle: 'none', + '&::-webkit-scrollbar': { + width: '6px', + }, + '&::-webkit-scrollbar-thumb': { + backgroundColor: 'slategrey', + }, +}); +export const NoFoldersTitleText = styled('h4')({ + fontSize: '24px', + marginBottom: '16px', + fontWeight: 600, +}); +export const BottomMarginSpacer = styled(Box)({ + marginBottom: '10px', +}); +export const HorizontalFlex = styled(Box)({ + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', +}); +export const VerticalFlex = styled(Box)({ + display: 'flex', + flexDirection: 'column', +}); +export const MappingEntryTitle = styled(Box)({ + fontSize: '16px', + fontWeight: 500, + marginLeft: '12px', + marginRight: '6px', +}); +export const MappingEntryFolder = styled(Box)({ + fontSize: '14px', + fontWeight: 500, + marginTop: '2px', + marginLeft: '12px', + marginRight: '6px', + marginBottom: '6px', + lineHeight: '18px', +}); +export const DialogBoxHeading = styled('h4')({ + fontSize: '24px', + marginBottom: '16px', + fontWeight: 600, +}); +export const DialogBoxText = styled('p')({ + fontWeight: 500, +}); +export const DialogBoxButton = styled(Button)({ + width: '140px', +}); diff --git a/src/components/WatchModal.tsx b/src/components/WatchModal.tsx deleted file mode 100644 index 4e167b1fb..000000000 --- a/src/components/WatchModal.tsx +++ /dev/null @@ -1,399 +0,0 @@ -import React, { useContext, useEffect, useState } from 'react'; -import { - Button, - CircularProgress, - Dialog, - Icon, - IconButton, - Menu, - MenuItem, -} from '@mui/material'; -import watchService from 'services/watchService'; -import Close from '@mui/icons-material/Close'; -import { CenteredFlex, SpaceBetweenFlex } from 'components/Container'; -import { WatchMapping } from 'types/watch'; -import { AppContext } from 'pages/_app'; -import CheckIcon from '@mui/icons-material/Check'; -import FolderOpenIcon from '@mui/icons-material/FolderOpen'; -import MoreHorizIcon from '@mui/icons-material/MoreHoriz'; -import DoNotDisturbOutlinedIcon from '@mui/icons-material/DoNotDisturbOutlined'; -import { default as MuiStyled } from '@mui/styled-engine'; -import { Box } from '@mui/system'; -import DialogBox from './DialogBox'; -import { GalleryContext } from 'pages/gallery'; -import constants from 'utils/strings/constants'; - -const ModalHeading = MuiStyled('h3')({ - fontSize: '28px', - marginBottom: '24px', - fontWeight: 600, -}); - -const FullWidthButtonWithTopMargin = MuiStyled(Button)({ - marginTop: '16px', - width: '100%', - borderRadius: '4px', -}); - -const PaddedContainer = MuiStyled(Box)({ - padding: '24px', -}); - -const FixedHeightContainer = MuiStyled(Box)({ - height: '450px', - display: 'flex', - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'space-between', -}); - -const FullHeightVerticallyCentered = MuiStyled(Box)({ - display: 'flex', - flexDirection: 'column', - height: '100%', - overflowY: 'auto', - margin: 0, - padding: 0, - listStyle: 'none', - '&::-webkit-scrollbar': { - width: '6px', - }, - '&::-webkit-scrollbar-thumb': { - backgroundColor: 'slategrey', - }, -}); - -const NoFoldersTitleText = MuiStyled('h4')({ - fontSize: '24px', - marginBottom: '16px', - fontWeight: 600, -}); - -const BottomMarginSpacer = MuiStyled(Box)({ - marginBottom: '10px', -}); - -const HorizontalFlex = MuiStyled(Box)({ - display: 'flex', - flexDirection: 'row', - justifyContent: 'space-between', -}); - -const VerticalFlex = MuiStyled(Box)({ - display: 'flex', - flexDirection: 'column', -}); - -const MappingEntryTitle = MuiStyled(Box)({ - fontSize: '16px', - fontWeight: 500, - marginLeft: '12px', - marginRight: '6px', -}); - -const MappingEntryFolder = MuiStyled(Box)({ - fontSize: '14px', - fontWeight: 500, - marginTop: '2px', - marginLeft: '12px', - marginRight: '6px', - marginBottom: '6px', - lineHeight: '18px', -}); - -const DialogBoxHeading = MuiStyled('h4')({ - fontSize: '24px', - marginBottom: '16px', - fontWeight: 600, -}); - -const DialogBoxText = MuiStyled('p')({ - fontWeight: 500, -}); - -const DialogBoxButton = MuiStyled(Button)({ - width: '140px', -}); - -function CheckmarkIcon() { - return ( - theme.palette.grey.A200, - }}> - - - ); -} - -function WatchModal({ - watchModalView, - setWatchModalView, -}: { - watchModalView: boolean; - setWatchModalView: (watchModalView: boolean) => void; -}) { - const [mappings, setMappings] = useState([]); - const { setDropZoneActive } = useContext(GalleryContext); - - useEffect(() => { - setMappings(watchService.getWatchMappings()); - }, []); - - useEffect(() => { - if (watchModalView) { - setDropZoneActive(false); - - const handleDrag = (e: DragEvent) => { - e.preventDefault(); - e.stopPropagation(); - }; - const handleDrop = (e: DragEvent) => { - e.preventDefault(); - e.stopPropagation(); - - const files = e.dataTransfer.files; - if (files.length > 0) { - handleFolderDrop(files); - } - }; - addEventListener('dragover', handleDrag); - addEventListener('drop', handleDrop); - - return () => { - setDropZoneActive(true); - removeEventListener('dragover', handleDrag); - removeEventListener('drop', handleDrop); - }; - } - }, [watchModalView]); - - const handleFolderDrop = async (folders: FileList) => { - for (let i = 0; i < folders.length; i++) { - const folder: any = folders[i]; - const path = (folder.path as string).replace(/\\/g, '/'); - if (await watchService.isFolder(path)) { - await handleAddWatchMapping(path); - } - } - }; - - const handleAddFolderClick = async () => { - await handleFolderSelection(); - }; - - const handleFolderSelection = async () => { - const folderPath = await watchService.selectFolder(); - await handleAddWatchMapping(folderPath); - }; - - const handleAddWatchMapping = async (inputFolderPath: string) => { - if (inputFolderPath?.length > 0) { - await watchService.addWatchMapping( - inputFolderPath.substring(inputFolderPath.lastIndexOf('/') + 1), - inputFolderPath - ); - setMappings(watchService.getWatchMappings()); - } - }; - - const handleRemoveWatchMapping = async (mapping: WatchMapping) => { - await watchService.removeWatchMapping(mapping.collectionName); - setMappings(watchService.getWatchMappings()); - }; - - const handleClose = () => { - setWatchModalView(false); - }; - - return ( - - - - - {constants.WATCHED_FOLDERS} - - - - - - {mappings.length === 0 ? ( - - - {constants.NO_FOLDERS_ADDED} - - {constants.FOLDERS_AUTOMATICALLY_MONITORED} - - - {' '} - {constants.UPLOAD_NEW_FILES_TO_ENTE} - - - {' '} - {constants.REMOVE_DELETED_FILES_FROM_ENTE} - - - ) : ( - - {mappings.map((mapping: WatchMapping) => { - return ( - - ); - })} - - )} - - - - + - - {constants.ADD_FOLDER} - - - - - - ); -} - -function MappingEntry({ - mapping, - handleRemoveMapping, -}: { - mapping: WatchMapping; - handleRemoveMapping: (mapping: WatchMapping) => void; -}) { - const appContext = React.useContext(AppContext); - - useEffect(() => { - console.log(appContext.watchServiceIsRunning); - }, [appContext.watchServiceIsRunning]); - - const [anchorEl, setAnchorEl] = useState(null); - const [dialogBoxOpen, setDialogBoxOpen] = useState(false); - const open = Boolean(anchorEl); - const handleClick = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }; - const handleClose = () => { - setAnchorEl(null); - }; - - return ( - <> - - - - - - {mapping.collectionName} - {appContext.watchServiceIsRunning && - watchService.currentEvent?.collectionName === - mapping.collectionName && ( - - )} - - theme.palette.grey[500], - }}> - {mapping.folderPath} - - - - - - - - - setDialogBoxOpen(true)} - sx={{ - fontWeight: 600, - color: (theme) => theme.palette.danger.main, - }}> - - - {' '} - {constants.STOP_WATCHING} - - - setDialogBoxOpen(false)} - attributes={{}}> - - {constants.STOP_WATCHING_FOLDER} - - - {constants.STOP_WATCHING_DIALOG_MESSAGE} - - - setDialogBoxOpen(false)}> - {constants.CANCEL} - - handleRemoveMapping(mapping)}> - {constants.YES_STOP} - - - - - ); -} - -export default WatchModal; From bf21d8391a2ee0deab4877684b64301c92cd0259 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 15 Jun 2022 14:29:17 +0530 Subject: [PATCH 036/295] update watch type --- src/services/watchService.ts | 16 ++++++++-------- src/types/watch/index.ts | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/services/watchService.ts b/src/services/watchService.ts index 225a49d50..7b4d9ed36 100644 --- a/src/services/watchService.ts +++ b/src/services/watchService.ts @@ -6,14 +6,14 @@ import { removeFromCollection, syncCollections } from './collectionService'; import { syncFiles } from './fileService'; import debounce from 'debounce-promise'; import { logError } from 'utils/sentry'; -import { EventQueueType, WatchMapping } from 'types/watch'; +import { EventQueueItem, WatchMapping } from 'types/watch'; import { ElectronAPIsInterface } from 'types/electron'; export class WatchService { ElectronAPIs: ElectronAPIsInterface; allElectronAPIsExist: boolean = false; - eventQueue: EventQueueType[] = []; - currentEvent: EventQueueType; + eventQueue: EventQueueItem[] = []; + currentEvent: EventQueueItem; trashingDirQueue: string[] = []; isEventRunning: boolean = false; uploadRunning: boolean = false; @@ -86,7 +86,7 @@ export class WatchService { }); if (filesToUpload.length > 0) { - const event: EventQueueType = { + const event: EventQueueItem = { type: 'upload', collectionName: mapping.collectionName, files: filesToUpload, @@ -106,7 +106,7 @@ export class WatchService { }); if (filesToRemove.length > 0) { - const event: EventQueueType = { + const event: EventQueueItem = { type: 'trash', collectionName: mapping.collectionName, paths: filesToRemove.map((file) => file.path), @@ -431,7 +431,7 @@ export class WatchService { // Batches all the files to be uploaded (or trashed) from the // event queue of same collection as the next event - private clubSameCollectionEvents(): EventQueueType { + private clubSameCollectionEvents(): EventQueueItem { const event = this.eventQueue.shift(); while ( this.eventQueue.length > 0 && @@ -469,7 +469,7 @@ async function diskFileAddedCallback( console.log('added (upload) to event queue', collectionName, file); - const event: EventQueueType = { + const event: EventQueueItem = { type: 'upload', collectionName, files: [file], @@ -494,7 +494,7 @@ async function diskFileRemovedCallback( return; } - const event: EventQueueType = { + const event: EventQueueItem = { type: 'trash', collectionName, paths: [filePath], diff --git a/src/types/watch/index.ts b/src/types/watch/index.ts index b34594e35..fdd29e4d9 100644 --- a/src/types/watch/index.ts +++ b/src/types/watch/index.ts @@ -9,7 +9,7 @@ export interface WatchMapping { }[]; } -export interface EventQueueType { +export interface EventQueueItem { type: 'upload' | 'trash'; collectionName: string; paths?: string[]; From 42c19b39a16d71a302e88ba680deb415b23882ac Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 15 Jun 2022 14:30:31 +0530 Subject: [PATCH 037/295] rename to watchFolder --- src/components/UploadProgress/index.tsx | 2 +- src/components/WatchFolder/index.tsx | 4 ++-- src/components/WatchFolder/mappingEntry.tsx | 4 ++-- src/components/pages/gallery/Upload.tsx | 2 +- src/services/upload/uploadManager.ts | 2 +- src/services/{watchService.ts => watchFolderService.ts} | 2 +- src/types/electron/index.ts | 4 ++-- src/types/{watch => watchFolder}/index.ts | 0 8 files changed, 10 insertions(+), 10 deletions(-) rename src/services/{watchService.ts => watchFolderService.ts} (99%) rename src/types/{watch => watchFolder}/index.ts (100%) diff --git a/src/components/UploadProgress/index.tsx b/src/components/UploadProgress/index.tsx index 7ff18daf4..293b6fb4b 100644 --- a/src/components/UploadProgress/index.tsx +++ b/src/components/UploadProgress/index.tsx @@ -13,7 +13,7 @@ import { InProgressUpload, } from 'types/upload/ui'; import UploadProgressContext from 'contexts/uploadProgress'; -import watchService from 'services/watchService'; +import watchService from 'services/watchFolderService'; interface Props { open: boolean; diff --git a/src/components/WatchFolder/index.tsx b/src/components/WatchFolder/index.tsx index 2007a20a2..fda40136b 100644 --- a/src/components/WatchFolder/index.tsx +++ b/src/components/WatchFolder/index.tsx @@ -9,10 +9,10 @@ import { } from './styledComponents'; import React, { useContext, useEffect, useState } from 'react'; import { Dialog, IconButton } from '@mui/material'; -import watchService from 'services/watchService'; +import watchService from 'services/watchFolderService'; import Close from '@mui/icons-material/Close'; import { CenteredFlex, SpaceBetweenFlex } from 'components/Container'; -import { WatchMapping } from 'types/watch'; +import { WatchMapping } from 'types/watchFolder'; import { GalleryContext } from 'pages/gallery'; import constants from 'utils/strings/constants'; import { CheckmarkIcon } from './checkmarkIcon'; diff --git a/src/components/WatchFolder/mappingEntry.tsx b/src/components/WatchFolder/mappingEntry.tsx index 976e6009b..510dbdea8 100644 --- a/src/components/WatchFolder/mappingEntry.tsx +++ b/src/components/WatchFolder/mappingEntry.tsx @@ -9,9 +9,9 @@ import { } from './styledComponents'; import React, { useEffect, useState } from 'react'; import { CircularProgress, IconButton, Menu, MenuItem } from '@mui/material'; -import watchService from 'services/watchService'; +import watchService from 'services/watchFolderService'; import { SpaceBetweenFlex } from 'components/Container'; -import { WatchMapping } from 'types/watch'; +import { WatchMapping } from 'types/watchFolder'; import { AppContext } from 'pages/_app'; import FolderOpenIcon from '@mui/icons-material/FolderOpen'; import MoreHorizIcon from '@mui/icons-material/MoreHoriz'; diff --git a/src/components/pages/gallery/Upload.tsx b/src/components/pages/gallery/Upload.tsx index ca1b7661c..77b6d3420 100644 --- a/src/components/pages/gallery/Upload.tsx +++ b/src/components/pages/gallery/Upload.tsx @@ -24,7 +24,7 @@ import UploadTypeSelector from '../../UploadTypeSelector'; import Router from 'next/router'; import { isCanvasBlocked } from 'utils/upload/isCanvasBlocked'; import { downloadApp } from 'utils/common'; -import watchService from 'services/watchService'; +import watchService from 'services/watchFolderService'; import DiscFullIcon from '@mui/icons-material/DiscFull'; import { NotificationAttributes } from 'types/Notification'; import { diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index dd884eaf5..121faf885 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -38,7 +38,7 @@ import uiService from './uiService'; import { logUploadInfo } from 'utils/upload'; import isElectron from 'is-electron'; import ImportService from 'services/importService'; -import watchService from 'services/watchService'; +import watchService from 'services/watchFolderService'; import { ProgressUpdater } from 'types/upload/ui'; const MAX_CONCURRENT_UPLOADS = 4; diff --git a/src/services/watchService.ts b/src/services/watchFolderService.ts similarity index 99% rename from src/services/watchService.ts rename to src/services/watchFolderService.ts index 7b4d9ed36..d4119347e 100644 --- a/src/services/watchService.ts +++ b/src/services/watchFolderService.ts @@ -6,7 +6,7 @@ import { removeFromCollection, syncCollections } from './collectionService'; import { syncFiles } from './fileService'; import debounce from 'debounce-promise'; import { logError } from 'utils/sentry'; -import { EventQueueItem, WatchMapping } from 'types/watch'; +import { EventQueueItem, WatchMapping } from 'types/watchFolder'; import { ElectronAPIsInterface } from 'types/electron'; export class WatchService { diff --git a/src/types/electron/index.ts b/src/types/electron/index.ts index c5705b5b5..448439a1c 100644 --- a/src/types/electron/index.ts +++ b/src/types/electron/index.ts @@ -1,6 +1,6 @@ -import { WatchService } from 'services/watchService'; +import { WatchService } from 'services/watchFolderService'; import { ElectronFile } from 'types/upload'; -import { WatchMapping } from 'types/watch'; +import { WatchMapping } from 'types/watchFolder'; export interface ElectronAPIsInterface { exists: (path: string) => boolean; diff --git a/src/types/watch/index.ts b/src/types/watchFolder/index.ts similarity index 100% rename from src/types/watch/index.ts rename to src/types/watchFolder/index.ts From 9120ebb7d3154e3286c92a4a5d16c7e66d4200f0 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Wed, 15 Jun 2022 16:20:04 +0530 Subject: [PATCH 038/295] fix clubbing same events --- src/services/watchFolderService.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/services/watchFolderService.ts b/src/services/watchFolderService.ts index d4119347e..5ec0ad240 100644 --- a/src/services/watchFolderService.ts +++ b/src/services/watchFolderService.ts @@ -438,7 +438,11 @@ export class WatchService { event.collectionName === this.eventQueue[0].collectionName && event.type === this.eventQueue[0].type ) { - event.paths = [...event.paths, ...this.eventQueue[0].paths]; + if (event.type === 'trash') { + event.paths = [...event.paths, ...this.eventQueue[0].paths]; + } else { + event.files = [...event.files, ...this.eventQueue[0].files]; + } this.eventQueue.shift(); } return event; From 5d9d92d386365b73014f1501a5f866659fac460c Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Fri, 17 Jun 2022 00:21:08 +0530 Subject: [PATCH 039/295] added overlay for add folder mapping drag and drop screen --- src/components/FullScreenDropZone.tsx | 34 +++++++++++++++++++++-- src/components/Sidebar/UtilitySection.tsx | 14 ++++++---- src/components/WatchFolder/index.tsx | 32 ++++----------------- src/pages/_app.tsx | 19 ++++++++++--- src/pages/gallery/index.tsx | 6 +++- src/utils/strings/englishConstants.tsx | 1 + 6 files changed, 66 insertions(+), 40 deletions(-) diff --git a/src/components/FullScreenDropZone.tsx b/src/components/FullScreenDropZone.tsx index d51ad0fcf..391785410 100644 --- a/src/components/FullScreenDropZone.tsx +++ b/src/components/FullScreenDropZone.tsx @@ -1,7 +1,8 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useContext } from 'react'; import { styled } from '@mui/material'; import constants from 'utils/strings/constants'; import CloseIcon from '@mui/icons-material/Close'; +import { AppContext } from 'pages/_app'; const CloseButtonWrapper = styled('div')` position: absolute; @@ -42,6 +43,8 @@ type Props = React.PropsWithChildren<{ }>; export default function FullScreenDropZone(props: Props) { + const appContext = useContext(AppContext); + const [isDragActive, setIsDragActive] = useState(false); const onDragEnter = () => setIsDragActive(true); const onDragLeave = () => setIsDragActive(false); @@ -53,18 +56,43 @@ export default function FullScreenDropZone(props: Props) { } }); }, []); + + useEffect(() => { + const handleWatchFolderDrop = (e: DragEvent) => { + if (!appContext.watchModalView) { + return; + } + + e.preventDefault(); + e.stopPropagation(); + const files = e.dataTransfer.files; + if (files.length > 0) { + appContext.setWatchModalFiles(files); + } + }; + + addEventListener('drop', handleWatchFolderDrop); + return () => { + removeEventListener('drop', handleWatchFolderDrop); + }; + }, [appContext.watchModalView]); + return ( - + {!appContext.watchModalView ? ( + + ) : null} {isDragActive && ( - {constants.UPLOAD_DROPZONE_MESSAGE} + {appContext.watchModalView + ? constants.WATCH_FOLDER_DROPZONE_MESSAGE + : constants.UPLOAD_DROPZONE_MESSAGE} )} {props.children} diff --git a/src/components/Sidebar/UtilitySection.tsx b/src/components/Sidebar/UtilitySection.tsx index 30487da6b..d1eadb44e 100644 --- a/src/components/Sidebar/UtilitySection.tsx +++ b/src/components/Sidebar/UtilitySection.tsx @@ -13,11 +13,15 @@ import WatchFolderModal from 'components/WatchFolder'; export default function UtilitySection({ closeSidebar }) { const router = useRouter(); - const { setDialogMessage, startLoading } = useContext(AppContext); + const { + setDialogMessage, + startLoading, + watchModalView, + setWatchModalView, + } = useContext(AppContext); const [recoverModalView, setRecoveryModalView] = useState(false); const [twoFactorModalView, setTwoFactorModalView] = useState(false); - const [watchFolderModalView, setWatchFolderModalView] = useState(false); // const [fixLargeThumbsView, setFixLargeThumbsView] = useState(false); const openRecoveryKeyModal = () => setRecoveryModalView(true); @@ -28,7 +32,7 @@ export default function UtilitySection({ closeSidebar }) { const openWatchModalView = () => { if (isElectron()) { - setWatchFolderModalView(true); + setWatchModalView(true); } else { setDialogMessage({ title: constants.DOWNLOAD_APP, @@ -67,7 +71,7 @@ export default function UtilitySection({ closeSidebar }) { close: { variant: 'danger' }, }); - const closeWatchFolderModal = () => setWatchFolderModalView(false); + const closeWatchFolderModal = () => setWatchModalView(false); return ( <> @@ -105,7 +109,7 @@ export default function UtilitySection({ closeSidebar }) { setLoading={startLoading} /> diff --git a/src/components/WatchFolder/index.tsx b/src/components/WatchFolder/index.tsx index fda40136b..6a1cf9c6d 100644 --- a/src/components/WatchFolder/index.tsx +++ b/src/components/WatchFolder/index.tsx @@ -13,7 +13,7 @@ import watchService from 'services/watchFolderService'; import Close from '@mui/icons-material/Close'; import { CenteredFlex, SpaceBetweenFlex } from 'components/Container'; import { WatchMapping } from 'types/watchFolder'; -import { GalleryContext } from 'pages/gallery'; +import { AppContext } from 'pages/_app'; import constants from 'utils/strings/constants'; import { CheckmarkIcon } from './checkmarkIcon'; import { MappingEntry } from './mappingEntry'; @@ -25,39 +25,17 @@ interface NewType { export default function WatchFolderModal({ open, onClose }: NewType) { const [mappings, setMappings] = useState([]); - const { setDropZoneActive } = useContext(GalleryContext); + const appContext = useContext(AppContext); useEffect(() => { setMappings(watchService.getWatchMappings()); }, []); useEffect(() => { - if (open) { - setDropZoneActive(false); - - const handleDrag = (e: DragEvent) => { - e.preventDefault(); - e.stopPropagation(); - }; - const handleDrop = (e: DragEvent) => { - e.preventDefault(); - e.stopPropagation(); - - const files = e.dataTransfer.files; - if (files.length > 0) { - handleFolderDrop(files); - } - }; - addEventListener('dragover', handleDrag); - addEventListener('drop', handleDrop); - - return () => { - setDropZoneActive(true); - removeEventListener('dragover', handleDrag); - removeEventListener('drop', handleDrop); - }; + if (appContext.watchModalFiles.length > 0) { + handleFolderDrop(appContext.watchModalFiles); } - }, [open]); + }, [appContext.watchModalFiles]); const handleFolderDrop = async (folders: FileList) => { for (let i = 0; i < folders.length; i++) { diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 87afed52b..aef1a787b 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -20,8 +20,8 @@ import { styled, ThemeProvider } from '@mui/material/styles'; import darkThemeOptions from 'themes/darkThemeOptions'; import { CssBaseline } from '@mui/material'; // eslint-disable-next-line @typescript-eslint/no-unused-vars -import * as types from 'styled-components/cssprop'; // need to css prop on styled component -import { SetDialogBoxAttributes, DialogBoxAttributes } from 'types/dialogBox'; +// import * as types from 'styled-components/cssprop'; // need to css prop on styled component +import { DialogBoxAttributes, SetDialogBoxAttributes } from 'types/dialogBox'; import { getFamilyPortalRedirectURL, getRoadmapRedirectURL, @@ -54,6 +54,10 @@ type AppContextType = { setDialogMessage: SetDialogBoxAttributes; watchServiceIsRunning: boolean; setWatchServiceIsRunning: (isRunning: boolean) => void; + watchModalView: boolean; + setWatchModalView: (isOpen: boolean) => void; + watchModalFiles: FileList; + setWatchModalFiles: (files: FileList) => void; }; export enum FLASH_MESSAGE_TYPE { @@ -89,6 +93,10 @@ export default function App({ Component, err }) { const [dialogMessage, setDialogMessage] = useState(); const [messageDialogView, setMessageDialogView] = useState(false); const [watchServiceIsRunning, setWatchServiceIsRunning] = useState(false); + const [watchModalView, setWatchModalView] = useState(false); + const [watchModalFiles, setWatchModalFiles] = useState( + new FileList() + ); useEffect(() => { if ( @@ -148,8 +156,7 @@ export default function App({ Component, err }) { typeof redirectMap.get(redirect) === 'function' ) { const redirectAction = redirectMap.get(redirect); - const url = await redirectAction(); - window.location.href = url; + window.location.href = await redirectAction(); } else { logError(CustomError.BAD_REQUEST, 'invalid redirection', { redirect, @@ -280,6 +287,10 @@ export default function App({ Component, err }) { setDialogMessage, watchServiceIsRunning, setWatchServiceIsRunning, + watchModalView, + setWatchModalView, + watchModalFiles, + setWatchModalFiles, }}> {loading ? ( diff --git a/src/pages/gallery/index.tsx b/src/pages/gallery/index.tsx index e817bbf96..a1292ca24 100644 --- a/src/pages/gallery/index.tsx +++ b/src/pages/gallery/index.tsx @@ -279,7 +279,11 @@ export default function Gallery() { [notificationAttributes] ); - useEffect(() => setDroppedFiles(acceptedFiles), [acceptedFiles]); + useEffect(() => { + if (!appContext.watchModalView) { + setDroppedFiles(acceptedFiles); + } + }, [acceptedFiles]); useEffect(() => { if (typeof activeCollection === 'undefined') { diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index 9203d638b..3db1ca778 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -144,6 +144,7 @@ const englishConstants = { UPLOAD_FIRST_PHOTO_DESCRIPTION: 'preserve your first memory with ente', UPLOAD_FIRST_PHOTO: 'Preserve', UPLOAD_DROPZONE_MESSAGE: 'Drop to backup your files', + WATCH_FOLDER_DROPZONE_MESSAGE: 'Drop to add watched folder', CONFIRM_DELETE: 'Confirm deletion', DELETE_MESSAGE: `The selected files will be permanently deleted and can't be restored `, DELETE_FILE: 'Delete files', From 17dd41152e21df8e0054ebf0fb58622a20180088 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Fri, 17 Jun 2022 11:14:12 +0530 Subject: [PATCH 040/295] fix build and bug --- src/components/WatchFolder/index.tsx | 5 ++++- src/pages/_app.tsx | 4 +--- src/services/watchFolderService.ts | 9 +++++---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/components/WatchFolder/index.tsx b/src/components/WatchFolder/index.tsx index 6a1cf9c6d..0e9233b3d 100644 --- a/src/components/WatchFolder/index.tsx +++ b/src/components/WatchFolder/index.tsx @@ -32,7 +32,10 @@ export default function WatchFolderModal({ open, onClose }: NewType) { }, []); useEffect(() => { - if (appContext.watchModalFiles.length > 0) { + if ( + appContext.watchModalFiles && + appContext.watchModalFiles.length > 0 + ) { handleFolderDrop(appContext.watchModalFiles); } }, [appContext.watchModalFiles]); diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index aef1a787b..9d1178e60 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -94,9 +94,7 @@ export default function App({ Component, err }) { const [messageDialogView, setMessageDialogView] = useState(false); const [watchServiceIsRunning, setWatchServiceIsRunning] = useState(false); const [watchModalView, setWatchModalView] = useState(false); - const [watchModalFiles, setWatchModalFiles] = useState( - new FileList() - ); + const [watchModalFiles, setWatchModalFiles] = useState(null); useEffect(() => { if ( diff --git a/src/services/watchFolderService.ts b/src/services/watchFolderService.ts index 5ec0ad240..16816cb7d 100644 --- a/src/services/watchFolderService.ts +++ b/src/services/watchFolderService.ts @@ -261,10 +261,11 @@ export class WatchService { mapping.collectionName === this.currentEvent.collectionName ); - mapping.files = [...mapping.files, ...uploadedFiles]; - - this.ElectronAPIs.setWatchMappings(mappings); - this.syncWithRemote(); + if (mapping) { + mapping.files = [...mapping.files, ...uploadedFiles]; + this.ElectronAPIs.setWatchMappings(mappings); + this.syncWithRemote(); + } } this.setIsEventRunning(false); From 6cef3352645b2059b8db44c920fbed68095d44ad Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Fri, 17 Jun 2022 12:05:34 +0530 Subject: [PATCH 041/295] clear electron store on logout --- src/services/userService.ts | 11 +++++++++++ src/types/electron/index.ts | 1 + 2 files changed, 12 insertions(+) diff --git a/src/services/userService.ts b/src/services/userService.ts index 496ec9c7e..30508997c 100644 --- a/src/services/userService.ts +++ b/src/services/userService.ts @@ -19,6 +19,9 @@ import { } from 'types/user'; import { getFamilyData, isPartOfFamily } from 'utils/billing'; import { ServerErrorCodes } from 'utils/error'; +import isElectron from 'is-electron'; +import { runningInBrowser } from '../utils/common'; +import { ElectronAPIsInterface } from '../types/electron'; const ENDPOINT = getEndpoint(); @@ -121,6 +124,14 @@ export const logoutUser = async () => { // ignore } await clearFiles(); + + if (isElectron()) { + const ElectronAPIs = (runningInBrowser() && + window['ElectronAPIs']) as ElectronAPIsInterface; + if (ElectronAPIs && ElectronAPIs.clearElectronStore) { + ElectronAPIs.clearElectronStore(); + } + } router.push(PAGES.ROOT); } catch (e) { logError(e, 'logoutUser failed'); diff --git a/src/types/electron/index.ts b/src/types/electron/index.ts index 448439a1c..9851b4334 100644 --- a/src/types/electron/index.ts +++ b/src/types/electron/index.ts @@ -62,4 +62,5 @@ export interface ElectronAPIsInterface { ) => Promise ) => void; doesFolderExists: (dirPath: string) => Promise; + clearElectronStore: () => void; } From 9f0cadacaa3c11292773e74dff3ecd1425cd464a Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 15 Jun 2022 19:28:49 +0530 Subject: [PATCH 042/295] udpate type --- src/components/WatchFolder/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/WatchFolder/index.tsx b/src/components/WatchFolder/index.tsx index 0e9233b3d..62fa01231 100644 --- a/src/components/WatchFolder/index.tsx +++ b/src/components/WatchFolder/index.tsx @@ -18,12 +18,12 @@ import constants from 'utils/strings/constants'; import { CheckmarkIcon } from './checkmarkIcon'; import { MappingEntry } from './mappingEntry'; -interface NewType { +interface Iprops { open: boolean; onClose: () => void; } -export default function WatchFolderModal({ open, onClose }: NewType) { +export default function WatchFolderModal({ open, onClose }: Iprops) { const [mappings, setMappings] = useState([]); const appContext = useContext(AppContext); From 03e29c6227621fabc0b77a8a20d00ec1dbe3a040 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sun, 19 Jun 2022 13:20:19 +0530 Subject: [PATCH 043/295] refactor components --- src/components/WatchFolder/checkmarkIcon.tsx | 13 +- src/components/WatchFolder/index.tsx | 111 +++++--------- src/components/WatchFolder/mappingEntry.tsx | 136 ------------------ .../WatchFolder/mappingEntry/entryHeading.tsx | 24 ++++ .../WatchFolder/mappingEntry/index.tsx | 55 +++++++ .../mappingEntry/syncProgressIcon.tsx | 13 ++ .../WatchFolder/mappingEntryOptions.tsx | 55 +++++++ src/components/WatchFolder/mappingList.tsx | 19 +++ .../WatchFolder/noMappingsContent.tsx | 24 ++++ .../WatchFolder/styledComponents.tsx | 85 +++-------- 10 files changed, 246 insertions(+), 289 deletions(-) delete mode 100644 src/components/WatchFolder/mappingEntry.tsx create mode 100644 src/components/WatchFolder/mappingEntry/entryHeading.tsx create mode 100644 src/components/WatchFolder/mappingEntry/index.tsx create mode 100644 src/components/WatchFolder/mappingEntry/syncProgressIcon.tsx create mode 100644 src/components/WatchFolder/mappingEntryOptions.tsx create mode 100644 src/components/WatchFolder/mappingList.tsx create mode 100644 src/components/WatchFolder/noMappingsContent.tsx diff --git a/src/components/WatchFolder/checkmarkIcon.tsx b/src/components/WatchFolder/checkmarkIcon.tsx index 28bf54fdb..75805d9c9 100644 --- a/src/components/WatchFolder/checkmarkIcon.tsx +++ b/src/components/WatchFolder/checkmarkIcon.tsx @@ -1,16 +1,13 @@ import React from 'react'; -import { Icon } from '@mui/material'; import CheckIcon from '@mui/icons-material/Check'; export function CheckmarkIcon() { return ( - theme.palette.grey.A200, - }}> - - + marginRight: '8px', + color: (theme) => theme.palette.secondary.main, + }} + /> ); } diff --git a/src/components/WatchFolder/index.tsx b/src/components/WatchFolder/index.tsx index 62fa01231..44e3b68cb 100644 --- a/src/components/WatchFolder/index.tsx +++ b/src/components/WatchFolder/index.tsx @@ -1,22 +1,13 @@ -import { - BottomMarginSpacer, - FixedHeightContainer, - FullHeightVerticallyCentered, - FullWidthButtonWithTopMargin, - ModalHeading, - NoFoldersTitleText, - PaddedContainer, -} from './styledComponents'; +import { MappingList } from './mappingList'; +import { NoMappingsContent } from './noMappingsContent'; import React, { useContext, useEffect, useState } from 'react'; -import { Dialog, IconButton } from '@mui/material'; +import { Button, DialogActions, DialogContent } from '@mui/material'; import watchService from 'services/watchFolderService'; -import Close from '@mui/icons-material/Close'; -import { CenteredFlex, SpaceBetweenFlex } from 'components/Container'; import { WatchMapping } from 'types/watchFolder'; import { AppContext } from 'pages/_app'; import constants from 'utils/strings/constants'; -import { CheckmarkIcon } from './checkmarkIcon'; -import { MappingEntry } from './mappingEntry'; +import DialogBoxBase from 'components/DialogBox/base'; +import DialogTitleWithCloseButton from 'components/DialogBox/titleWithCloseButton'; interface Iprops { open: boolean; @@ -75,69 +66,35 @@ export default function WatchFolderModal({ open, onClose }: Iprops) { }; return ( - - - - - {constants.WATCHED_FOLDERS} - - - - + + + {constants.WATCHED_FOLDERS} + + + {mappings.length !== 0 ? ( + + ) : ( + + )} + - {mappings.length === 0 ? ( - - - {constants.NO_FOLDERS_ADDED} - - {constants.FOLDERS_AUTOMATICALLY_MONITORED} - - - {' '} - {constants.UPLOAD_NEW_FILES_TO_ENTE} - - - {' '} - {constants.REMOVE_DELETED_FILES_FROM_ENTE} - - - ) : ( - - {mappings.map((mapping: WatchMapping) => { - return ( - - ); - })} - - )} - - - - + - - {constants.ADD_FOLDER} - - - - - + + + + ); } diff --git a/src/components/WatchFolder/mappingEntry.tsx b/src/components/WatchFolder/mappingEntry.tsx deleted file mode 100644 index 510dbdea8..000000000 --- a/src/components/WatchFolder/mappingEntry.tsx +++ /dev/null @@ -1,136 +0,0 @@ -import { - DialogBoxButton, - DialogBoxHeading, - DialogBoxText, - HorizontalFlex, - MappingEntryFolder, - MappingEntryTitle, - VerticalFlex, -} from './styledComponents'; -import React, { useEffect, useState } from 'react'; -import { CircularProgress, IconButton, Menu, MenuItem } from '@mui/material'; -import watchService from 'services/watchFolderService'; -import { SpaceBetweenFlex } from 'components/Container'; -import { WatchMapping } from 'types/watchFolder'; -import { AppContext } from 'pages/_app'; -import FolderOpenIcon from '@mui/icons-material/FolderOpen'; -import MoreHorizIcon from '@mui/icons-material/MoreHoriz'; -import DoNotDisturbOutlinedIcon from '@mui/icons-material/DoNotDisturbOutlined'; -import DialogBox from '../DialogBox'; -import constants from 'utils/strings/constants'; - -export function MappingEntry({ - mapping, - handleRemoveMapping, -}: { - mapping: WatchMapping; - handleRemoveMapping: (mapping: WatchMapping) => void; -}) { - const appContext = React.useContext(AppContext); - - useEffect(() => { - console.log(appContext.watchServiceIsRunning); - }, [appContext.watchServiceIsRunning]); - - const [anchorEl, setAnchorEl] = useState(null); - const [dialogBoxOpen, setDialogBoxOpen] = useState(false); - const open = Boolean(anchorEl); - const handleClick = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }; - const handleClose = () => { - setAnchorEl(null); - }; - - return ( - <> - - - - - - {mapping.collectionName} - {appContext.watchServiceIsRunning && - watchService.currentEvent?.collectionName === - mapping.collectionName && ( - - )} - - theme.palette.grey[500], - }}> - {mapping.folderPath} - - - - - - - - - setDialogBoxOpen(true)} - sx={{ - fontWeight: 600, - color: (theme) => theme.palette.danger.main, - }}> - - - {' '} - {constants.STOP_WATCHING} - - - setDialogBoxOpen(false)} - attributes={{}}> - - {constants.STOP_WATCHING_FOLDER} - - - {constants.STOP_WATCHING_DIALOG_MESSAGE} - - - setDialogBoxOpen(false)}> - {constants.CANCEL} - - handleRemoveMapping(mapping)}> - {constants.YES_STOP} - - - - - ); -} diff --git a/src/components/WatchFolder/mappingEntry/entryHeading.tsx b/src/components/WatchFolder/mappingEntry/entryHeading.tsx new file mode 100644 index 000000000..fb6347021 --- /dev/null +++ b/src/components/WatchFolder/mappingEntry/entryHeading.tsx @@ -0,0 +1,24 @@ +import React, { useContext } from 'react'; +import { Typography } from '@mui/material'; +import watchService from 'services/watchFolderService'; +import { AppContext } from 'pages/_app'; +import { SyncProgressIcon } from './syncProgressIcon'; + +export function EntryHeading({ mapping }) { + const appContext = useContext(AppContext); + return ( + + <> + {mapping.collectionName} + {appContext.watchServiceIsRunning && + watchService.currentEvent?.collectionName === + mapping.collectionName && } + + + ); +} diff --git a/src/components/WatchFolder/mappingEntry/index.tsx b/src/components/WatchFolder/mappingEntry/index.tsx new file mode 100644 index 000000000..f20badd38 --- /dev/null +++ b/src/components/WatchFolder/mappingEntry/index.tsx @@ -0,0 +1,55 @@ +import { EntryContainer, HorizontalFlex } from '../styledComponents'; +import React, { useEffect } from 'react'; +import { Typography } from '@mui/material'; +import { SpaceBetweenFlex } from 'components/Container'; +import { WatchMapping } from 'types/watchFolder'; +import { AppContext } from 'pages/_app'; +import FolderOpenIcon from '@mui/icons-material/FolderOpen'; +import constants from 'utils/strings/constants'; +import MappingEntryOptions from '../mappingEntryOptions'; +import { EntryHeading } from './entryHeading'; + +export function MappingEntry({ + mapping, + handleRemoveMapping, +}: { + mapping: WatchMapping; + handleRemoveMapping: (mapping: WatchMapping) => void; +}) { + const appContext = React.useContext(AppContext); + + useEffect(() => { + console.log(appContext.watchServiceIsRunning); + }, [appContext.watchServiceIsRunning]); + + const confirmStopWatching = () => { + appContext.setDialogMessage({ + title: constants.STOP_WATCHING_FOLDER, + content: constants.STOP_WATCHING_DIALOG_MESSAGE, + close: { + text: constants.CANCEL, + variant: 'primary', + }, + proceed: { + action: () => handleRemoveMapping(mapping), + text: constants.YES_STOP, + variant: 'danger', + }, + }); + }; + + return ( + + + + + + + {mapping.folderPath} + + + + + + ); +} diff --git a/src/components/WatchFolder/mappingEntry/syncProgressIcon.tsx b/src/components/WatchFolder/mappingEntry/syncProgressIcon.tsx new file mode 100644 index 000000000..d221a7fb7 --- /dev/null +++ b/src/components/WatchFolder/mappingEntry/syncProgressIcon.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { CircularProgress } from '@mui/material'; + +export function SyncProgressIcon() { + return ( + + ); +} diff --git a/src/components/WatchFolder/mappingEntryOptions.tsx b/src/components/WatchFolder/mappingEntryOptions.tsx new file mode 100644 index 000000000..10e9a7001 --- /dev/null +++ b/src/components/WatchFolder/mappingEntryOptions.tsx @@ -0,0 +1,55 @@ +import { IconButton, Menu, MenuItem } from '@mui/material'; +import React, { useState } from 'react'; +import constants from 'utils/strings/constants'; +import DoNotDisturbOutlinedIcon from '@mui/icons-material/DoNotDisturbOutlined'; +import MoreHorizIcon from '@mui/icons-material/MoreHoriz'; + +export default function MappingEntryOptions({ confirmStopWatching }) { + const [anchorEl, setAnchorEl] = useState(null); + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + + const open = Boolean(anchorEl); + return ( + <> + + + + + theme.palette.danger.main, + }}> + + + + {constants.STOP_WATCHING} + + + + ); +} diff --git a/src/components/WatchFolder/mappingList.tsx b/src/components/WatchFolder/mappingList.tsx new file mode 100644 index 000000000..c495bed30 --- /dev/null +++ b/src/components/WatchFolder/mappingList.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { MappingEntry } from './mappingEntry'; +import { MappingsContainer } from './styledComponents'; +export function MappingList({ mappings, handleRemoveWatchMapping }) { + return ( + + {mappings.map((mapping) => { + console.log(mapping); + return ( + + ); + })} + + ); +} diff --git a/src/components/WatchFolder/noMappingsContent.tsx b/src/components/WatchFolder/noMappingsContent.tsx new file mode 100644 index 000000000..54eccf06b --- /dev/null +++ b/src/components/WatchFolder/noMappingsContent.tsx @@ -0,0 +1,24 @@ +import { Typography } from '@mui/material'; +import { FlexWrapper } from 'components/Container'; +import React from 'react'; +import constants from 'utils/strings/constants'; +import { CheckmarkIcon } from './checkmarkIcon'; +import { NoMappingsContainer } from './styledComponents'; +export function NoMappingsContent() { + return ( + + + {constants.NO_FOLDERS_ADDED} + + + {constants.FOLDERS_AUTOMATICALLY_MONITORED} + + + {constants.UPLOAD_NEW_FILES_TO_ENTE} + + + {constants.REMOVE_DELETED_FILES_FROM_ENTE} + + + ); +} diff --git a/src/components/WatchFolder/styledComponents.tsx b/src/components/WatchFolder/styledComponents.tsx index 20ca60e1f..20e69e457 100644 --- a/src/components/WatchFolder/styledComponents.tsx +++ b/src/components/WatchFolder/styledComponents.tsx @@ -1,81 +1,30 @@ -import { Box, Button } from '@mui/material'; +import { Box } from '@mui/material'; import { styled } from '@mui/material/styles'; +import VerticallyCentered from 'components/Container'; -export const ModalHeading = styled('h3')({ - fontSize: '28px', - marginBottom: '24px', - fontWeight: 600, -}); -export const FullWidthButtonWithTopMargin = styled(Button)({ - marginTop: '16px', - width: '100%', - borderRadius: '4px', -}); -export const PaddedContainer = styled(Box)({ - padding: '24px', -}); -export const FixedHeightContainer = styled(Box)({ - height: '450px', - display: 'flex', - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'space-between', -}); -export const FullHeightVerticallyCentered = styled(Box)({ - display: 'flex', - flexDirection: 'column', - height: '100%', - overflowY: 'auto', - margin: 0, - padding: 0, - listStyle: 'none', +export const MappingsContainer = styled(Box)(({ theme }) => ({ + height: '278px', + overflow: 'auto', '&::-webkit-scrollbar': { - width: '6px', + width: '4px', }, '&::-webkit-scrollbar-thumb': { - backgroundColor: 'slategrey', + backgroundColor: theme.palette.secondary.main, }, +})); + +export const NoMappingsContainer = styled(VerticallyCentered)({ + height: '278px', + textAlign: 'left', + alignItems: 'flex-start', }); -export const NoFoldersTitleText = styled('h4')({ - fontSize: '24px', - marginBottom: '16px', - fontWeight: 600, -}); -export const BottomMarginSpacer = styled(Box)({ - marginBottom: '10px', -}); + export const HorizontalFlex = styled(Box)({ display: 'flex', - flexDirection: 'row', - justifyContent: 'space-between', }); -export const VerticalFlex = styled(Box)({ - display: 'flex', - flexDirection: 'column', -}); -export const MappingEntryTitle = styled(Box)({ - fontSize: '16px', - fontWeight: 500, + +export const EntryContainer = styled(Box)({ marginLeft: '12px', marginRight: '6px', -}); -export const MappingEntryFolder = styled(Box)({ - fontSize: '14px', - fontWeight: 500, - marginTop: '2px', - marginLeft: '12px', - marginRight: '6px', - marginBottom: '6px', - lineHeight: '18px', -}); -export const DialogBoxHeading = styled('h4')({ - fontSize: '24px', - marginBottom: '16px', - fontWeight: 600, -}); -export const DialogBoxText = styled('p')({ - fontWeight: 500, -}); -export const DialogBoxButton = styled(Button)({ - width: '140px', + marginBottom: '12px', }); From 8f2b1b0be69fdca0f09f4e97e929bb5d503a2514 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sun, 19 Jun 2022 13:27:42 +0530 Subject: [PATCH 044/295] revert testing change --- src/components/WatchFolder/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/WatchFolder/index.tsx b/src/components/WatchFolder/index.tsx index 44e3b68cb..591565788 100644 --- a/src/components/WatchFolder/index.tsx +++ b/src/components/WatchFolder/index.tsx @@ -71,7 +71,7 @@ export default function WatchFolderModal({ open, onClose }: Iprops) { {constants.WATCHED_FOLDERS} - {mappings.length !== 0 ? ( + {mappings.length === 0 ? ( ) : ( Date: Sun, 19 Jun 2022 16:18:47 +0530 Subject: [PATCH 045/295] fix typo --- src/components/UploadProgress/index.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/UploadProgress/index.tsx b/src/components/UploadProgress/index.tsx index 293b6fb4b..03baed0f9 100644 --- a/src/components/UploadProgress/index.tsx +++ b/src/components/UploadProgress/index.tsx @@ -45,7 +45,10 @@ export default function UploadProgress({ const [expanded, setExpanded] = useState(true); useEffect(() => { - if (appContext.watchServiceIsRunning && watchService.isUploadRunning) { + if ( + appContext.watchServiceIsRunning && + watchService.isUploadRunning() + ) { setExpanded(false); } }, [appContext.watchServiceIsRunning]); From 6b9ed0c6228bd81d85b67729c7663b7eb1463273 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sun, 19 Jun 2022 16:29:41 +0530 Subject: [PATCH 046/295] rename watchModal to watchFolder --- src/components/FullScreenDropZone.tsx | 10 +++---- src/components/Sidebar/UtilitySection.tsx | 13 ++++---- src/components/UploadProgress/index.tsx | 7 ++--- src/components/WatchFolder/index.tsx | 10 +++---- .../WatchFolder/mappingEntry/entryHeading.tsx | 2 +- .../WatchFolder/mappingEntry/index.tsx | 4 +-- src/components/pages/gallery/Upload.tsx | 2 +- src/pages/_app.tsx | 30 +++++++++---------- src/pages/gallery/index.tsx | 2 +- 9 files changed, 37 insertions(+), 43 deletions(-) diff --git a/src/components/FullScreenDropZone.tsx b/src/components/FullScreenDropZone.tsx index 391785410..46ff6de8c 100644 --- a/src/components/FullScreenDropZone.tsx +++ b/src/components/FullScreenDropZone.tsx @@ -59,7 +59,7 @@ export default function FullScreenDropZone(props: Props) { useEffect(() => { const handleWatchFolderDrop = (e: DragEvent) => { - if (!appContext.watchModalView) { + if (!appContext.watchFolderView) { return; } @@ -67,7 +67,7 @@ export default function FullScreenDropZone(props: Props) { e.stopPropagation(); const files = e.dataTransfer.files; if (files.length > 0) { - appContext.setWatchModalFiles(files); + appContext.setWatchFolderFiles(files); } }; @@ -75,14 +75,14 @@ export default function FullScreenDropZone(props: Props) { return () => { removeEventListener('drop', handleWatchFolderDrop); }; - }, [appContext.watchModalView]); + }, [appContext.watchFolderView]); return ( - {!appContext.watchModalView ? ( + {!appContext.watchFolderView ? ( ) : null} {isDragActive && ( @@ -90,7 +90,7 @@ export default function FullScreenDropZone(props: Props) { - {appContext.watchModalView + {appContext.watchFolderView ? constants.WATCH_FOLDER_DROPZONE_MESSAGE : constants.UPLOAD_DROPZONE_MESSAGE} diff --git a/src/components/Sidebar/UtilitySection.tsx b/src/components/Sidebar/UtilitySection.tsx index d1eadb44e..696e260ba 100644 --- a/src/components/Sidebar/UtilitySection.tsx +++ b/src/components/Sidebar/UtilitySection.tsx @@ -9,15 +9,15 @@ import { useRouter } from 'next/router'; import { AppContext } from 'pages/_app'; import isElectron from 'is-electron'; import { downloadApp } from 'utils/common'; -import WatchFolderModal from 'components/WatchFolder'; +import WatchFolder from 'components/WatchFolder'; export default function UtilitySection({ closeSidebar }) { const router = useRouter(); const { setDialogMessage, startLoading, - watchModalView, - setWatchModalView, + watchFolderView: watchModalView, + setWatchFolderView: setWatchModalView, } = useContext(AppContext); const [recoverModalView, setRecoveryModalView] = useState(false); @@ -71,7 +71,7 @@ export default function UtilitySection({ closeSidebar }) { close: { variant: 'danger' }, }); - const closeWatchFolderModal = () => setWatchModalView(false); + const closeWatchFolder = () => setWatchModalView(false); return ( <> @@ -108,10 +108,7 @@ export default function UtilitySection({ closeSidebar }) { closeSidebar={closeSidebar} setLoading={startLoading} /> - + {/* { - if ( - appContext.watchServiceIsRunning && - watchService.isUploadRunning() - ) { + if (appContext.isFolderSyncRunning && watchService.isUploadRunning()) { setExpanded(false); } - }, [appContext.watchServiceIsRunning]); + }, [appContext.isFolderSyncRunning]); function confirmCancelUpload() { appContext.setDialogMessage({ diff --git a/src/components/WatchFolder/index.tsx b/src/components/WatchFolder/index.tsx index 591565788..b4ef294b9 100644 --- a/src/components/WatchFolder/index.tsx +++ b/src/components/WatchFolder/index.tsx @@ -14,7 +14,7 @@ interface Iprops { onClose: () => void; } -export default function WatchFolderModal({ open, onClose }: Iprops) { +export default function WatchFolder({ open, onClose }: Iprops) { const [mappings, setMappings] = useState([]); const appContext = useContext(AppContext); @@ -24,12 +24,12 @@ export default function WatchFolderModal({ open, onClose }: Iprops) { useEffect(() => { if ( - appContext.watchModalFiles && - appContext.watchModalFiles.length > 0 + appContext.watchModalFolderFiles && + appContext.watchModalFolderFiles.length > 0 ) { - handleFolderDrop(appContext.watchModalFiles); + handleFolderDrop(appContext.watchModalFolderFiles); } - }, [appContext.watchModalFiles]); + }, [appContext.watchModalFolderFiles]); const handleFolderDrop = async (folders: FileList) => { for (let i = 0; i < folders.length; i++) { diff --git a/src/components/WatchFolder/mappingEntry/entryHeading.tsx b/src/components/WatchFolder/mappingEntry/entryHeading.tsx index fb6347021..5fc7a28b8 100644 --- a/src/components/WatchFolder/mappingEntry/entryHeading.tsx +++ b/src/components/WatchFolder/mappingEntry/entryHeading.tsx @@ -15,7 +15,7 @@ export function EntryHeading({ mapping }) { }}> <> {mapping.collectionName} - {appContext.watchServiceIsRunning && + {appContext.isFolderSyncRunning && watchService.currentEvent?.collectionName === mapping.collectionName && } diff --git a/src/components/WatchFolder/mappingEntry/index.tsx b/src/components/WatchFolder/mappingEntry/index.tsx index f20badd38..7451cdebb 100644 --- a/src/components/WatchFolder/mappingEntry/index.tsx +++ b/src/components/WatchFolder/mappingEntry/index.tsx @@ -19,8 +19,8 @@ export function MappingEntry({ const appContext = React.useContext(AppContext); useEffect(() => { - console.log(appContext.watchServiceIsRunning); - }, [appContext.watchServiceIsRunning]); + console.log(appContext.isFolderSyncRunning); + }, [appContext.isFolderSyncRunning]); const confirmStopWatching = () => { appContext.setDialogMessage({ diff --git a/src/components/pages/gallery/Upload.tsx b/src/components/pages/gallery/Upload.tsx index e511f0dc0..96e166a7b 100644 --- a/src/components/pages/gallery/Upload.tsx +++ b/src/components/pages/gallery/Upload.tsx @@ -132,7 +132,7 @@ export default function Upload(props: Props) { props.setElectronFiles, setCollectionName, props.syncWithRemote, - appContext.setWatchServiceIsRunning + appContext.setIsFolderSyncRunning ); } }, []); diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 9d1178e60..f4023ecd3 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -52,12 +52,12 @@ type AppContextType = { finishLoading: () => void; closeMessageDialog: () => void; setDialogMessage: SetDialogBoxAttributes; - watchServiceIsRunning: boolean; - setWatchServiceIsRunning: (isRunning: boolean) => void; - watchModalView: boolean; - setWatchModalView: (isOpen: boolean) => void; - watchModalFiles: FileList; - setWatchModalFiles: (files: FileList) => void; + isFolderSyncRunning: boolean; + setIsFolderSyncRunning: (isRunning: boolean) => void; + watchFolderView: boolean; + setWatchFolderView: (isOpen: boolean) => void; + watchModalFolderFiles: FileList; + setWatchFolderFiles: (files: FileList) => void; }; export enum FLASH_MESSAGE_TYPE { @@ -92,9 +92,9 @@ export default function App({ Component, err }) { const loadingBar = useRef(null); const [dialogMessage, setDialogMessage] = useState(); const [messageDialogView, setMessageDialogView] = useState(false); - const [watchServiceIsRunning, setWatchServiceIsRunning] = useState(false); - const [watchModalView, setWatchModalView] = useState(false); - const [watchModalFiles, setWatchModalFiles] = useState(null); + const [isFolderSyncRunning, setIsFolderSyncRunning] = useState(false); + const [watchFolderView, setWatchFolderView] = useState(false); + const [watchFolderFiles, setWatchFolderFiles] = useState(null); useEffect(() => { if ( @@ -283,12 +283,12 @@ export default function App({ Component, err }) { finishLoading, closeMessageDialog, setDialogMessage, - watchServiceIsRunning, - setWatchServiceIsRunning, - watchModalView, - setWatchModalView, - watchModalFiles, - setWatchModalFiles, + isFolderSyncRunning: isFolderSyncRunning, + setIsFolderSyncRunning: setIsFolderSyncRunning, + watchFolderView: watchFolderView, + setWatchFolderView: setWatchFolderView, + watchModalFolderFiles: watchFolderFiles, + setWatchFolderFiles: setWatchFolderFiles, }}> {loading ? ( diff --git a/src/pages/gallery/index.tsx b/src/pages/gallery/index.tsx index a1292ca24..f2bc7e996 100644 --- a/src/pages/gallery/index.tsx +++ b/src/pages/gallery/index.tsx @@ -280,7 +280,7 @@ export default function Gallery() { ); useEffect(() => { - if (!appContext.watchModalView) { + if (!appContext.watchFolderView) { setDroppedFiles(acceptedFiles); } }, [acceptedFiles]); From 0138873881dba21ca2906e20ba6d2235b154a312 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sun, 19 Jun 2022 16:32:14 +0530 Subject: [PATCH 047/295] update EntryHeading styles --- .../WatchFolder/mappingEntry/entryHeading.tsx | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/WatchFolder/mappingEntry/entryHeading.tsx b/src/components/WatchFolder/mappingEntry/entryHeading.tsx index 5fc7a28b8..e25bc2442 100644 --- a/src/components/WatchFolder/mappingEntry/entryHeading.tsx +++ b/src/components/WatchFolder/mappingEntry/entryHeading.tsx @@ -3,22 +3,22 @@ import { Typography } from '@mui/material'; import watchService from 'services/watchFolderService'; import { AppContext } from 'pages/_app'; import { SyncProgressIcon } from './syncProgressIcon'; +import { FlexWrapper } from 'components/Container'; export function EntryHeading({ mapping }) { const appContext = useContext(AppContext); return ( - - <> + + {mapping.collectionName} - {appContext.isFolderSyncRunning && - watchService.currentEvent?.collectionName === - mapping.collectionName && } - - + + {appContext.isFolderSyncRunning && + watchService.currentEvent?.collectionName === + mapping.collectionName && } + ); } From e51e6e8fa1b1d8692233a2bda21b3d01d07eba22 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sun, 19 Jun 2022 16:40:17 +0530 Subject: [PATCH 048/295] add typings --- .../WatchFolder/mappingEntry/entryHeading.tsx | 7 ++++++- src/components/WatchFolder/mappingEntry/index.tsx | 15 +++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/components/WatchFolder/mappingEntry/entryHeading.tsx b/src/components/WatchFolder/mappingEntry/entryHeading.tsx index e25bc2442..1c9ce028d 100644 --- a/src/components/WatchFolder/mappingEntry/entryHeading.tsx +++ b/src/components/WatchFolder/mappingEntry/entryHeading.tsx @@ -4,8 +4,13 @@ import watchService from 'services/watchFolderService'; import { AppContext } from 'pages/_app'; import { SyncProgressIcon } from './syncProgressIcon'; import { FlexWrapper } from 'components/Container'; +import { WatchMapping } from 'types/watchFolder'; -export function EntryHeading({ mapping }) { +interface Iprops { + mapping: WatchMapping; +} + +export function EntryHeading({ mapping }: Iprops) { const appContext = useContext(AppContext); return ( diff --git a/src/components/WatchFolder/mappingEntry/index.tsx b/src/components/WatchFolder/mappingEntry/index.tsx index 7451cdebb..7e5f08314 100644 --- a/src/components/WatchFolder/mappingEntry/index.tsx +++ b/src/components/WatchFolder/mappingEntry/index.tsx @@ -1,5 +1,5 @@ import { EntryContainer, HorizontalFlex } from '../styledComponents'; -import React, { useEffect } from 'react'; +import React from 'react'; import { Typography } from '@mui/material'; import { SpaceBetweenFlex } from 'components/Container'; import { WatchMapping } from 'types/watchFolder'; @@ -9,18 +9,13 @@ import constants from 'utils/strings/constants'; import MappingEntryOptions from '../mappingEntryOptions'; import { EntryHeading } from './entryHeading'; -export function MappingEntry({ - mapping, - handleRemoveMapping, -}: { +interface Iprops { mapping: WatchMapping; handleRemoveMapping: (mapping: WatchMapping) => void; -}) { - const appContext = React.useContext(AppContext); +} - useEffect(() => { - console.log(appContext.isFolderSyncRunning); - }, [appContext.isFolderSyncRunning]); +export function MappingEntry({ mapping, handleRemoveMapping }: Iprops) { + const appContext = React.useContext(AppContext); const confirmStopWatching = () => { appContext.setDialogMessage({ From 323b6af010e96c3c5dbd0595ac83e7cebac5c5fd Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sun, 19 Jun 2022 16:41:52 +0530 Subject: [PATCH 049/295] rename missed watchModal to watchFolder --- src/components/Sidebar/UtilitySection.tsx | 14 +++++++------- src/components/WatchFolder/index.tsx | 8 ++++---- src/pages/_app.tsx | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/components/Sidebar/UtilitySection.tsx b/src/components/Sidebar/UtilitySection.tsx index 696e260ba..b544d3117 100644 --- a/src/components/Sidebar/UtilitySection.tsx +++ b/src/components/Sidebar/UtilitySection.tsx @@ -16,8 +16,8 @@ export default function UtilitySection({ closeSidebar }) { const { setDialogMessage, startLoading, - watchFolderView: watchModalView, - setWatchFolderView: setWatchModalView, + watchFolderView, + setWatchFolderView, } = useContext(AppContext); const [recoverModalView, setRecoveryModalView] = useState(false); @@ -30,9 +30,9 @@ export default function UtilitySection({ closeSidebar }) { const openTwoFactorModalView = () => setTwoFactorModalView(true); const closeTwoFactorModalView = () => setTwoFactorModalView(false); - const openWatchModalView = () => { + const openWatchFolderView = () => { if (isElectron()) { - setWatchModalView(true); + setWatchFolderView(true); } else { setDialogMessage({ title: constants.DOWNLOAD_APP, @@ -71,7 +71,7 @@ export default function UtilitySection({ closeSidebar }) { close: { variant: 'danger' }, }); - const closeWatchFolder = () => setWatchModalView(false); + const closeWatchFolder = () => setWatchFolderView(false); return ( <> @@ -89,7 +89,7 @@ export default function UtilitySection({ closeSidebar }) { {constants.DEDUPLICATE_FILES} - + {constants.WATCH_FOLDERS} @@ -108,7 +108,7 @@ export default function UtilitySection({ closeSidebar }) { closeSidebar={closeSidebar} setLoading={startLoading} /> - + {/* { if ( - appContext.watchModalFolderFiles && - appContext.watchModalFolderFiles.length > 0 + appContext.watchFolderFiles && + appContext.watchFolderFiles.length > 0 ) { - handleFolderDrop(appContext.watchModalFolderFiles); + handleFolderDrop(appContext.watchFolderFiles); } - }, [appContext.watchModalFolderFiles]); + }, [appContext.watchFolderFiles]); const handleFolderDrop = async (folders: FileList) => { for (let i = 0; i < folders.length; i++) { diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index f4023ecd3..ddf283315 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -56,7 +56,7 @@ type AppContextType = { setIsFolderSyncRunning: (isRunning: boolean) => void; watchFolderView: boolean; setWatchFolderView: (isOpen: boolean) => void; - watchModalFolderFiles: FileList; + watchFolderFiles: FileList; setWatchFolderFiles: (files: FileList) => void; }; @@ -287,7 +287,7 @@ export default function App({ Component, err }) { setIsFolderSyncRunning: setIsFolderSyncRunning, watchFolderView: watchFolderView, setWatchFolderView: setWatchFolderView, - watchModalFolderFiles: watchFolderFiles, + watchFolderFiles: watchFolderFiles, setWatchFolderFiles: setWatchFolderFiles, }}> {loading ? ( From 6832ae46ff9f3ab9a00a842380a5c44d2b237bf1 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sun, 19 Jun 2022 16:44:03 +0530 Subject: [PATCH 050/295] add more typing --- src/components/WatchFolder/mappingList.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/WatchFolder/mappingList.tsx b/src/components/WatchFolder/mappingList.tsx index c495bed30..1578ed7af 100644 --- a/src/components/WatchFolder/mappingList.tsx +++ b/src/components/WatchFolder/mappingList.tsx @@ -1,11 +1,16 @@ import React from 'react'; +import { WatchMapping } from 'types/watchFolder'; import { MappingEntry } from './mappingEntry'; import { MappingsContainer } from './styledComponents'; -export function MappingList({ mappings, handleRemoveWatchMapping }) { +interface Iprops { + mappings: WatchMapping[]; + handleRemoveWatchMapping: (value: WatchMapping) => void; +} + +export function MappingList({ mappings, handleRemoveWatchMapping }: Iprops) { return ( {mappings.map((mapping) => { - console.log(mapping); return ( Date: Sun, 19 Jun 2022 17:55:47 +0530 Subject: [PATCH 051/295] remove unnessary import --- src/pages/_app.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index ddf283315..612dc8da0 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -19,8 +19,6 @@ import DialogBox from 'components/DialogBox'; import { styled, ThemeProvider } from '@mui/material/styles'; import darkThemeOptions from 'themes/darkThemeOptions'; import { CssBaseline } from '@mui/material'; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -// import * as types from 'styled-components/cssprop'; // need to css prop on styled component import { DialogBoxAttributes, SetDialogBoxAttributes } from 'types/dialogBox'; import { getFamilyPortalRedirectURL, From 1117534cc8a9db4c5bd915fa4db650175d924958 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sun, 19 Jun 2022 17:58:00 +0530 Subject: [PATCH 052/295] use shorthand --- src/pages/_app.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 612dc8da0..f0d971c64 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -281,12 +281,12 @@ export default function App({ Component, err }) { finishLoading, closeMessageDialog, setDialogMessage, - isFolderSyncRunning: isFolderSyncRunning, - setIsFolderSyncRunning: setIsFolderSyncRunning, - watchFolderView: watchFolderView, - setWatchFolderView: setWatchFolderView, - watchFolderFiles: watchFolderFiles, - setWatchFolderFiles: setWatchFolderFiles, + isFolderSyncRunning, + setIsFolderSyncRunning, + watchFolderView, + setWatchFolderView, + watchFolderFiles, + setWatchFolderFiles, }}> {loading ? ( From 99b5cce1c9f44a200353368743bd3162b247d510 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sun, 19 Jun 2022 18:07:55 +0530 Subject: [PATCH 053/295] rename watchService to watchFolderService --- src/components/UploadProgress/index.tsx | 7 ++++-- src/components/WatchFolder/index.tsx | 16 ++++++------- .../WatchFolder/mappingEntry/entryHeading.tsx | 4 ++-- src/components/pages/gallery/Upload.tsx | 6 ++--- src/services/upload/uploadManager.ts | 4 ++-- src/services/watchFolderService.ts | 23 ++++++++++--------- src/types/electron/index.ts | 10 ++++---- 7 files changed, 37 insertions(+), 33 deletions(-) diff --git a/src/components/UploadProgress/index.tsx b/src/components/UploadProgress/index.tsx index 71d839a7f..2de22f302 100644 --- a/src/components/UploadProgress/index.tsx +++ b/src/components/UploadProgress/index.tsx @@ -13,7 +13,7 @@ import { InProgressUpload, } from 'types/upload/ui'; import UploadProgressContext from 'contexts/uploadProgress'; -import watchService from 'services/watchFolderService'; +import watchFolderService from 'services/watchFolderService'; interface Props { open: boolean; @@ -45,7 +45,10 @@ export default function UploadProgress({ const [expanded, setExpanded] = useState(true); useEffect(() => { - if (appContext.isFolderSyncRunning && watchService.isUploadRunning()) { + if ( + appContext.isFolderSyncRunning && + watchFolderService.isUploadRunning() + ) { setExpanded(false); } }, [appContext.isFolderSyncRunning]); diff --git a/src/components/WatchFolder/index.tsx b/src/components/WatchFolder/index.tsx index 996c7e2e2..a504a5d4a 100644 --- a/src/components/WatchFolder/index.tsx +++ b/src/components/WatchFolder/index.tsx @@ -2,7 +2,7 @@ import { MappingList } from './mappingList'; import { NoMappingsContent } from './noMappingsContent'; import React, { useContext, useEffect, useState } from 'react'; import { Button, DialogActions, DialogContent } from '@mui/material'; -import watchService from 'services/watchFolderService'; +import watchFolderService from 'services/watchFolderService'; import { WatchMapping } from 'types/watchFolder'; import { AppContext } from 'pages/_app'; import constants from 'utils/strings/constants'; @@ -19,7 +19,7 @@ export default function WatchFolder({ open, onClose }: Iprops) { const appContext = useContext(AppContext); useEffect(() => { - setMappings(watchService.getWatchMappings()); + setMappings(watchFolderService.getWatchMappings()); }, []); useEffect(() => { @@ -35,7 +35,7 @@ export default function WatchFolder({ open, onClose }: Iprops) { for (let i = 0; i < folders.length; i++) { const folder: any = folders[i]; const path = (folder.path as string).replace(/\\/g, '/'); - if (await watchService.isFolder(path)) { + if (await watchFolderService.isFolder(path)) { await handleAddWatchMapping(path); } } @@ -46,23 +46,23 @@ export default function WatchFolder({ open, onClose }: Iprops) { }; const handleFolderSelection = async () => { - const folderPath = await watchService.selectFolder(); + const folderPath = await watchFolderService.selectFolder(); await handleAddWatchMapping(folderPath); }; const handleAddWatchMapping = async (inputFolderPath: string) => { if (inputFolderPath?.length > 0) { - await watchService.addWatchMapping( + await watchFolderService.addWatchMapping( inputFolderPath.substring(inputFolderPath.lastIndexOf('/') + 1), inputFolderPath ); - setMappings(watchService.getWatchMappings()); + setMappings(watchFolderService.getWatchMappings()); } }; const handleRemoveWatchMapping = async (mapping: WatchMapping) => { - await watchService.removeWatchMapping(mapping.collectionName); - setMappings(watchService.getWatchMappings()); + await watchFolderService.removeWatchMapping(mapping.collectionName); + setMappings(watchFolderService.getWatchMappings()); }; return ( diff --git a/src/components/WatchFolder/mappingEntry/entryHeading.tsx b/src/components/WatchFolder/mappingEntry/entryHeading.tsx index 1c9ce028d..fa8c52b46 100644 --- a/src/components/WatchFolder/mappingEntry/entryHeading.tsx +++ b/src/components/WatchFolder/mappingEntry/entryHeading.tsx @@ -1,6 +1,6 @@ import React, { useContext } from 'react'; import { Typography } from '@mui/material'; -import watchService from 'services/watchFolderService'; +import watchFolderService from 'services/watchFolderService'; import { AppContext } from 'pages/_app'; import { SyncProgressIcon } from './syncProgressIcon'; import { FlexWrapper } from 'components/Container'; @@ -22,7 +22,7 @@ export function EntryHeading({ mapping }: Iprops) { {mapping.collectionName} {appContext.isFolderSyncRunning && - watchService.currentEvent?.collectionName === + watchFolderService.currentEvent?.collectionName === mapping.collectionName && } ); diff --git a/src/components/pages/gallery/Upload.tsx b/src/components/pages/gallery/Upload.tsx index 96e166a7b..8e8674d49 100644 --- a/src/components/pages/gallery/Upload.tsx +++ b/src/components/pages/gallery/Upload.tsx @@ -24,7 +24,7 @@ import UploadTypeSelector from '../../UploadTypeSelector'; import Router from 'next/router'; import { isCanvasBlocked } from 'utils/upload/isCanvasBlocked'; import { downloadApp } from 'utils/common'; -import watchService from 'services/watchFolderService'; +import watchFolderService from 'services/watchFolderService'; import DiscFullIcon from '@mui/icons-material/DiscFull'; import { NotificationAttributes } from 'types/Notification'; import { @@ -128,7 +128,7 @@ export default function Upload(props: Props) { resumeDesktopUpload(type, electronFiles, collectionName); } ); - watchService.init( + watchFolderService.init( props.setElectronFiles, setCollectionName, props.syncWithRemote, @@ -380,7 +380,7 @@ export default function Upload(props: Props) { props.setUploadInProgress(false); props.syncWithRemote(); if (isElectron()) { - await watchService.allFileUploadsDone( + await watchFolderService.allFileUploadsDone( filesWithCollectionToUpload, collections ); diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index 121faf885..94ddf3778 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -38,7 +38,7 @@ import uiService from './uiService'; import { logUploadInfo } from 'utils/upload'; import isElectron from 'is-electron'; import ImportService from 'services/importService'; -import watchService from 'services/watchFolderService'; +import watchFolderService from 'services/watchFolderService'; import { ProgressUpdater } from 'types/upload/ui'; const MAX_CONCURRENT_UPLOADS = 4; @@ -407,7 +407,7 @@ class UploadManager { UPLOAD_RESULT.UPLOADED_WITH_STATIC_THUMBNAIL || fileUploadResult === UPLOAD_RESULT.ALREADY_UPLOADED ) { - await watchService.onFileUpload( + await watchFolderService.onFileUpload( fileWithCollection, uploadedFile ); diff --git a/src/services/watchFolderService.ts b/src/services/watchFolderService.ts index 16816cb7d..8ec0c2109 100644 --- a/src/services/watchFolderService.ts +++ b/src/services/watchFolderService.ts @@ -9,7 +9,7 @@ import { logError } from 'utils/sentry'; import { EventQueueItem, WatchMapping } from 'types/watchFolder'; import { ElectronAPIsInterface } from 'types/electron'; -export class WatchService { +export class watchFolderService { ElectronAPIs: ElectronAPIsInterface; allElectronAPIsExist: boolean = false; eventQueue: EventQueueItem[] = []; @@ -22,7 +22,7 @@ export class WatchService { setCollectionName: (collectionName: string) => void; syncWithRemote: () => void; showProgressView: () => void; - setWatchServiceIsRunning: (isRunning: boolean) => void; + setwatchFolderServiceIsRunning: (isRunning: boolean) => void; constructor() { this.ElectronAPIs = (runningInBrowser() && @@ -38,14 +38,15 @@ export class WatchService { setElectronFiles: (files: ElectronFile[]) => void, setCollectionName: (collectionName: string) => void, syncWithRemote: () => void, - setWatchServiceIsRunning: (isRunning: boolean) => void + setwatchFolderServiceIsRunning: (isRunning: boolean) => void ) { if (this.allElectronAPIsExist) { try { this.setElectronFiles = setElectronFiles; this.setCollectionName = setCollectionName; this.syncWithRemote = syncWithRemote; - this.setWatchServiceIsRunning = setWatchServiceIsRunning; + this.setwatchFolderServiceIsRunning = + setwatchFolderServiceIsRunning; let mappings = this.getWatchMappings(); @@ -179,7 +180,7 @@ export class WatchService { setIsEventRunning(isEventRunning: boolean) { this.isEventRunning = isEventRunning; - this.setWatchServiceIsRunning(isEventRunning); + this.setwatchFolderServiceIsRunning(isEventRunning); } async runNextEvent() { @@ -462,7 +463,7 @@ export class WatchService { } async function diskFileAddedCallback( - instance: WatchService, + instance: watchFolderService, file: ElectronFile ) { try { @@ -487,7 +488,7 @@ async function diskFileAddedCallback( } async function diskFileRemovedCallback( - instance: WatchService, + instance: watchFolderService, filePath: string ) { try { @@ -512,7 +513,7 @@ async function diskFileRemovedCallback( } async function diskFolderRemovedCallback( - instance: WatchService, + instance: watchFolderService, folderPath: string ) { try { @@ -529,12 +530,12 @@ async function diskFolderRemovedCallback( } } -const runNextEventByInstance = async (w: WatchService) => { +const runNextEventByInstance = async (w: watchFolderService) => { await w.runNextEvent(); }; const hasMappingSameFolderPath = ( - w: WatchService, + w: watchFolderService, collectionName: string, folderPath: string ) => { @@ -545,4 +546,4 @@ const hasMappingSameFolderPath = ( return mapping.folderPath === folderPath; }; -export default new WatchService(); +export default new watchFolderService(); diff --git a/src/types/electron/index.ts b/src/types/electron/index.ts index 9851b4334..e33dee0e5 100644 --- a/src/types/electron/index.ts +++ b/src/types/electron/index.ts @@ -1,4 +1,4 @@ -import { WatchService } from 'services/watchFolderService'; +import { watchFolderService } from 'services/watchFolderService'; import { ElectronFile } from 'types/upload'; import { WatchMapping } from 'types/watchFolder'; @@ -47,17 +47,17 @@ export interface ElectronAPIsInterface { ) => Promise; removeWatchMapping: (collectionName: string) => Promise; registerWatcherFunctions: ( - WatchServiceInstance: WatchService, + watchFolderServiceInstance: watchFolderService, addFile: ( - WatchServiceInstance: WatchService, + watchFolderServiceInstance: watchFolderService, file: ElectronFile ) => Promise, removeFile: ( - WatchServiceInstance: WatchService, + watchFolderServiceInstance: watchFolderService, path: string ) => Promise, removeFolder: ( - WatchServiceInstance: WatchService, + watchFolderServiceInstance: watchFolderService, folderPath: string ) => Promise ) => void; From 4f16669acf5a86e51588f45c5b4bdda5ace65382 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sun, 19 Jun 2022 18:29:16 +0530 Subject: [PATCH 054/295] restruct postUploadAction --- src/constants/upload/index.ts | 1 + src/services/upload/uploadManager.ts | 124 ++++++++++++++++----------- src/services/upload/uploader.ts | 4 +- 3 files changed, 74 insertions(+), 55 deletions(-) diff --git a/src/constants/upload/index.ts b/src/constants/upload/index.ts index 2c9d7a4e6..6056600e3 100644 --- a/src/constants/upload/index.ts +++ b/src/constants/upload/index.ts @@ -40,6 +40,7 @@ export enum UPLOAD_RESULT { LARGER_THAN_AVAILABLE_STORAGE, UPLOADED, UPLOADED_WITH_STATIC_THUMBNAIL, + ADDED_SYMLINK, } export const MAX_FILE_SIZE_SUPPORTED = 4 * 1024 * 1024 * 1024; // 4 GB diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index 94ddf3778..f711dae86 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -329,13 +329,12 @@ class UploadManager { this.existingFilesCollectionWise.get(collectionID) ?? []; const collection = this.collections.get(collectionID); fileWithCollection = { ...fileWithCollection, collection }; - const { fileUploadResult, uploadedFile, skipDecryption } = - await uploader( - worker, - existingFilesInCollection, - this.existingFiles, - fileWithCollection - ); + const { fileUploadResult, uploadedFile } = await uploader( + worker, + existingFilesInCollection, + this.existingFiles, + fileWithCollection + ); UIService.moveFileToResultList( fileWithCollection.localID, fileUploadResult @@ -344,7 +343,6 @@ class UploadManager { await this.postUploadTask( fileUploadResult, uploadedFile, - skipDecryption, fileWithCollection ); } @@ -353,66 +351,46 @@ class UploadManager { async postUploadTask( fileUploadResult: UPLOAD_RESULT, uploadedFile: EnteFile, - skipDecryption: boolean, fileWithCollection: FileWithCollection ) { try { + let decryptedFile: EnteFile; logUploadInfo(`uploadedFile ${JSON.stringify(uploadedFile)}`); - + this.updateElectronRemainingFiles(fileWithCollection); if ( - (fileUploadResult === UPLOAD_RESULT.UPLOADED || - fileUploadResult === - UPLOAD_RESULT.UPLOADED_WITH_STATIC_THUMBNAIL) && - !skipDecryption + fileUploadResult === + UPLOAD_RESULT.LARGER_THAN_AVAILABLE_STORAGE || + fileUploadResult === UPLOAD_RESULT.TOO_LARGE || + fileUploadResult === UPLOAD_RESULT.UNSUPPORTED ) { - const decryptedFile = await decryptFile( - uploadedFile, - fileWithCollection.collection.key - ); - this.existingFiles.push(decryptedFile); - this.existingFiles = sortFiles(this.existingFiles); - await setLocalFiles(this.existingFiles); - this.setFiles(preservePhotoswipeProps(this.existingFiles)); - if ( - !this.existingFilesCollectionWise.has( - decryptedFile.collectionID - ) - ) { - this.existingFilesCollectionWise.set( - decryptedFile.collectionID, - [] - ); - } - this.existingFilesCollectionWise - .get(decryptedFile.collectionID) - .push(decryptedFile); - } - if ( + // no-op + } else if ( fileUploadResult === UPLOAD_RESULT.FAILED || fileUploadResult === UPLOAD_RESULT.BLOCKED ) { this.failedFiles.push(fileWithCollection); - } - - if (isElectron()) { - this.remainingFiles = this.remainingFiles.filter( - (file) => - !areFileWithCollectionsSame(file, fileWithCollection) - ); - ImportService.updatePendingUploads(this.remainingFiles); - - if ( - fileUploadResult === UPLOAD_RESULT.UPLOADED || - fileUploadResult === - UPLOAD_RESULT.UPLOADED_WITH_STATIC_THUMBNAIL || - fileUploadResult === UPLOAD_RESULT.ALREADY_UPLOADED - ) { + } else if (fileUploadResult === UPLOAD_RESULT.ALREADY_UPLOADED) { + if (isElectron()) { await watchFolderService.onFileUpload( fileWithCollection, uploadedFile ); } + } else if ( + fileUploadResult === UPLOAD_RESULT.UPLOADED || + fileUploadResult === + UPLOAD_RESULT.UPLOADED_WITH_STATIC_THUMBNAIL + ) { + decryptedFile = await decryptFile( + uploadedFile, + fileWithCollection.collection.key + ); + } else { + decryptedFile = uploadedFile; } + await this.updateExistingFiles(decryptedFile); + this.updateExistingCollections(decryptedFile); + await this.watchFolderCallback(fileWithCollection, uploadedFile); } catch (e) { logError(e, 'failed to do post file upload action'); logUploadInfo( @@ -423,6 +401,48 @@ class UploadManager { } } + private async watchFolderCallback( + fileWithCollection: FileWithCollection, + uploadedFile: EnteFile + ) { + if (isElectron()) { + await watchFolderService.onFileUpload( + fileWithCollection, + uploadedFile + ); + } + } + + private updateExistingCollections(decryptedFile: EnteFile) { + if (!this.existingFilesCollectionWise.has(decryptedFile.collectionID)) { + this.existingFilesCollectionWise.set( + decryptedFile.collectionID, + [] + ); + } + this.existingFilesCollectionWise + .get(decryptedFile.collectionID) + .push(decryptedFile); + } + + private async updateExistingFiles(decryptedFile: EnteFile) { + this.existingFiles.push(decryptedFile); + this.existingFiles = sortFiles(this.existingFiles); + await setLocalFiles(this.existingFiles); + this.setFiles(preservePhotoswipeProps(this.existingFiles)); + } + + private updateElectronRemainingFiles( + fileWithCollection: FileWithCollection + ) { + if (isElectron()) { + this.remainingFiles = this.remainingFiles.filter( + (file) => !areFileWithCollectionsSame(file, fileWithCollection) + ); + ImportService.updatePendingUploads(this.remainingFiles); + } + } + async retryFailedFiles() { await this.queueFilesForUpload(this.failedFiles, [ ...this.collections.values(), diff --git a/src/services/upload/uploader.ts b/src/services/upload/uploader.ts index c43012b04..12365d9a7 100644 --- a/src/services/upload/uploader.ts +++ b/src/services/upload/uploader.ts @@ -20,7 +20,6 @@ import { addToCollection } from 'services/collectionService'; interface UploadResponse { fileUploadResult: UPLOAD_RESULT; uploadedFile?: EnteFile; - skipDecryption?: boolean; } export default async function uploader( worker: any, @@ -75,9 +74,8 @@ export default async function uploader( resultFile.collectionID = collection.id; await addToCollection(collection, [resultFile]); return { - fileUploadResult: UPLOAD_RESULT.UPLOADED, + fileUploadResult: UPLOAD_RESULT.ADDED_SYMLINK, uploadedFile: resultFile, - skipDecryption: true, }; } From 86d6bf934056c33900b146aebbf189091a3171b2 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sun, 19 Jun 2022 18:31:26 +0530 Subject: [PATCH 055/295] fix casing --- src/services/watchFolderService.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/services/watchFolderService.ts b/src/services/watchFolderService.ts index 8ec0c2109..150568a56 100644 --- a/src/services/watchFolderService.ts +++ b/src/services/watchFolderService.ts @@ -22,7 +22,7 @@ export class watchFolderService { setCollectionName: (collectionName: string) => void; syncWithRemote: () => void; showProgressView: () => void; - setwatchFolderServiceIsRunning: (isRunning: boolean) => void; + setWatchFolderServiceIsRunning: (isRunning: boolean) => void; constructor() { this.ElectronAPIs = (runningInBrowser() && @@ -38,15 +38,15 @@ export class watchFolderService { setElectronFiles: (files: ElectronFile[]) => void, setCollectionName: (collectionName: string) => void, syncWithRemote: () => void, - setwatchFolderServiceIsRunning: (isRunning: boolean) => void + setWatchFolderServiceIsRunning: (isRunning: boolean) => void ) { if (this.allElectronAPIsExist) { try { this.setElectronFiles = setElectronFiles; this.setCollectionName = setCollectionName; this.syncWithRemote = syncWithRemote; - this.setwatchFolderServiceIsRunning = - setwatchFolderServiceIsRunning; + this.setWatchFolderServiceIsRunning = + setWatchFolderServiceIsRunning; let mappings = this.getWatchMappings(); @@ -180,7 +180,7 @@ export class watchFolderService { setIsEventRunning(isEventRunning: boolean) { this.isEventRunning = isEventRunning; - this.setwatchFolderServiceIsRunning(isEventRunning); + this.setWatchFolderServiceIsRunning(isEventRunning); } async runNextEvent() { From da90b22576ab327c10f72769215dad76d64c6a10 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sun, 19 Jun 2022 19:56:09 +0530 Subject: [PATCH 056/295] move event handler to seperate file --- src/components/UploadProgress/index.tsx | 2 +- src/components/WatchFolder/index.tsx | 2 +- .../WatchFolder/mappingEntry/entryHeading.tsx | 7 +- src/components/pages/gallery/Upload.tsx | 2 +- src/services/upload/uploadManager.ts | 2 +- .../watchFolder/watchFolderEventHandlers.ts | 78 +++++++++ .../{ => watchFolder}/watchFolderService.ts | 149 +++++------------- src/types/electron/index.ts | 17 +- 8 files changed, 130 insertions(+), 129 deletions(-) create mode 100644 src/services/watchFolder/watchFolderEventHandlers.ts rename src/services/{ => watchFolder}/watchFolderService.ts (81%) diff --git a/src/components/UploadProgress/index.tsx b/src/components/UploadProgress/index.tsx index 2de22f302..9834daa80 100644 --- a/src/components/UploadProgress/index.tsx +++ b/src/components/UploadProgress/index.tsx @@ -13,7 +13,7 @@ import { InProgressUpload, } from 'types/upload/ui'; import UploadProgressContext from 'contexts/uploadProgress'; -import watchFolderService from 'services/watchFolderService'; +import watchFolderService from 'services/watchFolder/watchFolderService'; interface Props { open: boolean; diff --git a/src/components/WatchFolder/index.tsx b/src/components/WatchFolder/index.tsx index a504a5d4a..bbaf1e14e 100644 --- a/src/components/WatchFolder/index.tsx +++ b/src/components/WatchFolder/index.tsx @@ -2,7 +2,7 @@ import { MappingList } from './mappingList'; import { NoMappingsContent } from './noMappingsContent'; import React, { useContext, useEffect, useState } from 'react'; import { Button, DialogActions, DialogContent } from '@mui/material'; -import watchFolderService from 'services/watchFolderService'; +import watchFolderService from 'services/watchFolder/watchFolderService'; import { WatchMapping } from 'types/watchFolder'; import { AppContext } from 'pages/_app'; import constants from 'utils/strings/constants'; diff --git a/src/components/WatchFolder/mappingEntry/entryHeading.tsx b/src/components/WatchFolder/mappingEntry/entryHeading.tsx index fa8c52b46..0e2f6d42c 100644 --- a/src/components/WatchFolder/mappingEntry/entryHeading.tsx +++ b/src/components/WatchFolder/mappingEntry/entryHeading.tsx @@ -1,6 +1,6 @@ import React, { useContext } from 'react'; import { Typography } from '@mui/material'; -import watchFolderService from 'services/watchFolderService'; +import watchFolderService from 'services/watchFolder/watchFolderService'; import { AppContext } from 'pages/_app'; import { SyncProgressIcon } from './syncProgressIcon'; import { FlexWrapper } from 'components/Container'; @@ -22,8 +22,9 @@ export function EntryHeading({ mapping }: Iprops) { {mapping.collectionName} {appContext.isFolderSyncRunning && - watchFolderService.currentEvent?.collectionName === - mapping.collectionName && } + watchFolderService.isMappingSyncing(mapping) && ( + + )} ); } diff --git a/src/components/pages/gallery/Upload.tsx b/src/components/pages/gallery/Upload.tsx index 8e8674d49..4db1d7d8f 100644 --- a/src/components/pages/gallery/Upload.tsx +++ b/src/components/pages/gallery/Upload.tsx @@ -24,7 +24,7 @@ import UploadTypeSelector from '../../UploadTypeSelector'; import Router from 'next/router'; import { isCanvasBlocked } from 'utils/upload/isCanvasBlocked'; import { downloadApp } from 'utils/common'; -import watchFolderService from 'services/watchFolderService'; +import watchFolderService from 'services/watchFolder/watchFolderService'; import DiscFullIcon from '@mui/icons-material/DiscFull'; import { NotificationAttributes } from 'types/Notification'; import { diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index 94ddf3778..a07997ace 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -38,7 +38,7 @@ import uiService from './uiService'; import { logUploadInfo } from 'utils/upload'; import isElectron from 'is-electron'; import ImportService from 'services/importService'; -import watchFolderService from 'services/watchFolderService'; +import watchFolderService from 'services/watchFolder/watchFolderService'; import { ProgressUpdater } from 'types/upload/ui'; const MAX_CONCURRENT_UPLOADS = 4; diff --git a/src/services/watchFolder/watchFolderEventHandlers.ts b/src/services/watchFolder/watchFolderEventHandlers.ts new file mode 100644 index 000000000..aef05f852 --- /dev/null +++ b/src/services/watchFolder/watchFolderEventHandlers.ts @@ -0,0 +1,78 @@ +import { ElectronFile } from 'types/upload'; +import { EventQueueItem } from 'types/watchFolder'; +import { logError } from 'utils/sentry'; +import watchFolderService from './watchFolderService'; + +export async function diskFileAddedCallback(file: ElectronFile) { + try { + const collectionName = await watchFolderService.getCollectionName( + file.path + ); + + if (!collectionName) { + return; + } + + console.log('added (upload) to event queue', collectionName, file); + + const event: EventQueueItem = { + type: 'upload', + collectionName, + files: [file], + }; + watchFolderService.pushEvent(event); + } catch (e) { + logError(e, 'error while calling diskFileAddedCallback'); + } +} + +export async function diskFileRemovedCallback(filePath: string) { + try { + const collectionName = await watchFolderService.getCollectionName( + filePath + ); + + console.log('added (trash) to event queue', collectionName, filePath); + + if (!collectionName) { + return; + } + + const event: EventQueueItem = { + type: 'trash', + collectionName, + paths: [filePath], + }; + watchFolderService.pushEvent(event); + } catch (e) { + logError(e, 'error while calling diskFileRemovedCallback'); + } +} + +export async function diskFolderRemovedCallback(folderPath: string) { + try { + const collectionName = await watchFolderService.getCollectionName( + folderPath + ); + if (!collectionName) { + return; + } + + if (hasMappingSameFolderPath(collectionName, folderPath)) { + watchFolderService.pushTrashedDir(folderPath); + } + } catch (e) { + logError(e, 'error while calling diskFolderRemovedCallback'); + } +} + +const hasMappingSameFolderPath = ( + collectionName: string, + folderPath: string +) => { + const mappings = watchFolderService.getWatchMappings(); + const mapping = mappings.find( + (mapping) => mapping.collectionName === collectionName + ); + return mapping.folderPath === folderPath; +}; diff --git a/src/services/watchFolderService.ts b/src/services/watchFolder/watchFolderService.ts similarity index 81% rename from src/services/watchFolderService.ts rename to src/services/watchFolder/watchFolderService.ts index 150568a56..ecb312560 100644 --- a/src/services/watchFolderService.ts +++ b/src/services/watchFolder/watchFolderService.ts @@ -2,27 +2,32 @@ import { Collection } from 'types/collection'; import { EnteFile } from 'types/file'; import { ElectronFile, FileWithCollection } from 'types/upload'; import { runningInBrowser } from 'utils/common'; -import { removeFromCollection, syncCollections } from './collectionService'; -import { syncFiles } from './fileService'; -import debounce from 'debounce-promise'; +import { removeFromCollection, syncCollections } from '../collectionService'; +import { syncFiles } from '../fileService'; import { logError } from 'utils/sentry'; import { EventQueueItem, WatchMapping } from 'types/watchFolder'; import { ElectronAPIsInterface } from 'types/electron'; +import debounce from 'debounce-promise'; +import { + diskFileAddedCallback, + diskFileRemovedCallback, + diskFolderRemovedCallback, +} from './watchFolderEventHandlers'; -export class watchFolderService { - ElectronAPIs: ElectronAPIsInterface; - allElectronAPIsExist: boolean = false; - eventQueue: EventQueueItem[] = []; - currentEvent: EventQueueItem; - trashingDirQueue: string[] = []; - isEventRunning: boolean = false; - uploadRunning: boolean = false; - pathToIDMap = new Map(); - setElectronFiles: (files: ElectronFile[]) => void; - setCollectionName: (collectionName: string) => void; - syncWithRemote: () => void; - showProgressView: () => void; - setWatchFolderServiceIsRunning: (isRunning: boolean) => void; +class watchFolderService { + private ElectronAPIs: ElectronAPIsInterface; + private allElectronAPIsExist: boolean = false; + private eventQueue: EventQueueItem[] = []; + private currentEvent: EventQueueItem; + private trashingDirQueue: string[] = []; + private isEventRunning: boolean = false; + private uploadRunning: boolean = false; + private pathToIDMap = new Map(); + private setElectronFiles: (files: ElectronFile[]) => void; + private setCollectionName: (collectionName: string) => void; + private syncWithRemote: () => void; + private showProgressView: () => void; + private setWatchFolderServiceIsRunning: (isRunning: boolean) => void; constructor() { this.ElectronAPIs = (runningInBrowser() && @@ -76,6 +81,10 @@ export class watchFolderService { } } + isMappingSyncing(mapping: WatchMapping) { + return this.currentEvent?.collectionName === mapping.collectionName; + } + private uploadDiffOfFiles( mapping: WatchMapping, filesOnDisk: ElectronFile[] @@ -116,7 +125,7 @@ export class watchFolderService { } } - async filterOutDeletedMappings( + private async filterOutDeletedMappings( mappings: WatchMapping[] ): Promise { const notDeletedMappings = []; @@ -132,10 +141,18 @@ export class watchFolderService { return notDeletedMappings; } - setWatchFunctions() { + async pushEvent(event: EventQueueItem) { + this.eventQueue.push(event); + debounce(this.runNextEvent, 300); + } + + async pushTrashedDir(path: string) { + this.trashingDirQueue.push(path); + } + + private setWatchFunctions() { if (this.allElectronAPIsExist) { this.ElectronAPIs.registerWatcherFunctions( - this, diskFileAddedCallback, diskFileRemovedCallback, diskFolderRemovedCallback @@ -178,12 +195,12 @@ export class watchFolderService { return []; } - setIsEventRunning(isEventRunning: boolean) { + private setIsEventRunning(isEventRunning: boolean) { this.isEventRunning = isEventRunning; this.setWatchFolderServiceIsRunning(isEventRunning); } - async runNextEvent() { + private async runNextEvent() { console.log('runNextEvent mappings', this.getWatchMappings()); if (this.eventQueue.length === 0 || this.isEventRunning) { @@ -388,7 +405,7 @@ export class watchFolderService { } } - checkAndIgnoreIfFileEventsFromTrashedDir() { + private checkAndIgnoreIfFileEventsFromTrashedDir() { if (this.trashingDirQueue.length !== 0) { this.ignoreFileEventsFromTrashedDir(this.trashingDirQueue[0]); this.trashingDirQueue.shift(); @@ -398,7 +415,7 @@ export class watchFolderService { return false; } - ignoreFileEventsFromTrashedDir(trashingDir: string) { + private ignoreFileEventsFromTrashedDir(trashingDir: string) { this.eventQueue = this.eventQueue.filter((event) => event.paths.every((path) => !path.startsWith(trashingDir)) ); @@ -462,88 +479,4 @@ export class watchFolderService { } } -async function diskFileAddedCallback( - instance: watchFolderService, - file: ElectronFile -) { - try { - const collectionName = await instance.getCollectionName(file.path); - - if (!collectionName) { - return; - } - - console.log('added (upload) to event queue', collectionName, file); - - const event: EventQueueItem = { - type: 'upload', - collectionName, - files: [file], - }; - instance.eventQueue.push(event); - await debounce(runNextEventByInstance, 300)(instance); - } catch (e) { - logError(e, 'error while calling diskFileAddedCallback'); - } -} - -async function diskFileRemovedCallback( - instance: watchFolderService, - filePath: string -) { - try { - const collectionName = await instance.getCollectionName(filePath); - - console.log('added (trash) to event queue', collectionName, filePath); - - if (!collectionName) { - return; - } - - const event: EventQueueItem = { - type: 'trash', - collectionName, - paths: [filePath], - }; - instance.eventQueue.push(event); - await debounce(runNextEventByInstance, 300)(instance); - } catch (e) { - logError(e, 'error while calling diskFileRemovedCallback'); - } -} - -async function diskFolderRemovedCallback( - instance: watchFolderService, - folderPath: string -) { - try { - const collectionName = await instance.getCollectionName(folderPath); - if (!collectionName) { - return; - } - - if (hasMappingSameFolderPath(instance, collectionName, folderPath)) { - instance.trashingDirQueue.push(folderPath); - } - } catch (e) { - logError(e, 'error while calling diskFolderRemovedCallback'); - } -} - -const runNextEventByInstance = async (w: watchFolderService) => { - await w.runNextEvent(); -}; - -const hasMappingSameFolderPath = ( - w: watchFolderService, - collectionName: string, - folderPath: string -) => { - const mappings = w.getWatchMappings(); - const mapping = mappings.find( - (mapping) => mapping.collectionName === collectionName - ); - return mapping.folderPath === folderPath; -}; - export default new watchFolderService(); diff --git a/src/types/electron/index.ts b/src/types/electron/index.ts index e33dee0e5..944d259bf 100644 --- a/src/types/electron/index.ts +++ b/src/types/electron/index.ts @@ -1,4 +1,3 @@ -import { watchFolderService } from 'services/watchFolderService'; import { ElectronFile } from 'types/upload'; import { WatchMapping } from 'types/watchFolder'; @@ -47,19 +46,9 @@ export interface ElectronAPIsInterface { ) => Promise; removeWatchMapping: (collectionName: string) => Promise; registerWatcherFunctions: ( - watchFolderServiceInstance: watchFolderService, - addFile: ( - watchFolderServiceInstance: watchFolderService, - file: ElectronFile - ) => Promise, - removeFile: ( - watchFolderServiceInstance: watchFolderService, - path: string - ) => Promise, - removeFolder: ( - watchFolderServiceInstance: watchFolderService, - folderPath: string - ) => Promise + addFile: (file: ElectronFile) => Promise, + removeFile: (path: string) => Promise, + removeFolder: (folderPath: string) => Promise ) => void; doesFolderExists: (dirPath: string) => Promise; clearElectronStore: () => void; From 8e95b80a4e472e10da59bd7a9d1402fbb8967c90 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Sun, 19 Jun 2022 20:31:12 +0530 Subject: [PATCH 057/295] bind instance on next event run --- src/services/watchFolder/watchFolderService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/watchFolder/watchFolderService.ts b/src/services/watchFolder/watchFolderService.ts index ecb312560..dbba1a750 100644 --- a/src/services/watchFolder/watchFolderService.ts +++ b/src/services/watchFolder/watchFolderService.ts @@ -143,7 +143,7 @@ class watchFolderService { async pushEvent(event: EventQueueItem) { this.eventQueue.push(event); - debounce(this.runNextEvent, 300); + debounce(this.runNextEvent.bind(this), 300)(); } async pushTrashedDir(path: string) { From c3bb0f22f173ae3e2ec9fe7fd06a1cdd9f9cc37c Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Sun, 19 Jun 2022 21:10:07 +0530 Subject: [PATCH 058/295] change param to id of remove from collection --- src/services/collectionService.ts | 6 +++--- src/services/watchFolder/watchFolderService.ts | 2 +- src/utils/collection/index.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/services/collectionService.ts b/src/services/collectionService.ts index 3d6b9a84c..74a4efd0c 100644 --- a/src/services/collectionService.ts +++ b/src/services/collectionService.ts @@ -353,7 +353,7 @@ export const removeFromFavorites = async (file: EnteFile) => { if (!favCollection) { throw Error(CustomError.FAV_COLLECTION_MISSING); } - await removeFromCollection(favCollection, [file]); + await removeFromCollection(favCollection.id, [file]); } catch (e) { logError(e, 'remove from favorite failed'); } @@ -464,13 +464,13 @@ const encryptWithNewCollectionKey = async ( return fileKeysEncryptedWithNewCollection; }; export const removeFromCollection = async ( - collection: Collection, + collectionID: number, files: EnteFile[] ) => { try { const token = getToken(); const request: RemoveFromCollectionRequest = { - collectionID: collection.id, + collectionID: collectionID, fileIDs: files.map((file) => file.id), }; diff --git a/src/services/watchFolder/watchFolderService.ts b/src/services/watchFolder/watchFolderService.ts index dbba1a750..d55b01990 100644 --- a/src/services/watchFolder/watchFolderService.ts +++ b/src/services/watchFolder/watchFolderService.ts @@ -399,7 +399,7 @@ class watchFolderService { ); }); - await removeFromCollection(collection, filesToTrash); + await removeFromCollection(collection.id, filesToTrash); } catch (e) { logError(e, 'error while trashing by IDs'); } diff --git a/src/utils/collection/index.ts b/src/utils/collection/index.ts index efe3a6935..2e377f2ed 100644 --- a/src/utils/collection/index.ts +++ b/src/utils/collection/index.ts @@ -56,7 +56,7 @@ export async function handleCollectionOps( ); break; case COLLECTION_OPS_TYPE.REMOVE: - await removeFromCollection(collection, selectedFiles); + await removeFromCollection(collection.id, selectedFiles); break; case COLLECTION_OPS_TYPE.RESTORE: await restoreToCollection(collection, selectedFiles); From 380768c4c72b6fb143b012f4651ad533e10e0c99 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Sun, 19 Jun 2022 21:28:07 +0530 Subject: [PATCH 059/295] refactor --- src/components/WatchFolder/index.tsx | 2 +- .../watchFolder/watchFolderService.ts | 37 ++++++++++--------- src/types/electron/index.ts | 2 +- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/components/WatchFolder/index.tsx b/src/components/WatchFolder/index.tsx index bbaf1e14e..2cf3beaf3 100644 --- a/src/components/WatchFolder/index.tsx +++ b/src/components/WatchFolder/index.tsx @@ -61,7 +61,7 @@ export default function WatchFolder({ open, onClose }: Iprops) { }; const handleRemoveWatchMapping = async (mapping: WatchMapping) => { - await watchFolderService.removeWatchMapping(mapping.collectionName); + await watchFolderService.removeWatchMapping(mapping.folderPath); setMappings(watchFolderService.getWatchMappings()); }; diff --git a/src/services/watchFolder/watchFolderService.ts b/src/services/watchFolder/watchFolderService.ts index d55b01990..c5739a394 100644 --- a/src/services/watchFolder/watchFolderService.ts +++ b/src/services/watchFolder/watchFolderService.ts @@ -2,8 +2,11 @@ import { Collection } from 'types/collection'; import { EnteFile } from 'types/file'; import { ElectronFile, FileWithCollection } from 'types/upload'; import { runningInBrowser } from 'utils/common'; -import { removeFromCollection, syncCollections } from '../collectionService'; -import { syncFiles } from '../fileService'; +import { + getLocalCollections, + removeFromCollection, +} from '../collectionService'; +import { getLocalFiles } from '../fileService'; import { logError } from 'utils/sentry'; import { EventQueueItem, WatchMapping } from 'types/watchFolder'; import { ElectronAPIsInterface } from 'types/electron'; @@ -173,10 +176,10 @@ class watchFolderService { } } - async removeWatchMapping(collectionName: string) { + async removeWatchMapping(folderPath: string) { if (this.allElectronAPIsExist) { try { - await this.ElectronAPIs.removeWatchMapping(collectionName); + await this.ElectronAPIs.removeWatchMapping(folderPath); } catch (e) { logError(e, 'error while removing watch mapping'); } @@ -213,7 +216,9 @@ class watchFolderService { if (event.type === 'upload') { this.processUploadEvent(); } else { - this.processTrashEvent(); + await this.processTrashEvent(); + this.setIsEventRunning(false); + this.runNextEvent(); } } @@ -282,19 +287,22 @@ class watchFolderService { if (mapping) { mapping.files = [...mapping.files, ...uploadedFiles]; this.ElectronAPIs.setWatchMappings(mappings); - this.syncWithRemote(); } } - this.setIsEventRunning(false); - this.uploadRunning = false; - this.runNextEvent(); + this.runPostUploadsAction(); } catch (e) { logError(e, 'error while running all file uploads done'); } } } + private runPostUploadsAction() { + this.setIsEventRunning(false); + this.uploadRunning = false; + this.runNextEvent(); + } + private handleUploadedFile( fileWithCollection: FileWithCollection, uploadedFiles: { path: string; id: number }[] @@ -340,7 +348,6 @@ class watchFolderService { private async processTrashEvent() { try { if (this.checkAndIgnoreIfFileEventsFromTrashedDir()) { - this.runNextEvent(); return; } @@ -365,10 +372,6 @@ class watchFolderService { (file) => !filePathsToRemove.has(file.path) ); this.ElectronAPIs.setWatchMappings(mappings); - this.syncWithRemote(); - - this.setIsEventRunning(false); - this.runNextEvent(); } catch (e) { logError(e, 'error while running next trash'); } @@ -379,14 +382,14 @@ class watchFolderService { collectionName: string ) { try { - const collections = await syncCollections(); + const collections = await getLocalCollections(); const collection = collections.find( (collection) => collection.name === collectionName ); if (!collection) { return; } - const files = await syncFiles(collections, () => {}); + const files = await getLocalFiles(); const idSet = new Set(); for (const file of toTrashFiles) { @@ -400,6 +403,7 @@ class watchFolderService { }); await removeFromCollection(collection.id, filesToTrash); + this.syncWithRemote(); } catch (e) { logError(e, 'error while trashing by IDs'); } @@ -409,7 +413,6 @@ class watchFolderService { if (this.trashingDirQueue.length !== 0) { this.ignoreFileEventsFromTrashedDir(this.trashingDirQueue[0]); this.trashingDirQueue.shift(); - this.setIsEventRunning(false); return true; } return false; diff --git a/src/types/electron/index.ts b/src/types/electron/index.ts index 944d259bf..36e3b1e11 100644 --- a/src/types/electron/index.ts +++ b/src/types/electron/index.ts @@ -44,7 +44,7 @@ export interface ElectronAPIsInterface { collectionName: string, folderPath: string ) => Promise; - removeWatchMapping: (collectionName: string) => Promise; + removeWatchMapping: (folderPath: string) => Promise; registerWatcherFunctions: ( addFile: (file: ElectronFile) => Promise, removeFile: (path: string) => Promise, From 59e8112bd235ed771d9f56648ed6bdd0ecf3b3a5 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Tue, 21 Jun 2022 14:02:20 +0530 Subject: [PATCH 060/295] persist file paths for uploaded files --- src/services/upload/uploadManager.ts | 68 ++++++++++++++++------------ src/services/upload/uploader.ts | 49 +++++++++++++++++++- src/types/file/index.ts | 1 + src/types/upload/index.ts | 3 +- src/utils/file/index.ts | 25 +++++++++- src/utils/upload/index.ts | 18 +++++++- 6 files changed, 128 insertions(+), 36 deletions(-) diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index a07997ace..d546e09be 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -19,7 +19,7 @@ import UIService from './uiService'; import UploadService from './uploadService'; import { CustomError } from 'utils/error'; import { Collection } from 'types/collection'; -import { EnteFile } from 'types/file'; +import { EnteFile, FileMagicMetadata } from 'types/file'; import { FileWithCollection, MetadataAndFileTypeInfo, @@ -40,6 +40,7 @@ import isElectron from 'is-electron'; import ImportService from 'services/importService'; import watchFolderService from 'services/watchFolder/watchFolderService'; import { ProgressUpdater } from 'types/upload/ui'; +import { NEW_FILE_MAGIC_METADATA } from 'types/magicMetadata'; const MAX_CONCURRENT_UPLOADS = 4; const FILE_UPLOAD_COMPLETED = 100; @@ -229,38 +230,44 @@ class UploadManager { UIService.reset(mediaFiles.length); for (const { file, localID, collectionID } of mediaFiles) { try { - const { fileTypeInfo, metadata } = await (async () => { - if (file.size >= MAX_FILE_SIZE_SUPPORTED) { - logUploadInfo( - `${getFileNameSize( - file - )} rejected because of large size` - ); + const { fileTypeInfo, metadata, magicMetadata } = + await (async () => { + if (file.size >= MAX_FILE_SIZE_SUPPORTED) { + logUploadInfo( + `${getFileNameSize( + file + )} rejected because of large size` + ); - return { fileTypeInfo: null, metadata: null }; - } - const fileTypeInfo = await UploadService.getFileType( - file - ); - if (fileTypeInfo.fileType === FILE_TYPE.OTHERS) { + return { fileTypeInfo: null, metadata: null }; + } + const fileTypeInfo = + await UploadService.getFileType(file); + if (fileTypeInfo.fileType === FILE_TYPE.OTHERS) { + logUploadInfo( + `${getFileNameSize( + file + )} rejected because of unknown file format` + ); + return { fileTypeInfo, metadata: null }; + } logUploadInfo( - `${getFileNameSize( - file - )} rejected because of unknown file format` + ` extracting ${getFileNameSize(file)} metadata` ); - return { fileTypeInfo, metadata: null }; - } - logUploadInfo( - ` extracting ${getFileNameSize(file)} metadata` - ); - const metadata = - (await UploadService.extractFileMetadata( - file, - collectionID, - fileTypeInfo - )) || null; - return { fileTypeInfo, metadata }; - })(); + const metadata = + (await UploadService.extractFileMetadata( + file, + collectionID, + fileTypeInfo + )) || null; + const magicMetadata = { + ...NEW_FILE_MAGIC_METADATA, + data: { + filePaths: [(file as any).path as string], + }, + } as FileMagicMetadata; + return { fileTypeInfo, metadata, magicMetadata }; + })(); logUploadInfo( `metadata extraction successful${getFileNameSize( @@ -270,6 +277,7 @@ class UploadManager { this.metadataAndFileTypeInfoMap.set(localID, { fileTypeInfo: fileTypeInfo && { ...fileTypeInfo }, metadata: metadata && { ...metadata }, + magicMetadata: magicMetadata && { ...magicMetadata }, }); UIService.increaseFileUploaded(); } catch (e) { diff --git a/src/services/upload/uploader.ts b/src/services/upload/uploader.ts index c43012b04..91e699325 100644 --- a/src/services/upload/uploader.ts +++ b/src/services/upload/uploader.ts @@ -1,9 +1,10 @@ -import { EnteFile } from 'types/file'; +import { EnteFile, FileMagicMetadata } from 'types/file'; import { handleUploadError, CustomError } from 'utils/error'; import { logError } from 'utils/sentry'; import { findSameFileInCollection, findSameFileInOtherCollection, + getMergedMagicMetadataFilePaths, shouldDedupeAcrossCollection, } from 'utils/upload'; import UploadHttpClient from './uploadHttpClient'; @@ -16,12 +17,36 @@ import { logUploadInfo } from 'utils/upload'; import { convertBytesToHumanReadable } from 'utils/billing'; import { sleep } from 'utils/common'; import { addToCollection } from 'services/collectionService'; +import { updateMagicMetadataProps } from 'utils/magicMetadata'; +import { updateFileMagicMetadata } from 'services/fileService'; +import { NEW_FILE_MAGIC_METADATA } from 'types/magicMetadata'; +import { getFileKey } from 'utils/file'; interface UploadResponse { fileUploadResult: UPLOAD_RESULT; uploadedFile?: EnteFile; skipDecryption?: boolean; } + +const updateMagicMetadata = async ( + file: EnteFile, + magicMetadata: FileMagicMetadata, + collectionKey: string +) => { + magicMetadata.data.filePaths = getMergedMagicMetadataFilePaths( + file.magicMetadata, + magicMetadata + ); + file.key = await getFileKey(file, collectionKey); + const updatedMagicMetadata = await updateMagicMetadataProps( + file.magicMetadata ?? NEW_FILE_MAGIC_METADATA, + file.key, + { filePaths: magicMetadata.data.filePaths } + ); + file.magicMetadata = updatedMagicMetadata; + await updateFileMagicMetadata([file]); +}; + export default async function uploader( worker: any, existingFilesInCollection: EnteFile[], @@ -36,7 +61,7 @@ export default async function uploader( logUploadInfo(`uploader called for ${fileNameSize}`); UIService.setFileProgress(localID, 0); await sleep(0); - const { fileTypeInfo, metadata } = + const { fileTypeInfo, metadata, magicMetadata } = UploadService.getFileMetadataAndFileTypeInfo(localID); try { const fileSize = UploadService.getAssetSize(uploadAsset); @@ -56,6 +81,11 @@ export default async function uploader( ); if (sameFileInSameCollection) { logUploadInfo(`skipped upload for ${fileNameSize}`); + await updateMagicMetadata( + sameFileInSameCollection, + magicMetadata, + fileWithCollection.collection.key + ); return { fileUploadResult: UPLOAD_RESULT.ALREADY_UPLOADED, uploadedFile: sameFileInSameCollection, @@ -74,6 +104,11 @@ export default async function uploader( const resultFile = Object.assign({}, sameFileInOtherCollection); resultFile.collectionID = collection.id; await addToCollection(collection, [resultFile]); + await updateMagicMetadata( + resultFile, + magicMetadata, + fileWithCollection.collection.key + ); return { fileUploadResult: UPLOAD_RESULT.UPLOADED, uploadedFile: resultFile, @@ -91,6 +126,11 @@ export default async function uploader( metadata ); if (sameFileInOtherCollection) { + await updateMagicMetadata( + sameFileInOtherCollection, + magicMetadata, + fileWithCollection.collection.key + ); return { fileUploadResult: UPLOAD_RESULT.ALREADY_UPLOADED, uploadedFile: sameFileInOtherCollection, @@ -136,6 +176,11 @@ export default async function uploader( UIService.increaseFileUploaded(); logUploadInfo(`${fileNameSize} successfully uploaded`); + await updateMagicMetadata( + uploadedFile, + magicMetadata, + fileWithCollection.collection.key + ); return { fileUploadResult: metadata.hasStaticThumbnail ? UPLOAD_RESULT.UPLOADED_WITH_STATIC_THUMBNAIL diff --git a/src/types/file/index.ts b/src/types/file/index.ts index b55afbbbf..1bc7a8d0a 100644 --- a/src/types/file/index.ts +++ b/src/types/file/index.ts @@ -9,6 +9,7 @@ export interface fileAttribute { export interface FileMagicMetadataProps { visibility?: VISIBILITY_STATE; + filePaths?: string[]; } export interface FileMagicMetadata extends Omit { diff --git a/src/types/upload/index.ts b/src/types/upload/index.ts index 076ac7fff..0f7716bef 100644 --- a/src/types/upload/index.ts +++ b/src/types/upload/index.ts @@ -1,6 +1,6 @@ import { FILE_TYPE } from 'constants/file'; import { Collection } from 'types/collection'; -import { fileAttribute } from 'types/file'; +import { fileAttribute, FileMagicMetadata } from 'types/file'; export interface DataStream { stream: ReadableStream; @@ -92,6 +92,7 @@ export interface FileWithCollection extends UploadAsset { export interface MetadataAndFileTypeInfo { metadata: Metadata; fileTypeInfo: FileTypeInfo; + magicMetadata: FileMagicMetadata; } export type MetadataAndFileTypeInfoMap = Map; diff --git a/src/utils/file/index.ts b/src/utils/file/index.ts index 8be54c37f..f65571cee 100644 --- a/src/utils/file/index.ts +++ b/src/utils/file/index.ts @@ -274,14 +274,20 @@ export async function decryptFile(file: EnteFile, collectionKey: string) { encryptedMetadata.decryptionHeader, file.key ); - if (file.magicMetadata?.data) { + if ( + file.magicMetadata?.data && + typeof file.magicMetadata.data === 'string' + ) { file.magicMetadata.data = await worker.decryptMetadata( file.magicMetadata.data, file.magicMetadata.header, file.key ); } - if (file.pubMagicMetadata?.data) { + if ( + file.pubMagicMetadata?.data && + typeof file.pubMagicMetadata.data === 'string' + ) { file.pubMagicMetadata.data = await worker.decryptMetadata( file.pubMagicMetadata.data, file.pubMagicMetadata.header, @@ -295,6 +301,21 @@ export async function decryptFile(file: EnteFile, collectionKey: string) { } } +export async function getFileKey(file: EnteFile, collectionKey: string) { + try { + const worker = await new CryptoWorker(); + file.key = await worker.decryptB64( + file.encryptedKey, + file.keyDecryptionNonce, + collectionKey + ); + return file.key; + } catch (e) { + logError(e, 'get file key failed'); + throw e; + } +} + export const preservePhotoswipeProps = (newFiles: EnteFile[]) => (currentFiles: EnteFile[]): EnteFile[] => { diff --git a/src/utils/upload/index.ts b/src/utils/upload/index.ts index 71a27f47b..a002c29e6 100644 --- a/src/utils/upload/index.ts +++ b/src/utils/upload/index.ts @@ -1,5 +1,5 @@ import { ElectronFile, FileWithCollection, Metadata } from 'types/upload'; -import { EnteFile } from 'types/file'; +import { EnteFile, FileMagicMetadata } from 'types/file'; import { convertBytesToHumanReadable } from 'utils/billing'; import { formatDateTime } from 'utils/file'; import { getLogs, saveLogLine } from 'utils/storage'; @@ -141,3 +141,19 @@ export function areFileWithCollectionsSame( ): boolean { return firstFile.localID === secondFile.localID; } + +export function getMergedMagicMetadataFilePaths( + oldMetadata: FileMagicMetadata, + newMetadata: FileMagicMetadata +): string[] { + if (!oldMetadata || !oldMetadata.data.filePaths) { + return newMetadata.data.filePaths; + } + const mergedMetadataFilePaths = [...oldMetadata.data.filePaths]; + newMetadata.data.filePaths.forEach((newMetadataFilePath) => { + if (!mergedMetadataFilePaths.includes(newMetadataFilePath)) { + mergedMetadataFilePaths.push(newMetadataFilePath); + } + }); + return mergedMetadataFilePaths; +} From 9c6ae4e9063b76fb79926c2b0619c072e151b231 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Wed, 22 Jun 2022 10:14:51 +0530 Subject: [PATCH 061/295] added functionality to handle multiple folders --- src/components/WatchFolder/index.tsx | 3 +- .../watchFolder/watchFolderEventHandlers.ts | 43 +++++----- .../watchFolder/watchFolderService.ts | 79 +++++++++++++------ src/types/electron/index.ts | 3 +- src/types/watchFolder/index.ts | 4 +- 5 files changed, 78 insertions(+), 54 deletions(-) diff --git a/src/components/WatchFolder/index.tsx b/src/components/WatchFolder/index.tsx index 2cf3beaf3..8bb2cd234 100644 --- a/src/components/WatchFolder/index.tsx +++ b/src/components/WatchFolder/index.tsx @@ -54,7 +54,8 @@ export default function WatchFolder({ open, onClose }: Iprops) { if (inputFolderPath?.length > 0) { await watchFolderService.addWatchMapping( inputFolderPath.substring(inputFolderPath.lastIndexOf('/') + 1), - inputFolderPath + inputFolderPath, + true ); setMappings(watchFolderService.getWatchMappings()); } diff --git a/src/services/watchFolder/watchFolderEventHandlers.ts b/src/services/watchFolder/watchFolderEventHandlers.ts index aef05f852..a20c01316 100644 --- a/src/services/watchFolder/watchFolderEventHandlers.ts +++ b/src/services/watchFolder/watchFolderEventHandlers.ts @@ -5,11 +5,12 @@ import watchFolderService from './watchFolderService'; export async function diskFileAddedCallback(file: ElectronFile) { try { - const collectionName = await watchFolderService.getCollectionName( - file.path - ); + const { collectionName, folderPath } = + (await watchFolderService.getCollectionNameAndFolderPath( + file.path + )) ?? {}; - if (!collectionName) { + if (!folderPath) { return; } @@ -18,6 +19,7 @@ export async function diskFileAddedCallback(file: ElectronFile) { const event: EventQueueItem = { type: 'upload', collectionName, + folderPath, files: [file], }; watchFolderService.pushEvent(event); @@ -28,19 +30,20 @@ export async function diskFileAddedCallback(file: ElectronFile) { export async function diskFileRemovedCallback(filePath: string) { try { - const collectionName = await watchFolderService.getCollectionName( - filePath - ); - + const { collectionName, folderPath } = + (await watchFolderService.getCollectionNameAndFolderPath( + filePath + )) ?? {}; console.log('added (trash) to event queue', collectionName, filePath); - if (!collectionName) { + if (!folderPath) { return; } const event: EventQueueItem = { type: 'trash', collectionName, + folderPath, paths: [filePath], }; watchFolderService.pushEvent(event); @@ -51,28 +54,18 @@ export async function diskFileRemovedCallback(filePath: string) { export async function diskFolderRemovedCallback(folderPath: string) { try { - const collectionName = await watchFolderService.getCollectionName( - folderPath - ); - if (!collectionName) { + const { folderPath: mappedFolderPath } = + (await watchFolderService.getCollectionNameAndFolderPath( + folderPath + )) ?? {}; + if (!mappedFolderPath) { return; } - if (hasMappingSameFolderPath(collectionName, folderPath)) { + if (mappedFolderPath === folderPath) { watchFolderService.pushTrashedDir(folderPath); } } catch (e) { logError(e, 'error while calling diskFolderRemovedCallback'); } } - -const hasMappingSameFolderPath = ( - collectionName: string, - folderPath: string -) => { - const mappings = watchFolderService.getWatchMappings(); - const mapping = mappings.find( - (mapping) => mapping.collectionName === collectionName - ); - return mapping.folderPath === folderPath; -}; diff --git a/src/services/watchFolder/watchFolderService.ts b/src/services/watchFolder/watchFolderService.ts index c5739a394..15407fa30 100644 --- a/src/services/watchFolder/watchFolderService.ts +++ b/src/services/watchFolder/watchFolderService.ts @@ -29,7 +29,6 @@ class watchFolderService { private setElectronFiles: (files: ElectronFile[]) => void; private setCollectionName: (collectionName: string) => void; private syncWithRemote: () => void; - private showProgressView: () => void; private setWatchFolderServiceIsRunning: (isRunning: boolean) => void; constructor() { @@ -85,7 +84,7 @@ class watchFolderService { } isMappingSyncing(mapping: WatchMapping) { - return this.currentEvent?.collectionName === mapping.collectionName; + return this.currentEvent?.folderPath === mapping.folderPath; } private uploadDiffOfFiles( @@ -99,12 +98,17 @@ class watchFolderService { }); if (filesToUpload.length > 0) { - const event: EventQueueItem = { - type: 'upload', - collectionName: mapping.collectionName, - files: filesToUpload, - }; - this.eventQueue.push(event); + for (const file of filesToUpload) { + const event: EventQueueItem = { + type: 'upload', + collectionName: mapping.hasMultipleFolders + ? this.getParentFolderName(file.path) + : mapping.collectionName, + folderPath: mapping.folderPath, + files: [file], + }; + this.eventQueue.push(event); + } } } @@ -119,12 +123,17 @@ class watchFolderService { }); if (filesToRemove.length > 0) { - const event: EventQueueItem = { - type: 'trash', - collectionName: mapping.collectionName, - paths: filesToRemove.map((file) => file.path), - }; - this.eventQueue.push(event); + for (const file of filesToRemove) { + const event: EventQueueItem = { + type: 'trash', + collectionName: mapping.hasMultipleFolders + ? this.getParentFolderName(file.path) + : mapping.collectionName, + folderPath: mapping.folderPath, + paths: [file.path], + }; + this.eventQueue.push(event); + } } } @@ -163,12 +172,17 @@ class watchFolderService { } } - async addWatchMapping(collectionName: string, folderPath: string) { + async addWatchMapping( + collectionName: string, + folderPath: string, + hasMultipleFolders: boolean + ) { if (this.allElectronAPIsExist) { try { await this.ElectronAPIs.addWatchMapping( collectionName, - folderPath + folderPath, + hasMultipleFolders ); } catch (e) { logError(e, 'error while adding watch mapping'); @@ -204,7 +218,7 @@ class watchFolderService { } private async runNextEvent() { - console.log('runNextEvent mappings', this.getWatchMappings()); + console.log('mappings', this.getWatchMappings()); if (this.eventQueue.length === 0 || this.isEventRunning) { return; @@ -213,6 +227,7 @@ class watchFolderService { this.setIsEventRunning(true); const event = this.clubSameCollectionEvents(); this.currentEvent = event; + console.log('running event', event); if (event.type === 'upload') { this.processUploadEvent(); } else { @@ -281,8 +296,7 @@ class watchFolderService { const mappings = this.getWatchMappings(); const mapping = mappings.find( (mapping) => - mapping.collectionName === - this.currentEvent.collectionName + mapping.folderPath === this.currentEvent.folderPath ); if (mapping) { mapping.files = [...mapping.files, ...uploadedFiles]; @@ -351,12 +365,12 @@ class watchFolderService { return; } - const { collectionName, paths } = this.currentEvent; + const { collectionName, folderPath, paths } = this.currentEvent; const filePathsToRemove = new Set(paths); const mappings = this.getWatchMappings(); const mappingIdx = mappings.findIndex( - (mapping) => mapping.collectionName === collectionName + (mapping) => mapping.folderPath === folderPath ); if (mappingIdx === -1) { return; @@ -424,24 +438,37 @@ class watchFolderService { ); } - async getCollectionName(filePath: string) { + async getCollectionNameAndFolderPath(filePath: string) { try { const mappings = this.getWatchMappings(); - const collectionName = mappings.find((mapping) => + const mapping = mappings.find((mapping) => filePath.startsWith(mapping.folderPath + '/') - )?.collectionName; + ); - if (!collectionName) { + if (!mapping) { return null; } - return collectionName; + return { + collectionName: mapping.hasMultipleFolders + ? this.getParentFolderName(filePath) + : mapping.collectionName, + folderPath: mapping.folderPath, + }; } catch (e) { logError(e, 'error while getting collection name'); } } + private getParentFolderName(filePath: string) { + const folderPath = filePath.substring(0, filePath.lastIndexOf('/')); + const folderName = folderPath.substring( + folderPath.lastIndexOf('/') + 1 + ); + return folderName; + } + async selectFolder(): Promise { try { const folderPath = await this.ElectronAPIs.selectRootDirectory(); diff --git a/src/types/electron/index.ts b/src/types/electron/index.ts index 36e3b1e11..893b0174d 100644 --- a/src/types/electron/index.ts +++ b/src/types/electron/index.ts @@ -42,7 +42,8 @@ export interface ElectronAPIsInterface { setWatchMappings: (watchMappings: WatchMapping[]) => void; addWatchMapping: ( collectionName: string, - folderPath: string + folderPath: string, + hasMultipleFolders: boolean ) => Promise; removeWatchMapping: (folderPath: string) => Promise; registerWatcherFunctions: ( diff --git a/src/types/watchFolder/index.ts b/src/types/watchFolder/index.ts index fdd29e4d9..8d9757b36 100644 --- a/src/types/watchFolder/index.ts +++ b/src/types/watchFolder/index.ts @@ -3,6 +3,7 @@ import { ElectronFile } from 'types/upload'; export interface WatchMapping { collectionName: string; folderPath: string; + hasMultipleFolders: boolean; files: { path: string; id: number; @@ -11,7 +12,8 @@ export interface WatchMapping { export interface EventQueueItem { type: 'upload' | 'trash'; - collectionName: string; + folderPath: string; + collectionName?: string; paths?: string[]; files?: ElectronFile[]; } From 504154065cb2511df30a000b14c93dfa316757b4 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Wed, 22 Jun 2022 11:27:50 +0530 Subject: [PATCH 062/295] added choice modal --- src/components/WatchFolder/index.tsx | 96 ++++++++++++++++++---------- 1 file changed, 62 insertions(+), 34 deletions(-) diff --git a/src/components/WatchFolder/index.tsx b/src/components/WatchFolder/index.tsx index 8bb2cd234..4c4e12fd1 100644 --- a/src/components/WatchFolder/index.tsx +++ b/src/components/WatchFolder/index.tsx @@ -8,6 +8,7 @@ import { AppContext } from 'pages/_app'; import constants from 'utils/strings/constants'; import DialogBoxBase from 'components/DialogBox/base'; import DialogTitleWithCloseButton from 'components/DialogBox/titleWithCloseButton'; +import UploadStrategyChoiceModal from 'components/pages/gallery/UploadStrategyChoiceModal'; interface Iprops { open: boolean; @@ -16,6 +17,8 @@ interface Iprops { export default function WatchFolder({ open, onClose }: Iprops) { const [mappings, setMappings] = useState([]); + const [inputFolderPath, setInputFolderPath] = useState(''); + const [choicModalOpen, setChoiceModalOpen] = useState(false); const appContext = useContext(AppContext); useEffect(() => { @@ -36,7 +39,8 @@ export default function WatchFolder({ open, onClose }: Iprops) { const folder: any = folders[i]; const path = (folder.path as string).replace(/\\/g, '/'); if (await watchFolderService.isFolder(path)) { - await handleAddWatchMapping(path); + setInputFolderPath(path); + setChoiceModalOpen(true); } } }; @@ -47,16 +51,20 @@ export default function WatchFolder({ open, onClose }: Iprops) { const handleFolderSelection = async () => { const folderPath = await watchFolderService.selectFolder(); - await handleAddWatchMapping(folderPath); + if (folderPath?.length > 0) { + setInputFolderPath(folderPath); + setChoiceModalOpen(true); + } }; - const handleAddWatchMapping = async (inputFolderPath: string) => { + const handleAddWatchMapping = async (hasMultipleFolders: boolean) => { if (inputFolderPath?.length > 0) { await watchFolderService.addWatchMapping( inputFolderPath.substring(inputFolderPath.lastIndexOf('/') + 1), inputFolderPath, - true + hasMultipleFolders ); + setInputFolderPath(''); setMappings(watchFolderService.getWatchMappings()); } }; @@ -66,36 +74,56 @@ export default function WatchFolder({ open, onClose }: Iprops) { setMappings(watchFolderService.getWatchMappings()); }; - return ( - - - {constants.WATCHED_FOLDERS} - - - {mappings.length === 0 ? ( - - ) : ( - - )} - + const onChoiceModalClose = () => setChoiceModalOpen(false); - - - - + const uploadToSingleCollection = () => { + setChoiceModalOpen(false); + handleAddWatchMapping(false); + }; + + const uploadToMultipleCollection = () => { + setChoiceModalOpen(false); + handleAddWatchMapping(true); + }; + + return ( + <> + + + {constants.WATCHED_FOLDERS} + + + {mappings.length === 0 ? ( + + ) : ( + + )} + + + + + + + + ); } From 767d1d26133d173bdac0027a0b5349334c0bca92 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Wed, 22 Jun 2022 11:29:14 +0530 Subject: [PATCH 063/295] fix path bug --- src/services/watchFolder/watchFolderService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/watchFolder/watchFolderService.ts b/src/services/watchFolder/watchFolderService.ts index 15407fa30..0e3709f40 100644 --- a/src/services/watchFolder/watchFolderService.ts +++ b/src/services/watchFolder/watchFolderService.ts @@ -443,7 +443,7 @@ class watchFolderService { const mappings = this.getWatchMappings(); const mapping = mappings.find((mapping) => - filePath.startsWith(mapping.folderPath + '/') + filePath.startsWith(mapping.folderPath) ); if (!mapping) { From e568261c6d6a9d9d282a75bc34fabb9a11060a6a Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Wed, 22 Jun 2022 11:44:45 +0530 Subject: [PATCH 064/295] fix live photo magic metadata --- src/services/upload/livePhotoService.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/services/upload/livePhotoService.ts b/src/services/upload/livePhotoService.ts index 7fbf44a2b..80b84e4f3 100644 --- a/src/services/upload/livePhotoService.ts +++ b/src/services/upload/livePhotoService.ts @@ -1,6 +1,7 @@ import { FILE_TYPE } from 'constants/file'; import { LIVE_PHOTO_ASSET_SIZE_LIMIT } from 'constants/upload'; import { encodeMotionPhoto } from 'services/motionPhotoService'; +import { NEW_FILE_MAGIC_METADATA } from 'types/magicMetadata'; import { ElectronFile, FileTypeInfo, @@ -192,6 +193,15 @@ export function clusterLivePhotoFiles(mediaFiles: FileWithCollection[]) { uploadService.setFileMetadataAndFileTypeInfo(livePhotoLocalID, { fileTypeInfo: { ...livePhotoFileTypeInfo }, metadata: { ...livePhotoMetadata }, + magicMetadata: { + ...NEW_FILE_MAGIC_METADATA, + data: { + filePaths: [ + (firstMediaFile.file as any).path as string, + (secondMediaFile.file as any).path as string, + ], + }, + }, }); index += 2; } else { From fa471202835d0bbea7bfb197478c0fd0044c964f Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Wed, 22 Jun 2022 14:09:40 +0530 Subject: [PATCH 065/295] remove unrequired prop --- src/pages/gallery/index.tsx | 9 +-------- src/types/gallery/index.ts | 1 - 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/pages/gallery/index.tsx b/src/pages/gallery/index.tsx index e23cb7bb0..cadedbbb9 100644 --- a/src/pages/gallery/index.tsx +++ b/src/pages/gallery/index.tsx @@ -127,7 +127,6 @@ const defaultGalleryContext: GalleryContextType = { syncWithRemote: () => null, setNotificationAttributes: () => null, setBlockingLoad: () => null, - setDropZoneActive: () => null, photoListHeader: null, }; @@ -158,7 +157,6 @@ export default function Gallery() { const [collectionNamerView, setCollectionNamerView] = useState(false); const [search, setSearch] = useState(null); const [uploadInProgress, setUploadInProgress] = useState(false); - const [dropZoneDisabled, setDropZoneDisabled] = useState(false); const { getRootProps, @@ -169,7 +167,7 @@ export default function Gallery() { } = useDropzone({ noClick: true, noKeyboard: true, - disabled: uploadInProgress || dropZoneDisabled, + disabled: uploadInProgress, }); const [isInSearchMode, setIsInSearchMode] = useState(false); @@ -589,10 +587,6 @@ export default function Gallery() { finishLoading(); }; - const setDropZoneActive = (active: boolean) => { - setDropZoneDisabled(!active); - }; - const openUploader = () => { if (importService.checkAllElectronAPIsExists()) { setUploadTypeSelectorView(true); @@ -615,7 +609,6 @@ export default function Gallery() { syncWithRemote, setNotificationAttributes, setBlockingLoad, - setDropZoneActive, photoListHeader: photoListHeader, }}> Promise; setNotificationAttributes: (attributes: NotificationAttributes) => void; setBlockingLoad: (value: boolean) => void; - setDropZoneActive: (value: boolean) => void; photoListHeader: TimeStampListItem; }; From 97d9621dc7954a7c7c91e9eeb5efe95508c1e212 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Thu, 23 Jun 2022 16:28:45 +0530 Subject: [PATCH 066/295] refactor watch service --- src/components/WatchFolder/index.tsx | 27 ++++++------ .../WatchFolder/mappingEntry/entryHeading.tsx | 2 +- src/components/WatchFolder/mappingList.tsx | 2 +- src/components/pages/gallery/Upload.tsx | 2 +- src/services/watchFolder/utils.ts | 5 +++ .../watchFolder/watchFolderEventHandlers.ts | 33 +++++++------- .../watchFolder/watchFolderService.ts | 44 +++++++++++-------- src/types/electron/index.ts | 2 +- src/types/watchFolder/index.ts | 5 ++- 9 files changed, 67 insertions(+), 55 deletions(-) create mode 100644 src/services/watchFolder/utils.ts diff --git a/src/components/WatchFolder/index.tsx b/src/components/WatchFolder/index.tsx index 4c4e12fd1..0c9f90e6b 100644 --- a/src/components/WatchFolder/index.tsx +++ b/src/components/WatchFolder/index.tsx @@ -9,6 +9,7 @@ import constants from 'utils/strings/constants'; import DialogBoxBase from 'components/DialogBox/base'; import DialogTitleWithCloseButton from 'components/DialogBox/titleWithCloseButton'; import UploadStrategyChoiceModal from 'components/pages/gallery/UploadStrategyChoiceModal'; +import { UPLOAD_STRATEGY } from 'components/pages/gallery/Upload'; interface Iprops { open: boolean; @@ -57,16 +58,14 @@ export default function WatchFolder({ open, onClose }: Iprops) { } }; - const handleAddWatchMapping = async (hasMultipleFolders: boolean) => { - if (inputFolderPath?.length > 0) { - await watchFolderService.addWatchMapping( - inputFolderPath.substring(inputFolderPath.lastIndexOf('/') + 1), - inputFolderPath, - hasMultipleFolders - ); - setInputFolderPath(''); - setMappings(watchFolderService.getWatchMappings()); - } + const handleAddWatchMapping = async (uploadStrategy: UPLOAD_STRATEGY) => { + await watchFolderService.addWatchMapping( + inputFolderPath.substring(inputFolderPath.lastIndexOf('/') + 1), + inputFolderPath, + uploadStrategy + ); + setInputFolderPath(''); + setMappings(watchFolderService.getWatchMappings()); }; const handleRemoveWatchMapping = async (mapping: WatchMapping) => { @@ -74,16 +73,16 @@ export default function WatchFolder({ open, onClose }: Iprops) { setMappings(watchFolderService.getWatchMappings()); }; - const onChoiceModalClose = () => setChoiceModalOpen(false); + const closeChoiceModal = () => setChoiceModalOpen(false); const uploadToSingleCollection = () => { setChoiceModalOpen(false); - handleAddWatchMapping(false); + handleAddWatchMapping(UPLOAD_STRATEGY.SINGLE_COLLECTION); }; const uploadToMultipleCollection = () => { setChoiceModalOpen(false); - handleAddWatchMapping(true); + handleAddWatchMapping(UPLOAD_STRATEGY.COLLECTION_PER_FOLDER); }; return ( @@ -120,7 +119,7 @@ export default function WatchFolder({ open, onClose }: Iprops) { diff --git a/src/components/WatchFolder/mappingEntry/entryHeading.tsx b/src/components/WatchFolder/mappingEntry/entryHeading.tsx index 0e2f6d42c..12553f63e 100644 --- a/src/components/WatchFolder/mappingEntry/entryHeading.tsx +++ b/src/components/WatchFolder/mappingEntry/entryHeading.tsx @@ -19,7 +19,7 @@ export function EntryHeading({ mapping }: Iprops) { fontSize: '16px', lineHeight: '20px', }}> - {mapping.collectionName} + {mapping.rootFolderName} {appContext.isFolderSyncRunning && watchFolderService.isMappingSyncing(mapping) && ( diff --git a/src/components/WatchFolder/mappingList.tsx b/src/components/WatchFolder/mappingList.tsx index 1578ed7af..91633a312 100644 --- a/src/components/WatchFolder/mappingList.tsx +++ b/src/components/WatchFolder/mappingList.tsx @@ -13,7 +13,7 @@ export function MappingList({ mappings, handleRemoveWatchMapping }: Iprops) { {mappings.map((mapping) => { return ( diff --git a/src/components/pages/gallery/Upload.tsx b/src/components/pages/gallery/Upload.tsx index 4db1d7d8f..86d9451a1 100644 --- a/src/components/pages/gallery/Upload.tsx +++ b/src/components/pages/gallery/Upload.tsx @@ -59,7 +59,7 @@ interface Props { showSessionExpiredMessage: () => void; } -enum UPLOAD_STRATEGY { +export enum UPLOAD_STRATEGY { SINGLE_COLLECTION, COLLECTION_PER_FOLDER, } diff --git a/src/services/watchFolder/utils.ts b/src/services/watchFolder/utils.ts new file mode 100644 index 000000000..bce90a2c8 --- /dev/null +++ b/src/services/watchFolder/utils.ts @@ -0,0 +1,5 @@ +export const getParentFolderName = (filePath: string) => { + const folderPath = filePath.substring(0, filePath.lastIndexOf('/')); + const folderName = folderPath.substring(folderPath.lastIndexOf('/') + 1); + return folderName; +}; diff --git a/src/services/watchFolder/watchFolderEventHandlers.ts b/src/services/watchFolder/watchFolderEventHandlers.ts index a20c01316..cf3c80dd1 100644 --- a/src/services/watchFolder/watchFolderEventHandlers.ts +++ b/src/services/watchFolder/watchFolderEventHandlers.ts @@ -5,15 +5,15 @@ import watchFolderService from './watchFolderService'; export async function diskFileAddedCallback(file: ElectronFile) { try { - const { collectionName, folderPath } = - (await watchFolderService.getCollectionNameAndFolderPath( - file.path - )) ?? {}; + const collectionNameAndFolderPath = + await watchFolderService.getCollectionNameAndFolderPath(file.path); - if (!folderPath) { + if (!collectionNameAndFolderPath) { return; } + const { collectionName, folderPath } = collectionNameAndFolderPath; + console.log('added (upload) to event queue', collectionName, file); const event: EventQueueItem = { @@ -30,16 +30,17 @@ export async function diskFileAddedCallback(file: ElectronFile) { export async function diskFileRemovedCallback(filePath: string) { try { - const { collectionName, folderPath } = - (await watchFolderService.getCollectionNameAndFolderPath( - filePath - )) ?? {}; - console.log('added (trash) to event queue', collectionName, filePath); + const collectionNameAndFolderPath = + await watchFolderService.getCollectionNameAndFolderPath(filePath); - if (!folderPath) { + if (!collectionNameAndFolderPath) { return; } + const { collectionName, folderPath } = collectionNameAndFolderPath; + + console.log('added (trash) to event queue', collectionName, filePath); + const event: EventQueueItem = { type: 'trash', collectionName, @@ -54,14 +55,14 @@ export async function diskFileRemovedCallback(filePath: string) { export async function diskFolderRemovedCallback(folderPath: string) { try { - const { folderPath: mappedFolderPath } = - (await watchFolderService.getCollectionNameAndFolderPath( - folderPath - )) ?? {}; - if (!mappedFolderPath) { + const collectionNameAndFolderPath = + await watchFolderService.getCollectionNameAndFolderPath(folderPath); + if (!collectionNameAndFolderPath) { return; } + const { folderPath: mappedFolderPath } = collectionNameAndFolderPath; + if (mappedFolderPath === folderPath) { watchFolderService.pushTrashedDir(folderPath); } diff --git a/src/services/watchFolder/watchFolderService.ts b/src/services/watchFolder/watchFolderService.ts index 0e3709f40..201362647 100644 --- a/src/services/watchFolder/watchFolderService.ts +++ b/src/services/watchFolder/watchFolderService.ts @@ -16,6 +16,8 @@ import { diskFileRemovedCallback, diskFolderRemovedCallback, } from './watchFolderEventHandlers'; +import { UPLOAD_STRATEGY } from 'components/pages/gallery/Upload'; +import { getParentFolderName } from './utils'; class watchFolderService { private ElectronAPIs: ElectronAPIsInterface; @@ -101,9 +103,10 @@ class watchFolderService { for (const file of filesToUpload) { const event: EventQueueItem = { type: 'upload', - collectionName: mapping.hasMultipleFolders - ? this.getParentFolderName(file.path) - : mapping.collectionName, + collectionName: this.getCollectionNameForMapping( + mapping, + file.path + ), folderPath: mapping.folderPath, files: [file], }; @@ -126,9 +129,10 @@ class watchFolderService { for (const file of filesToRemove) { const event: EventQueueItem = { type: 'trash', - collectionName: mapping.hasMultipleFolders - ? this.getParentFolderName(file.path) - : mapping.collectionName, + collectionName: this.getCollectionNameForMapping( + mapping, + file.path + ), folderPath: mapping.folderPath, paths: [file.path], }; @@ -173,16 +177,16 @@ class watchFolderService { } async addWatchMapping( - collectionName: string, + rootFolderName: string, folderPath: string, - hasMultipleFolders: boolean + uploadStrategy: UPLOAD_STRATEGY ) { if (this.allElectronAPIsExist) { try { await this.ElectronAPIs.addWatchMapping( - collectionName, + rootFolderName, folderPath, - hasMultipleFolders + uploadStrategy === UPLOAD_STRATEGY.COLLECTION_PER_FOLDER ); } catch (e) { logError(e, 'error while adding watch mapping'); @@ -451,9 +455,10 @@ class watchFolderService { } return { - collectionName: mapping.hasMultipleFolders - ? this.getParentFolderName(filePath) - : mapping.collectionName, + collectionName: this.getCollectionNameForMapping( + mapping, + filePath + ), folderPath: mapping.folderPath, }; } catch (e) { @@ -461,12 +466,13 @@ class watchFolderService { } } - private getParentFolderName(filePath: string) { - const folderPath = filePath.substring(0, filePath.lastIndexOf('/')); - const folderName = folderPath.substring( - folderPath.lastIndexOf('/') + 1 - ); - return folderName; + private getCollectionNameForMapping( + mapping: WatchMapping, + filePath: string + ) { + return mapping.uploadStrategy === UPLOAD_STRATEGY.COLLECTION_PER_FOLDER + ? getParentFolderName(filePath) + : mapping.rootFolderName; } async selectFolder(): Promise { diff --git a/src/types/electron/index.ts b/src/types/electron/index.ts index 893b0174d..653ea77f3 100644 --- a/src/types/electron/index.ts +++ b/src/types/electron/index.ts @@ -43,7 +43,7 @@ export interface ElectronAPIsInterface { addWatchMapping: ( collectionName: string, folderPath: string, - hasMultipleFolders: boolean + uploadStrategy: boolean ) => Promise; removeWatchMapping: (folderPath: string) => Promise; registerWatcherFunctions: ( diff --git a/src/types/watchFolder/index.ts b/src/types/watchFolder/index.ts index 8d9757b36..1c02e4b44 100644 --- a/src/types/watchFolder/index.ts +++ b/src/types/watchFolder/index.ts @@ -1,9 +1,10 @@ +import { UPLOAD_STRATEGY } from 'components/pages/gallery/Upload'; import { ElectronFile } from 'types/upload'; export interface WatchMapping { - collectionName: string; + rootFolderName: string; folderPath: string; - hasMultipleFolders: boolean; + uploadStrategy: UPLOAD_STRATEGY; files: { path: string; id: number; From 7f76945ddae3fc1607b7c648cc6c8a5bf58a2ba8 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Thu, 23 Jun 2022 16:50:52 +0530 Subject: [PATCH 067/295] change if else to switch case --- src/services/upload/uploadManager.ts | 60 ++++++++++++++-------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index f711dae86..f3643110b 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -357,36 +357,38 @@ class UploadManager { let decryptedFile: EnteFile; logUploadInfo(`uploadedFile ${JSON.stringify(uploadedFile)}`); this.updateElectronRemainingFiles(fileWithCollection); - if ( - fileUploadResult === - UPLOAD_RESULT.LARGER_THAN_AVAILABLE_STORAGE || - fileUploadResult === UPLOAD_RESULT.TOO_LARGE || - fileUploadResult === UPLOAD_RESULT.UNSUPPORTED - ) { - // no-op - } else if ( - fileUploadResult === UPLOAD_RESULT.FAILED || - fileUploadResult === UPLOAD_RESULT.BLOCKED - ) { - this.failedFiles.push(fileWithCollection); - } else if (fileUploadResult === UPLOAD_RESULT.ALREADY_UPLOADED) { - if (isElectron()) { - await watchFolderService.onFileUpload( - fileWithCollection, - uploadedFile - ); + switch (fileUploadResult) { + case UPLOAD_RESULT.LARGER_THAN_AVAILABLE_STORAGE: + case UPLOAD_RESULT.TOO_LARGE: + case UPLOAD_RESULT.UNSUPPORTED: + // no-op + return; + case UPLOAD_RESULT.FAILED: + case UPLOAD_RESULT.BLOCKED: + this.failedFiles.push(fileWithCollection); + return; + case UPLOAD_RESULT.ALREADY_UPLOADED: { + if (isElectron()) { + await watchFolderService.onFileUpload( + fileWithCollection, + uploadedFile + ); + } + return; } - } else if ( - fileUploadResult === UPLOAD_RESULT.UPLOADED || - fileUploadResult === - UPLOAD_RESULT.UPLOADED_WITH_STATIC_THUMBNAIL - ) { - decryptedFile = await decryptFile( - uploadedFile, - fileWithCollection.collection.key - ); - } else { - decryptedFile = uploadedFile; + case UPLOAD_RESULT.UPLOADED: + case UPLOAD_RESULT.UPLOADED_WITH_STATIC_THUMBNAIL: { + decryptedFile = await decryptFile( + uploadedFile, + fileWithCollection.collection.key + ); + break; + } + case UPLOAD_RESULT.ADDED_SYMLINK: + decryptedFile = uploadedFile; + break; + default: + throw Error('invalid result'); } await this.updateExistingFiles(decryptedFile); this.updateExistingCollections(decryptedFile); From 59295f849d1aebfa27cf8420488902c3a339bfa3 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Thu, 23 Jun 2022 16:59:10 +0530 Subject: [PATCH 068/295] reorder cases --- src/services/upload/uploadManager.ts | 34 +++++++++++++--------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index f3643110b..e7678930c 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -358,41 +358,39 @@ class UploadManager { logUploadInfo(`uploadedFile ${JSON.stringify(uploadedFile)}`); this.updateElectronRemainingFiles(fileWithCollection); switch (fileUploadResult) { - case UPLOAD_RESULT.LARGER_THAN_AVAILABLE_STORAGE: - case UPLOAD_RESULT.TOO_LARGE: - case UPLOAD_RESULT.UNSUPPORTED: - // no-op - return; case UPLOAD_RESULT.FAILED: case UPLOAD_RESULT.BLOCKED: this.failedFiles.push(fileWithCollection); - return; - case UPLOAD_RESULT.ALREADY_UPLOADED: { + break; + case UPLOAD_RESULT.ALREADY_UPLOADED: if (isElectron()) { await watchFolderService.onFileUpload( fileWithCollection, uploadedFile ); } - return; - } + break; + case UPLOAD_RESULT.ADDED_SYMLINK: + decryptedFile = uploadedFile; + break; case UPLOAD_RESULT.UPLOADED: - case UPLOAD_RESULT.UPLOADED_WITH_STATIC_THUMBNAIL: { + case UPLOAD_RESULT.UPLOADED_WITH_STATIC_THUMBNAIL: decryptedFile = await decryptFile( uploadedFile, fileWithCollection.collection.key ); break; - } - case UPLOAD_RESULT.ADDED_SYMLINK: - decryptedFile = uploadedFile; - break; default: - throw Error('invalid result'); + // no-op + } + if (decryptedFile) { + await this.updateExistingFiles(decryptedFile); + this.updateExistingCollections(decryptedFile); + await this.watchFolderCallback( + fileWithCollection, + uploadedFile + ); } - await this.updateExistingFiles(decryptedFile); - this.updateExistingCollections(decryptedFile); - await this.watchFolderCallback(fileWithCollection, uploadedFile); } catch (e) { logError(e, 'failed to do post file upload action'); logUploadInfo( From 0842abf8a3c16c866d39b8b99a40c55ece7cdfba Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Thu, 23 Jun 2022 19:10:19 +0530 Subject: [PATCH 069/295] refactor --- src/services/upload/livePhotoService.ts | 22 ++++++------ src/services/upload/uploadManager.ts | 18 ++++++---- src/services/upload/uploader.ts | 48 ++----------------------- src/types/upload/index.ts | 2 +- src/utils/file/index.ts | 33 +++++++++++++++++ src/utils/upload/index.ts | 18 +--------- 6 files changed, 61 insertions(+), 80 deletions(-) diff --git a/src/services/upload/livePhotoService.ts b/src/services/upload/livePhotoService.ts index 80b84e4f3..740c4da2a 100644 --- a/src/services/upload/livePhotoService.ts +++ b/src/services/upload/livePhotoService.ts @@ -1,7 +1,7 @@ import { FILE_TYPE } from 'constants/file'; import { LIVE_PHOTO_ASSET_SIZE_LIMIT } from 'constants/upload'; import { encodeMotionPhoto } from 'services/motionPhotoService'; -import { NEW_FILE_MAGIC_METADATA } from 'types/magicMetadata'; +import { FileMagicMetadata } from 'types/file'; import { ElectronFile, FileTypeInfo, @@ -62,6 +62,14 @@ export function getLivePhotoMetadata( }; } +export function getLivePhotoMagicMetadata( + imageFile: FileWithCollection +): FileMagicMetadata['data'] { + return { + filePaths: [getLivePhotoName((imageFile.file as any).path as string)], + }; +} + export function getLivePhotoSize(livePhotoAssets: LivePhotoAssets) { return livePhotoAssets.image.size + livePhotoAssets.video.size; } @@ -190,18 +198,12 @@ export function clusterLivePhotoFiles(mediaFiles: FileWithCollection[]) { imageAsset.metadata, videoAsset.metadata ); + const livePhotoMagicMetadata: FileMagicMetadata['data'] = + getLivePhotoMagicMetadata(firstMediaFile); uploadService.setFileMetadataAndFileTypeInfo(livePhotoLocalID, { fileTypeInfo: { ...livePhotoFileTypeInfo }, metadata: { ...livePhotoMetadata }, - magicMetadata: { - ...NEW_FILE_MAGIC_METADATA, - data: { - filePaths: [ - (firstMediaFile.file as any).path as string, - (secondMediaFile.file as any).path as string, - ], - }, - }, + magicMetadata: { ...livePhotoMagicMetadata }, }); index += 2; } else { diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index d546e09be..05dd0adb8 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -6,6 +6,7 @@ import { sortFiles, preservePhotoswipeProps, decryptFile, + changeMagicMetadataFilePaths, } from 'utils/file'; import { logError } from 'utils/sentry'; import { getMetadataJSONMapKey, parseMetadataJSON } from './metadataService'; @@ -40,7 +41,6 @@ import isElectron from 'is-electron'; import ImportService from 'services/importService'; import watchFolderService from 'services/watchFolder/watchFolderService'; import { ProgressUpdater } from 'types/upload/ui'; -import { NEW_FILE_MAGIC_METADATA } from 'types/magicMetadata'; const MAX_CONCURRENT_UPLOADS = 4; const FILE_UPLOAD_COMPLETED = 100; @@ -261,11 +261,8 @@ class UploadManager { fileTypeInfo )) || null; const magicMetadata = { - ...NEW_FILE_MAGIC_METADATA, - data: { - filePaths: [(file as any).path as string], - }, - } as FileMagicMetadata; + filePaths: [(file as any).path as string], + } as FileMagicMetadata['data']; return { fileTypeInfo, metadata, magicMetadata }; })(); @@ -344,6 +341,15 @@ class UploadManager { this.existingFiles, fileWithCollection ); + const filePaths = UploadService.getFileMetadataAndFileTypeInfo( + fileWithCollection.localID + ).magicMetadata.filePaths; + await changeMagicMetadataFilePaths( + fileUploadResult, + uploadedFile, + fileWithCollection.collection.key, + filePaths + ); UIService.moveFileToResultList( fileWithCollection.localID, fileUploadResult diff --git a/src/services/upload/uploader.ts b/src/services/upload/uploader.ts index 91e699325..c62522630 100644 --- a/src/services/upload/uploader.ts +++ b/src/services/upload/uploader.ts @@ -1,10 +1,9 @@ -import { EnteFile, FileMagicMetadata } from 'types/file'; +import { EnteFile } from 'types/file'; import { handleUploadError, CustomError } from 'utils/error'; import { logError } from 'utils/sentry'; import { findSameFileInCollection, findSameFileInOtherCollection, - getMergedMagicMetadataFilePaths, shouldDedupeAcrossCollection, } from 'utils/upload'; import UploadHttpClient from './uploadHttpClient'; @@ -17,10 +16,6 @@ import { logUploadInfo } from 'utils/upload'; import { convertBytesToHumanReadable } from 'utils/billing'; import { sleep } from 'utils/common'; import { addToCollection } from 'services/collectionService'; -import { updateMagicMetadataProps } from 'utils/magicMetadata'; -import { updateFileMagicMetadata } from 'services/fileService'; -import { NEW_FILE_MAGIC_METADATA } from 'types/magicMetadata'; -import { getFileKey } from 'utils/file'; interface UploadResponse { fileUploadResult: UPLOAD_RESULT; @@ -28,25 +23,6 @@ interface UploadResponse { skipDecryption?: boolean; } -const updateMagicMetadata = async ( - file: EnteFile, - magicMetadata: FileMagicMetadata, - collectionKey: string -) => { - magicMetadata.data.filePaths = getMergedMagicMetadataFilePaths( - file.magicMetadata, - magicMetadata - ); - file.key = await getFileKey(file, collectionKey); - const updatedMagicMetadata = await updateMagicMetadataProps( - file.magicMetadata ?? NEW_FILE_MAGIC_METADATA, - file.key, - { filePaths: magicMetadata.data.filePaths } - ); - file.magicMetadata = updatedMagicMetadata; - await updateFileMagicMetadata([file]); -}; - export default async function uploader( worker: any, existingFilesInCollection: EnteFile[], @@ -61,7 +37,7 @@ export default async function uploader( logUploadInfo(`uploader called for ${fileNameSize}`); UIService.setFileProgress(localID, 0); await sleep(0); - const { fileTypeInfo, metadata, magicMetadata } = + const { fileTypeInfo, metadata } = UploadService.getFileMetadataAndFileTypeInfo(localID); try { const fileSize = UploadService.getAssetSize(uploadAsset); @@ -81,11 +57,6 @@ export default async function uploader( ); if (sameFileInSameCollection) { logUploadInfo(`skipped upload for ${fileNameSize}`); - await updateMagicMetadata( - sameFileInSameCollection, - magicMetadata, - fileWithCollection.collection.key - ); return { fileUploadResult: UPLOAD_RESULT.ALREADY_UPLOADED, uploadedFile: sameFileInSameCollection, @@ -104,11 +75,6 @@ export default async function uploader( const resultFile = Object.assign({}, sameFileInOtherCollection); resultFile.collectionID = collection.id; await addToCollection(collection, [resultFile]); - await updateMagicMetadata( - resultFile, - magicMetadata, - fileWithCollection.collection.key - ); return { fileUploadResult: UPLOAD_RESULT.UPLOADED, uploadedFile: resultFile, @@ -126,11 +92,6 @@ export default async function uploader( metadata ); if (sameFileInOtherCollection) { - await updateMagicMetadata( - sameFileInOtherCollection, - magicMetadata, - fileWithCollection.collection.key - ); return { fileUploadResult: UPLOAD_RESULT.ALREADY_UPLOADED, uploadedFile: sameFileInOtherCollection, @@ -176,11 +137,6 @@ export default async function uploader( UIService.increaseFileUploaded(); logUploadInfo(`${fileNameSize} successfully uploaded`); - await updateMagicMetadata( - uploadedFile, - magicMetadata, - fileWithCollection.collection.key - ); return { fileUploadResult: metadata.hasStaticThumbnail ? UPLOAD_RESULT.UPLOADED_WITH_STATIC_THUMBNAIL diff --git a/src/types/upload/index.ts b/src/types/upload/index.ts index 0f7716bef..e473f419f 100644 --- a/src/types/upload/index.ts +++ b/src/types/upload/index.ts @@ -92,7 +92,7 @@ export interface FileWithCollection extends UploadAsset { export interface MetadataAndFileTypeInfo { metadata: Metadata; fileTypeInfo: FileTypeInfo; - magicMetadata: FileMagicMetadata; + magicMetadata: FileMagicMetadata['data']; } export type MetadataAndFileTypeInfoMap = Map; diff --git a/src/utils/file/index.ts b/src/utils/file/index.ts index 5a9e0f5e3..2fde13fe2 100644 --- a/src/utils/file/index.ts +++ b/src/utils/file/index.ts @@ -26,6 +26,8 @@ import ffmpegService from 'services/ffmpeg/ffmpegService'; import { NEW_FILE_MAGIC_METADATA, VISIBILITY_STATE } from 'types/magicMetadata'; import { IsArchived, updateMagicMetadataProps } from 'utils/magicMetadata'; import { ARCHIVE_SECTION, TRASH_SECTION } from 'constants/collection'; +import { UPLOAD_RESULT } from 'constants/upload'; +import { updateFileMagicMetadata } from 'services/fileService'; export function downloadAsFile(filename: string, content: string) { const file = new Blob([content], { type: 'text/plain', @@ -452,6 +454,37 @@ export async function changeFileName(file: EnteFile, editedName: string) { return file; } +export const changeMagicMetadataFilePaths = async ( + fileUploadResult: UPLOAD_RESULT, + file: EnteFile, + collectionKey: string, + filePaths: string[] +) => { + if ( + fileUploadResult === UPLOAD_RESULT.UPLOADED || + fileUploadResult === UPLOAD_RESULT.ALREADY_UPLOADED || + fileUploadResult === UPLOAD_RESULT.UPLOADED_WITH_STATIC_THUMBNAIL + ) { + let mergedMetadataFilePaths = [...filePaths]; + if (file.magicMetadata?.data.filePaths?.length > 0) { + mergedMetadataFilePaths = [ + ...new Set([ + ...file.magicMetadata.data.filePaths, + ...filePaths, + ]), + ]; + } + file.key = await getFileKey(file, collectionKey); + const updatedMagicMetadata = await updateMagicMetadataProps( + file.magicMetadata ?? NEW_FILE_MAGIC_METADATA, + file.key, + { filePaths: mergedMetadataFilePaths } + ); + file.magicMetadata = updatedMagicMetadata; + await updateFileMagicMetadata([file]); + } +}; + export function isSharedFile(file: EnteFile) { const user: User = getData(LS_KEYS.USER); diff --git a/src/utils/upload/index.ts b/src/utils/upload/index.ts index a002c29e6..71a27f47b 100644 --- a/src/utils/upload/index.ts +++ b/src/utils/upload/index.ts @@ -1,5 +1,5 @@ import { ElectronFile, FileWithCollection, Metadata } from 'types/upload'; -import { EnteFile, FileMagicMetadata } from 'types/file'; +import { EnteFile } from 'types/file'; import { convertBytesToHumanReadable } from 'utils/billing'; import { formatDateTime } from 'utils/file'; import { getLogs, saveLogLine } from 'utils/storage'; @@ -141,19 +141,3 @@ export function areFileWithCollectionsSame( ): boolean { return firstFile.localID === secondFile.localID; } - -export function getMergedMagicMetadataFilePaths( - oldMetadata: FileMagicMetadata, - newMetadata: FileMagicMetadata -): string[] { - if (!oldMetadata || !oldMetadata.data.filePaths) { - return newMetadata.data.filePaths; - } - const mergedMetadataFilePaths = [...oldMetadata.data.filePaths]; - newMetadata.data.filePaths.forEach((newMetadataFilePath) => { - if (!mergedMetadataFilePaths.includes(newMetadataFilePath)) { - mergedMetadataFilePaths.push(newMetadataFilePath); - } - }); - return mergedMetadataFilePaths; -} From d3242dbfa5ef330f7111f515f00c6784b555654b Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Thu, 23 Jun 2022 19:22:29 +0530 Subject: [PATCH 070/295] fix type --- src/services/watchFolder/watchFolderService.ts | 2 +- src/types/electron/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/watchFolder/watchFolderService.ts b/src/services/watchFolder/watchFolderService.ts index 201362647..61999ee6a 100644 --- a/src/services/watchFolder/watchFolderService.ts +++ b/src/services/watchFolder/watchFolderService.ts @@ -186,7 +186,7 @@ class watchFolderService { await this.ElectronAPIs.addWatchMapping( rootFolderName, folderPath, - uploadStrategy === UPLOAD_STRATEGY.COLLECTION_PER_FOLDER + uploadStrategy ); } catch (e) { logError(e, 'error while adding watch mapping'); diff --git a/src/types/electron/index.ts b/src/types/electron/index.ts index 653ea77f3..a239e3daa 100644 --- a/src/types/electron/index.ts +++ b/src/types/electron/index.ts @@ -43,7 +43,7 @@ export interface ElectronAPIsInterface { addWatchMapping: ( collectionName: string, folderPath: string, - uploadStrategy: boolean + uploadStrategy: number ) => Promise; removeWatchMapping: (folderPath: string) => Promise; registerWatcherFunctions: ( From 087a35f6a35d071511547f7ba59371b01e532aad Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Thu, 23 Jun 2022 19:26:46 +0530 Subject: [PATCH 071/295] rename --- src/services/upload/uploadManager.ts | 4 ++-- src/utils/file/index.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index 05dd0adb8..051a455e8 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -6,7 +6,7 @@ import { sortFiles, preservePhotoswipeProps, decryptFile, - changeMagicMetadataFilePaths, + changeFilePaths, } from 'utils/file'; import { logError } from 'utils/sentry'; import { getMetadataJSONMapKey, parseMetadataJSON } from './metadataService'; @@ -344,7 +344,7 @@ class UploadManager { const filePaths = UploadService.getFileMetadataAndFileTypeInfo( fileWithCollection.localID ).magicMetadata.filePaths; - await changeMagicMetadataFilePaths( + await changeFilePaths( fileUploadResult, uploadedFile, fileWithCollection.collection.key, diff --git a/src/utils/file/index.ts b/src/utils/file/index.ts index 2fde13fe2..ceed4ba2f 100644 --- a/src/utils/file/index.ts +++ b/src/utils/file/index.ts @@ -454,7 +454,7 @@ export async function changeFileName(file: EnteFile, editedName: string) { return file; } -export const changeMagicMetadataFilePaths = async ( +export const changeFilePaths = async ( fileUploadResult: UPLOAD_RESULT, file: EnteFile, collectionKey: string, From b0b54661c2a6188567b3c333f0034f2ca0141500 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Fri, 24 Jun 2022 11:11:21 +0530 Subject: [PATCH 072/295] move upload strategy to constants --- src/components/WatchFolder/index.tsx | 2 +- src/components/pages/gallery/Upload.tsx | 7 +------ src/constants/upload/index.ts | 5 +++++ src/services/watchFolder/watchFolderService.ts | 2 +- src/types/watchFolder/index.ts | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/WatchFolder/index.tsx b/src/components/WatchFolder/index.tsx index 0c9f90e6b..8bbed4a25 100644 --- a/src/components/WatchFolder/index.tsx +++ b/src/components/WatchFolder/index.tsx @@ -9,7 +9,7 @@ import constants from 'utils/strings/constants'; import DialogBoxBase from 'components/DialogBox/base'; import DialogTitleWithCloseButton from 'components/DialogBox/titleWithCloseButton'; import UploadStrategyChoiceModal from 'components/pages/gallery/UploadStrategyChoiceModal'; -import { UPLOAD_STRATEGY } from 'components/pages/gallery/Upload'; +import { UPLOAD_STRATEGY } from 'constants/upload'; interface Iprops { open: boolean; diff --git a/src/components/pages/gallery/Upload.tsx b/src/components/pages/gallery/Upload.tsx index 86d9451a1..5e57c1806 100644 --- a/src/components/pages/gallery/Upload.tsx +++ b/src/components/pages/gallery/Upload.tsx @@ -33,7 +33,7 @@ import { SegregatedFinishedUploads, InProgressUpload, } from 'types/upload/ui'; -import { UPLOAD_STAGES } from 'constants/upload'; +import { UPLOAD_STAGES, UPLOAD_STRATEGY } from 'constants/upload'; const FIRST_ALBUM_NAME = 'My First Album'; @@ -59,11 +59,6 @@ interface Props { showSessionExpiredMessage: () => void; } -export enum UPLOAD_STRATEGY { - SINGLE_COLLECTION, - COLLECTION_PER_FOLDER, -} - export enum DESKTOP_UPLOAD_TYPE { FILES = 'files', FOLDERS = 'folders', diff --git a/src/constants/upload/index.ts b/src/constants/upload/index.ts index 2c9d7a4e6..709122682 100644 --- a/src/constants/upload/index.ts +++ b/src/constants/upload/index.ts @@ -31,6 +31,11 @@ export enum UPLOAD_STAGES { FINISH, } +export enum UPLOAD_STRATEGY { + SINGLE_COLLECTION, + COLLECTION_PER_FOLDER, +} + export enum UPLOAD_RESULT { FAILED, ALREADY_UPLOADED, diff --git a/src/services/watchFolder/watchFolderService.ts b/src/services/watchFolder/watchFolderService.ts index 61999ee6a..717b6daf2 100644 --- a/src/services/watchFolder/watchFolderService.ts +++ b/src/services/watchFolder/watchFolderService.ts @@ -16,8 +16,8 @@ import { diskFileRemovedCallback, diskFolderRemovedCallback, } from './watchFolderEventHandlers'; -import { UPLOAD_STRATEGY } from 'components/pages/gallery/Upload'; import { getParentFolderName } from './utils'; +import { UPLOAD_STRATEGY } from 'constants/upload'; class watchFolderService { private ElectronAPIs: ElectronAPIsInterface; diff --git a/src/types/watchFolder/index.ts b/src/types/watchFolder/index.ts index 1c02e4b44..97fa96dce 100644 --- a/src/types/watchFolder/index.ts +++ b/src/types/watchFolder/index.ts @@ -1,4 +1,4 @@ -import { UPLOAD_STRATEGY } from 'components/pages/gallery/Upload'; +import { UPLOAD_STRATEGY } from 'constants/upload'; import { ElectronFile } from 'types/upload'; export interface WatchMapping { From ff04c9c89c72cf91a63e6c25e4c5926b02a02dce Mon Sep 17 00:00:00 2001 From: Abhinav Date: Fri, 24 Jun 2022 11:30:15 +0530 Subject: [PATCH 073/295] simply filePaths handling --- src/services/upload/livePhotoService.ts | 14 ++++---------- src/services/upload/uploadManager.ts | 23 +++++++++++------------ src/types/upload/index.ts | 4 ++-- src/utils/file/index.ts | 8 ++++---- 4 files changed, 21 insertions(+), 28 deletions(-) diff --git a/src/services/upload/livePhotoService.ts b/src/services/upload/livePhotoService.ts index 740c4da2a..1b2000cc1 100644 --- a/src/services/upload/livePhotoService.ts +++ b/src/services/upload/livePhotoService.ts @@ -1,7 +1,6 @@ import { FILE_TYPE } from 'constants/file'; import { LIVE_PHOTO_ASSET_SIZE_LIMIT } from 'constants/upload'; import { encodeMotionPhoto } from 'services/motionPhotoService'; -import { FileMagicMetadata } from 'types/file'; import { ElectronFile, FileTypeInfo, @@ -62,12 +61,8 @@ export function getLivePhotoMetadata( }; } -export function getLivePhotoMagicMetadata( - imageFile: FileWithCollection -): FileMagicMetadata['data'] { - return { - filePaths: [getLivePhotoName((imageFile.file as any).path as string)], - }; +export function getLivePhotoFilePath(imageAsset: Asset): string { + return getLivePhotoName((imageAsset.file as any).path); } export function getLivePhotoSize(livePhotoAssets: LivePhotoAssets) { @@ -198,12 +193,11 @@ export function clusterLivePhotoFiles(mediaFiles: FileWithCollection[]) { imageAsset.metadata, videoAsset.metadata ); - const livePhotoMagicMetadata: FileMagicMetadata['data'] = - getLivePhotoMagicMetadata(firstMediaFile); + const livePhotoPath = getLivePhotoFilePath(imageAsset); uploadService.setFileMetadataAndFileTypeInfo(livePhotoLocalID, { fileTypeInfo: { ...livePhotoFileTypeInfo }, metadata: { ...livePhotoMetadata }, - magicMetadata: { ...livePhotoMagicMetadata }, + filePath: livePhotoPath, }); index += 2; } else { diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index 051a455e8..cd548d885 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -6,7 +6,7 @@ import { sortFiles, preservePhotoswipeProps, decryptFile, - changeFilePaths, + appendFilePath, } from 'utils/file'; import { logError } from 'utils/sentry'; import { getMetadataJSONMapKey, parseMetadataJSON } from './metadataService'; @@ -20,7 +20,7 @@ import UIService from './uiService'; import UploadService from './uploadService'; import { CustomError } from 'utils/error'; import { Collection } from 'types/collection'; -import { EnteFile, FileMagicMetadata } from 'types/file'; +import { EnteFile } from 'types/file'; import { FileWithCollection, MetadataAndFileTypeInfo, @@ -230,7 +230,7 @@ class UploadManager { UIService.reset(mediaFiles.length); for (const { file, localID, collectionID } of mediaFiles) { try { - const { fileTypeInfo, metadata, magicMetadata } = + const { fileTypeInfo, metadata, filePath } = await (async () => { if (file.size >= MAX_FILE_SIZE_SUPPORTED) { logUploadInfo( @@ -260,10 +260,8 @@ class UploadManager { collectionID, fileTypeInfo )) || null; - const magicMetadata = { - filePaths: [(file as any).path as string], - } as FileMagicMetadata['data']; - return { fileTypeInfo, metadata, magicMetadata }; + const filePath = (file as any).path as string; + return { fileTypeInfo, metadata, filePath }; })(); logUploadInfo( @@ -274,7 +272,7 @@ class UploadManager { this.metadataAndFileTypeInfoMap.set(localID, { fileTypeInfo: fileTypeInfo && { ...fileTypeInfo }, metadata: metadata && { ...metadata }, - magicMetadata: magicMetadata && { ...magicMetadata }, + filePath: filePath, }); UIService.increaseFileUploaded(); } catch (e) { @@ -341,14 +339,15 @@ class UploadManager { this.existingFiles, fileWithCollection ); - const filePaths = UploadService.getFileMetadataAndFileTypeInfo( + const filePath = UploadService.getFileMetadataAndFileTypeInfo( fileWithCollection.localID - ).magicMetadata.filePaths; - await changeFilePaths( + ).filePath; + + await appendFilePath( fileUploadResult, uploadedFile, fileWithCollection.collection.key, - filePaths + filePath ); UIService.moveFileToResultList( fileWithCollection.localID, diff --git a/src/types/upload/index.ts b/src/types/upload/index.ts index e473f419f..f42d5ead6 100644 --- a/src/types/upload/index.ts +++ b/src/types/upload/index.ts @@ -1,6 +1,6 @@ import { FILE_TYPE } from 'constants/file'; import { Collection } from 'types/collection'; -import { fileAttribute, FileMagicMetadata } from 'types/file'; +import { fileAttribute } from 'types/file'; export interface DataStream { stream: ReadableStream; @@ -92,7 +92,7 @@ export interface FileWithCollection extends UploadAsset { export interface MetadataAndFileTypeInfo { metadata: Metadata; fileTypeInfo: FileTypeInfo; - magicMetadata: FileMagicMetadata['data']; + filePath: string; } export type MetadataAndFileTypeInfoMap = Map; diff --git a/src/utils/file/index.ts b/src/utils/file/index.ts index ceed4ba2f..c768f6831 100644 --- a/src/utils/file/index.ts +++ b/src/utils/file/index.ts @@ -454,23 +454,23 @@ export async function changeFileName(file: EnteFile, editedName: string) { return file; } -export const changeFilePaths = async ( +export const appendFilePath = async ( fileUploadResult: UPLOAD_RESULT, file: EnteFile, collectionKey: string, - filePaths: string[] + filePath: string ) => { if ( fileUploadResult === UPLOAD_RESULT.UPLOADED || fileUploadResult === UPLOAD_RESULT.ALREADY_UPLOADED || fileUploadResult === UPLOAD_RESULT.UPLOADED_WITH_STATIC_THUMBNAIL ) { - let mergedMetadataFilePaths = [...filePaths]; + let mergedMetadataFilePaths = [filePath]; if (file.magicMetadata?.data.filePaths?.length > 0) { mergedMetadataFilePaths = [ ...new Set([ ...file.magicMetadata.data.filePaths, - ...filePaths, + ...mergedMetadataFilePaths, ]), ]; } From e022f5ef891f14825e0bfbe44c0d5384789351d5 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Fri, 24 Jun 2022 11:43:34 +0530 Subject: [PATCH 074/295] move file path upadting logic to post Upload action --- src/services/upload/uploadManager.ts | 39 +++++++++++++----------- src/utils/file/index.ts | 45 ++++++++++------------------ 2 files changed, 38 insertions(+), 46 deletions(-) diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index 0781bc3d9..2dd948c1b 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -6,7 +6,7 @@ import { sortFiles, preservePhotoswipeProps, decryptFile, - appendFilePath, + appendNewFilePath, } from 'utils/file'; import { logError } from 'utils/sentry'; import { getMetadataJSONMapKey, parseMetadataJSON } from './metadataService'; @@ -338,26 +338,18 @@ class UploadManager { this.existingFiles, fileWithCollection ); - const filePath = UploadService.getFileMetadataAndFileTypeInfo( - fileWithCollection.localID - ).filePath; - await appendFilePath( - fileUploadResult, - uploadedFile, - fileWithCollection.collection.key, - filePath - ); - UIService.moveFileToResultList( - fileWithCollection.localID, - fileUploadResult - ); - UploadService.reducePendingUploadCount(); - await this.postUploadTask( + const finalUploadResult = await this.postUploadTask( fileUploadResult, uploadedFile, fileWithCollection ); + + UIService.moveFileToResultList( + fileWithCollection.localID, + finalUploadResult + ); + UploadService.reducePendingUploadCount(); } } @@ -403,14 +395,16 @@ class UploadManager { fileWithCollection, uploadedFile ); + await this.updateFilePaths(decryptedFile, fileWithCollection); } + return fileUploadResult; } catch (e) { logError(e, 'failed to do post file upload action'); logUploadInfo( `failed to do post file upload action -> ${e.message} ${(e as Error).stack}` ); - throw e; + return UPLOAD_RESULT.FAILED; } } @@ -456,6 +450,17 @@ class UploadManager { } } + private async updateFilePaths( + decryptedFile: EnteFile, + fileWithCollection: FileWithCollection + ) { + const filePath = UploadService.getFileMetadataAndFileTypeInfo( + fileWithCollection.localID + ).filePath; + + await appendNewFilePath(decryptedFile, filePath); + } + async retryFailedFiles() { await this.queueFilesForUpload(this.failedFiles, [ ...this.collections.values(), diff --git a/src/utils/file/index.ts b/src/utils/file/index.ts index c768f6831..49f1c9589 100644 --- a/src/utils/file/index.ts +++ b/src/utils/file/index.ts @@ -26,7 +26,6 @@ import ffmpegService from 'services/ffmpeg/ffmpegService'; import { NEW_FILE_MAGIC_METADATA, VISIBILITY_STATE } from 'types/magicMetadata'; import { IsArchived, updateMagicMetadataProps } from 'utils/magicMetadata'; import { ARCHIVE_SECTION, TRASH_SECTION } from 'constants/collection'; -import { UPLOAD_RESULT } from 'constants/upload'; import { updateFileMagicMetadata } from 'services/fileService'; export function downloadAsFile(filename: string, content: string) { const file = new Blob([content], { @@ -454,35 +453,23 @@ export async function changeFileName(file: EnteFile, editedName: string) { return file; } -export const appendFilePath = async ( - fileUploadResult: UPLOAD_RESULT, - file: EnteFile, - collectionKey: string, - filePath: string -) => { - if ( - fileUploadResult === UPLOAD_RESULT.UPLOADED || - fileUploadResult === UPLOAD_RESULT.ALREADY_UPLOADED || - fileUploadResult === UPLOAD_RESULT.UPLOADED_WITH_STATIC_THUMBNAIL - ) { - let mergedMetadataFilePaths = [filePath]; - if (file.magicMetadata?.data.filePaths?.length > 0) { - mergedMetadataFilePaths = [ - ...new Set([ - ...file.magicMetadata.data.filePaths, - ...mergedMetadataFilePaths, - ]), - ]; - } - file.key = await getFileKey(file, collectionKey); - const updatedMagicMetadata = await updateMagicMetadataProps( - file.magicMetadata ?? NEW_FILE_MAGIC_METADATA, - file.key, - { filePaths: mergedMetadataFilePaths } - ); - file.magicMetadata = updatedMagicMetadata; - await updateFileMagicMetadata([file]); +export const appendNewFilePath = async (file: EnteFile, filePath: string) => { + let mergedMetadataFilePaths = [filePath]; + if (file.magicMetadata?.data.filePaths?.length > 0) { + mergedMetadataFilePaths = [ + ...new Set([ + ...file.magicMetadata.data.filePaths, + ...mergedMetadataFilePaths, + ]), + ]; } + const updatedMagicMetadata = await updateMagicMetadataProps( + file.magicMetadata ?? NEW_FILE_MAGIC_METADATA, + file.key, + { filePaths: mergedMetadataFilePaths } + ); + file.magicMetadata = updatedMagicMetadata; + await updateFileMagicMetadata([file]); }; export function isSharedFile(file: EnteFile) { From 7b584774cdef78337a889caae126c7c31239db8c Mon Sep 17 00:00:00 2001 From: Abhinav Date: Fri, 24 Jun 2022 11:45:32 +0530 Subject: [PATCH 075/295] move api call to update path to uploadManager --- src/services/upload/uploadManager.ts | 9 +++++++-- src/utils/file/index.ts | 5 +++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index 2dd948c1b..709237c2f 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -1,4 +1,8 @@ -import { getLocalFiles, setLocalFiles } from '../fileService'; +import { + getLocalFiles, + setLocalFiles, + updateFileMagicMetadata, +} from '../fileService'; import { SetFiles } from 'types/gallery'; import { getDedicatedCryptoWorker } from 'utils/crypto'; import { @@ -458,7 +462,8 @@ class UploadManager { fileWithCollection.localID ).filePath; - await appendNewFilePath(decryptedFile, filePath); + const updatedFile = await appendNewFilePath(decryptedFile, filePath); + await updateFileMagicMetadata([updatedFile]); } async retryFailedFiles() { diff --git a/src/utils/file/index.ts b/src/utils/file/index.ts index 49f1c9589..c009d1ab9 100644 --- a/src/utils/file/index.ts +++ b/src/utils/file/index.ts @@ -26,7 +26,7 @@ import ffmpegService from 'services/ffmpeg/ffmpegService'; import { NEW_FILE_MAGIC_METADATA, VISIBILITY_STATE } from 'types/magicMetadata'; import { IsArchived, updateMagicMetadataProps } from 'utils/magicMetadata'; import { ARCHIVE_SECTION, TRASH_SECTION } from 'constants/collection'; -import { updateFileMagicMetadata } from 'services/fileService'; + export function downloadAsFile(filename: string, content: string) { const file = new Blob([content], { type: 'text/plain', @@ -469,7 +469,8 @@ export const appendNewFilePath = async (file: EnteFile, filePath: string) => { { filePaths: mergedMetadataFilePaths } ); file.magicMetadata = updatedMagicMetadata; - await updateFileMagicMetadata([file]); + + return file; }; export function isSharedFile(file: EnteFile) { From 0a1076c584d0f04c317bcf2df8c7035fee455911 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Fri, 24 Jun 2022 12:27:01 +0530 Subject: [PATCH 076/295] fix update file paths when already uploaded --- src/services/upload/uploadManager.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index 709237c2f..276410dc5 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -378,6 +378,10 @@ class UploadManager { uploadedFile ); } + await this.updateFilePaths( + uploadedFile, + fileWithCollection + ); break; case UPLOAD_RESULT.ADDED_SYMLINK: decryptedFile = uploadedFile; From 93a80ec38894b76d7cf608a38e8d77351f2e4946 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Sat, 23 Jul 2022 22:38:39 +0530 Subject: [PATCH 077/295] fix build --- src/components/Sidebar/UtilitySection.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Sidebar/UtilitySection.tsx b/src/components/Sidebar/UtilitySection.tsx index b544d3117..599fada0c 100644 --- a/src/components/Sidebar/UtilitySection.tsx +++ b/src/components/Sidebar/UtilitySection.tsx @@ -36,7 +36,7 @@ export default function UtilitySection({ closeSidebar }) { } else { setDialogMessage({ title: constants.DOWNLOAD_APP, - content: constants.DOWNLOAD_APP_MESSAGE(), + content: constants.DOWNLOAD_APP_MESSAGE, proceed: { text: constants.DOWNLOAD, From 82f2652519c29d5803bdd576f1485a00db04373f Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Sun, 24 Jul 2022 00:06:42 +0530 Subject: [PATCH 078/295] fix build --- src/components/WatchFolder/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/WatchFolder/index.tsx b/src/components/WatchFolder/index.tsx index 8bbed4a25..2f6825975 100644 --- a/src/components/WatchFolder/index.tsx +++ b/src/components/WatchFolder/index.tsx @@ -7,8 +7,8 @@ import { WatchMapping } from 'types/watchFolder'; import { AppContext } from 'pages/_app'; import constants from 'utils/strings/constants'; import DialogBoxBase from 'components/DialogBox/base'; -import DialogTitleWithCloseButton from 'components/DialogBox/titleWithCloseButton'; -import UploadStrategyChoiceModal from 'components/pages/gallery/UploadStrategyChoiceModal'; +import DialogTitleWithCloseButton from 'components/DialogBox/TitleWithCloseButton'; +import UploadStrategyChoiceModal from 'components/Upload/UploadStrategyChoiceModal'; import { UPLOAD_STRATEGY } from 'constants/upload'; interface Iprops { From 8f9e434a5a06a341d5fd436eeccc60084d98182f Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Tue, 26 Jul 2022 19:51:16 +0530 Subject: [PATCH 079/295] handle user upload when watch service is running --- src/components/Upload/Uploader.tsx | 88 +++++++++++++------ src/services/upload/uploadManager.ts | 21 +++++ .../watchFolder/watchFolderService.ts | 76 +++++++++++----- src/types/watchFolder/index.ts | 5 ++ src/utils/common/index.ts | 14 +++ 5 files changed, 153 insertions(+), 51 deletions(-) diff --git a/src/components/Upload/Uploader.tsx b/src/components/Upload/Uploader.tsx index 5cc76f8c9..8bfcdb6ed 100644 --- a/src/components/Upload/Uploader.tsx +++ b/src/components/Upload/Uploader.tsx @@ -35,6 +35,7 @@ import { UPLOAD_STAGES } from 'constants/upload'; import importService from 'services/importService'; import { getDownloadAppMessage } from 'utils/ui'; import UploadTypeSelector from './UploadTypeSelector'; +import { newLock } from 'utils/common'; const FIRST_ALBUM_NAME = 'My First Album'; @@ -114,6 +115,8 @@ export default function Uploader(props: Props) { const pendingDesktopUploadCollectionName = useRef(''); const uploadType = useRef(null); const zipPaths = useRef(null); + const waitForPrevUpload = useRef>(null); + const uploadDone = useRef<() => void>(null); useEffect(() => { UploadManager.initUploader( @@ -156,8 +159,14 @@ export default function Uploader(props: Props) { appContext.sharedFiles?.length > 0 ) { if (props.uploadInProgress) { - // no-op - // a upload is already in progress + if (watchFolderService.isUploadRunning()) { + // pause watch folder service on user upload + uploadManager.pauseWatchService(); + } else { + // no-op + // a user upload is already in progress + return; + } } else if (isCanvasBlocked()) { appContext.setDialogMessage({ title: constants.CANVAS_BLOCKED_TITLE, @@ -170,29 +179,29 @@ export default function Uploader(props: Props) { variant: 'accent', }, }); - } else { - props.setLoading(true); - if (props.webFiles?.length > 0) { - // File selection by drag and drop or selection of file. - toUploadFiles.current = props.webFiles; - props.setWebFiles([]); - } else if (appContext.sharedFiles?.length > 0) { - toUploadFiles.current = appContext.sharedFiles; - appContext.resetSharedFiles(); - } else if (props.electronFiles?.length > 0) { - // File selection from desktop app - toUploadFiles.current = props.electronFiles; - props.setElectronFiles([]); - } - const analysisResult = analyseUploadFiles(); - setAnalysisResult(analysisResult); - - handleCollectionCreationAndUpload( - analysisResult, - props.isFirstUpload - ); - props.setLoading(false); + return; } + props.setLoading(true); + if (props.webFiles?.length > 0) { + // File selection by drag and drop or selection of file. + toUploadFiles.current = props.webFiles; + props.setWebFiles([]); + } else if (appContext.sharedFiles?.length > 0) { + toUploadFiles.current = appContext.sharedFiles; + appContext.resetSharedFiles(); + } else if (props.electronFiles?.length > 0) { + // File selection from desktop app + toUploadFiles.current = props.electronFiles; + props.setElectronFiles([]); + } + const analysisResult = analyseUploadFiles(); + setAnalysisResult(analysisResult); + + handleCollectionCreationAndUpload( + analysisResult, + props.isFirstUpload + ); + props.setLoading(false); } }, [props.webFiles, appContext.sharedFiles, props.electronFiles]); @@ -351,6 +360,10 @@ export default function Uploader(props: Props) { collections: Collection[] ) => { try { + if (waitForPrevUpload.current !== null) { + await waitForPrevUpload.current; // wait for previous upload to finish + } + setUploadLock(); uploadInit(); props.setUploadInProgress(true); props.closeCollectionSelector(); @@ -380,17 +393,36 @@ export default function Uploader(props: Props) { setUploadProgressView(false); throw err; } finally { + unsetUploadLock(); props.setUploadInProgress(false); props.syncWithRemote(); if (isElectron()) { - await watchFolderService.allFileUploadsDone( - filesWithCollectionToUpload, - collections - ); + if (watchFolderService.isUploadRunning()) { + await watchFolderService.allFileUploadsDone( + filesWithCollectionToUpload, + collections + ); + } else { + if (watchFolderService.isServicePaused()) { + // resume the service after user upload is done + watchFolderService.resumeService(); + } + } } } }; + const setUploadLock = () => { + const { wait, unlock } = newLock(); + waitForPrevUpload.current = wait; + uploadDone.current = unlock; + }; + + const unsetUploadLock = () => { + uploadDone.current(); + waitForPrevUpload.current = null; + }; + const retryFailed = async () => { try { props.setUploadInProgress(true); diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index 2c49a313a..fb2dee28e 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -61,6 +61,7 @@ class UploadManager { private existingFiles: EnteFile[]; private setFiles: SetFiles; private collections: Map; + private isUploadPausing: boolean = false; public initUploader(progressUpdater: ProgressUpdater, setFiles: SetFiles) { UIService.init(progressUpdater); this.setFiles = setFiles; @@ -179,6 +180,9 @@ class UploadManager { ); throw e; } finally { + if (this.isUploadPausing) { + this.isUploadPausing = false; + } for (let i = 0; i < MAX_CONCURRENT_UPLOADS; i++) { this.cryptoWorkers[i]?.worker.terminate(); } @@ -193,6 +197,10 @@ class UploadManager { for (const { file, collectionID } of metadataFiles) { try { + if (this.isUploadPausing) { + return; + } + logUploadInfo( `parsing metadata json file ${getFileNameSize(file)}` ); @@ -234,6 +242,9 @@ class UploadManager { logUploadInfo(`extractMetadataFromFiles executed`); UIService.reset(mediaFiles.length); for (const { file, localID, collectionID } of mediaFiles) { + if (this.isUploadPausing) { + return; + } try { const { fileTypeInfo, metadata, filePath } = await (async () => { @@ -361,6 +372,9 @@ class UploadManager { private async uploadNextFileInQueue(worker: any) { while (this.filesToBeUploaded.length > 0) { + if (this.isUploadPausing) { + return; + } let fileWithCollection = this.filesToBeUploaded.pop(); const { collectionID } = fileWithCollection; const existingFilesInCollection = @@ -459,6 +473,13 @@ class UploadManager { } } + public pauseWatchService() { + if (isElectron()) { + watchFolderService.pauseService(); + this.isUploadPausing = true; + } + } + private updateExistingCollections(decryptedFile: EnteFile) { if (!this.existingFilesCollectionWise.has(decryptedFile.collectionID)) { this.existingFilesCollectionWise.set( diff --git a/src/services/watchFolder/watchFolderService.ts b/src/services/watchFolder/watchFolderService.ts index 717b6daf2..030f322ea 100644 --- a/src/services/watchFolder/watchFolderService.ts +++ b/src/services/watchFolder/watchFolderService.ts @@ -28,6 +28,7 @@ class watchFolderService { private isEventRunning: boolean = false; private uploadRunning: boolean = false; private pathToIDMap = new Map(); + private isPaused = false; private setElectronFiles: (files: ElectronFile[]) => void; private setCollectionName: (collectionName: string) => void; private syncWithRemote: () => void; @@ -43,6 +44,10 @@ class watchFolderService { return this.uploadRunning; } + isServicePaused() { + return this.isPaused; + } + async init( setElectronFiles: (files: ElectronFile[]) => void, setCollectionName: (collectionName: string) => void, @@ -57,34 +62,46 @@ class watchFolderService { this.setWatchFolderServiceIsRunning = setWatchFolderServiceIsRunning; - let mappings = this.getWatchMappings(); - - console.log('mappings', mappings); - - if (!mappings) { - return; - } - - mappings = await this.filterOutDeletedMappings(mappings); - - for (const mapping of mappings) { - const filesOnDisk: ElectronFile[] = - await this.ElectronAPIs.getAllFilesFromDir( - mapping.folderPath - ); - - this.uploadDiffOfFiles(mapping, filesOnDisk); - this.trashDiffOfFiles(mapping, filesOnDisk); - } - - this.setWatchFunctions(); - await this.runNextEvent(); + await this.getAndSyncDiffOfFiles(true); } catch (e) { logError(e, 'error while initializing watch service'); } } } + async getAndSyncDiffOfFiles(init = false) { + try { + let mappings = this.getWatchMappings(); + + console.log('mappings', mappings); + + if (!mappings) { + return; + } + + mappings = await this.filterOutDeletedMappings(mappings); + + this.eventQueue = []; + + for (const mapping of mappings) { + const filesOnDisk: ElectronFile[] = + await this.ElectronAPIs.getAllFilesFromDir( + mapping.folderPath + ); + + this.uploadDiffOfFiles(mapping, filesOnDisk); + this.trashDiffOfFiles(mapping, filesOnDisk); + } + + if (init) { + this.setWatchFunctions(); + } + await this.runNextEvent(); + } catch (e) { + logError(e, 'error while getting and syncing diff of files'); + } + } + isMappingSyncing(mapping: WatchMapping) { return this.currentEvent?.folderPath === mapping.folderPath; } @@ -224,7 +241,11 @@ class watchFolderService { private async runNextEvent() { console.log('mappings', this.getWatchMappings()); - if (this.eventQueue.length === 0 || this.isEventRunning) { + if ( + this.eventQueue.length === 0 || + this.isEventRunning || + this.isPaused + ) { return; } @@ -513,6 +534,15 @@ class watchFolderService { logError(e, 'error while checking if folder exists'); } } + + pauseService() { + this.isPaused = true; + } + + resumeService() { + this.isPaused = false; + this.getAndSyncDiffOfFiles(); + } } export default new watchFolderService(); diff --git a/src/types/watchFolder/index.ts b/src/types/watchFolder/index.ts index 97fa96dce..28c8f677e 100644 --- a/src/types/watchFolder/index.ts +++ b/src/types/watchFolder/index.ts @@ -18,3 +18,8 @@ export interface EventQueueItem { paths?: string[]; files?: ElectronFile[]; } + +export interface Lock { + wait: Promise; + unlock(): void; +} diff --git a/src/utils/common/index.ts b/src/utils/common/index.ts index 7dcd502da..253832bc0 100644 --- a/src/utils/common/index.ts +++ b/src/utils/common/index.ts @@ -1,6 +1,7 @@ import constants from 'utils/strings/constants'; import { CustomError } from 'utils/error'; import GetDeviceOS, { OS } from './deviceDetection'; +import { Lock } from 'types/watchFolder'; const DESKTOP_APP_GITHUB_DOWNLOAD_URL = 'https://github.com/ente-io/bhari-frame/releases/latest'; @@ -92,3 +93,16 @@ export function openLink(href: string, newTab?: boolean) { a.rel = 'noreferrer noopener'; a.click(); } + +export function newLock(): Lock { + let resolver: () => void = null; + const wait = new Promise((resolve) => { + resolver = resolve; + }); + return { + wait, + unlock: () => { + resolver(); + }, + }; +} From 4110ce8a3dd68dbc7652f75014ee3f8e6bca6717 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Wed, 27 Jul 2022 12:43:22 +0530 Subject: [PATCH 080/295] fix merge --- src/services/upload/uploadManager.ts | 62 ++++++++++------------------ 1 file changed, 21 insertions(+), 41 deletions(-) diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index fb2dee28e..b7256e2ae 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -245,52 +245,25 @@ class UploadManager { if (this.isUploadPausing) { return; } + let fileTypeInfo = null; + let metadata = null; + let filePath = null; try { - const { fileTypeInfo, metadata, filePath } = - await (async () => { - if (file.size >= MAX_FILE_SIZE_SUPPORTED) { - logUploadInfo( - `${getFileNameSize( - file - )} rejected because of large size` - ); - - return { fileTypeInfo: null, metadata: null }; - } - const fileTypeInfo = - await UploadService.getFileType(file); - if (fileTypeInfo.fileType === FILE_TYPE.OTHERS) { - logUploadInfo( - `${getFileNameSize( - file - )} rejected because of unknown file format` - ); - return { fileTypeInfo, metadata: null }; - } - logUploadInfo( - ` extracting ${getFileNameSize(file)} metadata` - ); - const metadata = - (await UploadService.extractFileMetadata( - file, - collectionID, - fileTypeInfo - )) || null; - const filePath = (file as any).path as string; - return { fileTypeInfo, metadata, filePath }; - })(); - + logUploadInfo( + `metadata extraction started ${getFileNameSize(file)} ` + ); + const result = await this.extractFileTypeAndMetadata( + file, + collectionID + ); + fileTypeInfo = result.fileTypeInfo; + metadata = result.metadata; + filePath = result.filePath; logUploadInfo( `metadata extraction successful${getFileNameSize( file )} ` ); - this.metadataAndFileTypeInfoMap.set(localID, { - fileTypeInfo: fileTypeInfo && { ...fileTypeInfo }, - metadata: metadata && { ...metadata }, - filePath: filePath, - }); - UIService.increaseFileUploaded(); } catch (e) { logError(e, 'metadata extraction failed for a file'); logUploadInfo( @@ -299,6 +272,12 @@ class UploadManager { )} error: ${e.message}` ); } + this.metadataAndFileTypeInfoMap.set(localID, { + fileTypeInfo: fileTypeInfo && { ...fileTypeInfo }, + metadata: metadata && { ...metadata }, + filePath: filePath, + }); + UIService.increaseFileUploaded(); } } catch (e) { logError(e, 'error extracting metadata'); @@ -333,7 +312,8 @@ class UploadManager { collectionID, fileTypeInfo )) || null; - return { fileTypeInfo, metadata }; + const filePath = (file as any).path as string; + return { fileTypeInfo, metadata, filePath }; } private async uploadMediaFiles(mediaFiles: FileWithCollection[]) { From f852ddc84ed887e52066477ae641a28e4d5bfcb6 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Wed, 27 Jul 2022 16:36:27 +0530 Subject: [PATCH 081/295] added pausing uploads --- src/constants/upload/index.ts | 1 + src/services/upload/uploadManager.ts | 20 +++++++++++++++++--- src/utils/strings/englishConstants.tsx | 2 +- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/constants/upload/index.ts b/src/constants/upload/index.ts index 625dc7de2..b5e8a267a 100644 --- a/src/constants/upload/index.ts +++ b/src/constants/upload/index.ts @@ -29,6 +29,7 @@ export enum UPLOAD_STAGES { EXTRACTING_METADATA, UPLOADING, FINISH, + PAUSING, } export enum UPLOAD_STRATEGY { diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index b7256e2ae..3aaeb5ea8 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -109,6 +109,10 @@ class UploadManager { UPLOAD_STAGES.READING_GOOGLE_METADATA_FILES ); await this.parseMetadataJSONFiles(metadataJSONFiles); + if (this.isUploadPausing) { + this.uploadPausingDone(); + return; + } UploadService.setParsedMetadataJSONMap( this.parsedMetadataJSONMap ); @@ -116,6 +120,10 @@ class UploadManager { if (mediaFiles.length) { UIService.setUploadStage(UPLOAD_STAGES.EXTRACTING_METADATA); await this.extractMetadataFromFiles(mediaFiles); + if (this.isUploadPausing) { + this.uploadPausingDone(); + return; + } UploadService.setMetadataAndFileTypeInfoMap( this.metadataAndFileTypeInfoMap ); @@ -169,6 +177,10 @@ class UploadManager { ); await this.uploadMediaFiles(allFiles); + if (this.isUploadPausing) { + this.uploadPausingDone(); + return; + } } UIService.setUploadStage(UPLOAD_STAGES.FINISH); UIService.setPercentComplete(FILE_UPLOAD_COMPLETED); @@ -180,9 +192,6 @@ class UploadManager { ); throw e; } finally { - if (this.isUploadPausing) { - this.isUploadPausing = false; - } for (let i = 0; i < MAX_CONCURRENT_UPLOADS; i++) { this.cryptoWorkers[i]?.worker.terminate(); } @@ -457,9 +466,14 @@ class UploadManager { if (isElectron()) { watchFolderService.pauseService(); this.isUploadPausing = true; + UIService.setUploadStage(UPLOAD_STAGES.PAUSING); } } + private async uploadPausingDone() { + this.isUploadPausing = false; + } + private updateExistingCollections(decryptedFile: EnteFile) { if (!this.existingFilesCollectionWise.has(decryptedFile.collectionID)) { this.existingFilesCollectionWise.set( diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index 9ce6b5ac3..37e58b985 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -111,7 +111,7 @@ const englishConstants = { 3: (fileCounter) => `${fileCounter.finished} / ${fileCounter.total} files backed up`, 4: 'Backup complete', - 5: 'Cancelling remaining uploads', + 5: 'Pausing remaining uploads', }, UPLOADING_FILES: 'File upload', FILE_NOT_UPLOADED_LIST: 'The following files were not uploaded', From 5d519e3a969e7b37efeb526b8a8542b7497333e0 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Wed, 10 Aug 2022 14:21:25 +0530 Subject: [PATCH 082/295] fix modal width --- src/components/DialogBox/base.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/DialogBox/base.tsx b/src/components/DialogBox/base.tsx index 6db198c75..bc3362daa 100644 --- a/src/components/DialogBox/base.tsx +++ b/src/components/DialogBox/base.tsx @@ -1,9 +1,9 @@ import { Dialog, styled } from '@mui/material'; -const DialogBoxBase = styled(Dialog)(({ theme }) => ({ +const DialogBoxBase = styled(Dialog)(({ theme, maxWidth }) => ({ '& .MuiDialog-paper': { padding: theme.spacing(1, 1.5), - maxWidth: '346px', + maxWidth: maxWidth ?? '346px', }, '& .MuiDialogTitle-root': { padding: theme.spacing(2), From a01789100455b628d64c289659fd5f32c365ab03 Mon Sep 17 00:00:00 2001 From: Rushikesh Tote Date: Wed, 10 Aug 2022 14:24:26 +0530 Subject: [PATCH 083/295] added more conditions to handle paused uploads --- src/constants/upload/index.ts | 1 + src/services/upload/fileService.ts | 8 +++++++ src/services/upload/livePhotoService.ts | 3 +++ src/services/upload/multiPartUploadService.ts | 4 ++++ src/services/upload/uploadManager.ts | 23 +++++++++++-------- src/services/upload/uploadService.ts | 17 +++++++++++++- src/services/upload/uploader.ts | 2 ++ src/types/upload/index.ts | 4 ++++ src/utils/error/index.ts | 1 + 9 files changed, 53 insertions(+), 10 deletions(-) diff --git a/src/constants/upload/index.ts b/src/constants/upload/index.ts index b5e8a267a..37c54215f 100644 --- a/src/constants/upload/index.ts +++ b/src/constants/upload/index.ts @@ -47,6 +47,7 @@ export enum UPLOAD_RESULT { UPLOADED, UPLOADED_WITH_STATIC_THUMBNAIL, ADDED_SYMLINK, + PAUSED, } export const MAX_FILE_SIZE_SUPPORTED = 4 * 1024 * 1024 * 1024; // 4 GB diff --git a/src/services/upload/fileService.ts b/src/services/upload/fileService.ts index 939e769e6..38e0011a6 100644 --- a/src/services/upload/fileService.ts +++ b/src/services/upload/fileService.ts @@ -22,6 +22,8 @@ import { getUint8ArrayView, } from '../readerService'; import { generateThumbnail } from './thumbnailService'; +import uploadService from './uploadService'; +import { CustomError } from 'utils/error'; const EDITED_FILE_SUFFIX = '-edited'; @@ -37,6 +39,9 @@ export async function readFile( fileTypeInfo: FileTypeInfo, rawFile: File | ElectronFile ): Promise { + if (uploadService.isUploadPausing()) { + throw Error(CustomError.UPLOAD_PAUSED); + } const { thumbnail, hasStaticThumbnail } = await generateThumbnail( rawFile, fileTypeInfo @@ -98,6 +103,9 @@ export async function encryptFile( encryptionKey: string ): Promise { try { + if (uploadService.isUploadPausing()) { + throw Error(CustomError.UPLOAD_PAUSED); + } const { key: fileKey, file: encryptedFiledata } = await encryptFiledata( worker, file.filedata diff --git a/src/services/upload/livePhotoService.ts b/src/services/upload/livePhotoService.ts index 1b2000cc1..e68a2f0ee 100644 --- a/src/services/upload/livePhotoService.ts +++ b/src/services/upload/livePhotoService.ts @@ -79,6 +79,9 @@ export async function readLivePhoto( fileTypeInfo: FileTypeInfo, livePhotoAssets: LivePhotoAssets ) { + if (uploadService.isUploadPausing()) { + throw Error(CustomError.UPLOAD_PAUSED); + } const { thumbnail, hasStaticThumbnail } = await generateThumbnail( livePhotoAssets.image, { diff --git a/src/services/upload/multiPartUploadService.ts b/src/services/upload/multiPartUploadService.ts index 4b7070398..638044a02 100644 --- a/src/services/upload/multiPartUploadService.ts +++ b/src/services/upload/multiPartUploadService.ts @@ -8,6 +8,7 @@ import UploadHttpClient from './uploadHttpClient'; import * as convert from 'xml-js'; import { CustomError } from 'utils/error'; import { DataStream, MultipartUploadURLs } from 'types/upload'; +import uploadService from './uploadService'; interface PartEtag { PartNumber: number; @@ -51,6 +52,9 @@ export async function uploadStreamInParts( index, fileUploadURL, ] of multipartUploadURLs.partURLs.entries()) { + if (uploadService.isUploadPausing()) { + throw Error(CustomError.UPLOAD_PAUSED); + } const uploadChunk = await combineChunksToFormUploadPart(streamReader); const progressTracker = UIService.trackUploadProgress( fileLocalID, diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index 9c2cbee28..951f57bce 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -27,6 +27,7 @@ import { EnteFile } from 'types/file'; import { ElectronFile, FileWithCollection, + IsUploadPausing, Metadata, MetadataAndFileTypeInfo, MetadataAndFileTypeInfoMap, @@ -61,7 +62,9 @@ class UploadManager { private existingFiles: EnteFile[]; private setFiles: SetFiles; private collections: Map; - private isUploadPausing: boolean = false; + private isUploadPausing: IsUploadPausing = { + val: false, + }; public initUploader(progressUpdater: ProgressUpdater, setFiles: SetFiles) { UIService.init(progressUpdater); this.setFiles = setFiles; @@ -107,7 +110,7 @@ class UploadManager { UPLOAD_STAGES.READING_GOOGLE_METADATA_FILES ); await this.parseMetadataJSONFiles(metadataJSONFiles); - if (this.isUploadPausing) { + if (this.isUploadPausing.val) { this.uploadPausingDone(); return; } @@ -118,7 +121,7 @@ class UploadManager { if (mediaFiles.length) { UIService.setUploadStage(UPLOAD_STAGES.EXTRACTING_METADATA); await this.extractMetadataFromFiles(mediaFiles); - if (this.isUploadPausing) { + if (this.isUploadPausing.val) { this.uploadPausingDone(); return; } @@ -175,7 +178,7 @@ class UploadManager { ); await this.uploadMediaFiles(allFiles); - if (this.isUploadPausing) { + if (this.isUploadPausing.val) { this.uploadPausingDone(); return; } @@ -204,7 +207,7 @@ class UploadManager { for (const { file, collectionID } of metadataFiles) { try { - if (this.isUploadPausing) { + if (this.isUploadPausing.val) { return; } @@ -249,7 +252,7 @@ class UploadManager { addLogLine(`extractMetadataFromFiles executed`); UIService.reset(mediaFiles.length); for (const { file, localID, collectionID } of mediaFiles) { - if (this.isUploadPausing) { + if (this.isUploadPausing.val) { return; } let fileTypeInfo = null; @@ -366,7 +369,7 @@ class UploadManager { private async uploadNextFileInQueue(worker: any) { while (this.filesToBeUploaded.length > 0) { - if (this.isUploadPausing) { + if (this.isUploadPausing.val) { return; } let fileWithCollection = this.filesToBeUploaded.pop(); @@ -470,13 +473,15 @@ class UploadManager { public pauseWatchService() { if (isElectron()) { watchFolderService.pauseService(); - this.isUploadPausing = true; + this.isUploadPausing.val = true; + UploadService.setUploadPausing(true); UIService.setUploadStage(UPLOAD_STAGES.PAUSING); } } private async uploadPausingDone() { - this.isUploadPausing = false; + this.isUploadPausing.val = false; + UploadService.setUploadPausing(false); } private updateExistingCollections(decryptedFile: EnteFile) { diff --git a/src/services/upload/uploadService.ts b/src/services/upload/uploadService.ts index 3234dd951..f96517555 100644 --- a/src/services/upload/uploadService.ts +++ b/src/services/upload/uploadService.ts @@ -3,7 +3,7 @@ import { logError } from 'utils/sentry'; import UploadHttpClient from './uploadHttpClient'; import { extractFileMetadata, getFilename } from './fileService'; import { getFileType } from '../typeDetectionService'; -import { handleUploadError } from 'utils/error'; +import { CustomError, handleUploadError } from 'utils/error'; import { B64EncryptionResult, BackupedFile, @@ -13,6 +13,7 @@ import { FileWithCollection, FileWithMetadata, isDataStream, + IsUploadPausing, Metadata, MetadataAndFileTypeInfo, MetadataAndFileTypeInfoMap, @@ -44,6 +45,9 @@ class UploadService { number, MetadataAndFileTypeInfo >(); + private uploadPausing: IsUploadPausing = { + val: false, + }; private pendingUploadCount: number = 0; async setFileCount(fileCount: number) { @@ -77,6 +81,14 @@ class UploadService { : getFilename(file); } + setUploadPausing(isPausing: boolean) { + this.uploadPausing.val = isPausing; + } + + isUploadPausing(): boolean { + return this.uploadPausing.val; + } + async getFileType(file: File | ElectronFile) { return getFileType(file); } @@ -131,6 +143,9 @@ class UploadService { async uploadToBucket(file: ProcessedFile): Promise { try { + if (this.isUploadPausing()) { + throw Error(CustomError.UPLOAD_PAUSED); + } let fileObjectKey: string = null; if (isDataStream(file.file.encryptedData)) { fileObjectKey = await uploadStreamUsingMultipart( diff --git a/src/services/upload/uploader.ts b/src/services/upload/uploader.ts index 624fdfd81..9ff5c45b5 100644 --- a/src/services/upload/uploader.ts +++ b/src/services/upload/uploader.ts @@ -149,6 +149,8 @@ export default async function uploader( }); const error = handleUploadError(e); switch (error.message) { + case CustomError.UPLOAD_PAUSED: + return { fileUploadResult: UPLOAD_RESULT.PAUSED }; case CustomError.ETAG_MISSING: return { fileUploadResult: UPLOAD_RESULT.BLOCKED }; case CustomError.UNSUPPORTED_FILE_FORMAT: diff --git a/src/types/upload/index.ts b/src/types/upload/index.ts index f42d5ead6..4c185dd04 100644 --- a/src/types/upload/index.ts +++ b/src/types/upload/index.ts @@ -143,3 +143,7 @@ export interface ParsedExtractedMetadata { location: Location; creationTime: number; } + +export interface IsUploadPausing { + val: boolean; +} diff --git a/src/utils/error/index.ts b/src/utils/error/index.ts index eb3117ffc..d115716fc 100644 --- a/src/utils/error/index.ts +++ b/src/utils/error/index.ts @@ -45,6 +45,7 @@ export enum CustomError { FILE_ID_NOT_FOUND = 'file with id not found', WEAK_DEVICE = 'password decryption failed on the device', INCORRECT_PASSWORD = 'incorrect password', + UPLOAD_PAUSED = 'upload paused', } function parseUploadErrorCodes(error) { From 0559812506d2aa7e498769ef72e95635e7871fcb Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 16 Aug 2022 11:48:12 +0530 Subject: [PATCH 084/295] revert change to DialogBoxBase --- src/components/DialogBox/base.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/DialogBox/base.tsx b/src/components/DialogBox/base.tsx index bc3362daa..6db198c75 100644 --- a/src/components/DialogBox/base.tsx +++ b/src/components/DialogBox/base.tsx @@ -1,9 +1,9 @@ import { Dialog, styled } from '@mui/material'; -const DialogBoxBase = styled(Dialog)(({ theme, maxWidth }) => ({ +const DialogBoxBase = styled(Dialog)(({ theme }) => ({ '& .MuiDialog-paper': { padding: theme.spacing(1, 1.5), - maxWidth: maxWidth ?? '346px', + maxWidth: '346px', }, '& .MuiDialogTitle-root': { padding: theme.spacing(2), From a2cf90992d2b74bf978ac0472b174415d95ef17a Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 16 Aug 2022 12:26:36 +0530 Subject: [PATCH 085/295] update watch no mapping content ui --- src/components/WatchFolder/index.tsx | 64 ++++++++++--------- .../WatchFolder/styledComponents.tsx | 1 - src/pages/_app.tsx | 2 +- src/pages/gallery/index.tsx | 2 +- 4 files changed, 37 insertions(+), 32 deletions(-) diff --git a/src/components/WatchFolder/index.tsx b/src/components/WatchFolder/index.tsx index 2f6825975..a3fe616ff 100644 --- a/src/components/WatchFolder/index.tsx +++ b/src/components/WatchFolder/index.tsx @@ -1,12 +1,11 @@ import { MappingList } from './mappingList'; import { NoMappingsContent } from './noMappingsContent'; import React, { useContext, useEffect, useState } from 'react'; -import { Button, DialogActions, DialogContent } from '@mui/material'; +import { Button, Dialog, DialogContent, Stack } from '@mui/material'; import watchFolderService from 'services/watchFolder/watchFolderService'; import { WatchMapping } from 'types/watchFolder'; import { AppContext } from 'pages/_app'; import constants from 'utils/strings/constants'; -import DialogBoxBase from 'components/DialogBox/base'; import DialogTitleWithCloseButton from 'components/DialogBox/TitleWithCloseButton'; import UploadStrategyChoiceModal from 'components/Upload/UploadStrategyChoiceModal'; import { UPLOAD_STRATEGY } from 'constants/upload'; @@ -87,36 +86,43 @@ export default function WatchFolder({ open, onClose }: Iprops) { return ( <> - - + + {constants.WATCHED_FOLDERS} - - {mappings.length === 0 ? ( - - ) : ( - - )} - + + + {mappings.length === 0 ? ( + + ) : ( + + )} - - - - + + + + ({ })); export const NoMappingsContainer = styled(VerticallyCentered)({ - height: '278px', textAlign: 'left', alignItems: 'flex-start', }); diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index ed219cfd2..d53e74c45 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -94,7 +94,7 @@ export default function App({ Component, err }) { const [dialogMessage, setDialogMessage] = useState(); const [messageDialogView, setMessageDialogView] = useState(false); const [isFolderSyncRunning, setIsFolderSyncRunning] = useState(false); - const [watchFolderView, setWatchFolderView] = useState(false); + const [watchFolderView, setWatchFolderView] = useState(true); const [watchFolderFiles, setWatchFolderFiles] = useState(null); const isMobile = useMediaQuery('(max-width:428px)'); diff --git a/src/pages/gallery/index.tsx b/src/pages/gallery/index.tsx index 7bf1172d7..9457e0274 100644 --- a/src/pages/gallery/index.tsx +++ b/src/pages/gallery/index.tsx @@ -207,7 +207,7 @@ export default function Gallery() { const [webFiles, setWebFiles] = useState([]); const [uploadTypeSelectorView, setUploadTypeSelectorView] = useState(false); - const [sidebarView, setSidebarView] = useState(false); + const [sidebarView, setSidebarView] = useState(true); const closeSidebar = () => setSidebarView(false); const openSidebar = () => setSidebarView(true); From 153a2e49b9d454721c96bde584500f6e2593c070 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 16 Aug 2022 12:32:51 +0530 Subject: [PATCH 086/295] refactor, move, rename --- src/components/Sidebar/UtilitySection.tsx | 30 +++++++---------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/src/components/Sidebar/UtilitySection.tsx b/src/components/Sidebar/UtilitySection.tsx index 599fada0c..53a90e492 100644 --- a/src/components/Sidebar/UtilitySection.tsx +++ b/src/components/Sidebar/UtilitySection.tsx @@ -8,8 +8,8 @@ import { PAGES } from 'constants/pages'; import { useRouter } from 'next/router'; import { AppContext } from 'pages/_app'; import isElectron from 'is-electron'; -import { downloadApp } from 'utils/common'; import WatchFolder from 'components/WatchFolder'; +import { getDownloadAppMessage } from 'utils/ui'; export default function UtilitySection({ closeSidebar }) { const router = useRouter(); @@ -27,28 +27,17 @@ export default function UtilitySection({ closeSidebar }) { const openRecoveryKeyModal = () => setRecoveryModalView(true); const closeRecoveryKeyModal = () => setRecoveryModalView(false); - const openTwoFactorModalView = () => setTwoFactorModalView(true); - const closeTwoFactorModalView = () => setTwoFactorModalView(false); + const openTwoFactorModal = () => setTwoFactorModalView(true); + const closeTwoFactorModal = () => setTwoFactorModalView(false); - const openWatchFolderView = () => { + const openWatchFolder = () => { if (isElectron()) { setWatchFolderView(true); } else { - setDialogMessage({ - title: constants.DOWNLOAD_APP, - content: constants.DOWNLOAD_APP_MESSAGE, - - proceed: { - text: constants.DOWNLOAD, - action: downloadApp, - variant: 'accent', - }, - close: { - text: constants.CLOSE, - }, - }); + setDialogMessage(getDownloadAppMessage()); } }; + const closeWatchFolder = () => setWatchFolderView(false); const redirectToChangePasswordPage = () => { closeSidebar(); @@ -71,13 +60,12 @@ export default function UtilitySection({ closeSidebar }) { close: { variant: 'danger' }, }); - const closeWatchFolder = () => setWatchFolderView(false); return ( <> {constants.RECOVERY_KEY} - + {constants.TWO_FACTOR} @@ -89,7 +77,7 @@ export default function UtilitySection({ closeSidebar }) { {constants.DEDUPLICATE_FILES} - + {constants.WATCH_FOLDERS} @@ -104,7 +92,7 @@ export default function UtilitySection({ closeSidebar }) { /> From 15dec7a8ea3d8a3155beb1b6cdc528295178615e Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 16 Aug 2022 12:55:02 +0530 Subject: [PATCH 087/295] refactor closeUploadProgress --- src/components/Upload/Uploader.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/Upload/Uploader.tsx b/src/components/Upload/Uploader.tsx index 82f37d3ad..60a1cc3e9 100644 --- a/src/components/Upload/Uploader.tsx +++ b/src/components/Upload/Uploader.tsx @@ -108,6 +108,8 @@ export default function Uploader(props: Props) { const uploadType = useRef(null); const zipPaths = useRef(null); + const closeUploadProgress = () => setUploadProgressView(false); + useEffect(() => { UploadManager.initUploader( { @@ -312,7 +314,7 @@ export default function Uploader(props: Props) { ); } } catch (e) { - setUploadProgressView(false); + closeUploadProgress(); logError(e, 'Failed to create album'); appContext.setDialogMessage({ title: constants.ERROR, @@ -359,7 +361,7 @@ export default function Uploader(props: Props) { ); } catch (err) { showUserFacingError(err.message); - setUploadProgressView(false); + closeUploadProgress(); throw err; } finally { props.setUploadInProgress(false); @@ -376,7 +378,7 @@ export default function Uploader(props: Props) { } catch (err) { showUserFacingError(err.message); - setUploadProgressView(false); + closeUploadProgress(); } finally { props.setUploadInProgress(false); props.syncWithRemote(); @@ -507,7 +509,7 @@ export default function Uploader(props: Props) { }; const cancelUploads = async () => { - setUploadProgressView(false); + closeUploadProgress(); if (isElectron()) { ImportService.cancelRemainingUploads(); } @@ -515,8 +517,6 @@ export default function Uploader(props: Props) { Router.reload(); }; - const closeUploadProgress = () => setUploadProgressView(false); - const handleUpload = (type) => () => { if (isElectron() && importService.checkAllElectronAPIsExists()) { handleDesktopUpload(type); From e76217a051b0c81d5557253af554365042abc37d Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 16 Aug 2022 13:00:32 +0530 Subject: [PATCH 088/295] don't need to use expanded prop to show Snackbar as its already managed by UploadProgress component --- src/components/Upload/UploadProgress/minimized.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Upload/UploadProgress/minimized.tsx b/src/components/Upload/UploadProgress/minimized.tsx index c314dcd98..1d860083f 100644 --- a/src/components/Upload/UploadProgress/minimized.tsx +++ b/src/components/Upload/UploadProgress/minimized.tsx @@ -1,10 +1,10 @@ import { Snackbar, Paper } from '@mui/material'; import React from 'react'; import { UploadProgressHeader } from './header'; -export function MinimizedUploadProgress(props) { +export function MinimizedUploadProgress() { return ( Date: Tue, 16 Aug 2022 12:55:02 +0530 Subject: [PATCH 089/295] refactor closeUploadProgress --- src/components/Upload/Uploader.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/Upload/Uploader.tsx b/src/components/Upload/Uploader.tsx index 8bfcdb6ed..b636424bc 100644 --- a/src/components/Upload/Uploader.tsx +++ b/src/components/Upload/Uploader.tsx @@ -118,6 +118,8 @@ export default function Uploader(props: Props) { const waitForPrevUpload = useRef>(null); const uploadDone = useRef<() => void>(null); + const closeUploadProgress = () => setUploadProgressView(false); + useEffect(() => { UploadManager.initUploader( { @@ -339,7 +341,7 @@ export default function Uploader(props: Props) { ); } } catch (e) { - setUploadProgressView(false); + closeUploadProgress(); logError(e, 'Failed to create album'); appContext.setDialogMessage({ title: constants.ERROR, @@ -390,7 +392,7 @@ export default function Uploader(props: Props) { ); } catch (err) { showUserFacingError(err.message); - setUploadProgressView(false); + closeUploadProgress(); throw err; } finally { unsetUploadLock(); @@ -432,7 +434,7 @@ export default function Uploader(props: Props) { } catch (err) { showUserFacingError(err.message); - setUploadProgressView(false); + closeUploadProgress(); } finally { props.setUploadInProgress(false); props.syncWithRemote(); @@ -563,7 +565,7 @@ export default function Uploader(props: Props) { }; const cancelUploads = async () => { - setUploadProgressView(false); + closeUploadProgress(); if (isElectron()) { ImportService.cancelRemainingUploads(); } @@ -571,8 +573,6 @@ export default function Uploader(props: Props) { Router.reload(); }; - const closeUploadProgress = () => setUploadProgressView(false); - const handleUpload = (type) => () => { if (isElectron() && importService.checkAllElectronAPIsExists()) { handleDesktopUpload(type); From 6cc326384fe60e3690760b457cfc16b4664ad98a Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 16 Aug 2022 13:00:32 +0530 Subject: [PATCH 090/295] don't need to use expanded prop to show Snackbar as its already managed by UploadProgress component --- src/components/Upload/UploadProgress/minimized.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/Upload/UploadProgress/minimized.tsx b/src/components/Upload/UploadProgress/minimized.tsx index db3398677..1d860083f 100644 --- a/src/components/Upload/UploadProgress/minimized.tsx +++ b/src/components/Upload/UploadProgress/minimized.tsx @@ -1,12 +1,10 @@ import { Snackbar, Paper } from '@mui/material'; -import UploadProgressContext from 'contexts/uploadProgress'; -import React, { useContext } from 'react'; +import React from 'react'; import { UploadProgressHeader } from './header'; export function MinimizedUploadProgress() { - const { open } = useContext(UploadProgressContext); return ( Date: Tue, 16 Aug 2022 13:15:49 +0530 Subject: [PATCH 091/295] made prebuild as a caller of actual microtask yarn lint --- package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index fb90582a0..86b946949 100644 --- a/package.json +++ b/package.json @@ -5,10 +5,11 @@ "scripts": { "dev": "next dev", "albums": "next dev -p 3002", - "prebuild": "eslint \"src/**/*.{js,jsx,ts,tsx}\"", + "lint": "eslint \"src/**/*.{js,jsx,ts,tsx}\"", + "prebuild": "yarn lint", "build": "next build", - "build-analyze": "ANALYZE=true next build", "postbuild": "next export", + "build-analyze": "ANALYZE=true next build", "start": "next start", "prepare": "husky install" }, From 73484eac2cdd6785b4447e08c4ec6ee89f4bb560 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 16 Aug 2022 12:38:50 +0530 Subject: [PATCH 092/295] add comment --- src/components/Upload/UploadProgress/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Upload/UploadProgress/index.tsx b/src/components/Upload/UploadProgress/index.tsx index 4ecc45b59..5d538f0cb 100644 --- a/src/components/Upload/UploadProgress/index.tsx +++ b/src/components/Upload/UploadProgress/index.tsx @@ -43,6 +43,7 @@ export default function UploadProgress({ const appContext = useContext(AppContext); const [expanded, setExpanded] = useState(true); + // run watch folder minimized by default useEffect(() => { if ( appContext.isFolderSyncRunning && From 6d8528cdc8f83999a3be04e80951a6c321fcda77 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 16 Aug 2022 19:35:52 +0530 Subject: [PATCH 093/295] change lock system to queue system --- src/components/Upload/Uploader.tsx | 47 ++++++++++++------------------ src/types/watchFolder/index.ts | 5 ---- src/utils/common/index.ts | 25 ++++++++-------- 3 files changed, 32 insertions(+), 45 deletions(-) diff --git a/src/components/Upload/Uploader.tsx b/src/components/Upload/Uploader.tsx index b636424bc..4ee5ed203 100644 --- a/src/components/Upload/Uploader.tsx +++ b/src/components/Upload/Uploader.tsx @@ -21,7 +21,7 @@ import { SetLoading, SetFiles } from 'types/gallery'; import { ElectronFile, FileWithCollection } from 'types/upload'; import Router from 'next/router'; import { isCanvasBlocked } from 'utils/upload/isCanvasBlocked'; -import { downloadApp } from 'utils/common'; +import { downloadApp, waitAndRun } from 'utils/common'; import watchFolderService from 'services/watchFolder/watchFolderService'; import DiscFullIcon from '@mui/icons-material/DiscFull'; import { NotificationAttributes } from 'types/Notification'; @@ -35,7 +35,6 @@ import { UPLOAD_STAGES } from 'constants/upload'; import importService from 'services/importService'; import { getDownloadAppMessage } from 'utils/ui'; import UploadTypeSelector from './UploadTypeSelector'; -import { newLock } from 'utils/common'; const FIRST_ALBUM_NAME = 'My First Album'; @@ -61,12 +60,6 @@ interface Props { showUploadDirsDialog: () => void; } -export enum DESKTOP_UPLOAD_TYPE { - FILES = 'files', - FOLDERS = 'folders', - ZIPS = 'zips', -} - enum UPLOAD_STRATEGY { SINGLE_COLLECTION, COLLECTION_PER_FOLDER, @@ -115,8 +108,7 @@ export default function Uploader(props: Props) { const pendingDesktopUploadCollectionName = useRef(''); const uploadType = useRef(null); const zipPaths = useRef(null); - const waitForPrevUpload = useRef>(null); - const uploadDone = useRef<() => void>(null); + const previousUploadPromise = useRef>(null); const closeUploadProgress = () => setUploadProgressView(false); @@ -300,7 +292,9 @@ export default function Uploader(props: Props) { localID: index, collectionID: collection.id, })); - await uploadFiles(filesWithCollectionToUpload, [collection]); + await waitInQueueAndUploadFiles(filesWithCollectionToUpload, [ + collection, + ]); } catch (e) { logError(e, 'Failed to upload files to existing collections'); } @@ -351,21 +345,30 @@ export default function Uploader(props: Props) { }); throw e; } - await uploadFiles(filesWithCollectionToUpload, collections); + await waitInQueueAndUploadFiles( + filesWithCollectionToUpload, + collections + ); } catch (e) { logError(e, 'Failed to upload files to new collections'); } }; + const waitInQueueAndUploadFiles = async ( + filesWithCollectionToUpload: FileWithCollection[], + collections: Collection[] + ) => { + const currentPromise = previousUploadPromise.current; + previousUploadPromise.current = waitAndRun(currentPromise, () => + uploadFiles(filesWithCollectionToUpload, collections) + ); + }; + const uploadFiles = async ( filesWithCollectionToUpload: FileWithCollection[], collections: Collection[] ) => { try { - if (waitForPrevUpload.current !== null) { - await waitForPrevUpload.current; // wait for previous upload to finish - } - setUploadLock(); uploadInit(); props.setUploadInProgress(true); props.closeCollectionSelector(); @@ -395,7 +398,6 @@ export default function Uploader(props: Props) { closeUploadProgress(); throw err; } finally { - unsetUploadLock(); props.setUploadInProgress(false); props.syncWithRemote(); if (isElectron()) { @@ -414,17 +416,6 @@ export default function Uploader(props: Props) { } }; - const setUploadLock = () => { - const { wait, unlock } = newLock(); - waitForPrevUpload.current = wait; - uploadDone.current = unlock; - }; - - const unsetUploadLock = () => { - uploadDone.current(); - waitForPrevUpload.current = null; - }; - const retryFailed = async () => { try { props.setUploadInProgress(true); diff --git a/src/types/watchFolder/index.ts b/src/types/watchFolder/index.ts index 28c8f677e..97fa96dce 100644 --- a/src/types/watchFolder/index.ts +++ b/src/types/watchFolder/index.ts @@ -18,8 +18,3 @@ export interface EventQueueItem { paths?: string[]; files?: ElectronFile[]; } - -export interface Lock { - wait: Promise; - unlock(): void; -} diff --git a/src/utils/common/index.ts b/src/utils/common/index.ts index 253832bc0..0e71519e6 100644 --- a/src/utils/common/index.ts +++ b/src/utils/common/index.ts @@ -1,7 +1,6 @@ import constants from 'utils/strings/constants'; import { CustomError } from 'utils/error'; import GetDeviceOS, { OS } from './deviceDetection'; -import { Lock } from 'types/watchFolder'; const DESKTOP_APP_GITHUB_DOWNLOAD_URL = 'https://github.com/ente-io/bhari-frame/releases/latest'; @@ -94,15 +93,17 @@ export function openLink(href: string, newTab?: boolean) { a.click(); } -export function newLock(): Lock { - let resolver: () => void = null; - const wait = new Promise((resolve) => { - resolver = resolve; - }); - return { - wait, - unlock: () => { - resolver(); - }, - }; +export async function waitAndRun(waitPromise: Promise, task: () => void) { + if (waitPromise && isPromise(waitPromise)) { + await waitPromise; + } + task(); +} + +function isPromise(p: any) { + if (typeof p === 'object' && typeof p.then === 'function') { + return true; + } + + return false; } From 008e6b5d36e347860958e5af51e48af17c69b8e5 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 16 Aug 2022 19:40:08 +0530 Subject: [PATCH 094/295] need to run also if running watch service upload was paused --- src/components/Upload/Uploader.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Upload/Uploader.tsx b/src/components/Upload/Uploader.tsx index 4ee5ed203..cb360aa05 100644 --- a/src/components/Upload/Uploader.tsx +++ b/src/components/Upload/Uploader.tsx @@ -161,7 +161,8 @@ export default function Uploader(props: Props) { // a user upload is already in progress return; } - } else if (isCanvasBlocked()) { + } + if (isCanvasBlocked()) { appContext.setDialogMessage({ title: constants.CANVAS_BLOCKED_TITLE, From 1c2a03ba5ec625cb5d106662b5c25ceba3196398 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 16 Aug 2022 19:41:11 +0530 Subject: [PATCH 095/295] move code --- src/components/Upload/Uploader.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/Upload/Uploader.tsx b/src/components/Upload/Uploader.tsx index cb360aa05..a1a2a82ad 100644 --- a/src/components/Upload/Uploader.tsx +++ b/src/components/Upload/Uploader.tsx @@ -112,6 +112,11 @@ export default function Uploader(props: Props) { const closeUploadProgress = () => setUploadProgressView(false); + const setCollectionName = (collectionName: string) => { + isPendingDesktopUpload.current = true; + pendingDesktopUploadCollectionName.current = collectionName; + }; + useEffect(() => { UploadManager.initUploader( { @@ -141,11 +146,6 @@ export default function Uploader(props: Props) { } }, []); - const setCollectionName = (collectionName: string) => { - isPendingDesktopUpload.current = true; - pendingDesktopUploadCollectionName.current = collectionName; - }; - useEffect(() => { if ( props.electronFiles?.length > 0 || From 4b37a0166658322b7faf1d93da764402882a996b Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 17 Aug 2022 10:26:16 +0530 Subject: [PATCH 096/295] moved post upload complete action to uploadManager and rename --- src/components/Upload/Uploader.tsx | 23 +++++------------------ src/services/upload/uploadManager.ts | 17 ++++++++++++++--- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/components/Upload/Uploader.tsx b/src/components/Upload/Uploader.tsx index a1a2a82ad..f39a4e0dd 100644 --- a/src/components/Upload/Uploader.tsx +++ b/src/components/Upload/Uploader.tsx @@ -356,17 +356,17 @@ export default function Uploader(props: Props) { }; const waitInQueueAndUploadFiles = async ( - filesWithCollectionToUpload: FileWithCollection[], + filesWithCollectionToUploadIn: FileWithCollection[], collections: Collection[] ) => { const currentPromise = previousUploadPromise.current; previousUploadPromise.current = waitAndRun(currentPromise, () => - uploadFiles(filesWithCollectionToUpload, collections) + uploadFiles(filesWithCollectionToUploadIn, collections) ); }; const uploadFiles = async ( - filesWithCollectionToUpload: FileWithCollection[], + filesWithCollectionToUploadIn: FileWithCollection[], collections: Collection[] ) => { try { @@ -385,13 +385,13 @@ export default function Uploader(props: Props) { } await ImportService.setToUploadFiles( UPLOAD_TYPE.FILES, - filesWithCollectionToUpload.map( + filesWithCollectionToUploadIn.map( ({ file }) => (file as ElectronFile).path ) ); } await uploadManager.queueFilesForUpload( - filesWithCollectionToUpload, + filesWithCollectionToUploadIn, collections ); } catch (err) { @@ -401,19 +401,6 @@ export default function Uploader(props: Props) { } finally { props.setUploadInProgress(false); props.syncWithRemote(); - if (isElectron()) { - if (watchFolderService.isUploadRunning()) { - await watchFolderService.allFileUploadsDone( - filesWithCollectionToUpload, - collections - ); - } else { - if (watchFolderService.isServicePaused()) { - // resume the service after user upload is done - watchFolderService.resumeService(); - } - } - } } }; diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index 951f57bce..f551498ab 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -93,16 +93,16 @@ class UploadManager { } public async queueFilesForUpload( - fileWithCollectionToBeUploaded: FileWithCollection[], + filesWithCollectionToUploadIn: FileWithCollection[], collections: Collection[] ) { try { await this.init(collections); addLogLine( - `received ${fileWithCollectionToBeUploaded.length} files to upload` + `received ${filesWithCollectionToUploadIn.length} files to upload` ); const { metadataJSONFiles, mediaFiles } = - segregateMetadataAndMediaFiles(fileWithCollectionToBeUploaded); + segregateMetadataAndMediaFiles(filesWithCollectionToUploadIn); addLogLine(`has ${metadataJSONFiles.length} metadata json files`); addLogLine(`has ${mediaFiles.length} media files`); if (metadataJSONFiles.length) { @@ -196,6 +196,17 @@ class UploadManager { for (let i = 0; i < MAX_CONCURRENT_UPLOADS; i++) { this.cryptoWorkers[i]?.worker.terminate(); } + if (isElectron()) { + if (watchFolderService.isUploadRunning()) { + await watchFolderService.allFileUploadsDone( + filesWithCollectionToUploadIn, + collections + ); + } else if (watchFolderService.isServicePaused()) { + // resume the service after user upload is done + watchFolderService.resumeService(); + } + } } } From ab7427c644d0f4edab1416a722f8eea153318410 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 17 Aug 2022 10:32:00 +0530 Subject: [PATCH 097/295] fix typo --- src/components/WatchFolder/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/WatchFolder/index.tsx b/src/components/WatchFolder/index.tsx index a3fe616ff..227672c11 100644 --- a/src/components/WatchFolder/index.tsx +++ b/src/components/WatchFolder/index.tsx @@ -18,7 +18,7 @@ interface Iprops { export default function WatchFolder({ open, onClose }: Iprops) { const [mappings, setMappings] = useState([]); const [inputFolderPath, setInputFolderPath] = useState(''); - const [choicModalOpen, setChoiceModalOpen] = useState(false); + const [choiceModalOpen, setChoiceModalOpen] = useState(false); const appContext = useContext(AppContext); useEffect(() => { @@ -124,7 +124,7 @@ export default function WatchFolder({ open, onClose }: Iprops) { Date: Wed, 17 Aug 2022 10:32:41 +0530 Subject: [PATCH 098/295] clean code --- src/components/WatchFolder/checkmarkIcon.tsx | 2 +- .../WatchFolder/mappingEntry/entryHeading.tsx | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/components/WatchFolder/checkmarkIcon.tsx b/src/components/WatchFolder/checkmarkIcon.tsx index 75805d9c9..10b4f0459 100644 --- a/src/components/WatchFolder/checkmarkIcon.tsx +++ b/src/components/WatchFolder/checkmarkIcon.tsx @@ -5,7 +5,7 @@ export function CheckmarkIcon() { return ( theme.palette.secondary.main, }} /> diff --git a/src/components/WatchFolder/mappingEntry/entryHeading.tsx b/src/components/WatchFolder/mappingEntry/entryHeading.tsx index 12553f63e..c0f559996 100644 --- a/src/components/WatchFolder/mappingEntry/entryHeading.tsx +++ b/src/components/WatchFolder/mappingEntry/entryHeading.tsx @@ -13,14 +13,8 @@ interface Iprops { export function EntryHeading({ mapping }: Iprops) { const appContext = useContext(AppContext); return ( - - - {mapping.rootFolderName} - + + {mapping.rootFolderName} {appContext.isFolderSyncRunning && watchFolderService.isMappingSyncing(mapping) && ( From 787ff7e8511e3d114bea1197996b138c283721da Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 16 Aug 2022 14:36:37 +0530 Subject: [PATCH 099/295] refactor lock util and types --- src/types/lock/index.ts | 4 ++++ src/utils/lock/index.ts | 14 ++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 src/types/lock/index.ts create mode 100644 src/utils/lock/index.ts diff --git a/src/types/lock/index.ts b/src/types/lock/index.ts new file mode 100644 index 000000000..bec0a7365 --- /dev/null +++ b/src/types/lock/index.ts @@ -0,0 +1,4 @@ +export interface Lock { + wait: Promise; + unlock(): void; +} diff --git a/src/utils/lock/index.ts b/src/utils/lock/index.ts new file mode 100644 index 000000000..23aaa939e --- /dev/null +++ b/src/utils/lock/index.ts @@ -0,0 +1,14 @@ +import { Lock } from 'types/lock'; + +export function newLock(): Lock { + let resolver: () => void = null; + const wait = new Promise((resolve) => { + resolver = resolve; + }); + return { + wait, + unlock: () => { + resolver(); + }, + }; +} From d9894e654f4d749d62cf0ce7050fdbb10924fdba Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 17 Aug 2022 13:13:28 +0530 Subject: [PATCH 100/295] move HorizontalFlex to root container component --- src/components/Container.ts | 4 ++++ src/components/WatchFolder/mappingEntry/index.tsx | 4 ++-- src/components/WatchFolder/styledComponents.tsx | 5 +---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/components/Container.ts b/src/components/Container.ts index 76844f93b..bcb7464ca 100644 --- a/src/components/Container.ts +++ b/src/components/Container.ts @@ -73,3 +73,7 @@ export const Overlay = styled(Box)` export const IconButtonWithBG = styled(IconButton)(({ theme }) => ({ backgroundColor: theme.palette.fill.dark, })); + +export const HorizontalFlex = styled(Box)({ + display: 'flex', +}); diff --git a/src/components/WatchFolder/mappingEntry/index.tsx b/src/components/WatchFolder/mappingEntry/index.tsx index 7e5f08314..ef6b199de 100644 --- a/src/components/WatchFolder/mappingEntry/index.tsx +++ b/src/components/WatchFolder/mappingEntry/index.tsx @@ -1,7 +1,7 @@ -import { EntryContainer, HorizontalFlex } from '../styledComponents'; +import { EntryContainer } from '../styledComponents'; import React from 'react'; import { Typography } from '@mui/material'; -import { SpaceBetweenFlex } from 'components/Container'; +import { HorizontalFlex, SpaceBetweenFlex } from 'components/Container'; import { WatchMapping } from 'types/watchFolder'; import { AppContext } from 'pages/_app'; import FolderOpenIcon from '@mui/icons-material/FolderOpen'; diff --git a/src/components/WatchFolder/styledComponents.tsx b/src/components/WatchFolder/styledComponents.tsx index ad3bdca77..5b4427beb 100644 --- a/src/components/WatchFolder/styledComponents.tsx +++ b/src/components/WatchFolder/styledComponents.tsx @@ -1,3 +1,4 @@ +import {} from './../Container'; import { Box } from '@mui/material'; import { styled } from '@mui/material/styles'; import VerticallyCentered from 'components/Container'; @@ -18,10 +19,6 @@ export const NoMappingsContainer = styled(VerticallyCentered)({ alignItems: 'flex-start', }); -export const HorizontalFlex = styled(Box)({ - display: 'flex', -}); - export const EntryContainer = styled(Box)({ marginLeft: '12px', marginRight: '6px', From 5d6ec4d755527f6b68f5244aebe943066809033a Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 17 Aug 2022 13:15:33 +0530 Subject: [PATCH 101/295] fix EntryHeading --- src/components/WatchFolder/mappingEntry/entryHeading.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/WatchFolder/mappingEntry/entryHeading.tsx b/src/components/WatchFolder/mappingEntry/entryHeading.tsx index c0f559996..d4386eb0b 100644 --- a/src/components/WatchFolder/mappingEntry/entryHeading.tsx +++ b/src/components/WatchFolder/mappingEntry/entryHeading.tsx @@ -13,7 +13,7 @@ interface Iprops { export function EntryHeading({ mapping }: Iprops) { const appContext = useContext(AppContext); return ( - + {mapping.rootFolderName} {appContext.isFolderSyncRunning && watchFolderService.isMappingSyncing(mapping) && ( From 600ef6bd5187c4ed11ec3dc619d727f030ed9136 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 17 Aug 2022 13:25:06 +0530 Subject: [PATCH 102/295] refactor mapping entry options --- .../WatchFolder/mappingEntry/index.tsx | 6 +- .../WatchFolder/mappingEntryOptions.tsx | 64 +++++-------------- 2 files changed, 22 insertions(+), 48 deletions(-) diff --git a/src/components/WatchFolder/mappingEntry/index.tsx b/src/components/WatchFolder/mappingEntry/index.tsx index ef6b199de..dd8d3a781 100644 --- a/src/components/WatchFolder/mappingEntry/index.tsx +++ b/src/components/WatchFolder/mappingEntry/index.tsx @@ -17,6 +17,10 @@ interface Iprops { export function MappingEntry({ mapping, handleRemoveMapping }: Iprops) { const appContext = React.useContext(AppContext); + const stopWatching = () => { + handleRemoveMapping(mapping); + }; + const confirmStopWatching = () => { appContext.setDialogMessage({ title: constants.STOP_WATCHING_FOLDER, @@ -26,7 +30,7 @@ export function MappingEntry({ mapping, handleRemoveMapping }: Iprops) { variant: 'primary', }, proceed: { - action: () => handleRemoveMapping(mapping), + action: stopWatching, text: constants.YES_STOP, variant: 'danger', }, diff --git a/src/components/WatchFolder/mappingEntryOptions.tsx b/src/components/WatchFolder/mappingEntryOptions.tsx index 10e9a7001..cd1b6af3c 100644 --- a/src/components/WatchFolder/mappingEntryOptions.tsx +++ b/src/components/WatchFolder/mappingEntryOptions.tsx @@ -1,55 +1,25 @@ -import { IconButton, Menu, MenuItem } from '@mui/material'; -import React, { useState } from 'react'; +import React from 'react'; import constants from 'utils/strings/constants'; import DoNotDisturbOutlinedIcon from '@mui/icons-material/DoNotDisturbOutlined'; import MoreHorizIcon from '@mui/icons-material/MoreHoriz'; +import OverflowMenu from 'components/OverflowMenu/menu'; +import { OverflowMenuOption } from 'components/OverflowMenu/option'; -export default function MappingEntryOptions({ confirmStopWatching }) { - const [anchorEl, setAnchorEl] = useState(null); - const handleClick = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }; - const handleClose = () => { - setAnchorEl(null); - }; +interface Iprops { + confirmStopWatching: () => void; +} - const open = Boolean(anchorEl); +export default function MappingEntryOptions({ confirmStopWatching }: Iprops) { return ( - <> - - - - - theme.palette.danger.main, - }}> - - - - {constants.STOP_WATCHING} - - - + }> + }> + {constants.STOP_WATCHING} + + ); } From c2424322680c771202e176e26b73a1bbe2637ba2 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 17 Aug 2022 13:28:56 +0530 Subject: [PATCH 103/295] remove syncProgressIcon and directly use Circular progress --- .../WatchFolder/mappingEntry/entryHeading.tsx | 7 +++---- .../WatchFolder/mappingEntry/syncProgressIcon.tsx | 13 ------------- 2 files changed, 3 insertions(+), 17 deletions(-) delete mode 100644 src/components/WatchFolder/mappingEntry/syncProgressIcon.tsx diff --git a/src/components/WatchFolder/mappingEntry/entryHeading.tsx b/src/components/WatchFolder/mappingEntry/entryHeading.tsx index d4386eb0b..333e71baf 100644 --- a/src/components/WatchFolder/mappingEntry/entryHeading.tsx +++ b/src/components/WatchFolder/mappingEntry/entryHeading.tsx @@ -1,8 +1,7 @@ import React, { useContext } from 'react'; -import { Typography } from '@mui/material'; +import { CircularProgress, Typography } from '@mui/material'; import watchFolderService from 'services/watchFolder/watchFolderService'; import { AppContext } from 'pages/_app'; -import { SyncProgressIcon } from './syncProgressIcon'; import { FlexWrapper } from 'components/Container'; import { WatchMapping } from 'types/watchFolder'; @@ -13,11 +12,11 @@ interface Iprops { export function EntryHeading({ mapping }: Iprops) { const appContext = useContext(AppContext); return ( - + {mapping.rootFolderName} {appContext.isFolderSyncRunning && watchFolderService.isMappingSyncing(mapping) && ( - + )} ); diff --git a/src/components/WatchFolder/mappingEntry/syncProgressIcon.tsx b/src/components/WatchFolder/mappingEntry/syncProgressIcon.tsx deleted file mode 100644 index d221a7fb7..000000000 --- a/src/components/WatchFolder/mappingEntry/syncProgressIcon.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import { CircularProgress } from '@mui/material'; - -export function SyncProgressIcon() { - return ( - - ); -} From 853fa414de3dda2d06a5e3800673f5b7b9dd2259 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 17 Aug 2022 13:30:17 +0530 Subject: [PATCH 104/295] rename function --- src/components/WatchFolder/mappingEntry/entryHeading.tsx | 2 +- src/services/watchFolder/watchFolderService.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/WatchFolder/mappingEntry/entryHeading.tsx b/src/components/WatchFolder/mappingEntry/entryHeading.tsx index 333e71baf..6d1912de7 100644 --- a/src/components/WatchFolder/mappingEntry/entryHeading.tsx +++ b/src/components/WatchFolder/mappingEntry/entryHeading.tsx @@ -15,7 +15,7 @@ export function EntryHeading({ mapping }: Iprops) { {mapping.rootFolderName} {appContext.isFolderSyncRunning && - watchFolderService.isMappingSyncing(mapping) && ( + watchFolderService.isMappingSyncInProgress(mapping) && ( )} diff --git a/src/services/watchFolder/watchFolderService.ts b/src/services/watchFolder/watchFolderService.ts index 030f322ea..c64296f4d 100644 --- a/src/services/watchFolder/watchFolderService.ts +++ b/src/services/watchFolder/watchFolderService.ts @@ -102,7 +102,7 @@ class watchFolderService { } } - isMappingSyncing(mapping: WatchMapping) { + isMappingSyncInProgress(mapping: WatchMapping) { return this.currentEvent?.folderPath === mapping.folderPath; } From abe5de2ec57a9432dacf0b891683e895933dea7b Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 17 Aug 2022 13:35:34 +0530 Subject: [PATCH 105/295] move mapping options inside mapping entry component directory --- .../WatchFolder/mappingEntry/index.tsx | 2 +- .../mappingEntry/mappingEntryOptions.tsx | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 src/components/WatchFolder/mappingEntry/mappingEntryOptions.tsx diff --git a/src/components/WatchFolder/mappingEntry/index.tsx b/src/components/WatchFolder/mappingEntry/index.tsx index dd8d3a781..5330c3828 100644 --- a/src/components/WatchFolder/mappingEntry/index.tsx +++ b/src/components/WatchFolder/mappingEntry/index.tsx @@ -6,7 +6,7 @@ import { WatchMapping } from 'types/watchFolder'; import { AppContext } from 'pages/_app'; import FolderOpenIcon from '@mui/icons-material/FolderOpen'; import constants from 'utils/strings/constants'; -import MappingEntryOptions from '../mappingEntryOptions'; +import MappingEntryOptions from './mappingEntryOptions'; import { EntryHeading } from './entryHeading'; interface Iprops { diff --git a/src/components/WatchFolder/mappingEntry/mappingEntryOptions.tsx b/src/components/WatchFolder/mappingEntry/mappingEntryOptions.tsx new file mode 100644 index 000000000..cd1b6af3c --- /dev/null +++ b/src/components/WatchFolder/mappingEntry/mappingEntryOptions.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import constants from 'utils/strings/constants'; +import DoNotDisturbOutlinedIcon from '@mui/icons-material/DoNotDisturbOutlined'; +import MoreHorizIcon from '@mui/icons-material/MoreHoriz'; +import OverflowMenu from 'components/OverflowMenu/menu'; +import { OverflowMenuOption } from 'components/OverflowMenu/option'; + +interface Iprops { + confirmStopWatching: () => void; +} + +export default function MappingEntryOptions({ confirmStopWatching }: Iprops) { + return ( + }> + }> + {constants.STOP_WATCHING} + + + ); +} From 93430df7cf9b6c4ec565810c08b1087c81dec296 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 17 Aug 2022 13:40:05 +0530 Subject: [PATCH 106/295] remove duplicate --- .../WatchFolder/mappingEntryOptions.tsx | 25 ------------------- 1 file changed, 25 deletions(-) delete mode 100644 src/components/WatchFolder/mappingEntryOptions.tsx diff --git a/src/components/WatchFolder/mappingEntryOptions.tsx b/src/components/WatchFolder/mappingEntryOptions.tsx deleted file mode 100644 index cd1b6af3c..000000000 --- a/src/components/WatchFolder/mappingEntryOptions.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; -import constants from 'utils/strings/constants'; -import DoNotDisturbOutlinedIcon from '@mui/icons-material/DoNotDisturbOutlined'; -import MoreHorizIcon from '@mui/icons-material/MoreHoriz'; -import OverflowMenu from 'components/OverflowMenu/menu'; -import { OverflowMenuOption } from 'components/OverflowMenu/option'; - -interface Iprops { - confirmStopWatching: () => void; -} - -export default function MappingEntryOptions({ confirmStopWatching }: Iprops) { - return ( - }> - }> - {constants.STOP_WATCHING} - - - ); -} From 6b353c6d7cd98a8a1511ab2efe70420df0cacf26 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 17 Aug 2022 13:49:24 +0530 Subject: [PATCH 107/295] refactor code --- src/components/WatchFolder/noMappingsContent.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/components/WatchFolder/noMappingsContent.tsx b/src/components/WatchFolder/noMappingsContent.tsx index 54eccf06b..eda61870c 100644 --- a/src/components/WatchFolder/noMappingsContent.tsx +++ b/src/components/WatchFolder/noMappingsContent.tsx @@ -13,11 +13,15 @@ export function NoMappingsContent() { {constants.FOLDERS_AUTOMATICALLY_MONITORED} - - {constants.UPLOAD_NEW_FILES_TO_ENTE} + + + {constants.UPLOAD_NEW_FILES_TO_ENTE} - - {constants.REMOVE_DELETED_FILES_FROM_ENTE} + + + + {constants.REMOVE_DELETED_FILES_FROM_ENTE} + ); From ee686d430b7e25adf42508169d87dd1de1269d5e Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 17 Aug 2022 13:58:22 +0530 Subject: [PATCH 108/295] refactor code --- src/components/WatchFolder/index.tsx | 16 ++++------------ .../{mappingList.tsx => mappingList/index.tsx} | 9 ++++++--- .../noMappingsContent}/checkmarkIcon.tsx | 0 .../noMappingsContent}/noMappingsContent.tsx | 2 +- 4 files changed, 11 insertions(+), 16 deletions(-) rename src/components/WatchFolder/{mappingList.tsx => mappingList/index.tsx} (71%) rename src/components/WatchFolder/{ => mappingList/noMappingsContent}/checkmarkIcon.tsx (100%) rename src/components/WatchFolder/{ => mappingList/noMappingsContent}/noMappingsContent.tsx (94%) diff --git a/src/components/WatchFolder/index.tsx b/src/components/WatchFolder/index.tsx index 227672c11..b16c1ecc1 100644 --- a/src/components/WatchFolder/index.tsx +++ b/src/components/WatchFolder/index.tsx @@ -1,5 +1,4 @@ import { MappingList } from './mappingList'; -import { NoMappingsContent } from './noMappingsContent'; import React, { useContext, useEffect, useState } from 'react'; import { Button, Dialog, DialogContent, Stack } from '@mui/material'; import watchFolderService from 'services/watchFolder/watchFolderService'; @@ -98,17 +97,10 @@ export default function WatchFolder({ open, onClose }: Iprops) { - {mappings.length === 0 ? ( - - ) : ( - - )} - + + + + )} diff --git a/src/components/Upload/UploadButton.tsx b/src/components/Upload/UploadButton.tsx index 26454e286..916b99e05 100644 --- a/src/components/Upload/UploadButton.tsx +++ b/src/components/Upload/UploadButton.tsx @@ -3,6 +3,7 @@ import { IconButton, styled } from '@mui/material'; import FileUploadOutlinedIcon from '@mui/icons-material/FileUploadOutlined'; import { Button } from '@mui/material'; import constants from 'utils/strings/constants'; +import uploadManager from 'services/upload/uploadManager'; const Wrapper = styled('div')` display: flex; @@ -28,14 +29,23 @@ interface Iprops { } function UploadButton({ openUploader }: Iprops) { return ( - + - + + diff --git a/src/pages/gallery/index.tsx b/src/pages/gallery/index.tsx index 23e02179b..2645c7609 100644 --- a/src/pages/gallery/index.tsx +++ b/src/pages/gallery/index.tsx @@ -564,9 +564,7 @@ export default function Gallery() { }; const openUploader = () => { - if (!uploadInProgress) { - setUploadTypeSelectorView(true); - } + setUploadTypeSelectorView(true); }; return ( diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index 686eaa4d8..1a4f3ff59 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -545,6 +545,10 @@ class UploadManager { const updatedFile = await appendNewFilePath(decryptedFile, filePath); await updateFileMagicMetadata([updatedFile]); } + + public shouldAllowNewUpload = () => { + return !this.uploadInProgress || watchFolderService.isUploadRunning(); + }; } export default new UploadManager(); From 73c94d139ef672767a5cf13a64e4cfc4c2bde6e3 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sat, 3 Sep 2022 17:40:33 +0530 Subject: [PATCH 196/295] convert console logs to addlogLine --- src/services/watchFolder/watchFolderEventHandlers.ts | 8 ++++---- src/services/watchFolder/watchFolderService.ts | 10 +++++++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/services/watchFolder/watchFolderEventHandlers.ts b/src/services/watchFolder/watchFolderEventHandlers.ts index cf3c80dd1..820acf3bc 100644 --- a/src/services/watchFolder/watchFolderEventHandlers.ts +++ b/src/services/watchFolder/watchFolderEventHandlers.ts @@ -1,5 +1,6 @@ import { ElectronFile } from 'types/upload'; import { EventQueueItem } from 'types/watchFolder'; +import { addLogLine } from 'utils/logging'; import { logError } from 'utils/sentry'; import watchFolderService from './watchFolderService'; @@ -14,8 +15,6 @@ export async function diskFileAddedCallback(file: ElectronFile) { const { collectionName, folderPath } = collectionNameAndFolderPath; - console.log('added (upload) to event queue', collectionName, file); - const event: EventQueueItem = { type: 'upload', collectionName, @@ -23,6 +22,7 @@ export async function diskFileAddedCallback(file: ElectronFile) { files: [file], }; watchFolderService.pushEvent(event); + addLogLine(`added (upload) to event queue, ${JSON.stringify(event)}`); } catch (e) { logError(e, 'error while calling diskFileAddedCallback'); } @@ -39,8 +39,6 @@ export async function diskFileRemovedCallback(filePath: string) { const { collectionName, folderPath } = collectionNameAndFolderPath; - console.log('added (trash) to event queue', collectionName, filePath); - const event: EventQueueItem = { type: 'trash', collectionName, @@ -48,6 +46,7 @@ export async function diskFileRemovedCallback(filePath: string) { paths: [filePath], }; watchFolderService.pushEvent(event); + addLogLine(`added (trash) to event queue, ${JSON.stringify(event)}`); } catch (e) { logError(e, 'error while calling diskFileRemovedCallback'); } @@ -66,6 +65,7 @@ export async function diskFolderRemovedCallback(folderPath: string) { if (mappedFolderPath === folderPath) { watchFolderService.pushTrashedDir(folderPath); } + addLogLine(`added trashedDir, ${folderPath}`); } catch (e) { logError(e, 'error while calling diskFolderRemovedCallback'); } diff --git a/src/services/watchFolder/watchFolderService.ts b/src/services/watchFolder/watchFolderService.ts index 94b6d941a..255cd3353 100644 --- a/src/services/watchFolder/watchFolderService.ts +++ b/src/services/watchFolder/watchFolderService.ts @@ -19,6 +19,7 @@ import { import { getParentFolderName } from './utils'; import { UPLOAD_STRATEGY } from 'constants/upload'; import uploadManager from 'services/upload/uploadManager'; +import { addLogLine } from 'utils/logging'; class watchFolderService { private ElectronAPIs: ElectronAPIs; @@ -74,7 +75,7 @@ class watchFolderService { try { let mappings = this.getWatchMappings(); - console.log('mappings', mappings); + addLogLine(`mappings, ${mappings.map((m) => JSON.stringify(m))}`); if (!mappings?.length) { return; @@ -237,7 +238,10 @@ class watchFolderService { } private async runNextEvent() { - console.log('mappings', this.getWatchMappings()); + addLogLine( + `mappings, + ${this.getWatchMappings().map((m) => JSON.stringify(m))}` + ); if ( this.eventQueue.length === 0 || @@ -250,7 +254,7 @@ class watchFolderService { this.setIsEventRunning(true); const event = this.clubSameCollectionEvents(); this.currentEvent = event; - console.log('running event', event); + addLogLine(`running event', ${JSON.stringify(event)}`); if (event.type === 'upload') { this.processUploadEvent(); } else { From 9a12deebb02aa6cd3d082a6020ba21620200cd45 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sat, 3 Sep 2022 17:42:01 +0530 Subject: [PATCH 197/295] don't update File Path --- src/services/upload/uploadManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index 1a4f3ff59..e33eef96c 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -468,7 +468,6 @@ class UploadManager { ].includes(fileUploadResult) ) { this.updateExistingFiles(decryptedFile); - await this.updateFilePaths(decryptedFile, fileWithCollection); await this.watchFolderCallback( fileWithCollection, uploadedFile @@ -534,6 +533,7 @@ class UploadManager { } } + // not used private async updateFilePaths( decryptedFile: EnteFile, fileWithCollection: FileWithCollection From faac1e6f51eb5d83d2d50b7286dc0c4ee123e805 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sat, 3 Sep 2022 18:01:44 +0530 Subject: [PATCH 198/295] moved watch folder to top of utility section --- src/components/Sidebar/UtilitySection.tsx | 7 +++---- src/utils/strings/englishConstants.tsx | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/Sidebar/UtilitySection.tsx b/src/components/Sidebar/UtilitySection.tsx index 53a90e492..cb55ca95d 100644 --- a/src/components/Sidebar/UtilitySection.tsx +++ b/src/components/Sidebar/UtilitySection.tsx @@ -62,6 +62,9 @@ export default function UtilitySection({ closeSidebar }) { return ( <> + + {constants.WATCH_FOLDERS} + {constants.RECOVERY_KEY} @@ -77,10 +80,6 @@ export default function UtilitySection({ closeSidebar }) { {constants.DEDUPLICATE_FILES} - - {constants.WATCH_FOLDERS} - - {/* {constants.COMPRESS_THUMBNAILS} */} diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index d0e19568d..694024b4f 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -739,7 +739,7 @@ const englishConstants = {

), - WATCH_FOLDERS: 'Watch Folders', + WATCH_FOLDERS: 'Watch folders', UPGRADE_NOW: 'Upgrade now', RENEW_NOW: 'Renew now', STORAGE: 'Storage', From 5628fbbb816670e64e2df96001b98553a2d2de70 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sat, 3 Sep 2022 18:02:29 +0530 Subject: [PATCH 199/295] fix casing --- src/utils/strings/englishConstants.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index 694024b4f..a0fdaa229 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -748,7 +748,7 @@ const englishConstants = { FAMILY: 'Family', FREE: 'free', OF: 'of', - WATCHED_FOLDERS: 'Watched Folders', + WATCHED_FOLDERS: 'Watched folders', NO_FOLDERS_ADDED: 'No folders added yet!', FOLDERS_AUTOMATICALLY_MONITORED: 'The folders you add here will monitored to automatically', From 1fb2d10ccec52d11f3c0fac7c43c74bbe221ed1c Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sat, 3 Sep 2022 18:03:39 +0530 Subject: [PATCH 200/295] update max width --- src/components/WatchFolder/index.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/WatchFolder/index.tsx b/src/components/WatchFolder/index.tsx index 89f944811..3a86f3b8c 100644 --- a/src/components/WatchFolder/index.tsx +++ b/src/components/WatchFolder/index.tsx @@ -105,10 +105,9 @@ export default function WatchFolder({ open, onClose }: Iprops) { return ( <> + PaperProps={{ sx: { height: '450px', maxWidth: '432px' } }}> From 75e28d0a376c98253ed1ee701b6ab63eb4b52072 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sat, 3 Sep 2022 18:05:11 +0530 Subject: [PATCH 201/295] made cancel button secondary --- src/components/WatchFolder/mappingEntry/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/WatchFolder/mappingEntry/index.tsx b/src/components/WatchFolder/mappingEntry/index.tsx index 5e8749aa0..ec8e360b8 100644 --- a/src/components/WatchFolder/mappingEntry/index.tsx +++ b/src/components/WatchFolder/mappingEntry/index.tsx @@ -29,7 +29,7 @@ export function MappingEntry({ mapping, handleRemoveMapping }: Iprops) { content: constants.STOP_WATCHING_DIALOG_MESSAGE, close: { text: constants.CANCEL, - variant: 'primary', + variant: 'secondary', }, proceed: { action: stopWatching, From bcb0fe7ed8d199bafc570c87d0f341668ee4a448 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sat, 3 Sep 2022 18:26:05 +0530 Subject: [PATCH 202/295] make ui changes --- .../noMappingsContent/checkmarkIcon.tsx | 5 +++- .../noMappingsContent/noMappingsContent.tsx | 29 ++++++++++--------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/components/WatchFolder/mappingList/noMappingsContent/checkmarkIcon.tsx b/src/components/WatchFolder/mappingList/noMappingsContent/checkmarkIcon.tsx index 10b4f0459..631736329 100644 --- a/src/components/WatchFolder/mappingList/noMappingsContent/checkmarkIcon.tsx +++ b/src/components/WatchFolder/mappingList/noMappingsContent/checkmarkIcon.tsx @@ -4,8 +4,11 @@ import CheckIcon from '@mui/icons-material/Check'; export function CheckmarkIcon() { return ( theme.palette.secondary.main, }} /> diff --git a/src/components/WatchFolder/mappingList/noMappingsContent/noMappingsContent.tsx b/src/components/WatchFolder/mappingList/noMappingsContent/noMappingsContent.tsx index 85a4a8749..335057465 100644 --- a/src/components/WatchFolder/mappingList/noMappingsContent/noMappingsContent.tsx +++ b/src/components/WatchFolder/mappingList/noMappingsContent/noMappingsContent.tsx @@ -1,28 +1,31 @@ import { Typography } from '@mui/material'; -import { FlexWrapper } from 'components/Container'; import React from 'react'; import constants from 'utils/strings/constants'; -import { CheckmarkIcon } from './checkmarkIcon'; import { NoMappingsContainer } from '../../styledComponents'; +import { FlexWrapper } from 'components/Container'; +import { CheckmarkIcon } from './checkmarkIcon'; + export function NoMappingsContent() { return ( - + {constants.NO_FOLDERS_ADDED} - + {constants.FOLDERS_AUTOMATICALLY_MONITORED} - - - {constants.UPLOAD_NEW_FILES_TO_ENTE} - - - - + + + + {constants.UPLOAD_NEW_FILES_TO_ENTE} + + + + + {constants.REMOVE_DELETED_FILES_FROM_ENTE} - - +
+ ); } From ea2d142d60b2a61c7a337e9d87e4c258448bb4ac Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sat, 3 Sep 2022 18:47:47 +0530 Subject: [PATCH 203/295] fix sizing and layout --- src/components/WatchFolder/index.tsx | 2 +- src/components/WatchFolder/styledComponents.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/WatchFolder/index.tsx b/src/components/WatchFolder/index.tsx index 3a86f3b8c..ff5ca59aa 100644 --- a/src/components/WatchFolder/index.tsx +++ b/src/components/WatchFolder/index.tsx @@ -107,7 +107,7 @@ export default function WatchFolder({ open, onClose }: Iprops) { + PaperProps={{ sx: { height: '448px', maxWidth: '414px' } }}> diff --git a/src/components/WatchFolder/styledComponents.tsx b/src/components/WatchFolder/styledComponents.tsx index 5b4427beb..3e6d595c1 100644 --- a/src/components/WatchFolder/styledComponents.tsx +++ b/src/components/WatchFolder/styledComponents.tsx @@ -17,6 +17,7 @@ export const MappingsContainer = styled(Box)(({ theme }) => ({ export const NoMappingsContainer = styled(VerticallyCentered)({ textAlign: 'left', alignItems: 'flex-start', + marginBottom: '32px', }); export const EntryContainer = styled(Box)({ From 1e433594142b9cf1642709de39f61187195c9633 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sat, 3 Sep 2022 18:54:41 +0530 Subject: [PATCH 204/295] make overflow menu background to over paper --- .../WatchFolder/mappingEntry/mappingEntryOptions.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/WatchFolder/mappingEntry/mappingEntryOptions.tsx b/src/components/WatchFolder/mappingEntry/mappingEntryOptions.tsx index cd1b6af3c..8189a4c3d 100644 --- a/src/components/WatchFolder/mappingEntry/mappingEntryOptions.tsx +++ b/src/components/WatchFolder/mappingEntry/mappingEntryOptions.tsx @@ -12,6 +12,12 @@ interface Iprops { export default function MappingEntryOptions({ confirmStopWatching }: Iprops) { return ( + theme.palette.background.overPaper, + }, + }} ariaControls={'watch-mapping-option'} triggerButtonIcon={}> Date: Sat, 3 Sep 2022 19:30:07 +0530 Subject: [PATCH 205/295] fix already upload file not getting added as synced file --- src/services/upload/uploadManager.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index e33eef96c..6ebd4a319 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -468,6 +468,15 @@ class UploadManager { ].includes(fileUploadResult) ) { this.updateExistingFiles(decryptedFile); + } + if ( + [ + UPLOAD_RESULT.ADDED_SYMLINK, + UPLOAD_RESULT.UPLOADED, + UPLOAD_RESULT.UPLOADED_WITH_STATIC_THUMBNAIL, + UPLOAD_RESULT.ALREADY_UPLOADED, + ].includes(fileUploadResult) + ) { await this.watchFolderCallback( fileWithCollection, uploadedFile From 88d265409572031dacb7ad1530595844c7e3004a Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sat, 3 Sep 2022 19:30:44 +0530 Subject: [PATCH 206/295] add local log --- .../watchFolder/watchFolderService.ts | 40 ++++++++++++++----- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/services/watchFolder/watchFolderService.ts b/src/services/watchFolder/watchFolderService.ts index 255cd3353..17f97bd39 100644 --- a/src/services/watchFolder/watchFolderService.ts +++ b/src/services/watchFolder/watchFolderService.ts @@ -19,7 +19,7 @@ import { import { getParentFolderName } from './utils'; import { UPLOAD_STRATEGY } from 'constants/upload'; import uploadManager from 'services/upload/uploadManager'; -import { addLogLine } from 'utils/logging'; +import { addLocalLog, addLogLine } from 'utils/logging'; class watchFolderService { private ElectronAPIs: ElectronAPIs; @@ -276,6 +276,7 @@ class watchFolderService { } async onFileUpload(fileWithCollection: FileWithCollection, file: EnteFile) { + addLocalLog(() => `onFileUpload called`); if (!this.isUploadRunning) { return; } @@ -302,10 +303,17 @@ class watchFolderService { ) { if (this.allElectronAPIsExist) { try { + addLocalLog( + () => + `allFileUploadsDone,${JSON.stringify( + filesWithCollection + )} ${JSON.stringify(collections)}` + ); const collection = collections.find( (collection) => collection.id === filesWithCollection[0].collectionID ); + addLocalLog(() => `got collection ${!!collection}`); if ( !this.isEventRunning || this.currentEvent.collectionName !== collection?.name @@ -319,6 +327,10 @@ class watchFolderService { this.handleUploadedFile(fileWithCollection, uploadedFiles); } + addLocalLog( + () => `uploadedFiles ${JSON.stringify(uploadedFiles)}` + ); + if (uploadedFiles.length > 0) { const mappings = this.getWatchMappings(); const mapping = mappings.find( @@ -363,15 +375,22 @@ class watchFolderService { this.pathToIDMap.has(imagePath) && this.pathToIDMap.has(videoPath) ) { - uploadedFiles.push({ + const imageFile = { path: imagePath, id: this.pathToIDMap.get(imagePath), - }); - uploadedFiles.push({ + }; + const videoFile = { path: videoPath, id: this.pathToIDMap.get(videoPath), - }); - + }; + uploadedFiles.push(imageFile); + uploadedFiles.push(videoFile); + addLocalLog( + () => + `added image ${JSON.stringify( + imageFile + )} and video file ${JSON.stringify(videoFile)}` + ); this.pathToIDMap.delete(imagePath); this.pathToIDMap.delete(videoPath); } @@ -379,11 +398,12 @@ class watchFolderService { const filePath = (fileWithCollection.file as ElectronFile).path; if (this.pathToIDMap.has(filePath)) { - uploadedFiles.push({ + const file = { path: filePath, id: this.pathToIDMap.get(filePath), - }); - + }; + uploadedFiles.push(file); + addLocalLog(() => `added file ${JSON.stringify(file)} `); this.pathToIDMap.delete(filePath); } } @@ -480,7 +500,7 @@ class watchFolderService { ); if (!mapping) { - return null; + throw Error(`no mapping found for ${filePath}`); } return { From f8b0706d81c886771b05754abeb9217a0593e7cf Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sat, 3 Sep 2022 20:38:25 +0530 Subject: [PATCH 207/295] add files that can't be uploaded to ignored files --- src/services/upload/uploadManager.ts | 20 +- .../watchFolder/watchFolderService.ts | 242 ++++++++++++------ src/types/electron/index.ts | 8 +- src/types/watchFolder/index.ts | 11 +- 4 files changed, 177 insertions(+), 104 deletions(-) diff --git a/src/services/upload/uploadManager.ts b/src/services/upload/uploadManager.ts index 6ebd4a319..e4c50c6d4 100644 --- a/src/services/upload/uploadManager.ts +++ b/src/services/upload/uploadManager.ts @@ -469,19 +469,11 @@ class UploadManager { ) { this.updateExistingFiles(decryptedFile); } - if ( - [ - UPLOAD_RESULT.ADDED_SYMLINK, - UPLOAD_RESULT.UPLOADED, - UPLOAD_RESULT.UPLOADED_WITH_STATIC_THUMBNAIL, - UPLOAD_RESULT.ALREADY_UPLOADED, - ].includes(fileUploadResult) - ) { - await this.watchFolderCallback( - fileWithCollection, - uploadedFile - ); - } + await this.watchFolderCallback( + fileUploadResult, + fileWithCollection, + uploadedFile + ); return fileUploadResult; } catch (e) { logError(e, 'failed to do post file upload action'); @@ -494,11 +486,13 @@ class UploadManager { } private async watchFolderCallback( + fileUploadResult: UPLOAD_RESULT, fileWithCollection: FileWithCollection, uploadedFile: EnteFile ) { if (isElectron()) { await watchFolderService.onFileUpload( + fileUploadResult, fileWithCollection, uploadedFile ); diff --git a/src/services/watchFolder/watchFolderService.ts b/src/services/watchFolder/watchFolderService.ts index 17f97bd39..2d9ac7ae1 100644 --- a/src/services/watchFolder/watchFolderService.ts +++ b/src/services/watchFolder/watchFolderService.ts @@ -17,7 +17,7 @@ import { diskFolderRemovedCallback, } from './watchFolderEventHandlers'; import { getParentFolderName } from './utils'; -import { UPLOAD_STRATEGY } from 'constants/upload'; +import { UPLOAD_RESULT, UPLOAD_STRATEGY } from 'constants/upload'; import uploadManager from 'services/upload/uploadManager'; import { addLocalLog, addLogLine } from 'utils/logging'; @@ -26,10 +26,12 @@ class watchFolderService { private allElectronAPIsExist: boolean = false; private eventQueue: EventQueueItem[] = []; private currentEvent: EventQueueItem; + private currentlySyncedMapping: WatchMapping; private trashingDirQueue: string[] = []; private isEventRunning: boolean = false; private uploadRunning: boolean = false; - private pathToIDMap = new Map(); + private filePathToUploadedFileIDMap = new Map(); + private unUploadableFilePaths = new Set(); private isPaused = false; private setElectronFiles: (files: ElectronFile[]) => void; private setCollectionName: (collectionName: string) => void; @@ -108,8 +110,10 @@ class watchFolderService { filesOnDisk: ElectronFile[] ) { const filesToUpload = filesOnDisk.filter((electronFile) => { - return !mapping.files.find( - (file) => file.path === electronFile.path + return ( + !mapping.syncedFiles.find( + (file) => file.path === electronFile.path + ) && !mapping.ignoredFiles.includes(electronFile.path) ); }); @@ -133,7 +137,7 @@ class watchFolderService { mapping: WatchMapping, filesOnDisk: ElectronFile[] ) { - const filesToRemove = mapping.files.filter((file) => { + const filesToRemove = mapping.syncedFiles.filter((file) => { return !filesOnDisk.find( (electronFile) => electronFile.path === file.path ); @@ -238,27 +242,40 @@ class watchFolderService { } private async runNextEvent() { - addLogLine( - `mappings, + try { + addLogLine( + `mappings, ${this.getWatchMappings().map((m) => JSON.stringify(m))}` - ); + ); - if ( - this.eventQueue.length === 0 || - this.isEventRunning || - this.isPaused - ) { - return; - } + if ( + this.eventQueue.length === 0 || + this.isEventRunning || + this.isPaused + ) { + return; + } - this.setIsEventRunning(true); - const event = this.clubSameCollectionEvents(); - this.currentEvent = event; - addLogLine(`running event', ${JSON.stringify(event)}`); - if (event.type === 'upload') { - this.processUploadEvent(); - } else { - await this.processTrashEvent(); + this.setIsEventRunning(true); + const event = this.clubSameCollectionEvents(); + this.currentEvent = event; + const mappings = this.getWatchMappings(); + const mapping = mappings.find( + (mapping) => mapping.folderPath === this.currentEvent.folderPath + ); + if (!mapping) { + throw Error('no Mapping found for event'); + } + this.currentlySyncedMapping = mapping; + addLogLine(`running event', ${JSON.stringify(event)}`); + if (event.type === 'upload') { + this.processUploadEvent(); + } else { + await this.processTrashEvent(); + } + } catch (e) { + logError(e, 'runNextEvent failed'); + } finally { this.setIsEventRunning(false); this.runNextEvent(); } @@ -275,25 +292,59 @@ class watchFolderService { } } - async onFileUpload(fileWithCollection: FileWithCollection, file: EnteFile) { + async onFileUpload( + fileUploadResult: UPLOAD_RESULT, + fileWithCollection: FileWithCollection, + file: EnteFile + ) { addLocalLog(() => `onFileUpload called`); if (!this.isUploadRunning) { return; } - if (fileWithCollection.isLivePhoto) { - this.pathToIDMap.set( - (fileWithCollection.livePhotoAssets.image as ElectronFile).path, - file.id - ); - this.pathToIDMap.set( - (fileWithCollection.livePhotoAssets.video as ElectronFile).path, - file.id - ); - } else { - this.pathToIDMap.set( - (fileWithCollection.file as ElectronFile).path, - file.id - ); + if ( + [ + UPLOAD_RESULT.ADDED_SYMLINK, + UPLOAD_RESULT.UPLOADED, + UPLOAD_RESULT.UPLOADED_WITH_STATIC_THUMBNAIL, + UPLOAD_RESULT.ALREADY_UPLOADED, + ].includes(fileUploadResult) + ) { + if (fileWithCollection.isLivePhoto) { + this.filePathToUploadedFileIDMap.set( + (fileWithCollection.livePhotoAssets.image as ElectronFile) + .path, + file.id + ); + this.filePathToUploadedFileIDMap.set( + (fileWithCollection.livePhotoAssets.video as ElectronFile) + .path, + file.id + ); + } else { + this.filePathToUploadedFileIDMap.set( + (fileWithCollection.file as ElectronFile).path, + file.id + ); + } + } else if ( + [UPLOAD_RESULT.UNSUPPORTED, UPLOAD_RESULT.TOO_LARGE].includes( + fileUploadResult + ) + ) { + if (fileWithCollection.isLivePhoto) { + this.unUploadableFilePaths.add( + (fileWithCollection.livePhotoAssets.image as ElectronFile) + .path + ); + this.unUploadableFilePaths.add( + (fileWithCollection.livePhotoAssets.video as ElectronFile) + .path + ); + } else { + this.unUploadableFilePaths.add( + (fileWithCollection.file as ElectronFile).path + ); + } } } @@ -321,29 +372,41 @@ class watchFolderService { return; } - const uploadedFiles: WatchMapping['files'] = []; + const syncedFiles: WatchMapping['syncedFiles'] = []; + const ignoredFiles: WatchMapping['ignoredFiles'] = []; for (const fileWithCollection of filesWithCollection) { - this.handleUploadedFile(fileWithCollection, uploadedFiles); + this.handleUploadedFile( + fileWithCollection, + syncedFiles, + ignoredFiles + ); } + addLocalLog(() => `syncedFiles ${JSON.stringify(syncedFiles)}`); addLocalLog( - () => `uploadedFiles ${JSON.stringify(uploadedFiles)}` + () => `ignoredFiles ${JSON.stringify(ignoredFiles)}` ); - if (uploadedFiles.length > 0) { - const mappings = this.getWatchMappings(); - const mapping = mappings.find( - (mapping) => - mapping.folderPath === this.currentEvent.folderPath + if (syncedFiles.length > 0) { + this.currentlySyncedMapping.syncedFiles = [ + ...this.currentlySyncedMapping.syncedFiles, + ...syncedFiles, + ]; + this.ElectronAPIs.updateWatchMappingSyncedFiles( + this.currentlySyncedMapping.folderPath, + this.currentlySyncedMapping.syncedFiles + ); + } + if (ignoredFiles.length > 0) { + this.currentlySyncedMapping.ignoredFiles = [ + ...this.currentlySyncedMapping.ignoredFiles, + ...ignoredFiles, + ]; + this.ElectronAPIs.updateWatchMappingIgnoredFiles( + this.currentlySyncedMapping.folderPath, + this.currentlySyncedMapping.ignoredFiles ); - if (mapping) { - mapping.files = [...mapping.files, ...uploadedFiles]; - this.ElectronAPIs.updateWatchMappingFiles( - mapping.folderPath, - mapping.files - ); - } } this.runPostUploadsAction(); @@ -361,7 +424,8 @@ class watchFolderService { private handleUploadedFile( fileWithCollection: FileWithCollection, - uploadedFiles: { path: string; id: number }[] + syncedFiles: { path: string; id: number }[], + ignoredFiles: string[] ) { if (fileWithCollection.isLivePhoto) { const imagePath = ( @@ -372,40 +436,55 @@ class watchFolderService { ).path; if ( - this.pathToIDMap.has(imagePath) && - this.pathToIDMap.has(videoPath) + this.filePathToUploadedFileIDMap.has(imagePath) && + this.filePathToUploadedFileIDMap.has(videoPath) ) { const imageFile = { path: imagePath, - id: this.pathToIDMap.get(imagePath), + id: this.filePathToUploadedFileIDMap.get(imagePath), }; const videoFile = { path: videoPath, - id: this.pathToIDMap.get(videoPath), + id: this.filePathToUploadedFileIDMap.get(videoPath), }; - uploadedFiles.push(imageFile); - uploadedFiles.push(videoFile); + syncedFiles.push(imageFile); + syncedFiles.push(videoFile); addLocalLog( () => `added image ${JSON.stringify( imageFile - )} and video file ${JSON.stringify(videoFile)}` + )} and video file ${JSON.stringify( + videoFile + )} to uploadedFiles` + ); + } else if ( + this.unUploadableFilePaths.has(imagePath) && + this.unUploadableFilePaths.has(videoPath) + ) { + ignoredFiles.push(imagePath); + ignoredFiles.push(videoPath); + addLocalLog( + () => + `added image ${imagePath} and video file ${videoPath} to rejectedFiles` ); - this.pathToIDMap.delete(imagePath); - this.pathToIDMap.delete(videoPath); } + this.filePathToUploadedFileIDMap.delete(imagePath); + this.filePathToUploadedFileIDMap.delete(videoPath); } else { const filePath = (fileWithCollection.file as ElectronFile).path; - if (this.pathToIDMap.has(filePath)) { + if (this.filePathToUploadedFileIDMap.has(filePath)) { const file = { path: filePath, - id: this.pathToIDMap.get(filePath), + id: this.filePathToUploadedFileIDMap.get(filePath), }; - uploadedFiles.push(file); + syncedFiles.push(file); addLocalLog(() => `added file ${JSON.stringify(file)} `); - this.pathToIDMap.delete(filePath); + } else if (this.unUploadableFilePaths.has(filePath)) { + ignoredFiles.push(filePath); + addLocalLog(() => `added file ${filePath} to rejectedFiles`); } + this.filePathToUploadedFileIDMap.delete(filePath); } } @@ -415,29 +494,22 @@ class watchFolderService { return; } - const { collectionName, folderPath, paths } = this.currentEvent; + const { collectionName, paths } = this.currentEvent; const filePathsToRemove = new Set(paths); - const mappings = this.getWatchMappings(); - const mappingIdx = mappings.findIndex( - (mapping) => mapping.folderPath === folderPath - ); - if (mappingIdx === -1) { - return; - } - - const files = mappings[mappingIdx].files.filter((file) => - filePathsToRemove.has(file.path) + const files = this.currentlySyncedMapping.syncedFiles.filter( + (file) => filePathsToRemove.has(file.path) ); await this.trashByIDs(files, collectionName); - mappings[mappingIdx].files = mappings[mappingIdx].files.filter( - (file) => !filePathsToRemove.has(file.path) - ); - this.ElectronAPIs.updateWatchMappingFiles( - mappings[mappingIdx].folderPath, - mappings[mappingIdx].files + this.currentlySyncedMapping.syncedFiles = + this.currentlySyncedMapping.syncedFiles.filter( + (file) => !filePathsToRemove.has(file.path) + ); + this.ElectronAPIs.updateWatchMappingSyncedFiles( + this.currentlySyncedMapping.folderPath, + this.currentlySyncedMapping.syncedFiles ); } catch (e) { logError(e, 'error while running next trash'); @@ -445,7 +517,7 @@ class watchFolderService { } private async trashByIDs( - toTrashFiles: WatchMapping['files'], + toTrashFiles: WatchMapping['syncedFiles'], collectionName: string ) { try { diff --git a/src/types/electron/index.ts b/src/types/electron/index.ts index 59615347b..3bb4f59bc 100644 --- a/src/types/electron/index.ts +++ b/src/types/electron/index.ts @@ -37,9 +37,13 @@ export interface ElectronAPIs { setToUploadCollection: (collectionName: string) => void; getDirFiles: (dirPath: string) => Promise; getWatchMappings: () => WatchMapping[]; - updateWatchMappingFiles: ( + updateWatchMappingSyncedFiles: ( folderPath: string, - files: WatchMapping['files'] + files: WatchMapping['syncedFiles'] + ) => void; + updateWatchMappingIgnoredFiles: ( + folderPath: string, + files: WatchMapping['ignoredFiles'] ) => void; addWatchMapping: ( collectionName: string, diff --git a/src/types/watchFolder/index.ts b/src/types/watchFolder/index.ts index 97fa96dce..d191d7358 100644 --- a/src/types/watchFolder/index.ts +++ b/src/types/watchFolder/index.ts @@ -1,14 +1,17 @@ import { UPLOAD_STRATEGY } from 'constants/upload'; import { ElectronFile } from 'types/upload'; +interface WatchMappingSyncedFile { + path: string; + id: number; +} + export interface WatchMapping { rootFolderName: string; folderPath: string; uploadStrategy: UPLOAD_STRATEGY; - files: { - path: string; - id: number; - }[]; + syncedFiles: WatchMappingSyncedFile[]; + ignoredFiles: string[]; } export interface EventQueueItem { From 513c7cf167dd23c9ba8ef74e8be7583175218638 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sat, 3 Sep 2022 21:01:05 +0530 Subject: [PATCH 208/295] fix uploadDone not runnign --- src/services/watchFolder/watchFolderService.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/services/watchFolder/watchFolderService.ts b/src/services/watchFolder/watchFolderService.ts index 2d9ac7ae1..df4c8d294 100644 --- a/src/services/watchFolder/watchFolderService.ts +++ b/src/services/watchFolder/watchFolderService.ts @@ -272,12 +272,11 @@ class watchFolderService { this.processUploadEvent(); } else { await this.processTrashEvent(); + this.setIsEventRunning(false); + this.runNextEvent(); } } catch (e) { logError(e, 'runNextEvent failed'); - } finally { - this.setIsEventRunning(false); - this.runNextEvent(); } } @@ -365,6 +364,10 @@ class watchFolderService { collection.id === filesWithCollection[0].collectionID ); addLocalLog(() => `got collection ${!!collection}`); + addLocalLog( + () => + `${this.isEventRunning} ${this.currentEvent.collectionName} ${collection?.name}` + ); if ( !this.isEventRunning || this.currentEvent.collectionName !== collection?.name From 8aab1cbd0466895313e5fc3c8d5130b27aac47fd Mon Sep 17 00:00:00 2001 From: Abhinav Date: Mon, 5 Sep 2022 19:29:03 +0530 Subject: [PATCH 209/295] fix waitAndRun and better name --- src/components/Upload/Uploader.tsx | 16 +++++++++------- src/pages/gallery/index.tsx | 6 +++--- src/utils/common/index.ts | 7 +++++-- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/components/Upload/Uploader.tsx b/src/components/Upload/Uploader.tsx index 7b16ff456..b0739b84f 100644 --- a/src/components/Upload/Uploader.tsx +++ b/src/components/Upload/Uploader.tsx @@ -57,7 +57,7 @@ interface Props { setCollectionSelectorAttributes: SetCollectionSelectorAttributes; setCollectionNamerAttributes: SetCollectionNamerAttributes; setLoading: SetLoading; - setUploadInProgress: (value: boolean) => void; + setShouldDisableDropzone: (value: boolean) => void; showCollectionSelector: () => void; setFiles: SetFiles; setCollections: SetCollections; @@ -100,7 +100,7 @@ export default function Uploader(props: Props) { // This is set when the user choses a type to upload from the upload type selector dialog const pickedUploadType = useRef(null); const zipPaths = useRef(null); - const previousUploadPromise = useRef>(null); + const currentUploadPromise = useRef>(null); const [electronFiles, setElectronFiles] = useState(null); const [webFiles, setWebFiles] = useState([]); @@ -327,9 +327,11 @@ export default function Uploader(props: Props) { filesWithCollectionToUploadIn: FileWithCollection[], collections: Collection[] ) => { - const currentPromise = previousUploadPromise.current; - previousUploadPromise.current = waitAndRun(currentPromise, () => - uploadFiles(filesWithCollectionToUploadIn, collections) + const currentPromise = currentUploadPromise.current; + currentUploadPromise.current = waitAndRun( + currentPromise, + async () => + await uploadFiles(filesWithCollectionToUploadIn, collections) ); }; @@ -338,12 +340,12 @@ export default function Uploader(props: Props) { props.closeUploadTypeSelector(); uploadManager.prepareForNewUpload(); setUploadProgressView(true); - props.setUploadInProgress(true); + props.setShouldDisableDropzone(!uploadManager.shouldAllowNewUpload()); await props.syncWithRemote(true, true); }; function postUploadAction() { - props.setUploadInProgress(false); + props.setShouldDisableDropzone(false); props.syncWithRemote(); } diff --git a/src/pages/gallery/index.tsx b/src/pages/gallery/index.tsx index 2645c7609..44f698c28 100644 --- a/src/pages/gallery/index.tsx +++ b/src/pages/gallery/index.tsx @@ -149,7 +149,7 @@ export default function Gallery() { useState(null); const [collectionNamerView, setCollectionNamerView] = useState(false); const [search, setSearch] = useState(null); - const [uploadInProgress, setUploadInProgress] = useState(false); + const [shouldDisableDropzone, setShouldDisableDropzone] = useState(false); const { getRootProps: getDragAndDropRootProps, @@ -158,7 +158,7 @@ export default function Gallery() { } = useDropzone({ noClick: true, noKeyboard: true, - disabled: uploadInProgress, + disabled: shouldDisableDropzone, }); const { selectedFiles: webFileSelectorFiles, @@ -666,7 +666,7 @@ export default function Gallery() { )} setLoading={setBlockingLoad} setCollectionNamerAttributes={setCollectionNamerAttributes} - setUploadInProgress={setUploadInProgress} + setShouldDisableDropzone={setShouldDisableDropzone} setFiles={setFiles} setCollections={setCollections} isFirstUpload={hasNonEmptyCollections(collectionSummaries)} diff --git a/src/utils/common/index.ts b/src/utils/common/index.ts index 0e71519e6..b8eb8fef3 100644 --- a/src/utils/common/index.ts +++ b/src/utils/common/index.ts @@ -93,11 +93,14 @@ export function openLink(href: string, newTab?: boolean) { a.click(); } -export async function waitAndRun(waitPromise: Promise, task: () => void) { +export async function waitAndRun( + waitPromise: Promise, + task: () => Promise +) { if (waitPromise && isPromise(waitPromise)) { await waitPromise; } - task(); + await task(); } function isPromise(p: any) { From aafc3af100919849fe51ea325eea12da9a95bc63 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Mon, 5 Sep 2022 21:14:08 +0530 Subject: [PATCH 210/295] add getAccountDeleteChallenge api --- src/services/userService.ts | 19 +++++++++++++++++++ src/types/user/index.ts | 5 +++++ 2 files changed, 24 insertions(+) diff --git a/src/services/userService.ts b/src/services/userService.ts index 832455d1d..56a31c4c9 100644 --- a/src/services/userService.ts +++ b/src/services/userService.ts @@ -16,6 +16,7 @@ import { TwoFactorVerificationResponse, TwoFactorRecoveryResponse, UserDetails, + DeleteChallengeResponse, } from 'types/user'; import { getLocalFamilyData, isPartOfFamily } from 'utils/billing'; import { ServerErrorCodes } from 'utils/error'; @@ -324,3 +325,21 @@ export const getFamilyPortalRedirectURL = async () => { throw e; } }; + +export const getAccountDeleteChallenge = async () => { + try { + const token = getToken(); + + const resp = await HTTPService.get( + `${ENDPOINT}/users/delete-challenge`, + null, + { + 'X-Auth-Token': token, + } + ); + return resp.data as DeleteChallengeResponse; + } catch (e) { + logError(e, 'failed to get roadmap url'); + throw e; + } +}; diff --git a/src/types/user/index.ts b/src/types/user/index.ts index 243add01a..c47dca3fb 100644 --- a/src/types/user/index.ts +++ b/src/types/user/index.ts @@ -87,3 +87,8 @@ export interface UserDetails { subscription: Subscription; familyData?: FamilyData; } + +export interface DeleteChallengeResponse { + allowDelete: boolean; + encryptedChallenge: string; +} From dbfffdeb6ccb8cf0b4d12ca8475fbacc31c677f6 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Mon, 5 Sep 2022 21:16:51 +0530 Subject: [PATCH 211/295] refactored credential into verify master password form --- src/components/VerifyMasterPasswordForm.tsx | 80 ++++++++++++++++++ src/pages/credentials/index.tsx | 92 +++++---------------- 2 files changed, 102 insertions(+), 70 deletions(-) create mode 100644 src/components/VerifyMasterPasswordForm.tsx diff --git a/src/components/VerifyMasterPasswordForm.tsx b/src/components/VerifyMasterPasswordForm.tsx new file mode 100644 index 000000000..08674565a --- /dev/null +++ b/src/components/VerifyMasterPasswordForm.tsx @@ -0,0 +1,80 @@ +import React from 'react'; + +import constants from 'utils/strings/constants'; +import CryptoWorker from 'utils/crypto'; +import SingleInputForm, { + SingleInputFormProps, +} from 'components/SingleInputForm'; +import { logError } from 'utils/sentry'; +import { CustomError } from 'utils/error'; + +import { Input } from '@mui/material'; + +export default function VerifyMasterPasswordForm({ + user, + keyAttributes, + callback, +}) { + const verifyPassphrase: SingleInputFormProps['callback'] = async ( + passphrase, + setFieldError + ) => { + try { + const cryptoWorker = await new CryptoWorker(); + let kek: string = null; + try { + kek = await cryptoWorker.deriveKey( + passphrase, + keyAttributes.kekSalt, + keyAttributes.opsLimit, + keyAttributes.memLimit + ); + } catch (e) { + logError(e, 'failed to derive key'); + throw Error(CustomError.WEAK_DEVICE); + } + try { + const key: string = await cryptoWorker.decryptB64( + keyAttributes.encryptedKey, + keyAttributes.keyDecryptionNonce, + kek + ); + callback(true, key, passphrase); + } catch (e) { + logError(e, 'user entered a wrong password'); + throw Error(CustomError.INCORRECT_PASSWORD); + } + } catch (e) { + switch (e.message) { + case CustomError.WEAK_DEVICE: + setFieldError(constants.WEAK_DEVICE); + break; + case CustomError.INCORRECT_PASSWORD: + setFieldError(constants.INCORRECT_PASSPHRASE); + break; + default: + setFieldError(`${constants.UNKNOWN_ERROR} ${e.message}`); + } + } + }; + + return ( + paypal@ente.io to manage your - subscription + {provider}@ente.io{' '} + to manage your subscription ), - PAYPAL_MANAGE_NOT_SUPPORTED: 'Manage paypal plan', RENAME: 'Rename', RENAME_COLLECTION: 'Rename album', DELETE_COLLECTION_TITLE: 'Delete album?', From 2316d18384fad1984e195fbc14a8418f3fcdcaa3 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 13 Sep 2022 13:20:09 +0530 Subject: [PATCH 270/295] update mobile subscription error message --- src/components/pages/gallery/PlanSelector/card/index.tsx | 6 +++--- src/utils/strings/englishConstants.tsx | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/pages/gallery/PlanSelector/card/index.tsx b/src/components/pages/gallery/PlanSelector/card/index.tsx index d03985c4a..e242e51b9 100644 --- a/src/components/pages/gallery/PlanSelector/card/index.tsx +++ b/src/components/pages/gallery/PlanSelector/card/index.tsx @@ -108,9 +108,9 @@ function PlanSelectorCard(props: Props) { !isSubscriptionCancelled(subscription) ) { appContext.setDialogMessage({ - title: constants.ERROR, - content: constants.CANCEL_SUBSCRIPTION_ON_MOBILE, - close: { variant: 'danger' }, + title: constants.CANCEL_SUBSCRIPTION_ON_MOBILE, + content: constants.CANCEL_SUBSCRIPTION_ON_MOBILE_MESSAGE, + close: { variant: 'secondary' }, }); } else if ( hasPaidSubscription(subscription) && diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index a81baafbf..500b38e1c 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -338,7 +338,8 @@ const englishConstants = { SUBSCRIPTION_ACTIVATE_FAILED: 'Failed to reactivate subscription renewals', SUBSCRIPTION_PURCHASE_SUCCESS_TITLE: 'Thank you', - CANCEL_SUBSCRIPTION_ON_MOBILE: + CANCEL_SUBSCRIPTION_ON_MOBILE: 'Cancel mobile subscription', + CANCEL_SUBSCRIPTION_ON_MOBILE_MESSAGE: 'Please cancel your subscription from the mobile app to activate a subscription here', MANAGE_NOT_SUPPORTED_MESSAGE: (provider) => ( <> From 55f2a6b07a55184ab2122d730642b80ab508dc7b Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 13 Sep 2022 13:28:13 +0530 Subject: [PATCH 271/295] rearrange checks --- .../pages/gallery/PlanSelector/card/index.tsx | 55 +++++++++---------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/src/components/pages/gallery/PlanSelector/card/index.tsx b/src/components/pages/gallery/PlanSelector/card/index.tsx index e242e51b9..e59343004 100644 --- a/src/components/pages/gallery/PlanSelector/card/index.tsx +++ b/src/components/pages/gallery/PlanSelector/card/index.tsx @@ -104,25 +104,20 @@ function PlanSelectorCard(props: Props) { async function onPlanSelect(plan: Plan) { if ( - hasMobileSubscription(subscription) && - !isSubscriptionCancelled(subscription) + !hasPaidSubscription(subscription) || + isSubscriptionCancelled(subscription) ) { - appContext.setDialogMessage({ - title: constants.CANCEL_SUBSCRIPTION_ON_MOBILE, - content: constants.CANCEL_SUBSCRIPTION_ON_MOBILE_MESSAGE, - close: { variant: 'secondary' }, - }); - } else if ( - hasPaidSubscription(subscription) && - !isSubscriptionCancelled(subscription) - ) { - appContext.setDialogMessage({ - title: constants.MANAGE_PLAN, - content: constants.MANAGE_NOT_SUPPORTED_MESSAGE( - subscription.paymentProvider - ), - close: { variant: 'secondary' }, - }); + try { + props.setLoading(true); + await billingService.buySubscription(plan.stripeID); + } catch (e) { + props.setLoading(false); + appContext.setDialogMessage({ + title: constants.ERROR, + content: constants.SUBSCRIPTION_PURCHASE_FAILED, + close: { variant: 'danger' }, + }); + } } else if (hasStripeSubscription(subscription)) { appContext.setDialogMessage({ title: `${constants.CONFIRM} ${reverseString( @@ -142,18 +137,20 @@ function PlanSelectorCard(props: Props) { }, close: { text: constants.CANCEL }, }); + } else if (hasMobileSubscription(subscription)) { + appContext.setDialogMessage({ + title: constants.CANCEL_SUBSCRIPTION_ON_MOBILE, + content: constants.CANCEL_SUBSCRIPTION_ON_MOBILE_MESSAGE, + close: { variant: 'secondary' }, + }); } else { - try { - props.setLoading(true); - await billingService.buySubscription(plan.stripeID); - } catch (e) { - props.setLoading(false); - appContext.setDialogMessage({ - title: constants.ERROR, - content: constants.SUBSCRIPTION_PURCHASE_FAILED, - close: { variant: 'danger' }, - }); - } + appContext.setDialogMessage({ + title: constants.MANAGE_PLAN, + content: constants.MANAGE_NOT_SUPPORTED_MESSAGE( + subscription.paymentProvider + ), + close: { variant: 'secondary' }, + }); } } From 74bdf37f062b8190ea575b1775458c7b025cca29 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 13 Sep 2022 13:38:27 +0530 Subject: [PATCH 272/295] update email to mail at for billing support --- src/components/pages/gallery/PlanSelector/card/index.tsx | 4 +--- src/utils/strings/englishConstants.tsx | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/components/pages/gallery/PlanSelector/card/index.tsx b/src/components/pages/gallery/PlanSelector/card/index.tsx index e59343004..4af88bbed 100644 --- a/src/components/pages/gallery/PlanSelector/card/index.tsx +++ b/src/components/pages/gallery/PlanSelector/card/index.tsx @@ -146,9 +146,7 @@ function PlanSelectorCard(props: Props) { } else { appContext.setDialogMessage({ title: constants.MANAGE_PLAN, - content: constants.MANAGE_NOT_SUPPORTED_MESSAGE( - subscription.paymentProvider - ), + content: constants.MAIL_TO_MANAGE_SUBSCRIPTION, close: { variant: 'secondary' }, }); } diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index 500b38e1c..c12bc1514 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -341,11 +341,11 @@ const englishConstants = { CANCEL_SUBSCRIPTION_ON_MOBILE: 'Cancel mobile subscription', CANCEL_SUBSCRIPTION_ON_MOBILE_MESSAGE: 'Please cancel your subscription from the mobile app to activate a subscription here', - MANAGE_NOT_SUPPORTED_MESSAGE: (provider) => ( + MAIL_TO_MANAGE_SUBSCRIPTION: ( <> Please contact us at{' '} - {provider}@ente.io{' '} - to manage your subscription + support@ente.io to + manage your subscription ), RENAME: 'Rename', From ed4cc28f90d9dc5ff26839175a33be2968935dde Mon Sep 17 00:00:00 2001 From: Manav Date: Tue, 13 Sep 2022 19:06:35 +0530 Subject: [PATCH 273/295] Update the README screenshots * Update the screenshot in the README (had forgotten to update this after the redesigned version had been launched). * Also add manual line breaks to increasing the spacing above the sections (this matches what is there in the frame README). --- README.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d90d2e134..ea27599da 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ We have open-source apps across [Android](https://github.com/ente-io/frame), [iO This repository contains the code for our web app, built with a lot of ❤️, and a little bit of JavaScript.


-![App Screenshots](https://user-images.githubusercontent.com/1161789/154797467-a2c14f13-6b04-4282-ab61-f6a9f60c2026.png) + +![App Screenshots](https://user-images.githubusercontent.com/24503581/189914045-9d4e9c44-37c6-4ac6-9e17-d8c37aee1e08.png) ## ✨ Features @@ -19,10 +20,14 @@ This repository contains the code for our web app, built with a lot of ❤️, a - EXIF viewer - Zero third-party tracking / analytics +
+ ## 💻 Deployed Application The deployed application is accessible @ [web.ente.io](https://web.ente.io). +
+ ## 🧑‍💻 Building from source 1. Clone this repository with `git clone git@github.com:ente-io/bada-frame.git` @@ -32,26 +37,36 @@ The deployed application is accessible @ [web.ente.io](https://web.ente.io). Open [http://localhost:3000](http://localhost:3000) on your browser to see the live application. +
+ ## 🙋 Help We provide human support to our customers. Please write to [support@ente.io](mailto:support@ente.io) sharing as many details as possible about whatever it is that you need help with, and we will get back to you as soon as possible. +
+ ## 🧭 Roadmap We maintain a public roadmap, that's driven by our community @ [roadmap.ente.io](https://roadmap.ente.io). +
+ ## 🤗 Support If you like this project, please consider upgrading to a paid subscription. If you would like to motivate us to keep building, you can do so by [starring](https://github.com/ente-io/bada-frame/stargazers) this project. +
+ ## ❤️ Join the Community Follow us on [Twitter](https://twitter.com/enteio) and join [r/enteio](https://reddit.com/r/enteio) to get regular updates, connect with other customers, and discuss your ideas. An important part of our journey is to build better software by consistently listening to community feedback. Please feel free to [share your thoughts](mailto:feedback@ente.io) with us at any time. +
+ --- Cross-browser testing provided by From 65823eeaa5dcc9151fad48d95ac9754742e706eb Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 14 Sep 2022 11:07:49 +0530 Subject: [PATCH 274/295] fix verification data case --- src/services/billingService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/billingService.ts b/src/services/billingService.ts index 911f8be50..5d95e345a 100644 --- a/src/services/billingService.ts +++ b/src/services/billingService.ts @@ -133,7 +133,7 @@ class billingService { { paymentProvider: 'stripe', productID: null, - VerificationData: sessionID, + verificationData: sessionID, }, null, { From 4a67c76cb56ee8cdcdf737ff1b404bb04a657212 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Thu, 15 Sep 2022 14:26:50 +0530 Subject: [PATCH 275/295] don't use user filename as identifier while conversion --- src/services/ffmpeg/ffmpegClient.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/services/ffmpeg/ffmpegClient.ts b/src/services/ffmpeg/ffmpegClient.ts index 7aa7a29b0..6f59a7f5c 100644 --- a/src/services/ffmpeg/ffmpegClient.ts +++ b/src/services/ffmpeg/ffmpegClient.ts @@ -22,7 +22,9 @@ class FFmpegClient { async generateThumbnail(file: File) { await this.ready; - const inputFileName = `${Date.now().toString()}-${file.name}`; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [name, ext] = splitFilenameAndExtension(file.name); + const inputFileName = `${Date.now().toString()}-input.${ext}`; const thumbFileName = `${Date.now().toString()}-thumb.jpeg`; this.ffmpeg.FS( 'writeFile', @@ -57,7 +59,9 @@ class FFmpegClient { async extractVideoMetadata(file: File) { await this.ready; - const inputFileName = `${Date.now().toString()}-${file.name}`; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [name, ext] = splitFilenameAndExtension(file.name); + const inputFileName = `${Date.now().toString()}-input.${ext}`; const outFileName = `${Date.now().toString()}-metadata.txt`; this.ffmpeg.FS( 'writeFile', @@ -105,3 +109,13 @@ class FFmpegClient { } export default FFmpegClient; + +export function splitFilenameAndExtension(filename: string): [string, string] { + const lastDotPosition = filename.lastIndexOf('.'); + if (lastDotPosition === -1) return [filename, null]; + else + return [ + filename.slice(0, lastDotPosition), + filename.slice(lastDotPosition + 1), + ]; +} From 00a64b4c8c00609c10a30e32cc3c9f9468db9d62 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Thu, 15 Sep 2022 14:30:28 +0530 Subject: [PATCH 276/295] move util --- src/services/ffmpeg/ffmpegClient.ts | 15 ++++----------- src/utils/ffmpeg/index.ts | 10 ++++++++++ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/services/ffmpeg/ffmpegClient.ts b/src/services/ffmpeg/ffmpegClient.ts index 6f59a7f5c..b1787de4e 100644 --- a/src/services/ffmpeg/ffmpegClient.ts +++ b/src/services/ffmpeg/ffmpegClient.ts @@ -1,6 +1,9 @@ import { createFFmpeg, FFmpeg } from 'ffmpeg-wasm'; import { getUint8ArrayView } from 'services/readerService'; -import { parseFFmpegExtractedMetadata } from 'utils/ffmpeg'; +import { + parseFFmpegExtractedMetadata, + splitFilenameAndExtension, +} from 'utils/ffmpeg'; class FFmpegClient { private ffmpeg: FFmpeg; @@ -109,13 +112,3 @@ class FFmpegClient { } export default FFmpegClient; - -export function splitFilenameAndExtension(filename: string): [string, string] { - const lastDotPosition = filename.lastIndexOf('.'); - if (lastDotPosition === -1) return [filename, null]; - else - return [ - filename.slice(0, lastDotPosition), - filename.slice(lastDotPosition + 1), - ]; -} diff --git a/src/utils/ffmpeg/index.ts b/src/utils/ffmpeg/index.ts index c61d3f89f..916806c3e 100644 --- a/src/utils/ffmpeg/index.ts +++ b/src/utils/ffmpeg/index.ts @@ -61,3 +61,13 @@ function parseCreationTime(creationTime: string) { } return dateTime; } + +export function splitFilenameAndExtension(filename: string): [string, string] { + const lastDotPosition = filename.lastIndexOf('.'); + if (lastDotPosition === -1) return [filename, null]; + else + return [ + filename.slice(0, lastDotPosition), + filename.slice(lastDotPosition + 1), + ]; +} From a48fdc182af7d7aed004b44d6ac222bf759e845e Mon Sep 17 00:00:00 2001 From: Abhinav Date: Thu, 15 Sep 2022 14:31:12 +0530 Subject: [PATCH 277/295] use _ for dummy variable --- src/services/ffmpeg/ffmpegClient.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/ffmpeg/ffmpegClient.ts b/src/services/ffmpeg/ffmpegClient.ts index b1787de4e..cd4848744 100644 --- a/src/services/ffmpeg/ffmpegClient.ts +++ b/src/services/ffmpeg/ffmpegClient.ts @@ -26,7 +26,7 @@ class FFmpegClient { async generateThumbnail(file: File) { await this.ready; // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [name, ext] = splitFilenameAndExtension(file.name); + const [_, ext] = splitFilenameAndExtension(file.name); const inputFileName = `${Date.now().toString()}-input.${ext}`; const thumbFileName = `${Date.now().toString()}-thumb.jpeg`; this.ffmpeg.FS( @@ -63,7 +63,7 @@ class FFmpegClient { async extractVideoMetadata(file: File) { await this.ready; // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [name, ext] = splitFilenameAndExtension(file.name); + const [_, ext] = splitFilenameAndExtension(file.name); const inputFileName = `${Date.now().toString()}-input.${ext}`; const outFileName = `${Date.now().toString()}-metadata.txt`; this.ffmpeg.FS( From 86dd9b726d94d0641bcea494a9973db7dbd14a29 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Thu, 15 Sep 2022 14:43:11 +0530 Subject: [PATCH 278/295] fix button text and casings --- .../pages/gallery/PlanSelector/manageSubscription/index.tsx | 4 ++-- src/utils/strings/englishConstants.tsx | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/pages/gallery/PlanSelector/manageSubscription/index.tsx b/src/components/pages/gallery/PlanSelector/manageSubscription/index.tsx index 89771f8f9..66da8c05f 100644 --- a/src/components/pages/gallery/PlanSelector/manageSubscription/index.tsx +++ b/src/components/pages/gallery/PlanSelector/manageSubscription/index.tsx @@ -77,7 +77,7 @@ function StripeSubscriptionOptions({ title: constants.CANCEL_SUBSCRIPTION, content: constants.CANCEL_SUBSCRIPTION_MESSAGE(), proceed: { - text: constants.CANCEL_SUBSCRIPTION, + text: constants.CANCEL, action: cancelSubscription.bind( null, appContext.setDialogMessage, @@ -87,7 +87,7 @@ function StripeSubscriptionOptions({ variant: 'danger', }, close: { - text: constants.CANCEL, + text: constants.NEVERMIND, }, }); const openManagementPortal = updatePaymentMethod.bind( diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index c12bc1514..3bb9e7fef 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -216,7 +216,7 @@ const englishConstants = { CHANGE: 'Change', CHANGE_EMAIL: 'Change email', OK: 'Ok', - SUCCESS: 'success', + SUCCESS: 'Success', ERROR: 'Error', MESSAGE: 'Message', INSTALL_MOBILE_APP: () => ( @@ -820,6 +820,7 @@ const englishConstants = { AUTHENTICATE: 'Authenticate', UPLOADED_TO_SINGLE_COLLECTION: 'Uploaded to single collection', UPLOADED_TO_SEPARATE_COLLECTIONS: 'Uploaded to separate collections', + NEVERMIND: 'Nevermind', }; export default englishConstants; From bf477b46e16450842b2bc0416fe63811e6e96b8f Mon Sep 17 00:00:00 2001 From: Abhinav Date: Thu, 15 Sep 2022 14:44:09 +0530 Subject: [PATCH 279/295] fix casing --- src/utils/strings/englishConstants.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index 3bb9e7fef..cebaa653c 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -294,7 +294,7 @@ const englishConstants = { <>

We've received your payment

- your subscription is valid till{' '} + Your subscription is valid till{' '} {dateString(expiryTime)}

From ec28259b8ad143ba556954c5decc2fe0c71f7842 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Thu, 15 Sep 2022 14:51:44 +0530 Subject: [PATCH 280/295] fix reactive subscription dialog --- .../PlanSelector/manageSubscription/index.tsx | 12 ++++++------ src/utils/strings/englishConstants.tsx | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/pages/gallery/PlanSelector/manageSubscription/index.tsx b/src/components/pages/gallery/PlanSelector/manageSubscription/index.tsx index 66da8c05f..d08249d2a 100644 --- a/src/components/pages/gallery/PlanSelector/manageSubscription/index.tsx +++ b/src/components/pages/gallery/PlanSelector/manageSubscription/index.tsx @@ -52,14 +52,14 @@ function StripeSubscriptionOptions({ }: Iprops) { const appContext = useContext(AppContext); - const confirmActivation = () => + const confirmReactivation = () => appContext.setDialogMessage({ - title: constants.CONFIRM_ACTIVATE_SUBSCRIPTION, - content: constants.ACTIVATE_SUBSCRIPTION_MESSAGE( + title: constants.REACTIVATE_SUBSCRIPTION, + content: constants.REACTIVATE_SUBSCRIPTION_MESSAGE( subscription.expiryTime ), proceed: { - text: constants.ACTIVATE_SUBSCRIPTION, + text: constants.REACTIVATE, action: activateSubscription.bind( null, appContext.setDialogMessage, @@ -100,8 +100,8 @@ function StripeSubscriptionOptions({ {isSubscriptionCancelled(subscription) ? ( - {constants.ACTIVATE_SUBSCRIPTION} + onClick={confirmReactivation}> + {constants.REACTIVATE_SUBSCRIPTION} ) : ( + REACTIVATE_SUBSCRIPTION: 'Reactivate subscription', + REACTIVATE: 'Reactivate ', + REACTIVATE_SUBSCRIPTION_MESSAGE: (expiryTime) => `Once reactivated, you will be billed on ${dateString(expiryTime)}`, SUBSCRIPTION_ACTIVATE_SUCCESS: 'Subscription activated successfully ', SUBSCRIPTION_ACTIVATE_FAILED: 'Failed to reactivate subscription renewals', From 0ff9ceece104f3e54b69f71527db030bee84e352 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Thu, 15 Sep 2022 14:52:39 +0530 Subject: [PATCH 281/295] fix casing --- src/utils/strings/englishConstants.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index d1ce99b48..a12f68ef3 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -324,7 +324,7 @@ const englishConstants = { All of your data will be deleted from our servers at the end of this billing period.

-

are you sure that you want to cancel your subscription?

+

Are you sure that you want to cancel your subscription?

), SUBSCRIPTION_CANCEL_FAILED: 'Failed to cancel subscription', From 3bf078b8aaad2c15c47c86d8d12f5060376819a1 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Thu, 15 Sep 2022 15:14:31 +0530 Subject: [PATCH 282/295] change back strings --- .../pages/gallery/PlanSelector/manageSubscription/index.tsx | 4 ++-- src/utils/strings/englishConstants.tsx | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/pages/gallery/PlanSelector/manageSubscription/index.tsx b/src/components/pages/gallery/PlanSelector/manageSubscription/index.tsx index d08249d2a..5b9166e78 100644 --- a/src/components/pages/gallery/PlanSelector/manageSubscription/index.tsx +++ b/src/components/pages/gallery/PlanSelector/manageSubscription/index.tsx @@ -59,7 +59,7 @@ function StripeSubscriptionOptions({ subscription.expiryTime ), proceed: { - text: constants.REACTIVATE, + text: constants.REACTIVATE_SUBSCRIPTION, action: activateSubscription.bind( null, appContext.setDialogMessage, @@ -77,7 +77,7 @@ function StripeSubscriptionOptions({ title: constants.CANCEL_SUBSCRIPTION, content: constants.CANCEL_SUBSCRIPTION_MESSAGE(), proceed: { - text: constants.CANCEL, + text: constants.CANCEL_SUBSCRIPTION, action: cancelSubscription.bind( null, appContext.setDialogMessage, diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index a12f68ef3..27f83056b 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -331,7 +331,6 @@ const englishConstants = { SUBSCRIPTION_CANCEL_SUCCESS: 'Subscription canceled successfully', REACTIVATE_SUBSCRIPTION: 'Reactivate subscription', - REACTIVATE: 'Reactivate ', REACTIVATE_SUBSCRIPTION_MESSAGE: (expiryTime) => `Once reactivated, you will be billed on ${dateString(expiryTime)}`, SUBSCRIPTION_ACTIVATE_SUCCESS: 'Subscription activated successfully ', From df94f6a37d5d0fdf741dff7a2ebef1122dff183c Mon Sep 17 00:00:00 2001 From: Abhinav Date: Thu, 15 Sep 2022 15:19:01 +0530 Subject: [PATCH 283/295] make dialog options stacked --- src/components/DialogBox/base.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/DialogBox/base.tsx b/src/components/DialogBox/base.tsx index 6db198c75..395c55cc6 100644 --- a/src/components/DialogBox/base.tsx +++ b/src/components/DialogBox/base.tsx @@ -18,6 +18,12 @@ const DialogBoxBase = styled(Dialog)(({ theme }) => ({ '.MuiDialogTitle-root + .MuiDialogActions-root': { paddingTop: theme.spacing(3), }, + '& .MuiDialogActions-root': { + flexWrap: 'wrap-reverse', + }, + '& .MuiButton-root': { + margin: theme.spacing(0.5, 0), + }, })); export default DialogBoxBase; From 12f2b7d198f3bbbde00bca19025343db06248bf1 Mon Sep 17 00:00:00 2001 From: Manav Date: Thu, 15 Sep 2022 16:46:26 +0530 Subject: [PATCH 284/295] Add the bundle ID of the US mobile app to apple site association --- public/.well-known/apple-app-site-association | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/.well-known/apple-app-site-association b/public/.well-known/apple-app-site-association index 8e87b1279..265f647da 100644 --- a/public/.well-known/apple-app-site-association +++ b/public/.well-known/apple-app-site-association @@ -1,7 +1,8 @@ { "webcredentials": { "apps": [ + "6Z68YJY9Q2.io.ente.frame", "2BUSYC7FN9.io.ente.frame" ] } -} \ No newline at end of file +} From d0737030aaaf48f3390fece8935141adc8a4d39f Mon Sep 17 00:00:00 2001 From: Abhinav Date: Fri, 16 Sep 2022 15:57:58 +0530 Subject: [PATCH 285/295] fix hasNonSystemCollections check --- src/components/Collections/index.tsx | 4 ++-- src/pages/gallery/index.tsx | 6 ++++-- src/utils/collection/index.ts | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/components/Collections/index.tsx b/src/components/Collections/index.tsx index 06d16798d..3c9cc1f38 100644 --- a/src/components/Collections/index.tsx +++ b/src/components/Collections/index.tsx @@ -8,7 +8,7 @@ import CollectionShare from 'components/Collections/CollectionShare'; import { SetCollectionNamerAttributes } from 'components/Collections/CollectionNamer'; import { ITEM_TYPE, TimeStampListItem } from 'components/PhotoList'; import { - hasNonEmptyCollections, + hasNonSystemCollections, isSystemCollection, shouldBeShownOnCollectionBar, } from 'utils/collection'; @@ -50,7 +50,7 @@ export default function Collections(props: Iprops) { const activeCollection = useRef(null); const shouldBeHidden = - isInSearchMode || hasNonEmptyCollections(collectionSummaries); + isInSearchMode || !hasNonSystemCollections(collectionSummaries); useEffect(() => { collectionsMap.current = new Map( diff --git a/src/pages/gallery/index.tsx b/src/pages/gallery/index.tsx index 44f698c28..08baf3c35 100644 --- a/src/pages/gallery/index.tsx +++ b/src/pages/gallery/index.tsx @@ -73,7 +73,7 @@ import { getSelectedCollection, isFavoriteCollection, getArchivedCollections, - hasNonEmptyCollections, + hasNonSystemCollections, } from 'utils/collection'; import { logError } from 'utils/sentry'; import { @@ -669,7 +669,9 @@ export default function Gallery() { setShouldDisableDropzone={setShouldDisableDropzone} setFiles={setFiles} setCollections={setCollections} - isFirstUpload={hasNonEmptyCollections(collectionSummaries)} + isFirstUpload={ + !hasNonSystemCollections(collectionSummaries) + } webFileSelectorFiles={webFileSelectorFiles} webFolderSelectorFiles={webFolderSelectorFiles} dragAndDropFiles={dragAndDropFiles} diff --git a/src/utils/collection/index.ts b/src/utils/collection/index.ts index 97e5e8eaa..1743e2c91 100644 --- a/src/utils/collection/index.ts +++ b/src/utils/collection/index.ts @@ -197,10 +197,10 @@ export const getArchivedCollections = (collections: Collection[]) => { ); }; -export const hasNonEmptyCollections = ( +export const hasNonSystemCollections = ( collectionSummaries: CollectionSummaries ) => { - return collectionSummaries?.size <= 3; + return collectionSummaries?.size > 3; }; export const isUploadAllowedCollection = (type: CollectionSummaryType) => { From c5b8e3fac0b0505332a4d1508e5e1e0bef6a721b Mon Sep 17 00:00:00 2001 From: Abhinav Date: Fri, 16 Sep 2022 16:55:47 +0530 Subject: [PATCH 286/295] fix show nothing to see here me --- src/components/PhotoList.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/components/PhotoList.tsx b/src/components/PhotoList.tsx index 72a428af1..59003916e 100644 --- a/src/components/PhotoList.tsx +++ b/src/components/PhotoList.tsx @@ -220,7 +220,7 @@ export function PhotoList({ if (!skipMerge) { timeStampList = mergeTimeStampList(timeStampList, columns); } - if (timeStampList.length === 0) { + if (timeStampList.length === 1) { timeStampList.push(getEmptyListItem()); } if ( @@ -573,9 +573,6 @@ export function PhotoList({ return listItem.item; } }; - if (!timeStampList?.length) { - return <>; - } return ( Date: Fri, 16 Sep 2022 16:56:17 +0530 Subject: [PATCH 287/295] fix setPhotoListHeader --- src/components/Collections/index.tsx | 51 +++++++++++++--------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/src/components/Collections/index.tsx b/src/components/Collections/index.tsx index 3c9cc1f38..09d4ac1c8 100644 --- a/src/components/Collections/index.tsx +++ b/src/components/Collections/index.tsx @@ -49,8 +49,10 @@ export default function Collections(props: Iprops) { const collectionsMap = useRef>(new Map()); const activeCollection = useRef(null); - const shouldBeHidden = - isInSearchMode || !hasNonSystemCollections(collectionSummaries); + const shouldBeHidden = useMemo( + () => isInSearchMode || !hasNonSystemCollections(collectionSummaries), + [isInSearchMode, collectionSummaries] + ); useEffect(() => { collectionsMap.current = new Map( @@ -72,31 +74,26 @@ export default function Collections(props: Iprops) { [collectionSortBy, collectionSummaries] ); - useEffect( - () => - !shouldBeHidden && - setPhotoListHeader({ - item: ( - setActiveCollectionID(ALL_SECTION)} - showCollectionShareModal={() => - setCollectionShareModalView(true) - } - /> - ), - itemType: ITEM_TYPE.OTHER, - height: 68, - }), - [collectionSummaries, activeCollectionID, shouldBeHidden] - ); + useEffect(() => { + setPhotoListHeader({ + item: ( + setActiveCollectionID(ALL_SECTION)} + showCollectionShareModal={() => + setCollectionShareModalView(true) + } + /> + ), + itemType: ITEM_TYPE.OTHER, + height: 68, + }); + }, [collectionSummaries, activeCollectionID, shouldBeHidden]); if (shouldBeHidden) { return <>; From 3cd6a5e5ae3a3443caef4f53172f58c05ac8451d Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sat, 17 Sep 2022 12:40:19 +0530 Subject: [PATCH 288/295] revert unwanted change --- src/components/PhotoList.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/PhotoList.tsx b/src/components/PhotoList.tsx index 59003916e..b2157f12c 100644 --- a/src/components/PhotoList.tsx +++ b/src/components/PhotoList.tsx @@ -574,6 +574,10 @@ export function PhotoList({ } }; + if (!timeStampList?.length) { + return <>; + } + return ( Date: Sat, 17 Sep 2022 13:04:03 +0530 Subject: [PATCH 289/295] don't hide collection bar if user not in all section --- src/components/Collections/index.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/Collections/index.tsx b/src/components/Collections/index.tsx index 09d4ac1c8..b60e93e05 100644 --- a/src/components/Collections/index.tsx +++ b/src/components/Collections/index.tsx @@ -50,8 +50,11 @@ export default function Collections(props: Iprops) { const activeCollection = useRef(null); const shouldBeHidden = useMemo( - () => isInSearchMode || !hasNonSystemCollections(collectionSummaries), - [isInSearchMode, collectionSummaries] + () => + isInSearchMode || + (!hasNonSystemCollections(collectionSummaries) && + activeCollectionID === ALL_SECTION), + [isInSearchMode, collectionSummaries, activeCollectionID] ); useEffect(() => { @@ -93,7 +96,7 @@ export default function Collections(props: Iprops) { itemType: ITEM_TYPE.OTHER, height: 68, }); - }, [collectionSummaries, activeCollectionID, shouldBeHidden]); + }, [collectionSummaries, activeCollectionID]); if (shouldBeHidden) { return <>; From b2bd139c2bcafa115a73fcc3e12fa549ed6731a0 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sat, 17 Sep 2022 13:18:04 +0530 Subject: [PATCH 290/295] add batching to delete from trash --- src/services/fileService.ts | 41 +++++++++++++++++++++++++++++-------- src/types/file/index.ts | 4 ++++ 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/services/fileService.ts b/src/services/fileService.ts index e3a13e090..c6ddb76de 100644 --- a/src/services/fileService.ts +++ b/src/services/fileService.ts @@ -13,7 +13,7 @@ import { sortFiles, } from 'utils/file'; import CryptoWorker from 'utils/crypto'; -import { EnteFile, TrashRequest } from 'types/file'; +import { DeleteFilesRequest, EnteFile, TrashRequest } from 'types/file'; import { SetFiles } from 'types/gallery'; import { MAX_TRASH_BATCH_SIZE } from 'constants/file'; import { BulkUpdateMagicMetadataRequest } from 'types/magicMetadata'; @@ -199,20 +199,43 @@ export const trashFiles = async (filesToTrash: EnteFile[]) => { } }; -export const deleteFromTrash = async (filesToDelete: number[]) => { +export const batchAndDeleteFromTrash = async (filesToDelete: number[]) => { try { const token = getToken(); if (!token) { return; } - await HTTPService.post( - `${ENDPOINT}/trash/delete`, - { fileIDs: filesToDelete }, - null, - { - 'X-Auth-Token': token, + + const deleteBatch: DeleteFilesRequest = { + fileIDs: [], + }; + + for (const fileID of filesToDelete) { + deleteBatch.fileIDs.push(fileID); + + if (deleteBatch.fileIDs.length >= MAX_TRASH_BATCH_SIZE) { + await deleteFromTrash(token, deleteBatch); + deleteBatch.fileIDs = []; } - ); + } + + if (deleteBatch.fileIDs.length > 0) { + await deleteFromTrash(token, deleteBatch); + } + } catch (e) { + logError(e, 'batchAndDeleteFromTrash failed'); + throw e; + } +}; + +export const deleteFromTrash = async ( + token: string, + deleteBatch: DeleteFilesRequest +) => { + try { + await HTTPService.post(`${ENDPOINT}/trash/delete`, deleteBatch, null, { + 'X-Auth-Token': token, + }); } catch (e) { logError(e, 'delete from trash failed'); throw e; diff --git a/src/types/file/index.ts b/src/types/file/index.ts index 1bc7a8d0a..d5000683d 100644 --- a/src/types/file/index.ts +++ b/src/types/file/index.ts @@ -58,3 +58,7 @@ export interface TrashRequestItems { fileID: number; collectionID: number; } + +export interface DeleteFilesRequest { + fileIDs: number[]; +} From 83a10909af7adcee1e30843ed4011c7224d5330c Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sat, 17 Sep 2022 13:21:45 +0530 Subject: [PATCH 291/295] rename function --- src/services/fileService.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/services/fileService.ts b/src/services/fileService.ts index c6ddb76de..902aa8811 100644 --- a/src/services/fileService.ts +++ b/src/services/fileService.ts @@ -199,7 +199,7 @@ export const trashFiles = async (filesToTrash: EnteFile[]) => { } }; -export const batchAndDeleteFromTrash = async (filesToDelete: number[]) => { +export const deleteFromTrash = async (filesToDelete: number[]) => { try { const token = getToken(); if (!token) { @@ -214,13 +214,13 @@ export const batchAndDeleteFromTrash = async (filesToDelete: number[]) => { deleteBatch.fileIDs.push(fileID); if (deleteBatch.fileIDs.length >= MAX_TRASH_BATCH_SIZE) { - await deleteFromTrash(token, deleteBatch); + await deleteBatchFromTrash(token, deleteBatch); deleteBatch.fileIDs = []; } } if (deleteBatch.fileIDs.length > 0) { - await deleteFromTrash(token, deleteBatch); + await deleteBatchFromTrash(token, deleteBatch); } } catch (e) { logError(e, 'batchAndDeleteFromTrash failed'); @@ -228,7 +228,7 @@ export const batchAndDeleteFromTrash = async (filesToDelete: number[]) => { } }; -export const deleteFromTrash = async ( +const deleteBatchFromTrash = async ( token: string, deleteBatch: DeleteFilesRequest ) => { From a0e2d977e3bdc6fc6033c34a853f20f8006c8797 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sat, 17 Sep 2022 13:23:09 +0530 Subject: [PATCH 292/295] update error logging --- src/services/fileService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/fileService.ts b/src/services/fileService.ts index 902aa8811..216b47926 100644 --- a/src/services/fileService.ts +++ b/src/services/fileService.ts @@ -223,7 +223,7 @@ export const deleteFromTrash = async (filesToDelete: number[]) => { await deleteBatchFromTrash(token, deleteBatch); } } catch (e) { - logError(e, 'batchAndDeleteFromTrash failed'); + logError(e, 'deleteFromTrash failed'); throw e; } }; @@ -237,7 +237,7 @@ const deleteBatchFromTrash = async ( 'X-Auth-Token': token, }); } catch (e) { - logError(e, 'delete from trash failed'); + logError(e, 'deleteBatchFromTrash failed'); throw e; } }; From de858fd3883258b1e7f836a5911dffe8ca078989 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sat, 17 Sep 2022 13:25:15 +0530 Subject: [PATCH 293/295] remove unnecessary empty line --- src/services/fileService.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/services/fileService.ts b/src/services/fileService.ts index 216b47926..80b2682c5 100644 --- a/src/services/fileService.ts +++ b/src/services/fileService.ts @@ -205,20 +205,16 @@ export const deleteFromTrash = async (filesToDelete: number[]) => { if (!token) { return; } - const deleteBatch: DeleteFilesRequest = { fileIDs: [], }; - for (const fileID of filesToDelete) { deleteBatch.fileIDs.push(fileID); - if (deleteBatch.fileIDs.length >= MAX_TRASH_BATCH_SIZE) { await deleteBatchFromTrash(token, deleteBatch); deleteBatch.fileIDs = []; } } - if (deleteBatch.fileIDs.length > 0) { await deleteBatchFromTrash(token, deleteBatch); } From 9055f67ff4e6775c93b71e3b073f2988d78873e7 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sat, 17 Sep 2022 13:41:03 +0530 Subject: [PATCH 294/295] remove DeleteFilesRequest interface --- src/services/fileService.ts | 30 +++++++++++++++--------------- src/types/file/index.ts | 4 ---- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/services/fileService.ts b/src/services/fileService.ts index 80b2682c5..ed92ffe1a 100644 --- a/src/services/fileService.ts +++ b/src/services/fileService.ts @@ -13,7 +13,7 @@ import { sortFiles, } from 'utils/file'; import CryptoWorker from 'utils/crypto'; -import { DeleteFilesRequest, EnteFile, TrashRequest } from 'types/file'; +import { EnteFile, TrashRequest } from 'types/file'; import { SetFiles } from 'types/gallery'; import { MAX_TRASH_BATCH_SIZE } from 'constants/file'; import { BulkUpdateMagicMetadataRequest } from 'types/magicMetadata'; @@ -205,17 +205,15 @@ export const deleteFromTrash = async (filesToDelete: number[]) => { if (!token) { return; } - const deleteBatch: DeleteFilesRequest = { - fileIDs: [], - }; + let deleteBatch: number[]; for (const fileID of filesToDelete) { - deleteBatch.fileIDs.push(fileID); - if (deleteBatch.fileIDs.length >= MAX_TRASH_BATCH_SIZE) { + deleteBatch.push(fileID); + if (deleteBatch.length >= MAX_TRASH_BATCH_SIZE) { await deleteBatchFromTrash(token, deleteBatch); - deleteBatch.fileIDs = []; + deleteBatch = []; } } - if (deleteBatch.fileIDs.length > 0) { + if (deleteBatch.length > 0) { await deleteBatchFromTrash(token, deleteBatch); } } catch (e) { @@ -224,14 +222,16 @@ export const deleteFromTrash = async (filesToDelete: number[]) => { } }; -const deleteBatchFromTrash = async ( - token: string, - deleteBatch: DeleteFilesRequest -) => { +const deleteBatchFromTrash = async (token: string, deleteBatch: number[]) => { try { - await HTTPService.post(`${ENDPOINT}/trash/delete`, deleteBatch, null, { - 'X-Auth-Token': token, - }); + await HTTPService.post( + `${ENDPOINT}/trash/delete`, + { fileIDs: deleteBatch }, + null, + { + 'X-Auth-Token': token, + } + ); } catch (e) { logError(e, 'deleteBatchFromTrash failed'); throw e; diff --git a/src/types/file/index.ts b/src/types/file/index.ts index d5000683d..1bc7a8d0a 100644 --- a/src/types/file/index.ts +++ b/src/types/file/index.ts @@ -58,7 +58,3 @@ export interface TrashRequestItems { fileID: number; collectionID: number; } - -export interface DeleteFilesRequest { - fileIDs: number[]; -} From 8df9ebd44aca1ea2fd8523fb210a32cddb7aa026 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sat, 17 Sep 2022 13:47:55 +0530 Subject: [PATCH 295/295] fix deleteFromTrash deleteBatch not intiialised --- src/services/fileService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/fileService.ts b/src/services/fileService.ts index ed92ffe1a..f2935ce04 100644 --- a/src/services/fileService.ts +++ b/src/services/fileService.ts @@ -205,7 +205,7 @@ export const deleteFromTrash = async (filesToDelete: number[]) => { if (!token) { return; } - let deleteBatch: number[]; + let deleteBatch: number[] = []; for (const fileID of filesToDelete) { deleteBatch.push(fileID); if (deleteBatch.length >= MAX_TRASH_BATCH_SIZE) {