Skip to content

Composer attachment picker accessibility improvements#6404

Merged
andremion merged 13 commits into
developfrom
feat/attachment-picker-accessibility
Apr 30, 2026
Merged

Composer attachment picker accessibility improvements#6404
andremion merged 13 commits into
developfrom
feat/attachment-picker-accessibility

Conversation

@andremion
Copy link
Copy Markdown
Contributor

@andremion andremion commented Apr 30, 2026

Goal

Address the accessibility audit (Notion DB). All findings concentrate on the attachment picker.

Implementation

Audit Fix Files
A1 (Critical) Media grid items announce label / role / selected state ImagesPicker.kt
A2 Picker tabs use Role.Tab via clearAndSetSemantics (suppresses ToggleableState) AttachmentTypePicker.kt
A3, A4(1) Composer button uses contentDescription = "Attachments" + stateDescription = "collapsed" / "expanded". paneTitle on the picker root announces the new pane on entry MessageComposerLeadingContent.kt, AttachmentPicker.kt
A5(1), A8(1) Accessibility announcements ("Photo / Video / File / Audio recording attached" / "Attachment removed") via View.announceForAccessibility() MessageComposerAttachmentAnnouncer.kt, MessageInput.kt
A5(2), A10 Action hint flips between "select" / "remove" based on isSelected ImagesPicker.kt, FilesPicker.kt
A6 Composer chip × labelled "Remove attachment" via additive contentDescription parameter on ComposerCancelIcon ComposerCancelIcon.kt + 3 chip call sites
A9 CreatePoll sheet announces the screen title before the auto-focused field via paneTitle + heading() CreatePollScreen.kt, PollCreationHeader.kt

Designer-aligned scope

Audit A7 (focus → text input on chip remove) is resolved by the new "Attachment removed" announcement: the literal fix pops the keyboard mid-picker, and TalkBack's natural left-to-right / top-to-bottom progression after removal lands on the mic — the announcement provides the audible confirmation. Designer accepted; A7 is closed in Notion. The focus halves of A4 and A8 share the same root cause (Compose programmatic focus moves don't reliably propagate to TalkBack across our merged-semantics tab nodes) and ship in the same state pending designer alignment.

API surface

  • ComposerCancelIcon gains a defaulted contentDescription: String parameter — additive, non-breaking per project convention. apiDump reflects the addition.
  • One unused public string ID removed (stream_compose_attachments). Theoretical soft binary break for any consumer that referenced it; in practice unused.
  • All other changes are internal.

Strings & translations

New string IDs added in the _message_composer_*, _attachment_picker_*, and _attachment_added_* / _attachment_removed namespaces. Translations added for the seven supported locales (it / in / ja / fr / hi / ko / es).

Testing

All scenarios assume the Compose sample app, a channel with media/files/audio attachments, and TalkBack enabled on a real device with a working TTS engine. Single-tap focuses; double-tap activates.

  1. Composer button. Tap the +. "Collapsed, attachments, button, double tap to activate." Activate it. "Expanded, attachments, button" + "Attachment picker" (paneTitle).
  2. Tab bar. Tap each tab. Active: "Selected, media, tab". Inactive: "Files, tab, double tap to activate". No "checkbox" / "checked" wording.
  3. Media grid. Tap a thumbnail: "Non-selected, photo, button, double tap to select". Activate: "Photo attached" + "Selected". Re-focus the same item: hint flips to "double tap to remove".
  4. File row. Switch to Files tab. Tap a row: "Non-selected, <filename>, <size>, button, double tap to select".
  5. Camera capture. Switch to Camera tab, take a photo: "Photo attached" fires automatically.
  6. Composer chip. Tap chip's ×: "Remove attachment, button". Activate: "Attachment removed".
  7. CreatePoll sheet. Switch to Polls tab, activate: "Create Poll" announced before the IME / field announcements.
  8. Visual regression. Scroll through the composer, picker, CreatePoll sheet. No visible changes vs develop. Paparazzi snapshots unchanged.

API impact: one additive defaulted parameter on ComposerCancelIcon. Everything else internal.

No visible UI changes.

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced accessibility across attachment UI with improved screen reader support and semantic descriptions.
    • Added attachment announcements for audio, file, photo, and video uploads.
    • Improved attachment picker and message composer with better semantic labeling.
  • API Changes

    • ComposerCancelIcon now accepts a customizable content description parameter.
  • Localization

    • Added localized attachment-related strings for Spanish, French, Hindi, Indonesian, Italian, Japanese, and Korean.

@andremion andremion added the pr:improvement Improvement label Apr 30, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 30, 2026

PR checklist ✅

All required conditions are satisfied:

  • Title length is OK (or ignored by label).
  • At least one pr: label exists.
  • Sections ### Goal, ### Implementation, and ### Testing are filled.

🎉 Great job! This PR is ready for review.

@andremion
Copy link
Copy Markdown
Contributor Author

@CodeRabbit review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 30, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 30, 2026

SDK Size Comparison 📏

SDK Before After Difference Status
stream-chat-android-client 5.82 MB 5.82 MB 0.00 MB 🟢
stream-chat-android-ui-components 11.02 MB 11.02 MB 0.00 MB 🟢
stream-chat-android-compose 12.35 MB 12.37 MB 0.02 MB 🟢

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 30, 2026

Walkthrough

The PR enhances accessibility across the Stream Chat Compose UI by adding configurable content descriptions to UI elements, introducing semantic annotations for screen readers, creating an attachment announcer composable, and adding comprehensive localized strings for attachment states and picker controls across multiple languages.

Changes

Cohort / File(s) Summary
ComposerCancelIcon API Update
api/stream-chat-android-compose.api, src/main/java/io/getstream/chat/android/compose/ui/components/ComposerCancelIcon.kt
ComposerCancelIcon function signature updated to include a new contentDescription: String parameter with a default value of stringResource(R.string.stream_compose_cancel), enabling accessibility customization for callers.
Attachment Picker Semantics
src/main/java/io/getstream/chat/android/compose/ui/components/attachments/files/FilesPicker.kt, src/main/java/io/getstream/chat/android/compose/ui/components/attachments/images/ImagesPicker.kt
File and image picker items enhanced with semantic button role, selected state, localized click labels, and cleared semantics on inner components (checkbox/radio) to prevent redundant accessibility info.
Attachment Announcement
src/main/java/io/getstream/chat/android/compose/ui/components/composer/MessageComposerAttachmentAnnouncer.kt, src/main/java/io/getstream/chat/android/compose/ui/components/composer/MessageInput.kt
New internal composable added to announce attachment additions/removals via announceForAccessibility() based on list size changes, with type-specific localized messages (image, video, audio, file).
Attachment Item Components
src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/attachments/MessageComposerAttachmentAudioRecordItem.kt, src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/attachments/MessageComposerAttachmentFileItem.kt, src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/attachments/MessageComposerAttachmentMediaItem.kt
ComposerCancelIcon calls updated to pass localized contentDescription parameter for remove/cancel actions on audio, file, and media attachments.
Screen & UI Semantics
src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentPicker.kt, src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentTypePicker.kt, src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/poll/CreatePollScreen.kt, src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/poll/PollCreationHeader.kt, src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/MessageComposerLeadingContent.kt
Multiple screens and UI components updated with semantic metadata: pane titles for attachment/poll screens, heading role for poll title, expanded/collapsed state descriptions for attachment button, and specific content descriptions for controls.
Localized String Resources
src/main/res/values/strings.xml, src/main/res/values-es/strings.xml, src/main/res/values-fr/strings.xml, src/main/res/values-hi/strings.xml, src/main/res/values-in/strings.xml, src/main/res/values-it/strings.xml, src/main/res/values-ja/strings.xml, src/main/res/values-ko/strings.xml
Added localized strings across 8 languages for attachment lifecycle announcements (added audio/file/photo/video), attachment picker controls, expanded/collapsed states, and remove actions; generic stream_compose_attachments string removed.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Complete missing translations across all modules #6374: Adds the new compose string resources (stream_compose_remove_attachment, stream_compose_message_composer_attachments, etc.) that this PR integrates into the updated ComposerCancelIcon signature and attachment components.

