From d29814186729a811df4bcf229f44c5d27ff9ae8d Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Fri, 16 Oct 2026 17:09:44 +0200 Subject: [PATCH] fix(app): dismiss review feedback confirmation with a toast The sent phase kept its confirmation over the diff indefinitely. Use the shared success toast and return the feedback workflow to idle after sending. --- .../e2e/browser/composer-diff-stat.spec.ts | 11 +++++------ .../app/src/review/feedback/index.test.ts | 3 +-- packages/app/src/review/feedback/index.ts | 5 ++--- packages/app/src/review/feedback/surface.tsx | 19 ++++++------------- .../app/src/review/feedback/use-feedback.ts | 17 +++++++++++++++-- 5 files changed, 29 insertions(+), 26 deletions(-) diff --git a/packages/app/e2e/browser/composer-diff-stat.spec.ts b/packages/app/e2e/browser/composer-diff-stat.spec.ts index 00705faccd..b3d1b56c12 100644 --- a/packages/app/e2e/browser/composer-diff-stat.spec.ts +++ b/packages/app/e2e/browser/composer-diff-stat.spec.ts @@ -310,7 +310,7 @@ async function sendFeedback(page: Page, count: number) { } async function expectFeedbackSent(page: Page, recipient: string) { - await expect(page.getByText(`Feedback sent to ${recipient}`, { exact: true })).toBeVisible(); + await expect(page.getByTestId("app-toast")).toHaveText(`Feedback sent to ${recipient}`); await expect(page.getByTestId("working-diff-panel")).toBeVisible(); } @@ -340,6 +340,9 @@ test.describe("send review feedback", () => { await expectFeedbackSent(page, "Composer diff stat"); await expectReviewAbsent(page, "Please simplify this."); await captureFeedback(page, "single"); + await expect( + page.getByText("Feedback sent to Composer diff stat", { exact: true }), + ).toHaveCount(0); }); await test.step("Return to the untouched composer draft", async () => { await showWideLayout(page); @@ -375,11 +378,7 @@ test.describe("send review feedback", () => { ).toBeDisabled(); expect(gate.requestCount()).toBe(1); gate.accept(); - await expect( - compactReviewSurface(page).getByText("Feedback sent to Composer diff stat", { - exact: true, - }), - ).toBeVisible(); + await expectFeedbackSent(page, "Composer diff stat"); }); } finally { await workspace.cleanup(); diff --git a/packages/app/src/review/feedback/index.test.ts b/packages/app/src/review/feedback/index.test.ts index b423855b75..6ef6d74287 100644 --- a/packages/app/src/review/feedback/index.test.ts +++ b/packages/app/src/review/feedback/index.test.ts @@ -172,8 +172,7 @@ describe("review feedback", () => { makeComment("added"), ]); expect(feedback.model.getState().phase).toEqual({ - kind: "sent", - recipient: makeRecipient("one"), + kind: "idle", }); }); it("clears unchanged sent comments while preserving additions", async () => { diff --git a/packages/app/src/review/feedback/index.ts b/packages/app/src/review/feedback/index.ts index 69e0592fe8..3aaba5c97d 100644 --- a/packages/app/src/review/feedback/index.ts +++ b/packages/app/src/review/feedback/index.ts @@ -23,8 +23,7 @@ export type FeedbackPhase = | { kind: "idle" } | { kind: "choosing" } | { kind: "sending"; recipient: FeedbackRecipient; commentCount: number } - | { kind: "failed"; message: string } - | { kind: "sent"; recipient: FeedbackRecipient }; + | { kind: "failed"; message: string }; export interface ReviewFeedbackState { phase: FeedbackPhase; commentCount: number; @@ -115,7 +114,7 @@ function createFeedbackView( try { await ports.send(recipient, attachment); ports.clearSent(sentComments); - workflow.changePhase({ kind: "sent", recipient }); + workflow.changePhase({ kind: "idle" }); } catch (error) { workflow.changePhase({ kind: "failed", diff --git a/packages/app/src/review/feedback/surface.tsx b/packages/app/src/review/feedback/surface.tsx index 7f87f46d88..fac499152e 100644 --- a/packages/app/src/review/feedback/surface.tsx +++ b/packages/app/src/review/feedback/surface.tsx @@ -80,17 +80,6 @@ function OpenReviewFeedbackAction({ let notice: ReactNode = unavailableNotice; if (state.phase.kind === "failed") notice = {state.phase.message}; - else if (state.phase.kind === "sent") - notice = ( - <> - - {unavailableNotice} - - ); return ( <> {state.commentCount > 0 ? ( @@ -229,7 +218,7 @@ function RecipientRow({ ); } -function SentNotice({ +export function FeedbackSentMessage({ recipient, serverId, workspaceId, @@ -241,7 +230,7 @@ function SentNotice({ const { t } = useTranslation(); const render = useCallback( (presentation: WorkspaceTabPresentation) => ( - + {t("review.feedback.sent", { recipient: presentation.label })} ), @@ -267,6 +256,10 @@ const styles = StyleSheet.create((theme) => ({ left: theme.spacing[4], alignItems: "flex-end", }, + sentMessage: { + color: theme.colors.foreground, + fontSize: theme.fontSize.sm, + }, text: { color: theme.colors.foreground, fontSize: theme.fontSize.sm, diff --git a/packages/app/src/review/feedback/use-feedback.ts b/packages/app/src/review/feedback/use-feedback.ts index e2b151953e..0596f3c55f 100644 --- a/packages/app/src/review/feedback/use-feedback.ts +++ b/packages/app/src/review/feedback/use-feedback.ts @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useRef, useSyncExternalStore } from "react"; +import { createElement, useEffect, useMemo, useRef, useSyncExternalStore } from "react"; import { useReviewDraftComments, getReviewDraftComments, @@ -22,6 +22,8 @@ import { type FeedbackAttachment, type FeedbackRecipient, } from "./index"; +import { useToast } from "@/contexts/toast-context"; +import { FeedbackSentMessage } from "./surface"; const feedbackScope = createReviewFeedbackScope(); @@ -32,6 +34,7 @@ export function useReviewFeedback(input: { attachment: FeedbackAttachment | null; }) { const { serverId, reviewDraftKey, workspaceId } = input; + const toast = useToast(); const isCompact = useIsCompactFormFactor(); const workspaceKey = workspaceId ? buildWorkspaceTabPersistenceKey({ serverId, workspaceId }) @@ -49,12 +52,14 @@ export function useReviewFeedback(input: { connected, attachment: input.attachment, sendBehavior: settings.sendBehavior, + toast, }); live.current = { client, connected, attachment: input.attachment, sendBehavior: settings.sendBehavior, + toast, }; const model = useMemo( () => @@ -85,12 +90,20 @@ export function useReviewFeedback(input: { ).turnId ?? undefined) : undefined, }); + live.current.toast.show( + createElement(FeedbackSentMessage, { + recipient, + serverId, + workspaceId: workspaceId ?? "", + }), + { variant: "success" }, + ); }, clearSent: (sentComments) => clearSentReviewDraftComments({ key: reviewDraftKey, comments: sentComments }), failedMessage: () => i18n.t("review.feedback.failed"), }), - [reviewDraftKey, serverId, workspaceKey], + [reviewDraftKey, serverId, workspaceKey, workspaceId], ); useEffect(() => { model.refresh();