Repository navigation
fix: replace corrupted youtube asset - #17
Merged
Merged
Conversation
github's web IDE corrupted the asset Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
bmc08gt
added a commit
that referenced
this pull request
Sep 26, 2026
* test: sync chat-reactions fixtures from flipcash-client-orchestrator#17 * refactor(chat): name the reaction version field version, as the proto does * feat(chat): add the reaction reducer, pill order, strip, recents and catalog * feat(chat): page reactors and batch-fetch reaction summaries by id getReactors now returns the server paging token and hasMore, threaded through the controller/repo/service/api with a pageSize param, so the reactors sheet can page past the first 100. Adds getReactionSummariesByIds, chunked at 100 ids per request via the MessageIdBatch oneof, for the reaction refresh-on-paging flow. * feat(chat): merge reactions by version on message upsert * feat(chat): add reactions-only DAO writes and a JSON-level merge * feat(chat): route stream reaction updates through the reducer and persist them * feat(chat): add ReactionOperations — toggle, refresh, and page reactors * test(chat): cover ReactionsDelegate toggle, rollback, coalescing, and refresh * fix(chat): tombstone reactions a Room merge omits, require strictly newer version mergeReactionsJson previously kept a stored emoji as-is when an incoming payload (a server-fetched message copy) omitted it, leaving a stale pill once the server side had removed it. Now it tombstones the omitted emoji (count 0, self/sample reactors cleared, version kept) and only lets an incoming entry win on a strictly greater version, matching ReactionState.accept's tie-breaking. * fix(chat): seed ReactionsDelegate.snapshot from Room, not just the overlay After relaunch, a confirmed self-reaction lives only in Room's reactions_json — ChatStateHolder's in-memory overlay starts empty. A freshly-created ReactionState picked up no prior confirmed state, so the first tap on an emoji already reacted to computed an ADD instead of a REMOVE. snapshot now seeds a new state from ChatMessageDataSource before folding in the overlay. * fix(chat): add MessageReactions.from for messages outside live overlays observeChatReactions only ever covered messages with a pending tap or a confirmed overlay from the event stream/refreshReactions; a message whose reactions live only in a paged Room read had no way to become pills. Document that contract on the method and give the ViewModel a pure fallback - MessageReactions.from(summary) runs a ChatMessage's persisted reactions through ReactionState.applySummary so tombstone filtering and pill order match the live path exactly. * feat(chat): add canReact predicate for reaction eligibility * feat(chat-ui): add ReactionPillRow with N-more collapse layout * feat(chat): add ReactionRefreshPlanner for paging-driven reaction refresh * feat(chat): add ReactionPicker/Reactors ChatStep and RecentReactionsStore.stats * feat(chat-ui): add reactionPills and canReact to ContentBubble * feat(chat): add ReactionPicker/Reactors nav stubs and ToggleReaction actions * feat(chat): wire reaction pills into mappedMessages and ToggleReaction/picker/reactors events * test(chat): cover ToggleReaction's selection-clearing reducer branch * feat(chat): wire reaction pills, quick strip, and picker/reactors navigation into ChatViewModel - Attach per-message reaction pills and canReact to ContentBubble in mappedMessages, sourced from the live reaction overlay with Room fallback via MessageReactions.from. - Add ToggleReaction/OpenReactionPicker/OpenReactors events, wired through ChatCoordinator.toggleReaction and the screen's navigation to the ChatStep.ReactionPicker/Reactors stubs. - Surface reactionErrors as a snackbar via BottomBarManager.showError. - Compose the quick strip's entries from RecentReactionsStore + EmojiCatalog + EmojiDrawability whenever the selected, reactable message changes; record a used emoji on add (not on remove). - Render ReactionPillRow under each bubble and QuickReactionStrip above a selected, reactable bubble in MessageRow/MessageList. * feat(chat): wire reaction refresh hooks into the transcript's paging lifecycle - Add ChatAction.RefreshReactionIds and ChatViewModel.Event.RefreshReactionIds, forwarding batches to ChatCoordinator.refreshReactions. - In MessageList, refresh the newest 60 loaded ids on open and on every screen-lifecycle resume (repeatOnLifecycle(RESUMED), mirroring ChatViewModel's own repeatOnLifecycle(STARTED) pattern), and refresh Room-sourced pages in ~40-id chunks as they page in, skipping pages the RemoteMediator just fetched (loadState.mediator?.append is Loading). * fix(chat): record reaction recents once, at tap time The ViewModel recorded on tap using only the live overlay, and ReactionsDelegate recorded again on a successful add, so each add counted twice and a take-back of a reaction stored only in Room counted as a use. Record in the delegate when the tap is made, against the Room-seeded state, as iOS ConversationReactions.toggle does. * fix(chat): show reaction errors as one-line toasts with the iOS copy iOS shows "Couldn't add reaction" and "This message has the maximum number of reactions" as a single-line toast. Drop the bottom-bar error and its invented second lines. * feat(emojis): add EmojiPickerModel search/section grouping Mirrors iOS's EmojiPickerModel: Frequently Used row (deduped, order preserved) then one section per catalog category, or a single search-results section when the query is non-empty. Undrawable entries are filtered from every section. * feat(chat): default getReactorsPage to a 50-row page REACTORS_PAGE_SIZE matches the reactors sheet's own page size (decision 4) rather than QueryOptions' general-purpose default of 100. * feat(chat): merge reactors sheet pages into one row per person * feat(chat): add reactor name resolution and reactors/picker sheet detents ReactorNameResolver is a pure precedence function over self, the cached UserProfile, the chat's member roster, and a requested sender-profile fetch, mirroring the sources ChatViewModel.memberParticipant already reads. ChatStep.ReactionPicker/Reactors now also implement HalfSheet, matching iOS's .medium/.large detents for both sheets. * feat(chat): add ReactorsPrefetchCache for OpenReactors-triggered loading One ReactorsListModel per messageId, started on OpenReactors (a pill long-press) rather than the sheet's own composition, so the fetch is already in flight by the time the sheet reads rows/loading for that id. * feat(chat): start reactors fetch on OpenReactors, resolve reactor names ChatViewModel now owns a ReactorsPrefetchCache keyed by messageId and starts it in the OpenReactors handler, one step before the navigator pushes the sheet. reactorName()/reactorParticipant() combine the cached-profile store, chat members, and sender profiles per decision 4's precedence, requesting a server fetch when none of them has an answer. * feat(chat): resolve reactor avatar alongside name reactorDisplay() replaces reactorName() with a small ReactorDisplay(name, profile) pair, so the reactors sheet's row can draw an avatar from the same resolved source rather than a second lookup. Self resolves to the viewer's own profilePicture under the 'You' label. * fix(chat): key ReactorsPrefetchCache flows so subscribers survive a race A composition reading rows()/loading() before OpenReactors calls start() was capturing a placeholder StateFlow that start() could never update. Back both by one shared map-keyed StateFlow instead, so a subscription taken before start() still sees the fetch land. * feat(chat): build the reactors sheet UI * feat(chat): build the reaction picker sheet UI * feat(chat): wire the picker and reactors sheets into ChatFlowScreen * test(messenger): pass userProfileDataSource mock to ChatViewModel in analytics tests * test(emojis): run the drawability vectors on a device Robolectric's Paint has no emoji font, so the unit test could only check that EmojiDrawability ran. The instrumented test asserts each #17 drawability vector against the device font, with a verbatim copy of reactions.json in androidTest assets. * fix(emojis): run the Hilt processor so the app sees ReactionsModule * fix(emojis): keep reaction recents per account * fix(persistence): read reaction rows stored under the old sequence key * fix(chat): fade overflowing reactor emoji and open profiles from the person only * feat(messenger): match reaction picker sheet to iOS Rework ReactionPickerSheet to mirror iOS's EmojiPickerSheet (node 9768:1402): drop the reused reactors-sheet title, replace the search row with a black-.26 capsule (magnifier + Search + clear), let the 7-column grid fill the sheet as it expands, and add a floating Haze glass category bar over the bottom of the grid with a scroll-synced selection indicator. Show a spinner while the catalog loads and the existing no-results copy when a search comes back empty. Departure from iOS: no drag-across-the-bar gesture on the category bar, tap-only. * test(chat): cover ReactionPillRow and QuickReactionStrip Tap/long-press/plus wiring, the previewer's read-only contract, narrow-width N-more collapse, selection semantics, and the quick strip's scroll-to-overflow all lacked coverage. ReactionPillRow's pill tap called onToggle even when canReact was false, so a previewer's tap silently registered a reaction attempt instead of doing nothing. Gate it on canReact. Also tag pills/more/plus chips for lookup and expose selfReacted/highlighted as semantics selected, since neither existed to assert against. * fix(chat): draw no pill row until a message has a reaction, and size "+" to 28dp iOS hides the row and its "+" while a message has no reactions; Android drew a "+" under every message. PlusChip also ignored its size, so the chip shrank to the glyph. * fix(chat): place the quick reaction strip beside the bubble, sized as on iOS The strip drew inside the bubble's box shifted up 12dp, so it covered the bubble and ran to the screen edge with "+" scrolled out of view. It now sits in a popup 16dp above the bubble (below it when the selection bar is in the way), hugging the sender's side and 16dp inside the window, at iOS's 55dp height and 313dp max width, with "+" pinned outside the scrolling emoji. * fix(chat): leave selection when the strip's + opens the picker The strip stayed on screen over the picker because the message stayed selected. iOS dismisses its context menu before presenting the picker; this does the same. * fix(chat): size reaction pills and their + as on iOS Pills and the "N more" pill never applied their 28dp height, so they shrank to the text and sat shorter than "+", and their text took Material's dark content color, leaving the count unreadable on the dark fill. They now match iOS: 28dp tall, a 15sp emoji and a 13sp medium count in textMain, 4dp apart. "+" uses the strip's add-reaction icon on a white-18% circle, and both "+" buttons are labelled "Add reaction" for TalkBack. * fix(messenger): slide the picker's category indicator and let it drag, as on iOS * fix(messenger): keep the picker's category bar on screen once the sheet can expand * fix(messenger): move the picker's category bar in step with the sheet The bar's lift came from onGloballyPositioned, which only sees the sheet's new position after layout, so the bar trailed the sheet by a frame and jumped while it was dragged. The sheet host now exposes LocalSheetOverhang, read from the sheet state's drag offset, and the bar reads it at placement so both move in the same frame. * fix(chat): grow the quick reaction strip in as iOS does The strip popped in at full size in its own popup window, a beat out of step with the bubble's raise. It now enters as the iOS strip does: the capsule widens out of the side it hugs with "+" on its growing edge, and the emoji scale in one after another, settling in about 200ms. Everything animates in the draw phase, so the popup never resizes. * fix(chat): keep pills under the backdrop and enter the strip with the lift Selecting a message hid every row's pill row, so the pills vanished from the whole transcript while one bubble was raised. They now stay, blurred and dimmed with their row, and ignore touches like the rest of it. The strip was built only after the selection landed: a glyph check over the first catalog category and a DataStore read, then a second state update. The popup therefore trailed the lift. Those inputs are now kept ready from init and refreshed after each toggle, and the strip is built as the message is selected and carried in on the same event. * fix(chat): place the reaction strip below the measured top bar * test(reactions): read reactions.json from test-vectors instead of three copies * fix(chat): line the reaction strip up with the bubble, not its row * fix(messenger): lift only the bubble, leaving its reaction pills behind the backdrop The lift scaled the whole row, so a selected message's pills rose with its bubble. iOS lifts only the bubble view; the pills stay in the cell under the backdrop. Move the scale onto the bubble and give the selected row's pills the same dim and blur as the rest of the transcript. * fix(chat): seed the stream's reaction state from Room before writing it back A reaction arriving over the event stream built its message's state from nothing, then merged it into Room as a full summary. Every stored emoji the stream hadn't mentioned this session was tombstoned at its stored version, and GetReactionSummaries at that same version can't replace it. A message with 16 emoji showed 10 after one live reaction. * fix(chat): animate reaction pills in and out, clip their ripple, and duck the picker's category bar Pills scale and fade in and out, the row's height springs as pills come and go, and each chip is clipped before its click handling so the ripple stays in the capsule. The picker's category bar stops lifting at the top of the grid, so collapsing the sheet takes the bar down with it instead of lifting it over the search field. ChatAnimations moves to chat-ui so the pill row can share the reaction springs.
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.
github's web IDE corrupted the asset