Skip to content

compose: Render composer input and message text in content direction#6527

Merged
andremion merged 2 commits into
developfrom
andrerego/and-1270-rtl-composer-text-direction
Jun 26, 2026
Merged

compose: Render composer input and message text in content direction#6527
andremion merged 2 commits into
developfrom
andrerego/and-1270-rtl-composer-text-direction

Conversation

@andremion

@andremion andremion commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Goal

The Compose composer input and message bubbles render RTL text (for example Arabic or Hebrew) with the wrong alignment when the device locale is LTR, or when RTL and LTR text are mixed. The input field and message text align to the left, and sentence-final punctuation appears at the visual start of the line.

This re-implements the v6 fix from #6487 (AND-993) on develop. The v6 patch could not be cherry-picked because the v7 composer and message theme were restructured.

Resolves AND-1270

Implementation

  • MessageComposerInputCenterContent: resolve the layout direction from the input content (the first strong directional character, falling back to the device locale) and wrap the BasicTextField in a CompositionLocalProvider that provides it. This is the v7 home of the old MessageInput change, because the text field now renders through the MessageComposerInputCenterContent component factory.
  • MessageStyling.textStyle: set textDirection = TextDirection.Content on the message text style so bubbles render in the direction of their content. This covers both the direct render path and the default MessageTextFormatter.
  • QuotedMessage: set textDirection = TextDirection.Content on the quoted text style.
  • Added Paparazzi snapshots for the RTL composer input and RTL message text (dark and light).

All changes are internal or private. apiCheck reports no public API change.

Difference from v6: the v6 fix wrapped the whole composer input row, so the send button mirrored too. Here only the text field follows the content direction and the send button keeps its position. Happy to switch to full-row mirroring if you prefer parity with v6.

🎨 UI Changes

Before After
before_composer_input after_composer_input
before_sent_message after_sent_message

Testing

Tested on an emulator with an LTR device locale, which is the AND-993 scenario (RTL keyboard, not device language):

  1. Open a channel.
  2. Type an RTL sentence with trailing punctuation, for example مرحبا، هذه رسالة تجريبية.
  3. The composer input aligns to the right, and the period stays at the logical end (visual left), not at the start.
  4. Send the message. The bubble renders the text right-aligned with the period at the logical end.
  5. Type LTR text and confirm it still aligns to the left.

I also checked the send path for the punctuation reorder reported in AND-993: the sent string is state.inputValue passed straight to buildNewMessage, which only prepends an active command and trims whitespace. No character reordering happens, so the symptom was a rendering issue, fixed by the message text direction change.

Validation:

  • ./gradlew :stream-chat-android-compose:detekt pass
  • ./gradlew :stream-chat-android-compose:apiCheck pass (no public API change)
  • ./gradlew :stream-chat-android-compose:verifyPaparazziDebug pass (full module)

Summary by CodeRabbit

  • New Features

    • Improved right-to-left (RTL) support in message input and message text previews, including Arabic sample content for better visual validation.
  • Bug Fixes

    • Fixed text alignment and direction handling so mixed, neutral, and RTL content displays more naturally in the composer and quoted messages.

Fix RTL text alignment in the Compose composer and message bubbles when
typing an RTL language (e.g. Arabic, Hebrew) on an LTR device or mixing
RTL/LTR text. The input field and message text rendered left-aligned, and
sentence-final punctuation appeared at the visual start.

Resolve the layout direction from the input content and wrap the composer
text field, and set textDirection = TextDirection.Content on the message
and quoted-message text styles. Re-implementation of v6 PR #6487 against
the redesigned v7 composer and theme.
@andremion andremion added the pr:bug Bug fix label Jun 26, 2026
@andremion

Copy link
Copy Markdown
Contributor Author

@CodeRabbit review

@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

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, or the PR is bot-authored.
  • An issue is linked (Linear ticket or GitHub issue), or the PR is bot-authored.

🎉 Great job! This PR is ready for review.

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

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

SDK Size Comparison 📏

SDK Before After Difference Status
stream-chat-android-client 5.90 MB 5.90 MB 0.00 MB 🟢
stream-chat-android-ui-components 11.15 MB 11.15 MB 0.00 MB 🟢
stream-chat-android-compose 12.63 MB 12.63 MB 0.00 MB 🟢

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The PR adds RTL preview and snapshot coverage for message input and message text, and updates composer input, quoted message text, and message styling to use content-aware layout or text direction.

Changes

RTL text direction handling

