- 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.
172 lines
5.6 KiB
TypeScript
172 lines
5.6 KiB
TypeScript
"use client"
|
|
|
|
import { useCallback, useMemo, useState } from "react"
|
|
import { toast } from "sonner"
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuTrigger,
|
|
} from "@/components/ui/dropdown-menu"
|
|
import { DriveMoveDialog, type DriveFolderPickerMode } from "@/components/drive/drive-move-dialog"
|
|
import { useDriveMutations } from "@/lib/api/hooks/use-drive-queries"
|
|
import type { DriveFileInfo } from "@/lib/api/types"
|
|
import { displayFileName } from "@/lib/drive/display-file-name"
|
|
import { resolveRenameName } from "@/lib/drive/drive-default-name"
|
|
import { guardDriveMenuPointer, stopDriveMenuBubble } from "@/lib/drive/drive-menu-guard"
|
|
import { useDriveUIStore } from "@/lib/stores/drive-ui-store"
|
|
import { DriveNameDialog } from "@/components/drive/drive-name-dialog"
|
|
import { DRIVE_MENU_SURFACE_CLASS } from "@/components/drive/drive-file-context-menu"
|
|
import { DriveFileMenuActions } from "@/components/drive/drive-file-menu-actions"
|
|
import { MoreVertical } from "lucide-react"
|
|
import { DRIVE_MENU_BTN } from "@/lib/drive/drive-chrome-classes"
|
|
import { cn } from "@/lib/utils"
|
|
|
|
export function useDriveActionTargets(file: DriveFileInfo, allItems: DriveFileInfo[]) {
|
|
const selectedPaths = useDriveUIStore((s) => s.selectedPaths)
|
|
return useMemo(() => {
|
|
if (selectedPaths.size > 1 && selectedPaths.has(file.path)) {
|
|
const picked = allItems.filter((item) => selectedPaths.has(item.path))
|
|
if (picked.length > 0) return picked
|
|
}
|
|
return [file]
|
|
}, [allItems, file, selectedPaths])
|
|
}
|
|
|
|
export function DriveFileMenuButton({
|
|
file,
|
|
allItems,
|
|
isTrash,
|
|
allowShare = true,
|
|
writable = true,
|
|
hideFavorite = false,
|
|
mutations: mutationsProp,
|
|
onDownloadRequest,
|
|
onOpen,
|
|
onActiveChange,
|
|
className,
|
|
}: {
|
|
file: DriveFileInfo
|
|
allItems: DriveFileInfo[]
|
|
isTrash?: boolean
|
|
allowShare?: boolean
|
|
writable?: boolean
|
|
hideFavorite?: boolean
|
|
mutations?: ReturnType<typeof useDriveMutations>
|
|
onDownloadRequest?: () => void
|
|
onOpen: () => void
|
|
/** Visual highlight only — does not update global selection or bulk bar. */
|
|
onActiveChange?: (active: boolean) => void
|
|
className?: string
|
|
}) {
|
|
const [open, setOpen] = useState(false)
|
|
const [renameOpen, setRenameOpen] = useState(false)
|
|
const [folderPickerMode, setFolderPickerMode] = useState<DriveFolderPickerMode | null>(null)
|
|
const setSharePath = useDriveUIStore((s) => s.setSharePath)
|
|
const mutationsDefault = useDriveMutations()
|
|
const mutations = mutationsProp ?? mutationsDefault
|
|
const targets = useDriveActionTargets(file, allItems)
|
|
const renameTarget = targets.length === 1 ? targets[0] : null
|
|
|
|
const handleRename = async (input: string) => {
|
|
const target = targets[0]
|
|
if (!target) return
|
|
const newName = resolveRenameName(target, input)
|
|
if (displayFileName(target.name) === newName) return
|
|
try {
|
|
await mutations.rename.mutateAsync({ path: target.path, new_name: newName })
|
|
toast.success("Renommé")
|
|
} catch {
|
|
toast.error("Impossible de renommer")
|
|
throw new Error("rename failed")
|
|
}
|
|
}
|
|
|
|
const closeDropdown = useCallback(() => {
|
|
setOpen(false)
|
|
onActiveChange?.(false)
|
|
guardDriveMenuPointer()
|
|
}, [onActiveChange])
|
|
|
|
const openFolderPicker = (mode: DriveFolderPickerMode) => {
|
|
closeDropdown()
|
|
window.setTimeout(() => setFolderPickerMode(mode), 0)
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<DriveNameDialog
|
|
open={renameOpen}
|
|
onOpenChange={setRenameOpen}
|
|
title="Renommer"
|
|
defaultValue={renameTarget ? displayFileName(renameTarget.name) : ""}
|
|
confirmLabel="Renommer"
|
|
onConfirm={handleRename}
|
|
/>
|
|
<DriveMoveDialog
|
|
open={folderPickerMode !== null}
|
|
onOpenChange={(next) => {
|
|
if (!next) setFolderPickerMode(null)
|
|
}}
|
|
mode={folderPickerMode ?? "move"}
|
|
sources={targets}
|
|
/>
|
|
<DropdownMenu
|
|
modal
|
|
open={open}
|
|
onOpenChange={(next) => {
|
|
if (next) {
|
|
setOpen(true)
|
|
onActiveChange?.(true)
|
|
return
|
|
}
|
|
closeDropdown()
|
|
}}
|
|
>
|
|
<DropdownMenuTrigger asChild>
|
|
<button
|
|
type="button"
|
|
data-drive-menu-btn
|
|
aria-label="Actions"
|
|
className={cn(DRIVE_MENU_BTN, className)}
|
|
onClick={(e) => stopDriveMenuBubble(e)}
|
|
onPointerDown={(e) => stopDriveMenuBubble(e)}
|
|
>
|
|
<MoreVertical className="h-4 w-4" />
|
|
</button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent
|
|
align="end"
|
|
data-drive-menu-surface
|
|
className={cn(DRIVE_MENU_SURFACE_CLASS, "w-52")}
|
|
onCloseAutoFocus={(e) => e.preventDefault()}
|
|
onPointerDown={(e) => stopDriveMenuBubble(e)}
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<DriveFileMenuActions
|
|
variant="dropdown"
|
|
targets={targets}
|
|
isTrash={isTrash}
|
|
allowShare={allowShare}
|
|
writable={writable}
|
|
hideFavorite={hideFavorite}
|
|
onOpen={() => {
|
|
closeDropdown()
|
|
onOpen()
|
|
}}
|
|
onClose={closeDropdown}
|
|
setSharePath={setSharePath}
|
|
mutations={mutations}
|
|
onRenameRequest={() => {
|
|
closeDropdown()
|
|
window.setTimeout(() => setRenameOpen(true), 0)
|
|
}}
|
|
onMoveRequest={isTrash ? undefined : () => openFolderPicker("move")}
|
|
onCopyRequest={isTrash ? undefined : () => openFolderPicker("copy")}
|
|
onDownloadRequest={onDownloadRequest}
|
|
/>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</>
|
|
)
|
|
}
|