Skip to content

Commit c7cd517

Browse files
committed
Merge branch 'main' into fix/63358
2 parents 561df82 + 0754dfc commit c7cd517

212 files changed

Lines changed: 5966 additions & 8568 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.

.github/workflows/presubmit.yml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: PR Presubmit Reviews
2+
3+
permissions:
4+
contents: read
5+
pull-requests: write
6+
7+
on:
8+
pull_request:
9+
types: [opened, synchronize, labeled]
10+
pull_request_review_comment:
11+
types: [created]
12+
13+
jobs:
14+
review:
15+
runs-on: ubuntu-latest
16+
if: contains(github.event.pull_request.labels.*.name, 'AI Review')
17+
steps:
18+
- name: Checkout code
19+
uses: actions/checkout@v4
20+
21+
- name: Check required secrets
22+
run: |
23+
if [ -z "${{ secrets.LLM_API_KEY }}" ]; then
24+
echo "Error: LLM_API_KEY secret is not configured"
25+
exit 1
26+
fi
27+
28+
- name: Read RULES.md
29+
id: read-rules
30+
run: |
31+
RULES_CONTENT=$(cat "contributingGuides/review/RULES.md")
32+
{
33+
echo "content<<EOF"
34+
echo "$RULES_CONTENT"
35+
echo "EOF"
36+
} >> "$GITHUB_OUTPUT"
37+
38+
- name: Print RULES.md
39+
run: |
40+
echo "📋 RULES.md"
41+
echo "======================================"
42+
echo "${{ steps.read-rules.outputs.content }}"
43+
echo "======================================"
44+
45+
- uses: aldo-expensify/ai-reviewer-hackaton@c91b349b41efdf1eeaa290716628d76068148f0b
46+
env:
47+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
48+
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
49+
LLM_MODEL: "claude-3-7-sonnet-20250219"
50+
with:
51+
style_guide_rules: ${{ steps.read-rules.outputs.content }}

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 1009019100
118+
versionName "9.1.91-0"
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"

assets/images/simple-illustrations/simple-illustration__empty-shelves.svg

Lines changed: 194 additions & 0 deletions
Loading
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+
---

cspell.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"ADDCOMMENT",
99
"Addendums",
1010
"ADFS",
11+
"aeiou",
1112
"Aeroplan",
1213
"águero",
1314
"Aircall",

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.90</string>
26+
<string>9.1.91</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.90.1</string>
47+
<string>9.1.91.0</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.90</string>
14+
<string>9.1.91</string>
1515
<key>CFBundleVersion</key>
16-
<string>9.1.90.1</string>
16+
<string>9.1.91.0</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

0 commit comments

Comments
 (0)