47 lines · 1.9 KB
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<string> {
  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<string[]> {
    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 }