ultisuite-client/lib/api/hooks/use-drive-queries.ts
2026-06-09 17:06:20 +02:00

270 lines
8.9 KiB
TypeScript

"use client"
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
import { apiClient } from "@/lib/api/client"
import { driveFilesListApiPath, driveFilterCorpusApiPath } from "@/lib/api/drive-download"
import { useAuthReady } from "@/lib/api/use-auth-ready"
import type { DriveFileInfo, DriveListResponse, DriveQuota, DriveShare, ShareRecipientLookup } from "@/lib/api/types"
import type { DriveShareMode } from "@/lib/drive/drive-share-types"
function filesKey(path: string, page: number, q: string) {
return ["drive", "files", path, page, q] as const
}
function filterCorpusKey(path: string) {
return ["drive", "filter-corpus", path] as const
}
export function useDriveFilterCorpus(path: string, enabled = true) {
const { ready, authenticated } = useAuthReady()
const apiPath = driveFilterCorpusApiPath(path)
return useQuery({
queryKey: filterCorpusKey(path),
enabled: ready && authenticated && enabled,
staleTime: 60_000,
queryFn: () => apiClient.get<DriveListResponse>(apiPath),
})
}
export function useDriveList(path: string, page = 1, q = "", enabled = true) {
const { ready, authenticated } = useAuthReady()
const apiPath = driveFilesListApiPath(path)
return useQuery({
queryKey: filesKey(path, page, q),
enabled: ready && authenticated && enabled,
queryFn: () =>
apiClient.get<DriveListResponse>(`${apiPath}?page=${page}&page_size=50${q ? `&q=${encodeURIComponent(q)}` : ""}`),
})
}
export function useDriveFileById(fileId: string, enabled = true) {
const { ready, authenticated } = useAuthReady()
return useQuery({
queryKey: ["drive", "file", fileId],
enabled: ready && authenticated && enabled && Boolean(fileId),
staleTime: 30_000,
queryFn: () => apiClient.get<DriveFileInfo>(`/drive/files/id/${encodeURIComponent(fileId)}`),
})
}
export function useDriveSharedWithMe(page = 1, q = "", enabled = true) {
const { ready, authenticated } = useAuthReady()
return useQuery({
queryKey: ["drive", "shared", page, q],
enabled: ready && authenticated && enabled,
queryFn: () =>
apiClient.get<DriveListResponse>(
`/drive/shared?page=${page}&page_size=50${q ? `&q=${encodeURIComponent(q)}` : ""}`
),
})
}
export function useDriveTrash(page = 1, q = "") {
const { ready, authenticated } = useAuthReady()
return useQuery({
queryKey: ["drive", "trash", page, q],
enabled: ready && authenticated,
queryFn: () =>
apiClient.get<DriveListResponse>(`/drive/trash?page=${page}&page_size=50${q ? `&q=${encodeURIComponent(q)}` : ""}`),
})
}
export function useDriveRecent() {
const { ready, authenticated } = useAuthReady()
return useQuery({
queryKey: ["drive", "recent"],
enabled: ready && authenticated,
retry: 1,
queryFn: () => apiClient.get<DriveListResponse>("/drive/recent?page_size=50"),
})
}
export function useDriveStarred(path = "/") {
const { ready, authenticated } = useAuthReady()
const suffix = path === "/" ? "" : path
return useQuery({
queryKey: ["drive", "starred", path],
enabled: ready && authenticated,
queryFn: () => apiClient.get<DriveListResponse>(`/drive/starred${suffix}?page_size=50`),
})
}
function searchKey(q: string, scope: string, path: string, page: number, suggest: boolean) {
return ["drive", "search", q, scope, path, page, suggest] as const
}
export function useDriveSearch(
q: string,
scope: string,
path: string,
page = 1,
enabled = true
) {
const { ready, authenticated } = useAuthReady()
const trimmed = q.trim()
return useQuery({
queryKey: searchKey(trimmed, scope, path, page, false),
enabled: ready && authenticated && enabled && trimmed.length > 0,
queryFn: () => {
const params = new URLSearchParams({
q: trimmed,
scope,
page: String(page),
page_size: "50",
})
if (scope === "folder" && path !== "/") {
params.set("path", path)
}
return apiClient.get<DriveListResponse>(`/drive/search?${params.toString()}`)
},
})
}
export function useDriveSearchSuggestions(
q: string,
scope: string,
path: string,
enabled = true
) {
const { ready, authenticated } = useAuthReady()
const trimmed = q.trim()
return useQuery({
queryKey: searchKey(trimmed, scope, path, 1, true),
enabled: ready && authenticated && enabled && trimmed.length >= 2,
staleTime: 30_000,
queryFn: () => {
const params = new URLSearchParams({
q: trimmed,
scope,
suggest: "1",
page_size: "8",
})
if (scope === "folder" && path !== "/") {
params.set("path", path)
}
return apiClient.get<DriveListResponse>(`/drive/search?${params.toString()}`)
},
})
}
export function useDriveQuota() {
const { ready, authenticated } = useAuthReady()
return useQuery({
queryKey: ["drive", "quota"],
enabled: ready && authenticated,
retry: 1,
queryFn: () => apiClient.get<DriveQuota>("/drive/quota"),
})
}
export function useDriveShares(filePath: string, enabled: boolean) {
const { ready, authenticated } = useAuthReady()
return useQuery({
queryKey: ["drive", "shares", filePath],
enabled: ready && authenticated && enabled && Boolean(filePath),
queryFn: () =>
apiClient.get<{ shares: DriveShare[] }>(`/drive/shares?path=${encodeURIComponent(filePath)}`),
})
}
export function useDriveMutations() {
const qc = useQueryClient()
const invalidate = () => qc.invalidateQueries({ queryKey: ["drive"] })
const createFolder = useMutation({
mutationFn: (path: string) => apiClient.post<void>(`/drive/folders${path}`, {}),
onSuccess: invalidate,
})
const deleteFile = useMutation({
mutationFn: (path: string) => apiClient.delete(`/drive/files${path}`),
onSuccess: invalidate,
})
const rename = useMutation({
mutationFn: (body: { path: string; new_name: string }) => apiClient.post<void>("/drive/rename", body),
onSuccess: invalidate,
})
const move = useMutation({
mutationFn: (body: { source: string; destination: string }) => apiClient.post<void>("/drive/move", body),
onSuccess: invalidate,
})
const copy = useMutation({
mutationFn: (body: { source: string; destination: string }) => apiClient.post<void>("/drive/copy", body),
onSuccess: invalidate,
})
const favorite = useMutation({
mutationFn: (body: { path: string; favorite: boolean }) => apiClient.post<void>("/drive/favorite", body),
onSuccess: invalidate,
})
const restore = useMutation({
mutationFn: (name: string) => apiClient.post<void>("/drive/trash/restore", { name }),
onSuccess: invalidate,
})
const deleteTrash = useMutation({
mutationFn: (name: string) => apiClient.post<void>("/drive/trash/delete", { name }),
onSuccess: invalidate,
})
const emptyTrash = useMutation({
mutationFn: () => apiClient.delete("/drive/trash"),
onSuccess: invalidate,
})
const createShare = useMutation({
mutationFn: (body: {
path: string
mode?: DriveShareMode
role?: string
permissions?: number
share_type?: number
share_with?: string
note?: string
send_mail?: boolean
}) =>
apiClient.post<DriveShare>("/drive/shares", {
path: body.path,
mode: body.mode ?? "public",
...(body.mode === "contact"
? {
share_with: body.share_with,
note: body.note,
send_mail: body.send_mail ?? true,
}
: body.mode === "internal"
? { share_type: 3 }
: { share_type: body.share_type ?? 3 }),
...(body.permissions != null && body.permissions > 0
? { permissions: body.permissions }
: { role: body.role ?? "viewer" }),
}),
onSuccess: (_data, variables) => {
qc.invalidateQueries({ queryKey: ["drive"] })
if (variables.path) {
qc.invalidateQueries({ queryKey: ["drive", "shares", variables.path] })
}
},
})
const deleteShare = useMutation({
mutationFn: (shareId: string) => apiClient.delete(`/drive/shares/${shareId}`),
onSuccess: invalidate,
})
const lookupShareRecipient = useMutation({
mutationFn: (email: string) =>
apiClient.get<ShareRecipientLookup>(
`/drive/shares/recipients/lookup?email=${encodeURIComponent(email)}`
),
})
const createFile = useMutation({
mutationFn: (body: { parent_path: string; name: string; kind: string }) =>
apiClient.post<{ path: string }>("/office/create", body),
onSuccess: invalidate,
})
return { createFolder, deleteFile, rename, move, copy, favorite, restore, deleteTrash, emptyTrash, createShare, deleteShare, lookupShareRecipient, createFile, invalidate }
}
/** @deprecated Use openDriveFileInNewTab / downloadDriveFile — API requires Authorization. */
export function fileDownloadUrl(path: string): string {
const base = process.env.NEXT_PUBLIC_API_URL ?? "/api/v1"
return `${base}/drive/download${path.startsWith("/") ? path : `/${path}`}`
}
export type { DriveFileInfo }