Skip to content

fix: render Attachment file name in Thread Message preview when body is empty#7323

Merged
diegolmello merged 2 commits into
developfrom
thread-attachment-preview
May 14, 2026
Merged

fix: render Attachment file name in Thread Message preview when body is empty#7323
diegolmello merged 2 commits into
developfrom
thread-attachment-preview

Conversation

@diegolmello

@diegolmello diegolmello commented May 14, 2026

Copy link
Copy Markdown
Member

Proposed changes

When a Thread Message with no text body (msg empty) but one or more Attachments is rendered inline in the parent Room view (the abbreviated thread-reply preview row beneath the parent Message), the row showed only a small avatar with no body content — file name, caption, and image preview were all missing. This affected every Attachment type (.pptx, .png, audio, video, generic files).

This PR fixes the inline preview branch only. The dedicated Thread view continues to render the full Attachment.

Changes:

  • New pure helper getPreviewMessageFromAttachment(attachment, translateLanguage) in app/containers/message/utils.ts returns the best single-line preview string for one Attachment: translated caption > description > title > undefined.
  • Content.tsx's isPreview branch now falls back to the helper's result via MarkdownPreview when msg is empty and attachments has at least one entry. The encrypted branch fires first, so "Encrypted message" continues to render with no file-name leak.
  • IMessageContent widened with optional attachments?: IAttachment[] and autoTranslateLanguage?: string. Message.tsx already forwards {...props} to Content, so no rewiring is needed at the call site.
  • getMessageFromAttachment is left untouched — Full Attachment and Quote renderers depend on its existing "description / translation only" semantics.
  • For multi-Attachment Thread replies, only the first Attachment is used for the preview text.

Issue(s)

https://rocketchat.atlassian.net/browse/SUP-1041

How to test or reproduce

  1. In a Channel, send a parent message that opens a Thread.
  2. From inside the Thread, upload a file with no caption (e.g. presentation.pptx, example.png, an audio clip) and check "also send to channel" so the Thread reply renders inline in the parent Room.
  3. Back in the parent Room view, the inline Thread reply row now shows the file name (or the caption you typed, if any). Previously it was an empty avatar row.
  4. Open the dedicated Thread view — unchanged; the full Attachment card / image preview / audio player still renders as before.
  5. Repeat with a caption + auto-translate enabled on the Room and verify the translated caption appears inline.
  6. Repeat in an end-to-end encrypted Room — the inline row continues to show "Encrypted message" with no file-name leak.

Storybook coverage was extended (MessageWithThread, MessageWithThreadLargeFont) with .png, .pptx, and multi-Attachment cases alongside the existing audio case.

Screenshots

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • Improvement (non-breaking change which improves a current function)
  • New feature (non-breaking change which adds functionality)
  • Documentation update (if none of the other choices apply)

Checklist

  • I have read the CONTRIBUTING doc
  • I have signed the CLA
  • Lint and unit tests pass locally with my changes
  • I have added tests that prove my fix is effective or that my feature works (if applicable)
  • I have added necessary documentation (if applicable)
  • Any dependent changes have been merged and published in downstream modules

Further comments

  • Jest coverage: 6 unit tests for the helper (precedence and fallback rules) and 8 unit tests for Content.tsx's preview rendering (image-title, file-title, description wins, translation wins, body wins over Attachment, encrypted no leak, non-preview no fallback, multi-Attachment first-only).
  • The removeQuote filter in Attachments.tsx (which can drop bare file Attachments outside threads) is an adjacent gap that is intentionally out of scope for this fix.

Summary by CodeRabbit

  • New Features

    • Thread message previews now derive text from attachments (description > title), use only the first attachment, respect auto-translation, and hide filenames for encrypted messages.
    • Previews re-render when attachments are added or when auto-translation language changes.
  • Tests

    • Expanded tests for attachment preview precedence, translation selection, encrypted UI, and re-render behavior.
  • Documentation

    • Storybook examples added for thread messages with image and file attachments.

Review Change Stack

