"use client";
/**
* The submit gate for composers that accept attachments (MUL-4808).
*
* While a file is still uploading, the editor holds a `blob:` placeholder and
* the attachment's id does not exist yet. Serializing at that moment strips
* the blob (see ContentEditor's stripBlobUrls) and binds no id, so the send
* succeeds while the file silently disappears from what the recipient gets.
* Every action that FIXES a draft — send, create, save, and the mode switch
* that re-serializes a draft into another form — must therefore wait.
*
* Two signals, because they answer different questions:
*
* `uploading` — reactive, drives the rendered button (disabled / "Uploading…"
* / aria-busy). Sourced from the editor document via
* `onUploadingChange`, which is the actual upload queue.
*
* `isBlocked()` — imperative, read INSIDE the submit handler. A rendered
* disabled state is a frame behind: Cmd+Enter and Enter-on-title
* bypass the button entirely, and a click can land in the same
* tick an upload starts. This reads the document at invocation
* time, which is the only answer that can't be stale.
*
* Hosts must use both. Gating the button alone leaves the shortcut paths open;
* gating the handler alone leaves a live-looking button that silently no-ops.
*
* Kept free of `api` / i18n / toast imports on purpose — the failure toast
* lives in `use-editor-upload` — so host tests can run the real gate against a
* mocked editor without pulling the API singleton into the module graph.
*/
import { useCallback, useState, type RefObject } from "react";
import type { ContentEditorRef } from "./content-editor";
interface UploadGate {
/** True while any attachment in this editor is mid-upload. Render state. */
uploading: boolean;
/** Pass to ``. */
onUploadingChange: (uploading: boolean) => void;
/** Submit-time truth. Call at the top of every submit/save/switch handler. */
isBlocked: () => boolean;
}
/**
* Reactive + submit-time upload state for one editor.
*
* The returned `isBlocked` reads the editor ref rather than `uploading` on
* purpose — see the module docstring.
*/
function useUploadGate(editorRef: RefObject): UploadGate {
const [uploading, setUploading] = useState(false);
const isBlocked = useCallback(
() => editorRef.current?.hasActiveUploads() === true,
[editorRef],
);
return { uploading, onUploadingChange: setUploading, isBlocked };
}
export { useUploadGate, type UploadGate };