- Updated .env.example to include configuration for OnlyOffice Document Server. - Modified the workspace configuration to remove the drive-suite path. - Adjusted TypeScript environment imports for consistency. - Enhanced Next.js configuration to disable canvas in Webpack. - Updated package.json to include new dependencies for OnlyOffice and PDF.js. - Added global styles for OnlyOffice theme integration in the CSS. - Created new layout and page components for the Drive feature, including public sharing and editing functionalities. - Updated metadata handling across various layouts to reflect the new app structure.
157 lines
4.2 KiB
TypeScript
157 lines
4.2 KiB
TypeScript
"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<number | null>(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<typeof resolveSources>) => {
|
|
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),
|
|
}
|
|
}
|