Skip to content

Commit 74022de

Browse files
authored
Merge branch 'main' into refactor-onyx-17
2 parents 0371287 + c90a042 commit 74022de

149 files changed

Lines changed: 1939 additions & 8113 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 1009019001
118-
versionName "9.1.90-1"
117+
versionCode 1009019005
118+
versionName "9.1.90-5"
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"
Lines changed: 16 additions & 0 deletions
Loading

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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
</dict>
4545
</array>
4646
<key>CFBundleVersion</key>
47-
<string>9.1.90.1</string>
47+
<string>9.1.90.5</string>
4848
<key>FullStory</key>
4949
<dict>
5050
<key>OrgId</key>

ios/NotificationServiceExtension/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<key>CFBundleShortVersionString</key>
1414
<string>9.1.90</string>
1515
<key>CFBundleVersion</key>
16-
<string>9.1.90.1</string>
16+
<string>9.1.90.5</string>
1717
<key>NSExtension</key>
1818
<dict>
1919
<key>NSExtensionPointIdentifier</key>

ios/Podfile.lock

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3270,11 +3270,11 @@ PODS:
32703270
- SocketRocket (0.7.1)
32713271
- Turf (2.8.0)
32723272
- TweetNacl (1.0.2)
3273-
- VisionCamera (4.6.1):
3274-
- VisionCamera/Core (= 4.6.1)
3275-
- VisionCamera/React (= 4.6.1)
3276-
- VisionCamera/Core (4.6.1)
3277-
- VisionCamera/React (4.6.1):
3273+
- VisionCamera (4.7.0):
3274+
- VisionCamera/Core (= 4.7.0)
3275+
- VisionCamera/React (= 4.7.0)
3276+
- VisionCamera/Core (4.7.0)
3277+
- VisionCamera/React (4.7.0):
32783278
- React-Core
32793279
- Yoga (0.0.0)
32803280

@@ -3923,7 +3923,7 @@ SPEC CHECKSUMS:
39233923
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
39243924
Turf: aa2ede4298009639d10db36aba1a7ebaad072a5e
39253925
TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6
3926-
VisionCamera: c95a8ad535f527562be1fb05fb2fd324578e769c
3926+
VisionCamera: b3c0cf261b9ad1ac7d60ec292d252cf3368b801e
39273927
Yoga: 50518ade05048235d91a78b803336dbb5b159d5d
39283928

39293929
PODFILE CHECKSUM: f7a8d931f2dd2be0fed9bb22df37140219a07ff3

ios/ShareViewController/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<key>CFBundleShortVersionString</key>
1414
<string>9.1.90</string>
1515
<key>CFBundleVersion</key>
16-
<string>9.1.90.1</string>
16+
<string>9.1.90.5</string>
1717
<key>NSExtension</key>
1818
<dict>
1919
<key>NSExtensionAttributes</key>

package-lock.json

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

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "new.expensify",
3-
"version": "9.1.90-1",
3+
"version": "9.1.90-5",
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=308 --cache --cache-location=node_modules/.cache/eslint",
49+
"lint": "NODE_OPTIONS=--max_old_space_size=8192 eslint . --max-warnings=295 --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",
@@ -198,7 +198,7 @@
198198
"react-native-tab-view": "^4.1.0",
199199
"react-native-url-polyfill": "^2.0.0",
200200
"react-native-view-shot": "4.0.0",
201-
"react-native-vision-camera": "^4.6.1",
201+
"react-native-vision-camera": "^4.7.0",
202202
"react-native-web": "0.20.0",
203203
"react-native-webview": "13.13.1",
204204
"react-plaid-link": "3.3.2",

0 commit comments

Comments
 (0)