Repository navigation
Stop Home refetching on first expense loads and quick tab returns - #103996
mjasikowski merged 5 commits into
Conversation
Opening an expense the Home cards only know from their snapshot writes its transaction to Onyx for the first time. spendDataSignature counted that as a change, so closing the RHP still refetched every card. A first-seen key now only counts when it carries a pending write, which is what a local create has. Returning to the app also refetched every card each time, so flicking between browser tabs fired a full Home refresh per return. A return within 60s of the last refresh is now skipped; tab focus and data changes still refresh at once.
|
@aimane-chnaif Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Hi @aimane-chnaif could you review it today ? |
|
yes, starting soon |
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppAndroid: mWeb ChromeiOS: HybridAppiOS: mWeb SafariMacOS: Chrome / Safaritest.movScreen.Recording.2026-10-22.at.7.58.29.AM.mov |
|
Hi @aimane-chnaif any update ? |
|
@codex review |
|
@MelvinBot review |
|
Looks good but let's get second pass from bots |
ReviewThe logic looks correct, and I found no blocking bugs. CI passes, except
What I checked
|
Regressions foundI found three regressions. None is a crash, but each leaves the Home cards stale where they used to refresh. Local creates, edits, deletes, splits, and duplicates still refresh correctly.
Also: the comment "One made elsewhere arrives here" is no longer true. See useRecentlyAddedData.ts:147. Unverified items and what I checkedPossible, not verified
Checked and safe
|
|
Codex Review: Something went wrong. Try again later by commenting “@codex review”. ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
|
@codex review |
A first-seen transaction stored by the server in the last 10 minutes now moves the spend signature, so statement imports, Pusher pushes and members' expenses refresh the Home cards again. Opening an older expense still costs no refetch. A return to the app inside the 60s window is now replayed once the window ends instead of being dropped, and a device clock that moved back no longer blocks returns.
|
Codex Review: Something went wrong. Try again later by commenting “@codex review”. ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
I addressed those in my last push |
fixed as well |
|
|
||
| // A key seen for the first time is usually a load, like opening an expense the cards only know from their | ||
| // snapshot, so only a local create or a just-stored expense counts. Recording it lets a later edit count. | ||
| if (!lastSeen && transaction && !hasPendingWrite(transaction) && !isRecentlyInserted(transaction)) { |
There was a problem hiding this comment.
Pls. check if valid bug:
"Awaiting approval" doesn't update when an older expense is edited on another device
The Awaiting approval refresh relies on the expense counter to catch edits on outstanding reports (useYourSpendData.ts#L615-L616). An edit from another device reaches this device through Pusher. If this device never loaded that expense and it was created more than 10 minutes ago, the new first-seen rule records it without bumping the counter (spendDataSignature.ts#L114-L119). On main, the counter bumps and the total refreshes.
- Use an account on a paid workspace (Collect or Control) with approvals on. Have a phone and web ready.
- On the phone, create a $10.00 expense on that workspace and submit the report.
- Wait at least 10 minutes. This moves the expense's
insertedtime outside the new 10-minute window. - On web, sign in fresh so web has never loaded that expense. Do not open the report.
- On web, open Home and note Your spend → Awaiting approval.
- On the phone, change that expense's amount to $25.00.
- On web, stay on Home for about 10 seconds.
Expected: Awaiting approval goes up by $15.00.
Actual: Awaiting approval keeps the old total until you switch tabs and come back.
There was a problem hiding this comment.
Pls. check if valid bug:
"Awaiting approval" doesn't update when an older expense is edited on another device
The Awaiting approval refresh relies on the expense counter to catch edits on outstanding reports (useYourSpendData.ts#L615-L616). An edit from another device reaches this device through Pusher. If this device never loaded that expense and it was created more than 10 minutes ago, the new first-seen rule records it without bumping the counter (spendDataSignature.ts#L114-L119). On main, the counter bumps and the total refreshes.
- Use an account on a paid workspace (Collect or Control) with approvals on. Have a phone and web ready.
- On the phone, create a $10.00 expense on that workspace and submit the report.
- Wait at least 10 minutes. This moves the expense's
insertedtime outside the new 10-minute window.- On web, sign in fresh so web has never loaded that expense. Do not open the report.
- On web, open Home and note Your spend → Awaiting approval.
- On the phone, change that expense's amount to $25.00.
- On web, stay on Home for about 10 seconds.
Expected: Awaiting approval goes up by $15.00. Actual: Awaiting approval keeps the old total until you switch tabs and come back.
Hi I fixed it please try again
This test is failing. Am I missing anything? (the expense was created more than 10 min ago and also waited more than a minute on Home tab) Screen.Recording.2026-10-22.at.7.54.26.AM.mov |
An edit from another device can reach an expense this device never loaded, which the expense counter skips as a first load. The report total still moves, so the approval refresh key now reads it.
Please try again now - I also updated the qa steps |
|
Please merge main |
merged |
|
Hi @aimane-chnaif :) |
No, just awaiting final review from @mjasikowski |
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🚧 mjasikowski has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
🚀 Deployed to staging by https://github.com/mjasikowski in version: 9.5.24-1 🚀
|
|
No help site update needed: this PR only reduces background |
Explanation of Change
This is a follow-up to #100551 (Stop RHP close from refiring every Home search). After that PR reached production, the number of Home
Searchcalls per user did not go down. Testing locally found two more causes, and this PR fixes both.A transaction loaded for the first time counted as a change (
src/libs/actions/OnyxDerived/configs/spendDataSignature.ts).The Home cards only know some expenses from their search snapshot. When you open one of those expenses,
OpenReportwritestransactions_<id>to Onyx for the first time. The signature counted that new key as a change, so every Home card refetched when the RHP closed.Fix: a transaction key seen for the first time now only counts as a change when it has a pending write (a local create) or the server stored it in the last 10 minutes (
NEW_EXPENSE_WINDOW_MS, read frominserted). That covers a Pusher push, a statement import, or a workspace member's new expense. Opening an older expense counts nothing. The key is still recorded, so later edits to it still count.The "Awaiting approval" refresh key also reads each outstanding report's total (
outstandingReportTotalsinsrc/pages/home/YourSpendSection/useYourSpendData.ts). An edit from another device can reach an expense this device never loaded, which the rule above skips, but the report total still moves, so the card still refreshes.Every return to the app refetched every card (
src/hooks/useTabFocusedRefresh.ts).Each time the browser tab became visible or the app came to the foreground, all Home cards refetched. Switching between browser tabs fired a full Home refresh every time.
Fix: a return to the app within 60 seconds (
APP_RETURN_REFRESH_INTERVAL_MS) of the card's last refresh does not refresh right away. It refreshes once when the 60 seconds end, so an expense made elsewhere while you were away still shows up. Any other refresh before then cancels it. Tab focus and data changes still refresh right away.Measured in the browser (web dev, new account with one expense,
transactions_keys deleted from OnyxDB to simulate a cold Onyx), counting networkSearchcalls:ReconnectApp, since Pusher does not reach local dev)Unit tests:
tests/unit/OnyxDerived/spendDataSignatureTest.ts: reset state inbeforeEach; tests that relied on a first-seen bump now seed a baseline first and then edit. New cases: first load does not bump, an edit after the first load bumps, a local create bumps, a first-seen expense stored in the last 10 minutes bumps, an older one does not.tests/unit/hooks/useTabFocusedRefresh.test.ts: use fake timers; app-return tests move the clock past 60s. New cases: quick returns are skipped, a data change right after a skipped return still refreshes, a skipped return refreshes once the window ends, another refresh cancels it, a return after the device clock moved back refreshes.tests/unit/Search/yourSpendApplicabilityTest.ts: new cases foroutstandingReportTotals: it lists only outstanding reports with their totals, and it changes when a report total changes while the report ID list stays the same.Fixed Issues
$ #101552
PROPOSAL: N/A (follow-up to #100551)
Tests
On web:
api/Search.Searchrequest is sent.Searchrequest is sent.Searchrequest is sent when you come back, and that one Home refresh (Searchrequests for the Home cards) happens about a minute after the last refresh.Offline tests
Searchrequest is attempted.QA Steps
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectionAvatar, I verified the components usingAvatarare working as expected)npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari