1import type { ThreadTitleRegenerationOutcome } from "@t3tools/contracts";
2import {
3 type AtomCommandResult,
4 isAtomCommandInterrupted,
5 squashAtomCommandFailure,
6} from "@t3tools/client-runtime/state/runtime";
7
8import { stackedThreadToast } from "./ui/toast";
9
10type TitleRegenerationResult = AtomCommandResult<ThreadTitleRegenerationOutcome | null, unknown>;
11
12function failureMessage(result: TitleRegenerationResult): string | null {
13 if (result._tag === "Failure") {
14 if (isAtomCommandInterrupted(result)) return null;
15 const error = squashAtomCommandFailure(result);
16 return error instanceof Error ? error.message : "An error occurred.";
17 }
18 return result.value?.status === "failed"
19 ? (result.value.message ?? "Title generation failed.")
20 : null;
21}
22
23function isKept(result: TitleRegenerationResult): boolean {
24 return result._tag === "Success" && result.value?.status === "unchanged";
25}
26
27export function titleRegenerationToast(results: ReadonlyArray<TitleRegenerationResult>) {
28 const failures = results.flatMap((result) => {
29 const message = failureMessage(result);
30 return message === null ? [] : [message];
31 });
32 if (failures.length > 0) {
33 return stackedThreadToast({
34 type: "error",
35 title:
36 results.length === 1
37 ? "Failed to regenerate thread title"
38 : `Failed to regenerate ${failures.length} of ${results.length} thread titles`,
39 description: failures[0],
40 });
41 }
42 const kept = results.filter(isKept).length;
43 if (kept === 0) return null;
44 if (results.length === 1) {
45 return stackedThreadToast({
46 type: "info",
47 title: "Title kept",
48 description: "The current title already fits this thread.",
49 });
50 }
51 return stackedThreadToast({
52 type: "info",
53 title: kept === results.length ? "Titles kept" : `Kept ${kept} of ${results.length} titles`,
54 description:
55 kept === results.length
56 ? "The current titles already fit these threads."
57 : "Those titles already fit their threads.",
58 });
59}