Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
3301 commits
Select commit Hold shift + click to select a range
b59988f
chore: merge main into task-app-96572
yuwenmemon Oct 20, 2026
c471029
Merge pull request #104033 from Krishna2323/krishna2323/rules-engine-…
JS00001 Oct 20, 2026
a6e2a09
Merge pull request #103927 from akinwale/task-app-96572
francoisl Oct 20, 2026
b661277
chore(rules): merge main into the describe rule branch
yuwenmemon Oct 20, 2026
4bc2926
Merge pull request #102346 from apeyada/fix-97769
Julesssss Oct 20, 2026
553f0cc
Merge pull request #103683 from linhvovan29546/refactor/66418-remove-…
Julesssss Oct 20, 2026
05d1f97
Merge pull request #103527 from yuvrajangadsingh/fix/98624-flicker-re…
Julesssss Oct 20, 2026
eec590d
Add justifications to eslint-disable comments in the distance rate na…
MelvinBot Oct 20, 2026
bc54f60
Merge pull request #103810 from Expensify/yuwen-683436-describeRuleAg…
yuwenmemon Oct 20, 2026
0f9800a
chore(rules): merge main after #103810 landed
yuwenmemon Oct 20, 2026
0c16456
Translations
yuwenmemon Oct 20, 2026
9a6ee66
Merge remote-tracking branch 'origin/main' into refactor/66418-remove…
linhvovan29546 Oct 21, 2026
d73490e
Revert unrelated GH action bundle rebuilds from merge
linhvovan29546 Oct 21, 2026
8066c32
Add search to ValuePicker lists with 12 or more options
MelvinBot Oct 21, 2026
1684cdf
fix: expose empty receipt upload as an accessible button
nyomanjyotisa Oct 20, 2026
748f9b7
Make ValuePicker search opt-in and enable it only for the new room wo…
MelvinBot Oct 21, 2026
d855d2b
Merge branch 'main' into feat/103091-add-ashby-approver-field
bernhardoj Oct 21, 2026
34d49be
Revert "Make ValuePicker search opt-in and enable it only for the new…
MelvinBot Oct 21, 2026
cbb1ffd
don't show anything when final approver is not set
bernhardoj Oct 21, 2026
5b0e1b7
add missing space
bernhardoj Oct 21, 2026
8af2bf5
fix: Don't offer Save in the header for Spend footer selection changes
truph01 Oct 21, 2026
a081e52
Merge branch 'Expensify:main' into fix/99452-follow-up
truph01 Oct 21, 2026
21f77f3
Show the Reimbursable label on the Amount field in multi-expense repo…
MelvinBot Oct 21, 2026
d4d47e9
Merge remote-tracking branch 'origin/main' into claude-migrateHoldRej…
MelvinBot Oct 21, 2026
8863340
address comments
DylanDylann Oct 21, 2026
f3d205f
Merge remote-tracking branch 'origin/main' into claude-migrateDecisio…
MelvinBot Oct 21, 2026
ec73acf
Leave the Xero help docs to the follow-up docs PR
c3024 Oct 21, 2026
307c564
Merge pull request #103553 from Expensify/claude-migrateDecisionModal…
roryabraham Oct 21, 2026
6b11596
Merge branch 'main' into fix/103620
nkdengineer Oct 21, 2026
e344e3f
extend getTransactionFromTransactionListItem util and move it to new …
nkdengineer Oct 21, 2026
6560138
docs: replace arrow with plain words in Wise banner bank account comment
WojtekBoman Oct 21, 2026
d2d4187
fix test
bernhardoj Oct 21, 2026
e4dc155
remove test
bernhardoj Oct 21, 2026
ad49197
fix: fall back to bankCurrency when checking Wise global reimbursemen…
WojtekBoman Oct 21, 2026
b4577ba
Merge branch 'main' into @GCyganek/map-animations-improvements
GCyganek Oct 21, 2026
0688c72
improve migration
nkdengineer Oct 21, 2026
e0bcdeb
resolve conflict
nkdengineer Oct 21, 2026
cfedf6d
follow the Xero connect flow
nkdengineer Oct 21, 2026
8eb08cc
Rerun Jest tests
GCyganek Oct 21, 2026
ed93d18
Merge main
c3024 Oct 21, 2026
af3023a
fix: move Enable Global Reimbursements to second item in Wallet bank …
WojtekBoman Oct 21, 2026
e0a05ff
Pass translate to the later period chart test
c3024 Oct 21, 2026
a9339cd
Merge remote-tracking branch 'upstream/main' into fix/102824-thread-p…
oqildev Oct 21, 2026
c547377
Navigate back when deleting the only expense of a report in the Inbox…
MelvinBot Oct 21, 2026
46b1485
Merge remote-tracking branch 'origin/main' into pnc-ignore-primitives
fabioh8010 Oct 21, 2026
e158b72
Remove the suppressions that came back with the merge
fabioh8010 Oct 21, 2026
551fd2c
Zero-pad typed date filter boundaries
c3024 Oct 21, 2026
f3b0c1e
Merge remote-tracking branch 'origin/main' into claude-migrateHoldRej…
MelvinBot Oct 21, 2026
7837396
Split hold/reject educational modal hooks and chain follow-ups off th…
MelvinBot Oct 21, 2026
d4febc9
Merge remote-tracking branch 'origin/main' into claude-allowReceiptRe…
MelvinBot Oct 21, 2026
9a0b78e
Add separate When sections to the receipt replace tests
MelvinBot Oct 21, 2026
1e76dc4
Merge pull request #102140 from software-mansion-labs/jmgraa/NumericI…
mountiny Oct 21, 2026
83909a9
Keep what a Wise onboarding still needs on the bank account
grgia Oct 21, 2026
ab86229
Add translations for describeSearch strings in all supported languages
marcaaron Oct 21, 2026
3975485
Merge main and preserve report-field cleanup
KJ21-ENG Oct 21, 2026
11e464e
Export the requirement type from its file only
grgia Oct 21, 2026
7428d93
Re-run CI after an unrelated Jest worker SIGKILL
oqildev Oct 21, 2026
bc24df7
Fix false missingAttendees violation in Search rows
VickyStash Oct 21, 2026
066b630
add patch, to fix hovering charts on web
collectioneur Oct 21, 2026
3edc903
add comprehensive layout and responsiveness tests for NumericInput
jmgraa Oct 7, 2026
2268644
Merge branch 'main' into collectioneur/gesture-handler-hooks-charts
collectioneur Oct 21, 2026
7941c00
Close hold/reject educational modals on browser Back
MelvinBot Oct 21, 2026
07f8fac
Fix guest leaving and joining workspace room
ShridharGoel Oct 21, 2026
13bafa9
merge main
lorretheboy Oct 21, 2026
75683f1
Spell out what kycRequirements holds before and after Submit
grgia Oct 21, 2026
e41b76b
use getReimbursementChoice
lorretheboy Oct 21, 2026
fb778b3
Correct the type
VickyStash Oct 21, 2026
d40d699
use readOnceOnyxValue instead
nkdengineer Oct 21, 2026
e637898
add upstream pr link
collectioneur Oct 21, 2026
86b1bb1
Merge pull request #103455 from software-mansion-labs/collectioneur/g…
mountiny Oct 21, 2026
5bd9516
Auto-tighten eslint-seatbelt baseline
OSBotify Oct 21, 2026
926d26d
Share the Yes/No search cell and split the held sort test steps
c3024 Oct 21, 2026
19a6503
Show "removed" instead of "set to \"\"" when a text report field is c…
MelvinBot Oct 21, 2026
a2d24c4
Send the encrypted auth token only to Expensify URLs
mountiny Oct 21, 2026
aa5d265
Merge pull request #104449 from Expensify/claude-keepPDFFocusOnCarous…
inimaga Oct 21, 2026
b219144
Merge pull request #103347 from software-mansion-labs/fix/add-saml-se…
mountiny Oct 21, 2026
b8c2b6a
feat: add close tooltip to Wise global reimbursements banner
WojtekBoman Oct 21, 2026
9d3fc0a
Merge branch 'main' into wise-global-reimbursements-workspace-entry-p…
WojtekBoman Oct 21, 2026
70c4011
Add Given-When-Then structure to ReportUtils tests
ShridharGoel Oct 21, 2026
c64b55d
Merge pull request #104368 from software-mansion-labs/war-in/fix-insi…
grgia Oct 21, 2026
a172bc9
Remove the defaultValue fallback from the optimistic CHANGEFIELD action
MelvinBot Oct 21, 2026
f5a6996
fix: keep receipt alternatives outside upload button
nyomanjyotisa Oct 21, 2026
dc3c938
Fold canvasWidth into WrappedLabelConfig and simplify label layout
adamgrzybowski Oct 21, 2026
52c3292
Use toMatchObject instead of the deprecated originalMessage property …
MelvinBot Oct 21, 2026
ab6988c
merge main
Gonals Oct 21, 2026
6218067
Drop a requirement state Wise never sends
grgia Oct 21, 2026
b3af17d
perf: record source tab and preload wait on the Inbox tab span
BartekObudzinski Oct 21, 2026
be5570d
chore: trim comments on the Inbox tab span attributes
BartekObudzinski Oct 21, 2026
ca464f0
add confirm button
Gonals Oct 21, 2026
416ec1e
Merge pull request #104485 from software-mansion-labs/wise-global-rei…
grgia Oct 21, 2026
bc95a71
Merge pull request #104076 from c3024/chaitanya/691882-held-search-fi…
JS00001 Oct 21, 2026
aedd78c
Merge pull request #104252 from Expensify/claude-perDiemDownloadCsvBe…
JS00001 Oct 21, 2026
447eca2
allow office deletion when not needed
Gonals Oct 21, 2026
51fb69a
Merge pull request #104468 from c3024/chaitanya/xero-vendor-label
NicolasBonet Oct 21, 2026
0a1f77f
Merge pull request #104551 from Expensify/claude-fixClearedTextReport…
grgia Oct 21, 2026
916b823
Update version to 9.5.15-2
OSBotify Oct 21, 2026
f2ae43b
Update Mobile-Expensify submodule version to 9.5.15-2
OSBotify Oct 21, 2026
9d25e88
use badge for primary office
Gonals Oct 21, 2026
879eb5f
Merge pull request #104538 from Expensify/georgia-wise-kyc-requiremen…
grgia Oct 21, 2026
54cb527
merge main
Gonals Oct 21, 2026
be2772a
Merge branch 'main' into fix-96846
zeryns Oct 21, 2026
905a85b
Merge pull request #104434 from dukenv0307/fix/104225
arosiclair Oct 21, 2026
2799361
Merge pull request #103457 from software-mansion-labs/collectioneur/g…
mountiny Oct 21, 2026
0609fbf
Auto-tighten eslint-seatbelt baseline
OSBotify Oct 21, 2026
0d7b341
Merge remote-tracking branch 'origin/main' into claude-videoDownloadF…
MelvinBot Oct 21, 2026
1c03c87
better optimistic handling
Gonals Oct 21, 2026
e79cf4f
Merge branch 'main' into perf/3039-transaction-rows-item-level-useonyx
sosek108 Oct 21, 2026
02fcdc8
Merge pull request #104075 from c3024/chaitanya/690021-dash-open-periods
JS00001 Oct 21, 2026
5564918
allow people admins to make changes
Gonals Oct 21, 2026
eb55765
remove memo
Gonals Oct 21, 2026
10be66c
deduplicate helpers and remove redundant test cases
jmgraa Oct 21, 2026
724ccec
strengthen assertions and close coverage gap for NumericInput layout
jmgraa Oct 21, 2026
765abb4
Merge pull request #104264 from Expensify/claude-hideDistanceRateDate…
neil-marcellini Oct 21, 2026
7255f7a
Merge pull request #104347 from software-mansion-labs/insights/persis…
luacmartins Oct 21, 2026
7cb9aa2
Merge pull request #103703 from Expensify/claude-reapplySingleExpense…
Beamanator Oct 21, 2026
0afb910
Merge remote-tracking branch 'origin/main' into stale-vendors-refetch…
ShridharGoel Oct 21, 2026
aec9ef8
Merge pull request #101903 from Expensify/claude-enforceCashExpenseNo…
puneetlath Oct 21, 2026
b06e4df
Merge remote-tracking branch 'origin/main' into claude-appendCustomTa…
MelvinBot Oct 21, 2026
1d8ba8c
Merge branch 'main' of github.com:software-mansion-labs/expensify-app…
Guccio163 Oct 21, 2026
24b1aa6
Add notification to arrangement change
Gonals Oct 21, 2026
9a6e664
Merge pull request #103952 from Expensify/yuwen-683438-describeRuleAg…
JS00001 Oct 21, 2026
dee89c5
Revert unintended .github/actions/javascript changes
Guccio163 Oct 21, 2026
6b711f6
Merge pull request #104540 from callstack-internal/VickyStash/bugfix/…
mountiny Oct 21, 2026
7e166a3
Merge pull request #103134 from bernhardoj/feat/103091-add-ashby-appr…
yuwenmemon Oct 21, 2026
4a5e245
Merge pull request #103424 from oqildev/fix/102824-thread-prompt-afte…
yuwenmemon Oct 21, 2026
02706e1
Merge pull request #101783 from Expensify/melvin-docs-ies-custom-dime…
Julesssss Oct 21, 2026
b84ade7
Merge pull request #103070 from software-mansion-labs/Guccio163/confi…
mountiny Oct 21, 2026
e09cd57
Merge pull request #100170 from zeryns/fix-96846
mollfpr Oct 21, 2026
d9d88a9
Merge pull request #104542 from ShridharGoel/fix/104424
flodnv Oct 21, 2026
c668d93
Merge pull request #104058 from truph01/fix/99452-follow-up
JS00001 Oct 21, 2026
ade5ef2
remove optimistic change logs
Gonals Oct 21, 2026
564eaa1
Auto-tighten eslint-seatbelt baseline
OSBotify Oct 21, 2026
23a7842
fix: dont scroll search andd filers when shrinking
war-in Oct 21, 2026
aabbf37
force data refresh for the manual QBD sync action
akinwale Oct 21, 2026
c17f3ed
feat: Enable Insights on web ahead of native rollout
luacmartins Oct 21, 2026
b8ef5aa
Merge pull request #103239 from lorretheboy/rework/95750
chuckdries Oct 21, 2026
ad9aa45
Merge remote-tracking branch 'upstream/main' into krishna2323/issue/1…
Krishna2323 Oct 21, 2026
4e179ab
fix: extract `data-table-column-scroller="true"`
war-in Oct 21, 2026
d5db705
feat: Include Insights comparison in web rollout
luacmartins Oct 21, 2026
8655413
Rewrite PERF-3 for per-row Onyx reads
sosek108 Oct 21, 2026
8517d44
feat: Limit Insights rollout to desktop web
luacmartins Oct 21, 2026
8fd8406
Update PERF-11 for Onyx 3.0.117 store
sosek108 Oct 21, 2026
14a3c8d
Drop Onyx version history from rules
sosek108 Oct 21, 2026
6b42b8f
Merge pull request #102380 from Krishna2323/krishna2323/issue/101898-…
JS00001 Oct 21, 2026
c8a45f4
Merge pull request #104528 from nyomanjyotisa/jyo/receipt-empty-state…
lakchote Oct 21, 2026
a326f94
Bump Node to 26.12.1 to block the compromised eMudhra intermediates
robertjchen Oct 21, 2026
2906b7e
translate office and work arrangement change logs outside the chat
Gonals Oct 21, 2026
6b318fc
Merge pull request #104591 from Expensify/cmartins-insights-web-first
luacmartins Oct 21, 2026
cc0e43a
Merge pull request #104265 from Expensify/claude-letterAvatarForInvit…
aldo-expensify Oct 21, 2026
fd38b0b
Update version to 9.5.15-3
OSBotify Oct 21, 2026
6e99d83
Update Mobile-Expensify submodule version to 9.5.15-3
OSBotify Oct 21, 2026
0b69c7b
Merge pull request #103689 from nkdengineer/fix/103231
lakchote Oct 21, 2026
2280581
fix tests
JS00001 Oct 21, 2026
b0015dc
remove mobile-e
JS00001 Oct 21, 2026
6fbe99c
Merge remote-tracking branch 'origin/main' into jsenyitko-failing-test
JS00001 Oct 21, 2026
1077f6a
Merge branch 'main' into krishna2323/issue/66413/part-4a
Krishna2323 Oct 21, 2026
d1c3dd1
fix failing check.
Krishna2323 Oct 21, 2026
1f52829
Bump npm to 11.20.0 to match Node 26.12.1
robertjchen Oct 21, 2026
daf6ca5
Merge pull request #102237 from Expensify/marcaaron-nl-filters-ui
marcaaron Oct 21, 2026
f879dd7
Merge pull request #104537 from callstack-internal/pnc-ignore-primitives
roryabraham Oct 21, 2026
496ab3a
Merge pull request #102260 from Krishna2323/krishna2323/issue/66413/p…
danieldoglas Oct 21, 2026
ffeca72
fix eslint
JS00001 Oct 21, 2026
cad067e
Merge pull request #103003 from Expensify/claude-bumpMathjs
jasperhuangg Oct 21, 2026
3ac06a4
Update Mobile-Expensify submodule to c880190
OSBotify Oct 21, 2026
c6171c4
merge main
Gonals Oct 21, 2026
5a43187
Merge branch 'main' into robert-nodeEmudhraBlocklist
robertjchen Oct 21, 2026
a388ae0
Update Mobile-Expensify submodule to 21d5357
OSBotify Oct 21, 2026
5bfc256
fix eslint
JS00001 Oct 21, 2026
5d38ffe
Merge pull request #103552 from DylanDylann/dylann/103132-category-dr…
puneetlath Oct 21, 2026
f7f97bc
Update Mobile-Expensify submodule to 2430692
OSBotify Oct 21, 2026
837e1f7
Merge pull request #104601 from Expensify/jsenyitko-failing-test
JS00001 Oct 21, 2026
991d83d
Merge branch 'main' into robert-nodeEmudhraBlocklist
robertjchen Oct 21, 2026
ca0c6d1
Merge pull request #104466 from software-mansion-labs/@adamgrzybowski…
luacmartins Oct 21, 2026
ee50305
Auto-tighten eslint-seatbelt baseline
OSBotify Oct 21, 2026
50ba670
Merge pull request #104292 from TaduJR/fix/android-enable-r8-optimizer
AndrewGable Oct 21, 2026
ce478a8
Merge pull request #104599 from Expensify/robert-nodeEmudhraBlocklist
robertjchen Oct 21, 2026
fc6a463
Update Mobile-Expensify submodule to 79eae6d
OSBotify Oct 21, 2026
9e45525
Update version to 9.5.16-0
OSBotify Oct 21, 2026
5821540
Update Mobile-Expensify submodule version to 9.5.16-0
OSBotify Oct 21, 2026
c68158d
Merge pull request #104179 from Expensify/chuckdries/mfa-tokenization
rafecolton Oct 21, 2026
e6ba03f
Generalize PERF-3 reasoning
sosek108 Oct 21, 2026
6c555ca
Split PERF-3 into member-key and row-level reads
sosek108 Oct 21, 2026
9c70ecc
Split row Onyx reads into PERF-3 and PERF-21
sosek108 Oct 21, 2026
f7010c7
Restore PERF-3 scope, move member subscriptions to PERF-22
sosek108 Oct 21, 2026
a1b9355
Recycle Jest workers past 2GB to stop CI OOM kills
robertjchen Oct 21, 2026
c892d1c
Merge pull request #104639 from Expensify/robert-jestWorkerMemory
chuckdries Oct 21, 2026
7c8f6ce
Merge pull request #103899 from Krishna2323/krishna2323/issue/66425-p…
madmax330 Oct 21, 2026
ec8371a
Merge pull request #102905 from ishpaul777/ishpaul/655943-reimbursed-…
madmax330 Oct 21, 2026
cf519d7
Merge remote-tracking branch 'exfy/main' into perf/3039-transaction-r…
sosek108 Oct 21, 2026
65bab6a
Add files via upload
stephanieelliott Oct 21, 2026
3e2c3c1
Fix read-only vendor toggle and vendor picker loading states
ShridharGoel Oct 21, 2026
da8b9c2
Merge pull request #103709 from Expensify/claude-migrateHoldRejectEdu…
roryabraham Oct 21, 2026
b3857d4
Remove useCallback from vendor fetch for React Compiler compliance
ShridharGoel Oct 21, 2026
d55157e
Merge pull request #101868 from software-mansion-labs/reassure-perf-t…
mountiny Oct 21, 2026
7d29b73
Update Expensify-Home-Overview.md
stephanieelliott Oct 21, 2026
21a72ab
Update Navigating-New-Expensify.md
stephanieelliott Oct 21, 2026
70a6ac3
Update How-to-Use-Insights.md
stephanieelliott Oct 21, 2026
2716a6e
Update Account-Settings.md
stephanieelliott Oct 21, 2026
f2978e4
Merge pull request #102902 from FitseTLT/fix-audit-removeSMSDomain-pa…
mountiny Oct 21, 2026
7ef9a7c
Merge pull request #103549 from ShridharGoel/stale-vendors-refetch-main
Beamanator Oct 21, 2026
3249384
Revert "Merge pull request #103703 from Expensify/claude-reapplySingl…
MelvinBot Oct 21, 2026
de833b7
Address review: explain the proxy root filter and drop the redundant …
mountiny Oct 21, 2026
6333d13
Merge pull request #102832 from Expensify/claude-insightsRelease1Help…
stephanieelliott Oct 21, 2026
fd80838
Merge pull request #103968 from linhvovan29546/refactor/66418-remove-…
Julesssss Oct 21, 2026
4c39844
Merge pull request #104526 from Expensify/claude-addSearchToValuePicker
Julesssss Oct 21, 2026
ab211f4
Merge pull request #104450 from nkdengineer/fix/103620
Julesssss Oct 21, 2026
7f3c40d
Merge pull request #104134 from KJ21-ENG/seatbelt-v2/batch-20261015-r…
blimpich Oct 21, 2026
1f1c523
Merge pull request #103582 from software-mansion-labs/@GCyganek/map-a…
blimpich Oct 21, 2026
e9b6d90
Merge pull request #103920 from KJ21-ENG/seatbelt-v2/batch-20261013-r…
blimpich Oct 21, 2026
f082fcc
Auto-tighten eslint-seatbelt baseline
OSBotify Oct 21, 2026
7374617
Merge pull request #103840 from hoangzinh/66387/part2-pr2-pr3
carlosmiceli Oct 22, 2026
c3571a4
Justify the eslint-disable in the SingleAvatar test mock
MelvinBot Oct 22, 2026
6d7fe90
Merge pull request #104383 from Expensify/claude-docsRejectedSearchFi…
stephanieelliott Oct 22, 2026
2da3516
Merge pull request #103990 from Expensify/claude-docsCompanyCardBalan…
stephanieelliott Oct 22, 2026
281586c
Merge pull request #104385 from Expensify/claude-docsGuestWorkspaceRo…
stephanieelliott Oct 22, 2026
72419cc
Fix typecheck on main
c3024 Oct 22, 2026
beec381
Merge pull request #104512 from Expensify/claude-avatarTooltipCentere…
roryabraham Oct 22, 2026
439f896
Merge pull request #104672 from c3024/chaitanya/fix-main-typecheck
roryabraham Oct 22, 2026
15658b7
Merge remote-tracking branch 'origin/main' into claude-videoDownloadF…
MelvinBot Oct 22, 2026
ebf5be3
Merge pull request #104592 from akinwale/task-app-693713-ee
yuwenmemon Oct 22, 2026
0a1f808
Merge pull request #103359 from Expensify/claude-appendCustomTagList
yuwenmemon Oct 22, 2026
45145fc
Merge pull request #104072 from Expensify/claude-videoDownloadFileName
yuwenmemon Oct 22, 2026
fcebb0c
Merge pull request #104552 from Expensify/vit-sendAuthTokenOnlyToExpe…
mjasikowski Oct 22, 2026
d0ba3f1
Merge pull request #103753 from software-mansion-labs/fix/report-name…
mollfpr Oct 22, 2026
9eadf5b
Merge branch 'main' into perf/3039-transaction-rows-item-level-useonyx
sosek108 Oct 22, 2026
5b95a06
psalm
Gonals Oct 22, 2026
ac15a33
Mock useLoadPolicyCategories in CategoryPickerTest
Gonals Oct 22, 2026
3d491ba
Merge pull request #104711 from Expensify/alberto-categoryPickerTestC…
Gonals Oct 22, 2026
5cec1bc
check office locations, not just company address
Gonals Oct 22, 2026
5305b7e
properly check adress for default office
Gonals Oct 22, 2026
fad7682
Merge pull request #104215 from Expensify/claude-allowReceiptReplaceD…
MariaHCD Oct 22, 2026
6dabd8e
Merge pull request #104588 from software-mansion-labs/war-in/fix-resi…
JS00001 Oct 22, 2026
32253ff
Merge pull request #104344 from Expensify/alberto-commuteR4Users
mjasikowski Oct 22, 2026
ebf3e87
Merge pull request #104661 from Expensify/claude-revertSingleExpenseR…
mountiny Oct 22, 2026
9838335
Update version to 9.5.16-1
OSBotify Oct 22, 2026
9238f51
Update Mobile-Expensify submodule version to 9.5.16-1
OSBotify Oct 22, 2026
664dec4
Limit PERF-11 row exception to PERF-22 key shapes
sosek108 Oct 22, 2026
c9949a7
Merge pull request #104593 from callstack-internal/perf-3-per-row-ony…
mountiny Oct 22, 2026
f6897b2
Merge pull request #104567 from callstack-internal/perf/inbox-tab-spa…
JS00001 Oct 22, 2026
d347157
Merge pull request #104544 from software-mansion-labs/jmgraa/numeric-…
mountiny Oct 22, 2026
540ab7f
Merge branch 'main' into perf/3039-transaction-rows-item-level-useonyx
sosek108 Oct 22, 2026
a49b5d5
Use useStableArrayReference for report columns
sosek108 Oct 22, 2026
4618b62
Remove useMemo from useMoneyRequestReportSortedTransactions
sosek108 Oct 22, 2026
72c0429
Drop stale selector stability comment
sosek108 Oct 22, 2026
9973dd7
Reorganize transaction row and list component internals
sosek108 Oct 22, 2026
d8955b8
Flip violation state on every write across reassure runs
sosek108 Oct 22, 2026
17b407e
Select only the row's own card
sosek108 Oct 23, 2026
800c8c8
Use the US spelling in the prompt record test comment
oqildev Nov 18, 2026
c374f57
Preserve the app review prompt record on sign-out, not just on reconnect
oqildev Nov 18, 2026
e8dad8b
Merge pull request #102904 from oqildev/fix/99564-app-review-prompt-loop
marcaaron Oct 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 4 additions & 1 deletion .claude/skills/app-coding-standards/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Coding standards for the Expensify App. Each standard is a standalone file in `r
### Performance
- [PERF-1](rules/perf-1-no-spread-in-renderitem.md) — No spread in renderItem
- [PERF-2](rules/perf-2-early-return.md) — Return early before expensive work
- [PERF-3](rules/perf-3-onyx-list-item-provider.md) — Use OnyxListItemProvider in renderItem
- [PERF-3](rules/perf-3-onyx-list-item-provider.md) — Shared data in list rows comes from OnyxListItemProvider
- [PERF-5](rules/perf-5-shallow-comparison.md) — Shallow over deep comparisons
- [PERF-6](rules/perf-6-derive-state-from-props.md) — Derive state from props
- [PERF-7](rules/perf-7-reset-via-key-prop.md) — Reset via key prop
Expand All @@ -38,6 +38,9 @@ Coding standards for the Expensify App. Each standard is a standalone file in `r
- [PERF-17](rules/perf-17-pass-raw-index-on-demand.md) — Pass raw source, index on demand (no pre-built digest)
- [PERF-18](rules/perf-18-use-pre-mount-destination.md) — Use usePreMountDestination for RHP pre-mounting
- [PERF-19](rules/perf-19-scalar-collection-selectors.md) — Collection selectors return only what the consumer uses
- [PERF-20](rules/perf-20-no-ad-hoc-deferral.md) — Defer heavy work to happen after visual transitions
- [PERF-21](rules/perf-21-rows-read-own-data.md) — List rows read their own Onyx data
- [PERF-22](rules/perf-22-row-single-member-subscriptions.md) — List rows subscribe to single collection members

