diff --git a/android/app/build.gradle b/android/app/build.gradle
index 998a43a8c3ff..21b152277957 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -2,20 +2,12 @@ apply plugin: "com.android.application"
apply plugin: "org.jetbrains.kotlin.android"
apply plugin: "com.facebook.react"
apply plugin: "com.google.firebase.firebase-perf"
-apply plugin: "fullstory"
apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle"
/**
* This is the configuration block to customize your React Native Android app.
* By default you don't need to apply any configuration, just uncomment the lines you need.
*/
-
-/* Fullstory settings */
-fullstory {
- org 'o-1WN56P-na1'
- enabledVariants 'all'
-}
-
react {
/* Folders */
// The root of your project, i.e. where "package.json" lives. Default is '..'
@@ -106,8 +98,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1001046500
- versionName "1.4.65-0"
+ versionCode 1001046505
+ versionName "1.4.65-5"
// Supported language variants must be declared here to avoid from being removed during the compilation.
// This also helps us to not include unnecessary language variants in the APK.
resConfigs "en", "es"
@@ -170,7 +162,7 @@ android {
signingConfig null
// buildTypes take precedence over productFlavors when it comes to the signing configuration,
// thus we need to manually set the signing config, so that the e2e uses the debug config again.
- // In other words, the signingConfig setting above will be ignored when we build the flavor in release mode.
+ // In other words, the signingConfig setting above will be ignored when we build the flavor in release mode.
productFlavors.all { flavor ->
// All release builds should be signed with the release config ...
flavor.signingConfig signingConfigs.release
diff --git a/android/build.gradle b/android/build.gradle
index 7ecd482b38f0..10600480d8bb 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -20,7 +20,6 @@ buildscript {
repositories {
google()
mavenCentral()
- maven {url "https://maven.fullstory.com"}
}
dependencies {
classpath("com.android.tools.build:gradle")
@@ -28,9 +27,6 @@ buildscript {
classpath("com.google.gms:google-services:4.3.4")
classpath("com.google.firebase:firebase-crashlytics-gradle:2.7.1")
classpath("com.google.firebase:perf-plugin:1.4.1")
- // Fullstory integration
- classpath ("com.fullstory:gradle-plugin-local:1.45.1")
-
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
@@ -74,7 +70,7 @@ allprojects {
// 'mapbox' is the fixed username for Mapbox's Maven repository.
username = 'mapbox'
- // The value for password is read from the 'MAPBOX_DOWNLOADS_TOKEN' gradle property.
+ // The value for password is read from the 'MAPBOX_DOWNLOADS_TOKEN' gradle property.
// Run "npm run setup-mapbox-sdk" to set this property in «USER_HOME»/.gradle/gradle.properties
// Example gradle.properties entry:
diff --git a/assets/images/all.svg b/assets/images/all.svg
new file mode 100644
index 000000000000..d1a833d280ce
--- /dev/null
+++ b/assets/images/all.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/babel.config.js b/babel.config.js
index 0660cdb452fb..9f8b7a711d78 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -14,23 +14,6 @@ const defaultPlugins = [
// source code transformation as we do not use class property assignment.
'transform-class-properties',
- /* Fullstory */
- [
- '@fullstory/react-native',
- {
- version: '1.4.0',
- org: 'o-1WN56P-na1',
- enabledVariants: 'all',
- },
- ],
- [
- '@fullstory/babel-plugin-annotate-react',
- {
- native: true,
- setFSTagName: true,
- },
- ],
-
// Keep it last
'react-native-reanimated/plugin',
];
diff --git a/config/webpack/webpack.common.ts b/config/webpack/webpack.common.ts
index 9d397b9557a3..7cafafca9973 100644
--- a/config/webpack/webpack.common.ts
+++ b/config/webpack/webpack.common.ts
@@ -98,7 +98,7 @@ const getCommonConfiguration = ({file = '.env', platform = 'web'}: Environment):
{from: 'web/apple-touch-icon.png'},
{from: 'assets/images/expensify-app-icon.svg'},
{from: 'web/manifest.json'},
- {from: 'web/thirdPartyScripts.js'},
+ {from: 'web/gtm.js'},
{from: 'assets/css', to: 'css'},
{from: 'assets/fonts/web', to: 'fonts'},
{from: 'assets/sounds', to: 'sounds'},
diff --git a/contributingGuides/ACCESSIBILITY.md b/contributingGuides/ACCESSIBILITY.md
index b94cbf3087c8..ff73eaf2942e 100644
--- a/contributingGuides/ACCESSIBILITY.md
+++ b/contributingGuides/ACCESSIBILITY.md
@@ -19,9 +19,9 @@ When implementing pressable components, it's essential to create accessible flow
- ensure that after performing press focus is set on the correct next element - this is especially important for keyboard users who rely on focus to navigate the app. All Pressable components have a `nextFocusRef` prop that can be used to set the next focusable element after the pressable component. This prop accepts a ref to the next focusable element. For example, if you have a button that opens a modal, you can set the next focus to the first focusable element in the modal. This way, when the user presses the button, focus will be set on the first focusable element in the modal, and the user can continue navigating the modal using the keyboard.
-- size of any pressable component should be at least 44x44dp. This is the minimum size recommended by Apple and Google for touch targets. If the pressable component is smaller than `44x44dp`, it will be difficult for users with motor disabilities to interact with it. Pressable components have a `autoHitSlop` prop that can be used to automatically increase the size of the pressable component to `44x44dp`. This prop accepts a boolean value. If set to true, the pressable component will automatically increase its touchable size to 44x44dp. If set to false, the pressable component will not increase its size. By default, this prop is set to false.
+- size of any pressable component should be at least 44x44dp. This is the minimum size recommended by Apple and Google for touch targets. If the pressable component is smaller than `44x44dp`, it will be difficult for users with motor disabilities to interact with it. Pressable components have a `autoHitSlop` prop that can be used to automatically increase the size of the pressable component to `44x44dp`. This prop accepts a boolean value. If set to true, the pressable component will automatically increase its touchable size to `44x44dp`. If set to false, the pressable component will not increase its size. By default, this prop is set to false.
-- ensure that the pressable component has a label and hint. This is especially important for users with visual disabilities who rely on screen readers to navigate the app. All Pressable components have a `accessibilitylabel` prop that can be used to set the label of the pressable component. This prop accepts a string value. All Pressable components also have a `accessibilityHint` prop that can be used to set the hint of the pressable component. This prop accepts a string value. The accessibilityHint prop is optional. If not set, the pressable component will fallback to the accessibilityLabel prop. For example, if you have a button that opens a modal, you can set the accessibilityLabel to "Open modal" and the accessibilityHint to "Opens a modal with more information". This way, when the user focuses on the button, the screen reader will read "Open modal. Opens a modal with more information". This will help the user understand what the button does and what to expect after pressing it.
+- ensure that the pressable component has a label and hint. This is especially important for users with visual disabilities who rely on screen readers to navigate the app. All Pressable components have a `accessibilitylabel` prop that can be used to set the label of the pressable component. This prop accepts a string value. All Pressable components also have a `accessibilityHint` prop that can be used to set the hint of the pressable component. This prop accepts a string value. The `accessibilityHint` prop is optional. If not set, the pressable component will fallback to the `accessibilityLabel` prop. For example, if you have a button that opens a modal, you can set the `accessibilityLabel` to "Open modal" and the `accessibilityHint` to "Opens a modal with more information". This way, when the user focuses on the button, the screen reader will read "Open modal. Opens a modal with more information". This will help the user understand what the button does and what to expect after pressing it.
- the `enableInScreenReaderStates` prop proves invaluable when aiming to enhance the accessibility of clickable elements, particularly when desiring to enlarge the clickable area of a component, such as an entire row. This can be especially useful, for instance, when dealing with tables where only a small portion of a row, like a checkbox, might traditionally trigger an action. By employing this prop, developers can ensure that the entirety of a designated component, in this case a row, is made accessible to users employing screen readers. This creates a more inclusive user experience, allowing individuals relying on screen readers to interact with the component effortlessly. For instance, in a table, using this prop on a row component enables users to click anywhere within the row to trigger an action, significantly improving accessibility and user-friendliness.
diff --git a/contributingGuides/API.md b/contributingGuides/API.md
index aea684417e41..cf17b4093244 100644
--- a/contributingGuides/API.md
+++ b/contributingGuides/API.md
@@ -8,11 +8,11 @@ These are best practices related to the current API used for App.
- Data is pushed to the client and put straight into Onyx by low-level libraries.
- Clients should be kept up-to-date with many small incremental changes to data.
- Creating data needs to be optimistic on every connection (offline, slow 3G, etc), eg. `RequestMoney` or `SplitBill` should work without waiting for a server response.
-- For new objects created from the client (reports, reportActions, policies) we're going to generate a random string ID immediately on the client, rather than needing to wait for the server to give us an ID for the created object.
+- For new objects created from the client (reports, reportActions, policies), we're going to generate a random string ID immediately on the client, rather than needing to wait for the server to give us an ID for the created object.
- This client-generated ID will become the primary key for that record in the database. This will provide more offline functionality than was previously possible.
## Response Handling
-When the web server responds to an API call the response is sent to the server in one of two ways.
+When the web server responds to an API call, the response is sent to the server in one of two ways.
1. **HTTPS Response** - Data that is returned with the HTTPS response is only sent to the client that initiated the request.
The network library will look for any `onyxData` in the response and send it straight to `Onyx.update(response.onyxData)`.
@@ -20,31 +20,38 @@ When the web server responds to an API call the response is sent to the server i
1. **Pusher Event** (web socket) - Data returned with a Pusher event is sent to all currently connected clients for the user that made the request, as well as any other necessary participants (eg. like other people in the chat)
Pusher listens for an `onyxApiUpdate` event and sends the data straight to `Onyx.update(pushJSON)`.
+
### READ Responses
This is a response that returns data from the database.
-A READ response is very specific to the client making the request, so it's data is returned with the **HTTPS Response**. This prevents a lot of unnecessary data from being sent to other clients that will never use it.
+A READ response is very specific to the client making the request, so its data is returned with the **HTTPS Response**. This prevents a lot of unnecessary data from being sent to other clients that will never use it.
In PHP, the response is added like this:
+
```php
$response['onyxData'][] = blahblahblah;
```
+
The data will be returned with the HTTPS response.
+
### WRITE Responses
This response happens when new data is created in the database.
New data (`jsonCode===200`) should be sent to all connected clients so a **Pusher Event** is used to update another currently connected clients with the new data that was created.
In PHP, the response is added like this:
+
```php
$onyxUpdate[] = blahblahblah;
```
+
The data will automatically be sent to the user via Pusher.
#### WRITE Response Errors
When there is an error on a WRITE response (`jsonCode!==200`), the error must come back to the client on the HTTPS response. The error is only relevant to the client that made the request and it wouldn't make sense to send it out to all connected clients.
Error messages should be returned and stored as an object under the `errors` property, keyed by an integer [microtime](https://github.com/Expensify/Web-Expensify/blob/25d056c9c531ea7f12c9bf3283ec554dd5d1d316/lib/Onyx.php#L148-L154). It's also common to store errors keyed by microtime under `errorFields.fieldName`. Use this format when error messages should be saved on a general object but are only relevant to a specific field / key on the object. If absolutely needed, additional error properties can be stored under other, more specific fields that sit at the same level as `errors`:
+
```php
[
'onyxMethod' => Onyx::METHOD_MERGE,
diff --git a/contributingGuides/APPLE_GOOGLE_SIGNIN.md b/contributingGuides/APPLE_GOOGLE_SIGNIN.md
index e6b999b7cb01..08a444a6b8e4 100644
--- a/contributingGuides/APPLE_GOOGLE_SIGNIN.md
+++ b/contributingGuides/APPLE_GOOGLE_SIGNIN.md
@@ -41,7 +41,7 @@ The redirect URI must match a URI in the Google or Apple client ID configuration
Pop-up mode opens a pop-up window to show the third-party sign-in form. But it also changes how tokens are given to the client app. Instead of an HTTPS request, they are returned by the JS libraries in memory, either via a callback (Google) or a promise (Apple).
-Apple and Google both check that the client app is running on an allowed domain. The sign-in process will fail otherwise. Google allows localhost, but Apple does not, and so testing Apple in development environments requires hosting the client app on a domain that the Apple client ID (or "service ID", in Apple's case) has been configured with.
+Apple and Google both check that the client app is running on an allowed domain. The sign-in process will fail otherwise. Google allows `localhost`, but Apple does not, and so testing Apple in development environments requires hosting the client app on a domain that the Apple client ID (or "service ID", in Apple's case) has been configured with.
In the case of Apple, sometimes it will silently fail at the very end of the sign-in process, where the only sign that something is wrong is that the pop-up fails to close. In this case, it's very likely that configuration mismatch is the issue.
@@ -125,24 +125,24 @@ If you need to check that you received the correct data, check it on [jwt.io](ht
Hardcode this token into `Session.beginAppleSignIn`, and but also verify a valid token was passed into the function, for example:
-```
+```js
function beginAppleSignIn(idToken) {
-+ // Show that a token was passed in, without logging the token, for privacy
-+ window.alert(`ORIGINAL ID TOKEN LENGTH: ${idToken.length}`);
-+ const hardcodedToken = '...';
+ // Show that a token was passed in, without logging the token, for privacy
+ window.alert(`ORIGINAL ID TOKEN LENGTH: ${idToken.length}`);
+ const hardcodedToken = '...';
const {optimisticData, successData, failureData} = signInAttemptState();
-+ API.write('SignInWithApple', {idToken: hardcodedToken}, {optimisticData, successData, failureData});
-- API.write('SignInWithApple', {idToken}, {optimisticData, successData, failureData});
+ API.write('SignInWithApple', {idToken: hardcodedToken}, {optimisticData, successData, failureData});
+ API.write('SignInWithApple', {idToken}, {optimisticData, successData, failureData});
}
```
#### Configure the SSH tunneling
-You can use any SSH tunneling service that allows you to configure custom subdomains so that we have a consistent address to use. We'll use ngrok in these examples, but ngrok requires a paid account for this. If you need a free option, try serveo.net.
+You can use any SSH tunneling service that allows you to configure custom subdomains so that we have a consistent address to use. We'll use ngrok in these examples, but ngrok requires a paid account for this. If you need a free option, try [serveo.net](https://serveo.net).
After you've set ngrok up to be able to run on your machine (requires configuring a key with the command line tool, instructions provided by the ngrok website after you create an account), test hosting the web app on a custom subdomain. This example assumes the development web app is running at `dev.new.expensify.com:8082`:
-```
+```shell
ngrok http 8082 --host-header="dev.new.expensify.com:8082" --subdomain=mysubdomain
```
@@ -183,7 +183,7 @@ Desktop will require the same configuration, with these additional steps:
#### Configure web app URL in .env
-Add `NEW_EXPENSIFY_URL` to .env, and set it to the HTTPS URL where the web app can be found, for example:
+Add `NEW_EXPENSIFY_URL` to `.env`, and set it to the HTTPS URL where the web app can be found, for example:
```
NEW_EXPENSIFY_URL=https://subdomain.ngrok-free.app
@@ -195,7 +195,7 @@ Note that changing this value to a domain that isn't configured for use with Exp
#### Set Environment to something other than "Development"
-The DeepLinkWrapper component will not handle deep links in the development environment. To be able to test deep linking, you must set the environment to something other than "Development".
+The `DeepLinkWrapper` component will not handle deep links in the development environment. To be able to test deep linking, you must set the environment to something other than "Development".
Within the `.env` file, set `envName` to something other than "Development", for example:
@@ -203,7 +203,7 @@ Within the `.env` file, set `envName` to something other than "Development", for
envName=Staging
```
-Alternatively, within the `DeepLinkWrapper/index.website.js` file you can set the `CONFIG.ENVIRONMENT` to something other than "Development".
+Alternatively, within the `DeepLinkWrapper/index.website.js` file, you can set the `CONFIG.ENVIRONMENT` to something other than "Development".
#### Handle deep links in dev on MacOS
@@ -211,7 +211,7 @@ If developing on MacOS, the development desktop app can't handle deeplinks corre
1. Create a "real" build of the desktop app, which can handle deep links, open the build folder, and install the dmg there:
-```
+```shell
npm run desktop-build
open desktop-build
# Then double-click "NewExpensify.dmg" in Finder window
diff --git a/contributingGuides/CONTRIBUTING.md b/contributingGuides/CONTRIBUTING.md
index 25f54c668b24..13f7592b65e1 100644
--- a/contributingGuides/CONTRIBUTING.md
+++ b/contributingGuides/CONTRIBUTING.md
@@ -34,7 +34,7 @@ At this time, we are not hiring contractors in Crimea, North Korea, Russia, Iran
## Slack channels
All contributors should be a member of a shared Slack channel called [#expensify-open-source](https://expensify.slack.com/archives/C01GTK53T8Q) -- this channel is used to ask **general questions**, facilitate **discussions**, and make **feature requests**.
-Before requesting an invite to Slack please ensure your Upwork account is active, since we only pay via Upwork (see [below](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#payment-for-contributions)). To request an invite to Slack, email contributors@expensify.com with the subject `Slack Channel Invites`. We'll send you an invite!
+Before requesting an invite to Slack, please ensure your Upwork account is active, since we only pay via Upwork (see [below](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#payment-for-contributions)). To request an invite to Slack, email contributors@expensify.com with the subject `Slack Channel Invites`. We'll send you an invite!
Note: Do not send direct messages to the Expensify team in Slack or Expensify Chat, they will not be able to respond.
@@ -44,7 +44,7 @@ Note: if you are hired for an Upwork job and have any job-specific questions, pl
If you've found a vulnerability, please email security@expensify.com with the subject `Vulnerability Report` instead of creating an issue.
## Payment for Contributions
-We hire and pay external contributors via Upwork.com. If you'd like to be paid for contributing, please create an Upwork account, apply for an available job in [GitHub](https://github.com/Expensify/App/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22), and finally apply for the job in Upwork once your proposal gets selected in GitHub. Please make sure your Upwork profile is **fully verified** before applying, otherwise you run the risk of not being paid. If you think your compensation should be increased for a specific job, you can request a reevaluation by commenting in the Github issue where the Upwork job was posted.
+We hire and pay external contributors via [Upwork.com](https://www.upwork.com). If you'd like to be paid for contributing, please create an Upwork account, apply for an available job in [GitHub](https://github.com/Expensify/App/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22), and finally apply for the job in Upwork once your proposal gets selected in GitHub. Please make sure your Upwork profile is **fully verified** before applying, otherwise you run the risk of not being paid. If you think your compensation should be increased for a specific job, you can request a reevaluation by commenting in the Github issue where the Upwork job was posted.
Payment for your contributions will be made no less than 7 days after the pull request is deployed to production to allow for [regression](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#regressions) testing. If you have not received payment after 8 days of the PR being deployed to production, and there are no [regressions](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#regressions), please add a comment to the issue mentioning the BugZero team member (Look for the melvin-bot "Triggered auto assignment to... (`Bug`)" to see who this is).
@@ -75,10 +75,10 @@ This is the most common scenario for contributors. The Expensify team posts new
>**Solution:** Start up time will perceptibly decrease by 1042ms if we prevent the unnecessary re-renders of this component.
## Working on Expensify Jobs
-*Reminder: For technical guidance please refer to the [README](https://github.com/Expensify/App/blob/main/README.md)*.
+*Reminder: For technical guidance, please refer to the [README](https://github.com/Expensify/App/blob/main/README.md)*.
## Posting Ideas
-Additionally if you want to discuss an idea with the open source community without having a P/S statement yet, you can post it in #expensify-open-source with the prefix `IDEA:`. All ideas to build the future of Expensify are always welcome! i.e.: "`IDEA:` I don't have a P/S for this yet, but just kicking the idea around... what if we [insert crazy idea]?".
+Additionally, if you want to discuss an idea with the open source community without having a P/S statement yet, you can post it in #expensify-open-source with the prefix `IDEA:`. All ideas to build the future of Expensify are always welcome! i.e.: "`IDEA:` I don't have a P/S for this yet, but just kicking the idea around... what if we [insert crazy idea]?".
#### Make sure you can test on all platforms
* Expensify requires that you can test the app on iOS, MacOS, Android, Web, and mWeb.
@@ -147,7 +147,7 @@ Additionally if you want to discuss an idea with the open source community witho
#### Timeline expectations and asking for help along the way
- If you have made a change to your pull request and are ready for another review, leave a comment that says "Updated" on the pull request itself.
- Please keep the conversation in GitHub, and do not ping individual reviewers in Slack or Upwork to get their attention.
-- Pull Request reviews can sometimes take a few days. If your pull request has not been addressed after four days please let us know via the #expensify-open-source Slack channel.
+- Pull Request reviews can sometimes take a few days. If your pull request has not been addressed after four days, please let us know via the #expensify-open-source Slack channel.
- On occasion, our engineers will need to focus on a feature release and choose to place a hold on the review of your PR.
#### Important note about JavaScript Style
diff --git a/contributingGuides/FORMS.md b/contributingGuides/FORMS.md
index b2f912277dc5..e53be6f6b269 100644
--- a/contributingGuides/FORMS.md
+++ b/contributingGuides/FORMS.md
@@ -5,7 +5,8 @@ This document lists specific guidelines for using our Form component and general
## General Form UI/UX
### Inputs
-Any form input needs to be wrapped in [InputWrapper](https://github.com/Expensify/App/blob/029d009731dcd3c44cd1321672b9672ef0d3d7d9/src/components/Form/InputWrapper.js) and passed as `InputComponent` property additionally it's necessary po pass an unique `inputID`. All other props of the input can be passed as `InputWrapper` props.
+Any form input needs to be wrapped in [InputWrapper](https://github.com/Expensify/App/blob/029d009731dcd3c44cd1321672b9672ef0d3d7d9/src/components/Form/InputWrapper.js) and passed as `InputComponent` property, additionally, it's necessary to pass an unique `inputID`. All other props of the input can be passed as `InputWrapper` props.
+
```jsx
```
-We also have [keyboardType](https://github.com/Expensify/App/blob/9418b870515102631ea2156b5ea253ee05a98ff1/src/CONST.js#L760-L763) and should be used for specific use cases when there is no `inputMode` equivalent of the value exist. and should be used like so:
+We also have [keyboardType](https://github.com/Expensify/App/blob/9418b870515102631ea2156b5ea253ee05a98ff1/src/CONST.js#L760-L763) and should be used for specific use cases when there is no `inputMode` equivalent of the value exist, and should be used like so:
```jsx
` is inside a `` we will want to disable the default safe area padding applied there e.g.
+Any `FormProvider.js` that has a button will also add safe area padding by default. If the `` is inside a ``, we will want to disable the default safe area padding applied there e.g.
-```js
+```jsx
{...}
diff --git a/contributingGuides/HOW_TO_BECOME_A_CONTRIBUTOR_PLUS.md b/contributingGuides/HOW_TO_BECOME_A_CONTRIBUTOR_PLUS.md
index e7dcf5404c34..a72af88ae00b 100644
--- a/contributingGuides/HOW_TO_BECOME_A_CONTRIBUTOR_PLUS.md
+++ b/contributingGuides/HOW_TO_BECOME_A_CONTRIBUTOR_PLUS.md
@@ -6,7 +6,7 @@ C+ are contributors who are experienced at working with Expensify and have gaine
## Why would someone want to be a C+
- C+ are compensated the same price as the contributor for reviewing proposals and the associated PR. (ie. if a job is listed at $1000, that’s how much the C+ will make if they review both the proposals and PR). If regressions are found that should have* been caught after the PR has been approved, C+ payment is reduced by 50% for each regression found.
- * Should have = C+ should have caught the bug by fully following the PR checklist. If C+ skips a step or completed the checklist incompletely, payment will be cut in half.
-- C+ can also work on jobs as a contributor
+- C+ can also work on jobs as a contributor.
- Earning potential is variable, it depends on how much a C+ wants to work and other jobs they’re hired for. We’ve seen C+ make ~$100k/year.
- There isn’t a set number of hours a C+ needs to work in a week. Proposals and PRs reviews are expected to be addressed within 24 hours on weekdays.
- Dedicated #contributor-plus Slack room to discuss issues, processes and proposals.
diff --git a/contributingGuides/HOW_TO_BUILD_APP_ON_PHYSICAL_IOS_DEVICE.md b/contributingGuides/HOW_TO_BUILD_APP_ON_PHYSICAL_IOS_DEVICE.md
index a62939aebc25..0f90d71d52ea 100644
--- a/contributingGuides/HOW_TO_BUILD_APP_ON_PHYSICAL_IOS_DEVICE.md
+++ b/contributingGuides/HOW_TO_BUILD_APP_ON_PHYSICAL_IOS_DEVICE.md
@@ -11,7 +11,7 @@
> [!Important]
> You must have a Apple Developer account to run your app on a physical device. If you don't have one, you can register here: [Apple Developer Program](https://developer.apple.com/).
- 2.1. Go to `Signing and Capabilities` then in the section called `Signing (Debug/Development and Release/Development)`
+ 2.1. Go to `Signing and Capabilities`, then in the section called `Signing (Debug/Development and Release/Development)`
![Step 2.1 Screenshot](https://github.com/Expensify/App/assets/104348397/4c668612-ab29-4a91-8e2d-a146e2940017)
@@ -24,7 +24,7 @@
![Step 2.4 Screenshot](https://github.com/Expensify/App/assets/104348397/4ce3f250-4b7c-4e7c-9f1d-09df7bdfc5e0)
> [!Note]
->Please be aware that the app built with your own bundle id doesn't support authenticated services like push notification, apple signin, deeplinking etc. which should be only available in Expensify developer account.
+>Please be aware that the app built with your own bundle id doesn't support authenticated services like push notification, Apple signin, deeplinking etc. which should be only available in Expensify developer account.
2.5. Scroll down and Remove Associated Domains, Communication Notifications, Push Notifications, and Sign In With Apple capabilities
diff --git a/contributingGuides/HOW_TO_CREATE_A_PLAN.md b/contributingGuides/HOW_TO_CREATE_A_PLAN.md
index 28ebf1502e71..4f1b2e78a650 100644
--- a/contributingGuides/HOW_TO_CREATE_A_PLAN.md
+++ b/contributingGuides/HOW_TO_CREATE_A_PLAN.md
@@ -34,7 +34,7 @@ Once you have your solutions, it’s time to decide on the preferred solution (a
- Future-proofing - does one solution have more longevity or pave the way for future development?
- Independence - does a solution rely on a different problem to be solved first? Does it rely on another piece to be done later?
-If you are finding the solution to be difficult, go back and beat harder on the problem to break it up into smaller pieces. Keep repeating until you have a general list of prioritized stages, with early stages solving the dependencies required by later stages, each of which is extremely well defined, with a reasonably obvious preferred solution.
+If you are finding the solution to be difficult, go back and beat harder on the problem to break it up into smaller pieces. Keep repeating until you have a general list of prioritized stages, with early stages solving the dependencies required by later stages, each of which is extremely well-defined, with a reasonably obvious preferred solution.
## Step 3: Write out each problem and solution (P/S statement)
Have a trusted peer or two proof your P/S statement and help you ensure it is well-defined. If you're in need of a peer to proof, post in #expensify-open-source to ask for help. Refine it and then share with another peer or two until you have a clear, understandable P/S statement. The more complex the problem and solution, the more people should review it. Keep going back to step 1 if needed.
diff --git a/contributingGuides/KSv2.md b/contributingGuides/KSv2.md
index 881d191ad886..44b1f5b36fe8 100644
--- a/contributingGuides/KSv2.md
+++ b/contributingGuides/KSv2.md
@@ -24,13 +24,13 @@ In the dashboard, you can first see the PRs assigned to you as `Reviewer`. As pa
### Issues assigned to you
-In the next section you can see all issues assigned to you, prioritized from most urgent (on the left) to least urgent (on the right). Issues will also change color depending on other factors - e.g. if they have "HOLD" in the title or if they have the `Overdue`, `Planning`, or `Waiting for copy` labels applied.
+In the next section, you can see all issues assigned to you, prioritized from most urgent (on the left) to least urgent (on the right). Issues will also change color depending on other factors - e.g. if they have "HOLD" in the title or if they have the `Overdue`, `Planning`, or `Waiting for copy` labels applied.
If a GitHub issue has the `Overdue` label, the text will be red. This means that the issue hasn't been updated in the amount of time allotted for an update (ex - A weekly issue becomes overdue if it hasn't been updated in a week).
### Your Pull Requests
-After the issues section you will find a section that lists the PRs you've created.
+After the issues section, you will find a section that lists the PRs you've created.
diff --git a/contributingGuides/NAVIGATION.md b/contributingGuides/NAVIGATION.md
index 5bb6dfb85851..6c0a5b460654 100644
--- a/contributingGuides/NAVIGATION.md
+++ b/contributingGuides/NAVIGATION.md
@@ -4,13 +4,13 @@ The navigation in the App consists of a top-level Stack Navigator (called `RootS
## Terminology
-`RHP` - Right hand panel that shows content inside a dismissible modal that takes up a partial portion of the screen on large format devices e.g. desktop/web/tablets. On smaller screens the content shown in the RHP fills the entire screen.
+`RHP` - Right hand panel that shows content inside a dismissible modal that takes up a partial portion of the screen on large format devices e.g. desktop/web/tablets. On smaller screens, the content shown in the RHP fills the entire screen.
Navigation Actions - User actions correspond to resulting navigation actions that we will define now. The navigation actions are: `Back`, `Up`, `Dismiss`, `Forward` and `Push`.
- `Back` - Moves the user “back” in the history stack by popping the screen or stack of screens. Note: This can potentially make the user exit the app itself (native) or display a previous app (not Expensify), or just the empty state of the browser.
-- `Up` - Pops the current screen off the current stack. This action is very easy to confuse with `Back`. Unless you’ve navigated from one screen to a nested screen in a stack of screens these actions will almost always be the same. Unlike a “back” action, “up” should never result in the user exiting the app and should only be an option if there is somewhere to go “up” to.
+- `Up` - Pops the current screen off the current stack. This action is very easy to confuse with `Back`. Unless you’ve navigated from one screen to a nested screen in a stack of screens, these actions will almost always be the same. Unlike a “back” action, “up” should never result in the user exiting the app and should only be an option if there is somewhere to go “up” to.
- `Dismiss` - Closes any modals (outside the navigation hierarchy) or pops a nested stack of screens off returning the user to the previous screen in the main stack.
@@ -26,7 +26,7 @@ Most of the time, if you want to add some of the flows concerning one of your re
- If you want to create new flow, add a `Screen` in `RightModalNavigator.tsx` and make new modal in `ModalStackNavigators.tsx` with chosen pages.
-When creating RHP flows, you have to remember a couple things:
+When creating RHP flows, you have to remember a couple of things:
- Since you can deeplink to different pages inside the RHP navigator, it is important to provide the possibility for the user to properly navigate back from any page with UP press (`HeaderWithBackButton` component).
@@ -87,7 +87,7 @@ Using [react-freeze](https://github.com/software-mansion/react-freeze) allows us
- The wide layout is rendered with our custom `ThreePaneView.js` and the narrow layout is rendered with `StackView` from `@react-navigation/stack`
-- To make sure that we have the correct navigation state after changing the layout we need to force react to create new instance of the `NavigationContainer`. Without this, the navigation state could be broken after changing the type of layout.
+- To make sure that we have the correct navigation state after changing the layout, we need to force react to create new instance of the `NavigationContainer`. Without this, the navigation state could be broken after changing the type of layout.
- We are getting the new instance by changing the `key` prop of `NavigationContainer` that depends on the `isSmallScreenWidth`.
@@ -115,10 +115,11 @@ Broken behavior is the outcome of two things:
The reason why `getActionFromState` provided by `react-navigation` is dispatched at the top level of the navigation hierarchy is that it doesn't know about current navigation state, only about desired one.
-In this example it doesn't know if the `RightModalNavigator` and `Settings` are already mounted.
+In this example, it doesn't know if the `RightModalNavigator` and `Settings` are already mounted.
-The action for the first step looks like that:
+The action for the first step looks like that:
+
```json
{
"type": "NAVIGATE",
@@ -193,7 +194,7 @@ If we can create simple action that will only push one screen to the existing na
The `getMinimalAction` compares action generated by the `getActionFromState` with the current navigation state and tries to find the smallest action possible.
-The action for the first step created with `getMinimalAction` looks like this:
+The action for the first step created with `getMinimalAction` looks like this:
```json
{
diff --git a/contributingGuides/OFFLINE_UX.md b/contributingGuides/OFFLINE_UX.md
index cd45bebdce4b..9fefbaeea111 100644
--- a/contributingGuides/OFFLINE_UX.md
+++ b/contributingGuides/OFFLINE_UX.md
@@ -60,7 +60,7 @@ This is the pattern where we queue the request to be sent when the user is onlin
- the user should be given instant feedback and
- the user does not need to know when the change is done on the server in the background
-**How to implement:** Use [`API.write()`](https://github.com/Expensify/App/blob/3493f3ca3a1dc6cdbf9cb8bd342866fcaf45cf1d/src/libs/API.js#L7-L28) to implement this pattern. For this pattern we should only put `optimisticData` in the options. We don't need `successData` or `failureData` as we don't care what response comes back at all.
+**How to implement:** Use [`API.write()`](https://github.com/Expensify/App/blob/3493f3ca3a1dc6cdbf9cb8bd342866fcaf45cf1d/src/libs/API.js#L7-L28) to implement this pattern. For this pattern, we should only put `optimisticData` in the options. We don't need `successData` or `failureData` as we don't care what response comes back at all.
**Example:** Pinning a chat.
@@ -77,7 +77,7 @@ When the user is offline:
**How to implement:**
- Use API.write() to implement this pattern
- Optimistic data should include `pendingAction` ([with these possible values](https://github.com/Expensify/App/blob/15f7fa622805ee2971808d6bc67181c4715f0c62/src/CONST.js#L775-L779))
-- To ensure the UI is shown as described above, you should enclose the components that contain the data that was added/updated/deleted with the OfflineWithFeedback component
+- To ensure the UI is shown as described above, you should enclose the components that contain the data that was added/updated/deleted with the `OfflineWithFeedback` component
- Include this data in the action call:
- `optimisticData` - always include this object when using the Pattern B
- `successData` - include this if the action is `update` or `delete`. You do not have to include this if the action is `add` (same data was already passed using the `optimisticData` object)
@@ -86,9 +86,9 @@ When the user is offline:
**Handling errors:**
- The [OfflineWithFeedback component](https://github.com/Expensify/App/blob/main/src/components/OfflineWithFeedback.js) already handles showing errors too, as long as you pass the error field in the [errors prop](https://github.com/Expensify/App/blob/128ea378f2e1418140325c02f0b894ee60a8e53f/src/components/OfflineWithFeedback.js#L29-L31)
-- When dismissing the error, the onClose prop will be called, there we need to call an action that either:
+- When dismissing the error, the `onClose` prop will be called, there we need to call an action that either:
- If the pendingAction was `delete`, it removes the data altogether
- - Otherwise, it would clear the errors and pendingAction properties from the data
+ - Otherwise, it would clear the errors and `pendingAction` properties from the data
- We also need to show a Red Brick Road (RBR) guiding the user to the error. We need to manually do this for each piece of data using pattern B Optimistic WITH Feedback. Some common components like `MenuItem` already have a prop for it (`brickRoadIndicator`)
- A Brick Road is the pattern of guiding members towards places that require their attention by following a series of UI elements that have the same color
diff --git a/contributingGuides/PERFORMANCE.md b/contributingGuides/PERFORMANCE.md
index 0e8ee14d70a4..1942c97af913 100644
--- a/contributingGuides/PERFORMANCE.md
+++ b/contributingGuides/PERFORMANCE.md
@@ -4,7 +4,7 @@
- Use [`PureComponent`](https://reactjs.org/docs/react-api.html#reactpurecomponent), [`React.memo()`](https://reactjs.org/docs/react-api.html#reactmemo), and [`shouldComponentUpdate()`](https://reactjs.org/docs/react-component.html#shouldcomponentupdate) to prevent re-rendering expensive components.
- Using a combination of [React DevTools Profiler](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en) and [Chrome Dev Tools Performance Timing](https://calibreapp.com/blog/react-performance-profiling-optimization) can help identify unnecessary re-renders. Both tools can be used to time an interaction like the app starting up or navigating to a new screen.
- Watch out for [very large lists](https://reactnative.dev/docs/optimizing-flatlist-configuration) and things like `Image` components re-fetching images on render when a remote uri did not change.
-- Avoid the temptation to over-optimize. There is added cost in both code complexity and performance when adding checks like `shouldComponentUpdate()`. Be selective about when you use this and make sure there is a measureable difference before proposing the change. As a very general rule it should be measurably faster to run logic to avoid the re-render (e.g. do a deep comparison) than it would be to let React take care of it without any extra intervention from us.
+- Avoid the temptation to over-optimize. There is added cost in both code complexity and performance when adding checks like `shouldComponentUpdate()`. Be selective about when you use this and make sure there is a measureable difference before proposing the change. As a very general rule, it should be measurably faster to run logic to avoid the re-render (e.g. do a deep comparison) than it would be to let React take care of it without any extra intervention from us.
- Use caution when adding subscriptions that might re-render very large trees of components e.g. subscribing to state that changes often (current report, current route, etc) in the app root.
- Avoid using arrow function callbacks in components that are expensive to re-render. React will re-render this component since each time the parent renders it creates a new instance of that function. **Alternative:** Bind the method in the constructor instead.
@@ -22,12 +22,12 @@
It's possible, but slightly trickier to profile the JS running on Android devices as it does not run in a browser but a JS VM that React Native must spin up first then run the app code. The VM we are currently using on both Android and iOS is called [Hermes](https://reactnative.dev/docs/profile-hermes) and is developed by Facebook.
-In order to profile with Hermes follow these steps:
+In order to profile with Hermes, follow these steps:
-- In the metro bundler window press `d` on your keyboard to bring up the developer menu on your device.
+- In the metro bundler window, press `d` on your keyboard to bring up the developer menu on your device.
- Select "Settings"
- Select "Start Sampling Profiler on Init"
-- In metro bundler refresh by pressing r
+- In metro bundler, refresh by pressing r
- The app will start up and a profile will begin
- Once the app loads take whatever action you want to profile
- Press `d` again and select "Disable Sampling Profiler"
@@ -52,7 +52,7 @@ In order to profile with Hermes follow these steps:
### Performance Metrics (Opt-In on local release builds)
-To capture reliable performance metrics for native app launch we must test against a release build. To make this easier for everyone to do we created an opt-in tool (using [`react-native-performance`](https://github.com/oblador/react-native-performance) that will capture metrics and display them in an alert once the app becomes interactive. To set this up just set `CAPTURE_METRICS=true` in your `.env` file then create a release build on iOS or Android. The metrics this tool shows are as follows:
+To capture reliable performance metrics for native app launch, we must test against a release build. To make this easier for everyone to do, we created an opt-in tool (using [`react-native-performance`](https://github.com/oblador/react-native-performance)) that will capture metrics and display them in an alert once the app becomes interactive. To set this up, just set `CAPTURE_METRICS=true` in your `.env` file, then create a release build on iOS or Android. The metrics this tool shows are as follows:
- `nativeLaunch` - Total time for the native process to initialize
- `runJSBundle` - Total time to parse and execute the JS bundle
@@ -73,22 +73,23 @@ signingConfigs {
keyAlias 'your_key_alias'
keyPassword 'Password1'
}
+}
```
- Delete any existing apps off emulator or device
- Run `react-native run-android --variant release`
## Reconciliation
-React is pretty smart and in many cases is able to tell if something needs to update. The process by which React goes about updating the "tree" or view heirarchy is called reconciliation. If React thinks something needs to update it will render it again. React also assumes that if a parent component rendered then it's child should also re-render.
+React is pretty smart and in many cases is able to tell if something needs to update. The process by which React goes about updating the "tree" or view hierarchy is called reconciliation. If React thinks something needs to update, it will render it again. React also assumes that if a parent component rendered, then its child should also re-render.
-Re-rendering can be expensive at times and when dealing with nested props or state React may render when it doesn't need to which can be wasteful. A good example of this is a component that is being passed an object as a prop. Let's say the component only requires one or two properties from that object in order to build it's view, but doesn't care about some others. React will still re-render that component even if nothing it cares about has changed. Most of the time this is fine since reconciliation is pretty fast. But we might run into performance issues when re-rendering massive lists.
+Re-rendering can be expensive at times and when dealing with nested props or state React may render when it doesn't need to which can be wasteful. A good example of this is a component that is being passed an object as a prop. Let's say the component only requires one or two properties from that object in order to build its view, but doesn't care about some others. React will still re-render that component even if nothing it cares about has changed. Most of the time this is fine since reconciliation is pretty fast. But we might run into performance issues when re-rendering massive lists.
In this example, the most preferable solution would be to **only pass the properties that the object needs to know about** to the component in the first place.
Another option would be to use `shouldComponentUpdate` or `React.memo()` to add more specific rules comparing `props` to **explicitly tell React not to perform a re-render**.
-React might still take some time to re-render a component when it's parent component renders. If it takes a long time to re-render the child even though we have no props changing then we can use `PureComponent` or `React.memo()` (without a callback) which will "shallow compare" the `props` to see if a component should re-render.
+React might still take some time to re-render a component when its parent component renders. If it takes a long time to re-render the child even though we have no props changing, then we can use `PureComponent` or `React.memo()` (without a callback) which will "shallow compare" the `props` to see if a component should re-render.
-If you aren't sure what exactly is changing about some deeply nested object prop you can use `Performance.diffObject()` method in `componentDidUpdate()` which should show you exactly what is changing from one update to the next.
+If you aren't sure what exactly is changing about some deeply nested object prop, you can use `Performance.diffObject()` method in `componentDidUpdate()` which should show you exactly what is changing from one update to the next.
**Suggested:** [React Docs - Reconciliation](https://reactjs.org/docs/reconciliation.html)
diff --git a/contributingGuides/PROPOSAL_TEMPLATE.md b/contributingGuides/PROPOSAL_TEMPLATE.md
index 53330dfe96c9..8c9fa7968fe2 100644
--- a/contributingGuides/PROPOSAL_TEMPLATE.md
+++ b/contributingGuides/PROPOSAL_TEMPLATE.md
@@ -14,13 +14,13 @@
<% if (htmlWebpackPlugin.options.isProduction) { %>
-
-
-
+
+
+
<% } %>
<% } %>
diff --git a/web/thirdPartyScripts.js b/web/thirdPartyScripts.js
deleted file mode 100644
index 15e77dbd490e..000000000000
--- a/web/thirdPartyScripts.js
+++ /dev/null
@@ -1,155 +0,0 @@
-/* eslint-disable */
-// Google Tag Manager
-(function (w, d, s, l, i) {
- w[l] = w[l] || [];
- w[l].push({'gtm.start': new Date().getTime(), event: 'gtm.js'});
- const f = d.getElementsByTagName(s)[0];
- const j = d.createElement(s);
- const dl = l !== 'dataLayer' ? '&l=' + l : '';
- j.async = true;
- j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl;
- f.parentNode.insertBefore(j, f);
-})(window, document, 'script', 'dataLayer', 'GTM-N4M3FLJZ');
-
-// FullStory
-window['_fs_host'] = 'fullstory.com';
-window['_fs_script'] = 'edge.fullstory.com/s/fs.js';
-window['_fs_org'] = 'o-1WN56P-na1';
-window['_fs_namespace'] = 'FS';
-!(function (m, n, e, t, l, o, g, y) {
- var s,
- f,
- a = (function (h) {
- return !(h in m) || (m.console && m.console.log && m.console.log('FullStory namespace conflict. Please set window["_fs_namespace"].'), !1);
- })(e);
- function p(b) {
- var h,
- d = [];
- function j() {
- h &&
- (d.forEach(function (b) {
- var d;
- try {
- d = b[h[0]] && b[h[0]](h[1]);
- } catch (h) {
- return void (b[3] && b[3](h));
- }
- d && d.then ? d.then(b[2], b[3]) : b[2] && b[2](d);
- }),
- (d.length = 0));
- }
- function r(b) {
- return function (d) {
- h || ((h = [b, d]), j());
- };
- }
- return (
- b(r(0), r(1)),
- {
- then: function (b, h) {
- return p(function (r, i) {
- d.push([b, h, r, i]), j();
- });
- },
- }
- );
- }
- a &&
- ((g = m[e] =
- (function () {
- var b = function (b, d, j, r) {
- function i(i, c) {
- h(b, d, j, i, c, r);
- }
- r = r || 2;
- var c,
- u = /Async$/;
- return u.test(b) ? ((b = b.replace(u, '')), 'function' == typeof Promise ? new Promise(i) : p(i)) : h(b, d, j, c, c, r);
- };
- function h(h, d, j, r, i, c) {
- return b._api ? b._api(h, d, j, r, i, c) : (b.q && b.q.push([h, d, j, r, i, c]), null);
- }
- return (b.q = []), b;
- })()),
- (y = function (b) {
- function h(h) {
- 'function' == typeof h[4] && h[4](new Error(b));
- }
- var d = g.q;
- if (d) {
- for (var j = 0; j < d.length; j++) h(d[j]);
- (d.length = 0), (d.push = h);
- }
- }),
- (function () {
- ((o = n.createElement(t)).async = !0),
- (o.crossOrigin = 'anonymous'),
- (o.src = 'https://' + l),
- (o.onerror = function () {
- y('Error loading ' + l);
- });
- var b = n.getElementsByTagName(t)[0];
- b && b.parentNode ? b.parentNode.insertBefore(o, b) : n.head.appendChild(o);
- })(),
- (function () {
- function b() {}
- function h(b, h, d) {
- g(b, h, d, 1);
- }
- function d(b, d, j) {
- h('setProperties', {type: b, properties: d}, j);
- }
- function j(b, h) {
- d('user', b, h);
- }
- function r(b, h, d) {
- j(
- {
- uid: b,
- },
- d,
- ),
- h && j(h, d);
- }
- (g.identify = r),
- (g.setUserVars = j),
- (g.identifyAccount = b),
- (g.clearUserCookie = b),
- (g.setVars = d),
- (g.event = function (b, d, j) {
- h(
- 'trackEvent',
- {
- name: b,
- properties: d,
- },
- j,
- );
- }),
- (g.anonymize = function () {
- r(!1);
- }),
- (g.shutdown = function () {
- h('shutdown');
- }),
- (g.restart = function () {
- h('restart');
- }),
- (g.log = function (b, d) {
- h('log', {level: b, msg: d});
- }),
- (g.consent = function (b) {
- h('setIdentity', {consent: !arguments.length || b});
- });
- })(),
- (s = 'fetch'),
- (f = 'XMLHttpRequest'),
- (g._w = {}),
- (g._w[f] = m[f]),
- (g._w[s] = m[s]),
- m[s] &&
- (m[s] = function () {
- return g._w[s].apply(this, arguments);
- }),
- (g._v = '2.0.0'));
-})(window, document, window._fs_namespace, 'script', window._fs_script);