Layer / File(s) Summary
Composer input layout direction
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/MessageComposerInputCenterContent.kt, stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/composer/MessageInput.kt, stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/MessageComposerInputTest.kt
MessageComposerInputCenterContent derives a LayoutDirection from the current input text, provides it to BasicTextField, and adds RTL preview and snapshot coverage for the composer input.
Message text styling and snapshots
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/MessageStyling.kt, stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/QuotedMessage.kt, stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/MessageText.kt, stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/messages/MessageTextTest.kt
MessageStyling and quoted message text set TextDirection.Content, and MessageTextRtl plus its snapshot test render an Arabic message.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant MessageComposerInputCenterContent
  participant String.contentLayoutDirection
  participant CompositionLocalProvider
  participant BasicTextField

  User->>MessageComposerInputCenterContent: updates input value
  MessageComposerInputCenterContent->>String.contentLayoutDirection: inspect first strong character
  String.contentLayoutDirection-->>MessageComposerInputCenterContent: LayoutDirection or fallback
  MessageComposerInputCenterContent->>CompositionLocalProvider: provide LocalLayoutDirection
  CompositionLocalProvider->>BasicTextField: render text with content direction
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

pr:improvement

Suggested reviewers

  • aleksandar-apostolov

Poem

A bunny hops through text at night,
With Arabic carrots left and right.
The cursor twirls, the previews glow,
And snapshots say, “Yes, this looks so.” 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the RTL content-direction rendering fix for composer input and message text.
Description check ✅ Passed The PR description covers the required goal, implementation, UI changes, and testing sections.
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.
✨ 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 andrerego/and-1270-rtl-composer-text-direction

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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/messages/QuotedMessage.kt (1)

213-223: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a Paparazzi case for quoted RTL text.

This changes the quoted-message rendering path, but the supplied test additions only cover the composer and full message text. A reply-specific snapshot would keep this fix from regressing silently in QuotedMessage. As per coding guidelines, stream-chat-android-compose/**/*{Test,Snapshot}.kt: Add Paparazzi snapshots for Compose UI regressions and run verifyPaparazziDebug.

🤖 Prompt for 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.

In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/QuotedMessage.kt`
around lines 213 - 223, Add a Paparazzi snapshot test for quoted RTL text in
QuotedMessage to cover this rendering path and prevent regressions. Extend the
existing Compose snapshot coverage with a reply-specific case that exercises
QuotedMessage using RTL content and verifies the quoted preview renders
correctly, following the same pattern used in the current Test/Snapshot files.
Keep the new case alongside the existing quoted-message test helpers so it is
easy to locate with QuotedMessage and the related snapshot test classes.

Source: Coding guidelines

🧹 Nitpick comments (1)
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/MessageComposerInputCenterContent.kt (1)

62-62: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document or remove the new LongMethod suppression.

This adds a fresh suppression without explaining why the exception is needed. Please either extract the new direction-handling block into a helper or add a brief rationale next to the annotation so the deviation is intentional and reviewable. As per coding guidelines, **/*.kt: Use @OptIn annotations explicitly in Kotlin code; avoid suppressions unless documented.

🤖 Prompt for 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.

In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/MessageComposerInputCenterContent.kt`
at line 62, The new LongMethod suppression on MessageComposerInputCenterContent
is undocumented, so either remove it by extracting the direction-handling logic
into a helper or add a brief inline rationale next to the suppression so the
exception is explicit and reviewable. Update the affected composer code in
MessageComposerInputCenterContent to keep the intent clear, and prefer an
explicit `@OptIn` approach where applicable instead of an unexplained suppression.

Source: Coding guidelines

🤖 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.

Outside diff comments:
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/QuotedMessage.kt`:
- Around line 213-223: Add a Paparazzi snapshot test for quoted RTL text in
QuotedMessage to cover this rendering path and prevent regressions. Extend the
existing Compose snapshot coverage with a reply-specific case that exercises
QuotedMessage using RTL content and verifies the quoted preview renders
correctly, following the same pattern used in the current Test/Snapshot files.
Keep the new case alongside the existing quoted-message test helpers so it is
easy to locate with QuotedMessage and the related snapshot test classes.

---

Nitpick comments:
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/MessageComposerInputCenterContent.kt`:
- Line 62: The new LongMethod suppression on MessageComposerInputCenterContent
is undocumented, so either remove it by extracting the direction-handling logic
into a helper or add a brief inline rationale next to the suppression so the
exception is explicit and reviewable. Update the affected composer code in
MessageComposerInputCenterContent to keep the intent clear, and prefer an
explicit `@OptIn` approach where applicable instead of an unexplained suppression.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c4d83af8-a971-4d2a-8672-a18d12c04296

