-
Notifications
You must be signed in to change notification settings - Fork 310
Make Poll Results Dialog Shows All Votes with Pagination Support #6043
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
SDK Size Comparison 📏
|
...at-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ViewModelStore.kt
Show resolved
Hide resolved
.../main/kotlin/io/getstream/chat/android/ui/common/state/messages/poll/PollResultsViewState.kt
Outdated
Show resolved
Hide resolved
...e/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.kt
Outdated
Show resolved
Hide resolved
...e/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.kt
Outdated
Show resolved
Hide resolved
...o/getstream/chat/android/ui/feature/messages/list/internal/poll/PollResultsDialogFragment.kt
Outdated
Show resolved
Hide resolved
|
I think the wrong ticket is linked here, should be: AND-953 |
WalkthroughAdds poll results UI with stateful controllers and ViewModels, paginated "Show All" vote dialogs for Compose and XML, pagination helpers, updated layouts and strings, and tests. Introduces PollResultsViewState/PollOptionVotesViewState, controllers, Compose and Fragment ViewModels, and dialog/fragments for viewing and paginating votes. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant ComposeDialog as PollResultsDialog (Compose)
participant PRViewModel as PollResultsViewModel
participant PRController as PollResultsViewController
participant Poll
User->>ComposeDialog: Open poll results
ComposeDialog->>PRViewModel: instantiate(poll)
PRViewModel->>PRController: initialize with Poll
PRController->>Poll: read options, votes (preview)
PRController-->>PRViewModel: emit PollResultsViewState
PRViewModel-->>ComposeDialog: state flow updates
ComposeDialog->>User: render results with Show All
User->>ComposeDialog: Click Show All (option)
ComposeDialog->>PollOptionDialog: open(option)
activate PollOptionDialog
sequenceDiagram
participant User
participant PollOptionDialog as PollOptionVotesDialog
participant POVViewModel as PollOptionVotesViewModel
participant POVController as PollOptionVotesViewController
participant ChatClient
PollOptionDialog->>POVViewModel: instantiate(poll, option)
POVViewModel->>POVController: initialize
POVController->>ChatClient: queryPollVotes(option, limit)
ChatClient-->>POVController: votes page
POVController-->>POVViewModel: emit PollOptionVotesViewState (results)
POVViewModel-->>PollOptionDialog: state flow updates (render list)
User->>PollOptionDialog: scroll bottom -> trigger LoadMoreRequested
PollOptionDialog->>POVController: onViewAction(LoadMoreRequested)
POVController->>ChatClient: queryPollVotes(nextPage)
ChatClient-->>POVController: more votes or next=null
POVController-->>PollOptionDialog: updated state (append / canLoadMore=false)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (10)
stream-chat-android-ui-components/src/main/res/layout/stream_ui_fragment_poll_results.xml (1)
79-98: Consider accessibility and touch-blocking behavior.The loading overlay implementation is structurally sound, but consider these improvements:
Accessibility: The ProgressBar lacks a
contentDescription, which prevents screen readers from announcing the loading state to users with disabilities.Touch handling: The FrameLayout has no background and isn't marked
clickable="true". By default, this allows touch events to pass through to the content below when the overlay is visible. If the intent is to block user interaction during loading, this should be addressed.🔎 Proposed improvements
<FrameLayout android:id="@+id/loadingContainer" android:layout_width="0dp" android:layout_height="0dp" android:visibility="gone" + android:clickable="true" + android:focusable="true" app:layout_constraintTop_toBottomOf="@+id/toolbar" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toBottomOf="parent" > <ProgressBar android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" + android:contentDescription="@string/stream_ui_message_list_loading_messages" android:indeterminateDrawable="@drawable/stream_ui_rotating_indeterminate_progress_gradient" android:indeterminateDuration="1000" /> </FrameLayout>stream-chat-android-compose/src/main/res/values/strings.xml (1)
226-226: Consider more explicit button text for improved clarity.The resource name
stream_compose_poll_show_all_votessuggests this button shows all votes, but the display text is just "Show All". While this is concise for the UI, consider using "Show All Votes" for better clarity and accessibility, especially for screen readers or users viewing the button out of context.Alternative text option
- <string name="stream_compose_poll_show_all_votes">Show All</string> + <string name="stream_compose_poll_show_all_votes">Show All Votes</string>stream-chat-android-ui-components/api/stream-chat-android-ui-components.api (1)
5168-5179: PollResultsViewModel API looks consistent with existing ViewModel patternsThe new
PollResultsViewModel(+ nestedFactory(Poll)) fits the existing UI‑components pattern (getState(),getEvents(),onViewAction(...)), and the nested factory offers a clear entry point for Java/Kotlin consumers; the API surface itself looks good to expose.If you expect XML clients to wire this outside
PollResultsDialogFragmentas well, consider (optionally) adding a small binding helper similar to other*ViewModelBindingutilities for consistency, but this can be deferred.stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ViewModelStore.kt (1)
36-49: Clarify comment and keying; preferDisposableEffect(*keys)for intent clarityThe implementation matches the intended behavior (one scoped
ViewModelStorecleared on dispose / key change), but:
- Line 36 comment says “Create a fresh ViewModelStore on each new composition”, while we actually reuse a remembered store and clear it; consider rewording to avoid confusion.
- Line 45
DisposableEffect(keys)relies on the array overload;DisposableEffect(*keys)makes it obvious that recomposition/disposal is keyed off the individual values rather than the array instance itself.This is non-breaking but improves readability and future maintainability.
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionViewResultDialog.kt (1)
260-266: Consider using@StreamPreviewhelper for consistency.As per coding guidelines, Compose previews in the compose module should use
@StreamPreviewhelpers instead of the standard@Previewannotation.🔎 Proposed fix
-@Preview(showBackground = true) +@StreamPreview @Composable private fun PollOptionResultsLoadingPreview() { ChatTheme { PollOptionResultsLoading() } }Based on coding guidelines, Compose previews should use
@StreamPreviewhelpers.stream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewControllerTest.kt (1)
301-315: Potential null return when bothresultanderrorare null.If
givenQueryPollVotesResultis called without providing eitherresultorerror, the mock will returnnull, which could cause unexpected behavior in tests. Consider requiring at least one parameter.🔎 Proposed fix
fun givenQueryPollVotesResult( next: String? = null, result: QueryPollVotesResult? = null, error: Error? = null, ) = apply { + require(result != null || error != null) { "Either result or error must be provided" } whenever( chatClient.queryPollVotes( pollId = poll.id, filter = null, limit = 25, next = next, sort = QuerySortByField.descByName("created_at"), ), - ) doAnswer { result?.asCall() ?: error?.asCall() } + ) doAnswer { result?.asCall() ?: error!!.asCall() } }stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewController.kt (1)
43-49: Unused logger and potentially unnecessary@OptIn.The
loggeris defined but never used in the current implementation. Additionally,@OptIn(ExperimentalCoroutinesApi::class)may not be needed since no experimental coroutine APIs appear to be used in this file.🔎 Proposed fix
-@OptIn(ExperimentalCoroutinesApi::class) @InternalStreamChatApi public class PollResultsViewController( private val poll: Poll, ) { - - private val logger by taggedLogger("Chat:PollResultsViewController") private val _state = MutableStateFlow(stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.kt (1)
134-170: Consider displaying loading state for better UX.The
Contentcomposable receivesPollResultsViewStatewhich likely includes loading flags (as seen inPollOptionResultsViewStatewhich hasisLoadingandisLoadingMore), but no loading indicator is shown.While the PR description mentions "the idea was not to show only the loading UI and block the user, since we already have an initial poll object," consider showing a subtle loading indicator (e.g., a small spinner in the header or footer) during pagination to provide feedback, especially if the initial data might be stale or incomplete.
stream-chat-android-compose/api/stream-chat-android-compose.api (1)
1963-1971: Lambda arity change on PollViewResultDialog singleton looks safe but is an API change
lambda-1and its getter now exposeFunction2instead ofFunction3. This is expected if the underlyingPollViewResultDialogcomposable’s default lambda signature changed (e.g., fewer parameters), and it matches how other ComposableSingletons evolve, but it is still a public API change.If you treat these singleton lambdas as internal implementation details only, this is fine; otherwise just confirm you’re OK with the binary/source compatibility impact for anyone referencing them directly.
stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/internal/poll/PollResultsDialogFragment.kt (1)
115-129: Recreating adapter on every state update is inefficient.
ResultsAdapteris instantiated on every state emission (line 119), which rebuilds the entire list and may cause scroll position loss and performance issues. Consider either:
- Using
ListAdapterwithDiffUtilfor efficient updates- Moving adapter creation outside the observer and only updating its data
🔎 Suggested approach
// Create adapter once, then update data private var resultsAdapter: ResultsAdapter? = null private fun observeState() { viewModel.state.observe(viewLifecycleOwner) { state -> val poll = state.poll binding.question.text = poll.name // Update existing adapter or create if needed resultsAdapter?.updateData(poll, state.winner) ?: ResultsAdapter(poll, state.winner).also { resultsAdapter = it binding.optionList.adapter = it } // ... rest of the code } }This would require adding an
updateDatamethod toResultsAdapterand usingnotifyDataSetChanged()orDiffUtil.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (6)
stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollViewResultDialogTest_content_in_dark_mode.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollViewResultDialogTest_content_in_light_mode.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollViewResultDialogTest_loading_in_dark_mode.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollViewResultDialogTest_loading_in_light_mode.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollViewResultDialogTest_loading_more_in_dark_mode.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollViewResultDialogTest_loading_more_in_light_mode.pngis excluded by!**/*.png
📒 Files selected for processing (27)
stream-chat-android-compose/api/stream-chat-android-compose.apistream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/chats/ChatsScreen.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionViewResultDialog.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ViewModelStore.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/PollOptionResultsViewModel.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/PollResultsViewModel.ktstream-chat-android-compose/src/main/res/values/strings.xmlstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialogTest.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.ktstream-chat-android-ui-common/api/stream-chat-android-ui-common.apistream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionResultsViewAction.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionResultsViewController.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionResultsViewEvent.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewAction.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewController.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewEvent.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/state/messages/poll/PollOptionResultsViewState.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/state/messages/poll/PollResultsViewState.ktstream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewControllerTest.ktstream-chat-android-ui-components/api/stream-chat-android-ui-components.apistream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/internal/poll/PollResultsDialogFragment.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/PollResultsViewModel.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/widgets/NestedScrollViewPaginationHelper.ktstream-chat-android-ui-components/src/main/res/layout/stream_ui_fragment_poll_results.xmlstream-chat-android-ui-components/src/main/res/layout/stream_ui_item_poll_answer.xmlstream-chat-android-ui-components/src/main/res/values/strings.xml
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{kt,kts}
📄 CodeRabbit inference engine (AGENTS.md)
Format and apply Kotlin style with Spotless (4 spaces, no wildcard imports, licence headers)
Files:
stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewAction.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionResultsViewAction.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialogTest.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/PollOptionResultsViewModel.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/state/messages/poll/PollOptionResultsViewState.ktstream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewControllerTest.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewEvent.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewController.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionResultsViewEvent.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/chats/ChatsScreen.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/PollResultsViewModel.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionViewResultDialog.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionResultsViewController.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ViewModelStore.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/state/messages/poll/PollResultsViewState.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/widgets/NestedScrollViewPaginationHelper.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/internal/poll/PollResultsDialogFragment.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/PollResultsViewModel.kt
**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
**/*.kt: Use@OptInannotations explicitly; avoid suppressions unless documented
Document public APIs with KDoc, including thread expectations and state notes
Files:
stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewAction.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionResultsViewAction.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialogTest.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/PollOptionResultsViewModel.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/state/messages/poll/PollOptionResultsViewState.ktstream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewControllerTest.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewEvent.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewController.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionResultsViewEvent.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/chats/ChatsScreen.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/PollResultsViewModel.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionViewResultDialog.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionResultsViewController.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ViewModelStore.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/state/messages/poll/PollResultsViewState.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/widgets/NestedScrollViewPaginationHelper.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/internal/poll/PollResultsDialogFragment.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/PollResultsViewModel.kt
**/src/test/**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
**/src/test/**/*.kt: Use backtick test names (for example:funmessage list filters muted channels()) for readability
Use deterministic tests withrunTest+ virtual time for concurrency-sensitive logic (uploads, sync, message state)
Keep helper extensions private/internal in test files
Files:
stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialogTest.ktstream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewControllerTest.kt
**/stream-chat-android-compose/**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
**/stream-chat-android-compose/**/*.kt: Compose components should follow noun-based naming (e.g.,MessageList,ChannelListHeader)
Compose previews should use@StreamPreviewhelpers
Files:
stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialogTest.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/PollOptionResultsViewModel.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/chats/ChatsScreen.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionViewResultDialog.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ViewModelStore.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/PollResultsViewModel.kt
**/stream-chat-android-compose/**/*Test.kt
📄 CodeRabbit inference engine (AGENTS.md)
Add Paparazzi snapshots for Compose UI regressions and run
verifyPaparazziDebug
Files:
stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialogTest.kt
🧠 Learnings (6)
📚 Learning: 2025-12-17T15:00:07.506Z
Learnt from: CR
Repo: GetStream/stream-chat-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T15:00:07.506Z
Learning: Applies to **/stream-chat-android-ui-components/**/*Test.kt : Record Shot baselines when behaviour changes in XML kit UI tests
Applied to files:
stream-chat-android-ui-components/src/main/res/layout/stream_ui_item_poll_answer.xmlstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialogTest.ktstream-chat-android-ui-components/src/main/res/layout/stream_ui_fragment_poll_results.xmlstream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewControllerTest.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/chats/ChatsScreen.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/state/messages/poll/PollResultsViewState.ktstream-chat-android-ui-common/api/stream-chat-android-ui-common.apistream-chat-android-compose/api/stream-chat-android-compose.apistream-chat-android-ui-components/api/stream-chat-android-ui-components.api
📚 Learning: 2025-12-17T15:00:07.506Z
Learnt from: CR
Repo: GetStream/stream-chat-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T15:00:07.506Z
Learning: Applies to **/stream-chat-android-compose/**/*Test.kt : Add Paparazzi snapshots for Compose UI regressions and run `verifyPaparazziDebug`
Applied to files:
stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialogTest.ktstream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewControllerTest.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/chats/ChatsScreen.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ViewModelStore.ktstream-chat-android-compose/api/stream-chat-android-compose.api
📚 Learning: 2025-12-17T15:00:07.506Z
Learnt from: CR
Repo: GetStream/stream-chat-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T15:00:07.506Z
Learning: Applies to **/stream-chat-android-compose/**/*.kt : Compose previews should use `StreamPreview` helpers
Applied to files:
stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialogTest.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/PollOptionResultsViewModel.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/chats/ChatsScreen.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionViewResultDialog.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ViewModelStore.ktstream-chat-android-compose/api/stream-chat-android-compose.apistream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/PollResultsViewModel.kt
📚 Learning: 2025-12-17T15:00:07.506Z
Learnt from: CR
Repo: GetStream/stream-chat-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T15:00:07.506Z
Learning: Keep Compose and XML UI kits behaviourally aligned; update shared fixtures/tests when touching one side
Applied to files:
stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialogTest.kt
📚 Learning: 2025-12-17T15:00:07.506Z
Learnt from: CR
Repo: GetStream/stream-chat-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T15:00:07.506Z
Learning: Applies to **/src/test/**/*.kt : Use deterministic tests with `runTest` + virtual time for concurrency-sensitive logic (uploads, sync, message state)
Applied to files:
stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.ktstream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewControllerTest.kt
📚 Learning: 2025-12-17T15:00:07.506Z
Learnt from: CR
Repo: GetStream/stream-chat-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T15:00:07.506Z
Learning: Applies to **/stream-chat-android-compose/**/*.kt : Compose components should follow noun-based naming (e.g., `MessageList`, `ChannelListHeader`)
Applied to files:
stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/chats/ChatsScreen.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ViewModelStore.ktstream-chat-android-compose/api/stream-chat-android-compose.api
🧬 Code graph analysis (5)
stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.kt (1)
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ViewModelStore.kt (1)
ViewModelStore(31-54)
stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialogTest.kt (1)
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.kt (1)
PollResultsContent(309-326)
stream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewControllerTest.kt (1)
stream-chat-android-core/src/testFixtures/kotlin/io/getstream/chat/android/Mother.kt (3)
randomPoll(865-907)randomPollVote(927-941)randomString(94-98)
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.kt (2)
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ViewModelStore.kt (1)
ViewModelStore(31-54)stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionViewResultDialog.kt (3)
Content(133-215)PollOptionViewResultDialog(85-131)PollVoteItem(217-258)
stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/internal/poll/PollResultsDialogFragment.kt (1)
stream-chat-android-docs/src/main/kotlin/io/getstream/chat/docs/kotlin/ui/Overview.kt (1)
viewModels(20-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: compare-sdk-sizes / Compare SDK sizes
- GitHub Check: base-android-ci / Build
- GitHub Check: Build / compose apks
- GitHub Check: base-android-ci / Run unit tests
- GitHub Check: base-android-ci / Run static checks
- GitHub Check: Detekt
🔇 Additional comments (37)
stream-chat-android-ui-components/src/main/res/layout/stream_ui_item_poll_answer.xml (1)
68-68: LGTM! Consistent avatar styling.The border removal is consistently applied to both vote preview avatars, aligning with the UI polish described in the PR.
Based on learnings, please verify that Record Shot baselines have been updated for this visual change in the XML UI components.
Also applies to: 78-78
stream-chat-android-ui-components/src/main/res/values/strings.xml (1)
203-203: LGTM! Error message string is well-written and appropriately placed.The new error string resource follows the established naming convention, is grammatically correct, and provides clear, user-friendly feedback with an actionable suggestion to try again later. The placement within the Poll section is logical.
stream-chat-android-ui-components/src/main/res/layout/stream_ui_fragment_poll_results.xml (1)
37-37: LGTM!Adding the ID to the NestedScrollView is necessary to enable pagination functionality via
NestedScrollViewPaginationHelper.stream-chat-android-compose/src/main/res/values/strings.xml (1)
226-227: LGTM! String resources follow best practices.Both new string resources are well-structured:
- Proper naming convention following
stream_compose_poll_*pattern- Appropriately marked as translatable for user-facing text
- Logically placed within the Poll section
- Error message is clear and user-friendly
stream-chat-android-ui-components/api/stream-chat-android-ui-components.api (1)
3445-3453: Verify new PollResultsDialogFragment lifecycle overrides are symmetrical and leak‑freeThe added
onCreate(Bundle)/onDestroy()hooks are reasonable for wiring the new poll results controller / ViewModel, but please double‑check that:
- Both methods delegate to their respective
super.*implementations.- Any registration into the static poll map / controller (for reference counting across config changes) is paired with a single, symmetric cleanup in
onDestroyand not duplicated withonDestroyView.- If these changes altered the XML poll results dialog visuals, Shot baselines for XML kit UI tests have been updated accordingly. Based on learnings, …
stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.kt (1)
22-58: Good reuse of sharedViewModelStoreutility in testsWrapping
PickerTabContentwith the sharedViewModelStorehelper keeps the test setup consistent with production composition and avoids duplicating local lifecycle scaffolding. No issues spotted.stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/state/messages/poll/PollOptionResultsViewState.kt (1)
23-46: Poll option results state shape looks consistent and well‑documentedThe state model cleanly separates total
voteCountfrom the paginatedresultslist, with explicit initial and pagination loading flags. KDoc and@InternalStreamChatApiusage look appropriate; no changes needed from this file alone.stream-chat-android-ui-common/api/stream-chat-android-ui-common.api (1)
1030-1074: New poll results API entries are consistent with the Kotlin sourcesThe newly exported types (
PollOptionResultsViewAction.LoadMoreRequested,PollOptionResultsViewEvent.LoadError,PollResultsViewAction/LoadMoreRequested,PollResultsViewEvent.LoadError, andPollResultsViewState+ResultItem) match the described responsibilities and the state/action patterns used elsewhere in the UI-common module. The API dump looks in sync with the Kotlin definitions.Also applies to: 2830-2863
stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewAction.kt (1)
19-28: Sealed poll results action fits the intended usageThe
PollResultsViewActionsealed interface with a singleLoadMoreRequestedobject is a clear and extensible way to model user intents from the poll results UI. Documentation and visibility look appropriate.stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialogTest.kt (1)
31-70: Expanded snapshot coverage for poll results states looks solidCovering loading, content, and “loading more” in both light and dark modes via dedicated composables aligns well with the new state-driven poll results UI and should give good regression protection in Paparazzi.
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/chats/ChatsScreen.kt (1)
64-80: ScopedViewModelStoreusage inDetailPane/InfoPanematches navigation semanticsUsing the shared
ViewModelStorehelper to wrap:
MessagesScreenkeyed by(channelId, messageId, parentMessageId)inDetailPane, andinfoContent(arguments)keyed byargumentsinInfoPaneensures that all
viewModel(...)calls within those panes share a scoped store, are recreated when the selection/arguments change, and are cleared when the pane leaves composition. This preserves the previous per-destination ViewModel lifecycles while centralizing the logic in a reusable utility.Also applies to: 739-768
stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionResultsViewAction.kt (1)
21-30: Option‑level load‑more action is minimal and appropriateThe
PollOptionResultsViewActionsealed interface with an opt‑inLoadMoreRequestedobject cleanly models the only current user intent from the option results view, and matches the broader poll results action pattern.stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewEvent.kt (1)
21-31: LGTM!Clean and well-documented sealed interface for poll results events. The KDoc properly documents the error parameter and the overall purpose.
stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/state/messages/poll/PollResultsViewState.kt (1)
31-56: LGTM!Well-structured immutable state model with comprehensive KDoc documentation. The nested
ResultItemclass cleanly encapsulates per-option data including the preview votes and the showAllButton flag for navigation.stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionResultsViewEvent.kt (1)
25-33: Verify visibility consistency withPollResultsViewEvent.
PollOptionResultsViewEventis annotated with@InternalStreamChatApi, while the similarPollResultsViewEventin the same package is public without this annotation. If both are intended for the same level of exposure, consider aligning them. If this is intentional (e.g., one is consumed by public ViewModel, the other is internal), the current design is fine.stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionViewResultDialog.kt (2)
217-258: LGTM on vote item rendering.The
PollVoteItemcomposable properly handles the nullable user with an early return, and cleanly displays the avatar, name, and timestamp information.
104-130: LGTM on ViewModel setup and event handling.The ViewModel is properly scoped within
ViewModelStore, and theLaunchedEffectcorrectly collects events and displays Toast messages for load errors. Good use ofcollectLatestto ensure event handling.stream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewControllerTest.kt (2)
42-89: LGTM on initialization tests.Good coverage of both non-anonymous and anonymous poll initialization scenarios, verifying loading states, option sorting, and winner detection. Test names follow the backtick convention per coding guidelines.
140-181: LGTM on pagination tests.The
load more succeedstest properly verifies the pagination flow: initial load → loading more indicator → appended results → final state with no more pages. Good use of Turbine'stestblock for sequential state verification.stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/PollOptionResultsViewModel.kt (1)
37-66: LGTM on ViewModel structure.Clean delegation pattern to the controller. The lazy initialization with
controllerProviderenables testability while keeping the default production behavior. Thestateandeventsproperties will trigger the lazy initialization when accessed, which is the intended behavior.stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewController.kt (1)
51-73: LGTM on state initialization.Clean initialization logic that sorts options by vote count, determines winner, and builds
ResultItementries with vote previews capped atMAX_VOTES_TO_SHOW. TheshowAllButtonflag is correctly set whenvoteCount > MAX_VOTES_TO_SHOW.stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionResultsViewController.kt (3)
100-109: LGTM on reactive loading flow.Good use of
onStartto trigger initial load andflatMapLatestto handle concurrent requests. The guards inloadMore()prevent duplicate requests, and the flow properly handles both initial load and pagination scenarios.
164-184: LGTM on loadMore guards.Proper defensive checks: prevents load-more during initial loading, when no more pages exist, or when already loading more. The logging provides good debugging information.
134-151: LGTM on success handling.Correctly distinguishes between initial load (resets results) and pagination (appends results) based on the
isLoadingflag. ThenextPagetoken is properly stored for subsequent pagination requests.stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/widgets/NestedScrollViewPaginationHelper.kt (1)
30-91: LGTM! Well-structured pagination helper with proper safeguards.The implementation includes:
- Validation of non-negative threshold in the init block
- Double-check of
paginationEnabledflag (lines 42 and 53) to prevent race conditions- Correct use of
post()to invoke the listener on the view thread- Proper density conversion for the threshold calculation
The fallback to
0for missing children (line 46) is appropriate, as it prevents false pagination triggers.stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/PollResultsViewModel.kt (1)
37-64: LGTM! Proper ViewModel implementation with correct scope management.The ViewModel correctly:
- Passes
viewModelScopeto the controller (line 42) to ensure operations are properly scoped to the ViewModel lifecycle- Converts controller's Flow-based state and events to LiveData for consumption in XML/View-based UI
- Provides a standard Factory for ViewModel instantiation
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.kt (2)
87-131: LGTM! Well-structured modal dialog with proper state management.The refactor to
ModalBottomSheetwithViewModelStoreandAnimatedContentprovides:
- Proper window insets handling via system bars padding
- Smooth transitions between overview and detail views
- Isolated ViewModel scope that's automatically cleared on disposal
- Correct back navigation handling
230-273: LGTM! Consistent vote item implementation.The
PollVoteItemimplementation matches the pattern used inPollOptionViewResultDialogwith proper full-width layout, stable alignment, and complete user information display.stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/PollResultsViewModel.kt (1)
31-38: The controller does not require a scope parameter.
PollResultsViewControllerhas been refactored and now only takes apollparameter. It performs no async operations—it only processes existing poll data to initialize state flows. The Compose implementation is correct; no lifecycle issues will occur.Likely an incorrect or invalid review comment.
stream-chat-android-compose/api/stream-chat-android-compose.api (3)
1948-1961: New PollOption dialog singleton aligns with existing Compose patterns
ComposableSingletons$PollOptionViewResultDialogKtmirrors otherComposableSingletons$…Kthelpers (INSTANCE + lambda fields) and follows the established naming/arity conventions. No API or consistency concerns from this addition.
5138-5145: PollOptionResultsViewModel API shape is consistent with other controller-backed VMsConstructor
(Poll, Option, Function1)plusgetState(),getEvents(), andonViewAction(PollOptionResultsViewAction)matches the pattern used by other Compose viewmodels that wrap ui-common controllers (e.g., channel/pinned/mentions). Public surface looks appropriate for the new paginated poll-option results use case.
5147-5152: Confirm intended minimal surface for PollResultsViewModel
PollResultsViewModelexposes onlygetState()and noeventsoronViewAction, unlikePollOptionResultsViewModel. If poll-level results are meant to be read-only with all interaction routed via other components (e.g., option-level dialogs or higher-level controllers), this minimal API is fine; just confirm that you don’t expect to handle view actions (like manual retry/load-more) through this VM in the near term.stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/internal/poll/PollResultsDialogFragment.kt (5)
58-68: Potential crash ifpollIdis accessed before arguments are set.The
pollIdproperty usesrequireArguments()which throws if called beforeonCreate. SincepollislazyandviewModelusesby viewModels, they defer initialization until first access. This is safe as long as nothing accesses these beforeonCreate. The current code appears safe, but consider adding a comment noting the lifecycle dependency.
131-142: LGTM!Error handling is appropriate - showing a user-friendly toast and disabling pagination to prevent repeated failures.
144-154: LGTM!Lifecycle cleanup is properly structured: view-scoped resources cleaned in
onDestroyView, poll reference decremented inonDestroy. This correctly handles configuration changes.
193-205: LGTM!Good refactoring: winner calculation moved to ViewModel, and vote filtering now properly excludes null users at the data mapping level.
255-261: LGTM!The null check is defensive since votes are already filtered in line 202, but it's harmless and provides an extra safety layer.
...e/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.kt
Show resolved
Hide resolved
...o/getstream/chat/android/ui/feature/messages/list/internal/poll/PollResultsDialogFragment.kt
Outdated
Show resolved
Hide resolved
0003a30 to
5c433e1
Compare
...main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionViewResultDialog.kt
Outdated
Show resolved
Hide resolved
...main/java/io/getstream/chat/android/compose/viewmodel/messages/PollOptionResultsViewModel.kt
Outdated
Show resolved
Hide resolved
… for displaying poll results. It handles fetching poll votes with pagination. The following new classes have been added to support this controller: - `PollResultsViewState` to represent the different states of the view (Loading, Content, Error). - `PollResultsViewAction` for user actions like requesting to load more votes. - `PollResultsViewEvent` for events from the controller, such as load errors. Unit tests for `PollResultsViewController` have also been added to ensure its correctness.
…g ModalBottomSheet, which provides similar and improved animation, without the padding issue.
The `PollResultsViewState` is simplified from a sealed interface with `Loading`, `Content`, and `Error` states to a single data class. This change streamlines state management by using boolean flags like `isLoading` and `isLoadingMore`. Error handling is now managed through a separate event channel, and the query limit for fetching poll votes has been increased.
The `PollResultsViewState` is simplified from a sealed interface with `Loading`, `Content`, and `Error` states to a single data class. This change streamlines state management by using boolean flags like `isLoading` and `isLoadingMore`. Error handling is now managed through a separate event channel, and the query limit for fetching poll votes has been increased.
… to the PollResultsViewController
…ogic in the remaining previews.
This change refactors how the `Poll` object is passed to the `PollResultsDialogFragment` to prevent crashes during process recreation. The `Poll` object is now stored in a static map within the companion object, and its ID is passed through the fragment's arguments. Reference counting is used to manage the lifecycle of the poll object in the map, ensuring it's cleared when no longer needed. This avoids serializing the large `Poll` object directly into the arguments bundle.
5c433e1 to
5c7899b
Compare
The `Option` class was not saveable, causing a crash when viewing poll results after a configuration change. This is fixed by adding a custom `Saver` for the nullable `Option` state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
🤖 Fix all issues with AI agents
In `@stream-chat-android-compose/api/stream-chat-android-compose.api`:
- Around line 1948-1963: The public API is leaking compiler-generated classes
like ComposableSingletons$PollOptionVotesDialogKt and
ComposableSingletons$ModeratedMessageDialogKt for internal composables
(PollOptionVotesDialog, ModeratedMessageDialog); update the metalava publishing
configuration to exclude/kick these generated singleton classes (e.g. filter
class names matching ComposableSingletons$*) so only intentional public
composables (like PollViewResultDialog) and their generated artifacts are
published; ensure the rule targets the ComposableSingletons$* pattern and verify
the public API after republishing.
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.kt`:
- Around line 304-318: NullableOptionSaver currently only saves id and text,
losing Option.extraData across configuration changes; update the save lambda for
NullableOptionSaver to also putSerializable("extraData",
HashMap(option.extraData)) and update the restore lambda to read
bundle.getSerializable("extraData") as? Map<String, Any> (falling back to
emptyMap()) and pass that into Option(id = id, text = text, extraData =
extraData) so all Option properties are preserved.
In
`@stream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewControllerTest.kt`:
- Around line 75-91: Rename the misleading test method in
PollResultsViewControllerTest: change the function named `when multiple options
have same vote count, should mark all as winners` to a name that reflects the
actual assertion (e.g., `when multiple options have same vote count, should not
mark any as winner` or
`whenMultipleOptionsHaveSameVoteCount_shouldNotMarkAnyAsWinner`) and update any
references; leave the test body and the assertion `assertFalse(state.results.any
{ it.isWinner })` unchanged.
In
`@stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/internal/poll/PollResultsDialogFragment.kt`:
- Around line 214-220: The bind(Vote) currently returns early when vote.user is
null, leaving recycled views showing previous data; update bind(vote: Vote) to
handle a null user by explicitly clearing the UI before returning: set
binding.name.text = "" (or null), call binding.userAvatarView.setUser(null) or
its clear/placeholder method to remove the previous avatar, and clear
binding.date.text and binding.time.text (e.g., ""), then return; otherwise
proceed to set name, avatar, date and time as now.
In
`@stream-chat-android-ui-components/src/main/res/layout/stream_ui_item_poll_option_header.xml`:
- Around line 24-34: The ImageView with id "award" lacks an accessibility label;
add an android:contentDescription attribute to the @+id/award ImageView (e.g.,
android:contentDescription="@string/stream_ui_award_description") and add the
corresponding string resource (like "Winner" or "Winning option") to
strings.xml; ensure the description is localized and used instead of leaving it
empty so screen readers can announce the award icon when visible.
♻️ Duplicate comments (1)
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.kt (1)
106-132: Add error handling for consistency withPollOptionVotesDialog.The
PollOptionVotesDialogin this package observesviewModel.eventsand displays error messages via Toast whenLoadErrorevents occur. This dialog should implement similar error handling to inform users when poll results fail to load.Suggested implementation
ViewModelStore { + val context = LocalContext.current Crossfade( modifier = Modifier.fillMaxSize(), targetState = showAllOptionVotes, ) { option -> when (option) { null -> { val viewModel = viewModel { PollResultsViewModel(selectedPoll.poll) } val state by viewModel.state.collectAsState() + + LaunchedEffect(viewModel) { + viewModel.events.collectLatest { event -> + when (event) { + is PollResultsViewEvent.LoadError -> { + val errorMessage = context.getString(R.string.stream_compose_poll_view_results_error) + Toast.makeText(context, errorMessage, Toast.LENGTH_SHORT).show() + } + } + } + } + Content( state = state, onBackPressed = onBackPressed, onShowAllClick = { option -> showAllOptionVotes = option }, ) }You'll also need to add the imports:
import android.widget.Toast import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.platform.LocalContext import io.getstream.chat.android.ui.common.feature.messages.poll.PollResultsViewEvent import kotlinx.coroutines.flow.collectLatest
🧹 Nitpick comments (15)
stream-chat-android-ui-components/src/main/res/layout/stream_ui_item_result.xml (1)
73-89: Touch feedback may be lost due to background override.Setting
android:background="@android:color/transparent"explicitly will override the ripple drawable typically provided byborderlessButtonStyle, potentially removing the visual touch feedback.Consider removing the explicit background and relying solely on the borderless style, or use a
?attr/selectableItemBackgroundBorderlessto preserve touch feedback:Option 1: Remove explicit background (preferred)
<androidx.appcompat.widget.AppCompatButton android:id="@+id/showAll" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="@dimen/stream_ui_spacing_small" - android:background="@android:color/transparent" android:paddingHorizontal="@dimen/stream_ui_spacing_small" android:text="@string/stream_ui_poll_show_all_votes" android:textAllCaps="false" android:textAppearance="@style/StreamUiTextAppearance.Body" android:textColor="@color/stream_ui_accent_blue" android:visibility="gone" style="?attr/borderlessButtonStyle"Option 2: Use selectableItemBackgroundBorderless for ripple
<androidx.appcompat.widget.AppCompatButton android:id="@+id/showAll" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="@dimen/stream_ui_spacing_small" - android:background="@android:color/transparent" + android:background="?attr/selectableItemBackgroundBorderless" android:paddingHorizontal="@dimen/stream_ui_spacing_small"stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/widgets/NestedScrollViewPaginationHelper.kt (1)
41-58: Consider adding a loading guard to prevent duplicate pagination calls.The scroll listener can fire multiple times rapidly while the user scrolls near the bottom. Even with the
paginationEnabledcheck insidepost, multiple scroll events can queue up posts before the caller has a chance to calldisablePagination(). This could result in multipleloadMoreListenerinvocations for the same page.Consider adding an internal loading flag that's set when the listener fires and requires explicit reset, or document that callers must synchronously disable pagination within the callback.
♻️ Option 1: Add internal loading guard
internal class NestedScrollViewPaginationHelper( private val loadMoreThresholdDp: Int = DEFAULT_LOAD_MORE_THRESHOLD_DP, private val loadMoreListener: () -> Unit, private val resources: Resources, ) { init { require(loadMoreThresholdDp >= 0) { "Load more threshold must not be negative" } } private var paginationEnabled: Boolean = false + private var isLoading: Boolean = false private val scrollChangeListener = NestedScrollView.OnScrollChangeListener { nestedScrollView, _, scrollY, _, _ -> - if (!paginationEnabled) { + if (!paginationEnabled || isLoading) { return@OnScrollChangeListener } val totalHeight = nestedScrollView.getChildAt(0)?.height ?: 0 val scrollViewHeight = nestedScrollView.height val scrollPosition = scrollY + scrollViewHeight val threshold = loadMoreThresholdDp * resources.displayMetrics.density if (totalHeight > 0 && (totalHeight - scrollPosition) < threshold) { + isLoading = true nestedScrollView.post { if (paginationEnabled) { loadMoreListener() } } } } + /** + * Resets the loading state after pagination completes. + * Must be called after [loadMoreListener] finishes loading. + */ + fun onLoadingComplete() { + isLoading = false + }♻️ Option 2: Document caller responsibility
/** * Helper class for handling pagination with [NestedScrollView]. * Triggers load more when the user scrolls near the bottom of the scroll view. * + * **Important:** The [loadMoreListener] may be called multiple times in rapid succession + * if scrolling continues near the threshold. Callers should call [disablePagination] + * immediately within [loadMoreListener] to prevent duplicate loads, and re-enable + * pagination after loading completes. + * * `@param` loadMoreThresholdDp The distance in dp from the bottom where pagination should be triggered. Default is 200dp. * `@param` loadMoreListener The handler which is called when pagination should be triggered. * `@param` resources The resources used to convert dp to pixels. */stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ViewModelStore.kt (1)
31-54: LGTM!The utility correctly provides a scoped
ViewModelStorefor composable content. The store is cleared viaDisposableEffectwhen keys change or composition is disposed, which aligns with the intended behavior discussed in previous reviews.One minor documentation suggestion: consider adding
@paramKDoc tags forkeysandcontentto clarify usage expectations.stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.kt (1)
320-326: Consider using@StreamPreviewhelper for consistency.Per coding guidelines, Compose previews should use
@StreamPreviewhelpers for consistency across the codebase. Based on learnings.stream-chat-android-compose/api/stream-chat-android-compose.api (1)
1965-1974:ComposableSingletons$PollViewResultDialogKt.lambda-1type change is a (likely accidental) ABI break.Changing
lambda-1fromFunction3toFunction2is technically binary-incompatible for any consumer referencing that field (even if unlikely). Same broader point: the generated singleton classes/fields being public makes harmless internal refactors look like API breaks.If you keep these in the public surface, ensure your release notes / compat expectations cover this; otherwise, prefer hiding these generated symbols from API checks/published stubs.
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionVotesDialog.kt (2)
1-15: Copyright year inconsistency.The copyright header uses 2024, but other new files in this PR (e.g.,
PollOptionVotesViewEvent.kt) use 2025. Consider updating for consistency.Suggested fix
-/* - * Copyright (c) 2014-2024 Stream.io Inc. All rights reserved. +/* + * Copyright (c) 2014-2025 Stream.io Inc. All rights reserved.
264-331: Consider using@StreamPreviewhelpers per coding guidelines.Per coding guidelines, Compose previews should use
@StreamPreviewhelpers. However, the current pattern with internal helper composables enables reuse in Paparazzi snapshot tests, which may be intentional.stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/internal/poll/PollOptionVotesDialogFragment.kt (1)
1-15: Copyright year typo: 2026 should be 2025.Suggested fix
-/* - * Copyright (c) 2014-2026 Stream.io Inc. All rights reserved. +/* + * Copyright (c) 2014-2025 Stream.io Inc. All rights reserved.stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewController.kt (1)
25-25: Unused@OptIn(ExperimentalCoroutinesApi::class)and logger.The
@OptIn(ExperimentalCoroutinesApi::class)annotation appears unnecessary as no experimental coroutines APIs are used in this controller. Additionally, theloggerproperty is declared but never used.Suggested cleanup
-import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableSharedFlow ... -@OptIn(ExperimentalCoroutinesApi::class) `@InternalStreamChatApi` public class PollResultsViewController( private val poll: Poll, ) { - - private val logger by taggedLogger("Chat:PollResultsViewController")Also applies to: 43-49
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/PollOptionVotesViewModel.kt (1)
49-59: Thelazydelegate is immediately evaluated.The
stateandeventsproperties are initialized at class construction time and accesscontroller.stateandcontroller.eventsdirectly. This forces the lazy delegate to evaluate immediately, making thelazykeyword ineffective.If deferred initialization is intended, consider delegating the properties as well:
val state: StateFlow<PollOptionVotesViewState> get() = controller.state val events: SharedFlow<PollOptionVotesViewEvent> get() = controller.eventsAlternatively, remove
lazysince the controller is created immediately anyway.stream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionVotesViewControllerTest.kt (2)
254-258: Same nested test block concern.This has the same fragility issue as the initial load failure test. The
events.testblock insidestate.testcould miss events depending on emission timing.
360-386: Test doesn't explicitly verify the filter.The comment states verification is implicit, but this test doesn't actually assert the filter parameters. If the controller uses incorrect parameters, the mock returns null (or the wrong result), causing unclear failures rather than a specific assertion error.
Consider using Mockito's
verify()to explicitly assert the call:♻️ Suggested improvement
import org.mockito.kotlin.verify `@Test` fun `when querying votes, should use correct filter for option`() = runTest { // ... existing setup ... sut.state.test { skipItems(1) awaitItem() } verify(chatClient).queryPollVotes( pollId = "poll1", filter = Filters.eq("option_id", "opt1"), limit = 25, next = null, sort = QuerySortByField.descByName("created_at"), ) }This requires exposing the
chatClientmock from theFixtureor restructuring the test.stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/state/messages/poll/PollResultsViewState.kt (1)
22-55: Public state shape looks good; consider clarifying winner/tie semantics in KDoc.
isWinneris UI-critical; adding a one-liner like “false for ties” (if that’s the intended behavior) would prevent misinterpretation by downstream consumers.stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/PollResultsViewModel.kt (1)
59-69: Avoid@Suppress("UNCHECKED_CAST")inFactory(or document why it’s acceptable here).
Guidelines ask to avoid suppressions unless documented; this can typically be implemented without suppression.Proposed fix
@@ public class Factory( private val poll: Poll, ) : ViewModelProvider.Factory { - `@Suppress`("UNCHECKED_CAST") override fun <T : ViewModel> create(modelClass: Class<T>): T { - require(modelClass == PollResultsViewModel::class.java) { + require(modelClass.isAssignableFrom(PollResultsViewModel::class.java)) { "Factory can only create instances of PollResultsViewModel" } - return PollResultsViewModel(poll) as T + return modelClass.cast(PollResultsViewModel(poll)) } } }stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/internal/poll/PollResultsDialogFragment.kt (1)
94-100: Loading UI is always turned off; ensure that’s intended.
binding.loadingContainer.isVisible = falseis unconditional (Line 98), so any “loading” state won’t be represented. If the results are always available from local poll data, consider removing the loading container entirely to avoid misleading dead UI.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (8)
stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollOptionVotesDialogTest_content_in_dark_mode.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollOptionVotesDialogTest_content_in_light_mode.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollOptionVotesDialogTest_loading_in_dark_mode.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollOptionVotesDialogTest_loading_in_light_mode.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollOptionVotesDialogTest_loading_more_in_dark_mode.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollOptionVotesDialogTest_loading_more_in_light_mode.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollViewResultDialogTest_dark_mode.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollViewResultDialogTest_light_mode.pngis excluded by!**/*.png
📒 Files selected for processing (35)
stream-chat-android-compose/api/stream-chat-android-compose.apistream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/chats/ChatsScreen.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionVotesDialog.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ViewModelStore.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/PollOptionVotesViewModel.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/PollResultsViewModel.ktstream-chat-android-compose/src/main/res/values/strings.xmlstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollOptionVotesDialogTest.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialogTest.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.ktstream-chat-android-ui-common/api/stream-chat-android-ui-common.apistream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionVotesViewAction.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionVotesViewController.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionVotesViewEvent.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewAction.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewController.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewEvent.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/state/messages/poll/PollOptionVotesViewState.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/state/messages/poll/PollResultsViewState.ktstream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionVotesViewControllerTest.ktstream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewControllerTest.ktstream-chat-android-ui-components/api/stream-chat-android-ui-components.apistream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/internal/poll/PollOptionVotesDialogFragment.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/internal/poll/PollResultsDialogFragment.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/PollOptionVotesViewModel.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/PollResultsViewModel.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/widgets/NestedScrollViewPaginationHelper.ktstream-chat-android-ui-components/src/main/res/layout/stream_ui_fragment_poll_option_votes.xmlstream-chat-android-ui-components/src/main/res/layout/stream_ui_fragment_poll_results.xmlstream-chat-android-ui-components/src/main/res/layout/stream_ui_item_poll_answer.xmlstream-chat-android-ui-components/src/main/res/layout/stream_ui_item_poll_option_header.xmlstream-chat-android-ui-components/src/main/res/layout/stream_ui_item_result.xmlstream-chat-android-ui-components/src/main/res/layout/stream_ui_item_result_user.xmlstream-chat-android-ui-components/src/main/res/values/strings.xml
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{kt,kts}
📄 CodeRabbit inference engine (AGENTS.md)
Format and apply Kotlin style with Spotless (4 spaces, no wildcard imports, licence headers)
Files:
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/chats/ChatsScreen.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/state/messages/poll/PollResultsViewState.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ViewModelStore.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewAction.ktstream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionVotesViewControllerTest.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/PollResultsViewModel.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/internal/poll/PollResultsDialogFragment.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionVotesViewController.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/PollOptionVotesViewModel.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollOptionVotesDialogTest.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/PollOptionVotesViewModel.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/internal/poll/PollOptionVotesDialogFragment.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionVotesViewAction.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewEvent.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionVotesDialog.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewController.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialogTest.ktstream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewControllerTest.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/PollResultsViewModel.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/state/messages/poll/PollOptionVotesViewState.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/widgets/NestedScrollViewPaginationHelper.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionVotesViewEvent.kt
**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
**/*.kt: Use@OptInannotations explicitly; avoid suppressions unless documented
Document public APIs with KDoc, including thread expectations and state notes
Files:
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/chats/ChatsScreen.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/state/messages/poll/PollResultsViewState.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ViewModelStore.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewAction.ktstream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionVotesViewControllerTest.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/PollResultsViewModel.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/internal/poll/PollResultsDialogFragment.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionVotesViewController.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/PollOptionVotesViewModel.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollOptionVotesDialogTest.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/PollOptionVotesViewModel.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/internal/poll/PollOptionVotesDialogFragment.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionVotesViewAction.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewEvent.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionVotesDialog.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewController.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialogTest.ktstream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewControllerTest.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/PollResultsViewModel.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/state/messages/poll/PollOptionVotesViewState.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.ktstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/widgets/NestedScrollViewPaginationHelper.ktstream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionVotesViewEvent.kt
**/stream-chat-android-compose/**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
**/stream-chat-android-compose/**/*.kt: Compose components should follow noun-based naming (e.g.,MessageList,ChannelListHeader)
Compose previews should use@StreamPreviewhelpers
Files:
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/chats/ChatsScreen.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ViewModelStore.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollOptionVotesDialogTest.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/PollOptionVotesViewModel.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionVotesDialog.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialogTest.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/PollResultsViewModel.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.kt
**/src/test/**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
**/src/test/**/*.kt: Use backtick test names (for example:funmessage list filters muted channels()) for readability
Use deterministic tests withrunTest+ virtual time for concurrency-sensitive logic (uploads, sync, message state)
Keep helper extensions private/internal in test files
Files:
stream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionVotesViewControllerTest.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollOptionVotesDialogTest.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialogTest.ktstream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewControllerTest.kt
**/stream-chat-android-compose/**/*Test.kt
📄 CodeRabbit inference engine (AGENTS.md)
Add Paparazzi snapshots for Compose UI regressions and run
verifyPaparazziDebug
Files:
stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollOptionVotesDialogTest.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialogTest.kt
🧠 Learnings (6)
📚 Learning: 2025-12-17T15:00:07.506Z
Learnt from: CR
Repo: GetStream/stream-chat-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T15:00:07.506Z
Learning: Applies to **/stream-chat-android-compose/**/*Test.kt : Add Paparazzi snapshots for Compose UI regressions and run `verifyPaparazziDebug`
Applied to files:
stream-chat-android-compose/src/main/res/values/strings.xmlstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/chats/ChatsScreen.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ViewModelStore.ktstream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionVotesViewControllerTest.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollOptionVotesDialogTest.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialogTest.ktstream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewControllerTest.ktstream-chat-android-compose/api/stream-chat-android-compose.api
📚 Learning: 2025-12-17T15:00:07.506Z
Learnt from: CR
Repo: GetStream/stream-chat-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T15:00:07.506Z
Learning: Applies to **/stream-chat-android-compose/**/*.kt : Compose previews should use `StreamPreview` helpers
Applied to files:
stream-chat-android-compose/src/main/res/values/strings.xmlstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/chats/ChatsScreen.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ViewModelStore.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollOptionVotesDialogTest.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/PollOptionVotesViewModel.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialogTest.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/PollResultsViewModel.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.ktstream-chat-android-compose/api/stream-chat-android-compose.api
📚 Learning: 2025-12-17T15:00:07.506Z
Learnt from: CR
Repo: GetStream/stream-chat-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T15:00:07.506Z
Learning: Applies to **/stream-chat-android-compose/**/*.kt : Compose components should follow noun-based naming (e.g., `MessageList`, `ChannelListHeader`)
Applied to files:
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/chats/ChatsScreen.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.ktstream-chat-android-compose/api/stream-chat-android-compose.api
📚 Learning: 2025-12-17T15:00:07.506Z
Learnt from: CR
Repo: GetStream/stream-chat-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T15:00:07.506Z
Learning: Applies to **/stream-chat-android-ui-components/**/*Test.kt : Record Shot baselines when behaviour changes in XML kit UI tests
Applied to files:
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/chats/ChatsScreen.ktstream-chat-android-ui-components/src/main/res/layout/stream_ui_item_poll_option_header.xmlstream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionVotesViewControllerTest.ktstream-chat-android-ui-components/src/main/res/layout/stream_ui_fragment_poll_option_votes.xmlstream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/internal/poll/PollResultsDialogFragment.ktstream-chat-android-ui-components/src/main/res/layout/stream_ui_item_result_user.xmlstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollOptionVotesDialogTest.ktstream-chat-android-ui-components/src/main/res/layout/stream_ui_item_result.xmlstream-chat-android-ui-components/src/main/res/layout/stream_ui_item_poll_answer.xmlstream-chat-android-ui-components/src/main/res/layout/stream_ui_fragment_poll_results.xmlstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.ktstream-chat-android-ui-components/api/stream-chat-android-ui-components.apistream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialogTest.ktstream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewControllerTest.ktstream-chat-android-ui-common/api/stream-chat-android-ui-common.api
📚 Learning: 2025-12-17T15:00:07.506Z
Learnt from: CR
Repo: GetStream/stream-chat-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T15:00:07.506Z
Learning: Applies to **/src/test/**/*.kt : Use deterministic tests with `runTest` + virtual time for concurrency-sensitive logic (uploads, sync, message state)
Applied to files:
stream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionVotesViewControllerTest.ktstream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewControllerTest.kt
📚 Learning: 2025-12-17T15:00:07.506Z
Learnt from: CR
Repo: GetStream/stream-chat-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T15:00:07.506Z
Learning: Keep Compose and XML UI kits behaviourally aligned; update shared fixtures/tests when touching one side
Applied to files:
stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialogTest.kt
🧬 Code graph analysis (7)
stream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionVotesViewControllerTest.kt (1)
stream-chat-android-core/src/testFixtures/kotlin/io/getstream/chat/android/Mother.kt (3)
randomPoll(866-908)randomPollVote(928-942)randomString(95-99)
stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollOptionVotesDialogTest.kt (1)
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionVotesDialog.kt (3)
PollOptionVotesLoading(272-284)PollOptionVotesContent(294-307)PollOptionVotesLoadingMore(317-331)
stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactoryPickerTabContentTest.kt (1)
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ViewModelStore.kt (1)
ViewModelStore(31-54)
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionVotesDialog.kt (6)
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ViewModelStore.kt (1)
ViewModelStore(31-54)stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.kt (2)
Content(136-173)PollVoteItem(233-276)stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollDialogHeader.kt (1)
PollDialogHeader(39-68)stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/ContentBox.kt (1)
ContentBox(38-65)stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/LoadingIndicator.kt (1)
LoadingIndicator(32-40)stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatTheme.kt (1)
ChatTheme(320-496)
stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialogTest.kt (1)
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.kt (1)
PollResultsContent(328-345)
stream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewControllerTest.kt (1)
stream-chat-android-core/src/testFixtures/kotlin/io/getstream/chat/android/Mother.kt (2)
randomPoll(866-908)randomPollVote(928-942)
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.kt (2)
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ViewModelStore.kt (1)
ViewModelStore(31-54)stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionVotesDialog.kt (2)
Content(133-219)PollOptionVotesDialog(85-131)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Test compose (0)
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
...e/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.kt
Show resolved
Hide resolved
...n/io/getstream/chat/android/ui/common/feature/messages/poll/PollOptionVotesViewController.kt
Show resolved
Hide resolved
...n/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewControllerTest.kt
Show resolved
Hide resolved
...o/getstream/chat/android/ui/feature/messages/list/internal/poll/PollResultsDialogFragment.kt
Show resolved
Hide resolved
...o/getstream/chat/android/ui/feature/messages/list/internal/poll/PollResultsDialogFragment.kt
Show resolved
Hide resolved
stream-chat-android-ui-components/src/main/res/layout/stream_ui_item_poll_option_header.xml
Show resolved
Hide resolved
...e/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.kt
Outdated
Show resolved
Hide resolved
...in/kotlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewAction.kt
Outdated
Show resolved
Hide resolved
...otlin/io/getstream/chat/android/ui/common/feature/messages/poll/PollResultsViewController.kt
Outdated
Show resolved
Hide resolved
...tstream/chat/android/ui/feature/messages/list/internal/poll/PollOptionVotesDialogFragment.kt
Show resolved
Hide resolved
...o/getstream/chat/android/ui/feature/messages/list/internal/poll/PollResultsDialogFragment.kt
Show resolved
Hide resolved
Use `activity?` instead of `requireActivity()` to avoid a potential `IllegalStateException` in `onDestroy` when the activity is not available. Also, removes an unused logger.
Extract the `PollVoteItem` composable into its own file to improve reusability and code organization. This new component is now used in `PollOptionVotesDialog` and `PollViewResultDialog`.
This commit removes unused event and action classes related to the poll results view. The controller no longer makes API calls that could fail, so these event-handling mechanisms are no longer necessary.
|


🎯 Goal
Fix poll results dialogs showing only 10 votes when polls have more votes. The
/channels/{type}/{id}/queryendpoint returns a limited number of latest votes inlatest_votes_by_option, while the totalvote_countcan be higher.Closes https://linear.app/stream/issue/AND-953
🛠 Implementation details
PollResultsViewControllerinui-commonto manage poll results state and pagination/polls/{poll_id}/votesendpoint (page size: 25, same as iOS)PollResultsViewModelfor both Compose and XML SDKsPollViewResultDialog(Compose) andPollResultsDialogFragment(XML) to use ViewModel🎨 UI Changes
xml_before.webm
xml_after.webm
compose_before.webm
compose_after.webm
🧪 Testing
Summary by CodeRabbit
New Features
Improvements
Tests
✏️ Tip: You can customize this high-level summary in your review settings.