compose: Render composer input and message text in content direction#6527
Conversation
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.
|
@CodeRabbit review |
PR checklist ✅All required conditions are satisfied:
🎉 Great job! This PR is ready for review. |
✅ Action performedReview finished.
|
SDK Size Comparison 📏
|
WalkthroughThe 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. ChangesRTL text direction handling
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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 winAdd 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 winDocument or remove the new
LongMethodsuppression.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@OptInannotations 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
⛔ 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.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.messages_MessageComposerInputTest_rtl_input_aligns_to_the_end.pngis excluded by!**/*.png
📒 Files selected for processing (7)
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/composer/MessageInput.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/MessageText.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/QuotedMessage.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/MessageComposerInputCenterContent.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/MessageStyling.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/messages/MessageTextTest.ktstream-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.
|
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.
Adding the Paparazzi snapshot for the RTL quoted-message path in (•ᴗ•)🐇 ✏️ Learnings added
|
|
|
🚀 Available in v7.5.0 |



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 theBasicTextFieldin aCompositionLocalProviderthat provides it. This is the v7 home of the oldMessageInputchange, because the text field now renders through theMessageComposerInputCenterContentcomponent factory.MessageStyling.textStyle: settextDirection = TextDirection.Contenton the message text style so bubbles render in the direction of their content. This covers both the direct render path and the defaultMessageTextFormatter.QuotedMessage: settextDirection = TextDirection.Contenton the quoted text style.All changes are internal or private.
apiCheckreports 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
Testing
Tested on an emulator with an LTR device locale, which is the AND-993 scenario (RTL keyboard, not device language):
مرحبا، هذه رسالة تجريبية.I also checked the send path for the punctuation reorder reported in AND-993: the sent string is
state.inputValuepassed straight tobuildNewMessage, 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:detektpass./gradlew :stream-chat-android-compose:apiCheckpass (no public API change)./gradlew :stream-chat-android-compose:verifyPaparazziDebugpass (full module)Summary by CodeRabbit
New Features
Bug Fixes