### Consistency
- [CONSISTENCY-1](rules/consistency-1-no-platform-checks.md) — No platform-specific checks in components
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ title: Optimize data selection and handling

### Reasoning

`useOnyx` supports an optional `selector` to narrow data before it reaches the component. Selectors control both **what** data the component receives and **how** Onyx detects changes:
`useOnyx` supports an optional `selector` to narrow data before it reaches the component. Onyx notifies subscribers per key: a write to a collection member notifies that member's subscribers and the collection's subscribers, and unchanged members keep their references. Selectors control both **what** data the component receives and **how** Onyx detects changes:

- **With a selector**: Onyx runs `deepEqual` on the selector output to decide whether to re-render. This guards against unnecessary re-renders when unrelated data changes, but the comparison cost scales with the size of the output.
- **Without a selector**: Onyx uses `shallowEqual` on raw references, which is much cheaper. However, the component will re-render whenever **any** part of the subscribed data changes, since there is no narrowing to filter out irrelevant updates.
- **Without a selector**: Onyx compares by reference only, which is free. However, the component re-renders on **every** write to the key, even when the new value has the same contents. For a collection key, that means every write to any member.

This means selectors are a double-edged sword. A well-written selector that returns a primitive or a small object is highly effective — it skips re-renders when unrelated data changes, and `deepEqual` on a small value is trivial. But a poorly-written selector that returns a large object, a full collection, or a non-plain type like `Set`/`Map` makes things worse — it forces an expensive `deepEqual` on every Onyx update with no re-render savings.

