-
Notifications
You must be signed in to change notification settings - Fork 6
Improve send/buy store state machine to model full flow lifecycle #933
Copy link
Copy link
Open
Description
Problem
The send and buy stores (send-store.ts, buy-store.ts) only have two status states: idle and quoting. But the stores live across multiple routes (input → confirmation → transaction details), so the status doesn't represent where the user actually is in the flow.
This causes a concrete bug: when quoting completes, the store resets to idle before React Router navigation starts. The continue button flashes: loading → not loading → loading. There's a brief gap between the async quote completing and isNavigating becoming true.
Current fix (PR #925)
Added a local isContinuing boolean in the input components (send-input.tsx, buy-input.tsx). Loading is derived as:
loading={status === 'quoting' || isContinuing}This works but:
- The store status "lies" — on the confirmation page, status is
idleeven though the user is mid-flow - Back navigation leaves the store in a stale state (was
successbefore Fix send/buy continue loading state across navigation #925, now just doesn't track at all) - Boolean flags tend to proliferate as more states get added
Proposed improvement
Model the full flow lifecycle in the store status:
idle → quoting → confirmation → (success → unmount)
| State | Meaning | Where |
|---|---|---|
idle |
Ready for input | Input page |
quoting |
Creating quote (async) | Input page (loading) |
confirmation |
Quote ready, user reviewing | Confirmation page |
success |
Payment initiated, navigating to tx details | Confirmation page |
Transitions
idle → quoting: User clicks Continue,proceedWithSend()/getBuyQuote()startsquoting → confirmation: Quote created successfully. Set byuseLayoutEffectin confirmation component (after view transition completes), replacing the current pattern whereproceedWithSendsetsidleafter quotingquoting → idle: Quote fails (error toast, stay on input)confirmation → success: Payment mutation succeeds (already tracked by TanStack mutation status on confirmation page, but store could also reflect this)- On back navigation to input:
useLayoutEffecton input component resets status toidle
Key design considerations
- View transitions: We use separate routes for each step because of view transition ergonomics. The store must bridge the gap between async operations completing and navigation finishing.
isNavigatinggap: React Router'sisNavigatingdoesn't becometrueimmediately when navigation is triggered programmatically — there's a tick between mutation completion and navigation start.- Store unmounts on completion: When navigating to
/transactions/:id, the send/buy layout unmounts, destroying the store. Sosuccessis effectively terminal. - Loading derivation: With the full state machine, the continue button can derive loading from
status === 'quoting'alone. TheisContinuinglocal state and the['pending', 'success'].includes(mutationStatus)pattern on confirmation could potentially be simplified.
Scope
- Update
send-store.tsstatus type to'idle' | 'quoting' | 'confirmation' - Update
buy-store.tsstatus type to'idle' | 'quoting' | 'confirmation' - Transition to
confirmationviauseLayoutEffectin confirmation components - Transition to
idleviauseLayoutEffectin input components (handles back navigation) - Remove
isContinuinglocal state from input components - Keep
proceedWithSend/getBuyQuotesettingquotingon start, but NOT resetting toidleon success (let confirmation component handle that transition) - Consider whether
successstate is needed or if TanStack mutation status on the confirmation page is sufficient
Context
- Discussion: Discord thread between gudnuf and josip (2025-03-05)
- Related PR: Fix send/buy continue loading state across navigation #925 (interim fix with
isContinuingboolean) - The send confirmation page already uses TanStack Query mutation status for its own loading (
['pending', 'success'].includes(status)) — separate from the store - The buy checkout page has no confirm button (payment is external via Cash App)
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels