Attach dropped files in every composer (#1750)

* fix(app): attach dropped files in composer

Composer parents had to expose attachment callbacks, so New Workspace only wired image drops and JSON files never became file attachments. Centralize dropped-file ingestion in Composer so all composer surfaces share the same image and file path.

* fix(app): handle desktop dropped attachment paths
This commit is contained in:
Mohamed Boudra
2026-06-27 09:03:04 +08:00
committed by GitHub
parent f275275074
commit 65f1143e6a
12 changed files with 488 additions and 388 deletions
+3 -38
View File
@@ -2,35 +2,9 @@ import { useCallback, useRef } from "react";
import * as DocumentPicker from "expo-document-picker";
import { File } from "expo-file-system";
import { getDesktopHost, isElectronRuntime } from "@/desktop/host";
import { copyDesktopAttachmentFile } from "@/desktop/attachments/desktop-file-commands";
import { readDesktopFileBase64 } from "@/desktop/attachments/desktop-preview-url";
import { isWeb } from "@/constants/platform";
import { getFileExtension, getMimeTypeFromPath } from "@/attachments/file-types";
export interface PickedFile {
fileName: string;
mimeType: string;
bytes: Uint8Array;
}
function base64ToUint8Array(base64: string): Uint8Array {
const binaryString = atob(base64);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes;
}
export async function readDesktopFileBytes(path: string): Promise<Uint8Array> {
const { path: managedPath } = await copyDesktopAttachmentFile({
attachmentId: crypto.randomUUID(),
sourcePath: path,
extension: getFileExtension(path) || null,
});
const base64 = await readDesktopFileBase64(managedPath);
return base64ToUint8Array(base64);
}
import { getMimeTypeFromPath } from "@/attachments/file-types";
import { readDesktopFileBytes, type PickedFile } from "@/attachments/picked-file";
async function pickFilesWithDesktopDialog(): Promise<PickedFile[] | null> {
const dialog = getDesktopHost()?.dialog;
@@ -58,16 +32,7 @@ async function pickFilesWithDesktopDialog(): Promise<PickedFile[] | null> {
for (const filePath of paths) {
const fileName = filePath.split("/").pop() ?? filePath.split("\\").pop() ?? filePath;
const mimeType = getMimeTypeFromPath(filePath);
// Copy into managed storage so we can read it through the existing secure IPC.
const { path: managedPath } = await copyDesktopAttachmentFile({
attachmentId: crypto.randomUUID(),
sourcePath: filePath,
extension: getFileExtension(filePath) || null,
});
const base64 = await readDesktopFileBase64(managedPath);
const bytes = base64ToUint8Array(base64);
const bytes = await readDesktopFileBytes(filePath);
result.push({ fileName, mimeType, bytes });
}