Skip to content

Allow voters to suggest poll options in UI Components#6439

Open
gpunto wants to merge 1 commit into
v6from
v6-xml-poll-add-option
Open

Allow voters to suggest poll options in UI Components#6439
gpunto wants to merge 1 commit into
v6from
v6-xml-poll-add-option

Conversation

@gpunto
Copy link
Copy Markdown
Contributor

@gpunto gpunto commented May 15, 2026

Goal

Bring the "voters can suggest new options" poll feature to the XML UI Components, matching what's
already available in the Compose UI.

Implementation

  • PollView renders a new "Suggest an option" entry when poll.allowUserSuggestedOptions is true
    and the poll is open. Tapping it routes through a new OnSuggestPollOptionClickListener on
    MessageListView, whose default handler shows SuggestPollOptionDialogFragment.
  • SuggestPollOptionDialogFragment collects the option text and returns it via the fragment-result
    API; MessageListViewModel.bindView listens for the result and emits a new
    Event.PollOptionSuggested(pollId, option).
  • MessageListController now exposes an addPollOption(pollId, option) overload so the suggested
    option can be added without first resolving a Poll. The existing addPollOption(Poll, String)
    delegates to it.
  • PollViewStyle gains pollSuggestOptionTextStyle with matching streamUiPollSuggestOption*
    attributes for customization.

UI Changes

Poll with new button Add option dialog
Screenshot_20260515_122429 Screenshot_20260515_122436

Testing

  • Open a channel with a poll that has allowUserSuggestedOptions = true: a "Suggest an option"
    button shows up; closed polls and polls without the flag do not show it.
  • Tapping it opens the dialog; the Confirm button stays disabled until non-blank text is entered;
    confirming adds the option to the poll, dismissing does not.

Summary by CodeRabbit

Release Notes

  • New Features

    • Users can now suggest poll options in polls configured to allow suggestions.
    • New dialog interface for submitting poll option suggestions with real-time input validation.
    • Enhanced poll UI with customizable styling for suggested option interactions.
  • Style

    • Added new styling attributes for poll suggestion UI elements.

Review Change Stack

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 15, 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 (or ignored for dependabot PRs).

🎉 Great job! This PR is ready for review.

@gpunto gpunto added the pr:new-feature New feature label May 15, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 15, 2026

SDK Size Comparison 📏

SDK Before After Difference Status
stream-chat-android-client 5.26 MB 5.26 MB 0.00 MB 🟢
stream-chat-android-offline 5.49 MB 5.49 MB 0.00 MB 🟢
stream-chat-android-ui-components 10.64 MB 10.65 MB 0.01 MB 🟢
stream-chat-android-compose 12.87 MB 12.87 MB 0.00 MB 🟢

@gpunto
Copy link
Copy Markdown
Contributor Author

gpunto commented May 15, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 15, 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.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 15, 2026

Walkthrough

This PR implements end-to-end support for user-suggested poll options in the message list UI. It refactors the addPollOption controller API to accept a poll ID string, adds a dialog fragment for option input, renders a "suggest option" item in polls, wires listeners through the view hierarchy, and integrates fragment results with the ViewModel event system.

Changes

Suggest Poll Option Feature

Layer / File(s) Summary
MessageListController API Refactor
stream-chat-android-ui-common/api/stream-chat-android-ui-common.api, stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/list/MessageListController.kt
addPollOption(poll: Poll, option: String) now delegates to a new addPollOption(pollId: String, option: String) overload that launches the coroutine and calls the chat client.
Public API: Listener Interface and ViewModel Event
stream-chat-android-ui-components/api/stream-chat-android-ui-components.api, stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/MessageListView.kt, stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/MessageListViewModel.kt
Adds public OnSuggestPollOptionClickListener interface and setter to MessageListView, introduces PollOptionSuggested event to ViewModel.Event, and updates API surface signatures.
Listener Container Wiring
stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/MessageListListenerContainer.kt, stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/MessageListListenerContainerImpl.kt
Threads OnSuggestPollOptionClickListener through MessageListListeners interface and MessageListListenerContainerImpl with constructor parameter and ListenerDelegate property.
Suggest Poll Option Dialog Fragment
stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/internal/poll/SuggestPollOptionDialogFragment.kt
Implements AppCompatDialogFragment with themed alert dialog, input field validation (enable submit only when trimmed text is non-empty), and fragment-result API to return poll ID and option text.
Poll View Suggest Option Rendering
stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/view/internal/PollView.kt
Adds onSuggestOptionClick callback property, conditionally includes PollItem.SuggestOption when poll.allowUserSuggestedOptions is true and poll is open, wires view holder and applies styling.
Poll Styling for Suggest Option
stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/view/PollViewStyle.kt
Adds pollSuggestOptionTextStyle property to PollViewStyle data class, extends attribute parsing to read suggest-option styling from resources.
MessageListView Default Listener and Wiring
stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/MessageListView.kt
Introduces defaultOnSuggestPollOptionClickListener that opens SuggestPollOptionDialogFragment, wires into listener container, provides public override setter.
Poll ViewHolder Listener Binding
stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/viewholder/impl/PollViewHolder.kt
Connects poll UI suggest option click to messageListListeners?.onSuggestPollOptionClickListener?.onSuggestPollOptionClick(poll) in bindData.
ViewModel Fragment Result Binding
stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/MessageListViewModelBinding.kt
Registers fragment result listener that reads poll ID and trimmed option text from SuggestPollOptionDialogFragment, dispatches PollOptionSuggested event to ViewModel.
Layout and String Resources
stream-chat-android-ui-components/src/main/res/layout/stream_ui_dialog_suggest_poll_option.xml, stream-chat-android-ui-components/src/main/res/layout/stream_ui_item_poll_suggest_option.xml, stream-chat-android-ui-components/src/main/res/values/attrs_poll_view.xml, stream-chat-android-ui-components/src/main/res/values/strings.xml
Adds dialog input layout with edit text, poll list item layout with "Suggest an option" label, poll-view style attributes for suggest option appearance, and dialog title/button strings.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

