diff --git a/web/apps/cast/src/types/upload/index.ts b/web/apps/cast/src/types/upload/index.ts index ef44b4a23f6f2d6325d87b3793b96dc5c2cf3d4d..0e249846adca9c4f3ddd1b332aa51fcf484f97d2 100644 --- a/web/apps/cast/src/types/upload/index.ts +++ b/web/apps/cast/src/types/upload/index.ts @@ -95,13 +95,6 @@ export interface ParsedExtractedMetadata { height: number; } -// This is used to prompt the user the make upload strategy choice -export interface ImportSuggestion { - rootFolderName: string; - hasNestedFolders: boolean; - hasRootLevelFileWithFolder: boolean; -} - export interface PublicUploadProps { token: string; passwordToken: string; diff --git a/web/apps/photos/src/components/Upload/Uploader.tsx b/web/apps/photos/src/components/Upload/Uploader.tsx index 1d95c9310e08163763a466fb86ce519e33e7d625..ad0ff15b17d0a91155d2d7cc944dd6ebb4c5bfb6 100644 --- a/web/apps/photos/src/components/Upload/Uploader.tsx +++ b/web/apps/photos/src/components/Upload/Uploader.tsx @@ -5,11 +5,7 @@ import { CustomError } from "@ente/shared/error"; import { isPromise } from "@ente/shared/utils"; import DiscFullIcon from "@mui/icons-material/DiscFull"; import UserNameInputDialog from "components/UserNameInputDialog"; -import { - DEFAULT_IMPORT_SUGGESTION, - PICKED_UPLOAD_TYPE, - UPLOAD_STAGES, -} from "constants/upload"; +import { PICKED_UPLOAD_TYPE, UPLOAD_STAGES } from "constants/upload"; import { t } from "i18next"; import isElectron from "is-electron"; import { AppContext } from "pages/_app"; @@ -35,11 +31,7 @@ import { SetLoading, UploadTypeSelectorIntent, } from "types/gallery"; -import { - ElectronFile, - FileWithCollection, - ImportSuggestion, -} from "types/upload"; +import { ElectronFile, FileWithCollection } from "types/upload"; import { InProgressUpload, SegregatedFinishedUploads, @@ -53,9 +45,11 @@ import { getRootLevelFileWithFolderNotAllowMessage, } from "utils/ui"; import { + DEFAULT_IMPORT_SUGGESTION, filterOutSystemFiles, getImportSuggestion, groupFilesBasedOnParentFolder, + type ImportSuggestion, } from "utils/upload"; import { SetCollectionNamerAttributes } from "../Collections/CollectionNamer"; import { CollectionMappingChoiceModal } from "./CollectionMappingChoiceModal"; diff --git a/web/apps/photos/src/components/WatchFolder.tsx b/web/apps/photos/src/components/WatchFolder.tsx index 5715dfb45e4fd4b27a1e41c50501c7ded0abb616..f7951c5a87367ab5d9395ac7c6b9d8859e0c617e 100644 --- a/web/apps/photos/src/components/WatchFolder.tsx +++ b/web/apps/photos/src/components/WatchFolder.tsx @@ -1,4 +1,5 @@ import { ensureElectron } from "@/next/electron"; +import { basename } from "@/next/file"; import type { CollectionMapping, FolderWatch } from "@/next/types/ipc"; import { ensure } from "@/utils/ensure"; import { @@ -166,7 +167,7 @@ const WatchList: React.FC = ({ watches, removeWatch }) => { {watches.map((watch) => { return ( @@ -292,7 +293,7 @@ const EntryHeading: React.FC = ({ watch }) => { const appContext = useContext(AppContext); return ( - {watch.rootFolderName} + {basename(watch.folderPath)} {appContext.isFolderSyncRunning && watcher.isSyncingWatch(watch) && } diff --git a/web/apps/photos/src/constants/upload.ts b/web/apps/photos/src/constants/upload.ts index 6d9f63d7883d3de2e26fe084002544aa0901709f..1f8858bc3ced07bde7237597a698b8bb36254c66 100644 --- a/web/apps/photos/src/constants/upload.ts +++ b/web/apps/photos/src/constants/upload.ts @@ -1,11 +1,6 @@ import { ENCRYPTION_CHUNK_SIZE } from "@ente/shared/crypto/constants"; import { FILE_TYPE } from "constants/file"; -import { - FileTypeInfo, - ImportSuggestion, - Location, - ParsedExtractedMetadata, -} from "types/upload"; +import { FileTypeInfo, Location, ParsedExtractedMetadata } from "types/upload"; // list of format that were missed by type-detection for some files. export const WHITELISTED_FILE_FORMATS: FileTypeInfo[] = [ @@ -111,12 +106,6 @@ export const NULL_EXTRACTED_METADATA: ParsedExtractedMetadata = { export const A_SEC_IN_MICROSECONDS = 1e6; -export const DEFAULT_IMPORT_SUGGESTION: ImportSuggestion = { - rootFolderName: "", - hasNestedFolders: false, - hasRootLevelFileWithFolder: false, -}; - export const BLACK_THUMBNAIL_BASE64 = "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEB" + "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQ" + diff --git a/web/apps/photos/src/services/watch.ts b/web/apps/photos/src/services/watch.ts index 51d9484e6fd50fa594a9044cebf37ff2f06379cd..c8f330386c00af416bd071f01939bb10110751cf 100644 --- a/web/apps/photos/src/services/watch.ts +++ b/web/apps/photos/src/services/watch.ts @@ -115,9 +115,7 @@ class FolderWatcher { * collection do files belonging to nested directories go to. */ async addWatch(folderPath: string, mapping: CollectionMapping) { - const rootFolderName = folderPath.substring( - folderPath.lastIndexOf("/") + 1, - ); + const rootFolderName = basename(folderPath) await ensureElectron().addWatchMapping( rootFolderName, folderPath, diff --git a/web/apps/photos/src/types/upload/index.ts b/web/apps/photos/src/types/upload/index.ts index 3deab0ed79a35ee8ed1119544a7d0d39f4f3efb4..72eef39f6bda7943f9937d9856d8b1b5bdbfd55b 100644 --- a/web/apps/photos/src/types/upload/index.ts +++ b/web/apps/photos/src/types/upload/index.ts @@ -156,13 +156,6 @@ export interface ParsedExtractedMetadata { height: number; } -// This is used to prompt the user the make upload strategy choice -export interface ImportSuggestion { - rootFolderName: string; - hasNestedFolders: boolean; - hasRootLevelFileWithFolder: boolean; -} - export interface PublicUploadProps { token: string; passwordToken: string; diff --git a/web/apps/photos/src/utils/upload/index.ts b/web/apps/photos/src/utils/upload/index.ts index 49f39a676dc035902b0597d3cc1932445b431477..1d7674165dad5d6ae0fca2d470bdea6c2719b37a 100644 --- a/web/apps/photos/src/utils/upload/index.ts +++ b/web/apps/photos/src/utils/upload/index.ts @@ -1,19 +1,10 @@ import { basename, dirname } from "@/next/file"; import { FILE_TYPE } from "constants/file"; -import { - A_SEC_IN_MICROSECONDS, - DEFAULT_IMPORT_SUGGESTION, - PICKED_UPLOAD_TYPE, -} from "constants/upload"; +import { A_SEC_IN_MICROSECONDS, PICKED_UPLOAD_TYPE } from "constants/upload"; import isElectron from "is-electron"; import { exportMetadataDirectoryName } from "services/export"; import { EnteFile } from "types/file"; -import { - ElectronFile, - FileWithCollection, - ImportSuggestion, - Metadata, -} from "types/upload"; +import { ElectronFile, FileWithCollection, Metadata } from "types/upload"; const TYPE_JSON = "json"; const DEDUPE_COLLECTION = new Set(["icloud library", "icloudlibrary"]); @@ -120,6 +111,19 @@ export function areFileWithCollectionsSame( export const areAllInSameDirectory = (paths: string[]) => new Set(paths.map(dirname)).size == 1; +// This is used to prompt the user the make upload strategy choice +export interface ImportSuggestion { + rootFolderName: string; + hasNestedFolders: boolean; + hasRootLevelFileWithFolder: boolean; +} + +export const DEFAULT_IMPORT_SUGGESTION: ImportSuggestion = { + rootFolderName: "", + hasNestedFolders: false, + hasRootLevelFileWithFolder: false, +}; + export function getImportSuggestion( uploadType: PICKED_UPLOAD_TYPE, toUploadFiles: File[] | ElectronFile[], diff --git a/web/packages/next/types/ipc.ts b/web/packages/next/types/ipc.ts index 321fb219246058fcac0dd776eaf3927b8fb5c66e..c2adc517b9d3634e77f24f5d28efaae69eea7744 100644 --- a/web/packages/next/types/ipc.ts +++ b/web/packages/next/types/ipc.ts @@ -300,6 +300,21 @@ export interface Electron { * The returned paths are guaranteed to use POSIX separators ('/'). */ findFiles: (folderPath: string) => Promise; + + /** + * Add a new folder watch for the given {@link folderPath}. + * + * This adds a new entry in the list of watches (persisting them on + * disk), and also starts immediately observing for file system events + * that happen within {@link folderPath}. + * + * @param collectionMapping Determines how nested directories (if any) + * get mapped to Ente collections. + */ + add: ( + folderPath: string, + collectionMapping: CollectionMapping, + ) => Promise; }; registerWatcherFunctions: ( @@ -394,6 +409,12 @@ export interface AppUpdate { * side. */ export interface FolderWatch { + /** + * Name of the root folder. + * + * This is just `basename(folderPath)`, but is retained as a precomputed + * property for convenience instead of needing to recompute it every time. + */ rootFolderName: string; /** * Specify if nested files should all be mapped to the same single root @@ -401,8 +422,17 @@ export interface FolderWatch { * files. @see {@link CollectionMapping}. */ collectionMapping: CollectionMapping; + /** + * The path to the (root) folder we are watching. + */ folderPath: string; + /** + * Files that have already been uploaded. + */ syncedFiles: FolderWatchSyncedFile[]; + /** + * Files (paths) that should be ignored when uploading. + */ ignoredFiles: string[]; }