ultisuite-client/components/drive/office-editor.tsx
R3D347HR4Y 82ca9a27db
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
feat(drive): refactor document and drawing editors with new metadata handling
- Replaced suite page metadata with drive-specific metadata for document and drawing editors.
- Introduced new `driveEditorPageMetadata` function to manage titles and favicons based on editor type.
- Updated layout components for document and drawing editors to utilize the new metadata structure.
- Enhanced document title handling in various editor components to reflect the current editing context.
- Added new SVG icons for UltiDocs, UltiSheets, UltiSlides, and UltiDraw to improve visual consistency across editors.
- Improved print styles and layout handling for better document rendering in print and PDF formats.
2026-06-15 15:51:09 +02:00

160 lines
5.0 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.

"use client"
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
import { useRouter } from "next/navigation"
import Link from "next/link"
import { apiClient } from "@/lib/api/client"
import { Button } from "@/components/ui/button"
import { ArrowLeft } from "lucide-react"
import { OnlyOfficeMount } from "@/components/drive/onlyoffice-mount"
import { OfficeEditorChrome } from "@/components/drive/office-editor-chrome"
import { useDriveMutations, useDriveShares } from "@/lib/api/hooks/use-drive-queries"
import { displayFileBaseName } from "@/lib/drive/display-file-name"
import { resolveRenameName } from "@/lib/drive/drive-default-name"
import { driveFolderHref } from "@/lib/drive/drive-sidebar-tree"
import { buildDriveEditHref, resolveDriveEditReturnTo } from "@/lib/drive/drive-url"
import { resolveOnlyOfficeEditorKind } from "@/lib/drive/onlyoffice-formats"
import { useDriveDocumentTitle } from "@/lib/drive/use-drive-document-title"
import { useDriveUIStore } from "@/lib/stores/drive-ui-store"
function fileNameFromPath(filePath: string): string {
return filePath.split("/").filter(Boolean).pop() ?? filePath
}
function renameTargetPath(filePath: string, newName: string): string {
const parent = filePath.replace(/\/[^/]+$/, "") || "/"
const base = parent === "/" ? "" : parent
return `${base}/${newName}`.replace(/\/+/g, "/") || `/${newName}`
}
export function OfficeEditor({
filePath,
returnTo,
}: {
filePath: string
returnTo?: string | null
}) {
const router = useRouter()
const instanceSeq = useRef(0)
const setSharePath = useDriveUIStore((s) => s.setSharePath)
const [config, setConfig] = useState<Record<string, unknown> | null>(null)
const [serverUrl, setServerUrl] = useState("")
const [editorId, setEditorId] = useState<string | null>(null)
const [error, setError] = useState<string | null>(null)
const [displayPath, setDisplayPath] = useState(filePath)
useEffect(() => {
setDisplayPath(filePath)
}, [filePath])
const fileName = fileNameFromPath(displayPath)
const title = displayFileBaseName(fileName)
const editorKind = resolveOnlyOfficeEditorKind({ name: fileName })
useDriveDocumentTitle(title, editorKind)
const backHref = useMemo(
() =>
resolveDriveEditReturnTo(returnTo, displayPath, (folderPath) =>
driveFolderHref("files", folderPath)
),
[returnTo, displayPath]
)
const { data: sharesData } = useDriveShares(displayPath, Boolean(displayPath))
const { rename } = useDriveMutations()
const handleEditorError = useCallback((message: string) => {
setError(message)
}, [])
useEffect(() => {
let cancelled = false
setConfig(null)
setServerUrl("")
setEditorId(null)
setError(null)
void (async () => {
try {
const res = await apiClient.post<{
config: Record<string, unknown>
serverUrl: string
}>("/office/session", { path: displayPath, mode: "edit" })
if (cancelled) return
instanceSeq.current += 1
setConfig(res.config)
setServerUrl(res.serverUrl || process.env.NEXT_PUBLIC_ONLYOFFICE_URL || "")
setEditorId(`ultidrive-editor-${instanceSeq.current}`)
} catch {
if (!cancelled) setError("Impossible de charger léditeur.")
}
})()
return () => {
cancelled = true
}
}, [displayPath])
const handleRename = useCallback(
async (input: string) => {
const newName = resolveRenameName(
{ name: fileName, type: "file" },
input
)
if (displayFileBaseName(fileName) === input.trim()) return
await rename.mutateAsync({ path: displayPath, new_name: newName })
const nextPath = renameTargetPath(displayPath, newName)
setDisplayPath(nextPath)
router.replace(buildDriveEditHref(nextPath, returnTo ?? undefined))
},
[displayPath, fileName, rename, returnTo, router]
)
const openShareDialog = useCallback(() => {
setSharePath(displayPath, "file")
}, [displayPath, setSharePath])
if (error) {
return (
<div className="flex h-dvh flex-col items-center justify-center gap-4">
<p className="text-destructive">{error}</p>
<Button asChild variant="outline">
<Link href={backHref}>
<ArrowLeft className="mr-2 h-4 w-4" />
Retour
</Link>
</Button>
</div>
)
}
if (!config || !editorId || !serverUrl) {
return <p className="p-8 text-center text-muted-foreground">Ouverture du document</p>
}
const docServer = serverUrl.replace(/\/$/, "")
return (
<div className="flex h-dvh flex-col">
<OfficeEditorChrome
backHref={backHref}
backLabel="Drive"
title={title}
onRename={handleRename}
shares={sharesData?.shares ?? []}
onShareClick={openShareDialog}
showShare
showAccount
/>
<div className="relative min-h-0 flex-1">
<OnlyOfficeMount
editorId={editorId}
documentServerUrl={docServer}
config={config}
onError={handleEditorError}
/>
</div>
</div>
)
}