Skip to content

fix(ui): stop the poll option votes and comments dialogs listing other votes (v9) - #3060

Merged
VelikovPetar merged 2 commits into
fix/flu-974_poll_vote_list_other_polls_v9from
fix/flu-975_poll_vote_list_scope_v9
Oct 15, 2026
Merged

VelikovPetar merged 2 commits into
fix/flu-974_poll_vote_list_other_polls_v9from
fix/flu-975_poll_vote_list_scope_v9

Conversation

@VelikovPetar

@VelikovPetar VelikovPetar commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Submit a pull request

Linear: FLU-975
Github Issue: #

CLA

  • I have signed the Stream CLA (required).
  • The code changes follow best practices
  • Code changes are tested (add some information if not applicable)

Description of the pull request

Port of #3059 to v9. Stacked on #3058 (the v9 port of #3057) — review that first; this PR's base is its branch.

StreamPollOptionVotesDialog and StreamPollCommentsDialog scoped their StreamPollVoteListController only with a Filter (option_id / is_answer), which applies to the REST query alone. Live events went through the default StreamPollVoteEventHandler, which adds every cast vote and upserts every changed vote by id. So:

  • a vote cast for another option, or an answer, appeared in an option's voters dialog;
  • a vote changed from this option to another stayed listed, updated in place to the other option;
  • votes for poll options appeared as comments.

The SDK can't evaluate an arbitrary Filter locally, so stream_chat_flutter_core gets two constructors that tell the controller what the list covers:

  • StreamPollVoteListController.forOption(channel:, pollId:, optionId:)
  • StreamPollVoteListController.forAnswers(channel:, pollId:)

Each queries only those votes and makes the default handler ignore cast votes outside the list and remove a vote changed out of it. Both dialogs use them.

Additive only: the default constructor and .fromValue behave as before, and StreamPollVoteEventHandler's signatures are unchanged.

This matches iOS, whose PollVoteListQuery(pollId:optionId:) only links a non-answer vote when query.optionId == vote.optionId.

Same change as #3059; only formatting (80 columns) and the sheet → dialog naming differ.

Testing

  • stream_poll_vote_list_controller_test.dart (fix(core): ignore other polls' votes in StreamPollVoteListController (v9) #3058's file): option and answers scopes — the queried filter, cast votes inside/outside the scope, votes changed into/out of an option, answers vs option votes — plus whole-poll removal and change. The out-of-scope tests fail without the fix.
  • New stream_poll_option_votes_dialog_test.dart and stream_poll_comments_dialog_test.dart: an other-option vote isn't listed, a vote changed to another option stops being listed, an option vote isn't shown as a comment. They fail on the old dialogs.
  • Analyze clean; stream_chat_flutter_core suite passes.

Screenshots / Videos

Before After
— —

🤖 Generated with Claude Code

…r votes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 45b635c8-6deb-4239-8536-923611a53e00

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@VelikovPetar
VelikovPetar marked this pull request as ready for review October 14, 2026 07:49
@VelikovPetar
VelikovPetar added this pull request to stack #3062 October 14, 2026 07:50
@VelikovPetar
VelikovPetar requested a review from a team October 14, 2026 07:50
@codecov

codecov Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.14286% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 69.55%. Comparing base (df3c856) to head (04862ec).

Files with missing lines Patch % Lines
...core/lib/src/stream_poll_vote_list_controller.dart 96.87% 1 Missing ⚠️
Additional details and impacted files
@@                              Coverage Diff                              @@
##           fix/flu-974_poll_vote_list_other_polls_v9    #3060      +/-   ##
=============================================================================
+ Coverage                                      68.87%   69.55%   +0.68%     
=============================================================================
  Files                                            435      435              
  Lines                                          27711    27738      +27     
=============================================================================
+ Hits                                           19085    19294     +209     
+ Misses                                          8626     8444     -182     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@xsahil03x xsahil03x left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Faithful port of #3059: the controller is identical apart from v9's own base (package: import, no disposed guard), the dialogs get the same one-line swaps, and v9's answer resolver keeps the payload the same way. Correct for the same reasons as #3059, and CI is green.

The inline comments mirror #3059: rename option / answers → forOption / forAnswers and fix the "live votes" wording. The handler-doc suggestions are rewrapped for v9's 80-column limit. None block.

On the description: same as #3059, the "Notes:" section reads better as comments on the lines it justifies.

