Repository navigation
feat(bills): scanning a tip card lands in the chat - #1461
Merged
Merged
Conversation
Nothing presents it: the scanned card now goes straight to the chat, and the amount it used to take is entered in the chat instead. action_swipeToTip stays for now — the chat's own swipe label still reads it, and that caller goes away with the "Start Chatting" CTA change.
The inset and vertical-bias animations existed to slide the tip card up out of the modal's way. Nothing reports a management height for a tip card now, so both animations ran to a target they already sat at. The min-tip affordability read went with them: the modal was its only consumer, and the chat does its own balance check.
The flow waited on "Swipe to Tip" and asserted "Tip Brandon McAnsh", both of which belonged to the deleted modal. It now waits on chat_screen, the id the route registration derives, so the assertion survives the chat's call-to-action copy changing.
The scanned card's dismissal used PutInWallet, whose exit is a 600ms slide back down to the wallet. The bill overlay lives inside the navigation entry being pushed away from, so the entry starts sliding out roughly 50ms later and takes the card with it — a recording of the handoff shows the card leaving at full size and full opacity, never having animated at all. Dismiss with Grabbed instead: fadeOut(100ms) + scaleOut, the same pop the card already enters with, short enough to finish inside the handoff. Call it before the push so both start on the same frame. ScannableDecoratorContext.onDismiss now takes the BillDeterminationResult, since the result is what picks the exit; PayableDecorator passes the PutInWallet it had hardcoded.
The card read "Tip Erik" over the handle. Scanning it no longer takes a tip — it opens the chat, where sending money is one of several things you can do — so the face now carries the display name alone and leaves the verb out of it. `label_tipUser` stays; the tip flow and menu still use it.
Dismissing the card and pushing the chat in the same frame removed the card instead of animating it out. A 60fps capture shows it fully opaque in one frame and gone in the next, 34ms later, where the 100ms fade-and-scale should have spanned several. A 50ms head start puts that fade under way before the push takes the outgoing entry; the same capture now shows the card half-faded and scaled up while the chat is already sliding in. The delay also has to outlive the effect it runs in. Dismissing sets the bill to null, which flips `tipPresented` and restarts `LaunchedEffect`, and that was cancelling the pending push — the chat never opened at all. `NonCancellable` around the hand-off keeps it.
The hand-off into the chat dismisses the bill ~50ms before it pushes, and the push is what eventually detaches the analyzer. For the length of the transition the camera is still on the card with every gate open: `onCodeScan`'s "a bill is up" check has just been released, `scannedRendezvous` is only ever written on the cash path, and `TipCardDelegate.inFlight` cleared when the first profile fetch returned. The card re-resolves and re-presents over the chat it just opened, and that copy of the decorator pushes a second chat behind it. Tip payloads now carry their own suppression, keyed on user id. A cooldown rather than the permanent entry cash gets, because a tip card is a person and scanning them again later is legitimate — 3s, the same window the QR analyzer already debounces the other scan route to this card by. Accepting a scan refreshes the window and a suppressed frame does not, so holding the camera on a card runs the cooldown out instead of extending it. Synchronized: MultiCodeAnalyzer decodes each frame in its own coroutine, so an unguarded check-then-set lets two frames both through.
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.
Node 10074:18893.
Scanning someone's tip card raised a modal over the card, took a tip amount, sent it, and only then opened the chat. Searching for the same person by
@handleopened their chat straight away and let the payment happen inside it. Two routes to the same person, two different flows, and only one of them could reach a conversation that hadn't started yet.Scanning now does what the search does. The card holds for 450ms — the same beat the modal used to take sliding up — and then the chat opens on
ChatIdentifier.ByUser, started or not. The fee is charged by the chat's own call to action, which is the same fee the modal was collecting.A card whose profile has no server-provided
userIdhas no chat to open, so it stays on screen rather than dismissing into nothing.The deposit gate goes with the modal: the chat is worth reaching whether or not you can afford the fee today, and the chat's send path already prompts to add money.
BillOverlay's inset and vertical-bias animations go too — they existed to slide the card up out of the modal's way, and now run to a target the card already sits at.TipUserModalis deleted. The tip-amount flow it was the only entry point to —AppRoute.Sheets.TipAmountEntry,TipAmountEntryScreen,TippingCoordinator.confirmTip,TokenPurpose.Tip— is now unreachable and comes out in a follow-up, to keep this PR about the behaviour change.action_swipeToTipstays for the same reason: the chat's own swipe label still reads it.maestro/tip_deeplink.yamlwaited on the modal's "Swipe to Tip"; it now waits onchat_screen.The card face carries the display name alone. It read
Tip Erikover the handle, and a scan no longer takes a tip.label_tipUserstays — the tip flow and the menu still use it.On the way out the card fades and scales rather than sliding back to the wallet, and the chat is pushed 50ms into that. Dismissing and pushing in the same frame removed the card instead of animating it out; a 60fps capture had it opaque in one frame and gone 34ms later. The hand-off runs
NonCancellablebecause dismissing clears the bill, which restarts the effect that owns it and was cancelling the push outright.