"use client" import { useCallback, useRef, useState } from "react" import { toast } from "sonner" import { useDriveMutations } from "@/lib/api/hooks/use-drive-queries" import { getDriveDragData } from "@/lib/drive/drive-dnd" import { isMoveDestinationBlocked, isMoveToSameFolder, moveDriveItemsToFolder, } from "@/lib/drive/drive-move-items" import { useDriveUIStore } from "@/lib/stores/drive-ui-store" const HOVER_EXPAND_MS = 700 export function useDriveDropTarget({ folderPath, disabled, hasChildFolders, onExpandRequest, }: { folderPath: string disabled?: boolean hasChildFolders?: boolean onExpandRequest?: () => void }) { const [isOver, setIsOver] = useState(false) const [canDrop, setCanDrop] = useState(false) const enterCountRef = useRef(0) const expandTimerRef = useRef(null) const mutations = useDriveMutations() const clearSelection = useDriveUIStore((s) => s.clearSelection) const draggingItems = useDriveUIStore((s) => s.draggingItems) const setDraggingItems = useDriveUIStore((s) => s.setDraggingItems) const clearExpandTimer = useCallback(() => { if (expandTimerRef.current !== null) { window.clearTimeout(expandTimerRef.current) expandTimerRef.current = null } }, []) const resolveSources = useCallback( (event: React.DragEvent) => { return getDriveDragData(event.dataTransfer) ?? draggingItems }, [draggingItems] ) const evaluateDrop = useCallback( (sources: ReturnType) => { if (disabled || !sources || sources.length === 0) return false if (isMoveDestinationBlocked(sources, folderPath)) return false if (isMoveToSameFolder(sources, folderPath)) return false return true }, [disabled, folderPath] ) const onDragOver = useCallback( (event: React.DragEvent) => { if (disabled) return const sources = draggingItems const allowed = evaluateDrop(sources) if (!allowed) return event.preventDefault() event.dataTransfer.dropEffect = "move" setCanDrop(true) if (hasChildFolders && onExpandRequest && expandTimerRef.current === null) { expandTimerRef.current = window.setTimeout(() => { expandTimerRef.current = null onExpandRequest() }, HOVER_EXPAND_MS) } }, [disabled, draggingItems, evaluateDrop, hasChildFolders, onExpandRequest] ) const onDragEnter = useCallback( (event: React.DragEvent) => { if (disabled) return enterCountRef.current += 1 if (enterCountRef.current === 1) { setIsOver(true) setCanDrop(evaluateDrop(draggingItems)) } }, [disabled, draggingItems, evaluateDrop] ) const onDragLeave = useCallback(() => { enterCountRef.current = Math.max(0, enterCountRef.current - 1) if (enterCountRef.current === 0) { setIsOver(false) setCanDrop(false) clearExpandTimer() } }, [clearExpandTimer]) const onDrop = useCallback( (event: React.DragEvent) => { event.preventDefault() event.stopPropagation() enterCountRef.current = 0 setIsOver(false) setCanDrop(false) clearExpandTimer() if (disabled) return const sources = resolveSources(event) if (!sources) return if (isMoveDestinationBlocked(sources, folderPath)) { toast.error("Impossible de déplacer un dossier dans lui-même") return } if (isMoveToSameFolder(sources, folderPath)) return void (async () => { try { await moveDriveItemsToFolder(sources, folderPath, (body) => mutations.move.mutateAsync(body) ) toast.success(sources.length > 1 ? "Éléments déplacés" : "Élément déplacé") clearSelection() } catch { toast.error("Impossible de déplacer") } finally { setDraggingItems(null) } })() }, [ clearExpandTimer, clearSelection, disabled, folderPath, mutations.move, resolveSources, setDraggingItems, ] ) return { isOver, canDrop, dropProps: disabled ? ({} as const) : ({ onDragOver, onDragEnter, onDragLeave, onDrop, } as const), } }