This commit is contained in:
Manav Rathi 2024-04-29 15:43:17 +05:30
parent 864a53afa2
commit 2d40f530a7
No known key found for this signature in database
4 changed files with 67 additions and 106 deletions

View file

@ -37,7 +37,7 @@
* - [main] desktop/src/main/ipc.ts contains impl
*/
import { contextBridge, ipcRenderer } from "electron/renderer";
import { contextBridge, ipcRenderer, webUtils } from "electron/renderer";
// While we can't import other code, we can import types since they're just
// needed when compiling and will not be needed or looked around for at runtime.
@ -242,6 +242,8 @@ const watchFindFiles = (folderPath: string): Promise<string[]> =>
// - Upload
const pathForFile = (file: File) => webUtils.getPathForFile(file);
const listZipEntries = (zipPath: string): Promise<ZipEntry[]> =>
ipcRenderer.invoke("listZipEntries", zipPath);
@ -370,6 +372,7 @@ contextBridge.exposeInMainWorld("electron", {
// - Upload
pathForFile,
listZipEntries,
pendingUploads,
setPendingUploads,

View file

@ -1,6 +1,6 @@
import log from "@/next/log";
import { ElectronFile, type FileAndPath } from "@/next/types/file";
import type { CollectionMapping, ZipEntry } from "@/next/types/ipc";
import type { CollectionMapping, Electron, ZipEntry } from "@/next/types/ipc";
import { CustomError } from "@ente/shared/error";
import { isPromise } from "@ente/shared/utils";
import DiscFullIcon from "@mui/icons-material/DiscFull";
@ -86,7 +86,13 @@ interface Props {
activeCollection?: Collection;
}
export default function Uploader(props: Props) {
export default function Uploader({
dragAndDropFiles,
fileSelectorFiles,
folderSelectorFiles,
fileSelectorZipFiles,
...props
}: Props) {
const appContext = useContext(AppContext);
const galleryContext = useContext(GalleryContext);
const publicCollectionGalleryContext = useContext(
@ -266,108 +272,28 @@ export default function Uploader(props: Props) {
// as they are folder being dropped for watching
return;
}
if (
pickedUploadType.current === PICKED_UPLOAD_TYPE.FOLDERS &&
props.webFolderSelectorFiles?.length > 0
) {
log.info(`received folder upload request`);
setWebFiles(props.webFolderSelectorFiles);
} else if (
pickedUploadType.current === PICKED_UPLOAD_TYPE.FILES &&
props.webFileSelectorFiles?.length > 0
) {
log.info(`received file upload request`);
setWebFiles(props.webFileSelectorFiles);
} else if (
pickedUploadType.current === PICKED_UPLOAD_TYPE.ZIPS &&
props.webFileSelectorZipFiles?.length > 0
) {
if (electron) {
const main = async () => {
const zips: File[] = [];
let electronFiles = [] as ElectronFile[];
for (const file of props.webFileSelectorZipFiles) {
if (file.name.endsWith(".zip")) {
const zipFiles = await electron.lsZip(
(file as any).path,
);
log.info(
`zip file - ${file.name} contains ${zipFiles.length} files`,
);
zips.push(file);
// TODO(MR): This cast is incorrect, but interim.
electronFiles = [
...electronFiles,
...(zipFiles as unknown as ElectronFile[]),
];
}
}
// setWebFiles(props.webFileSelectorZipFiles);
zipPaths.current = zips.map((file) => (file as any).path);
setElectronFiles(electronFiles);
};
main();
}
} else if (props.dragAndDropFiles?.length > 0) {
isDragAndDrop.current = true;
if (electron) {
const main = async () => {
try {
// check and parse dropped files which are zip files
log.info(`uploading dropped files from desktop app`);
const zips: File[] = [];
let electronFiles = [] as ElectronFile[];
for (const file of props.dragAndDropFiles) {
if (file.name.endsWith(".zip")) {
const zipFiles = await electron.lsZip(
(file as any).path,
);
log.info(
`zip file - ${file.name} contains ${zipFiles.length} files`,
);
zips.push(file);
// TODO(MR): This cast is incorrect, but interim.
electronFiles = [
...electronFiles,
...(zipFiles as unknown as ElectronFile[]),
];
} else {
// type cast to ElectronFile as the file is dropped from desktop app
// type file and ElectronFile should be interchangeable, but currently they have some differences.
// Typescript is giving error
// Conversion of type 'File' to type 'ElectronFile' may be a mistake because neither type sufficiently
// overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
// Type 'File' is missing the following properties from type 'ElectronFile': path, blob
// for now patching by type casting first to unknown and then to ElectronFile
// TODO: fix types and remove type cast
electronFiles.push(
file as unknown as ElectronFile,
);
}
}
log.info(
`uploading dropped files from desktop app - ${electronFiles.length} files found`,
);
zipPaths.current = zips.map(
(file) => (file as any).path,
);
setElectronFiles(electronFiles);
} catch (e) {
log.error("failed to upload desktop dropped files", e);
setWebFiles(props.dragAndDropFiles);
}
};
main();
} else {
log.info(`uploading dropped files from web app`);
setWebFiles(props.dragAndDropFiles);
}
const files = [
dragAndDropFiles,
fileSelectorFiles,
folderSelectorFiles,
fileSelectorZipFiles,
].flat();
if (electron) {
desktopFilesAndZipEntries(electron, files).then(
({ fileAndPaths, zipEntries }) => {
setDesktopFiles(fileAndPaths);
setDesktopZipEntries(zipEntries);
},
);
} else {
setWebFiles(files);
}
}, [
props.dragAndDropFiles,
props.webFileSelectorFiles,
props.webFolderSelectorFiles,
props.webFileSelectorZipFiles,
dragAndDropFiles,
fileSelectorFiles,
folderSelectorFiles,
fileSelectorZipFiles,
]);
useEffect(() => {
@ -905,6 +831,25 @@ async function waitAndRun(
await task();
}
const desktopFilesAndZipEntries = async (
electron: Electron,
files: File[],
): Promise<{ fileAndPaths: FileAndPath[]; zipEntries: ZipEntry[] }> => {
const fileAndPaths: FileAndPath[] = [];
const zipEntries: ZipEntry[] = [];
for (const file of files) {
const path = electron.pathForFile(file);
if (file.name.endsWith(".zip")) {
zipEntries = zipEntries.concat(await electron.listZipEntries(path));
} else {
fileAndPaths.push({ file, path });
}
}
return { fileAndPaths, zipEntries };
};
// This is used to prompt the user the make upload strategy choice
interface ImportSuggestion {
rootFolderName: string;

View file

@ -18,8 +18,9 @@ export interface ElectronFile {
/**
* When we are running in the context of our desktop app, we have access to the
* absolute path of the file under consideration. This type combines these two
* bits of information to remove the need to query it again and again.
* absolute path of {@link File} objects. This convenience type clubs these two
* bits of information, saving us the need to query the path again and again
* using the {@link getPathForFile} method of {@link Electron}.
*/
export interface FileAndPath {
file: File;

View file

@ -465,6 +465,18 @@ export interface Electron {
// - Upload
/**
* Return the file system path that this File object points to.
*
* This method is a bit different from the other methods on the Electron
* object in the sense that there is no actual IPC happening - the
* implementation of this method is completely in the preload script. Thus
* we can pass it an otherwise unserializable File object.
*
* Consequently, it is also _not_ async.
*/
pathForFile: (file: File) => string;
/**
* Get the list of files that are present in the given zip file.
*
@ -478,7 +490,7 @@ export interface Electron {
*
* To read the contents of the files themselves, see [Note: IPC streams].
*/
listZipEntries : (zipPath: string) => Promise<ZipEntry[]>
listZipEntries: (zipPath: string) => Promise<ZipEntry[]>;
/**
* Return any pending uploads that were previously enqueued but haven't yet