1import {
2 alternateComposerDispatchAction,
3 type ActiveTurnComposerAction,
4} from "@t3tools/client-runtime/state/composer-dispatch";
5
6import type { FollowUpBehavior } from "../../lib/followUpBehavior";
7
8export interface ComposerSendPresentation {
9 readonly label: string;
10 readonly icon: "arrow.up" | "checkmark" | "list.number" | "arrow.turn.left.up";
11 /** What a plain tap does while a turn runs, or null when the turn is idle. */
12 readonly action: ActiveTurnComposerAction | null;
13 /** What the long-press menu and the Command chord do instead. */
14 readonly alternate: ActiveTurnComposerAction | null;
15 /** The follow-up menu is meaningless outside a running turn or during an edit. */
16 readonly offersFollowUpChoice: boolean;
17}
18
19const ACTION_LABEL: Record<ActiveTurnComposerAction, string> = {
20 queue: "Queue",
21 steer: "Steer",
22 restart: "Restart",
23};
24
25/**
26 * What the composer's primary button says and does. Steering is only offered
27 * when the provider can actually steer the live turn, so the button never
28 * promises something the server would have to silently downgrade.
29 */
30export function resolveComposerSendPresentation(input: {
31 readonly editingQueuedMessage: boolean;
32 readonly running: boolean;
33 readonly canSteer: boolean;
34 readonly followUpBehavior: FollowUpBehavior;
35 /** Outbox reasons the send waits rather than leaving immediately. */
36 readonly deliveryDeferred: boolean;
37}): ComposerSendPresentation {
38 if (input.editingQueuedMessage) {
39 return {
40 label: "Update queued message",
41 icon: "checkmark",
42 action: null,
43 alternate: null,
44 offersFollowUpChoice: false,
45 };
46 }
47 if (!input.running) {
48 return {
49 label: input.deliveryDeferred ? "Queue" : "Send",
50 icon: "arrow.up",
51 action: null,
52 alternate: null,
53 offersFollowUpChoice: false,
54 };
55 }
56 // Without steering support the choice collapses: every follow-up queues, so
57 // offering a menu with one usable entry would be noise.
58 const action: ActiveTurnComposerAction = input.canSteer ? input.followUpBehavior : "queue";
59 const alternate = alternateComposerDispatchAction(action);
60 return {
61 label: ACTION_LABEL[action],
62 icon: action === "steer" ? "arrow.turn.left.up" : "list.number",
63 action,
64 alternate: input.canSteer ? alternate : null,
65 offersFollowUpChoice: input.canSteer,
66 };
67}