**Prefer a member key:** when the component needs one or a few members of a collection and knows their IDs, subscribe to `${ONYXKEYS.COLLECTION.X}${id}` instead of the collection. It needs no selector and wakes only for that member. In list rows this is required, see [PERF-22](perf-22-row-single-member-subscriptions.md).

**When to use a selector:**
- To pick a few fields from a single Onyx key (reduces re-renders from unrelated field changes)
- To compute a final scalar (boolean, number, string) from a larger dataset
Expand Down Expand Up @@ -94,7 +96,10 @@ function UserProfile({userId}) {
return <Text>{user?.name}</Text>;
}

// GOOD: No selector on collection — shallowEqual is cheap, transform inline
// GOOD: The member is known, so read it by key instead of selecting it from the collection
const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`);

// GOOD: The component needs every policy. No selector: the reference check is free, transform inline
const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY);
const mappedPolicies = Object.fromEntries(
Object.entries(policies ?? {}).map(([key, policy]) => [
Expand Down Expand Up @@ -140,6 +145,7 @@ Flag ONLY when ANY of these are true:
- The selector meaningfully reduces a large dataset to a small result (e.g., a primitive or a few items) by iterating over the subscribed collection itself — the `deepEqual` cost on a small result is negligible
- The `useOnyx` call is on a single-item key (not a collection), and the selector picks specific fields
- The data structure is static or the function requires the entire object for valid operations
- The code is a list row subscribing to a whole collection (`ONYXKEYS.COLLECTION.*`) or a derived map with an entry per record. Report [PERF-22](perf-22-row-single-member-subscriptions.md) instead. A row reading any other broad key, such as `BANK_ACCOUNT_LIST`, is still in scope here

**Search Patterns** (hints for reviewers):
- `useOnyx`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
---
ruleId: PERF-20
title: Defer heavy work to happen after visual transitions
---

## [PERF-20] Defer heavy work to happen after visual transitions

### Reasoning

Work that runs during a navigation, modal, or keyboard transition competes with the animation for the JS thread. `TransitionTracker` knows when these transitions start and end: screen push/pop/replace, modal open/close, and keyboard dismiss through `KeyboardUtils.dismiss`. The App has shared primitives built on it:

- `usePreMountDestination` to mount the screen an RHP flow ends on while the RHP is still open
- `<NavigationDeferredMount>` to mount a subtree after tracked transitions settle
- `API.writeWhenReady` to apply an optimistic write once a barrier settles (by default, tracked transitions)
- for one-off imperative work:
- the `afterTransition` callback option on `Navigation` methods and `KeyboardUtils.dismiss`
- `Navigation.runAfterTransition` and `Navigation.runAfterUpcomingTransition`
- `TransitionTracker.runAfterTransitions`

`TransitionTracker.runAfterTransitions({callback})` runs the callback synchronously when no transition is active. When it is called before the transition starts (for example, right after a navigation call), pass `waitForUpcomingTransition`. These primitives wait for tracked transitions to settle, with a fallback timeout if none starts. Two of them can also release earlier:

- `KeyboardUtils.dismiss` calls `afterTransition` right away when the keyboard is not visible, and always on desktop web
- `API.writeWhenReady` runs the write right away when the app goes inactive or to the background

See [DEFER_AFTER_PAINT.md](../../../../contributingGuides/DEFER_AFTER_PAINT.md) for which primitive fits which case, and for when `API.writeWhenReady` is not a safe replacement for `API.write`.

### When to use

Use one of these primitives when work triggered by an interaction is not needed for the first frame of the destination and would otherwise run while a navigation, modal, or keyboard transition is animating: a follow-up API write, focus or scroll, a follow-up navigation, or a heavy subtree on a freshly navigated screen.

### When NOT to use

- The delay is a UI duration that doesn't depend on a transition, such as a tooltip hover delay or how long a toast stays on screen
- The code needs a first frame or layout pass for a reason other than a transition (measuring, hydration)
- The destination needs the work to render its first frame
- None of the primitives fits. Flag it to a human, the pattern needs to be extended thoughtfully instead of worked around with inline timing

### Incorrect

The names below (`SOME_COMMAND`, `pendingWrite.ts`, `setPendingWrite`, etc...) are placeholders, not real App symbols.

```tsx
// Pattern 1: timer or frame callback right after a navigation call
Navigation.dismissModal();