📥 Commits

Reviewing files that changed from the base of the PR and between 636018c and b92d5b7.

⛔ Files ignored due to path filters (2)
  • stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_MessageTextTest_rtl_text_aligns_to_the_end.png is excluded by !**/*.png
  • stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.messages_MessageComposerInputTest_rtl_input_aligns_to_the_end.png is excluded by !**/*.png
📒 Files selected for processing (7)
  • 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/components/messages/MessageText.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/QuotedMessage.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/MessageComposerInputCenterContent.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/MessageStyling.kt
  • stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/messages/MessageTextTest.kt
  • stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/MessageComposerInputTest.kt

Add a Paparazzi snapshot covering the RTL quoted-message text path, and
regenerate the public API dump to include the preview-lambda accessors
generated by the new @Preview composables.
@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

The Comment images show Android chat UI screenshots for a channel titled “Third Shower” with “9 Members, 1 Online” in the header, a back arrow on the left, and a circular avatar on the right. The status bar shows times around 1:54–2:11, plus muted, Wi‑Fi, and battery icons.

In the first Comment image, the conversation includes earlier link-preview messages (“Scalable Feeds, Chat, & Video - Powerful APIs and Components by Stream” and a Google preview), followed by a light-blue outgoing Arabic message bubble on the right reading "مرحبا، هذه رسالة تجريبية." with a timestamp around 1:55 PM. At the bottom, the composer contains the same Arabic text, but it wraps into two lines, with the cursor visible and a blue send button on the right.

In the second Comment image, the same chat is visible, but the composer shows the Arabic text on a single line: "مرحبا، هذه رسالة تجريبية." with the blue send button at the right edge. This makes the punctuation placement and right-aligned composition easier to compare.

In the third Comment image, two outgoing Arabic bubbles are visible on the right: one timestamped 1:55 PM and another 2:11 PM, both showing "مرحبا، هذه رسالة تجريبية." The bottom input is disabled by slow mode and reads “Slow mode, wait 17s…” with a circular counter showing 18.

In the fourth Comment image, a single outgoing Arabic bubble is shown on the right with the same text "مرحبا، هذه رسالة تجريبية." and timestamp 1:54 PM, again with the slow-mode input state “Slow mode, wait 17s…” and the 18 counter. Across these images, the Arabic content appears right-aligned in both the composer and sent message bubbles, with sentence-final punctuation rendered at the visual end rather than the start.

@andremion Thanks — that all sounds good.

Adding the Paparazzi snapshot for the RTL quoted-message path in cecc34b174bbb15cbd6b17e8519d6cb6a36fc296 closes the remaining coverage gap nicely, and the rationale for keeping the LongMethod suppression on MessageComposerInputCenterContent as-is makes sense given the BasicTextField/decorationBox cohesion.

(•ᴗ•)🐇


✏️ Learnings added
Learnt from: andremion
URL: https://github.com/GetStream/stream-chat-android/pull/6527

Timestamp: 2026-06-26T13:39:28.698Z
Learning: In pull request `#6527` of the `GetStream/stream-chat-android` repository, RTL quoted-message rendering coverage is provided by a Paparazzi snapshot added in commit `cecc34b174bbb15cbd6b17e8519d6cb6a36fc296`.

Learnt from: andremion
URL: https://github.com/GetStream/stream-chat-android/pull/6527

Timestamp: 2026-06-26T13:39:28.698Z
Learning: In `stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/MessageComposerInputCenterContent.kt`, the `LongMethod` suppression should be kept without an added doc comment because the `BasicTextField` and its `decorationBox` are treated as one cohesive declaration and splitting them would not improve readability.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@sonarqubecloud

Copy link
Copy Markdown

@andremion andremion marked this pull request as ready for review June 26, 2026 13:47
@andremion andremion requested a review from a team as a code owner June 26, 2026 13:47
@andremion andremion enabled auto-merge (squash) June 26, 2026 13:48
@andremion andremion merged commit 12d2ea3 into develop Jun 26, 2026
19 checks passed
@andremion andremion deleted the andrerego/and-1270-rtl-composer-text-direction branch June 26, 2026 14:31
@stream-public-bot stream-public-bot added the released Included in a release label Jul 1, 2026
@stream-public-bot

Copy link
Copy Markdown
Contributor

🚀 Available in v7.5.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:bug Bug fix released Included in a release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants