Files
Aether/frontend/src/hooks/useInternalDrag.ts
T
marcandClaude Opus 5 b5a83e4057 fix(upload): gezogenes Vorschaubild wurde erneut hochgeladen
Zieht man das angezeigte Bild ein Stueck zur Seite, legt Chrome es als
echte Datei in den Ziehvorgang. dataTransfer.types enthaelt dann
"Files" - nicht zu unterscheiden von einer Datei aus dem Dateimanager.
Die Ablageflaeche lud das eigene Bild daraufhin ein zweites Mal hoch.

Zwei Riegel: draggable={false} an allen Bildern, die kein eigenes
Ziehen brauchen (die Kachel bleibt ziehbar und verschiebt die Datei),
und useInternalDrag - was in dieser Seite losgezogen ist, wird nie als
Upload behandelt. dragstart feuert nur bei eigener Quelle.

Gemessen: vorher 1 Upload, nachher 0. Ein Zug von aussen laedt
unveraendert hoch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 17:24:22 +02:00

45 lines
1.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useEffect, useRef } from 'react'
/**
* Läuft gerade ein Ziehvorgang, der **in dieser Seite** begonnen hat?
*
* Anlass war ein Fehler, den man nur beim Benutzen findet: Zieht man das
* angezeigte Bild ein Stück zur Seite, legt Chrome es als echte Datei in die
* Zwischenablage des Ziehvorgangs – `dataTransfer.types` enthält dann
* `Files`, genau wie bei einer Datei aus dem Dateimanager. Die Ablagefläche
* konnte beides nicht unterscheiden und lud das Bild ein zweites Mal hoch.
*
* Die Kennungen `aether-file-id` und `aether-folder-id` helfen hier nicht:
* Die setzt unser eigener Verschiebe-Code, ein vom Browser begonnener
* Bild-Zug hat sie nicht.
*
* Deshalb der Umweg über das Ereignis: `dragstart` feuert nur, wenn die
* Quelle in diesem Dokument liegt. Kommt etwas von außen, gibt es kein
* `dragstart` – und nur dann darf hochgeladen werden.
*/
export function useInternalDrag(): () => boolean {
const active = useRef(false)
useEffect(() => {
const start = () => { active.current = true }
// Der Rückweg über zwei Ereignisse: `dragend` feuert an der Quelle,
// `drop` am Ziel. Fällt eines aus (abgebrochener Zug außerhalb des
// Fensters), setzt das andere zurück.
const stop = () => { active.current = false }
// Setzen in der Erfassungsphase, damit die Marke steht, bevor irgendein
// Handler sie liest. **Zurücksetzen dagegen in der Blasenphase**: Ein
// `drop`-Listener am Dokument mit `capture` liefe *vor* der Ablagefläche,
// hätte die Marke also schon gelöscht, wenn sie gebraucht wird.
document.addEventListener('dragstart', start, true)
document.addEventListener('dragend', stop)
document.addEventListener('drop', stop)
return () => {
document.removeEventListener('dragstart', start, true)
document.removeEventListener('dragend', stop)
document.removeEventListener('drop', stop)
}
}, [])
return () => active.current
}