Skip to content

Commit 84a1c4a

Browse files
committed
Merge branch 'main' of https://github.com/Expensify/App into fix/39947
2 parents f4f6931 + 611b86f commit 84a1c4a

File tree

348 files changed

+5355
-2872
lines changed

Some content is hidden

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

348 files changed

+5355
-2872
lines changed

.eslintrc.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ module.exports = {
100100
__DEV__: 'readonly',
101101
},
102102
rules: {
103-
'@typescript-eslint/no-unsafe-argument': 'off',
104103
'@typescript-eslint/no-unsafe-call': 'off',
105104
'@typescript-eslint/no-unsafe-member-access': 'off',
106105
'@typescript-eslint/no-unsafe-assignment': 'off',
@@ -156,6 +155,7 @@ module.exports = {
156155
fixMixedExportsWithInlineTypeSpecifier: false,
157156
},
158157
],
158+
'@typescript-eslint/no-use-before-define': ['error', {functions: false}],
159159

160160
// ESLint core rules
161161
'es/no-nullish-coalescing-operators': 'off',

.github/actions/javascript/bumpVersion/bumpVersion.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as core from '@actions/core';
22
import {exec as originalExec} from 'child_process';
33
import fs from 'fs';
4+
import type {PackageJson} from 'type-fest';
45
import {promisify} from 'util';
56
import {generateAndroidVersionCode, updateAndroidVersion, updateiOSVersion} from '@github/libs/nativeVersionUpdater';
67
import * as versionUpdater from '@github/libs/versionUpdater';
@@ -19,7 +20,7 @@ function updateNativeVersions(version: string) {
1920
.then(() => {
2021
console.log('Successfully updated Android!');
2122
})
22-
.catch((err) => {
23+
.catch((err: string | Error) => {
2324
console.error('Error updating Android');
2425
core.setFailed(err);
2526
});
@@ -47,8 +48,12 @@ if (!semanticVersionLevel || !Object.keys(versionUpdater.SEMANTIC_VERSION_LEVELS
4748
console.log(`Invalid input for 'SEMVER_LEVEL': ${semanticVersionLevel}`, `Defaulting to: ${semanticVersionLevel}`);
4849
}
4950

50-
const {version: previousVersion} = JSON.parse(fs.readFileSync('./package.json').toString());
51-
const newVersion = versionUpdater.incrementVersion(previousVersion, semanticVersionLevel);
51+
const {version: previousVersion}: PackageJson = JSON.parse(fs.readFileSync('./package.json').toString());
52+
if (!previousVersion) {
53+
core.setFailed('Error: Could not read package.json');
54+
}
55+
56+
const newVersion = versionUpdater.incrementVersion(previousVersion ?? '', semanticVersionLevel);
5257
console.log(`Previous version: ${previousVersion}`, `New version: ${newVersion}`);
5358

5459
updateNativeVersions(newVersion);

.github/actions/javascript/bumpVersion/index.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -3478,7 +3478,10 @@ if (!semanticVersionLevel || !Object.keys(versionUpdater.SEMANTIC_VERSION_LEVELS
34783478
console.log(`Invalid input for 'SEMVER_LEVEL': ${semanticVersionLevel}`, `Defaulting to: ${semanticVersionLevel}`);
34793479
}
34803480
const { version: previousVersion } = JSON.parse(fs_1.default.readFileSync('./package.json').toString());
3481-
const newVersion = versionUpdater.incrementVersion(previousVersion, semanticVersionLevel);
3481+
if (!previousVersion) {
3482+
core.setFailed('Error: Could not read package.json');
3483+
}
3484+
const newVersion = versionUpdater.incrementVersion(previousVersion ?? '', semanticVersionLevel);
34823485
console.log(`Previous version: ${previousVersion}`, `New version: ${newVersion}`);
34833486
updateNativeVersions(newVersion);
34843487
console.log(`Setting npm version to ${newVersion}`);

.github/actions/javascript/checkDeployBlockers/checkDeployBlockers.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ const run = function (): Promise<void> {
6060
core.setOutput('HAS_DEPLOY_BLOCKERS', false);
6161
}
6262
})
63-
.catch((error) => {
63+
.catch((error: string | Error) => {
6464
console.error('A problem occurred while trying to communicate with the GitHub API', error);
6565
core.setFailed(error);
6666
});

.github/actions/javascript/getGraphiteString/getGraphiteString.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const run = () => {
2828

2929
// Extract timestamp, Graphite accepts timestamp in seconds
3030
if (current.metadata?.creationDate) {
31-
timestamp = Math.floor(new Date(current.metadata.creationDate).getTime() / 1000);
31+
timestamp = Math.floor(new Date(current.metadata.creationDate as string).getTime() / 1000);
3232
}
3333

3434
if (current.name && current.meanDuration && current.meanCount && timestamp) {
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
import * as core from '@actions/core';
22
import {readFileSync} from 'fs';
3+
import type {PackageJson} from 'type-fest';
34
import * as versionUpdater from '@github/libs/versionUpdater';
45

56
const semverLevel = core.getInput('SEMVER_LEVEL', {required: true});
67
if (!semverLevel || !Object.values<string>(versionUpdater.SEMANTIC_VERSION_LEVELS).includes(semverLevel)) {
78
core.setFailed(`'Error: Invalid input for 'SEMVER_LEVEL': ${semverLevel}`);
89
}
910

10-
const {version: currentVersion} = JSON.parse(readFileSync('./package.json', 'utf8'));
11-
const previousVersion = versionUpdater.getPreviousVersion(currentVersion, semverLevel);
11+
const {version: currentVersion}: PackageJson = JSON.parse(readFileSync('./package.json', 'utf8'));
12+
if (!currentVersion) {
13+
core.setFailed('Error: Could not read package.json');
14+
}
15+
16+
const previousVersion = versionUpdater.getPreviousVersion(currentVersion ?? '', semverLevel);
1217
core.setOutput('PREVIOUS_VERSION', previousVersion);

.github/actions/javascript/getPreviousVersion/index.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -2728,7 +2728,10 @@ if (!semverLevel || !Object.values(versionUpdater.SEMANTIC_VERSION_LEVELS).inclu
27282728
core.setFailed(`'Error: Invalid input for 'SEMVER_LEVEL': ${semverLevel}`);
27292729
}
27302730
const { version: currentVersion } = JSON.parse((0, fs_1.readFileSync)('./package.json', 'utf8'));
2731-
const previousVersion = versionUpdater.getPreviousVersion(currentVersion, semverLevel);
2731+
if (!currentVersion) {
2732+
core.setFailed('Error: Could not read package.json');
2733+
}
2734+
const previousVersion = versionUpdater.getPreviousVersion(currentVersion ?? '', semverLevel);
27322735
core.setOutput('PREVIOUS_VERSION', previousVersion);
27332736

27342737

.github/actions/javascript/reviewerChecklist/reviewerChecklist.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ function checkIssueForCompletedChecklist(numberOfChecklistItems: number) {
9090

9191
getNumberOfItemsFromReviewerChecklist()
9292
.then(checkIssueForCompletedChecklist)
93-
.catch((err) => {
93+
.catch((err: string | Error) => {
9494
console.error(err);
9595
core.setFailed(err);
9696
});

.github/scripts/detectRedirectCycle.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ function detectCycle(): boolean {
5252

5353
fs.createReadStream(`${process.cwd()}/docs/redirects.csv`)
5454
.pipe(parser)
55-
.on('data', (row) => {
55+
.on('data', (row: [string, string]) => {
5656
// Create a directed graph of sourceURL -> targetURL
5757
addEdge(row[0], row[1]);
5858
})

.github/workflows/e2ePerformanceTests.yml

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ on:
1515
type: string
1616
required: true
1717

18+
concurrency:
19+
group: ${{ github.ref == 'refs/heads/main' && format('{0}-{1}', github.ref, github.sha) || github.ref }}-e2e
20+
cancel-in-progress: true
21+
1822
jobs:
1923
buildBaseline:
2024
runs-on: ubuntu-latest-xl

.github/workflows/lint.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ on:
88
paths: ['**.js', '**.ts', '**.tsx', '**.json', '**.mjs', '**.cjs', 'config/.editorconfig', '.watchmanconfig', '.imgbotconfig']
99

1010
concurrency:
11-
group: "${{ github.ref }}-lint"
11+
group: ${{ github.ref == 'refs/heads/main' && format('{0}-{1}', github.ref, github.sha) || github.ref }}-lint
1212
cancel-in-progress: true
1313

1414
jobs:

.github/workflows/test.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ on:
88
paths: ['**.js', '**.ts', '**.tsx', '**.sh', 'package.json', 'package-lock.json']
99

1010
concurrency:
11-
group: "${{ github.ref }}-jest"
11+
group: ${{ github.ref == 'refs/heads/main' && format('{0}-{1}', github.ref, github.sha) || github.ref }}-jest
1212
cancel-in-progress: true
1313

1414
jobs:

.github/workflows/typecheck.yml

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ on:
77
branches-ignore: [staging, production]
88
paths: ['**.js', '**.ts', '**.tsx', 'package.json', 'package-lock.json', 'tsconfig.json']
99

10+
concurrency:
11+
group: ${{ github.ref == 'refs/heads/main' && format('{0}-{1}', github.ref, github.sha) || github.ref }}-typecheck
12+
cancel-in-progress: true
13+
1014
jobs:
1115
typecheck:
1216
if: ${{ github.actor != 'OSBotify' || github.event_name == 'workflow_call' }}

android/app/build.gradle

+12-3
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,21 @@ apply plugin: "com.android.application"
22
apply plugin: "org.jetbrains.kotlin.android"
33
apply plugin: "com.facebook.react"
44
apply plugin: "com.google.firebase.firebase-perf"
5+
apply plugin: "fullstory"
56
apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle"
67

78
/**
89
* This is the configuration block to customize your React Native Android app.
910
* By default you don't need to apply any configuration, just uncomment the lines you need.
1011
*/
12+
13+
/* Fullstory settings */
14+
fullstory {
15+
org 'o-1WN56P-na1'
16+
enabledVariants 'all'
17+
logcatLevel 'debug'
18+
}
19+
1120
react {
1221
/* Folders */
1322
// The root of your project, i.e. where "package.json" lives. Default is '..'
@@ -98,8 +107,8 @@ android {
98107
minSdkVersion rootProject.ext.minSdkVersion
99108
targetSdkVersion rootProject.ext.targetSdkVersion
100109
multiDexEnabled rootProject.ext.multiDexEnabled
101-
versionCode 1001047404
102-
versionName "1.4.74-4"
110+
versionCode 1001047606
111+
versionName "1.4.76-6"
103112
// Supported language variants must be declared here to avoid from being removed during the compilation.
104113
// This also helps us to not include unnecessary language variants in the APK.
105114
resConfigs "en", "es"
@@ -162,7 +171,7 @@ android {
162171
signingConfig null
163172
// buildTypes take precedence over productFlavors when it comes to the signing configuration,
164173
// thus we need to manually set the signing config, so that the e2e uses the debug config again.
165-
// In other words, the signingConfig setting above will be ignored when we build the flavor in release mode.
174+
// In other words, the signingConfig setting above will be ignored when we build the flavor in release mode.
166175
productFlavors.all { flavor ->
167176
// All release builds should be signed with the release config ...
168177
flavor.signingConfig signingConfigs.release

android/build.gradle

+5-1
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,17 @@ buildscript {
2020
repositories {
2121
google()
2222
mavenCentral()
23+
maven {url "https://maven.fullstory.com"}
2324
}
2425
dependencies {
2526
classpath("com.android.tools.build:gradle")
2627
classpath("com.facebook.react:react-native-gradle-plugin")
2728
classpath("com.google.gms:google-services:4.3.4")
2829
classpath("com.google.firebase:firebase-crashlytics-gradle:2.7.1")
2930
classpath("com.google.firebase:perf-plugin:1.4.1")
31+
// Fullstory integration
32+
classpath ("com.fullstory:gradle-plugin-local:1.47.0")
33+
3034
// NOTE: Do not place your application dependencies here; they belong
3135
// in the individual module build.gradle files
3236
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
@@ -70,7 +74,7 @@ allprojects {
7074
// 'mapbox' is the fixed username for Mapbox's Maven repository.
7175
username = 'mapbox'
7276

73-
// The value for password is read from the 'MAPBOX_DOWNLOADS_TOKEN' gradle property.
77+
// The value for password is read from the 'MAPBOX_DOWNLOADS_TOKEN' gradle property.
7478
// Run "npm run setup-mapbox-sdk" to set this property in «USER_HOME»/.gradle/gradle.properties
7579

7680
// Example gradle.properties entry:

assets/images/receipt-plus.svg

+12
Loading

babel.config.js

+11
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,17 @@ const metro = {
3535
['@babel/plugin-proposal-private-property-in-object', {loose: true}],
3636
// The reanimated babel plugin needs to be last, as stated here: https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/installation
3737
'react-native-reanimated/plugin',
38+
39+
/* Fullstory */
40+
'@fullstory/react-native',
41+
[
42+
'@fullstory/babel-plugin-annotate-react',
43+
{
44+
native: true,
45+
setFSTagName: true,
46+
},
47+
],
48+
3849
// Import alias for native devices
3950
[
4051
'module-resolver',

config/webpack/webpack.common.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ const getCommonConfiguration = ({file = '.env', platform = 'web'}: Environment):
9898
{from: 'web/apple-touch-icon.png'},
9999
{from: 'assets/images/expensify-app-icon.svg'},
100100
{from: 'web/manifest.json'},
101-
{from: 'web/gtm.js'},
101+
{from: 'web/thirdPartyScripts.js'},
102102
{from: 'assets/css', to: 'css'},
103103
{from: 'assets/fonts/web', to: 'fonts'},
104104
{from: 'assets/sounds', to: 'sounds'},

contributingGuides/CONTRIBUTING.md

+5-4
Original file line numberDiff line numberDiff line change
@@ -95,18 +95,19 @@ Additionally, if you want to discuss an idea with the open source community with
9595

9696
#### Propose a solution for the job
9797
4. You can propose solutions on any issue at any time, but if you propose solutions to jobs before the `Help Wanted` label is applied, you do so at your own risk. Proposals will not be reviewed until the label is added and there is always a chance that we might not add the label or hire an external contributor for the job.
98-
5. After you reproduce the issue, complete the [proposal template here](./PROPOSAL_TEMPLATE.md) and post it as a comment in the corresponding GitHub issue (linked in the Upwork job).
98+
5. Contributors should **not** submit proposals on issues when they have assigned issues or PRs that are awaiting an action from them. If so, they will be in violation of Rule #1 (Get Shit Done) in our [Code of Conduct](https://github.com/Expensify/App/blob/main/CODE_OF_CONDUCT.md) and will receive a warning. Multiple warnings can lead to removal from the program.
99+
6. After you reproduce the issue, complete the [proposal template here](./PROPOSAL_TEMPLATE.md) and post it as a comment in the corresponding GitHub issue (linked in the Upwork job).
99100
- Note: Before submitting a proposal on an issue, be sure to read any other existing proposals. ALL NEW PROPOSALS MUST BE DIFFERENT FROM EXISTING PROPOSALS. The *difference* should be important, meaningful or considerable.
100-
6. Refrain from leaving additional comments until someone from the Contributor-Plus team and / or someone from Expensify provides feedback on your proposal (do not create a pull request yet).
101+
7. Refrain from leaving additional comments until someone from the Contributor-Plus team and / or someone from Expensify provides feedback on your proposal (do not create a pull request yet).
101102
- Do not leave more than one proposal.
102103
- Do not make extensive changes to your current proposal until after it has been reviewed.
103104
- If you want to make an entirely new proposal or update an existing proposal, please go back and edit your original proposal, then post a new comment to the issue in this format to alert everyone that it has been updated:
104105
```
105106
## Proposal
106107
[Updated](link to proposal)
107108
```
108-
7. If your proposal is accepted by the Expensify engineer assigned to the issue, Expensify will hire you on Upwork and assign the GitHub issue to you.
109-
8. Once hired, post a comment in the Github issue stating when you expect to have your PR ready for review.
109+
8. If your proposal is accepted by the Expensify engineer assigned to the issue, Expensify will hire you on Upwork and assign the GitHub issue to you.
110+
9. Once hired, post a comment in the Github issue stating when you expect to have your PR ready for review.
110111
111112
#### Begin coding your solution in a pull request
112113
9. When you are ready to start, fork the repository and create a new branch.

contributingGuides/STYLE.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ const foo: object = [1, 2, 3]; // TypeScript does not error
242242

243243
If you know that the type of data is an object but don't know what properties or values it has beforehand, use `Record<string, unknown>`.
244244

245-
> Even though `string` is specified as a key, `Record<string, unknown>` type can still accepts objects whose keys are numbers. This is because numbers are converted to strings when used as an object index. Note that you cannot use [symbols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol) for `Record<string, unknown>`.
245+
> Even though `string` is specified as a key, `Record<string, unknown>` type can still accept objects whose keys are numbers. This is because numbers are converted to strings when used as an object index. Note that you cannot use [symbols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol) for `Record<string, unknown>`.
246246
247247
```ts
248248
function logObject(object: Record<string, unknown>) {

desktop/ELECTRON_EVENTS.ts

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ const ELECTRON_EVENTS = {
99
KEYBOARD_SHORTCUTS_PAGE: 'keyboard-shortcuts-page',
1010
START_UPDATE: 'start-update',
1111
UPDATE_DOWNLOADED: 'update-downloaded',
12+
DOWNLOAD: 'download',
13+
DOWNLOAD_COMPLETED: 'download-completed',
14+
DOWNLOAD_FAILED: 'download-started',
15+
DOWNLOAD_CANCELED: 'download-canceled',
1216
SILENT_UPDATE: 'silent-update',
1317
} as const;
1418

desktop/contextBridge.ts

+11-2
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,19 @@ const WHITELIST_CHANNELS_RENDERER_TO_MAIN = [
1616
ELECTRON_EVENTS.REQUEST_VISIBILITY,
1717
ELECTRON_EVENTS.START_UPDATE,
1818
ELECTRON_EVENTS.LOCALE_UPDATED,
19+
ELECTRON_EVENTS.DOWNLOAD,
1920
ELECTRON_EVENTS.SILENT_UPDATE,
2021
] as const;
2122

22-
const WHITELIST_CHANNELS_MAIN_TO_RENDERER = [ELECTRON_EVENTS.KEYBOARD_SHORTCUTS_PAGE, ELECTRON_EVENTS.UPDATE_DOWNLOADED, ELECTRON_EVENTS.FOCUS, ELECTRON_EVENTS.BLUR] as const;
23+
const WHITELIST_CHANNELS_MAIN_TO_RENDERER = [
24+
ELECTRON_EVENTS.KEYBOARD_SHORTCUTS_PAGE,
25+
ELECTRON_EVENTS.UPDATE_DOWNLOADED,
26+
ELECTRON_EVENTS.FOCUS,
27+
ELECTRON_EVENTS.BLUR,
28+
ELECTRON_EVENTS.DOWNLOAD_COMPLETED,
29+
ELECTRON_EVENTS.DOWNLOAD_FAILED,
30+
ELECTRON_EVENTS.DOWNLOAD_CANCELED,
31+
] as const;
2332

2433
const getErrorMessage = (channel: string): string => `Electron context bridge cannot be used with channel '${channel}'`;
2534

@@ -67,7 +76,7 @@ contextBridge.exposeInMainWorld('electron', {
6776
}
6877

6978
// Deliberately strip event as it includes `sender`
70-
ipcRenderer.on(channel, (event, ...args) => func(...args));
79+
ipcRenderer.on(channel, (event, ...args: unknown[]) => func(...args));
7180
},
7281

7382
/** Remove listeners for a single channel from the main process and sent to the renderer process. */

0 commit comments

Comments
 (0)