Coming from here and also stemming from this disucssion
Background
When a user creates an expense from the global create FAB (or any flow that lands them on the Spend page), New Expensify confirms the expense was saved by navigating to Reports > Expense, finding the new row in the sorted/grouped FlashList, scrolling to it, and running a Reanimated worklet that fades its background. Producing that visible effect chains together navigation readiness, screen mount, deferred API writes, deferred heavy sort/group work, snapshot delivery, FlashList mount, and an onLayout-driven scrollToIndex — and two unrelated 300ms timers in useSearchHighlightAndScroll clear the orchestration state regardless of whether any of that has finished. Optimistic rows are placed at the bottom of page 1, then re-placed at their true sorted position once the server snapshot arrives, which routinely falls on a later page. The same logic produces meaningfully different observable behaviour on web, Android, and iOS, because every stage in the chain (navigation timing, InteractionManager, FlashList native engine, Reanimated scheduling) behaves differently per platform — see my case-by-case investigation (cases 1a/1b/2a/2b) in the issue thread.
Problem
When a user creates an expense from a flow that lands on the Spend page, if the new row is not present at its final sorted position in the rendered FlashList within ~300ms, then the row is neither scrolled to nor highlighted, leaving the user with no visual confirmation that the expense was created.
Solution
After the optimistic write is queued, fire a single toast — built on the existing GrowlNotification already mounted globally — that says “Expense created.” with a trailing View link, and remove the scroll-and-highlight choreography from Spend on every platform.
GrowlNotification is rendered at the root of the tree and animated by Reanimated on the UI thread, so its appearance has no dependency on the Spend list mounting, the snapshot loading, sort order being final, or onLayout firing within any deadline. Because the trigger fires synchronously with the optimistic write, the user sees the confirmation before the navigation pipeline finishes, and the View link reaches the new expense by transactionID directly — bypassing the “find a moving target in a paginated, re-sorting list” problem entirely. Visual treatment, position, and link styling follow the design discussion in the issue thread
Issue Owner
Current Issue Owner: @mhawryluk
Coming from here and also stemming from this disucssion
Background
When a user creates an expense from the global create FAB (or any flow that lands them on the Spend page), New Expensify confirms the expense was saved by navigating to Reports > Expense, finding the new row in the sorted/grouped FlashList, scrolling to it, and running a Reanimated worklet that fades its background. Producing that visible effect chains together navigation readiness, screen mount, deferred API writes, deferred heavy sort/group work, snapshot delivery, FlashList mount, and an onLayout-driven scrollToIndex — and two unrelated 300ms timers in useSearchHighlightAndScroll clear the orchestration state regardless of whether any of that has finished. Optimistic rows are placed at the bottom of page 1, then re-placed at their true sorted position once the server snapshot arrives, which routinely falls on a later page. The same logic produces meaningfully different observable behaviour on web, Android, and iOS, because every stage in the chain (navigation timing, InteractionManager, FlashList native engine, Reanimated scheduling) behaves differently per platform — see my case-by-case investigation (cases 1a/1b/2a/2b) in the issue thread.
Problem
When a user creates an expense from a flow that lands on the Spend page, if the new row is not present at its final sorted position in the rendered FlashList within ~300ms, then the row is neither scrolled to nor highlighted, leaving the user with no visual confirmation that the expense was created.
Solution
After the optimistic write is queued, fire a single toast — built on the existing GrowlNotification already mounted globally — that says “Expense created.” with a trailing View link, and remove the scroll-and-highlight choreography from Spend on every platform.
GrowlNotification is rendered at the root of the tree and animated by Reanimated on the UI thread, so its appearance has no dependency on the Spend list mounting, the snapshot loading, sort order being final, or onLayout firing within any deadline. Because the trigger fires synchronously with the optimistic write, the user sees the confirmation before the navigation pipeline finishes, and the View link reaches the new expense by transactionID directly — bypassing the “find a moving target in a paginated, re-sorting list” problem entirely. Visual treatment, position, and link styling follow the design discussion in the issue thread
Issue Owner
Current Issue Owner: @mhawryluk