pr:improvement

Suggested reviewers

  • VelikovPetar

Poem

🐰 A whisper suggests a choice so new,
Poll options bloom in dialog's view,
From click to style, a path runs true,
Suggesting voices, tried and true! 🗳️✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% 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 'Allow voters to suggest poll options in UI Components' directly and clearly describes the main change: enabling voters to suggest poll options in the XML UI Components module.
Description check ✅ Passed The PR description includes all key sections from the template: Goal (why the change), Implementation (detailed technical approach), UI Changes (with screenshots), and Testing (how to validate). Minor checklist items are unchecked, but the core content is comprehensive.
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 v6-xml-poll-add-option

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.

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.

Caution

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

⚠️ Outside diff range comments (2)
stream-chat-android-ui-components/api/stream-chat-android-ui-components.api (2)

3074-3086: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Use a default method instead of abstract to maintain binary compatibility.

Adding a new abstract method to a public interface breaks binary compatibility with existing compiled implementations. Pre-compiled code implementing MessageListListeners will fail at runtime when the JVM expects the new method. Replace the abstract getter with a default method that returns a sensible default (or no-op) implementation.

🤖 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-ui-components/api/stream-chat-android-ui-components.api`
around lines 3074 - 3086, The new abstract getter
getOnSuggestPollOptionClickListener added to the MessageListListeners interface
breaks binary compatibility; change it from an abstract declaration to a default
method on interface
io.getstream.chat.android.ui.feature.messages.list.adapter.MessageListListeners
that returns a safe default (e.g., null or a no-op
MessageListView.OnSuggestPollOptionClickListener) so existing compiled
implementors continue to work; implement the default directly on the interface
alongside the other getters (e.g., provide default fun
getOnSuggestPollOptionClickListener():
MessageListView.OnSuggestPollOptionClickListener? = null).

3167-3178: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

This PollViewStyle signature change is ABI-breaking.

Adding a new primary-constructor property changes the public constructor, copy, copy$default, and componentN surface of an existing public type. Existing compiled consumers of the old API won't link cleanly. Preserve the old constructor/copy ABI and surface the new style through a compatibility-preserving API.

🤖 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-ui-components/api/stream-chat-android-ui-components.api`
around lines 3167 - 3178, The change to PollViewStyle added a new
primary-constructor property which breaks the public ABI (constructor, copy,
copy$default, componentN); restore backward compatibility by preserving the old
constructor/copy/component methods: keep the original constructor signature
available (add a secondary/overload that matches the previous parameter list and
delegates to the new primary, supplying a sensible default for the new
property), and provide compatibility shims for copy/copy$default and
component1..componentN (implement overloads or synthetic forwarding methods with
the original signatures that delegate to the new implementations) so existing
compiled consumers can still link against PollViewStyle while exposing the new
property via the new API.
🧹 Nitpick comments (2)
stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/list/MessageListController.kt (1)

2453-2463: ⚡ Quick win

Consider adding error handling for better user experience.

The method silently swallows errors when the API call fails, leaving users without feedback when suggesting an option fails. For consistency with other pollId-based methods in this class (e.g., closePoll(pollId: String) at line 2004, castVote at line 1945), consider using the .enqueue(onError = ...) pattern to handle errors gracefully.

Suggested error handling pattern
 public fun addPollOption(pollId: String, option: String) {
-    scope.launch {
-        chatClient.createPollOption(pollId, PollOption(text = option)).await()
-    }
+    chatClient.createPollOption(pollId, PollOption(text = option))
+        .enqueue(onError = { error ->
+            onActionResult(error) {
+                ErrorEvent.PollCastingVoteError(it)
+            }
+        })
 }
🤖 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-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/list/MessageListController.kt`
around lines 2453 - 2463, The addPollOption method currently launches a
coroutine and calls chatClient.createPollOption(...).await() but swallows
errors; update addPollOption(pollId: String, option: String) to use the same
enqueue(onError = ...) pattern used by closePoll and castVote: call
chatClient.createPollOption(pollId, PollOption(text = option)).enqueue(onError =
{ /* log or surface error to UI / callback */ }) (or propagate the error via the
controller callback/state) so failures are logged/handled instead of silently
ignored.
stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/internal/poll/SuggestPollOptionDialogFragment.kt (1)

19-19: 💤 Low value

Prefer androidx.appcompat.app.AlertDialog for consistency with AppCompat base class.

Since this fragment extends AppCompatDialogFragment, using androidx.appcompat.app.AlertDialog instead of android.app.AlertDialog would be more consistent with the AndroidX/AppCompat ecosystem.

♻️ Suggested import change
-import android.app.AlertDialog
+import androidx.appcompat.app.AlertDialog
🤖 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-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/internal/poll/SuggestPollOptionDialogFragment.kt`
at line 19, Replace the platform AlertDialog import with the AppCompat one for
consistency: in SuggestPollOptionDialogFragment (class extending
AppCompatDialogFragment) remove import android.app.AlertDialog and use
androidx.appcompat.app.AlertDialog instead so the fragment uses AppCompat's
AlertDialog; update any fully-qualified references if present to the
androidx.appcompat.app.AlertDialog symbol.
🤖 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-ui-components/api/stream-chat-android-ui-components.api`:
- Around line 3074-3086: The new abstract getter
getOnSuggestPollOptionClickListener added to the MessageListListeners interface
breaks binary compatibility; change it from an abstract declaration to a default
method on interface
io.getstream.chat.android.ui.feature.messages.list.adapter.MessageListListeners
that returns a safe default (e.g., null or a no-op
MessageListView.OnSuggestPollOptionClickListener) so existing compiled
implementors continue to work; implement the default directly on the interface
alongside the other getters (e.g., provide default fun
getOnSuggestPollOptionClickListener():
MessageListView.OnSuggestPollOptionClickListener? = null).
- Around line 3167-3178: The change to PollViewStyle added a new
primary-constructor property which breaks the public ABI (constructor, copy,
copy$default, componentN); restore backward compatibility by preserving the old
constructor/copy/component methods: keep the original constructor signature
available (add a secondary/overload that matches the previous parameter list and
delegates to the new primary, supplying a sensible default for the new
property), and provide compatibility shims for copy/copy$default and
component1..componentN (implement overloads or synthetic forwarding methods with
the original signatures that delegate to the new implementations) so existing
compiled consumers can still link against PollViewStyle while exposing the new
property via the new API.

---

Nitpick comments:
In
`@stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/list/MessageListController.kt`:
- Around line 2453-2463: The addPollOption method currently launches a coroutine
and calls chatClient.createPollOption(...).await() but swallows errors; update
addPollOption(pollId: String, option: String) to use the same enqueue(onError =
...) pattern used by closePoll and castVote: call
chatClient.createPollOption(pollId, PollOption(text = option)).enqueue(onError =
{ /* log or surface error to UI / callback */ }) (or propagate the error via the
controller callback/state) so failures are logged/handled instead of silently
ignored.

In
`@stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/internal/poll/SuggestPollOptionDialogFragment.kt`:
- Line 19: Replace the platform AlertDialog import with the AppCompat one for
consistency: in SuggestPollOptionDialogFragment (class extending
AppCompatDialogFragment) remove import android.app.AlertDialog and use
androidx.appcompat.app.AlertDialog instead so the fragment uses AppCompat's
AlertDialog; update any fully-qualified references if present to the
androidx.appcompat.app.AlertDialog symbol.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0f4de5d3-8cc1-4dbb-b1b8-7b7a31adbac6

📥 Commits

Reviewing files that changed from the base of the PR and between 99a0274 and ea18512.

📒 Files selected for processing (16)
  • stream-chat-android-ui-common/api/stream-chat-android-ui-common.api
  • stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/list/MessageListController.kt
  • stream-chat-android-ui-components/api/stream-chat-android-ui-components.api
  • stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/MessageListView.kt
  • stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/MessageListListenerContainer.kt
  • stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/MessageListListenerContainerImpl.kt
  • stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/view/PollViewStyle.kt
  • stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/view/internal/PollView.kt
  • stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/viewholder/impl/PollViewHolder.kt
  • stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/internal/poll/SuggestPollOptionDialogFragment.kt
  • stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/MessageListViewModel.kt
  • stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/MessageListViewModelBinding.kt
  • stream-chat-android-ui-components/src/main/res/layout/stream_ui_dialog_suggest_poll_option.xml
  • stream-chat-android-ui-components/src/main/res/layout/stream_ui_item_poll_suggest_option.xml
  • stream-chat-android-ui-components/src/main/res/values/attrs_poll_view.xml
  • stream-chat-android-ui-components/src/main/res/values/strings.xml

@gpunto gpunto force-pushed the v6-xml-poll-add-option branch from ea18512 to d0a51b5 Compare May 15, 2026 10:46
@sonarqubecloud
Copy link
Copy Markdown

@gpunto gpunto marked this pull request as ready for review May 15, 2026 12:29
@gpunto gpunto requested a review from a team as a code owner May 15, 2026 12:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:new-feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant