mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-27 21:33:41 +02:00
Submitting a composer mid-upload silently lost the file: the editor holds a `blob:` placeholder that gets stripped during serialization, and the attachment id does not exist yet, so the send succeeded without the file. Chat and Quick Create gated this; manual issue create, Feedback's button, comments, replies and comment edit did not. Gate every action that FIXES a draft — Send/Create/Save, Cmd/Ctrl+Enter, Enter on the title, and the manual/agent mode switches — behind one source of truth: the editor document, which IS the upload queue. ContentEditor publishes queue transitions via `onUploadingChange` (a transaction listener, not `onUpdate` — that path is debounced and skips no-change emissions, and a failed upload leaves byte-identical markdown, so the un-gate would never fire). `useUploadGate` pairs that render state with an `isBlocked()` re-read at submit time, since the shortcut paths never consult the button. The publisher emits its current answer on subscribe rather than only on flips: hosts outlive editor instances (comment edit remounts on cancel, chat swaps by key on agent switch), and an editor torn down mid-upload would otherwise leave submit wedged shut with no pending node left to reopen it. Also fixes the failure toast that never fired: `uploadWithToast` only reported errors if a caller passed `onError`, and none did, so failed uploads removed their placeholder and vanished unexplained. `useEditorUpload` now supplies it once for every composer. Per MUL-4808: drops Quick Create's upload-time lock on the attach button (files can queue), and leaves description autosave ungated by design. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: multica-agent <github@multica.ai>
35 lines
1.1 KiB
TypeScript
35 lines
1.1 KiB
TypeScript
"use client";
|
|
|
|
import { useCallback } from "react";
|
|
import { toast } from "sonner";
|
|
import { api } from "@multica/core/api";
|
|
import { useFileUpload } from "@multica/core/hooks/use-file-upload";
|
|
import { useT } from "../i18n";
|
|
|
|
/**
|
|
* `useFileUpload` wired to the failure toast the upload gate depends on.
|
|
*
|
|
* The gate's failure fallback is "drop the placeholder, say so, let them
|
|
* submit again". Dropping the placeholder is handled in the upload extension,
|
|
* but without the toast the file just vanishes mid-upload with no explanation
|
|
* — which is what every composer did, since `uploadWithToast` only toasts if
|
|
* the caller supplies `onError` and no caller ever did (MUL-4808).
|
|
*
|
|
* `useFileUpload` lives in `@multica/core`, which may not import a UI library,
|
|
* so the toast is supplied here — once, rather than per composer.
|
|
*/
|
|
function useEditorUpload() {
|
|
const { t } = useT("editor");
|
|
const onError = useCallback(
|
|
(error: Error, file: File) => {
|
|
toast.error(
|
|
t(($) => $.upload.failed, { filename: file.name, reason: error.message }),
|
|
);
|
|
},
|
|
[t],
|
|
);
|
|
return useFileUpload(api, onError);
|
|
}
|
|
|
|
export { useEditorUpload };
|