diff --git a/.eslintrc.js b/.eslintrc.js
index c2198da60c52..56a5236a7899 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -94,7 +94,6 @@ module.exports = {
rules: {
'prefer-regex-literals': 'off',
'rulesdir/no-multiple-onyx-in-file': 'off',
- 'rulesdir/onyx-props-must-have-default': 'off',
'react-native-a11y/has-accessibility-hint': ['off'],
'react/jsx-no-constructed-context-values': 'error',
'react-native-a11y/has-valid-accessibility-descriptors': [
diff --git a/.github/scripts/verifyRedirect.sh b/.github/scripts/verifyRedirect.sh
new file mode 100644
index 000000000000..737d9bffacf9
--- /dev/null
+++ b/.github/scripts/verifyRedirect.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+# HelpDot - Verifies that redirects.csv does not have any duplicates
+# Duplicate sourceURLs break redirection on cloudflare pages
+
+declare -r REDIRECTS_FILE="docs/redirects.csv"
+
+duplicates=$(awk -F, 'a[$1]++{print $1}' $REDIRECTS_FILE)
+
+if [[ -z "$duplicates" ]]; then
+ exit 0
+fi
+
+echo "duplicate redirects are not allowed: $duplicates"
+exit 1
diff --git a/.github/workflows/deployExpensifyHelp.yml b/.github/workflows/deployExpensifyHelp.yml
index d4577e112d59..699bd379fb77 100644
--- a/.github/workflows/deployExpensifyHelp.yml
+++ b/.github/workflows/deployExpensifyHelp.yml
@@ -36,6 +36,9 @@ jobs:
- name: Create docs routes file
run: ./.github/scripts/createDocsRoutes.sh
+
+ - name: Check duplicates in redirect.csv
+ run: ./.github/scripts/verifyRedirect.sh
- name: Build with Jekyll
uses: actions/jekyll-build-pages@0143c158f4fa0c5dcd99499a5d00859d79f70b0e
diff --git a/.github/workflows/platformDeploy.yml b/.github/workflows/platformDeploy.yml
index d97ea2b86269..bb850e6eda10 100644
--- a/.github/workflows/platformDeploy.yml
+++ b/.github/workflows/platformDeploy.yml
@@ -87,11 +87,12 @@ jobs:
MYAPP_UPLOAD_STORE_PASSWORD: ${{ secrets.MYAPP_UPLOAD_STORE_PASSWORD }}
MYAPP_UPLOAD_KEY_PASSWORD: ${{ secrets.MYAPP_UPLOAD_KEY_PASSWORD }}
- - name: Run Fastlane production
- if: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }}
- run: bundle exec fastlane android production
- env:
- VERSION: ${{ env.VERSION_CODE }}
+ # Note: Android production deploys are temporarily disabled until https://github.com/Expensify/App/issues/40108 is resolved
+ # - name: Run Fastlane production
+ # if: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }}
+ # run: bundle exec fastlane android production
+ # env:
+ # VERSION: ${{ env.VERSION_CODE }}
- name: Archive Android sourcemaps
uses: actions/upload-artifact@v3
@@ -158,7 +159,7 @@ jobs:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
GCP_GEOLOCATION_API_KEY: $${{ secrets.GCP_GEOLOCATION_API_KEY_PRODUCTION }}
-
+
- name: Build staging desktop app
if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }}
diff --git a/.well-known/apple-app-site-association b/.well-known/apple-app-site-association
index b3adf0f59b9c..a2c7365f7de8 100644
--- a/.well-known/apple-app-site-association
+++ b/.well-known/apple-app-site-association
@@ -32,10 +32,6 @@
"/": "/iou/*",
"comment": "I Owe You reports"
},
- {
- "/": "/request/*",
- "comment": "Money request"
- },
{
"/": "/enable-payments/*",
"comment": "Payments setup"
@@ -54,11 +50,11 @@
},
{
"/": "/split/*",
- "comment": "Split Bill"
+ "comment": "Split Expense"
},
{
"/": "/request/*",
- "comment": "Request Money"
+ "comment": "Submit Expense"
},
{
"/": "/new/*",
@@ -82,7 +78,7 @@
},
{
"/": "/send/*",
- "comment": "Send money"
+ "comment": "Pay someone"
},
{
"/": "/money2020/*",
diff --git a/README.md b/README.md
index 026a63606db0..8adadfc9cbe6 100644
--- a/README.md
+++ b/README.md
@@ -82,6 +82,16 @@ If you want to run the app on an actual physical iOS device, please follow the i
## Running the MacOS desktop app 🖥
* To run the **Development app**, run: `npm run desktop`, this will start a new Electron process running on your MacOS desktop in the `dist/Mac` folder.
+## Receiving Notifications
+To receive notifications on development build of the app while hitting the Staging or Production API, you need to use the production airship config.
+### Android
+1. Copy the [production config](https://github.com/Expensify/App/blob/d7c1256f952c0020344d809ee7299b49a4c70db2/android/app/src/main/assets/airshipconfig.properties#L1-L7) to the [development config](https://github.com/Expensify/App/blob/d7c1256f952c0020344d809ee7299b49a4c70db2/android/app/src/development/assets/airshipconfig.properties#L1-L8).
+2. Rebuild the app.
+
+### iOS
+1. Replace the [development key and secret](https://github.com/Expensify/App/blob/d7c1256f952c0020344d809ee7299b49a4c70db2/ios/AirshipConfig.plist#L7-L10) with the [production values](https://github.com/Expensify/App/blob/d7c1256f952c0020344d809ee7299b49a4c70db2/ios/AirshipConfig.plist#L11-L14).
+2. Rebuild the app.
+
## Troubleshooting
1. If you are having issues with **_Getting Started_**, please reference [React Native's Documentation](https://reactnative.dev/docs/environment-setup)
2. If you are running into CORS errors like (in the browser dev console)
diff --git a/android/app/build.gradle b/android/app/build.gradle
index b9a7fa4d6b4b..0db4b032ec9d 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -98,8 +98,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1001046212
- versionName "1.4.62-12"
+ versionCode 1001046300
+ versionName "1.4.63-0"
// 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"
diff --git a/docs/redirects.csv b/docs/redirects.csv
index 51c8c7515e10..af595ecc5f83 100644
--- a/docs/redirects.csv
+++ b/docs/redirects.csv
@@ -152,7 +152,6 @@ https://help.expensify.com/articles/expensify-classic/manage-employees-and-repor
https://help.expensify.com/articles/expensify-classic/manage-employees-and-report-approvals/Invite-Members,https://help.expensify.com/articles/expensify-classic/copilots-and-delegates/Invite-Members
https://help.expensify.com/articles/expensify-classic/manage-employees-and-report-approvals/Removing-Members,https://help.expensify.com/articles/expensify-classic/copilots-and-delegates/Removing-Members
https://help.expensify.com/articles/expensify-classic/manage-employees-and-report-approvals/User-Roles,https://help.expensify.com/expensify-classic/hubs/copilots-and-delegates/
-https://help.expensify.com/articles/expensify-classic/reports/Currency,https://help.expensify.com/articles/expensify-classic/workspaces/Currency
https://help.expensify.com/articles/expensify-classic/send-payments/Reimbursing-Reports,https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/Reimbursing-Reports
https://help.expensify.com/articles/expensify-classic/workspace-and-domain-settings/SAML-SSO,https://help.expensify.com/articles/expensify-classic/settings/Enable-two-factor-authentication
https://help.expensify.com/articles/expensify-classic/workspaces/Budgets,https://help.expensify.com/articles/expensify-classic/workspaces/Set-budgets
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index 9db0bbd40945..ddcc64604581 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -19,7 +19,7 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 1.4.62
+ 1.4.63
CFBundleSignature
????
CFBundleURLTypes
@@ -40,7 +40,7 @@
CFBundleVersion
- 1.4.62.12
+ 1.4.63.0
ITSAppUsesNonExemptEncryption
LSApplicationQueriesSchemes
diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist
index c2fe74d90fcd..a57ffb9500c5 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -15,10 +15,10 @@
CFBundlePackageType
BNDL
CFBundleShortVersionString
- 1.4.62
+ 1.4.63
CFBundleSignature
????
CFBundleVersion
- 1.4.62.12
+ 1.4.63.0
diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist
index 7503dd04702b..3c8e8c1fb63f 100644
--- a/ios/NotificationServiceExtension/Info.plist
+++ b/ios/NotificationServiceExtension/Info.plist
@@ -11,9 +11,9 @@
CFBundleName
$(PRODUCT_NAME)
CFBundleShortVersionString
- 1.4.62
+ 1.4.63
CFBundleVersion
- 1.4.62.12
+ 1.4.63.0
NSExtension
NSExtensionPointIdentifier
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 1ebfc6bb1b62..f564bfd931e4 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -1835,7 +1835,7 @@ PODS:
- RNGoogleSignin (10.0.1):
- GoogleSignIn (~> 7.0)
- React-Core
- - RNLiveMarkdown (0.1.47):
+ - RNLiveMarkdown (0.1.62):
- glog
- hermes-engine
- RCT-Folly (= 2022.05.16.00)
@@ -1853,9 +1853,9 @@ PODS:
- React-utils
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- - RNLiveMarkdown/common (= 0.1.47)
+ - RNLiveMarkdown/common (= 0.1.62)
- Yoga
- - RNLiveMarkdown/common (0.1.47):
+ - RNLiveMarkdown/common (0.1.62):
- glog
- hermes-engine
- RCT-Folly (= 2022.05.16.00)
@@ -2564,7 +2564,7 @@ SPEC CHECKSUMS:
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
RNGestureHandler: 1190c218cdaaf029ee1437076a3fbbc3297d89fb
RNGoogleSignin: ccaa4a81582cf713eea562c5dd9dc1961a715fd0
- RNLiveMarkdown: f172c7199283dc9d21bccf7e21ea10741fd19e1d
+ RNLiveMarkdown: 47dfb50244f9ba1caefbc0efc6404ba41bf6620a
RNLocalize: d4b8af4e442d4bcca54e68fc687a2129b4d71a81
rnmapbox-maps: 3e273e0e867a079ec33df9ee33bb0482434b897d
RNPermissions: 8990fc2c10da3640938e6db1647cb6416095b729
@@ -2581,7 +2581,7 @@ SPEC CHECKSUMS:
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
Turf: 13d1a92d969ca0311bbc26e8356cca178ce95da2
VisionCamera: 3033e0dd5272d46e97bcb406adea4ae0e6907abf
- Yoga: 64cd2a583ead952b0315d5135bf39e053ae9be70
+ Yoga: 1b901a6d6eeba4e8a2e8f308f708691cdb5db312
PODFILE CHECKSUM: a25a81f2b50270f0c0bd0aff2e2ebe4d0b4ec06d
diff --git a/package-lock.json b/package-lock.json
index 0817e4bbbd2a..920fefc8242b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,19 +1,19 @@
{
"name": "new.expensify",
- "version": "1.4.62-12",
+ "version": "1.4.63-0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "new.expensify",
- "version": "1.4.62-12",
+ "version": "1.4.63-0",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@babel/plugin-proposal-private-methods": "^7.18.6",
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@dotlottie/react-player": "^1.6.3",
- "@expensify/react-native-live-markdown": "^0.1.49",
+ "@expensify/react-native-live-markdown": "0.1.62",
"@expo/metro-runtime": "~3.1.1",
"@formatjs/intl-datetimeformat": "^6.10.0",
"@formatjs/intl-listformat": "^7.2.2",
@@ -57,7 +57,7 @@
"date-fns-tz": "^2.0.0",
"dom-serializer": "^0.2.2",
"domhandler": "^4.3.0",
- "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#13de5b0606662df33fa1392ad82cc11daadff52e",
+ "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#c0f7f3b6558fbeda0527c80d68460d418afef219",
"expo": "^50.0.3",
"expo-av": "~13.10.4",
"expo-image": "1.11.0",
@@ -207,7 +207,7 @@
"electron-builder": "24.13.2",
"eslint": "^7.6.0",
"eslint-config-airbnb-typescript": "^17.1.0",
- "eslint-config-expensify": "^2.0.44",
+ "eslint-config-expensify": "^2.0.47",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jest": "^24.1.0",
@@ -3570,9 +3570,9 @@
}
},
"node_modules/@expensify/react-native-live-markdown": {
- "version": "0.1.49",
- "resolved": "https://registry.npmjs.org/@expensify/react-native-live-markdown/-/react-native-live-markdown-0.1.49.tgz",
- "integrity": "sha512-5l+/NtUTuSxWkdsT2JOlhKD5NW1hZ+nQcmgrCSz5e/TNIcfkYjJNiW/nEf8qmBV54afiTmTTwKYrh2DwM/BQ0g==",
+ "version": "0.1.62",
+ "resolved": "https://registry.npmjs.org/@expensify/react-native-live-markdown/-/react-native-live-markdown-0.1.62.tgz",
+ "integrity": "sha512-o70/tFIGZJ1U8U8aqTQu1HAZed6nt5LYWk74mrceRxQHOqsKhZgn2q5EuEy8EMIcnCGKjwxuDyZJbuRexgHx/A==",
"engines": {
"node": ">= 18.0.0"
},
@@ -16462,10 +16462,8 @@
},
"node_modules/classnames": {
"version": "2.5.0",
- "license": "MIT",
- "workspaces": [
- "benchmarks"
- ]
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.0.tgz",
+ "integrity": "sha512-FQuRlyKinxrb5gwJlfVASbSrDlikDJ07426TrfPsdGLvtochowmkbnSFdQGJ2aoXrSetq5KqGV9emvWpy+91xA=="
},
"node_modules/clean-css": {
"version": "5.3.2",
@@ -16551,7 +16549,8 @@
},
"node_modules/clipboard": {
"version": "2.0.11",
- "license": "MIT",
+ "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz",
+ "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
"dependencies": {
"good-listener": "^1.2.2",
"select": "^1.1.2",
@@ -18058,7 +18057,8 @@
},
"node_modules/delegate": {
"version": "3.2.0",
- "license": "MIT"
+ "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
+ "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
},
"node_modules/delegates": {
"version": "1.0.0",
@@ -19250,9 +19250,10 @@
}
},
"node_modules/eslint-config-expensify": {
- "version": "2.0.44",
+ "version": "2.0.48",
+ "resolved": "https://registry.npmjs.org/eslint-config-expensify/-/eslint-config-expensify-2.0.48.tgz",
+ "integrity": "sha512-PFegJ9Wfsiu5tgevhjA1toCxsZ8Etfk6pIjtXAnwpmVj7q4CtB3QDRusJoUDyJ3HrZr8AsFKViz7CU/CBTfwOw==",
"dev": true,
- "license": "ISC",
"dependencies": {
"@lwc/eslint-plugin-lwc": "^1.7.2",
"babel-eslint": "^10.1.0",
@@ -20212,8 +20213,8 @@
},
"node_modules/expensify-common": {
"version": "1.0.0",
- "resolved": "git+ssh://git@github.com/Expensify/expensify-common.git#13de5b0606662df33fa1392ad82cc11daadff52e",
- "integrity": "sha512-/NAZoAXqeqFWHvC61dueqq9VjRugF69urUtDdDhsfvu1sQE2PCnBoM7a+ACoAEWRYrnP82cyHHhdSA8e7fPuAg==",
+ "resolved": "git+ssh://git@github.com/Expensify/expensify-common.git#c0f7f3b6558fbeda0527c80d68460d418afef219",
+ "integrity": "sha512-zz0/y0apISP1orxXEQOgn+Uod45O4wVypwwtaqcDPV4dH1tC3i4L98NoLSZvLn7Y17EcceSkfN6QCEsscgFTDQ==",
"license": "MIT",
"dependencies": {
"classnames": "2.5.0",
@@ -20266,6 +20267,8 @@
},
"node_modules/expensify-common/node_modules/ua-parser-js": {
"version": "1.0.37",
+ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz",
+ "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==",
"funding": [
{
"type": "opencollective",
@@ -20280,7 +20283,6 @@
"url": "https://github.com/sponsors/faisalman"
}
],
- "license": "MIT",
"engines": {
"node": "*"
}
@@ -21728,7 +21730,8 @@
},
"node_modules/good-listener": {
"version": "1.2.2",
- "license": "MIT",
+ "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
+ "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
"dependencies": {
"delegate": "^3.1.2"
}
@@ -22779,7 +22782,8 @@
},
"node_modules/immediate": {
"version": "3.0.6",
- "license": "MIT"
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
},
"node_modules/import-fresh": {
"version": "3.3.0",
@@ -26838,7 +26842,8 @@
},
"node_modules/lie": {
"version": "3.1.1",
- "license": "MIT",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
+ "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==",
"dependencies": {
"immediate": "~3.0.5"
}
@@ -26981,7 +26986,8 @@
},
"node_modules/localforage": {
"version": "1.10.0",
- "license": "Apache-2.0",
+ "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz",
+ "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==",
"dependencies": {
"lie": "3.1.1"
}
@@ -33311,7 +33317,8 @@
},
"node_modules/select": {
"version": "1.1.2",
- "license": "MIT"
+ "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
+ "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
},
"node_modules/select-hose": {
"version": "2.0.0",
diff --git a/package.json b/package.json
index 60e6a320cfb0..20d066eabebe 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "1.4.62-12",
+ "version": "1.4.63-0",
"author": "Expensify, Inc.",
"homepage": "https://new.expensify.com",
"description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
@@ -64,7 +64,7 @@
"@babel/plugin-proposal-private-methods": "^7.18.6",
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@dotlottie/react-player": "^1.6.3",
- "@expensify/react-native-live-markdown": "0.1.49",
+ "@expensify/react-native-live-markdown": "0.1.62",
"@expo/metro-runtime": "~3.1.1",
"@formatjs/intl-datetimeformat": "^6.10.0",
"@formatjs/intl-listformat": "^7.2.2",
@@ -108,7 +108,7 @@
"date-fns-tz": "^2.0.0",
"dom-serializer": "^0.2.2",
"domhandler": "^4.3.0",
- "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#13de5b0606662df33fa1392ad82cc11daadff52e",
+ "expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#c0f7f3b6558fbeda0527c80d68460d418afef219",
"expo": "^50.0.3",
"expo-av": "~13.10.4",
"expo-image": "1.11.0",
@@ -258,7 +258,7 @@
"electron-builder": "24.13.2",
"eslint": "^7.6.0",
"eslint-config-airbnb-typescript": "^17.1.0",
- "eslint-config-expensify": "^2.0.44",
+ "eslint-config-expensify": "^2.0.47",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jest": "^24.1.0",
diff --git a/src/CONFIG.ts b/src/CONFIG.ts
index 76ea18d37d5f..9ed4242d7604 100644
--- a/src/CONFIG.ts
+++ b/src/CONFIG.ts
@@ -48,6 +48,7 @@ export default {
EXPENSIFY: {
// Note: This will be EXACTLY what is set for EXPENSIFY_URL whether the proxy is enabled or not.
EXPENSIFY_URL: expensifyURL,
+ SECURE_EXPENSIFY_URL: secureExpensifyUrl,
NEW_EXPENSIFY_URL: newExpensifyURL,
// The DEFAULT API is the API used by most environments, except staging, where we use STAGING (defined below)
@@ -72,7 +73,7 @@ export default {
IS_USING_LOCAL_WEB: useNgrok || expensifyURLRoot.includes('dev'),
PUSHER: {
APP_KEY: get(Config, 'PUSHER_APP_KEY', '268df511a204fbb60884'),
- SUFFIX: get(Config, 'PUSHER_DEV_SUFFIX', ''),
+ SUFFIX: ENVIRONMENT === CONST.ENVIRONMENT.DEV ? get(Config, 'PUSHER_DEV_SUFFIX', '') : '',
CLUSTER: 'mt1',
},
SITE_TITLE: 'New Expensify',
diff --git a/src/CONST.ts b/src/CONST.ts
index d47800f69308..f263307d37b8 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -879,7 +879,7 @@ const CONST = {
},
TIMING: {
CALCULATE_MOST_RECENT_LAST_MODIFIED_ACTION: 'calc_most_recent_last_modified_action',
- SEARCH_RENDER: 'search_render',
+ CHAT_FINDER_RENDER: 'search_render',
CHAT_RENDER: 'chat_render',
OPEN_REPORT: 'open_report',
HOMEPAGE_INITIAL_RENDER: 'homepage_initial_render',
@@ -1215,9 +1215,9 @@ const CONST = {
NOT_IMPORTED: 'NOT_IMPORTED',
IMPORTED: 'IMPORTED',
},
- QUICK_BOOKS_ONLINE: 'quickbooksOnline',
+ QUICKBOOKS_ONLINE: 'quickbooksOnline',
- QUICK_BOOKS_IMPORTS: {
+ QUICKBOOKS_IMPORTS: {
SYNC_CLASSES: 'syncClasses',
ENABLE_NEW_CATEGORIES: 'enableNewCategories',
SYNC_CUSTOMERS: 'syncCustomers',
@@ -1363,7 +1363,7 @@ const CONST = {
},
KYC_WALL_SOURCE: {
- REPORT: 'REPORT', // The user attempted to pay a money request
+ REPORT: 'REPORT', // The user attempted to pay an expense
ENABLE_WALLET: 'ENABLE_WALLET', // The user clicked on the `Enable wallet` button on the Wallet page
TRANSFER_BALANCE: 'TRANSFER_BALANCE', // The user attempted to transfer their wallet balance to their bank account or debit card
},
@@ -1399,7 +1399,7 @@ const CONST = {
},
IOU: {
- // This is the transactionID used when going through the create money request flow so that it mimics a real transaction (like the edit flow)
+ // This is the transactionID used when going through the create expense flow so that it mimics a real transaction (like the edit flow)
OPTIMISTIC_TRANSACTION_ID: '1',
// Note: These payment types are used when building IOU reportAction message values in the server and should
// not be changed.
@@ -1614,25 +1614,23 @@ const CONST = {
GENERAL_SETTINGS: 'generalSettings',
},
CONNECTIONS: {
- SYNC_STATUS: {
- STARTING: 'starting',
- FINISHED: 'finished',
- PROGRESS: 'progress',
- },
NAME: {
// Here we will add other connections names when we add support for them
QBO: 'quickbooksOnline',
},
SYNC_STAGE_NAME: {
STARTING_IMPORT: 'startingImport',
- QBO_CUSTOMERS: 'quickbooksOnlineImportCustomers',
- QBO_EMPLOYEES: 'quickbooksOnlineImportEmployees',
- QBO_ACCOUNTS: 'quickbooksOnlineImportAccounts',
- QBO_CLASSES: 'quickbooksOnlineImportClasses',
- QBO_LOCATIONS: 'quickbooksOnlineImportLocations',
- QBO_PROCESSING: 'quickbooksOnlineImportProcessing',
- QBO_PAYMENTS: 'quickbooksOnlineSyncBillPayments',
- QBO_TAX_CODES: 'quickbooksOnlineSyncTaxCodes',
+ QBO_IMPORT_MAIN: 'quickbooksOnlineImportMain',
+ QBO_IMPORT_CUSTOMERS: 'quickbooksOnlineImportCustomers',
+ QBO_IMPORT_EMPLOYEES: 'quickbooksOnlineImportEmployees',
+ QBO_IMPORT_ACCOUNTS: 'quickbooksOnlineImportAccounts',
+ QBO_IMPORT_CLASSES: 'quickbooksOnlineImportClasses',
+ QBO_IMPORT_LOCATIONS: 'quickbooksOnlineImportLocations',
+ QBO_IMPORT_PROCESSING: 'quickbooksOnlineImportProcessing',
+ QBO_SYNC_PAYMENTS: 'quickbooksOnlineSyncBillPayments',
+ QBO_IMPORT_TAX_CODES: 'quickbooksOnlineSyncTaxCodes',
+ QBO_CHECK_CONNECTION: 'quickbooksOnlineCheckConnection',
+ JOB_DONE: 'jobDone',
},
},
},
@@ -3301,6 +3299,13 @@ const CONST = {
SCAN: 'scan',
DISTANCE: 'distance',
},
+ TAB_SEARCH: {
+ ALL: 'all',
+ SENT: 'sent',
+ DRAFTS: 'drafts',
+ WAITING_ON_YOU: 'waitingOnYou',
+ FINISHED: 'finished',
+ },
STATUS_TEXT_MAX_LENGTH: 100,
DROPDOWN_BUTTON_SIZE: {
@@ -3551,12 +3556,11 @@ const CONST = {
ONBOARDING_CONCIERGE: {
[onboardingChoices.TRACK]:
- "# Welcome to Expensify, let's start tracking your expenses!\n" +
- "Hi there, I'm Concierge. Chat with me here for anything you need.\n" +
+ "# Let's start tracking your expenses!\n" +
'\n' +
"To track your expenses, create a workspace to keep everything in one place. Here's how:\n" +
'1. From the home screen, click the green + button > New Workspace\n' +
- '2. Give your workspace a name (e.g. "My business expenses”).\n' +
+ '2. Give your workspace a name (e.g. "My business expenses").\n' +
'\n' +
'Then, add expenses to your workspace:\n' +
'1. Find your workspace using the search field.\n' +
@@ -3565,8 +3569,7 @@ const CONST = {
'\n' +
"We'll store all expenses in your new workspace for easy access. Let me know if you have any questions!",
[onboardingChoices.EMPLOYER]:
- '# Welcome to Expensify, the fastest way to get paid back!\n' +
- "Hi there, I'm Concierge. Chat with me here for anything you need.\n" +
+ '# Expensify is the fastest way to get paid back!\n' +
'\n' +
'To submit expenses for reimbursement:\n' +
'1. From the home screen, click the green + button > Request money.\n' +
@@ -3574,21 +3577,19 @@ const CONST = {
'\n' +
"That'll send a request to get you paid back. Let me know if you have any questions!",
[onboardingChoices.MANAGE_TEAM]:
- "# Welcome to Expensify, let's start managing your team's expenses!\n" +
- "Hi there, I'm Concierge. Chat with me here for anything you need.\n" +
+ "# Let's start managing your team's expenses!\n" +
'\n' +
"To manage your team's expenses, create a workspace to keep everything in one place. Here's how:\n" +
'1. From the home screen, click the green + button > New Workspace\n' +
- '2. Give your workspace a name (e.g. “Sales team expenses”).\n' +
+ '2. Give your workspace a name (e.g. "Sales team expenses").\n' +
'\n' +
- 'Then, invite your team to your workspace via the Members pane and connect a business bank account to reimburse them. Let me know if you have any questions!',
+ 'Then, invite your team to your workspace via the Members pane and [connect a business bank account](https://help.expensify.com/articles/new-expensify/bank-accounts/Connect-a-Bank-Account) to reimburse them. Let me know if you have any questions!',
[onboardingChoices.PERSONAL_SPEND]:
- "# Welcome to Expensify, let's start tracking your expenses!\n" +
- "Hi there, I'm Concierge. Chat with me here for anything you need.\n" +
+ "# Let's start tracking your expenses! \n" +
'\n' +
"To track your expenses, create a workspace to keep everything in one place. Here's how:\n" +
'1. From the home screen, click the green + button > New Workspace\n' +
- '2. Give your workspace a name (e.g. "My expenses”).\n' +
+ '2. Give your workspace a name (e.g. "My expenses").\n' +
'\n' +
'Then, add expenses to your workspace:\n' +
'1. Find your workspace using the search field.\n' +
@@ -3597,19 +3598,13 @@ const CONST = {
'\n' +
"We'll store all expenses in your new workspace for easy access. Let me know if you have any questions!",
[onboardingChoices.CHAT_SPLIT]:
- '# Welcome to Expensify, where splitting the bill is an easy conversation!\n' +
- "Hi there, I'm Concierge. Chat with me here for anything you need.\n" +
+ '# Splitting the bill is as easy as a conversation!\n' +
'\n' +
'To split an expense:\n' +
'1. From the home screen, click the green + button > Request money.\n' +
'2. Enter an amount or scan a receipt, then choose who you want to split it with.\n' +
'\n' +
"We'll send a request to each person so they can pay you back. Let me know if you have any questions!",
- [onboardingChoices.LOOKING_AROUND]:
- '# Welcome to Expensify!\n' +
- "Hi there, I'm Concierge. Chat with me here for anything you need.\n" +
- '\n' +
- "Expensify is best known for expense and corporate card management, but we do a lot more than that. Let me know what you're interested in and I'll help get you started.",
},
REPORT_FIELD_TITLE_FIELD_ID: 'text_title',
@@ -4353,8 +4348,10 @@ const CONST = {
} as const;
type Country = keyof typeof CONST.ALL_COUNTRIES;
+
type IOUType = ValueOf;
+type IOUAction = ValueOf;
-export type {Country, IOUType};
+export type {Country, IOUAction, IOUType};
export default CONST;
diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts
index 3959f76a626f..819680db0e8a 100755
--- a/src/ONYXKEYS.ts
+++ b/src/ONYXKEYS.ts
@@ -126,7 +126,7 @@ const ONYXKEYS = {
/** The NVP with the last payment method used per policy */
NVP_LAST_PAYMENT_METHOD: 'nvp_private_lastPaymentMethod',
- /** This NVP holds to most recent waypoints that a person has used when creating a distance request */
+ /** This NVP holds to most recent waypoints that a person has used when creating a distance expense */
NVP_RECENT_WAYPOINTS: 'expensify_recentWaypoints',
/** This NVP will be `true` if the user has ever dismissed the engagement modal on either OldDot or NewDot. If it becomes true it should stay true forever. */
@@ -312,15 +312,19 @@ const ONYXKEYS = {
COLLECTION: {
DOWNLOAD: 'download_',
POLICY: 'policy_',
- POLICY_MEMBERS: 'policyMembers_',
POLICY_DRAFTS: 'policyDrafts_',
- POLICY_MEMBERS_DRAFTS: 'policyMembersDrafts_',
POLICY_JOIN_MEMBER: 'policyJoinMember_',
POLICY_CATEGORIES: 'policyCategories_',
POLICY_RECENTLY_USED_CATEGORIES: 'policyRecentlyUsedCategories_',
POLICY_TAGS: 'policyTags_',
POLICY_RECENTLY_USED_TAGS: 'nvp_recentlyUsedTags_',
+ // Whether the policy's connection data was attempted to be fetched in
+ // the current user session. As this state only exists client-side, it
+ // should not be included as part of the policy object. The policy
+ // object should mirror the data as it's stored in the database.
+ POLICY_HAS_CONNECTIONS_DATA_BEEN_FETCHED: 'policyHasConnectionsDataBeenFetched_',
OLD_POLICY_RECENTLY_USED_TAGS: 'policyRecentlyUsedTags_',
+ POLICY_CONNECTION_SYNC_PROGRESS: 'policyConnectionSyncProgress_',
WORKSPACE_INVITE_MEMBERS_DRAFT: 'workspaceInviteMembersDraft_',
WORKSPACE_INVITE_MESSAGE_DRAFT: 'workspaceInviteMessageDraft_',
REPORT: 'report_',
@@ -340,20 +344,17 @@ const ONYXKEYS = {
TRANSACTION: 'transactions_',
TRANSACTION_VIOLATIONS: 'transactionViolations_',
TRANSACTION_DRAFT: 'transactionsDraft_',
-
- // Holds temporary transactions used during the creation and edit flow
+ SKIP_CONFIRMATION: 'skipConfirmation_',
TRANSACTION_BACKUP: 'transactionsBackup_',
SPLIT_TRANSACTION_DRAFT: 'splitTransactionDraft_',
PRIVATE_NOTES_DRAFT: 'privateNotesDraft_',
NEXT_STEP: 'reportNextStep_',
- // Manual request tab selector
+ // Manual expense tab selector
SELECTED_TAB: 'selectedTab_',
/** This is deprecated, but needed for a migration, so we still need to include it here so that it will be initialized in Onyx.init */
DEPRECATED_POLICY_MEMBER_LIST: 'policyMemberList_',
-
- POLICY_CONNECTION_SYNC_PROGRESS: 'policyConnectionSyncProgress_',
},
/** List of Form ids */
@@ -526,10 +527,9 @@ type OnyxCollectionValuesMapping = {
[ONYXKEYS.COLLECTION.POLICY_DRAFTS]: OnyxTypes.Policy;
[ONYXKEYS.COLLECTION.POLICY_CATEGORIES]: OnyxTypes.PolicyCategories;
[ONYXKEYS.COLLECTION.POLICY_TAGS]: OnyxTypes.PolicyTagList;
- [ONYXKEYS.COLLECTION.POLICY_MEMBERS]: OnyxTypes.PolicyMembers;
- [ONYXKEYS.COLLECTION.POLICY_MEMBERS_DRAFTS]: OnyxTypes.PolicyMember;
[ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES]: OnyxTypes.RecentlyUsedCategories;
- [ONYXKEYS.COLLECTION.DEPRECATED_POLICY_MEMBER_LIST]: OnyxTypes.PolicyMembers;
+ [ONYXKEYS.COLLECTION.POLICY_HAS_CONNECTIONS_DATA_BEEN_FETCHED]: boolean;
+ [ONYXKEYS.COLLECTION.DEPRECATED_POLICY_MEMBER_LIST]: OnyxTypes.PolicyEmployeeList;
[ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT]: OnyxTypes.InvitedEmailsToAccountIDs;
[ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MESSAGE_DRAFT]: string;
[ONYXKEYS.COLLECTION.REPORT]: OnyxTypes.Report;
@@ -545,6 +545,7 @@ type OnyxCollectionValuesMapping = {
[ONYXKEYS.COLLECTION.SECURITY_GROUP]: OnyxTypes.SecurityGroup;
[ONYXKEYS.COLLECTION.TRANSACTION]: OnyxTypes.Transaction;
[ONYXKEYS.COLLECTION.TRANSACTION_DRAFT]: OnyxTypes.Transaction;
+ [ONYXKEYS.COLLECTION.SKIP_CONFIRMATION]: boolean;
[ONYXKEYS.COLLECTION.TRANSACTION_BACKUP]: OnyxTypes.Transaction;
[ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS]: OnyxTypes.TransactionViolations;
[ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT]: OnyxTypes.Transaction;
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index ec2bf11957e1..46f2e2fef049 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -1,5 +1,6 @@
import type {ValueOf} from 'type-fest';
import type CONST from './CONST';
+import type {IOUAction, IOUType} from './CONST';
import type {IOURequestType} from './libs/actions/IOU';
import type AssertTypesNotEqual from './types/utils/AssertTypesNotEqual';
@@ -22,13 +23,18 @@ const ROUTES = {
ALL_SETTINGS: 'all-settings',
+ SEARCH: {
+ route: '/search/:query',
+ getRoute: (query: string) => `search/${query}` as const,
+ },
+
// This is a utility route used to go to the user's concierge chat, or the sign-in page if the user's not authenticated
CONCIERGE: 'concierge',
FLAG_COMMENT: {
route: 'flag/:reportID/:reportActionID',
getRoute: (reportID: string, reportActionID: string) => `flag/${reportID}/${reportActionID}` as const,
},
- SEARCH: 'search',
+ CHAT_FINDER: 'chat-finder',
DETAILS: {
route: 'details',
getRoute: (login: string) => `details?login=${encodeURIComponent(login)}` as const,
@@ -205,7 +211,7 @@ const ROUTES = {
EDIT_REQUEST: {
route: 'r/:threadReportID/edit/:field/:tagIndex?',
getRoute: (threadReportID: string, field: ValueOf, tagIndex?: number) =>
- `r/${threadReportID}/edit/${field}${typeof tagIndex === 'number' ? `/${tagIndex}` : ''}` as const,
+ `r/${threadReportID}/edit/${field as string}${typeof tagIndex === 'number' ? `/${tagIndex}` : ''}` as const,
},
EDIT_CURRENCY_REQUEST: {
route: 'r/:threadReportID/edit/currency',
@@ -274,7 +280,7 @@ const ROUTES = {
EDIT_SPLIT_BILL: {
route: `r/:reportID/split/:reportActionID/edit/:field/:tagIndex?`,
getRoute: (reportID: string, reportActionID: string, field: ValueOf, tagIndex?: number) =>
- `r/${reportID}/split/${reportActionID}/edit/${field}${typeof tagIndex === 'number' ? `/${tagIndex}` : ''}` as const,
+ `r/${reportID}/split/${reportActionID}/edit/${field as string}${typeof tagIndex === 'number' ? `/${tagIndex}` : ''}` as const,
},
TASK_TITLE: {
route: 'r/:reportID/title',
@@ -306,122 +312,113 @@ const ROUTES = {
},
MONEY_REQUEST_PARTICIPANTS: {
route: ':iouType/new/participants/:reportID?',
- getRoute: (iouType: string, reportID = '') => `${iouType}/new/participants/${reportID}` as const,
+ getRoute: (iouType: IOUType, reportID = '') => `${iouType}/new/participants/${reportID}` as const,
},
MONEY_REQUEST_HOLD_REASON: {
- route: ':iouType/edit/reason/:transactionID?',
- getRoute: (iouType: string, transactionID: string, reportID: string, backTo: string) => `${iouType}/edit/reason/${transactionID}?backTo=${backTo}&reportID=${reportID}` as const,
+ route: ':type/edit/reason/:transactionID?',
+ getRoute: (type: ValueOf, transactionID: string, reportID: string, backTo: string) =>
+ `${type}/edit/reason/${transactionID}?backTo=${backTo}&reportID=${reportID}` as const,
},
MONEY_REQUEST_MERCHANT: {
route: ':iouType/new/merchant/:reportID?',
- getRoute: (iouType: string, reportID = '') => `${iouType}/new/merchant/${reportID}` as const,
+ getRoute: (iouType: IOUType, reportID = '') => `${iouType}/new/merchant/${reportID}` as const,
},
MONEY_REQUEST_RECEIPT: {
route: ':iouType/new/receipt/:reportID?',
- getRoute: (iouType: string, reportID = '') => `${iouType}/new/receipt/${reportID}` as const,
+ getRoute: (iouType: IOUType, reportID = '') => `${iouType}/new/receipt/${reportID}` as const,
},
MONEY_REQUEST_CREATE: {
route: ':action/:iouType/start/:transactionID/:reportID',
- getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string) =>
- `${action}/${iouType}/start/${transactionID}/${reportID}` as const,
+ getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string) => `${action as string}/${iouType as string}/start/${transactionID}/${reportID}` as const,
},
MONEY_REQUEST_STEP_CONFIRMATION: {
route: ':action/:iouType/confirmation/:transactionID/:reportID',
- getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string) =>
- `${action}/${iouType}/confirmation/${transactionID}/${reportID}` as const,
+ getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string) =>
+ `${action as string}/${iouType as string}/confirmation/${transactionID}/${reportID}` as const,
},
MONEY_REQUEST_STEP_AMOUNT: {
route: ':action/:iouType/amount/:transactionID/:reportID',
- getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, backTo = '') =>
- getUrlWithBackToParam(`${action}/${iouType}/amount/${transactionID}/${reportID}`, backTo),
+ getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '') =>
+ getUrlWithBackToParam(`${action as string}/${iouType as string}/amount/${transactionID}/${reportID}`, backTo),
},
MONEY_REQUEST_STEP_TAX_RATE: {
route: ':action/:iouType/taxRate/:transactionID/:reportID?',
- getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, backTo = '') =>
- getUrlWithBackToParam(`${action}/${iouType}/taxRate/${transactionID}/${reportID}`, backTo),
+ getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '') =>
+ getUrlWithBackToParam(`${action as string}/${iouType as string}/taxRate/${transactionID}/${reportID}`, backTo),
},
MONEY_REQUEST_STEP_TAX_AMOUNT: {
route: ':action/:iouType/taxAmount/:transactionID/:reportID?',
- getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, backTo = '') =>
- getUrlWithBackToParam(`${action}/${iouType}/taxAmount/${transactionID}/${reportID}`, backTo),
+ getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '') =>
+ getUrlWithBackToParam(`${action as string}/${iouType as string}/taxAmount/${transactionID}/${reportID}`, backTo),
},
MONEY_REQUEST_STEP_CATEGORY: {
route: ':action/:iouType/category/:transactionID/:reportID/:reportActionID?',
- getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, backTo = '', reportActionID?: string) =>
- getUrlWithBackToParam(`${action}/${iouType}/category/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}`, backTo),
+ getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '', reportActionID?: string) =>
+ getUrlWithBackToParam(`${action as string}/${iouType as string}/category/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}`, backTo),
},
MONEY_REQUEST_STEP_CURRENCY: {
route: ':action/:iouType/currency/:transactionID/:reportID/:pageIndex?',
- getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, pageIndex = '', currency = '', backTo = '') =>
- getUrlWithBackToParam(`${action}/${iouType}/currency/${transactionID}/${reportID}/${pageIndex}?currency=${currency}`, backTo),
+ getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, pageIndex = '', currency = '', backTo = '') =>
+ getUrlWithBackToParam(`${action as string}/${iouType as string}/currency/${transactionID}/${reportID}/${pageIndex}?currency=${currency}`, backTo),
},
MONEY_REQUEST_STEP_DATE: {
route: ':action/:iouType/date/:transactionID/:reportID',
- getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, backTo = '') =>
- getUrlWithBackToParam(`${action}/${iouType}/date/${transactionID}/${reportID}`, backTo),
+ getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '') =>
+ getUrlWithBackToParam(`${action as string}/${iouType as string}/date/${transactionID}/${reportID}`, backTo),
},
MONEY_REQUEST_STEP_DESCRIPTION: {
route: ':action/:iouType/description/:transactionID/:reportID/:reportActionID?',
- getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, backTo = '', reportActionID?: string) =>
- getUrlWithBackToParam(`${action}/${iouType}/description/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}`, backTo),
+ getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '', reportActionID?: string) =>
+ getUrlWithBackToParam(`${action as string}/${iouType as string}/description/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}`, backTo),
},
MONEY_REQUEST_STEP_DISTANCE: {
route: ':action/:iouType/distance/:transactionID/:reportID',
- getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, backTo = '') =>
- getUrlWithBackToParam(`${action}/${iouType}/distance/${transactionID}/${reportID}`, backTo),
+ getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '') =>
+ getUrlWithBackToParam(`${action as string}/${iouType as string}/distance/${transactionID}/${reportID}`, backTo),
},
MONEY_REQUEST_STEP_MERCHANT: {
route: ':action/:iouType/merchant/:transactionID/:reportID',
- getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, backTo = '') =>
- getUrlWithBackToParam(`${action}/${iouType}/merchant/${transactionID}/${reportID}`, backTo),
+ getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '') =>
+ getUrlWithBackToParam(`${action as string}/${iouType as string}/merchant/${transactionID}/${reportID}`, backTo),
},
MONEY_REQUEST_STEP_PARTICIPANTS: {
route: ':action/:iouType/participants/:transactionID/:reportID',
- getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '', action: ValueOf = 'create') =>
- getUrlWithBackToParam(`${action}/${iouType}/participants/${transactionID}/${reportID}`, backTo),
+ getRoute: (iouType: IOUType, transactionID: string, reportID: string, backTo = '', action: IOUAction = 'create') =>
+ getUrlWithBackToParam(`${action as string}/${iouType as string}/participants/${transactionID}/${reportID}`, backTo),
},
MONEY_REQUEST_STEP_SCAN: {
route: ':action/:iouType/scan/:transactionID/:reportID',
- getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string, backTo = '') =>
- getUrlWithBackToParam(`${action}/${iouType}/scan/${transactionID}/${reportID}`, backTo),
+ getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backTo = '') =>
+ getUrlWithBackToParam(`${action as string}/${iouType as string}/scan/${transactionID}/${reportID}`, backTo),
},
MONEY_REQUEST_STEP_TAG: {
route: ':action/:iouType/tag/:orderWeight/:transactionID/:reportID/:reportActionID?',
- getRoute: (
- action: ValueOf,
- iouType: ValueOf,
- orderWeight: number,
- transactionID: string,
- reportID: string,
- backTo = '',
- reportActionID?: string,
- ) => getUrlWithBackToParam(`${action}/${iouType}/tag/${orderWeight}/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}`, backTo),
+ getRoute: (action: IOUAction, iouType: IOUType, orderWeight: number, transactionID: string, reportID: string, backTo = '', reportActionID?: string) =>
+ getUrlWithBackToParam(`${action as string}/${iouType as string}/tag/${orderWeight}/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}`, backTo),
},
MONEY_REQUEST_STEP_WAYPOINT: {
route: ':action/:iouType/waypoint/:transactionID/:reportID/:pageIndex',
- getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID?: string, pageIndex = '', backTo = '') =>
- getUrlWithBackToParam(`${action}/${iouType}/waypoint/${transactionID}/${reportID}/${pageIndex}`, backTo),
+ getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID?: string, pageIndex = '', backTo = '') =>
+ getUrlWithBackToParam(`${action as string}/${iouType as string}/waypoint/${transactionID}/${reportID}/${pageIndex}`, backTo),
},
// This URL is used as a redirect to one of the create tabs below. This is so that we can message users with a link
// straight to those flows without needing to have optimistic transaction and report IDs.
MONEY_REQUEST_START: {
route: 'start/:iouType/:iouRequestType',
- getRoute: (iouType: ValueOf, iouRequestType: IOURequestType) => `start/${iouType}/${iouRequestType}` as const,
+ getRoute: (iouType: IOUType, iouRequestType: IOURequestType) => `start/${iouType as string}/${iouRequestType}` as const,
},
MONEY_REQUEST_CREATE_TAB_DISTANCE: {
route: ':action/:iouType/start/:transactionID/:reportID/distance',
- getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string) =>
- `create/${iouType}/start/${transactionID}/${reportID}/distance` as const,
+ getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string) => `create/${iouType as string}/start/${transactionID}/${reportID}/distance` as const,
},
MONEY_REQUEST_CREATE_TAB_MANUAL: {
route: ':action/:iouType/start/:transactionID/:reportID/manual',
- getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string) =>
- `${action}/${iouType}/start/${transactionID}/${reportID}/manual` as const,
+ getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string) =>
+ `${action as string}/${iouType as string}/start/${transactionID}/${reportID}/manual` as const,
},
MONEY_REQUEST_CREATE_TAB_SCAN: {
route: ':action/:iouType/start/:transactionID/:reportID/scan',
- getRoute: (action: ValueOf, iouType: ValueOf, transactionID: string, reportID: string) =>
- `create/${iouType}/start/${transactionID}/${reportID}/scan` as const,
+ getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string) => `create/${iouType as string}/start/${transactionID}/${reportID}/scan` as const,
},
MONEY_REQUEST_STATE_SELECTOR: {
@@ -563,7 +560,7 @@ const ROUTES = {
route: 'settings/workspaces/:policyID/members',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/members` as const,
},
- WORKSPACE_ACCOUNTING: {
+ POLICY_ACCOUNTING: {
route: 'settings/workspaces/:policyID/accounting',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting` as const,
},
@@ -654,7 +651,7 @@ const ROUTES = {
WORKSPACE_OWNER_CHANGE_CHECK: {
route: 'settings/workspaces/:policyID/change-owner/:accountID/:error',
getRoute: (policyID: string, accountID: number, error: ValueOf) =>
- `settings/workspaces/${policyID}/change-owner/${accountID}/${error}` as const,
+ `settings/workspaces/${policyID}/change-owner/${accountID}/${error as string}` as const,
},
WORKSPACE_TAX_CREATE: {
route: 'settings/workspaces/:policyID/taxes/new',
@@ -707,27 +704,27 @@ const ROUTES = {
route: 'r/:reportID/transaction/:transactionID/receipt',
getRoute: (reportID: string, transactionID: string) => `r/${reportID}/transaction/${transactionID}/receipt` as const,
},
- WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_IMPORT: {
+ POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_IMPORT: {
route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import` as const,
},
- WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_CHART_OF_ACCOUNTS: {
+ POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_CHART_OF_ACCOUNTS: {
route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import/accounts',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import/accounts` as const,
},
- WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_CLASSES: {
+ POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_CLASSES: {
route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import/classes',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import/classes` as const,
},
- WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_CUSTOMERS: {
+ POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_CUSTOMERS: {
route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import/customers',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import/customers` as const,
},
- WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_LOCATIONS: {
+ POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_LOCATIONS: {
route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import/locations',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import/locations` as const,
},
- WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_TAXES: {
+ POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_TAXES: {
route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import/taxes',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import/taxes` as const,
},
@@ -742,7 +739,7 @@ const HYBRID_APP_ROUTES = {
MONEY_REQUEST_CREATE: '/request/new/scan',
} as const;
-export {getUrlWithBackToParam, HYBRID_APP_ROUTES};
+export {HYBRID_APP_ROUTES, getUrlWithBackToParam};
export default ROUTES;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -762,4 +759,4 @@ type RouteIsPlainString = AssertTypesNotEqual {
// Check if the file dimensions indicate corruption
- // The width/height for corrupt file is -1 on android native and 0 on ios native
- if (!fileData.width || !fileData.height || (fileData.width <= 0 && fileData.height <= 0)) {
+ // The width/height for a corrupted file is -1 on android native and 0 on ios native
+ // We must check only numeric values because the width/height can be undefined for non-image files
+ if ((typeof fileData.width === 'number' && fileData.width <= 0) || (typeof fileData.height === 'number' && fileData.height <= 0)) {
showImageCorruptionAlert();
return Promise.resolve();
}
diff --git a/src/components/ConfirmedRoute.tsx b/src/components/ConfirmedRoute.tsx
index 17c5097b8154..351129a5ee3e 100644
--- a/src/components/ConfirmedRoute.tsx
+++ b/src/components/ConfirmedRoute.tsx
@@ -24,7 +24,7 @@ type ConfirmedRoutePropsOnyxProps = {
};
type ConfirmedRouteProps = ConfirmedRoutePropsOnyxProps & {
- /** Transaction that stores the distance request data */
+ /** Transaction that stores the distance expense data */
transaction: OnyxEntry;
};
diff --git a/src/components/ConnectToQuickbooksOnlineButton/index.native.tsx b/src/components/ConnectToQuickbooksOnlineButton/index.native.tsx
new file mode 100644
index 000000000000..4d482cb92ead
--- /dev/null
+++ b/src/components/ConnectToQuickbooksOnlineButton/index.native.tsx
@@ -0,0 +1,68 @@
+import React, {useState} from 'react';
+import type {OnyxEntry} from 'react-native-onyx';
+import {withOnyx} from 'react-native-onyx';
+import {WebView} from 'react-native-webview';
+import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView';
+import Button from '@components/Button';
+import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
+import HeaderWithBackButton from '@components/HeaderWithBackButton';
+import Modal from '@components/Modal';
+import useLocalize from '@hooks/useLocalize';
+import {getQuickBooksOnlineSetupLink} from '@libs/actions/connections/QuickBooksOnline';
+import CONST from '@src/CONST';
+import ONYXKEYS from '@src/ONYXKEYS';
+import type {Session} from '@src/types/onyx';
+import type {ConnectToQuickbooksOnlineButtonProps} from './types';
+
+type ConnectToQuickbooksOnlineButtonOnyxProps = {
+ /** Session info for the currently logged in user. */
+ session: OnyxEntry;
+};
+
+const renderLoading = () => ;
+
+function ConnectToQuickbooksOnlineButton({policyID, session}: ConnectToQuickbooksOnlineButtonProps & ConnectToQuickbooksOnlineButtonOnyxProps) {
+ const [isModalOpen, setIsModalOpen] = useState(false);
+ const {translate} = useLocalize();
+
+ return (
+ <>
+