Skip to content

Commit f658585

Browse files
[fix][CLNP-7811] Treat whitespace-only input as empty for typing state
The previous empty-check used `getTextContentWithoutZeroWidthSpace(...) === ''` (no trim), while the same handler's `setIsInput` call used `hasTextContentWithoutZeroWidthSpace` (trim-aware). This split meant a user who typed text and then deleted it down to whitespace/newlines saw the send button disappear (functionally empty UI) but the typing indicator stayed visible to peers until the SDK server-side timeout expired. Use `hasTextContentWithoutZeroWidthSpace` for both checks so typing state follows the same trim semantics as the send-button state. The DOM read is also collapsed into a single call. Add 2 tests: - onStopTyping fires when input becomes whitespace-only after typing - onStartTyping does not fire on whitespace-only input Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 770af64 commit f658585

2 files changed

Lines changed: 33 additions & 8 deletions

File tree

src/ui/MessageInput/__tests__/MessageInput.spec.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,33 @@ describe('ui/MessageInput', () => {
318318

319319
expect(onStopTyping).toHaveBeenCalledTimes(1);
320320
});
321+
322+
it('should call onStopTyping when input becomes whitespace-only after typing', () => {
323+
const onStartTyping = jest.fn();
324+
const onStopTyping = jest.fn();
325+
render(<MessageInput onSendMessage={noop} onStartTyping={onStartTyping} onStopTyping={onStopTyping} />);
326+
327+
const input = screen.getByRole('textbox');
328+
input.textContent = 'hello';
329+
fireEvent.input(input);
330+
input.textContent = ' ';
331+
fireEvent.input(input);
332+
333+
expect(onStopTyping).toHaveBeenCalledTimes(1);
334+
});
335+
336+
it('should not call onStartTyping when input contains only whitespace', () => {
337+
const onStartTyping = jest.fn();
338+
const onStopTyping = jest.fn();
339+
render(<MessageInput onSendMessage={noop} onStartTyping={onStartTyping} onStopTyping={onStopTyping} />);
340+
341+
const input = screen.getByRole('textbox');
342+
input.textContent = ' ';
343+
fireEvent.input(input);
344+
345+
expect(onStartTyping).not.toHaveBeenCalled();
346+
expect(onStopTyping).not.toHaveBeenCalled();
347+
});
321348
});
322349
});
323350

src/ui/MessageInput/index.tsx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -526,17 +526,15 @@ const MessageInput = React.forwardRef<HTMLInputElement, MessageInputProps>((prop
526526
useMentionInputDetection();
527527
}}
528528
onInput={() => {
529-
const isEmpty = getTextContentWithoutZeroWidthSpace(internalRef?.current) === '';
530-
if (isEmpty) {
531-
if (wasTypingRef.current) {
532-
onStopTyping();
533-
wasTypingRef.current = false;
534-
}
535-
} else {
529+
const hasContent = hasTextContentWithoutZeroWidthSpace(internalRef?.current);
530+
if (hasContent) {
536531
onStartTyping();
537532
wasTypingRef.current = true;
533+
} else if (wasTypingRef.current) {
534+
onStopTyping();
535+
wasTypingRef.current = false;
538536
}
539-
setIsInput(hasTextContentWithoutZeroWidthSpace(internalRef?.current));
537+
setIsInput(hasContent);
540538
useMentionedLabelDetection();
541539
}}
542540
onPaste={(e) => {

0 commit comments

Comments
 (0)