Repository navigation
feat(chat): swipe a chat list row to open the mute sheet - #1556
Merged
Merged
Conversation
A trailing swipe on a chat list row now opens the same mute sheet the DM and group profiles open. It never mutes on its own, because a mute needs a duration, and unmuting stays the sheet's "Never" row. The icon is a crossed-out bell on an audible chat and a plain bell on a muted one. The sheet was the ChatStep.MuteChat flow step. The step carried nothing and read the chat id and viewer state off the chat flow's ChatViewModel, which the chat list does not have. It is now a top-level AppRoute.Messaging.MuteChat(chatId) sheet, opened the way Report is, backed by a MuteChatViewModel that observes that chat's viewer state and calls ChatCoordinator.mute/unmute. Both profiles push the new route, and the step, its flow entry and ChatViewModel's MuteChat/UnmuteChat events are removed, so MuteChatSheet still has one caller. The sheet closes on the tap and its view model is scoped to the sheet's entry, so the request runs NonCancellable. Otherwise closing the sheet would cancel the call whose answer is written to the store and shown as the row's muted indicator. The list items are keyed by chat id, so a row's swipe state stays with its chat when new activity reorders the list. The row also exposes the action as an accessibility custom action, since a screen reader user cannot swipe.
NavBillOverlayEntryDecorator wraps every root entry in a fillMaxSize Box to draw the bill overlay. Sheets get the Box too, so a sheet marked WrapContentSheet was stretched to the full expanded height. The mute sheet showed it once it moved to a root route; the flow-level step it replaced ran under a navigator without this decorator. The Box now fills only when the entry is not a wrap-content sheet.
A full swipe on a resetOnDismiss action is rejected, and the drag then settles on the nearest allowed anchor, Revealed, so the row stayed open under whatever the action opened. The row now animates back to Settled after the callback. The flag that fires the callback was cleared when that effect finished, but the drag's own settle can interrupt it, leaving the flag set. The next full swipe then wrote the same value, nothing relaunched, and the action never fired again. The flag is now re-armed when a drag starts, which still fires once per gesture: the settle confirms Dismissed on every frame it heads there. SwipeRevealGroup is new and optional: rows given the same group keep at most one open, closing the other as soon as a drag starts on another.
The mute action uses the profile's unverified badge treatment, warning at 15% behind a warning-tinted icon. The list shares a SwipeRevealGroup so swiping one row closes another left open. The muted bell moves from the end of the preview line to right after the chat name. The name takes one line and only the width it needs, so a long name ellipsizes before it pushes the bell or the timestamp off.
The chip's label went null the moment the mute cleared, while the chip was still fading out, so the text vanished and a bare bell faded on its own. The chip now keeps its last label through the exit. rememberIsMuted also held the previous answer for a frame after an unmute: produceState re-answers after composition, so the unmute drew one frame of "muted" with no deadline. It now reads the state in the same pass and only waits on the lapse of a timed mute.
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.
A trailing swipe on a chat list row opens the mute sheet the DM and group profiles already open. It never mutes directly: a mute needs a duration, and unmuting stays the sheet's "Never" row. This matches the iOS change, where the swipe also opens the sheet rather than toggling. The icon is a crossed-out bell on an audible chat and a plain bell on a muted one, on the amber-at-15% treatment of the profile's unverified badge rather than the delete red. There are no analytics, since neither platform has mute events yet.
Why a new sheet route
The sheet was reached only through
ChatStep.MuteChat. That step carries no arguments and reads the chat id and viewer state from the chat flow'sChatViewModel, which the chat list doesn't have. Opening it from the list meant either pulling the chat flow into:features:tippingor giving the sheet its own way to know which chat it's for.The sheet is now a top-level route,
AppRoute.Messaging.MuteChat(chatId), opened the way Report is.MuteChatScreen(chatId)hosts the unchangedMuteChatSheet, backed by aMuteChatViewModelthat observes that chat's viewer state and callsChatCoordinator.mute/unmute.Both profile screens move to the new route, and
ChatStep.MuteChat, its flow entry, andChatViewModel'sMuteChat/UnmuteChatevents are removed. Keeping the step would have left two paths into one sheet, each with its own mute call and error handling. With one route,MuteChatSheetkeeps a single caller.The sheet closes on the tap, and its view model is scoped to the sheet's entry, so the mute request runs
NonCancellable. Otherwise closing the sheet would cancel the call whose result is written to the store and shown as the row's muted indicator.Other changes this needed
NavBillOverlayEntryDecoratorwrapped every root entry, sheets included, in afillMaxSizebox, which stretched aWrapContentSheetto full height. The old flow step ran under a navigator without this decorator, so nothing hit it until now. The box no longer fills for wrap-content sheets, which also applies to the invite-contact sheet.SwipeActionRow, shared with the user profile and region selection:resetOnDismissrow now animates back to closed after its action. Before, the rejected dismiss settled on Revealed and the row stayed open under the sheet.SwipeRevealGroupkeeps one row open at a time. The chat list uses it; the other callers don't pass one and behave as before.