@diegolmello diegolmello temporarily deployed to approve_e2e_testing May 14, 2026 16:59 — with GitHub Actions Inactive
@coderabbitai

coderabbitai Bot commented May 14, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 53311fa8-b66a-4a8e-bce9-562ba09e62a9

📥 Commits

Reviewing files that changed from the base of the PR and between 405baf1 and e95b489.

⛔ Files ignored due to path filters (1)
  • app/containers/message/__snapshots__/Message.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (6)
  • app/containers/message/Content.test.tsx
  • app/containers/message/Content.tsx
  • app/containers/message/Message.stories.tsx
  • app/containers/message/interfaces.ts
  • app/containers/message/utils.test.ts
  • app/containers/message/utils.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • app/containers/message/interfaces.ts
  • app/containers/message/Content.tsx
  • app/containers/message/utils.test.ts
  • app/containers/message/utils.ts
  • app/containers/message/Message.stories.tsx
  • app/containers/message/Content.test.tsx
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: format

Walkthrough

Derive thread-reply preview text from the first attachment when the message body is empty (translation-aware); add attachments and autoTranslateLanguage to message content, new helper and tests, integrate into Content (memo comparator updated), and add Storybook examples.

Changes

Attachment Preview for Thread Messages

Layer / File(s) Summary
Attachment preview contract and utility
app/containers/message/interfaces.ts, app/containers/message/utils.ts, app/containers/message/utils.test.ts
IMessageContent gains optional attachments and autoTranslateLanguage. Added getPreviewMessageFromAttachment(attachment, translateLanguage?) which returns: translation match → descriptiontitleundefined. Unit tests cover translation selection and field precedence.
Content component preview rendering & memo
app/containers/message/Content.tsx, app/containers/message/Content.test.tsx
Content imports getPreviewMessageFromAttachment and, in the thread-reply preview branch (props.tmid && !props.isThreadRoom), uses props.msg or the first attachment's preview when msg is empty; MarkdownPreview renders only when a preview string exists. React.memo comparator now considers tmid, isThreadRoom, autoTranslateLanguage, and uses dequal for attachments. Tests exercise selection precedence, translation, encrypted-preview behavior (no filename leak), no-fallback when tmid missing, first-attachment-only behavior, and re-rendering when attachments or autoTranslateLanguage change.
Storybook thread-reply examples
app/containers/message/Message.stories.tsx
Adds thread-reply examples to MessageWithThread and MessageWithThreadLargeFont showing an image attachment, a file attachment with title_link, and multiple file attachments with title_links.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

