ultisuite-client/components/drive/office-editor.tsx
R3D347HR4Y 5304790ed5
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
feat(auth): enhance session management and identity provider settings
- Added SessionGuard component to manage session expiration and online status.
- Updated AuthProvider to streamline session fetching and handling.
- Introduced IdentityProvidersSection for managing OAuth, SAML, and LDAP identity providers.
- Implemented identity provider guides for easier configuration.
- Enhanced mail settings with infinite scroll option for improved user experience.
- Updated global styles and layout components for better consistency across the application.
2026-06-09 09:36:46 +02:00

158 lines
4.9 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 { 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)
useDriveDocumentTitle(title)
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>
)
}