// Wait for the modal close animation before writing
setTimeout(() => {
API.write(WRITE_COMMANDS.SOME_COMMAND, params, onyxData);
}, 500);
```

```tsx
// Pattern 2: a screen runs another flow's pending work when it gains focus

// pendingWrite.ts
let pendingWrite: (() => void) | undefined;
function setPendingWrite(write: () => void) {
pendingWrite = write;
}
function hasPendingWrite() {
return !!pendingWrite;
}
function flushPendingWrite() {
pendingWrite?.();
}
function clearPendingWrite() {
pendingWrite = undefined;
}

// Submit flow: queues the write, then navigates to ReportScreen
setPendingWrite(() => API.write(WRITE_COMMANDS.SOME_COMMAND, params, onyxData));
Navigation.dismissModalWithReport({reportID});

// ReportScreen.tsx: runs the queued write on focus, so it doesn't compete with the dismiss animation
useFocusEffect(
useCallback(() => {
if (!hasPendingWrite()) {
return;
}
flushPendingWrite();
clearPendingWrite();
}, []),
);
```

```tsx
// Pattern 3: a hand-rolled "ready" flag that gates rendering
function SomeScreenHeader() {
// Render the actions only after the navigation animation, they are expensive
const [isReady, setIsReady] = useState(false);
useEffect(() => {
const id = requestAnimationFrame(() => setIsReady(true));
return () => cancelAnimationFrame(id);
}, []);

return isReady ? <SecondaryActions /> : null;
}
```

### Correct

```tsx
// Pattern 1: the follow-up work runs once the dismiss transition settles
Navigation.dismissModal({afterTransition: () => doFollowUpWork()});

// Or, when the delayed work is a single API.write that can be deferred (see the caveats below)
const armed = API.armTransitionBarrier();
Navigation.dismissModal();
API.writeWhenReady(WRITE_COMMANDS.SOME_COMMAND, params, onyxData, armed.barrier);
```

```tsx
// Pattern 2: the flow that navigates owns the deferral, the destination screen has no flush
Navigation.dismissModalWithReport({reportID}, undefined, {
afterTransition: () => API.write(WRITE_COMMANDS.SOME_COMMAND, params, onyxData),
});
```

```tsx
// Pattern 3
<NavigationDeferredMount placeholder={<ActionsSkeleton />}>
<SecondaryActions />
</NavigationDeferredMount>
```

---

### Review Metadata

**Only flag what the diff shows.** Every condition of a pattern must be visible in the added or changed lines or in the surrounding code shown in the diff. Do not infer behavior from a function's name alone. If a condition can't be confirmed from the diff, do not flag. Missing a case this way is intended because a false positive costs more than a miss.

**Evidence of intent (patterns 1, 2 and 3).** Only flag when the code shows the delay exists to wait for a navigation, modal, or keyboard transition, for example a comment saying so or `CONST.ANIMATED_TRANSITION` as the delay. A navigation call before the timer is not enough on its own.

Flag these patterns in added or changed lines:

1. **Timer or frame after a navigation call.** In the same function body, `setTimeout` or `requestAnimationFrame` is called on a line after one of these calls. The timer must run on the same invocation of the function as the navigation call. Navigation calls include:
- `Navigation.navigate`, `Navigation.goBack`, `Navigation.dismissModal`, `Navigation.dismissModalWithReport`, etc...
- `KeyboardUtils.dismiss`

Nested `requestAnimationFrame` calls count. Suggest the `afterTransition` option of that navigation call.

Tell the author the fix must keep the old behavior:
- the navigation call still runs in the same cases as before, and the delayed work keeps the same guards. A guard that sat between the navigation call and the timer must not start skipping the navigation
- if the timer was cleared somewhere (for example on unmount), keep that. `afterTransition` has no cancel handle, so check a cancelled flag inside the callback. For `navigate`, `goBack` and `dismissModal` without `waitForTransition`, the work can instead be scheduled with `Navigation.runAfterUpcomingTransition` right after the call, and for `KeyboardUtils.dismiss` with `Navigation.runAfterTransition`. Then call `cancel()` on the returned handle in the cleanup
- some of the `Navigation` methods above can return early on some paths without calling `afterTransition`, while the old timer always ran. For example: `goBack` at the root of the stack, or when it falls back to resetting to the app root; `revealRouteBeforeDismissingModal` when navigation is not ready; `dismissToPreviousRHP` when there is nothing to dismiss. If the work must still run on those paths, it needs its own path
- the timing can change. `KeyboardUtils.dismiss` runs `afterTransition` synchronously when the keyboard is already hidden, and always on desktop web, so the work runs before the next line instead of later. When no transition starts, `afterTransition` waits up to `CONST.MAX_TRANSITION_START_WAIT_MS` (1000 ms), which can be later than the old timer

Do not flag pattern 1 when the delay is a UI duration that doesn't depend on a transition, such as a tooltip hover delay or how long a toast stays on screen. A tooltip or toast delayed to wait for a navigation, modal or keyboard transition is not exempt. If the purpose is unclear, do not flag.

2. **Pending work flushed on focus.** The evidence of intent is visible, and all of these are visible in the diff:
- a `useFocusEffect` callback checks a flag or value set outside this component: a module-level variable, or a getter from another file. The diff must show the getter and setter reading and writing the same variable, not just their names
- when the flag is set, the callback runs an API write, or a flush function whose body in the diff runs queued work
- the flag is set in the same function as a `Navigation.*` or `KeyboardUtils.dismiss` call (as in pattern 1), before that call
- the diff shows that the navigation goes to the screen with the focus callback: the same route or screen constant appears at the navigation call and in that screen, or a comment at the navigation call names that screen. If the diff doesn't show this, do not flag

Suggest moving the deferred work into the `afterTransition` option of the flow's navigation call.

Do not flag pattern 2 when the `useFocusEffect` callback only fetches or refreshes data, starts or stops a subscription, or sets focus, or when a comment says the work needs the destination screen itself (its data, its layout, or real focus). If the code that sets the flag is not in the diff, do not flag.

3. **Hand-rolled ready flag gating a render.** The whole component is visible in the diff, the evidence of intent is visible, and:
- a `useState` boolean starts as `false`
- it is set to `true` once, from an effect that runs on mount (empty dependency array), inside a `setTimeout`, `requestAnimationFrame`, or `TransitionTracker.runAfterTransitions` callback
- its setter is not called anywhere else and not passed as a value to another component or function (calling it inside the scheduled callback, or inside `startTransition` there, is fine)
- the scheduled callback does nothing except set the flag (wrapping the setter in `startTransition` still counts as only setting the flag)
- the flag is only used to decide whether children render (not in effects, event handlers or props)

Suggest `<NavigationDeferredMount>`, which mounts its children once after tracked transitions settle and yields to input with `startTransition`. It waits for an upcoming transition by default. Add a note that `waitForUpcomingTransition={false}` exists for components that can mount without a navigation transition (for example, remounted by a parent with a new `key`). Do not decide which value the component needs, since its mounting paths are usually not in the diff.

Do not flag pattern 3 when the delay is a UI duration that doesn't depend on a transition (a tooltip hover delay, a toast that shows after N ms). A tooltip or toast delayed to wait for a navigation, modal or keyboard transition is not exempt. Also do not flag when a comment says the code needs a first frame or layout pass for a reason other than a transition (measuring, hydration).

**Caveats for suggesting `API.writeWhenReady`:** only suggest it when the delayed work is a single `API.write` call and none of the cases in [Do not use `writeWhenReady` when](../../../../contributingGuides/DEFER_AFTER_PAINT.md#deferring-writes) apply. If the diff can't confirm that, suggest `afterTransition` instead.

**DO NOT flag if:**

- The delayed work being reviewed is itself scheduled with the `afterTransition` option, `API.writeWhenReady`, `Navigation.runAfterTransition`, `Navigation.runAfterUpcomingTransition`, or `TransitionTracker.runAfterTransitions`. A separate timer in the same function is still checked on its own. This covers patterns 1 and 2 only: pattern 3 still applies to a ready flag set inside `runAfterTransitions`. The body of an `afterTransition` callback is still checked, so a new navigation call followed by a timer inside it is pattern 1.
- The code is inside one of these files, which implement the primitives: `src/libs/Navigation/TransitionTracker.ts`, `src/libs/Navigation/Navigation.ts`, `src/libs/API/writeWhenReady.ts`, `src/components/NavigationDeferredMount.tsx`, `src/hooks/usePreMountDestination/`, `src/libs/Navigation/helpers/preMountBuffer.ts`
- The file is under `tests/` or its name ends in `.test.ts`, `.test.tsx`, `Test.ts` or `Test.tsx`

**Search patterns** (starting points only, not the full definition of a match; generic arguments like `useState<boolean>(false)` and whitespace do not change a match):

- `setTimeout(`
- `requestAnimationFrame(`
- `useFocusEffect(`
- `useState(false)`
Loading
Loading