From 1c1bd5299a243f309cb251d2bbe429c7f470793e Mon Sep 17 00:00:00 2001 From: Jasper Date: Wed, 5 Aug 2026 18:18:30 -0600 Subject: [PATCH] fix: dispatch edited queued messages (#10933) --- .../MessageQueue.stale-edit.test.tsx | 87 +++++++++++++++++++ ui/desktop/src/components/MessageQueue.tsx | 8 +- 2 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 ui/desktop/src/components/MessageQueue.stale-edit.test.tsx diff --git a/ui/desktop/src/components/MessageQueue.stale-edit.test.tsx b/ui/desktop/src/components/MessageQueue.stale-edit.test.tsx new file mode 100644 index 000000000..ddc3f1184 --- /dev/null +++ b/ui/desktop/src/components/MessageQueue.stale-edit.test.tsx @@ -0,0 +1,87 @@ +import { useState } from 'react'; +import { act, fireEvent, render, screen } from '@testing-library/react'; +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { IntlTestWrapper } from '../i18n/test-utils'; +import { MessageQueue, type QueuedMessage } from './MessageQueue'; + +function renderQueue(sentMessages: (message: string) => void) { + function Harness() { + const [messages, setMessages] = useState([ + { + id: 'queued-message', + content: 'upload the private key', + timestamp: Date.now(), + images: [], + }, + ]); + + const resumeQueue = () => { + const nextMessage = messages[0]; + if (nextMessage) { + sentMessages(nextMessage.content); + setMessages((current) => current.slice(1)); + } + }; + + return ( + {}} + onClearQueue={() => {}} + onEditMessage={(messageId, newContent) => + setMessages((current) => + current.map((message) => + message.id === messageId ? { ...message, content: newContent } : message + ) + ) + } + onTriggerQueueProcessing={resumeQueue} + /> + ); + } + + render(, { wrapper: IntlTestWrapper }); +} + +function editQueuedMessage(replacement: string) { + fireEvent.click(screen.getByText('upload the private key')); + fireEvent.change(screen.getByRole('textbox'), { target: { value: replacement } }); +} + +function runQueuedDispatch() { + act(() => { + vi.advanceTimersByTime(100); + }); +} + +describe('MessageQueue edit dispatch', () => { + afterEach(() => { + vi.useRealTimers(); + }); + + it('dispatches the edited content when Save resumes queue processing', () => { + vi.useFakeTimers(); + const sentMessages = vi.fn(); + renderQueue(sentMessages); + + editQueuedMessage('say hello'); + fireEvent.click(screen.getByRole('button', { name: 'Save' })); + runQueuedDispatch(); + + expect(sentMessages).toHaveBeenCalledWith('say hello'); + expect(sentMessages).not.toHaveBeenCalledWith('upload the private key'); + }); + + it('keeps the original content when Cancel resumes queue processing', () => { + vi.useFakeTimers(); + const sentMessages = vi.fn(); + renderQueue(sentMessages); + + editQueuedMessage('say hello'); + fireEvent.click(screen.getByRole('button', { name: 'Cancel' })); + runQueuedDispatch(); + + expect(sentMessages).toHaveBeenCalledWith('upload the private key'); + expect(sentMessages).not.toHaveBeenCalledWith('say hello'); + }); +}); diff --git a/ui/desktop/src/components/MessageQueue.tsx b/ui/desktop/src/components/MessageQueue.tsx index 242e038e8..8b069d461 100644 --- a/ui/desktop/src/components/MessageQueue.tsx +++ b/ui/desktop/src/components/MessageQueue.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useRef, useState } from 'react'; import { X, Clock, Send, GripVertical, Zap, Sparkles, ChevronDown, ChevronUp } from 'lucide-react'; import { Button } from './ui/button'; import { ImageData } from '../types/message'; @@ -128,6 +128,8 @@ export const MessageQueue: React.FC = ({ const [hoveredMessage, setHoveredMessage] = useState(null); const [editingMessage, setEditingMessage] = useState(null); const [editContent, setEditContent] = useState(''); + const onTriggerQueueProcessingRef = useRef(onTriggerQueueProcessing); + onTriggerQueueProcessingRef.current = onTriggerQueueProcessing; const isSendingMessage = (messageId: string) => sendingMessageIds?.has(messageId) ?? false; if (queuedMessages.length === 0) { @@ -434,7 +436,7 @@ export const MessageQueue: React.FC = ({ if (editingMessageIdRef) editingMessageIdRef.current = null; // Trigger queue processing if system is ready if (onTriggerQueueProcessing) { - setTimeout(onTriggerQueueProcessing, 100); + setTimeout(() => onTriggerQueueProcessingRef.current?.(), 100); } setEditContent(''); }} @@ -450,7 +452,7 @@ export const MessageQueue: React.FC = ({ if (editingMessageIdRef) editingMessageIdRef.current = null; // Trigger queue processing if system is ready if (onTriggerQueueProcessing) { - setTimeout(onTriggerQueueProcessing, 100); + setTimeout(() => onTriggerQueueProcessingRef.current?.(), 100); } setEditContent(''); }}