ultisuite-client/components/drive/public-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

163 lines
5.2 KiB
TypeScript
Raw Permalink 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 Link from "next/link"
import { Button } from "@/components/ui/button"
import { getOnlyOfficeUrl } from "@/lib/runtime-config"
import { ArrowLeft } from "lucide-react"
import { OnlyOfficeMount } from "@/components/drive/onlyoffice-mount"
import { OfficeEditorChrome } from "@/components/drive/office-editor-chrome"
import { displayFileBaseName } from "@/lib/drive/display-file-name"
import { getGuestEditorIdentity } from "@/lib/drive/guest-editor-identity"
import { resolvePublicShareEditReturnTo, shouldShowPublicShareEditorBack } from "@/lib/drive/public-share-url"
import type { PublicShareRootType } from "@/lib/drive/public-share-url"
import { resolveOnlyOfficeEditorKind } from "@/lib/drive/onlyoffice-formats"
import { useDriveDocumentTitle } from "@/lib/drive/use-drive-document-title"
function fileNameFromPath(filePath: string, fallback?: string): string {
const base = filePath.split("/").filter(Boolean).pop()
return base || fallback || filePath
}
export function PublicOfficeEditor({
token,
filePath,
password,
returnTo,
mode = "edit",
fileDisplayName,
shareRoot,
}: {
token: string
filePath: string
password?: string
returnTo?: string | null
mode?: "edit" | "view"
fileDisplayName?: string
shareRoot?: PublicShareRootType | null
}) {
const instanceSeq = useRef(0)
const guest = useMemo(() => getGuestEditorIdentity(token), [token])
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 [resolvedMode, setResolvedMode] = useState<"edit" | "view">(mode)
const fileName = fileDisplayName || fileNameFromPath(filePath)
const title = displayFileBaseName(fileName)
const editorKind = resolveOnlyOfficeEditorKind({ name: fileName })
useDriveDocumentTitle(title, editorKind)
const backHref = useMemo(
() => resolvePublicShareEditReturnTo(token, returnTo, filePath),
[token, returnTo, filePath]
)
const showBack = shouldShowPublicShareEditorBack(shareRoot, returnTo, filePath)
useEffect(() => {
let cancelled = false
setConfig(null)
setServerUrl("")
setEditorId(null)
setError(null)
setResolvedMode(mode)
void (async () => {
try {
const res = await fetch(
`/api/v1/drive/public/shares/${encodeURIComponent(token)}/office/session`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
path: filePath,
mode,
password: password ?? "",
guest_id: guest.guestId,
guest_name: guest.guestName,
display_name: fileName,
}),
}
)
if (!res.ok) throw new Error("session_failed")
const data = (await res.json()) as {
config: Record<string, unknown>
serverUrl: string
mode?: "edit" | "view"
}
if (cancelled) return
instanceSeq.current += 1
setConfig(data.config)
setServerUrl(data.serverUrl || getOnlyOfficeUrl() || "")
setEditorId(`ultidrive-public-editor-${instanceSeq.current}`)
if (data.mode === "edit" || data.mode === "view") {
setResolvedMode(data.mode)
} else {
const editorConfig = data.config?.editorConfig as { mode?: string } | undefined
if (editorConfig?.mode === "edit" || editorConfig?.mode === "view") {
setResolvedMode(editorConfig.mode)
}
}
} catch {
if (!cancelled) setError("Impossible de charger léditeur.")
}
})()
return () => {
cancelled = true
}
}, [token, filePath, password, mode, fileName])
const handleEditorError = useCallback((message: string) => {
setError(message)
}, [])
if (error) {
return (
<div className="flex h-dvh flex-col items-center justify-center gap-4">
<p className="text-destructive">{error}</p>
{showBack ? (
<Button asChild variant="outline">
<Link href={backHref}>
<ArrowLeft className="mr-2 h-4 w-4" />
Retour
</Link>
</Button>
) : null}
</div>
)
}
if (!config || !editorId || !serverUrl) {
return <p className="p-8 text-center text-muted-foreground">Ouverture du document</p>
}
return (
<div className="flex h-dvh flex-col">
<OfficeEditorChrome
backHref={backHref}
backLabel="Partage"
showBack={showBack}
title={title}
trailing={
resolvedMode === "view" ? (
<span className="rounded-md bg-muted px-2 py-1 text-xs text-muted-foreground">
Lecture seule
</span>
) : null
}
/>
<div className="relative min-h-0 flex-1">
<OnlyOfficeMount
editorId={editorId}
documentServerUrl={serverUrl.replace(/\/$/, "")}
config={config}
onError={handleEditorError}
scriptId="onlyoffice-docs-api-public"
/>
</div>
</div>
)
}