From 98e6d683c5bbb9dc9cd3d8996680aa20b05c99e9 Mon Sep 17 00:00:00 2001
From: grnd-alt <git@belakkaf.net>
Date: Wed, 18 Dec 2024 15:23:41 +0100
Subject: [PATCH] insert file element at cursor position

Signed-off-by: grnd-alt <git@belakkaf.net>
---
 src/files/SideBarDownload.tsx | 49 ++++++++++++++---------------------
 src/files/files.ts            | 33 ++++++++++++++---------
 2 files changed, 40 insertions(+), 42 deletions(-)

diff --git a/src/files/SideBarDownload.tsx b/src/files/SideBarDownload.tsx
index f004006..2a015a7 100644
--- a/src/files/SideBarDownload.tsx
+++ b/src/files/SideBarDownload.tsx
@@ -64,21 +64,12 @@ export function ResetDownloadButton() {
 	if (sideBar === undefined) {
 		return
 	}
-	const panelColumn = sideBar.querySelector('.panelColumn')
+	const panelColumn = sideBar.querySelector('.panelColumn') as HTMLElement
 	if (panelColumn) {
-		panelColumn.childNodes.forEach((node) => {
-			if (!node.ELEMENT_NODE) {
-				return
-			}
-			const element = node as HTMLElement
-			if (element.style.display === 'none') {
-				element.style.display = ''
-			}
-			if (element.classList.contains('nc-download')) {
-				panelColumn.removeChild(element)
-			}
-		})
+		panelColumn.style.display = ''
 	}
+	const downloadButton = document.getElementsByClassName('nc-download')[0]
+	sideBar.removeChild(downloadButton)
 }
 
 /**
@@ -88,7 +79,7 @@ export function ResetDownloadButton() {
  * @param onClick onClick callback
  */
 export function InsertDownloadButton(meta: Meta, onClick: () => void) {
-	const observer = new MutationObserver(() => {
+	const callback = () => {
 		const sideBar = document.getElementsByClassName('App-menu__left')[0]
 		if (sideBar === undefined) {
 			return
@@ -98,24 +89,22 @@ export function InsertDownloadButton(meta: Meta, onClick: () => void) {
 		newElement.classList.add('nc-download')
 		const root = createRoot(newElement)
 		root.render(renderDownloadButton(meta, onClick))
-		newElement.addEventListener('click', onClick)
 
-		const panelColumn = sideBar.querySelector('.panelColumn')
+		// hide all excalidraw settings
+		const panelColumn = sideBar.querySelector('.panelColumn') as HTMLElement
 		if (panelColumn) {
-			panelColumn.childNodes.forEach((node) => {
-				// hide all defautl excalidraw setting elements
-				if (!node.ELEMENT_NODE) {
-					return
-				}
-				const element = node as HTMLElement
-				element.style.display = 'none'
-			})
-			panelColumn.appendChild(newElement)
-		} else {
-			sideBar.appendChild(newElement)
+			panelColumn.style.display = 'none'
 		}
-	})
 
-	// wait until sidebar rendered by excalidraw
-	observer.observe(document.body, { childList: true, subtree: true })
+		sideBar.appendChild(newElement)
+	}
+
+	const observer = new MutationObserver(callback)
+
+	const sideBar = document.getElementsByClassName('App-menu__left')[0]
+	if (sideBar !== undefined) {
+		callback()
+	} else {
+		observer.observe(document.body, { childList: true, subtree: true })
+	}
 }
diff --git a/src/files/files.ts b/src/files/files.ts
index 0145633..6cd37ab 100644
--- a/src/files/files.ts
+++ b/src/files/files.ts
@@ -2,7 +2,10 @@
  * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
  * SPDX-License-Identifier: AGPL-3.0-or-later
  */
-import { convertToExcalidrawElements } from '@excalidraw/excalidraw'
+import {
+	convertToExcalidrawElements,
+	viewportCoordsToSceneCoords,
+} from '@excalidraw/excalidraw'
 import type {
 	BinaryFileData,
 	DataURL,
@@ -48,7 +51,7 @@ export class FileHandle {
 		this.excalidrawApi.onPointerDown(async (activeTool, state, event) => {
 			const clickedElement = state.hit.element
 			if (!clickedElement || !clickedElement.customData) {
-			   ResetDownloadButton()
+				ResetDownloadButton()
 				return
 			}
 			InsertDownloadButton(clickedElement.customData.meta, () =>
@@ -65,7 +68,7 @@ export class FileHandle {
 		a.click()
 	}
 
-	private filesDragEventListener(ev: Event) {
+	private filesDragEventListener(ev: DragEvent) {
 		if (ev instanceof DragEvent) {
 			for (const file of Array.from(ev.dataTransfer?.files || [])) {
 				this.handleFileInsert(file, ev)
@@ -97,7 +100,7 @@ export class FileHandle {
 					fileId: constructedFile.id,
 					dataURL: fr.result,
 				}
-				this.addCustomFileElement(constructedFile, meta)
+				this.addCustomFileElement(constructedFile, meta, ev.x, ev.y)
 			}
 		}
 	}
@@ -133,7 +136,13 @@ export class FileHandle {
 	private async addCustomFileElement(
 		constructedFile: BinaryFileData,
 		meta: Meta,
+		clientX: number,
+		clientY: number,
 	) {
+		const { x, y } = viewportCoordsToSceneCoords(
+			{ clientX, clientY },
+			this.excalidrawApi.getAppState(),
+		)
 		const iconId = await this.getMimeIcon(meta.type)
 		this.collab.portal.sendImageFiles({
 			[constructedFile.id]: constructedFile,
@@ -149,8 +158,8 @@ export class FileHandle {
 				strokeWidth: 1,
 				strokeStyle: 'solid',
 				opacity: 30,
-				x: 0,
-				y: 0,
+				x,
+				y,
 				strokeColor: '#1e1e1e',
 				backgroundColor: '#a5d8ff',
 				width: 260.62,
@@ -164,8 +173,8 @@ export class FileHandle {
 			{
 				type: 'image',
 				fileId: meta.fileId as FileId,
-				x: 28.8678679811,
-				y: 16.3505845419,
+				x: x + 28.8678679811,
+				y: y + 16.3505845419,
 				width: 1,
 				height: 1,
 				opacity: 0,
@@ -175,8 +184,8 @@ export class FileHandle {
 			{
 				type: 'image',
 				fileId: iconId,
-				x: 28.8678679811,
-				y: 16.3505845419,
+				x: x + 28.8678679811,
+				y: y + 16.3505845419,
 				width: 48.880073102719564,
 				height: 48.880073102719564,
 				locked: true,
@@ -190,8 +199,8 @@ export class FileHandle {
 				strokeWidth: 1,
 				strokeStyle: 'solid',
 				opacity: 100,
-				x: 85.2856430662,
-				y: 28.8678679811,
+				x: x + 85.2856430662,
+				y: y + 28.8678679811,
 				strokeColor: '#1e1e1e',
 				backgroundColor: 'transparent',
 				width: 140.625,