type: bug

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly addresses the main change: rendering attachment file names in thread message previews when the message body is empty. It accurately reflects the primary bug fix across the modified files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • SUP-1041: Request failed with status code 401

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/containers/message/Content.tsx`:
- Around line 56-61: The memo comparator for this component (which decides
whether to re-render the preview) currently ignores props.attachments and
props.autoTranslateLanguage while the preview value uses props.msg or
getPreviewMessageFromAttachment(props.attachments[0],
props.autoTranslateLanguage); update the equality check used by React.memo (or
the custom comparator) to also compare attachments (at least length and first
relevant attachment fields used by getPreviewMessageFromAttachment) and
autoTranslateLanguage so changes to attachments/title/translation force a
re-render of MarkdownPreview; apply the same change to the other memoized
preview block that mirrors this logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4341c8c5-38d4-4c2a-8a33-2d58d62b3f9c

📥 Commits

Reviewing files that changed from the base of the PR and between fca8b8e and d087821.

⛔ Files ignored due to path filters (1)
  • app/containers/message/__snapshots__/Message.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (6)
  • app/containers/message/Content.test.tsx
  • app/containers/message/Content.tsx
  • app/containers/message/Message.stories.tsx
  • app/containers/message/interfaces.ts
  • app/containers/message/utils.test.ts
  • app/containers/message/utils.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: ESLint and Test / run-eslint-and-test
  • GitHub Check: format
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use descriptive names for functions, variables, and classes that clearly convey their purpose
Write comments that explain the 'why' behind code decisions, not the 'what'
Keep functions small and focused on a single responsibility
Use const by default, let when reassignment is needed, and avoid var
Prefer async/await over .then() chains for handling asynchronous operations
Use explicit error handling with try/catch blocks for async operations
Avoid deeply nested code; refactor complex logic into helper functions

Files:

  • app/containers/message/utils.test.ts
  • app/containers/message/Content.tsx
  • app/containers/message/Content.test.tsx
  • app/containers/message/interfaces.ts
  • app/containers/message/Message.stories.tsx
  • app/containers/message/utils.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use TypeScript for type safety; add explicit type annotations to function parameters and return types
Prefer interfaces over type aliases for defining object shapes in TypeScript
Use enums for sets of related constants rather than magic strings or numbers

Files:

  • app/containers/message/utils.test.ts
  • app/containers/message/Content.tsx
  • app/containers/message/Content.test.tsx
  • app/containers/message/interfaces.ts
  • app/containers/message/Message.stories.tsx
  • app/containers/message/utils.ts
**/*.{ts,tsx,js,jsx,json}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Prettier formatting with tabs, single quotes, 130 character width, no trailing commas, avoid arrow parens, and bracket same line

Files:

  • app/containers/message/utils.test.ts
  • app/containers/message/Content.tsx
  • app/containers/message/Content.test.tsx
  • app/containers/message/interfaces.ts
  • app/containers/message/Message.stories.tsx
  • app/containers/message/utils.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use ESLint with @rocket.chat/eslint-config base including React, React Native, TypeScript, and Jest plugins

Files:

  • app/containers/message/utils.test.ts
  • app/containers/message/Content.tsx
  • app/containers/message/Content.test.tsx
  • app/containers/message/interfaces.ts
  • app/containers/message/Message.stories.tsx
  • app/containers/message/utils.ts
app/containers/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Place reusable UI components in 'app/containers/' directory

Files:

  • app/containers/message/utils.test.ts
  • app/containers/message/Content.tsx
  • app/containers/message/Content.test.tsx
  • app/containers/message/interfaces.ts
  • app/containers/message/Message.stories.tsx
  • app/containers/message/utils.ts
🧠 Learnings (2)
📚 Learning: 2026-04-30T17:07:51.020Z
Learnt from: diegolmello
Repo: RocketChat/Rocket.Chat.ReactNative PR: 7274
File: app/lib/services/voip/MediaCallEvents.ts:0-0
Timestamp: 2026-04-30T17:07:51.020Z
Learning: In this Rocket.Chat React Native codebase, the ESLint rule `no-void: error` is enforced. When you see a promise returned from an async call that is not awaited (a “floating promise”), do not silence it with the `void somePromise()` pattern. Instead, handle the promise explicitly by attaching `.catch(...)` (or otherwise awaiting/handling the error) so unhandled-rejection risks are addressed in a way that satisfies the existing ESLint configuration.

Applied to files:

  • app/containers/message/utils.test.ts
  • app/containers/message/Content.tsx
  • app/containers/message/Content.test.tsx
  • app/containers/message/interfaces.ts
  • app/containers/message/Message.stories.tsx
  • app/containers/message/utils.ts
📚 Learning: 2026-03-15T13:55:42.038Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6911
File: app/containers/markdown/Markdown.stories.tsx:104-104
Timestamp: 2026-03-15T13:55:42.038Z
Learning: In Rocket.Chat React Native, the markdown parser requires a space between the underscore wrapping italic text and a mention sigil (_ mention _ instead of _mention_). Ensure stories and tests that include italic-wrapped mentions follow this form to guarantee proper parsing. Specifically, for files like app/containers/markdown/Markdown.stories.tsx, and any test/content strings that exercise italic-mentions, use the pattern _ mention _ (with spaces) to prevent the mention from being treated as plain text. Validate any test strings or story content accordingly.

Applied to files:

  • app/containers/message/Message.stories.tsx
🔇 Additional comments (5)
app/containers/message/interfaces.ts (1)

74-75: LGTM!

app/containers/message/utils.ts (1)

215-223: LGTM!

app/containers/message/utils.test.ts (1)

1-34: LGTM!

app/containers/message/Content.test.tsx (1)

1-133: LGTM!

app/containers/message/Message.stories.tsx (1)

1555-1585: LGTM!

Also applies to: 1609-1639

Comment thread app/containers/message/Content.tsx
@diegolmello diegolmello had a problem deploying to official_android_build May 14, 2026 17:05 — with GitHub Actions Error
@diegolmello diegolmello temporarily deployed to experimental_ios_build May 14, 2026 17:05 — with GitHub Actions Inactive
@diegolmello diegolmello temporarily deployed to experimental_android_build May 14, 2026 17:05 — with GitHub Actions Inactive
@diegolmello diegolmello had a problem deploying to upload_experimental_android May 14, 2026 17:46 — with GitHub Actions Error
@github-actions

Copy link
Copy Markdown

Android Build Available

Rocket.Chat Experimental 4.73.0.108873

Internal App Sharing: https://play.google.com/apps/test/RQVpXLytHNc/ahAO29uNR3CvUte4zrFxtY9NE_lf2cXQZIyfdHz-VJtEafPomakv27_pbIWG-tO1jkdyp3l2R8_S3CI9NBEcWRh01r

@github-actions

Copy link
Copy Markdown

iOS Build Available

Rocket.Chat Experimental 4.73.0.108874

@diegolmello diegolmello temporarily deployed to approve_e2e_testing May 14, 2026 18:55 — with GitHub Actions Inactive
@diegolmello diegolmello temporarily deployed to experimental_android_build May 14, 2026 19:00 — with GitHub Actions Inactive
@diegolmello diegolmello temporarily deployed to experimental_ios_build May 14, 2026 19:00 — with GitHub Actions Inactive
@diegolmello diegolmello had a problem deploying to official_android_build May 14, 2026 19:00 — with GitHub Actions Error
@diegolmello diegolmello had a problem deploying to upload_experimental_android May 14, 2026 19:44 — with GitHub Actions Error
@github-actions

Copy link
Copy Markdown

Android Build Available

Rocket.Chat Experimental 4.73.0.108875

Internal App Sharing: https://play.google.com/apps/test/RQVpXLytHNc/ahAO29uNROJlGZfldeUe-M_ZdYWIZIb1N5qiQmVcB63qs6KZfllc8GgSBUvFWaxdDeLSVXycvBEyKRRCZ9PJudECrK

@OtavioStasiak OtavioStasiak left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

…is empty

Inline Thread Message previews with Attachments but no text body rendered as
empty avatar rows in the parent Room view. Add a `getPreviewMessageFromAttachment`
helper (translation > description > title) and use it in `Content.tsx`'s preview
branch so the file name (or caption) of the first Attachment appears via
MarkdownPreview. The encrypted branch still fires first, so no file-name leak.

`getMessageFromAttachment` is unchanged to preserve full Attachment / Quote
caption behavior.
The new preview fallback reads attachments[0] and autoTranslateLanguage
but the React.memo comparator ignored them, so a late-arriving
attachment or language switch could keep rendering a stale preview when
msg is empty.
@diegolmello diegolmello force-pushed the thread-attachment-preview branch from 405baf1 to e95b489 Compare May 14, 2026 20:02
@github-actions

Copy link
Copy Markdown

iOS Build Available

Rocket.Chat Experimental 4.73.0.108876

@diegolmello diegolmello had a problem deploying to approve_e2e_testing May 14, 2026 20:10 — with GitHub Actions Failure
@diegolmello diegolmello had a problem deploying to experimental_android_build May 14, 2026 20:14 — with GitHub Actions Failure
@diegolmello diegolmello had a problem deploying to official_android_build May 14, 2026 20:14 — with GitHub Actions Failure
@diegolmello diegolmello had a problem deploying to experimental_ios_build May 14, 2026 20:14 — with GitHub Actions Failure
@diegolmello diegolmello had a problem deploying to official_ios_build May 14, 2026 20:14 — with GitHub Actions Failure
@diegolmello diegolmello merged commit d460448 into develop May 14, 2026
6 of 11 checks passed
@diegolmello diegolmello deleted the thread-attachment-preview branch May 14, 2026 20:15
OtavioStasiak added a commit that referenced this pull request May 20, 2026
* feat: alt text

* action: organized translations

* fix: move alt text accessibility to button level and fix gallery height

* chore: format code and fix lint issues

* ix: stable gallery keys and forward alt text accessibility to gallery items

* fix: i18n fallbacks, alt text a11y label, stale altText on remove, and 8.4.0 boundary test

* action: organized translations

* fix: i18n

* action: organized translations

* fix: preserve caption on send, correct version gate for share extension, and translated a11y labels in gallery

* fix: alt text label

* feat: queue composer attachments inline

* feat: open attachment alt text in action sheet

* fix: remove image gallery

* action: organized translations

* feat: edit message alt text

* chore: format code and fix lint issues

* feat: backward compatibilities

* fix: edit

* remove image gallery

* fix: conflicts

* code improvements

* fix: composer

* code improvements

* fix: tests

* fix(a11y): improve image attachment accessibility labels and order

* action: organized translations

* fix: unit tests

* fix: focus

* fix: a11y labels

* chore: format code and fix lint issues

* test

* fix: merge conflicts

* feat: improve a11y experience

* fix: unlabelled on android

* fix: action sheet input keyboard

* fix: announce GIFs as interactive in ImageViewer for screen readers

* fix: ignore whitespace-only alt text in message a11y label

* refactor: extract sendAttachments helper from composer and share view

* action: organized translations

* feat: code improvements

* code improvements

* refactor: extract useImageDescriptionLabel hook and inline useAltTextSupported in Image

* fix: i18n

* action: organized translations

* refactor: stabilize FlatList renderers and harden altText checks

* fix(a11y): omit empty segments in message accessibilityLabel

* refactor: extract normalizeAttachment and preserve handler order
  in useChooseMedia

* refactor: extract normalizeAttachment and preserve handler order
  in useChooseMedia

* rollback prettier changes

* feat: attachment action sheet stories and test

* feat: attachment actionsheet stories and tests

* feat: announce images without description to screen readers

* action: organized translations

* fix: render Attachment file name in Thread Message preview when body is empty (#7323)

* fix: preference value changes causing reset to other option (#7313)

* fix: do not encrypt messages when workspace E2E is disabled (#7324)

* fix: snapshot test

* fix: snapshot test

* feat: unify thumbs

* efactor: rename handlePickedAttachments to handleSelectedAttachments

* fix: pass altText and isAnimated to ImageViewer in ShareView Preview

* refactor: make AltTextLabel altText optional with early return

* fix: stop leaking attachment.altText into caption rendering

* fix: if no alt text its rendering an empty absolute view

* test: cover useMessageAccessibilityLabel and keep suffix on
  translated

* refactor: prefer title_link/message_link over index for Reply attachment key

* refactor: unify ShareView Thumbs and ComposerAttachments under
  shared AttachmentThumbs

* feat: unify Thumbs

* remove memo of useImageDescriptionLabel

* fix: test and lint

* fix: test

* fix: i18n missing translation

* fix: fallback accessibility label when alt text is empty

* fix: remove unused code

* rollback prettier changes

* refactor: colocate useImageDescriptionLabel with message hooks

* refactor: simplify message accessibility label composition

* feat: add missing keys

* feat: use thumb as children instead of add it again

* refactor: tighten types and stable keys in message/share views

* fix: useTheme on AltTextInput

* action: organized translations

* chore: code organization

* chore: type improvement

* chore: code organization

* fix: image improvement

* code improvements

* fix: avoid undefined a11y label

* fix: altText trim and improvements

* fix: tests

* fix: use memo composer attachments

* chore: remove editAltText (not available)

* feat: standardize shareView composer

---------

Co-authored-by: OtavioStasiak <OtavioStasiak@users.noreply.github.com>
Co-authored-by: Diego Mello <diegolmello@gmail.com>
Co-authored-by: Rohit Bansal <40559587+Rohit3523@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants