Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion frontend/src/io-managers/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
let canvasFocused = true;
let inPointerLock = false;
const shakeSamples: { x: number; y: number; time: number }[] = [];
let lastMousePosition: [number, number] | undefined;
let lastShakeTime = 0;

// Event listeners
Expand Down Expand Up @@ -159,6 +160,8 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
function onPointerMove(e: PointerEvent) {
potentiallyRestoreCanvasFocus(e);

lastMousePosition = [e.clientX, e.clientY];

if (!e.buttons) viewportPointerInteractionOngoing = false;

// Don't redirect pointer movement to the backend if there's no ongoing interaction and it's over a floating menu, or the graph overlay, on top of the canvas
Expand Down Expand Up @@ -320,7 +323,7 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli

Array.from(dataTransfer.items).forEach(async (item) => {
if (item.type === "text/plain") item.getAsString((text) => editor.handle.pasteText(text));
await pasteFile(item, editor);
await pasteFile(item, editor, lastMousePosition);
});
Comment on lines 324 to 327
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using forEach with an async callback is an anti-pattern because forEach does not wait for the promises to resolve. This can lead to race conditions if multiple files are pasted, as they will be processed in parallel. It's safer to process them sequentially using a for...of loop.

Additionally, the logic can be made more robust by checking item.kind to distinguish between file and string items, and by awaiting the result of getAsString to ensure sequential processing of all pasted items. This avoids race conditions between pasting text and files.

Note that the onPaste function will also need to be marked as async for await to work.

for (const item of dataTransfer.items) {
	if (item.kind === "file") {
		await pasteFile(item, editor, lastMousePosition);
	} else if (item.kind === "string" && item.type === "text/plain") {
		const text = await new Promise<string>((resolve) => item.getAsString(resolve));
		editor.handle.pasteText(text);
	}
}

}

Expand Down