1import { resolveQueuedEditExit } from "@t3tools/client-runtime/queued-run-edit";
2import type { ChatAttachment } from "@t3tools/contracts";
3import {
4 composerDraftHasUserContent,
5 useComposerDraftStore,
6 type ComposerFileAttachment,
7 type ComposerImageAttachment,
8 type ComposerThreadTarget,
9} from "../../composerDraftStore";
10
11export function recoverQueuedMessageEdit(input: {
12 readonly editTarget: ComposerThreadTarget;
13 readonly threadTarget: ComposerThreadTarget;
14 readonly originalText: string;
15 readonly removedExistingAttachment?: boolean;
16}): "kept" | "discarded" | "clean" {
17 const store = useComposerDraftStore.getState();
18 const draft = store.getComposerDraft(input.editTarget);
19 const recovery = resolveQueuedEditExit({
20 editChanged:
21 input.removedExistingAttachment === true ||
22 (draft !== null &&
23 (draft.prompt !== input.originalText || draft.images.length > 0 || draft.files.length > 0)),
24 // A cleared edit has nothing to put back, so it can only be discarded.
25 threadDraftEmpty:
26 composerDraftHasUserContent(draft) &&
27 !composerDraftHasUserContent(store.getComposerDraft(input.threadTarget)),
28 });
29 if (recovery === "kept") {
30 store.moveComposerPromptAndImages(input.editTarget, input.threadTarget);
31 }
32 store.clearComposerContent(input.editTarget);
33 return recovery;
34}
35
36export async function prepareQueuedEditAttachments(input: {
37 readonly existingAttachments: ReadonlyArray<ChatAttachment>;
38 readonly images: ReadonlyArray<ComposerImageAttachment>;
39 readonly files: ReadonlyArray<ComposerFileAttachment>;
40 readonly uploadFiles: (
41 files: ReadonlyArray<ComposerFileAttachment>,
42 ) => Promise<ReadonlyArray<ChatAttachment>>;
43 readonly readImage: (file: File) => Promise<string>;
44}) {
45 const files = input.files.length === 0 ? [] : await input.uploadFiles(input.files);
46 if (files.length !== input.files.length)
47 throw new Error("Retry or remove failed uploads before saving.");
48 const images = await Promise.all(
49 input.images.map(async (image) => ({
50 type: "image" as const,
51 id: image.id,
52 name: image.name,
53 mimeType: image.mimeType,
54 sizeBytes: image.sizeBytes,
55 dataUrl: await input.readImage(image.file),
56 ...(image.source ? { source: image.source } : {}),
57 })),
58 );
59 return [...input.existingAttachments, ...images, ...files];
60}