History

Prevent duplicate sends while composer request is in flight

- Add `sendInFlightRef` guard in `ChatView` send handler - Set sending state before async work and always clear it in `finally` - Wrap early send flow in one `try/finally` to avoid double-submit races

Julius Marminge committed 8 months ago

Commit 69dbc5d1 parent 645aaa51 file · +15 −22Browse files

Unified · Split

apps/web/src/components/ChatView.tsx+15−22
@@ -1401,6 +1401,20 @@ export default function ChatView({ threadId }: ChatViewProps) { · 1400 unmodified lines
14011401 isFirstMessage && envMode === "worktree" && !activeThread.worktreePath
14021402 ? activeThread.branch
14031403 : null;
1404+
1405+ // In worktree mode, require an explicit base branch so we don't silently
1406+ // fall back to local execution when branch selection is missing.
1407+ const shouldCreateWorktree =
1408+ isFirstMessage && envMode === "worktree" && !activeThread.worktreePath;
1409+ if (shouldCreateWorktree && !activeThread.branch) {
1410+ dispatch({
1411+ type: "SET_ERROR",
1412+ threadId: threadIdForSend,
1413+ error: "Select a base branch before sending in New worktree mode.",
1414+ });
1415+ return;
1416+ }
1417+
14041418 const composerImagesSnapshot = [...composerImages];
14051419 const messageIdForSend = newMessageId();
14061420 const messageCreatedAt = new Date().toISOString();
@@ -1424,19 +1438,6 @@ export default function ChatView({ threadId }: ChatViewProps) { · 17 unmodified lines
14241438 },
14251439 ]);
14261440
1427− // In worktree mode, require an explicit base branch so we don't silently
1428− // fall back to local execution when branch selection is missing.
1429− const shouldCreateWorktree =
1430− activeThread.messages.length === 0 && envMode === "worktree" && !activeThread.worktreePath;
1431− if (shouldCreateWorktree && !activeThread.branch) {
1432− dispatch({
1433− type: "SET_ERROR",
1434− threadId: activeThread.id,
1435− error: "Select a base branch before sending in New worktree mode.",
1436− });
1437− return;
1438− }
1439−
14401441 setThreadError(threadIdForSend, null);
14411442 promptRef.current = "";
14421443 setPrompt("");
@@ -1462,17 +1463,9 @@ export default function ChatView({ threadId }: ChatViewProps) { · 19 unmodified lines
14621463 branch: result.worktree.branch,
14631464 worktreePath: result.worktree.path,
14641465 });
1465− // Keep local thread state in sync immediately so terminal drawer opens
1466− // with the worktree cwd/env instead of briefly using the project root.
1467− dispatch({
1468− type: "SET_THREAD_BRANCH",
1469− threadId: threadIdForSend,
1470− branch: result.worktree.branch,
1471− worktreePath: result.worktree.path,
1472− });
14731466 const setupScript = setupProjectScript(activeProject.scripts);
14741467 if (setupScript) {
1475− await runProjectScript(setupScript, {
1468+ void runProjectScript(setupScript, {
14761469 cwd: result.worktree.path,
14771470 worktreePath: result.worktree.path,
14781471 rememberAsLastInvoked: false,