"use client" import { useCallback, useEffect, useMemo, useRef, useState } from "react" import { apiClient } from "@/lib/api/client" import Link from "next/link" import { Button } from "@/components/ui/button" import { ArrowLeft } from "lucide-react" import { driveFolderHref } from "@/lib/drive/drive-sidebar-tree" import { resolveDriveEditReturnTo } from "@/lib/drive/drive-url" type DocEditorInstance = { destroyEditor: () => void } declare global { interface Window { DocsAPI?: { DocEditor: new (id: string, config: Record) => DocEditorInstance } DocEditor?: { instances: Record } } } let docsApiLoad: Promise | null = null function loadDocsApi(documentServerUrl: string): Promise { if (window.DocsAPI) return Promise.resolve() if (docsApiLoad) return docsApiLoad const base = documentServerUrl.replace(/\/$/, "") + "/" docsApiLoad = new Promise((resolve, reject) => { const script = document.createElement("script") script.id = "onlyoffice-docs-api" script.src = `${base}web-apps/apps/api/documents/api.js` script.async = true script.onload = () => resolve() script.onerror = () => { docsApiLoad = null reject(new Error(`Error load DocsAPI from ${base}`)) } document.body.appendChild(script) }) return docsApiLoad } function destroyDocEditor(id: string) { const inst = window.DocEditor?.instances?.[id] if (inst) { try { inst.destroyEditor() } catch { /* ignore */ } delete window.DocEditor!.instances[id] } document.getElementById(id)?.replaceChildren() } function OnlyOfficeMount({ editorId, documentServerUrl, config, onError, }: { editorId: string documentServerUrl: string config: Record onError: (message: string) => void }) { const configJson = JSON.stringify(config) const onErrorRef = useRef(onError) onErrorRef.current = onError useEffect(() => { let cancelled = false const id = editorId const parsed = JSON.parse(configJson) as Record const editorConfig: Record = { type: "desktop", width: "100%", height: "100%", events: { onDocumentReady: () => { /* loaded */ }, onError: (event: { data?: { errorCode?: number; errorDescription?: string } }) => { const code = event?.data?.errorCode const desc = event?.data?.errorDescription const msg = desc || (code != null ? `OnlyOffice error ${code}` : "Erreur OnlyOffice.") onErrorRef.current(msg) }, }, ...parsed, } void loadDocsApi(documentServerUrl) .then(() => { if (cancelled) return if (!window.DocsAPI) throw new Error("DocsAPI is not defined") destroyDocEditor(id) if (!window.DocEditor) window.DocEditor = { instances: {} } const editor = new window.DocsAPI.DocEditor(id, editorConfig) window.DocEditor.instances[id] = editor }) .catch((err: unknown) => { if (!cancelled) { onErrorRef.current( err instanceof Error ? err.message : "Impossible de charger OnlyOffice.", ) } }) return () => { cancelled = true destroyDocEditor(id) } }, [editorId, documentServerUrl, configJson]) return
} export function OfficeEditor({ filePath, returnTo, }: { filePath: string returnTo?: string | null }) { const instanceSeq = useRef(0) const [config, setConfig] = useState | null>(null) const [serverUrl, setServerUrl] = useState("") const [editorId, setEditorId] = useState(null) const [error, setError] = useState(null) const backHref = useMemo( () => resolveDriveEditReturnTo(returnTo, filePath, (folderPath) => driveFolderHref("files", folderPath) ), [returnTo, filePath] ) 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 serverUrl: string }>("/office/session", { path: filePath, 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 } }, [filePath]) if (error) { return (

{error}

) } if (!config || !editorId || !serverUrl) { return

Ouverture du document…

} const docServer = serverUrl.replace(/\/$/, "") return (
{filePath.split("/").pop()}
) }