diff --git a/desktop/src/main/ipc.ts b/desktop/src/main/ipc.ts index 093724251..bde28d940 100644 --- a/desktop/src/main/ipc.ts +++ b/desktop/src/main/ipc.ts @@ -52,6 +52,7 @@ import { saveEncryptionKey, } from "./services/store"; import { + clearPendingUploads, getElectronFilesFromGoogleZip, lsZip, pendingUploads, @@ -199,6 +200,8 @@ export const attachIPCHandlers = () => { // - Upload + ipcMain.handle("lsZip", (_, zipPath: string) => lsZip(zipPath)); + ipcMain.handle("pendingUploads", () => pendingUploads()); ipcMain.handle("setPendingUploadCollection", (_, collectionName: string) => @@ -211,7 +214,7 @@ export const attachIPCHandlers = () => { setPendingUploadFiles(type, filePaths), ); - ipcMain.handle("lsZip", (_, zipPath: string) => lsZip(zipPath)); + ipcMain.handle("clearPendingUploads", () => clearPendingUploads()); // - diff --git a/desktop/src/main/services/upload.ts b/desktop/src/main/services/upload.ts index 245edafe6..1ca7c2bc4 100644 --- a/desktop/src/main/services/upload.ts +++ b/desktop/src/main/services/upload.ts @@ -8,6 +8,24 @@ import { } from "../stores/upload-status"; import { getElectronFile, getZipFileStream } from "./fs"; +export const lsZip = async (zipPath: string) => { + const zip = new StreamZip.async({ file: zipPath }); + + const entries = await zip.entries(); + const entryPaths: string[] = []; + + for (const entry of Object.values(entries)) { + const basename = path.basename(entry.name); + // Ignore "hidden" files (files whose names begins with a dot). + if (entry.isFile && basename.length > 0 && basename[0] != ".") { + // `entry.name` is the path within the zip. + entryPaths.push(entry.name); + } + } + + return [entryPaths]; +}; + export const pendingUploads = async () => { const collectionName = uploadStatusStore.get("collectionName"); const filePaths = validSavedPaths("files"); @@ -60,6 +78,10 @@ export const setPendingUploadFiles = ( else uploadStatusStore.delete(key); }; +export const clearPendingUploads = () => { + uploadStatusStore.clear(); +}; + const storeKey = (type: PendingUploads["type"]): keyof UploadStatusStore => { switch (type) { case "zips": @@ -69,10 +91,6 @@ const storeKey = (type: PendingUploads["type"]): keyof UploadStatusStore => { } }; -export const lsZip = async (zipPath: string) => { - return [zipPath]; -}; - export const getElectronFilesFromGoogleZip = async (filePath: string) => { const zip = new StreamZip.async({ file: filePath, diff --git a/desktop/src/main/stores/upload-status.ts b/desktop/src/main/stores/upload-status.ts index e2d1880ce..5b7f7c112 100644 --- a/desktop/src/main/stores/upload-status.ts +++ b/desktop/src/main/stores/upload-status.ts @@ -1,24 +1,9 @@ import Store, { Schema } from "electron-store"; export interface UploadStatusStore { - /** - * The name of the collection (when uploading to a singular collection) or - * the root collection (when uploading to separate * albums) to which we - * these uploads are meant to go to. - */ collectionName: string; - /** - * Paths of regular files that need to be uploaded. - */ filePaths: string[]; - /** - * Paths of zip files that need to be uploaded. - */ zipPaths: string[]; - /** - * For each zip file, which of its entries (paths) within the zip file that - * need to be uploaded. - */ zipEntries: Record; } diff --git a/desktop/src/preload.ts b/desktop/src/preload.ts index 244cc9ffc..ad93a40f6 100644 --- a/desktop/src/preload.ts +++ b/desktop/src/preload.ts @@ -241,6 +241,9 @@ const watchFindFiles = (folderPath: string): Promise => // - Upload +const lsZip = (zipPath: string): Promise => + ipcRenderer.invoke("lsZip", zipPath); + const pendingUploads = (): Promise => ipcRenderer.invoke("pendingUploads"); @@ -253,8 +256,8 @@ const setPendingUploadFiles = ( ): Promise => ipcRenderer.invoke("setPendingUploadFiles", type, filePaths); -const lsZip = (zipPath: string): Promise => - ipcRenderer.invoke("lsZip", zipPath); +const clearPendingUploads = (): Promise => + ipcRenderer.invoke("clearPendingUploads"); // - TODO: AUDIT below this // - @@ -373,10 +376,11 @@ contextBridge.exposeInMainWorld("electron", { // - Upload + lsZip, pendingUploads, setPendingUploadCollection, setPendingUploadFiles, - lsZip, + clearPendingUploads, // - diff --git a/web/packages/next/types/ipc.ts b/web/packages/next/types/ipc.ts index f00409b00..105661036 100644 --- a/web/packages/next/types/ipc.ts +++ b/web/packages/next/types/ipc.ts @@ -601,14 +601,33 @@ export interface FolderWatchSyncedFile { } /** - * When the user starts an upload, we remember the files they'd selected or drag - * and dropped so that we can resume (if needed) when the app restarts after - * being stopped in the middle of the uploads. + * State about pending and in-progress uploads. + * + * When the user starts an upload, we remember the files they'd selected (or + * drag-dropped) so that we can resume if they restart the app in before the + * uploads have been completed. This state is kept on the Electron side, and + * this object is the IPC intermediary. */ export interface PendingUploads { - /** The collection to which we're uploading */ + /** + * The collection to which we're uploading, or the root collection. + * + * This is name of the collection (when uploading to a singular collection) + * or the root collection (when uploading to separate * albums) to which we + * these uploads are meant to go to. See {@link CollectionMapping}. + */ collectionName: string; - /* The upload can be either of a Google Takeout zip, or regular files */ - type: "files" | "zips"; - files: ElectronFile[]; + /** + * Paths of regular files that need to be uploaded. + */ + filePaths: string[]; + /** + * Paths of zip files that need to be uploaded. + */ + zipPaths: string[]; + /** + * For each zip file, which of its entries (paths) within the zip file that + * need to be uploaded. + */ + zipEntries: Record; }