Suggested reviewers

  • VelikovPetar
  • gpunto

Poem

🐰 A cancellation icon finds its voice,
With strings so fine in every choice,
Attachments announce with flair,
Screen readers hear what\'s there,
Accessibility hops with rejoice! 🎉

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description includes Goal, Implementation (with detailed audit-to-fix mapping and file references), Testing (comprehensive TalkBack scenarios), and API surface notes. However, it omits the required template sections: UI Changes (before/after screenshots/videos), Contributor Checklist, Reviewer Checklist, and GIF. Add missing template sections: UI Changes with before/after visual comparisons, complete both Contributor and Reviewer checklists, and include a GIF describing the work. Since no visual changes are present, note that explicitly and explain why screenshots are not needed.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change—accessibility improvements for the composer attachment picker—and aligns with the primary purpose documented in the PR description and objectives.
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.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/attachment-picker-accessibility

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
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/attachments/images/ImagesPicker.kt (1)

185-192: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't clear semantics on the video badge wrapper.

clearAndSetSemantics {} removes the duration text exposed by VideoBadge, so TalkBack loses the only useful metadata for video thumbnails. Keep the badge semantics intact and hide only the decorative icon.

♻️ Suggested fix
         if (isVideo) {
             VideoBadge(
                 modifier = Modifier
                     .align(Alignment.BottomStart)
                     .padding(StreamTokens.spacingXs)
-                    .clearAndSetSemantics {},
                 durationInSeconds = attachmentMetaData.videoLength,
             )
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/attachments/images/ImagesPicker.kt`
around lines 185 - 192, The VideoBadge wrapper in ImagesPicker currently calls
Modifier.clearAndSetSemantics {}, which strips the badge's accessible duration
text; remove that call from the modifier on VideoBadge so the badge's semantics
(e.g., durationInSeconds) remain exposed. Instead, mark only the decorative play
icon inside VideoBadge as non-essential for accessibility (i.e., hide that
icon's semantics) so TalkBack still reads the video duration while the icon is
ignored.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/attachments/files/FilesPicker.kt`:
- Around line 145-153: The semantics modifier is applied before the clickable
modifier which allows clickable to add/override its own semantic click action;
in FilesPicker (FilesPicker.kt) move the .semantics { role = Role.Button;
selected = fileItem.isSelected; onClick(label = onClickLabel) {
onItemSelected(fileItem); true } } so it is applied after the .clickable {
onItemSelected(fileItem) } call to preserve the custom onClick label; make the
same change for DefaultImagesPickerItem in ImagesPicker.kt where the semantics
block is currently before .clickable.

---

Outside diff comments:
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/attachments/images/ImagesPicker.kt`:
- Around line 185-192: The VideoBadge wrapper in ImagesPicker currently calls
Modifier.clearAndSetSemantics {}, which strips the badge's accessible duration
text; remove that call from the modifier on VideoBadge so the badge's semantics
(e.g., durationInSeconds) remain exposed. Instead, mark only the decorative play
icon inside VideoBadge as non-essential for accessibility (i.e., hide that
icon's semantics) so TalkBack still reads the video duration while the icon is
ignored.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 823b8b4d-c141-465c-9707-fa64e62edffa

📥 Commits

Reviewing files that changed from the base of the PR and between 5d39913 and 32e481a.

📒 Files selected for processing (22)
  • stream-chat-android-compose/api/stream-chat-android-compose.api
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/ComposerCancelIcon.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/attachments/files/FilesPicker.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/attachments/images/ImagesPicker.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/composer/MessageComposerAttachmentAnnouncer.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/composer/MessageInput.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentPicker.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentTypePicker.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/poll/CreatePollScreen.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/poll/PollCreationHeader.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/MessageComposerLeadingContent.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/attachments/MessageComposerAttachmentAudioRecordItem.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/attachments/MessageComposerAttachmentFileItem.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/attachments/MessageComposerAttachmentMediaItem.kt
  • stream-chat-android-compose/src/main/res/values-es/strings.xml
  • stream-chat-android-compose/src/main/res/values-fr/strings.xml
  • stream-chat-android-compose/src/main/res/values-hi/strings.xml
  • stream-chat-android-compose/src/main/res/values-in/strings.xml
  • stream-chat-android-compose/src/main/res/values-it/strings.xml
  • stream-chat-android-compose/src/main/res/values-ja/strings.xml
  • stream-chat-android-compose/src/main/res/values-ko/strings.xml
  • stream-chat-android-compose/src/main/res/values/strings.xml

…sibility

The attachments toggle button in the message composer announced as
"Attachments, double tap to activate" in both states, which is misleading
when the picker is already open and tapping closes it. Switch to a
state-aware content description: "Open attachments" when the picker is
hidden, "Close attachments" when it is shown.

Removes the now-unused stream_compose_attachments string from all
locales; the new strings live under the stream_compose_message_composer_*
namespace alongside other composer button labels.
The picker tabs (Media, Camera, Files, Polls, Commands) used a
FilledIconToggleButton, which emits Role.Checkbox + a toggleable state.
TalkBack announced an active tab as "Checked, Media, checkbox, double tap
to toggle" — wrong role and a misleading action hint.

Override the per-tab semantics with semantics(mergeDescendants = true) to
declare Role.Tab + selected, while keeping the underlying focusable /
focused / clickable semantics intact so the tab stays discoverable to
accessibility services. Role.Tab takes precedence over the lingering
ToggleableState in TalkBack's announcement, so the active tab now
announces as "Media, tab, selected".
The image/video grid items in the attachment picker had no
contentDescription, no role, and no selected state in the accessibility
tree. TalkBack focused them silently, leaving users unable to identify
or select photos or videos.

Add merged semantics on each item (contentDescription "Photo" or
"Video", Role.Button, selected = isSelected, plus the existing testTag)
and mark the decorative RadioCheck and VideoBadge overlays as
non-semantic with clearAndSetSemantics so they no longer announce
phantom checkbox state. Translations added for the 7 supported locales.
TalkBack announced "Double tap to activate" for file items in the picker,
which is misleading because tapping selects the file for attachment, not
activates it.

Add merged semantics on each file row with Role.Button, the current
selected state, and an onClick action labelled "select" so TalkBack now
announces "Double tap to select". Mark the decorative RadioCheck and
RadioButton indicators with clearAndSetSemantics so they no longer
contribute phantom toggle/radio state to the merged announcement.
Translations added for the 7 supported locales.
The "x" button on each composer attachment chip announced as "Cancel" —
misleading because the button removes a specific attachment from the
message, not abandons an unrelated action.

Add an optional contentDescription parameter to ComposerCancelIcon
(defaulted to the existing stream_compose_cancel for backwards
compatibility) and pass "Remove attachment" from the three composer
attachment chip call sites: media, file, and audio recording. Other
ComposerCancelIcon usages (edit indicator, quoted message, link
preview) keep the original "Cancel" label since they aren't attachment
chips. Translations added for the 7 supported locales.
Tapping a thumbnail to add an attachment, or capturing a photo via the
camera tab, played only a brief system sound and made no TalkBack
announcement, leaving users without confirmation that the attachment
was added. Removing an attachment was equally silent.

Add an internal MessageComposerLiveRegion composable that mounts inside
the composer's root column (always present) and watches attachment list
size. On size increase it announces the type of the latest attachment
("Photo attached", "Video attached", "Audio recording attached", or
"File attached"); on size decrease it announces "Attachment removed".
The text is hosted in a hidden Text marked with LiveRegionMode.Polite +
invisibleToUser, so it is not navigable but is announced when its value
changes. Translations added for the 7 supported locales.

Camera capture flows through the same composer state, so the same live
region also confirms photo capture without further wiring.
When a picker item is already selected, double-tapping toggles it off
rather than selecting it. The action hint should reflect that — TalkBack
should announce "double tap to remove" for already-selected items, and
"double tap to select" for not-yet-selected items.

Branch the onClick semantic action label on the item's selected state in
both the image grid (DefaultImagesPickerItem) and the file list row
(DefaultFilesPickerItem). Translations added for the 7 supported locales.
When the Create Poll sheet opened, TalkBack jumped straight to
announcing the IME and the auto-focused Question field, never
mentioning the screen the user had just entered.

Mark the "Create Poll" title in PollCreationHeader as a heading and add
a paneTitle semantic property on the screen root so accessibility
services announce the new pane on entry.
Real-device TalkBack testing surfaced three adjustments to the original
audit fixes:

- Composer +/× button: hearing "Close attachments" right after opening
  the picker reads backwards. Switch to the standard expandable-button
  pattern — contentDescription stays as "Attachments" + stateDescription
  "collapsed" / "expanded". Add paneTitle on the picker root so the new
  pane is announced on entry.

- Picker tabs leaked the underlying ToggleableState ("checked") through
  semantics(mergeDescendants = true). Switch to clearAndSetSemantics so
  only Role.Tab + selected reach TalkBack.

- The Compose liveRegion semantic on a hidden Text never fired —
  TalkBack skips zero-size / transparent nodes. Switch to
  View.announceForAccessibility() via LocalView. Rename the composable
  to MessageComposerAttachmentAnnouncer.
.com>
Move the picker item's accessibility click label from a duplicated
`semantics { onClick(label = …) }` block onto `Modifier.clickable`'s
`onClickLabel` parameter, removing the order dependency between the
two modifiers.
@andremion andremion force-pushed the feat/attachment-picker-accessibility branch from 32e481a to e7d3033 Compare April 30, 2026 09:22
@andremion andremion marked this pull request as ready for review April 30, 2026 09:25
@andremion andremion requested a review from a team as a code owner April 30, 2026 09:25
Extract `announceAddedAttachment` to `internal` (annotated `@VisibleForTesting`)
and add a JVM unit test exercising the image / video / audio recording / file /
null branches.
Copy link
Copy Markdown
Contributor

@gpunto gpunto left a comment

Choose a reason for hiding this comment

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

LG! Left a nitpick you can safely ignore

Replaces empty-block clearAndSetSemantics {} on the picker checkmark/radio
icons, the video duration badge, and the file-type label with the more
intent-revealing semantics { hideFromAccessibility() }. Non-empty
clearAndSetSemantics {…} sites that replace semantics with a clean set are
left as-is — hideFromAccessibility() would hide the node entirely.
@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
79.3% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@andremion andremion merged commit 5e38683 into develop Apr 30, 2026
15 of 16 checks passed
@andremion andremion deleted the feat/attachment-picker-accessibility branch April 30, 2026 13:09
@stream-public-bot stream-public-bot added the released Included in a release label May 5, 2026
@stream-public-bot
Copy link
Copy Markdown
Contributor

🚀 Available in v7.1.0

andremion added a commit that referenced this pull request May 12, 2026
* Disambiguate composer attachment remove buttons for TalkBack

The (x) remove button on each queued attachment in the Compose composer
announced as "Remove attachment" for every item, so TalkBack users could
not distinguish between multiple remove buttons. Replace the single
contentDescription with per-type, name-aware labels:

- Photos / videos: "Remove photo IMG_0023" / "Remove video clip.mp4"
  (fallback "Remove photo" / "Remove video" when the attachment has no
  title or name)
- Other files: "Remove file report.pdf" (fallback "Remove file")
- Voice recordings: "Remove voice message"

Adds matching translations for the 7 supported locales. The previously
generic stream_compose_remove_attachment resource has no remaining
callers and is dropped from all locales, mirroring the cleanup done in
PR #6404 for stream_compose_attachments.

* Cover unnamed-fallback branches of attachment remove descriptions

Add three Paparazzi snapshot tests rendering each composer attachment
item (image, video, file) with a copy of the existing preview data that
clears the title and name, exercising the previously uncovered fallback
arms of fileAttachmentRemoveDescription and mediaAttachmentRemoveDescription.
Extract a shared MessageComposerAttachmentItem helper to dedupe the
preview-handler wiring across the four media-item previews.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:improvement Improvement released Included in a release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants