Skip to content

Commit 3b20c91

Browse files
committed
Merge branch 'main' into fix/67256-save-user-information
2 parents 0408b1a + 821f714 commit 3b20c91

140 files changed

Lines changed: 1169 additions & 2604 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Mobile-Expensify

android/app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ android {
114114
minSdkVersion rootProject.ext.minSdkVersion
115115
targetSdkVersion rootProject.ext.targetSdkVersion
116116
multiDexEnabled rootProject.ext.multiDexEnabled
117-
versionCode 1009018921
118-
versionName "9.1.89-21"
117+
versionCode 1009019004
118+
versionName "9.1.90-4"
119119
// Supported language variants must be declared here to avoid from being removed during the compilation.
120120
// This also helps us to not include unnecessary language variants in the APK.
121121
resConfigs "en", "es"

contributingGuides/review/RULES.md

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
# ✅ AI Code Review Rules
2+
3+
These rules are used to conduct structured code reviews on pull request diffs.
4+
5+
Each rule includes:
6+
- A unique **Rule ID**
7+
- **Pass/Fail condition**
8+
- **Reasoning**: Technical explanation of why the rule is important
9+
- Examples of good and bad usage
10+
11+
Very important:
12+
- Make sure you include a separate comment for every rule violation
13+
- Every comment has to reference a **Rule ID** it violates
14+
15+
---
16+
17+
## Performance Rules
18+
19+
### [PERF-1] No spread in list item's renderItem
20+
- **Condition**: When passing data to components in renderItem functions, avoid using spread operators to extend objects. Instead, pass the base object and additional properties as separate props to prevent unnecessary object creation on each render.
21+
- **Reasoning**: `renderItem` functions execute for every visible list item on each render. Creating new objects with spread operators forces React to treat each item as changed, preventing reconciliation optimizations and causing unnecessary re-renders of child components.
22+
23+
Good:
24+
```tsx
25+
<Component
26+
item={item}
27+
isSelected={isSelected}
28+
shouldAnimateInHighlight={isItemHighlighted}
29+
/>
30+
```
31+
32+
Bad:
33+
```tsx
34+
<Component
35+
item={{
36+
shouldAnimateInHighlight: isItemHighlighted,
37+
isSelected: selected,
38+
...item,
39+
}}
40+
/>
41+
```
42+
43+
---
44+
45+
### [PERF-2] Use early returns in array iteration methods
46+
- **Condition**: When using `.every()`, `.some()`, or similar methods, perform simple checks first with early returns before expensive operations.
47+
- **Reasoning**: Expensive operations can be any long-running synchronous tasks (like complex calculations) and should be avoided when simple property checks can eliminate items early. This reduces unnecessary computation and improves iteration performance, especially on large datasets.
48+
49+
Good:
50+
```ts
51+
const areAllTransactionsValid = transactions.every((transaction) => {
52+
if (!transaction.rawData || transaction.amount <= 0) {
53+
return false;
54+
}
55+
const validation = validateTransaction(transaction);
56+
return validation.isValid;
57+
});
58+
```
59+
60+
Bad:
61+
```ts
62+
const areAllTransactionsValid = transactions.every((transaction) => {
63+
const validation = validateTransaction(transaction);
64+
return validation.isValid;
65+
});
66+
```
67+
68+
---
69+
70+
### [PERF-3] Use OnyxListItemProvider hooks instead of useOnyx in renderItem
71+
- **Condition**: Components rendered inside `renderItem` functions should use dedicated hooks from `OnyxListItemProvider` instead of individual `useOnyx` calls.
72+
- **Reasoning**: Individual `useOnyx` calls in renderItem create separate subscriptions for each list item, causing memory overhead and update cascades. `OnyxListItemProvider` hooks provide optimized data access patterns specifically designed for list rendering performance.
73+
74+
Good:
75+
```tsx
76+
const personalDetails = usePersonalDetails();
77+
```
78+
79+
Bad:
80+
```tsx
81+
const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST);
82+
```
83+
84+
---
85+
86+
### [PERF-4] Memoize objects and functions passed as props
87+
- **Condition**: Objects and functions passed as props should be properly memoized or simplified to primitive values to prevent unnecessary re-renders.
88+
- **Reasoning**: React uses referential equality to determine if props changed. New object/function instances on every render trigger unnecessary re-renders of child components, even when the actual data hasn't changed. Memoization preserves referential stability.
89+
90+
Good:
91+
```tsx
92+
const reportData = useMemo(() => ({
93+
reportID: report.reportID,
94+
type: report.type,
95+
isPinned: report.isPinned,
96+
}), [report.reportID, report.type, report.isPinned]);
97+
98+
return <ReportActionItem report={reportData} />
99+
```
100+
101+
Bad:
102+
```tsx
103+
const [report] = useOnyx(`ONYXKEYS.COLLECTION.REPORT${iouReport.id}`);
104+
105+
return <ReportActionItem report={report} />
106+
```
107+
108+
---
109+
110+
### [PERF-5] Use shallow comparisons instead of deep comparisons
111+
- **Condition**: In `React.memo` and similar optimization functions, compare only specific relevant properties instead of using deep equality checks.
112+
- **Reasoning**: Deep equality checks recursively compare all nested properties, creating performance overhead that often exceeds the re-render cost they aim to prevent. Shallow comparisons of specific relevant properties provide the same optimization benefits with minimal computational cost.
113+
114+
Good:
115+
```tsx
116+
memo(ReportActionItem, (prevProps, nextProps) =>
117+
prevProps.report.type === nextProps.report.type &&
118+
prevProps.report.reportID === nextProps.report.reportID &&
119+
prevProps.isSelected === nextProps.isSelected
120+
)
121+
```
122+
123+
Bad:
124+
```tsx
125+
memo(ReportActionItem, (prevProps, nextProps) =>
126+
deepEqual(prevProps.report, nextProps.report) &&
127+
prevProps.isSelected === nextProps.isSelected
128+
)
129+
```
130+
131+
---
132+
133+
### [PERF-6] Use specific properties as hook dependencies
134+
- **Condition**: In `useEffect`, `useMemo`, and `useCallback`, specify individual object properties as dependencies instead of passing entire objects.
135+
- **Reasoning**: Passing entire objects as dependencies causes hooks to re-execute whenever any property changes, even unrelated ones. Specifying individual properties creates more granular dependency tracking, reducing unnecessary hook executions and improving performance predictability.
136+
137+
Good:
138+
```tsx
139+
const {amountColumnSize, dateColumnSize, taxAmountColumnSize} = useMemo(() => {
140+
return {
141+
amountColumnSize: transactionItem.isAmountColumnWide ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL,
142+
taxAmountColumnSize: transactionItem.isTaxAmountColumnWide ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL,
143+
dateColumnSize: transactionItem.shouldShowYear ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL,
144+
};
145+
}, [transactionItem.isAmountColumnWide, transactionItem.isTaxAmountColumnWide, transactionItem.shouldShowYear]);
146+
```
147+
148+
Bad:
149+
```tsx
150+
const {amountColumnSize, dateColumnSize, taxAmountColumnSize} = useMemo(() => {
151+
return {
152+
amountColumnSize: transactionItem.isAmountColumnWide ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL,
153+
taxAmountColumnSize: transactionItem.isTaxAmountColumnWide ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL,
154+
dateColumnSize: transactionItem.shouldShowYear ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL,
155+
};
156+
}, [transactionItem]);
157+
```
158+
159+
---

ios/NewExpensify/Info.plist

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<key>CFBundlePackageType</key>
2424
<string>APPL</string>
2525
<key>CFBundleShortVersionString</key>
26-
<string>9.1.89</string>
26+
<string>9.1.90</string>
2727
<key>CFBundleSignature</key>
2828
<string>????</string>
2929
<key>CFBundleURLTypes</key>
@@ -44,7 +44,7 @@
4444
</dict>
4545
</array>
4646
<key>CFBundleVersion</key>
47-
<string>9.1.89.21</string>
47+
<string>9.1.90.4</string>
4848
<key>FullStory</key>
4949
<dict>
5050
<key>OrgId</key>

ios/NotificationServiceExtension/Info.plist

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
<key>CFBundleName</key>
1212
<string>$(PRODUCT_NAME)</string>
1313
<key>CFBundleShortVersionString</key>
14-
<string>9.1.89</string>
14+
<string>9.1.90</string>
1515
<key>CFBundleVersion</key>
16-
<string>9.1.89.21</string>
16+
<string>9.1.90.4</string>
1717
<key>NSExtension</key>
1818
<dict>
1919
<key>NSExtensionPointIdentifier</key>

ios/ShareViewController/Info.plist

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
<key>CFBundleName</key>
1212
<string>$(PRODUCT_NAME)</string>
1313
<key>CFBundleShortVersionString</key>
14-
<string>9.1.89</string>
14+
<string>9.1.90</string>
1515
<key>CFBundleVersion</key>
16-
<string>9.1.89.21</string>
16+
<string>9.1.90.4</string>
1717
<key>NSExtension</key>
1818
<dict>
1919
<key>NSExtensionAttributes</key>

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "new.expensify",
3-
"version": "9.1.89-21",
3+
"version": "9.1.90-4",
44
"author": "Expensify, Inc.",
55
"homepage": "https://new.expensify.com",
66
"description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
@@ -46,7 +46,7 @@
4646
"test:debug": "TZ=utc NODE_OPTIONS='--inspect-brk --experimental-vm-modules' jest --runInBand",
4747
"perf-test": "NODE_OPTIONS=--experimental-vm-modules npx reassure",
4848
"typecheck": "NODE_OPTIONS=--max_old_space_size=8192 tsc",
49-
"lint": "NODE_OPTIONS=--max_old_space_size=8192 eslint . --max-warnings=310 --cache --cache-location=node_modules/.cache/eslint",
49+
"lint": "NODE_OPTIONS=--max_old_space_size=8192 eslint . --max-warnings=300 --cache --cache-location=node_modules/.cache/eslint",
5050
"lint-changed": "NODE_OPTIONS=--max_old_space_size=8192 ./scripts/lintChanged.sh",
5151
"lint-watch": "npx eslint-watch --watch --changed",
5252
"shellcheck": "./scripts/shellCheck.sh",

src/CONST/index.ts

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1527,9 +1527,6 @@ const CONST = {
15271527
SEARCH_OPTION_LIST_DEBOUNCE_TIME: 300,
15281528
RESIZE_DEBOUNCE_TIME: 100,
15291529
UNREAD_UPDATE_DEBOUNCE_TIME: 300,
1530-
SEARCH_CONVERT_SEARCH_VALUES: 'search_convert_search_values',
1531-
SEARCH_MAKE_TREE: 'search_make_tree',
1532-
SEARCH_BUILD_TREE: 'search_build_tree',
15331530
SEARCH_FILTER_OPTIONS: 'search_filter_options',
15341531
USE_DEBOUNCED_STATE_DELAY: 300,
15351532
LIST_SCROLLING_DEBOUNCE_TIME: 200,
@@ -1538,10 +1535,8 @@ const CONST = {
15381535
PLAY_SOUND_MESSAGE_DEBOUNCE_TIME: 500,
15391536
NOTIFY_NEW_ACTION_DELAY: 700,
15401537
SKELETON_ANIMATION_SPEED: 3,
1541-
SEARCH_OPTIONS_COMPARISON: 'search_options_comparison',
15421538
SEARCH_MOST_RECENT_OPTIONS: 'search_most_recent_options',
15431539
DEBOUNCE_HANDLE_SEARCH: 'debounce_handle_search',
1544-
FAST_SEARCH_TREE_CREATION: 'fast_search_tree_creation',
15451540
},
15461541
PRIORITY_MODE: {
15471542
GSD: 'gsd',
@@ -2877,12 +2872,6 @@ const CONST = {
28772872
REIMBURSEMENT_NO: 'reimburseNo', // None
28782873
REIMBURSEMENT_MANUAL: 'reimburseManual', // Indirect
28792874
},
2880-
CASH_EXPENSE_REIMBURSEMENT_CHOICES: {
2881-
REIMBURSABLE_DEFAULT: 'reimbursableDefault', // Reimbursable by default
2882-
NON_REIMBURSABLE_DEFAULT: 'nonReimbursableDefault', // Non-reimbursable by default
2883-
ALWAYS_REIMBURSABLE: 'alwaysReimbursable', // Always Reimbursable
2884-
ALWAYS_NON_REIMBURSABLE: 'alwaysNonReimbursable', // Always Non Reimbursable
2885-
},
28862875
ID_FAKE: '_FAKE_',
28872876
EMPTY: 'EMPTY',
28882877
SECONDARY_ACTIONS: {
@@ -3127,7 +3116,6 @@ const CONST = {
31273116
CFPB_PREPAID: 'cfpb.gov/prepaid',
31283117
CFPB_COMPLAINT: 'cfpb.gov/complaint',
31293118
FDIC_PREPAID: 'fdic.gov/deposit/deposits/prepaid.html',
3130-
USE_EXPENSIFY_FEES: 'use.expensify.com/fees',
31313119
},
31323120

31333121
LAYOUT_WIDTH: {
@@ -3742,7 +3730,6 @@ const CONST = {
37423730
TAG: 'tag',
37433731
TAX_RATE: 'taxRate',
37443732
TAX_AMOUNT: 'taxAmount',
3745-
REIMBURSABLE: 'reimbursable',
37463733
REPORT: 'report',
37473734
},
37483735
FOOTER: {

src/ONYXKEYS.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -834,8 +834,8 @@ const ONYXKEYS = {
834834
SEARCH_SAVED_SEARCH_RENAME_FORM_DRAFT: 'searchSavedSearchRenameFormDraft',
835835
TEXT_PICKER_MODAL_FORM: 'textPickerModalForm',
836836
TEXT_PICKER_MODAL_FORM_DRAFT: 'textPickerModalFormDraft',
837-
RULES_CUSTOM_NAME_MODAL_FORM: 'rulesCustomNameModalForm',
838-
RULES_CUSTOM_NAME_MODAL_FORM_DRAFT: 'rulesCustomNameModalFormDraft',
837+
REPORTS_DEFAULT_TITLE_MODAL_FORM: 'ReportsDefaultTitleModalForm',
838+
REPORTS_DEFAULT_TITLE_MODAL_FORM_DRAFT: 'ReportsDefaultTitleModalFormDraft',
839839
RULES_AUTO_APPROVE_REPORTS_UNDER_MODAL_FORM: 'rulesAutoApproveReportsUnderModalForm',
840840
RULES_AUTO_APPROVE_REPORTS_UNDER_MODAL_FORM_DRAFT: 'rulesAutoApproveReportsUnderModalFormDraft',
841841
RULES_RANDOM_REPORT_AUDIT_MODAL_FORM: 'rulesRandomReportAuditModalForm',
@@ -954,7 +954,7 @@ type OnyxFormValuesMapping = {
954954
[ONYXKEYS.FORMS.SAGE_INTACCT_DIMENSION_TYPE_FORM]: FormTypes.SageIntacctDimensionForm;
955955
[ONYXKEYS.FORMS.SEARCH_ADVANCED_FILTERS_FORM]: FormTypes.SearchAdvancedFiltersForm;
956956
[ONYXKEYS.FORMS.TEXT_PICKER_MODAL_FORM]: FormTypes.TextPickerModalForm;
957-
[ONYXKEYS.FORMS.RULES_CUSTOM_NAME_MODAL_FORM]: FormTypes.RulesCustomNameModalForm;
957+
[ONYXKEYS.FORMS.REPORTS_DEFAULT_TITLE_MODAL_FORM]: FormTypes.ReportsDefaultTitleModalForm;
958958
[ONYXKEYS.FORMS.RULES_AUTO_APPROVE_REPORTS_UNDER_MODAL_FORM]: FormTypes.RulesAutoApproveReportsUnderModalForm;
959959
[ONYXKEYS.FORMS.RULES_RANDOM_REPORT_AUDIT_MODAL_FORM]: FormTypes.RulesRandomReportAuditModalForm;
960960
[ONYXKEYS.FORMS.RULES_AUTO_PAY_REPORTS_UNDER_MODAL_FORM]: FormTypes.RulesAutoPayReportsUnderModalForm;

0 commit comments

Comments
 (0)