Comment on lines +59 to +65
/// Creates a Stream poll vote list controller that lists only the votes for
/// the poll option with `optionId`.
///
/// Live votes for other options and answers are ignored, and a vote changed
/// to another option is removed. Setting [filter] later changes only the
/// query; live votes stay limited to this option.
StreamPollVoteListController.option({

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same as #3059: StreamPollVoteListController.option(...) reads as if the controller were an option. Suggest forOption (paired with forAnswers below), which reads as "the vote list for this option". Singular, since it takes one optionId.

Also: "live votes" isn't defined anywhere; saying when the votes arrive is clearer.

Suggested change
/// Creates a Stream poll vote list controller that lists only the votes for
/// the poll option with `optionId`.
///
/// Live votes for other options and answers are ignored, and a vote changed
/// to another option is removed. Setting [filter] later changes only the
/// query; live votes stay limited to this option.
StreamPollVoteListController.option({
/// Creates a Stream poll vote list controller that lists only the votes for
/// the poll option with `optionId`.
///
/// Votes for other options and answers cast while the list is loaded are
/// ignored, and a vote changed to another option is removed. Setting [filter]
/// later changes only the query; incoming votes stay limited to this option.
StreamPollVoteListController.forOption({

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Renamed to forOption and applied the wording in 04862ec.

Comment on lines +72 to +77
/// Creates a Stream poll vote list controller that lists only the answers of
/// the poll.
///
/// Live votes for poll options are ignored. Setting [filter] later changes
/// only the query; live votes stay limited to answers.
StreamPollVoteListController.answers({

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Pair with forOption above, and the same "live votes" wording fix.

Suggested change
/// Creates a Stream poll vote list controller that lists only the answers of
/// the poll.
///
/// Live votes for poll options are ignored. Setting [filter] later changes
/// only the query; live votes stay limited to answers.
StreamPollVoteListController.answers({
/// Creates a Stream poll vote list controller that lists only the answers of
/// the poll.
///
/// Option votes cast while the list is loaded are ignored. Setting [filter]
/// later changes only the query; incoming votes stay limited to answers.
StreamPollVoteListController.forAnswers({

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Renamed to forAnswers and applied the wording in 04862ec.

Comment on lines +321 to +323
/// [StreamPollVoteListController.option] or
/// [StreamPollVoteListController.answers] ignores votes for other options or
/// votes that aren't answers, respectively.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Rename to match forOption / forAnswers, rewrapped to stay within 80 columns.

Suggested change
/// [StreamPollVoteListController.option] or
/// [StreamPollVoteListController.answers] ignores votes for other options or
/// votes that aren't answers, respectively.
/// [StreamPollVoteListController.forOption] or
/// [StreamPollVoteListController.forAnswers] ignores votes for other options
/// or votes that aren't answers, respectively.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 04862ec.

Comment on lines +345 to +347
/// [StreamPollVoteListController.option] or
/// [StreamPollVoteListController.answers] instead removes a vote that is no
/// longer for that option or isn't an answer, respectively.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Rename to match forOption / forAnswers, rewrapped to stay within 80 columns.

Suggested change
/// [StreamPollVoteListController.option] or
/// [StreamPollVoteListController.answers] instead removes a vote that is no
/// longer for that option or isn't an answer, respectively.
/// [StreamPollVoteListController.forOption] or
/// [StreamPollVoteListController.forAnswers] instead removes a vote that is
/// no longer for that option or isn't an answer, respectively.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 04862ec.


void _initializeController() {
_controller = StreamPollVoteListController(
_controller = StreamPollVoteListController.answers(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Rename to match forOption / forAnswers.

Suggested change
_controller = StreamPollVoteListController.answers(
_controller = StreamPollVoteListController.forAnswers(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 04862ec.

group('when the list is scoped to an option', () {
test('queries only the votes for the scoped option', () async {
final filter = await _queriedFilter(
(channel) => StreamPollVoteListController.option(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Rename to match forOption / forAnswers.

Suggested change
(channel) => StreamPollVoteListController.option(
(channel) => StreamPollVoteListController.forOption(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 04862ec.

group('when the list is scoped to answers', () {
test('queries only the answers', () async {
final filter = await _queriedFilter(
(channel) => StreamPollVoteListController.answers(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Rename to match forOption / forAnswers.

Suggested change
(channel) => StreamPollVoteListController.answers(
(channel) => StreamPollVoteListController.forAnswers(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 04862ec.

}) {
return _loadedController(
votes: votes,
create: (channel) => StreamPollVoteListController.option(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Rename to match forOption / forAnswers.

Suggested change
create: (channel) => StreamPollVoteListController.option(
create: (channel) => StreamPollVoteListController.forOption(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 04862ec.

StreamController<Event> events
})> _loadedAnswersController() {
return _loadedController(
create: (channel) => StreamPollVoteListController.answers(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Rename to match forOption / forAnswers.

Suggested change
create: (channel) => StreamPollVoteListController.answers(
create: (channel) => StreamPollVoteListController.forAnswers(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 04862ec.


✅ Added

- Added `StreamPollVoteListController.option` and `StreamPollVoteListController.answers`, which list only a poll's votes for one option or only its answers.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Rename to match forOption / forAnswers.

Suggested change
- Added `StreamPollVoteListController.option` and `StreamPollVoteListController.answers`, which list only a poll's votes for one option or only its answers.
- Added `StreamPollVoteListController.forOption` and `StreamPollVoteListController.forAnswers`, which list only a poll's votes for one option or only its answers.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 04862ec.

…ption and forAnswers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@VelikovPetar

Copy link
Copy Markdown
Contributor Author

Thanks! Also trimmed the "Notes:" section in the description down to the compatibility note and the iOS comparison.

@xsahil03x xsahil03x left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks, all applied in 04862ec, including the 80-column rewraps. LGTM.

@VelikovPetar
VelikovPetar merged commit 88231c1 into v9 Oct 15, 2026
20 checks passed
@VelikovPetar
VelikovPetar deleted the fix/flu-975_poll_vote_list_scope_v9 branch October 15, 2026 11:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants