270 lines
8.9 KiB
TypeScript
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 }
|