Browse Source

Enum to type - wip

Manav Rathi 1 year ago
parent
commit
ca5b98b8d2

+ 8 - 15
web/apps/photos/src/components/Upload/Uploader.tsx

@@ -8,7 +8,7 @@ import {
     DEFAULT_IMPORT_SUGGESTION,
     PICKED_UPLOAD_TYPE,
     UPLOAD_STAGES,
-    UPLOAD_STRATEGY,
+    type CollectionMapping,
 } from "constants/upload";
 import { t } from "i18next";
 import isElectron from "is-electron";
@@ -391,7 +391,7 @@ export default function Uploader(props: Props) {
     };
 
     const uploadFilesToNewCollections = async (
-        strategy: UPLOAD_STRATEGY,
+        strategy: CollectionMapping,
         collectionName?: string,
     ) => {
         try {
@@ -405,7 +405,7 @@ export default function Uploader(props: Props) {
                 string,
                 (File | ElectronFile)[]
             >();
-            if (strategy === UPLOAD_STRATEGY.SINGLE_COLLECTION) {
+            if (strategy == "root") {
                 collectionNameToFilesMap.set(
                     collectionName,
                     toUploadFiles.current,
@@ -605,10 +605,7 @@ export default function Uploader(props: Props) {
     }
 
     const uploadToSingleNewCollection = (collectionName: string) => {
-        uploadFilesToNewCollections(
-            UPLOAD_STRATEGY.SINGLE_COLLECTION,
-            collectionName,
-        );
+        uploadFilesToNewCollections("root", collectionName);
     };
 
     const showCollectionCreateModal = (suggestedName: string) => {
@@ -647,7 +644,7 @@ export default function Uploader(props: Props) {
                         `upload pending files to collection - ${pendingDesktopUploadCollectionName.current}`,
                     );
                     uploadFilesToNewCollections(
-                        UPLOAD_STRATEGY.SINGLE_COLLECTION,
+                        "root",
                         pendingDesktopUploadCollectionName.current,
                     );
                     pendingDesktopUploadCollectionName.current = null;
@@ -655,17 +652,13 @@ export default function Uploader(props: Props) {
                     log.info(
                         `pending upload - strategy - "multiple collections" `,
                     );
-                    uploadFilesToNewCollections(
-                        UPLOAD_STRATEGY.COLLECTION_PER_FOLDER,
-                    );
+                    uploadFilesToNewCollections("leaf");
                 }
                 return;
             }
             if (isElectron() && pickedUploadType === PICKED_UPLOAD_TYPE.ZIPS) {
                 log.info("uploading zip files");
-                uploadFilesToNewCollections(
-                    UPLOAD_STRATEGY.COLLECTION_PER_FOLDER,
-                );
+                uploadFilesToNewCollections("leaf");
                 return;
             }
             if (isFirstUpload && !importSuggestion.rootFolderName) {
@@ -784,7 +777,7 @@ export default function Uploader(props: Props) {
             );
             return;
         }
-        uploadFilesToNewCollections(UPLOAD_STRATEGY.COLLECTION_PER_FOLDER);
+        uploadFilesToNewCollections("leaf");
     };
 
     return (

+ 25 - 31
web/apps/photos/src/components/WatchFolder.tsx

@@ -1,5 +1,6 @@
 import { ensureElectron } from "@/next/electron";
-import { FolderWatch } from "@/next/types/ipc";
+import type { CollectionMapping, FolderWatch } from "@/next/types/ipc";
+import { ensure } from "@/utils/ensure";
 import {
     FlexWrapper,
     HorizontalFlex,
@@ -26,7 +27,7 @@ import {
 } from "@mui/material";
 import { styled } from "@mui/material/styles";
 import UploadStrategyChoiceModal from "components/Upload/UploadStrategyChoiceModal";
-import { PICKED_UPLOAD_TYPE, UPLOAD_STRATEGY } from "constants/upload";
+import { PICKED_UPLOAD_TYPE } from "constants/upload";
 import { t } from "i18next";
 import { AppContext } from "pages/_app";
 import React, { useContext, useEffect, useState } from "react";
@@ -44,11 +45,17 @@ interface WatchFolderProps {
  * This is the screen that controls that "watch folder" feature in the app.
  */
 export const WatchFolder: React.FC<WatchFolderProps> = ({ open, onClose }) => {
+    // The folders we are watching
     const [watches, setWatches] = useState<FolderWatch[] | undefined>();
-    const [inputFolderPath, setInputFolderPath] = useState<
+    // Temporarily stash the folder path while we show a choice dialog to the
+    // user to select the collection mapping.
+    const [savedFolderPath, setSavedFolderPath] = useState<
         string | undefined
     >();
+    // True when we're showing the choice dialog to ask the user to set the
+    // collection mapping.
     const [choiceModalOpen, setChoiceModalOpen] = useState(false);
+
     const appContext = useContext(AppContext);
 
     useEffect(() => {
@@ -70,43 +77,34 @@ export const WatchFolder: React.FC<WatchFolderProps> = ({ open, onClose }) => {
             const folder: any = folders[i];
             const path = (folder.path as string).replace(/\\/g, "/");
             if (await watcher.isFolder(path)) {
-                await addFolderForWatching(path);
+                await selectCollectionMappingAndAddWatch(path);
             }
         }
     };
 
-    const addFolderForWatching = async (path: string) => {
-        setInputFolderPath(path);
+    const selectCollectionMappingAndAddWatch = async (path: string) => {
         const files = await ensureElectron().getDirFiles(path);
         const analysisResult = getImportSuggestion(
             PICKED_UPLOAD_TYPE.FOLDERS,
             files,
         );
         if (analysisResult.hasNestedFolders) {
+            setSavedFolderPath(path);
             setChoiceModalOpen(true);
         } else {
-            addWatchWithStrategy(UPLOAD_STRATEGY.SINGLE_COLLECTION, path);
+            addWatch(path, "root");
         }
     };
 
-    const addWatchWithStrategy = async (
-        uploadStrategy: UPLOAD_STRATEGY,
-        folderPath?: string,
-    ) => {
-        folderPath = folderPath || inputFolderPath;
-        await watcher.addWatchMapping(
-            folderPath.substring(folderPath.lastIndexOf("/") + 1),
-            folderPath,
-            uploadStrategy,
-        );
-        setInputFolderPath("");
+    const addWatch = async (folderPath: string, mapping: CollectionMapping) => {
+        await watcher.addWatch(folderPath, mapping);
         setWatches(await watcher.getWatchMappings());
     };
 
-    const addWatch = async () => {
+    const addNewWatch = async () => {
         const folderPath = await watcher.selectFolder();
         if (folderPath) {
-            await addFolderForWatching(folderPath);
+            await selectCollectionMappingAndAddWatch(folderPath);
         }
     };
 
@@ -117,14 +115,10 @@ export const WatchFolder: React.FC<WatchFolderProps> = ({ open, onClose }) => {
 
     const closeChoiceModal = () => setChoiceModalOpen(false);
 
-    const uploadToSingleCollection = () => {
-        closeChoiceModal();
-        addWatchWithStrategy(UPLOAD_STRATEGY.SINGLE_COLLECTION);
-    };
-
-    const uploadToMultipleCollection = () => {
+    const addWatchWithMapping = (mapping: CollectionMapping) => {
         closeChoiceModal();
-        addWatchWithStrategy(UPLOAD_STRATEGY.COLLECTION_PER_FOLDER);
+        setSavedFolderPath(undefined);
+        addWatch(ensure(savedFolderPath), mapping);
     };
 
     return (
@@ -143,7 +137,7 @@ export const WatchFolder: React.FC<WatchFolderProps> = ({ open, onClose }) => {
                 <DialogContent sx={{ flex: 1 }}>
                     <Stack spacing={1} p={1.5} height={"100%"}>
                         <WatchList {...{ watches, removeWatch }} />
-                        <Button fullWidth color="accent" onClick={addWatch}>
+                        <Button fullWidth color="accent" onClick={addNewWatch}>
                             <span>+</span>
                             <span
                                 style={{
@@ -158,8 +152,8 @@ export const WatchFolder: React.FC<WatchFolderProps> = ({ open, onClose }) => {
             <UploadStrategyChoiceModal
                 open={choiceModalOpen}
                 onClose={closeChoiceModal}
-                uploadToSingleCollection={uploadToSingleCollection}
-                uploadToMultipleCollection={uploadToMultipleCollection}
+                uploadToSingleCollection={() => addWatchWithMapping("root")}
+                uploadToMultipleCollection={() => addWatchWithMapping("parent")}
             />
         </>
     );
@@ -269,7 +263,7 @@ const WatchEntry: React.FC<WatchEntryProps> = ({ watch, removeWatch }) => {
     return (
         <SpaceBetweenFlex>
             <HorizontalFlex>
-                {watch.uploadStrategy === UPLOAD_STRATEGY.SINGLE_COLLECTION ? (
+                {watch.uploadStrategy === "root" ? (
                     <Tooltip title={t("UPLOADED_TO_SINGLE_COLLECTION")}>
                         <FolderOpenIcon />
                     </Tooltip>

+ 17 - 16
web/apps/photos/src/services/watch.ts

@@ -6,7 +6,7 @@
 import { ensureElectron } from "@/next/electron";
 import { nameAndExtension } from "@/next/file";
 import log from "@/next/log";
-import type { FolderWatch } from "@/next/types/ipc";
+import type { CollectionMapping, FolderWatch } from "@/next/types/ipc";
 import { UPLOAD_RESULT, UPLOAD_STRATEGY } from "constants/upload";
 import debounce from "debounce";
 import uploadManager from "services/upload/uploadManager";
@@ -140,21 +140,22 @@ class WatchFolderService {
         );
     }
 
-    async addWatchMapping(
-        rootFolderName: string,
-        folderPath: string,
-        uploadStrategy: UPLOAD_STRATEGY,
-    ) {
-        try {
-            await ensureElectron().addWatchMapping(
-                rootFolderName,
-                folderPath,
-                uploadStrategy,
-            );
-            this.syncWithDisk();
-        } catch (e) {
-            log.error("error while adding watch mapping", e);
-        }
+    /**
+     * Add a new folder watch for the given root {@link folderPath}
+     *
+     * @param mapping The {@link CollectionMapping} to use to decide which
+     * collection do files belonging to nested directories go to.
+     */
+    async addWatch(folderPath: string, mapping: CollectionMapping) {
+        const rootFolderName = folderPath.substring(
+            folderPath.lastIndexOf("/") + 1,
+        );
+        await ensureElectron().addWatchMapping(
+            rootFolderName,
+            folderPath,
+            mapping,
+        );
+        this.syncWithDisk();
     }
 
     /**

+ 8 - 2
web/packages/next/types/ipc.ts

@@ -377,8 +377,8 @@ export interface Electron {
  * A top level folder that was selected by the user for watching.
  *
  * The user can set up multiple such watches. Each of these can in turn be
- * syncing multiple on disk folders to one or more (dependening on the
- * {@link uploadStrategy}) Ente albums.
+ * syncing multiple on disk folders to one or more Ente collections (depending
+ * on the value of {@link collectionMapping}).
  *
  * This type is passed across the IPC boundary. It is persisted on the Node.js
  * side.
@@ -391,6 +391,12 @@ export interface FolderWatch {
     ignoredFiles: string[];
 }
 
+export type CollectionMapping =
+    /** Map everything to a single collection corresponding to the root directory */
+    | "root"
+    /** Map each file to a collection named after its parent directory */
+    | "parent";
+
 /**
  * An on-disk file that was synced as part of a folder watch.
  */

+ 7 - 0
web/packages/utils/ensure.ts

@@ -0,0 +1,7 @@
+/**
+ * Throw an exception if the given value is undefined.
+ */
+export const ensure = <T>(v: T | undefined): T => {
+    if (v === undefined) throw new Error("Required value was not found");
+    return v;
+};