authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-02-16 00:47:08-08:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-02-16 00:55:31-08:00
log73fa2d66c79eb1351fbae55a81c61e1f7b88c619
tree99d4be255d860e870410e7355264958145297d92
parent51dcb547edf34240a509ffb80f7486d382fd9445

Use stable IDs for approval actions in ChatView

- Derive `activeSessionId` and `activeThreadId` before approval callbacks - Avoid stale thread/session captures when submitting approval decisions - Pass `hasMessages` to `MessagesTimeline` instead of the full thread

1 files changed, 10 insertions(+), 9 deletions(-)

apps/web/src/components/ChatView.tsx+10-9
......@@ -51,7 +51,7 @@ import {
5151} from "../session-logic";
5252import { isScrollContainerNearBottom } from "../chat-scroll";
5353import { useStore } from "../store";
54import type { ChatImageAttachment, Thread } from "../types";
54import type { ChatImageAttachment } from "../types";
5555import BranchToolbar from "./BranchToolbar";
5656import GitActionsControl from "./GitActionsControl";
5757import {
......@@ -168,6 +168,7 @@ export default function ChatView() {
168168
169169 const activeThread = state.threads.find((t) => t.id === state.activeThreadId);
170170 const activeThreadId = activeThread?.id ?? null;
171 const activeSessionId = activeThread?.session?.sessionId;
171172 const activeProject = state.projects.find((p) => p.id === activeThread?.projectId);
172173 const selectedModel = resolveModelSlug(
173174 activeThread?.model ?? activeProject?.model ?? DEFAULT_MODEL,
......@@ -895,28 +896,28 @@ export default function ChatView() {
895896
896897 const onRespondToApproval = useCallback(
897898 async (requestId: string, decision: ProviderApprovalDecision) => {
898 if (!api || !activeThread?.session) return;
899 if (!api || !activeSessionId || !activeThreadId) return;
899900
900901 setRespondingRequestIds((existing) =>
901902 existing.includes(requestId) ? existing : [...existing, requestId],
902903 );
903904 try {
904905 await api.providers.respondToRequest({
905 sessionId: activeThread.session.sessionId,
906 sessionId: activeSessionId,
906907 requestId,
907908 decision,
908909 });
909910 } catch (err) {
910911 dispatch({
911912 type: "SET_ERROR",
912 threadId: activeThread.id,
913 threadId: activeThreadId,
913914 error: err instanceof Error ? err.message : "Failed to submit approval decision.",
914915 });
915916 } finally {
916917 setRespondingRequestIds((existing) => existing.filter((id) => id !== requestId));
917918 }
918919 },
919 [activeThread?.id, activeThread?.session, api, dispatch],
920 [activeSessionId, activeThreadId, api, dispatch],
920921 );
921922
922923 const onModelSelect = useCallback(
......@@ -1015,7 +1016,7 @@ export default function ChatView() {
10151016 onScroll={onMessagesScroll}
10161017 >
10171018 <MessagesTimeline
1018 activeThread={activeThread}
1019 hasMessages={activeThread.messages.length > 0}
10191020 isWorking={isWorking}
10201021 timelineEntries={timelineEntries}
10211022 completionDividerBeforeEntryId={completionDividerBeforeEntryId}
......@@ -1413,7 +1414,7 @@ const PendingApprovalsPanel = memo(function PendingApprovalsPanel({
14131414});
14141415
14151416interface MessagesTimelineProps {
1416 activeThread: Thread;
1417 hasMessages: boolean;
14171418 isWorking: boolean;
14181419 timelineEntries: ReturnType<typeof deriveTimelineEntries>;
14191420 completionDividerBeforeEntryId: string | null;
......@@ -1427,7 +1428,7 @@ interface MessagesTimelineProps {
14271428}
14281429
14291430const MessagesTimeline = memo(function MessagesTimeline({
1430 activeThread,
1431 hasMessages,
14311432 isWorking,
14321433 timelineEntries,
14331434 completionDividerBeforeEntryId,
......@@ -1439,7 +1440,7 @@ const MessagesTimeline = memo(function MessagesTimeline({
14391440 onImageExpand,
14401441 messagesEndRef,
14411442}: MessagesTimelineProps) {
1442 if (activeThread.messages.length === 0 && !isWorking) {
1443 if (!hasMessages && !isWorking) {
14431444 return (
14441445 <div className="flex h-full items-center justify-center">
14451446 <p className="text-sm text-muted-foreground/30">Send a message to start the conversation.</p>