import { useMutation } from "@tanstack/react-query"; import { errorMessage } from "~/lib/error-message"; import { upsellFromResponse } from "~/lib/upsell"; const MAX_FILES_PER_DROP = 5; type UploadState = { status: "idle" } | { status: "uploading"; count: number } | { status: "error"; message: string }; async function uploadOne(owner: string, repo: string, file: File): Promise { const response = await fetch(`/${owner}/${repo}/attachments?name=${encodeURIComponent(file.name)}`, { method: "POST", headers: { "x-gitfor-csrf": "1", "content-type": file.type || "application/octet-stream" }, body: file, }); const payload: { markdown?: string; error?: string } = await response.json(); upsellFromResponse(response.status, payload); if (!response.ok || !payload.markdown) throw new Error(payload.error ?? `Upload of ${file.name} failed`); return payload.markdown; } /** Uploads dropped or pasted files as repo attachments and yields markdown snippets to insert. */ export function useAttachmentUpload(owner: string | undefined, repo: string | undefined) { const enabled = Boolean(owner && repo); const mutation = useMutation({ mutationFn: async (files: File[]) => { if (!owner || !repo) return []; const snippets: string[] = []; for (const file of files) snippets.push(await uploadOne(owner, repo, file)); return snippets; }, }); async function upload(files: FileList | File[]): Promise { const list = [...files].slice(0, MAX_FILES_PER_DROP); if (!enabled || list.length === 0) return []; return mutation.mutateAsync(list).catch(() => []); } const state: UploadState = mutation.isPending ? { status: "uploading", count: mutation.variables.length } : mutation.isError ? { status: "error", message: errorMessage(mutation.error) } : { status: "idle" }; return { enabled, state, upload }; }