ultisuite-client/components/drive/office-editor.tsx
R3D347HR4Y d6d18f911b
Some checks failed
E2E / Playwright e2e (push) Has been cancelled
Lots of stuff and mobile app
2026-06-17 00:13:28 +02:00

161 lines
5.1 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 { getOnlyOfficeUrl } from "@/lib/runtime-config"
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 || getOnlyOfficeUrl() || "")
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>
)
}