From a899addf8a311790b40d98c84af79b29caa7a58b Mon Sep 17 00:00:00 2001 From: Oliver Lazoroski Date: Fri, 25 Sep 2026 15:02:45 +0200 Subject: [PATCH 1/2] fix(core): show an empty message preview when there is nothing to describe A message or draft with no text and no attachments fell through to the attachment wording in useMessagePreviewText: an absent `attachments` rendered "0 Files", and an empty array matched `onlyAudio` (0 === 0) and rendered "0 Audios". Return an empty preview instead, matching useMessagePreviewIcon, which already returns null in that case. Refs RN-432 --- .../__tests__/useMessagePreviewText.test.tsx | 57 +++++++++++++++++++ .../messagePreview/useMessagePreviewText.tsx | 10 +++- 2 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 package/src/hooks/messagePreview/__tests__/useMessagePreviewText.test.tsx diff --git a/package/src/hooks/messagePreview/__tests__/useMessagePreviewText.test.tsx b/package/src/hooks/messagePreview/__tests__/useMessagePreviewText.test.tsx new file mode 100644 index 0000000000..b2a961feb1 --- /dev/null +++ b/package/src/hooks/messagePreview/__tests__/useMessagePreviewText.test.tsx @@ -0,0 +1,57 @@ +import React, { PropsWithChildren } from 'react'; + +import { renderHook } from '@testing-library/react-native'; + +import type { Attachment } from 'stream-chat'; + +import { Chat } from '../../../components/Chat/Chat'; +import { initiateClientWithChannels } from '../../../mock-builders/api/initiateClientWithChannels'; +import { + generateAudioAttachment, + generateFileAttachment, + generateImageAttachment, +} from '../../../mock-builders/attachments'; +import { generateMessage } from '../../../mock-builders/generator/message'; +import { useMessagePreviewText } from '../useMessagePreviewText'; + +describe('useMessagePreviewText', () => { + const render = async (message: ReturnType) => { + const { client } = await initiateClientWithChannels(); + const wrapper = ({ children }: PropsWithChildren) => {children}; + const { result } = renderHook(() => useMessagePreviewText({ message }), { wrapper }); + return result; + }; + + const withAttachments = (attachments?: Attachment[]) => + generateMessage({ attachments, text: '' }); + + // Drafts can have no text and nothing attached; they used to render "0 Files" / "0 Audios". + it.each([ + ['absent', undefined], + ['empty', []], + ])('returns an empty preview when attachments are %s', async (_, attachments) => { + const result = await render(withAttachments(attachments)); + expect(result.current).toBe(''); + }); + + it('counts mixed attachments as files', async () => { + const result = await render( + withAttachments([generateImageAttachment(), generateFileAttachment()]), + ); + expect(result.current).toBe('2 Files'); + }); + + it('counts audio-only attachments as audios', async () => { + const result = await render( + withAttachments([generateAudioAttachment(), generateAudioAttachment()]), + ); + expect(result.current).toBe('2 Audios'); + }); + + it('prefers the message text over its attachments', async () => { + const result = await render( + generateMessage({ attachments: [generateFileAttachment()], text: 'hello' }), + ); + expect(result.current).toBe('hello'); + }); +}); diff --git a/package/src/hooks/messagePreview/useMessagePreviewText.tsx b/package/src/hooks/messagePreview/useMessagePreviewText.tsx index a3c9b88cfb..9919078444 100644 --- a/package/src/hooks/messagePreview/useMessagePreviewText.tsx +++ b/package/src/hooks/messagePreview/useMessagePreviewText.tsx @@ -67,6 +67,12 @@ export const useMessagePreviewText = ({ return message.text; } + // No text and nothing attached, as with some drafts. Without this, an absent `attachments` fell + // through to "0 Files" and an empty one matched `onlyAudio` ("0 Audios"). + if (!attachmentsLength) { + return ''; + } + if (onlyImages) { if (images?.length === 1) { return t('messagePreview.photo.label', 'Photo'); @@ -124,9 +130,7 @@ export const useMessagePreviewText = ({ } return t('messagePreview.files.label', { - // `attachments` may be absent on this fall-through path. It used to render a literal - // `{{count}}` placeholder there; 0 at least pluralises. - count: attachmentsLength ?? 0, + count: attachmentsLength, defaultValue_one: '{{count}} File', defaultValue_other: '{{count}} Files', }); From a5d25ca0452a9ecb739498eb4c77ce705f7e3e2a Mon Sep 17 00:00:00 2001 From: Oliver Lazoroski Date: Fri, 25 Sep 2026 15:21:28 +0200 Subject: [PATCH 2/2] refactor(core): drop the empty-preview comment in useMessagePreviewText --- package/src/hooks/messagePreview/useMessagePreviewText.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/package/src/hooks/messagePreview/useMessagePreviewText.tsx b/package/src/hooks/messagePreview/useMessagePreviewText.tsx index 9919078444..3e6f139969 100644 --- a/package/src/hooks/messagePreview/useMessagePreviewText.tsx +++ b/package/src/hooks/messagePreview/useMessagePreviewText.tsx @@ -67,8 +67,6 @@ export const useMessagePreviewText = ({ return message.text; } - // No text and nothing attached, as with some drafts. Without this, an absent `attachments` fell - // through to "0 Files" and an empty one matched `onlyAudio` ("0 Audios"). if (!attachmentsLength) { return ''; }