Skip to content

Enforce poll maxVotesAllowed on the XML voting side#6502

Draft
gpunto wants to merge 3 commits into
developfrom
fix/limit-poll-votes-xml
Draft

Enforce poll maxVotesAllowed on the XML voting side#6502
gpunto wants to merge 3 commits into
developfrom
fix/limit-poll-votes-xml

Conversation

@gpunto

@gpunto gpunto commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Goal

The XML UI SDK never checks Poll.maxVotesAllowed when the user taps an option to vote. Both the inline poll on the message (PollView) and the "see all options" bottom sheet (AllPollOptionsDialogFragment) call ChatClient.castPollVote unconditionally, so a user can vote past the configured limit.

Closes AND-1237

Implementation

  • New internal Poll.canCastVote() extension in ui-common.
  • XML: both voting sites (PollView, AllPollOptionsDialogFragment) skip the cast when canCastVote() is false. Un-voting always goes through.
  • Compose: replaced PollOptionVotingRow's checkedCount parameter with a direct poll.canCastVote() call. Both callers passed poll.ownVotes.size, so no behaviour change.

Testing

  • PollExtensionsTest: three new cases cover canCastVote() for maxVotesAllowed = null, ownVotes.size < maxVotesAllowed, and ownVotes.size == maxVotesAllowed.
  • Manual on the sample app: created polls with maxVotesAllowed = 1, 2, and unlimited in both XML and Compose. Verified that tapping a third option in a 2-vote poll is now a no-op (XML inline + "see all options" sheet, and Compose).

Summary by CodeRabbit

  • Bug Fixes

    • Improved vote casting validation in polls to consistently enforce vote limits across the app.
  • Tests

    • Added unit tests for poll vote limit validation behavior.

@gpunto gpunto added the pr:bug Bug fix label Jun 16, 2026
@github-actions

github-actions Bot commented Jun 16, 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.

@gpunto

gpunto commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 16, 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

github-actions Bot commented Jun 16, 2026

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.13 MB 11.13 MB 0.00 MB 🟢
stream-chat-android-compose 12.57 MB 12.57 MB 0.00 MB 🟢

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Introduces a Poll.canCastVote() extension function that checks whether the current user can cast another vote based on maxVotesAllowed and ownVotes.size. All poll UI components—PollOptionVotingRow, PollMoreOptionsDialog, PollView, and AllPollOptionsDialogFragment—are updated to use this extension, replacing scattered inline checkedCount-based eligibility logic.

Changes

Poll Vote Eligibility Centralization

Layer / File(s) Summary
Poll.canCastVote() extension and tests
stream-chat-android-ui-common/src/main/kotlin/.../utils/extensions/Poll.kt, stream-chat-android-ui-common/src/test/.../extensions/PollExtensionsTest.kt
Adds Poll.canCastVote() returning true when maxVotesAllowed is null or ownVotes.size is below the limit. Three unit tests cover the null-limit, below-limit, and at-limit cases.
PollOptionVotingRow and Compose callers
...compose/.../poll/PollOptionVotingRow.kt, ...compose/.../messages/PollMessageContent.kt, ...compose/.../poll/PollMoreOptionsDialog.kt
PollOptionVotingRow removes checkedCount: Int param and calls poll.canCastVote() for eligibility in onToggle. PollMessageContent and PollMoreOptionsDialog/PollMoreOptionItem are updated to pass a per-option checked: Boolean instead of checkedCount.
PollView and AllPollOptionsDialogFragment
...ui-components/.../adapter/view/internal/PollView.kt, ...ui-components/.../poll/AllPollOptionsDialogFragment.kt
PollView derives canCastVote from poll.canCastVote(), passes it into PollItem.Answer, and short-circuits click handling when canCastVote is false and no prior vote exists. AllPollOptionsDialogFragment.onOptionClick adds an explicit poll.canCastVote() guard before casting a new vote.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • GetStream/stream-chat-android#6461: Modifies PollMessageContent.kt poll option toggle handling, overlapping directly with this PR's per-option onToggle behavior changes.
  • GetStream/stream-chat-android#6494: Modifies PollOptionVotingRow.kt gesture and interaction wiring at the same composable level affected by this PR's eligibility refactor.

Suggested labels

pr:improvement, released

Suggested reviewers

  • andremion

🐇 A rabbit hopped through the vote-count maze,
Found checkedCount scattered in a haze.
One little function, canCastVote() it's named,
Now eligibility is neatly reclaimed!
No more inline math — the extension explains. 🗳️

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the primary change: enforcing the poll maxVotesAllowed limit on the XML voting side, which is the core bug fix addressed in this PR.
Description check ✅ Passed The description covers the Goal, Implementation, and Testing sections from the template. However, it lacks UI Changes (screenshots/videos), explicit GIF, and contributor/reviewer checklists.
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.

✨ 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 fix/limit-poll-votes-xml

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

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
`@stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/internal/poll/AllPollOptionsDialogFragment.kt`:
- Around line 214-220: In the AllPollOptionsDialogFragment, the vote mutation
logic for casting or removing poll votes does not guard against closed polls.
Add an early return guard immediately after retrieving the poll object (after
`val poll = pollState.value ?: return@launch`) that checks if the poll is closed
and returns early from the coroutine using `if (poll.closed) return@launch`.
This prevents closed polls from triggering castPollVote or removePollVote API
calls even though the option rows remain clickable in the UI.
🪄 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: f263849d-4c0f-4cba-9a0b-5986bc84709d

📥 Commits

Reviewing files that changed from the base of the PR and between 4c4779f and 2de0b86.

📒 Files selected for processing (7)
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/PollMessageContent.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollMoreOptionsDialog.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionVotingRow.kt
  • stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/utils/extensions/Poll.kt
  • stream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/utils/extensions/PollExtensionsTest.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/internal/poll/AllPollOptionsDialogFragment.kt
💤 Files with no reviewable changes (2)
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/PollMessageContent.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollMoreOptionsDialog.kt

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

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

See analysis details on SonarQube Cloud

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

Labels

pr:bug Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant