refactor(chat): drive the reaction sheets from ChatViewModel state - #1598
Merged
Merged
Conversation
bmc08gt
changed the base branch from
fix/reactors-sheet-refresh
to
code/cash
September 28, 2026 18:18
The reactors sheet, emoji picker and quick-reaction strip read the view model through public methods that returned flows or values, bypassing State and Event. The screen collected those flows itself, and the quick strip was built in the screen and passed back into the selection event. Each surface now has a slice of State: `reactors` (pills, rows with names and avatars resolved, loading, hasMore), `emojiPicker` (query, sections, loaded) and `quickReactionInputs`. Opening, paging, searching and dismissing go through Events, and a reactor tap sends OpenReactorProfile, answered by the ShowReactorProfile event. Message rows carry the viewer's own reactions, so selecting a bubble builds the strip in the same reduction as the selection. Picker sections for a query the user has already typed past are dropped. OpenReactors carries the long-pressed pills, so the sheet has a title before the live reaction data arrives.
The picker's search and sections don't belong to the chat, so they move off ChatViewModel into EmojiPickerViewModel, scoped to the picker's nav entry. It owns the search field's TextFieldState, debounces it and loads sections with mapLatest, which drops a load the reader has typed past. The sheet reads the field directly, so there is no separate query in state and no open or dismiss events to pair up. Choosing an emoji still dispatches ChatViewModel.Event.ToggleReaction.
EmojiPickerViewModelTest holds each section load open, finishes the "dog" search before the earlier "cat" one, and checks the late "cat" results don't replace "dog". Swapping mapLatest for flatMapMerge makes it fail with [cat face]. Robolectric's Paint reports no emoji glyphs, which empties every section, so the view model takes a GlyphProbe through an internal constructor. The @Inject constructor still passes PaintGlyphProbe.
bmc08gt
force-pushed
the
refactor/chat-reactions-mvi
branch
from
September 28, 2026 19:00
e626f23 to
fa494b8
Compare
The reactors sheet's rows, paging and pills lived on ChatViewModel, which fed them to the sheet through OpenReactors, LoadMoreReactors, ReactorsDismissed and a ShowReactorProfile round trip. ReactorsViewModel is scoped to the ChatStep.Reactors entry instead: it opens on the chat and message from the nav key, follows that message's live pills, owns the ReactorsPrefetchCache, and resolves names and avatars. The sheet pushes a reactor's profile itself. ChatStep.Reactors now carries the chat id, and OpenReactors drops the pills it carried for the old sheet title.
One test adds a reaction under the open sheet and checks the new reactor's row appears. The other checks an unnamed reactor's profile is requested once and the row picks up the name when it lands.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The reactions work added public methods on
ChatViewModelthat the screen called directly (reactorsRows,reactorsLoading,reactorsHasMore,loadMoreReactors,syncReactors,reactionPills,reactorDisplay,reactorParticipant,emojiPickerSectionsandquickReactionStripFor). The screen collected their flows itself, and the quick strip was computed in the screen and then passed back in onToggleMessageSelection. This gives the reactors sheet and the emoji picker their own view models, and moves the quick strip ontoChatViewModelState.ReactorsViewModelis scoped to theChatStep.Reactorsentry, which now carries the chat id. It follows the message's live pills (falling back to its stored reactions), owns theReactorsPrefetchCache, resolves names and avatars, and pages onLoadMore. Tapping a row pushes the profile from the sheet.ChatViewModelhas no reactors state or events left, andOpenReactorsno longer carries pills.EmojiPickerViewModelis scoped to the picker entry. It owns the searchTextFieldState, debounces it, and loads sections withmapLatest, so a load the user has typed past is dropped.ChatViewModelonly sees theToggleReactiona pick sends.selfReactions, and the strip's other inputs load intoState.quickReactionInputs, soToggleMessageSelectionbuilds the strip in the reducer from state alone.ChatReactionsReducerTesthas 6 reducer tests for the strip,ReactorsReducerTest4 for the reactors sheet,ReactorsViewModelTestchecks that a reaction added under the open sheet adds its row and that a reactor named later fills in,EmojiPickerReducerTest2 for the picker, andEmojiPickerViewModelTestchecks that a search the user typed past is dropped when it finishes late.memberParticipantis still a public method the screen calls fromMessengerScreen. It isn't reaction-specific, so it's left for a separate change.