diff --git a/android/app/build.gradle b/android/app/build.gradle
index 62d4c09179df..b538d4595dc4 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -110,8 +110,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1009003402
- versionName "9.0.34-2"
+ versionCode 1009003500
+ versionName "9.0.35-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/assets/images/bookmark.svg b/assets/images/bookmark.svg
new file mode 100644
index 000000000000..d7c1a8397b37
--- /dev/null
+++ b/assets/images/bookmark.svg
@@ -0,0 +1 @@
+
diff --git a/assets/images/companyCards/card-amex.svg b/assets/images/companyCards/card-amex.svg
new file mode 100644
index 000000000000..5282ca095760
--- /dev/null
+++ b/assets/images/companyCards/card-amex.svg
@@ -0,0 +1,39 @@
+
+
\ No newline at end of file
diff --git a/assets/images/companyCards/card-bank_of_america.svg b/assets/images/companyCards/card-bank_of_america.svg
new file mode 100644
index 000000000000..684a6a0a28f5
--- /dev/null
+++ b/assets/images/companyCards/card-bank_of_america.svg
@@ -0,0 +1,37 @@
+
+
\ No newline at end of file
diff --git a/assets/images/companyCards/card-brex.svg b/assets/images/companyCards/card-brex.svg
new file mode 100644
index 000000000000..068c9a054d39
--- /dev/null
+++ b/assets/images/companyCards/card-brex.svg
@@ -0,0 +1,35 @@
+
+
\ No newline at end of file
diff --git a/assets/images/companyCards/card-capital_one.svg b/assets/images/companyCards/card-capital_one.svg
new file mode 100644
index 000000000000..0a324710ae5d
--- /dev/null
+++ b/assets/images/companyCards/card-capital_one.svg
@@ -0,0 +1,42 @@
+
+
\ No newline at end of file
diff --git a/assets/images/companyCards/card-chase.svg b/assets/images/companyCards/card-chase.svg
new file mode 100644
index 000000000000..511453169813
--- /dev/null
+++ b/assets/images/companyCards/card-chase.svg
@@ -0,0 +1,32 @@
+
+
\ No newline at end of file
diff --git a/assets/images/companyCards/card-citi.svg b/assets/images/companyCards/card-citi.svg
new file mode 100644
index 000000000000..2d2bf71b1312
--- /dev/null
+++ b/assets/images/companyCards/card-citi.svg
@@ -0,0 +1,43 @@
+
+
\ No newline at end of file
diff --git a/assets/images/companyCards/card-expensify.svg b/assets/images/companyCards/card-expensify.svg
new file mode 100644
index 000000000000..d6b847d8f74f
--- /dev/null
+++ b/assets/images/companyCards/card-expensify.svg
@@ -0,0 +1,72 @@
+
+
\ No newline at end of file
diff --git a/assets/images/companyCards/card-mastercard.svg b/assets/images/companyCards/card-mastercard.svg
new file mode 100644
index 000000000000..b1a698fe9acc
--- /dev/null
+++ b/assets/images/companyCards/card-mastercard.svg
@@ -0,0 +1,40 @@
+
+
\ No newline at end of file
diff --git a/assets/images/companyCards/card-other.svg b/assets/images/companyCards/card-other.svg
new file mode 100644
index 000000000000..11ff21285626
--- /dev/null
+++ b/assets/images/companyCards/card-other.svg
@@ -0,0 +1,35 @@
+
+
\ No newline at end of file
diff --git a/assets/images/companyCards/card-stripe.svg b/assets/images/companyCards/card-stripe.svg
new file mode 100644
index 000000000000..e4c874452309
--- /dev/null
+++ b/assets/images/companyCards/card-stripe.svg
@@ -0,0 +1,45 @@
+
+
\ No newline at end of file
diff --git a/assets/images/companyCards/card-visa.svg b/assets/images/companyCards/card-visa.svg
new file mode 100644
index 000000000000..52d0f727a960
--- /dev/null
+++ b/assets/images/companyCards/card-visa.svg
@@ -0,0 +1,60 @@
+
+
\ No newline at end of file
diff --git a/assets/images/companyCards/card-wells_fargo.svg b/assets/images/companyCards/card-wells_fargo.svg
new file mode 100644
index 000000000000..66402710de97
--- /dev/null
+++ b/assets/images/companyCards/card-wells_fargo.svg
@@ -0,0 +1,35 @@
+
+
\ No newline at end of file
diff --git a/assets/images/user-eye.svg b/assets/images/user-eye.svg
new file mode 100644
index 000000000000..2265b4892ded
--- /dev/null
+++ b/assets/images/user-eye.svg
@@ -0,0 +1,12 @@
+
+
\ No newline at end of file
diff --git a/desktop/package-lock.json b/desktop/package-lock.json
index 1187b3182187..152ad1a4c5ba 100644
--- a/desktop/package-lock.json
+++ b/desktop/package-lock.json
@@ -9,7 +9,7 @@
"dependencies": {
"electron-context-menu": "^2.3.0",
"electron-log": "^4.4.8",
- "electron-updater": "^6.3.3",
+ "electron-updater": "^6.3.4",
"mime-types": "^2.1.35",
"node-machine-id": "^1.1.12"
},
@@ -154,9 +154,9 @@
"integrity": "sha512-QQ4GvrXO+HkgqqEOYbi+DHL7hj5JM+nHi/j+qrN9zeeXVKy8ZABgbu4CnG+BBqDZ2+tbeq9tUC4DZfIWFU5AZA=="
},
"node_modules/electron-updater": {
- "version": "6.3.3",
- "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.3.3.tgz",
- "integrity": "sha512-Kj1u6kfyxUyatnspvKa6qhGn82rMZfUD03WOvCGJ12PyRss/AC8kkYsN9IrJihKTlN8nRwTjZ1JM2UUXoD0KsA==",
+ "version": "6.3.4",
+ "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.3.4.tgz",
+ "integrity": "sha512-uZUo7p1Y53G4tl6Cgw07X1yF8Jlz6zhaL7CQJDZ1fVVkOaBfE2cWtx80avwDVi8jHp+I/FWawrMgTAeCCNIfAg==",
"license": "MIT",
"dependencies": {
"builder-util-runtime": "9.2.5",
@@ -165,7 +165,7 @@
"lazy-val": "^1.0.5",
"lodash.escaperegexp": "^4.1.2",
"lodash.isequal": "^4.5.0",
- "semver": "^7.3.8",
+ "semver": "^7.6.3",
"tiny-typed-emitter": "^2.1.0"
}
},
@@ -276,17 +276,6 @@
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
},
- "node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
@@ -349,12 +338,10 @@
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg=="
},
"node_modules/semver": {
- "version": "7.5.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
- "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
@@ -445,11 +432,6 @@
"engines": {
"node": ">=8"
}
- },
- "node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
}
},
"dependencies": {
@@ -552,9 +534,9 @@
"integrity": "sha512-QQ4GvrXO+HkgqqEOYbi+DHL7hj5JM+nHi/j+qrN9zeeXVKy8ZABgbu4CnG+BBqDZ2+tbeq9tUC4DZfIWFU5AZA=="
},
"electron-updater": {
- "version": "6.3.3",
- "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.3.3.tgz",
- "integrity": "sha512-Kj1u6kfyxUyatnspvKa6qhGn82rMZfUD03WOvCGJ12PyRss/AC8kkYsN9IrJihKTlN8nRwTjZ1JM2UUXoD0KsA==",
+ "version": "6.3.4",
+ "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.3.4.tgz",
+ "integrity": "sha512-uZUo7p1Y53G4tl6Cgw07X1yF8Jlz6zhaL7CQJDZ1fVVkOaBfE2cWtx80avwDVi8jHp+I/FWawrMgTAeCCNIfAg==",
"requires": {
"builder-util-runtime": "9.2.5",
"fs-extra": "^10.1.0",
@@ -562,7 +544,7 @@
"lazy-val": "^1.0.5",
"lodash.escaperegexp": "^4.1.2",
"lodash.isequal": "^4.5.0",
- "semver": "^7.3.8",
+ "semver": "^7.6.3",
"tiny-typed-emitter": "^2.1.0"
}
},
@@ -650,14 +632,6 @@
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
},
- "lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "requires": {
- "yallist": "^4.0.0"
- }
- },
"mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
@@ -705,12 +679,9 @@
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg=="
},
"semver": {
- "version": "7.5.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
- "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
- "requires": {
- "lru-cache": "^6.0.0"
- }
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="
},
"slice-ansi": {
"version": "3.0.0",
@@ -774,11 +745,6 @@
"modify-filename": "^1.1.0",
"path-exists": "^4.0.0"
}
- },
- "yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
}
}
}
diff --git a/desktop/package.json b/desktop/package.json
index cf3c3f4354b3..6c2158a74978 100644
--- a/desktop/package.json
+++ b/desktop/package.json
@@ -6,7 +6,7 @@
"dependencies": {
"electron-context-menu": "^2.3.0",
"electron-log": "^4.4.8",
- "electron-updater": "^6.3.3",
+ "electron-updater": "^6.3.4",
"mime-types": "^2.1.35",
"node-machine-id": "^1.1.12"
},
diff --git a/docs/articles/expensify-classic/domains/Claim-And-Verify-A-Domain.md b/docs/articles/expensify-classic/domains/Claim-And-Verify-A-Domain.md
index 509961b026e5..ed74224c622e 100644
--- a/docs/articles/expensify-classic/domains/Claim-And-Verify-A-Domain.md
+++ b/docs/articles/expensify-classic/domains/Claim-And-Verify-A-Domain.md
@@ -37,7 +37,7 @@ To complete this step, you must have a Control workspace, and you’ll need acce
Log in to your DNS service provider (which may be the website you purchased the domain from or that currently hosts the domain, like NameCheap, GoDaddy, DNSMadeEasy, or Amazon Route53. You may need to contact your company’s IT department if your domain is managed internally).
Find the page for DNS records, which might be labeled as DNS Management or Zone File Editor.
-
Add a new TXT record and set the value as 532F6180D8.
+
Add a new TXT record with the value assigned to you in the domain verification settings.
Save your changes.
In Expensify, click the Domain Members tab and click Verify.
@@ -48,4 +48,4 @@ After successful verification, an email will be sent to all members of the Expen
To add an additional domain, you’ll have to first add your email address that is connected with your domain as your [primary or secondary email] (https://help.expensify.com/articles/expensify-classic/settings/account-settings/Change-or-add-email-address) (for example, if your domain is yourcompany.com, then you want to add and verify your email address @yourcompany.com as your primary or secondary email address). Then you can complete the steps above to add the domain.
-
\ No newline at end of file
+
diff --git a/docs/articles/new-expensify/expenses-&-payments/Set-up-your-wallet.md b/docs/articles/new-expensify/expenses-&-payments/Set-up-your-wallet.md
index 7f7b6196707d..fbed5048e575 100644
--- a/docs/articles/new-expensify/expenses-&-payments/Set-up-your-wallet.md
+++ b/docs/articles/new-expensify/expenses-&-payments/Set-up-your-wallet.md
@@ -2,24 +2,21 @@
title: Set up your wallet
description: Send and receive payments by adding your payment account
---
-
-To send and receive money using Expensify, you’ll first need to set up your Expensify Wallet by adding your payment account.
-![The Wallet Tab where you can add a personal bank account]({{site.url}}/assets/images/ExpensifyHelp_R5_Wallet_1.png){:width="100%"}
+The Expensify Wallet allows you to receive peer-to-peer payments. To set this up, you’ll first need to connect a personal bank account.
-{% include selector.html values="desktop, mobile" %}
+![The Wallet Tab where you can add a personal bank account]({{site.url}}/assets/images/ExpensifyHelp_R5_Wallet_1.png){:width="100%"}
-{% include option.html value="desktop" %}
+**To enable the Expensify Wallet:**
1. Click your profile image or icon in the bottom left menu.
2. Click **Wallet** in the left menu.
3. Click **Enable wallet**.
-4. If you haven’t already added your bank account, click **Continue** and follow the prompts to add your bank account details with Plaid. If you have already connected your bank account, you’ll skip to the next step.
+4. If you haven’t already added your bank account, click **Continue** and follow the prompts to [connect your personal bank account](https://help.expensify.com/articles/new-expensify/expenses-&-payments/Connect-a-Personal-Bank-Account) via Plaid. If you have already connected your bank account, you’ll skip to the next step.
{% include info.html %}
Plaid is an encrypted third-party financial data platform that Expensify uses to securely verify your banking information.
{% include end-info.html %}
-{:start="5"}
5. Enter your personal details (including your name, address, date of birth, phone number, and the last 4 digits of your social security number).
6. Click **Save & continue**.
7. Review the Onfido terms and click **Accept**.
@@ -27,28 +24,3 @@ Plaid is an encrypted third-party financial data platform that Expensify uses to
9. Follow the prompts on your mobile device to submit your ID with Onfido.
When your ID is uploaded successfully, Onfido closes automatically. You can return to your Expensify Wallet to verify that it is now enabled. Once enabled, you are ready to send and receive payments.
-{% include end-option.html %}
-
-{% include option.html value="mobile" %}
-1. Tap your profile image or icon in the bottom menu.
-2. Tap **Wallet**.
-3. Tap **Enable wallet**.
-4. If you haven’t already added your bank account, tap **Continue** and follow the prompts to add your bank account details with Plaid. If you have already connected your bank account, you’ll skip to the next step.
-
-{% include info.html %}
-Plaid is an encrypted third-party financial data platform that Expensify uses to securely verify your banking information.
-{% include end-info.html %}
-
-{:start="5"}
-5. Enter your personal details (including your name, address, date of birth, phone number, and the last 4 digits of your social security number).
-6. Tap **Save & continue**.
-7. Review the Onfido terms and tap **Accept**.
-8. Follow the prompts to submit your ID with Onfido. When your ID is uploaded successfully, Onfido closes automatically.
-9. Tap **Enable wallet** again to enable payments for the wallet.
-
-Once enabled, you are ready to send and receive payments.
-{% include end-option.html %}
-
-{% include end-selector.html %}
-
-
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index 51e5a5c0d512..29571b4f35b5 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -19,7 +19,7 @@
CFBundlePackageTypeAPPLCFBundleShortVersionString
- 9.0.34
+ 9.0.35CFBundleSignature????CFBundleURLTypes
@@ -40,7 +40,7 @@
CFBundleVersion
- 9.0.34.2
+ 9.0.35.0FullStoryOrgId
diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist
index 3d812816f637..3e9f78796e29 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -15,10 +15,10 @@
CFBundlePackageTypeBNDLCFBundleShortVersionString
- 9.0.34
+ 9.0.35CFBundleSignature????CFBundleVersion
- 9.0.34.2
+ 9.0.35.0
diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist
index 34a3a269f0ff..a90aadadd876 100644
--- a/ios/NotificationServiceExtension/Info.plist
+++ b/ios/NotificationServiceExtension/Info.plist
@@ -11,9 +11,9 @@
CFBundleName$(PRODUCT_NAME)CFBundleShortVersionString
- 9.0.34
+ 9.0.35CFBundleVersion
- 9.0.34.2
+ 9.0.35.0NSExtensionNSExtensionPointIdentifier
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 945f47b718bf..6d879d275cdb 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -27,8 +27,6 @@ PODS:
- AppAuth/ExternalUserAgent (1.7.5):
- AppAuth/Core
- boost (1.84.0)
- - BVLinearGradient (2.8.1):
- - React-Core
- DoubleConversion (1.1.6)
- EXAV (14.0.7):
- ExpoModulesCore
@@ -1689,7 +1687,7 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- - react-native-keyboard-controller (1.12.2):
+ - react-native-keyboard-controller (1.12.7):
- DoubleConversion
- glog
- hermes-engine
@@ -2362,7 +2360,7 @@ PODS:
- RNGoogleSignin (10.0.1):
- GoogleSignIn (~> 7.0)
- React-Core
- - RNLiveMarkdown (0.1.120):
+ - RNLiveMarkdown (0.1.143):
- DoubleConversion
- glog
- hermes-engine
@@ -2382,9 +2380,9 @@ PODS:
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- - RNLiveMarkdown/common (= 0.1.120)
+ - RNLiveMarkdown/common (= 0.1.143)
- Yoga
- - RNLiveMarkdown/common (0.1.120):
+ - RNLiveMarkdown/common (0.1.143):
- DoubleConversion
- glog
- hermes-engine
@@ -2584,7 +2582,7 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- - RNShare (10.0.2):
+ - RNShare (11.0.2):
- DoubleConversion
- glog
- hermes-engine
@@ -2692,7 +2690,6 @@ PODS:
DEPENDENCIES:
- AirshipServiceExtension
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
- - BVLinearGradient (from `../node_modules/react-native-linear-gradient`)
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- EXAV (from `../node_modules/expo-av/ios`)
- EXImageLoader (from `../node_modules/expo-image-loader/ios`)
@@ -2856,8 +2853,6 @@ SPEC REPOS:
EXTERNAL SOURCES:
boost:
:podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec"
- BVLinearGradient:
- :path: "../node_modules/react-native-linear-gradient"
DoubleConversion:
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
EXAV:
@@ -3102,7 +3097,6 @@ SPEC CHECKSUMS:
AirshipServiceExtension: 9c73369f426396d9fb9ff222d86d842fac76ba46
AppAuth: 501c04eda8a8d11f179dbe8637b7a91bb7e5d2fa
boost: 26992d1adf73c1c7676360643e687aee6dda994b
- BVLinearGradient: 421743791a59d259aec53f4c58793aad031da2ca
DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5
EXAV: afa491e598334bbbb92a92a2f4dd33d7149ad37f
EXImageLoader: ab589d67d6c5f2c33572afea9917304418566334
@@ -3184,7 +3178,7 @@ SPEC CHECKSUMS:
react-native-geolocation: b9bd12beaf0ebca61a01514517ca8455bd26fa06
react-native-image-picker: f8a13ff106bcc7eb00c71ce11fdc36aac2a44440
react-native-key-command: aae312752fcdfaa2240be9a015fc41ce54087546
- react-native-keyboard-controller: 5075321af7b1c834cfb9582230659d032c963278
+ react-native-keyboard-controller: b9b2ba987e3c6f4b6534740e88d11dccc34f69dc
react-native-launch-arguments: 5f41e0abf88a15e3c5309b8875d6fd5ac43df49d
react-native-netinfo: fb5112b1fa754975485884ae85a3fb6a684f49d5
react-native-pager-view: 94195f1bf32e7f78359fa20057c97e632364a08b
@@ -3235,14 +3229,14 @@ SPEC CHECKSUMS:
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
RNGestureHandler: 8781e2529230a1bc3ea8d75e5c3cd071b6c6aed7
RNGoogleSignin: ccaa4a81582cf713eea562c5dd9dc1961a715fd0
- RNLiveMarkdown: cfc927fc0b1182e364237c72692e079107c6f5f1
+ RNLiveMarkdown: e44918843c2638692348f39eafc275698baf0444
RNLocalize: d4b8af4e442d4bcca54e68fc687a2129b4d71a81
rnmapbox-maps: 460d6ff97ae49c7d5708c3212c6521697c36a0c4
RNPermissions: 0b1429b55af59d1d08b75a8be2459f65a8ac3f28
RNReactNativeHapticFeedback: a15b431d2903bc2eb3474ff8d9a05d3e67a70199
RNReanimated: 76901886830e1032f16bbf820153f7dc3f02d51d
RNScreens: de6e57426ba0e6cbc3fb5b4f496e7f08cb2773c2
- RNShare: a3c2fbbca5682530b65ff405b34c91dad1e22442
+ RNShare: bd4fe9b95d1ee89a200778cc0753ebe650154bb0
RNSound: 6c156f925295bdc83e8e422e7d8b38d33bc71852
RNSVG: 1079f96b39a35753d481a20e30603fd6fc4f6fa9
SDWebImage: 066c47b573f408f18caa467d71deace7c0f8280d
@@ -3252,7 +3246,7 @@ SPEC CHECKSUMS:
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
Turf: aa2ede4298009639d10db36aba1a7ebaad072a5e
VisionCamera: c6c8aa4b028501fc87644550fbc35a537d4da3fb
- Yoga: 2a45d7e59592db061217551fd3bbe2dd993817ae
+ Yoga: a1d7895431387402a674fd0d1c04ec85e87909b8
PODFILE CHECKSUM: e479ec84cb53e5fd463486d71dfee91708d3fd9a
diff --git a/metro.config.js b/metro.config.js
index 0ad3bafc011e..c6e4ba6bb4ec 100644
--- a/metro.config.js
+++ b/metro.config.js
@@ -1,10 +1,13 @@
-const {getDefaultConfig} = require('expo/metro-config');
+const {getDefaultConfig: getExpoDefaultConfig} = require('expo/metro-config');
+const {getDefaultConfig: getReactNativeDefaultConfig} = require('@react-native/metro-config');
+
const {mergeConfig} = require('@react-native/metro-config');
const defaultAssetExts = require('metro-config/src/defaults/defaults').assetExts;
const defaultSourceExts = require('metro-config/src/defaults/defaults').sourceExts;
require('dotenv').config();
-const defaultConfig = getDefaultConfig(__dirname);
+const defaultConfig = getReactNativeDefaultConfig(__dirname);
+const expoConfig = getExpoDefaultConfig(__dirname);
const isE2ETesting = process.env.E2E_TESTING === 'true';
const e2eSourceExts = ['e2e.js', 'e2e.ts', 'e2e.tsx'];
@@ -23,4 +26,4 @@ const config = {
},
};
-module.exports = mergeConfig(defaultConfig, config);
+module.exports = mergeConfig(defaultConfig, expoConfig, config);
diff --git a/package-lock.json b/package-lock.json
index 18bac528ad99..65c27bf203cc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,33 +1,27 @@
{
"name": "new.expensify",
- "version": "9.0.34-2",
+ "version": "9.0.35-0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "new.expensify",
- "version": "9.0.34-2",
+ "version": "9.0.35-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.120",
+ "@expensify/react-native-live-markdown": "0.1.143",
"@expo/metro-runtime": "~3.2.3",
"@formatjs/intl-datetimeformat": "^6.12.5",
"@formatjs/intl-listformat": "^7.5.7",
"@formatjs/intl-locale": "^4.0.0",
"@formatjs/intl-numberformat": "^8.10.3",
"@formatjs/intl-pluralrules": "^5.2.14",
- "@fullstory/babel-plugin-annotate-react": "github:fullstorydev/fullstory-babel-plugin-annotate-react#ryanwang/react-native-web-demo",
- "@fullstory/babel-plugin-react-native": "^1.2.1",
"@fullstory/browser": "^2.0.3",
"@fullstory/react-native": "^1.4.2",
"@gorhom/portal": "^1.0.14",
"@invertase/react-native-apple-authentication": "^2.2.2",
- "@kie/act-js": "^2.6.2",
- "@kie/mock-github": "2.0.1",
"@onfido/react-native-sdk": "10.6.0",
"@react-native-camera-roll/camera-roll": "7.4.0",
"@react-native-clipboard/clipboard": "^1.13.2",
@@ -45,7 +39,6 @@
"@react-ng/bounds-observer": "^0.2.1",
"@rnmapbox/maps": "10.1.30",
"@shopify/flash-list": "1.7.1",
- "@types/mime-db": "^1.43.5",
"@ua/react-native-airship": "19.2.1",
"@vue/preload-webpack-plugin": "^2.0.0",
"awesome-phonenumber": "^5.4.0",
@@ -98,9 +91,8 @@
"react-native-image-picker": "^7.0.3",
"react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#cb392140db4953a283590d7cf93b4d0461baa2a9",
"react-native-key-command": "^1.0.8",
- "react-native-keyboard-controller": "^1.12.2",
+ "react-native-keyboard-controller": "1.12.7",
"react-native-launch-arguments": "^4.0.2",
- "react-native-linear-gradient": "^2.8.1",
"react-native-localize": "^2.2.6",
"react-native-modal": "^13.0.0",
"react-native-onyx": "2.0.66",
@@ -117,7 +109,7 @@
"react-native-render-html": "6.3.1",
"react-native-safe-area-context": "4.10.9",
"react-native-screens": "3.34.0",
- "react-native-share": "^10.0.2",
+ "react-native-share": "11.0.2",
"react-native-sound": "^0.11.2",
"react-native-svg": "15.6.0",
"react-native-tab-view": "^3.5.2",
@@ -125,14 +117,12 @@
"react-native-view-shot": "3.8.0",
"react-native-vision-camera": "4.0.0-beta.13",
"react-native-web": "^0.19.12",
- "react-native-web-linear-gradient": "^1.1.2",
"react-native-web-sound": "^0.1.3",
"react-native-webview": "13.8.6",
"react-pdf": "^7.7.3",
"react-plaid-link": "3.3.2",
"react-web-config": "^1.0.0",
"react-webcam": "^7.1.1",
- "react-window": "^1.8.9",
"semver": "^7.5.2",
"xlsx": "file:vendor/xlsx-0.20.3.tgz"
},
@@ -143,6 +133,8 @@
"@babel/parser": "^7.22.16",
"@babel/plugin-proposal-class-properties": "^7.12.1",
"@babel/plugin-proposal-export-namespace-from": "^7.18.9",
+ "@babel/plugin-proposal-private-methods": "^7.18.6",
+ "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@babel/preset-env": "^7.20.0",
"@babel/preset-flow": "^7.12.13",
"@babel/preset-react": "^7.10.4",
@@ -153,6 +145,7 @@
"@callstack/reassure-compare": "^1.0.0-rc.4",
"@dword-design/eslint-plugin-import-alias": "^5.0.0",
"@electron/notarize": "^2.1.0",
+ "@fullstory/babel-plugin-annotate-react": "^2.3.0",
"@jest/globals": "^29.5.0",
"@ngneat/falso": "^7.1.1",
"@octokit/core": "4.0.4",
@@ -185,6 +178,7 @@
"@types/js-yaml": "^4.0.5",
"@types/lodash": "^4.14.195",
"@types/mapbox-gl": "^2.7.13",
+ "@types/mime-db": "^1.43.5",
"@types/node": "^20.11.5",
"@types/pusher-js": "^5.1.0",
"@types/react": "^18.2.6",
@@ -233,7 +227,6 @@
"eslint-plugin-you-dont-need-lodash-underscore": "^6.14.0",
"html-webpack-plugin": "^5.5.0",
"http-server": "^14.1.1",
- "husky": "^9.1.5",
"jest": "29.4.1",
"jest-circus": "29.4.1",
"jest-cli": "29.4.1",
@@ -269,8 +262,7 @@
"webpack-bundle-analyzer": "^4.5.0",
"webpack-cli": "^5.0.4",
"webpack-dev-server": "^5.0.4",
- "webpack-merge": "^5.8.0",
- "yaml": "^2.2.1"
+ "webpack-merge": "^5.8.0"
},
"engines": {
"node": "20.15.1",
@@ -1349,7 +1341,10 @@
},
"node_modules/@babel/plugin-proposal-private-methods": {
"version": "7.18.6",
- "license": "MIT",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz",
+ "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==",
+ "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.",
+ "dev": true,
"dependencies": {
"@babel/helper-create-class-features-plugin": "^7.18.6",
"@babel/helper-plugin-utils": "^7.18.6"
@@ -1363,7 +1358,10 @@
},
"node_modules/@babel/plugin-proposal-private-property-in-object": {
"version": "7.21.11",
- "license": "MIT",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz",
+ "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==",
+ "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.",
+ "dev": true,
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.18.6",
"@babel/helper-create-class-features-plugin": "^7.21.0",
@@ -3571,9 +3569,9 @@
}
},
"node_modules/@expensify/react-native-live-markdown": {
- "version": "0.1.120",
- "resolved": "https://registry.npmjs.org/@expensify/react-native-live-markdown/-/react-native-live-markdown-0.1.120.tgz",
- "integrity": "sha512-MQ8/gPb2u8U1HPClwKhrf2sqjCpi56g5aEhonYOejMPd7kUKpV0nlccSJgy5UEwJFhtxL+cl7SgnXq8xJNwxng==",
+ "version": "0.1.143",
+ "resolved": "https://registry.npmjs.org/@expensify/react-native-live-markdown/-/react-native-live-markdown-0.1.143.tgz",
+ "integrity": "sha512-hZXYjKyTl/b2p7Ig9qhoB7cfVtTTcoE2cWvea8NJT3f5ZYckdyHDAgHI4pg0S0N68jP205Sk5pzqlltZUpZk5w==",
"workspaces": [
"parser",
"example",
@@ -5565,11 +5563,13 @@
},
"node_modules/@fullstory/babel-plugin-annotate-react": {
"version": "2.3.0",
- "resolved": "git+ssh://git@github.com/fullstorydev/fullstory-babel-plugin-annotate-react.git#25c26dadb644d5355e381a4ea4ca1cd05af4a8f6"
+ "resolved": "https://registry.npmjs.org/@fullstory/babel-plugin-annotate-react/-/babel-plugin-annotate-react-2.3.0.tgz",
+ "integrity": "sha512-gYLUL6Tu0exbvTIhK9nSCaztmqBlQAm07Fvtl/nKTc+lxwFkcX9vR8RrdTbyjJZKbPaA5EMlExQ6GeLCXkfm5g=="
},
"node_modules/@fullstory/babel-plugin-react-native": {
- "version": "1.2.1",
- "license": "MIT",
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@fullstory/babel-plugin-react-native/-/babel-plugin-react-native-1.3.0.tgz",
+ "integrity": "sha512-JSWV/fn5sEAUHhXD8CvyVTHAtttNjokLHguZ7pxh2EbG1TOg5yBCvXnF+yQ6heS5PKJen7TMS2mdBaXtnYEPIQ==",
"dependencies": {
"@babel/parser": "^7.0.0",
"@babel/types": "^7.0.0"
@@ -6516,59 +6516,9 @@
"react-native": "*"
}
},
- "node_modules/@kie/act-js": {
- "version": "2.6.2",
- "hasInstallScript": true,
- "license": "SEE LICENSE IN LICENSE",
- "dependencies": {
- "@kie/mock-github": "^2.0.0",
- "adm-zip": "^0.5.10",
- "ajv": "^8.12.0",
- "bin-links": "^4.0.1",
- "express": "^4.18.1",
- "follow-redirects": "^1.15.2",
- "tar": "^6.1.13",
- "yaml": "^2.1.3"
- },
- "bin": {
- "act-js": "bin/act"
- }
- },
- "node_modules/@kie/mock-github": {
- "version": "2.0.1",
- "license": "SEE LICENSE IN LICENSE",
- "dependencies": {
- "@octokit/openapi-types-ghec": "^18.0.0",
- "ajv": "^8.11.0",
- "express": "^4.18.1",
- "fast-glob": "^3.2.12",
- "fs-extra": "^10.1.0",
- "nock": "^13.2.7",
- "simple-git": "^3.8.0",
- "totalist": "^3.0.0"
- }
- },
- "node_modules/@kie/mock-github/node_modules/fs-extra": {
- "version": "10.1.0",
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@kie/mock-github/node_modules/totalist": {
- "version": "3.0.1",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@kwsites/file-exists": {
"version": "1.1.1",
+ "dev": true,
"license": "MIT",
"dependencies": {
"debug": "^4.1.1"
@@ -6576,6 +6526,7 @@
},
"node_modules/@kwsites/promise-deferred": {
"version": "1.1.1",
+ "dev": true,
"license": "MIT"
},
"node_modules/@leichtgewicht/ip-codec": {
@@ -6984,10 +6935,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@octokit/openapi-types-ghec": {
- "version": "18.1.1",
- "license": "MIT"
- },
"node_modules/@octokit/plugin-paginate-rest": {
"version": "3.1.0",
"dev": true,
@@ -15741,7 +15688,8 @@
"node_modules/@types/mime-db": {
"version": "1.43.5",
"resolved": "https://registry.npmjs.org/@types/mime-db/-/mime-db-1.43.5.tgz",
- "integrity": "sha512-/bfTiIUTNPUBnwnYvUxXAre5MhD88jgagLEQiQtIASjU+bwxd8kS/ASDA4a8ufd8m0Lheu6eeMJHEUpLHoJ28A=="
+ "integrity": "sha512-/bfTiIUTNPUBnwnYvUxXAre5MhD88jgagLEQiQtIASjU+bwxd8kS/ASDA4a8ufd8m0Lheu6eeMJHEUpLHoJ28A==",
+ "dev": true
},
"node_modules/@types/minimatch": {
"version": "3.0.5",
@@ -17013,13 +16961,6 @@
"node": ">=0.4.0"
}
},
- "node_modules/adm-zip": {
- "version": "0.5.10",
- "license": "MIT",
- "engines": {
- "node": ">=6.0"
- }
- },
"node_modules/agent-base": {
"version": "6.0.2",
"license": "MIT",
@@ -17569,6 +17510,7 @@
},
"node_modules/array-flatten": {
"version": "1.1.1",
+ "dev": true,
"license": "MIT"
},
"node_modules/array-includes": {
@@ -19114,40 +19056,6 @@
"node": "*"
}
},
- "node_modules/bin-links": {
- "version": "4.0.2",
- "license": "ISC",
- "dependencies": {
- "cmd-shim": "^6.0.0",
- "npm-normalize-package-bin": "^3.0.0",
- "read-cmd-shim": "^4.0.0",
- "write-file-atomic": "^5.0.0"
- },
- "engines": {
- "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
- }
- },
- "node_modules/bin-links/node_modules/signal-exit": {
- "version": "4.1.0",
- "license": "ISC",
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/bin-links/node_modules/write-file-atomic": {
- "version": "5.0.1",
- "license": "ISC",
- "dependencies": {
- "imurmurhash": "^0.1.4",
- "signal-exit": "^4.0.1"
- },
- "engines": {
- "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
- }
- },
"node_modules/binary-extensions": {
"version": "2.2.0",
"devOptional": true,
@@ -19206,6 +19114,7 @@
"version": "1.20.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"bytes": "3.1.2",
@@ -19230,6 +19139,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
@@ -19239,6 +19149,7 @@
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
@@ -19248,6 +19159,7 @@
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
@@ -19260,6 +19172,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true,
"license": "MIT"
},
"node_modules/bonjour-service": {
@@ -20404,13 +20317,6 @@
"node": ">=6"
}
},
- "node_modules/cmd-shim": {
- "version": "6.0.1",
- "license": "ISC",
- "engines": {
- "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
- }
- },
"node_modules/co": {
"version": "4.6.0",
"license": "MIT",
@@ -20919,6 +20825,7 @@
},
"node_modules/content-disposition": {
"version": "0.5.4",
+ "dev": true,
"license": "MIT",
"dependencies": {
"safe-buffer": "5.2.1"
@@ -20929,6 +20836,7 @@
},
"node_modules/content-disposition/node_modules/safe-buffer": {
"version": "5.2.1",
+ "dev": true,
"funding": [
{
"type": "github",
@@ -20949,6 +20857,7 @@
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
@@ -20971,6 +20880,7 @@
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
@@ -20978,6 +20888,7 @@
},
"node_modules/cookie-signature": {
"version": "1.0.6",
+ "dev": true,
"license": "MIT"
},
"node_modules/copy-descriptor": {
@@ -25014,6 +24925,7 @@
"version": "4.20.0",
"resolved": "https://registry.npmjs.org/express/-/express-4.20.0.tgz",
"integrity": "sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"accepts": "~1.3.8",
@@ -25054,6 +24966,7 @@
},
"node_modules/express/node_modules/debug": {
"version": "2.6.9",
+ "dev": true,
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
@@ -25063,6 +24976,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
@@ -25072,6 +24986,7 @@
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "dev": true,
"license": "MIT",
"bin": {
"mime": "cli.js"
@@ -25082,12 +24997,14 @@
},
"node_modules/express/node_modules/ms": {
"version": "2.0.0",
+ "dev": true,
"license": "MIT"
},
"node_modules/express/node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
+ "dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.0.4"
@@ -25101,6 +25018,7 @@
},
"node_modules/express/node_modules/safe-buffer": {
"version": "5.2.1",
+ "dev": true,
"funding": [
{
"type": "github",
@@ -25121,6 +25039,7 @@
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"debug": "2.6.9",
@@ -25145,6 +25064,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
@@ -25154,6 +25074,7 @@
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
"license": "MIT"
},
"node_modules/extend-shallow": {
@@ -25493,6 +25414,7 @@
},
"node_modules/finalhandler": {
"version": "1.2.0",
+ "dev": true,
"license": "MIT",
"dependencies": {
"debug": "2.6.9",
@@ -25509,6 +25431,7 @@
},
"node_modules/finalhandler/node_modules/debug": {
"version": "2.6.9",
+ "dev": true,
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
@@ -25516,6 +25439,7 @@
},
"node_modules/finalhandler/node_modules/ms": {
"version": "2.0.0",
+ "dev": true,
"license": "MIT"
},
"node_modules/find-babel-config": {
@@ -25682,6 +25606,7 @@
},
"node_modules/follow-redirects": {
"version": "1.15.5",
+ "dev": true,
"funding": [
{
"type": "individual",
@@ -25896,6 +25821,7 @@
},
"node_modules/forwarded": {
"version": "0.2.0",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
@@ -27179,21 +27105,6 @@
"ms": "^2.0.0"
}
},
- "node_modules/husky": {
- "version": "9.1.5",
- "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.5.tgz",
- "integrity": "sha512-rowAVRUBfI0b4+niA4SJMhfQwc107VLkBUgEYYAOQAbqDCnra1nYh83hF/MDmhYs9t9n1E3DuKOrs2LYNC+0Ag==",
- "dev": true,
- "bin": {
- "husky": "bin.js"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/typicode"
- }
- },
"node_modules/hyperdyperid": {
"version": "1.2.0",
"dev": true,
@@ -30701,6 +30612,7 @@
},
"node_modules/json-stringify-safe": {
"version": "5.0.1",
+ "dev": true,
"license": "ISC"
},
"node_modules/json5": {
@@ -31920,6 +31832,7 @@
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
@@ -32021,6 +31934,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
+ "dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -32049,6 +31963,7 @@
},
"node_modules/methods": {
"version": "1.1.2",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
@@ -32937,19 +32852,6 @@
"node": ">=12.0.0"
}
},
- "node_modules/nock": {
- "version": "13.3.3",
- "license": "MIT",
- "dependencies": {
- "debug": "^4.1.0",
- "json-stringify-safe": "^5.0.1",
- "lodash": "^4.17.21",
- "propagate": "^2.0.0"
- },
- "engines": {
- "node": ">= 10.13"
- }
- },
"node_modules/node-abi": {
"version": "3.65.0",
"dev": true,
@@ -33254,13 +33156,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/npm-normalize-package-bin": {
- "version": "3.0.1",
- "license": "ISC",
- "engines": {
- "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
- }
- },
"node_modules/npm-package-arg": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-7.0.0.tgz",
@@ -34401,6 +34296,7 @@
"version": "0.1.10",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
"integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==",
+ "dev": true,
"license": "MIT"
},
"node_modules/path-type": {
@@ -34934,19 +34830,13 @@
"version": "16.13.1",
"license": "MIT"
},
- "node_modules/propagate": {
- "version": "2.0.1",
- "license": "MIT",
- "engines": {
- "node": ">= 8"
- }
- },
"node_modules/protocol-buffers-schema": {
"version": "3.6.0",
"license": "MIT"
},
"node_modules/proxy-addr": {
"version": "2.0.7",
+ "dev": true,
"license": "MIT",
"dependencies": {
"forwarded": "0.2.0",
@@ -35189,6 +35079,7 @@
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
+ "dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.0.6"
@@ -35325,6 +35216,7 @@
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"bytes": "3.1.2",
@@ -35340,6 +35232,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
@@ -35349,6 +35242,7 @@
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
@@ -35954,13 +35848,13 @@
"license": "MIT"
},
"node_modules/react-native-keyboard-controller": {
- "version": "1.12.2",
- "resolved": "https://registry.npmjs.org/react-native-keyboard-controller/-/react-native-keyboard-controller-1.12.2.tgz",
- "integrity": "sha512-10Sy0+neSHGJxOmOxrUJR8TQznnrQ+jTFQtM1PP6YnblNQeAw1eOa+lO6YLGenRr5WuNSMZbks/3Ay0e2yMKLw==",
+ "version": "1.12.7",
+ "resolved": "https://registry.npmjs.org/react-native-keyboard-controller/-/react-native-keyboard-controller-1.12.7.tgz",
+ "integrity": "sha512-eccg0JtZk5n/up3I7pEg606Bo9eLsgVGtTPnmdv507AJg5UeAAMEhx93P7YXDZmwJ+38oFn3DbL7a0eIUE3Lxw==",
"peerDependencies": {
"react": "*",
"react-native": "*",
- "react-native-reanimated": ">=2.3.0"
+ "react-native-reanimated": ">=2.11.0"
}
},
"node_modules/react-native-launch-arguments": {
@@ -35971,14 +35865,6 @@
"react-native": ">=0.60.0-rc.0 <1.0.x"
}
},
- "node_modules/react-native-linear-gradient": {
- "version": "2.8.1",
- "license": "MIT",
- "peerDependencies": {
- "react": "*",
- "react-native": "*"
- }
- },
"node_modules/react-native-localize": {
"version": "2.2.6",
"license": "MIT",
@@ -37002,8 +36888,9 @@
}
},
"node_modules/react-native-share": {
- "version": "10.0.2",
- "license": "MIT",
+ "version": "11.0.2",
+ "resolved": "https://registry.npmjs.org/react-native-share/-/react-native-share-11.0.2.tgz",
+ "integrity": "sha512-7W7sb9qd8RjVEIMhbYc3MU//qGUNxf1XAqd3SlO/ivz89ed1jP1yUwYOcUK2Kf1NDY/kwWbPCkEKa6ZGVlcsOQ==",
"engines": {
"node": ">=16"
}
@@ -37094,13 +36981,6 @@
"react-dom": "^18.0.0"
}
},
- "node_modules/react-native-web-linear-gradient": {
- "version": "1.1.2",
- "license": "MIT",
- "peerDependencies": {
- "react-native-web": "*"
- }
- },
"node_modules/react-native-web-sound": {
"version": "0.1.3",
"license": "MIT",
@@ -38672,13 +38552,6 @@
"read-binary-file-arch": "cli.js"
}
},
- "node_modules/read-cmd-shim": {
- "version": "4.0.0",
- "license": "ISC",
- "engines": {
- "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
- }
- },
"node_modules/read-config-file": {
"version": "6.4.0",
"dev": true,
@@ -39942,6 +39815,7 @@
},
"node_modules/simple-git": {
"version": "3.24.0",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@kwsites/file-exists": "^1.1.1",
@@ -42070,6 +41944,7 @@
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"media-typer": "0.3.0",
diff --git a/package.json b/package.json
index 9673b5136c4a..b303d5d29f78 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "9.0.34-2",
+ "version": "9.0.35-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.",
@@ -67,24 +67,18 @@
"web:prod": "http-server ./dist --cors"
},
"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.120",
+ "@expensify/react-native-live-markdown": "0.1.143",
"@expo/metro-runtime": "~3.2.3",
"@formatjs/intl-datetimeformat": "^6.12.5",
"@formatjs/intl-listformat": "^7.5.7",
"@formatjs/intl-locale": "^4.0.0",
"@formatjs/intl-numberformat": "^8.10.3",
"@formatjs/intl-pluralrules": "^5.2.14",
- "@fullstory/babel-plugin-annotate-react": "github:fullstorydev/fullstory-babel-plugin-annotate-react#ryanwang/react-native-web-demo",
- "@fullstory/babel-plugin-react-native": "^1.2.1",
"@fullstory/browser": "^2.0.3",
"@fullstory/react-native": "^1.4.2",
"@gorhom/portal": "^1.0.14",
"@invertase/react-native-apple-authentication": "^2.2.2",
- "@kie/act-js": "^2.6.2",
- "@kie/mock-github": "2.0.1",
"@onfido/react-native-sdk": "10.6.0",
"@react-native-camera-roll/camera-roll": "7.4.0",
"@react-native-clipboard/clipboard": "^1.13.2",
@@ -102,7 +96,6 @@
"@react-ng/bounds-observer": "^0.2.1",
"@rnmapbox/maps": "10.1.30",
"@shopify/flash-list": "1.7.1",
- "@types/mime-db": "^1.43.5",
"@ua/react-native-airship": "19.2.1",
"@vue/preload-webpack-plugin": "^2.0.0",
"awesome-phonenumber": "^5.4.0",
@@ -155,9 +148,8 @@
"react-native-image-picker": "^7.0.3",
"react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#cb392140db4953a283590d7cf93b4d0461baa2a9",
"react-native-key-command": "^1.0.8",
- "react-native-keyboard-controller": "^1.12.2",
+ "react-native-keyboard-controller": "1.12.7",
"react-native-launch-arguments": "^4.0.2",
- "react-native-linear-gradient": "^2.8.1",
"react-native-localize": "^2.2.6",
"react-native-modal": "^13.0.0",
"react-native-onyx": "2.0.66",
@@ -174,7 +166,7 @@
"react-native-render-html": "6.3.1",
"react-native-safe-area-context": "4.10.9",
"react-native-screens": "3.34.0",
- "react-native-share": "^10.0.2",
+ "react-native-share": "11.0.2",
"react-native-sound": "^0.11.2",
"react-native-svg": "15.6.0",
"react-native-tab-view": "^3.5.2",
@@ -182,14 +174,12 @@
"react-native-view-shot": "3.8.0",
"react-native-vision-camera": "4.0.0-beta.13",
"react-native-web": "^0.19.12",
- "react-native-web-linear-gradient": "^1.1.2",
"react-native-web-sound": "^0.1.3",
"react-native-webview": "13.8.6",
"react-pdf": "^7.7.3",
"react-plaid-link": "3.3.2",
"react-web-config": "^1.0.0",
"react-webcam": "^7.1.1",
- "react-window": "^1.8.9",
"semver": "^7.5.2",
"xlsx": "file:vendor/xlsx-0.20.3.tgz"
},
@@ -200,6 +190,8 @@
"@babel/parser": "^7.22.16",
"@babel/plugin-proposal-class-properties": "^7.12.1",
"@babel/plugin-proposal-export-namespace-from": "^7.18.9",
+ "@babel/plugin-proposal-private-methods": "^7.18.6",
+ "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@babel/preset-env": "^7.20.0",
"@babel/preset-flow": "^7.12.13",
"@babel/preset-react": "^7.10.4",
@@ -210,6 +202,7 @@
"@callstack/reassure-compare": "^1.0.0-rc.4",
"@dword-design/eslint-plugin-import-alias": "^5.0.0",
"@electron/notarize": "^2.1.0",
+ "@fullstory/babel-plugin-annotate-react": "^2.3.0",
"@jest/globals": "^29.5.0",
"@ngneat/falso": "^7.1.1",
"@octokit/core": "4.0.4",
@@ -242,6 +235,7 @@
"@types/js-yaml": "^4.0.5",
"@types/lodash": "^4.14.195",
"@types/mapbox-gl": "^2.7.13",
+ "@types/mime-db": "^1.43.5",
"@types/node": "^20.11.5",
"@types/pusher-js": "^5.1.0",
"@types/react": "^18.2.6",
@@ -290,7 +284,6 @@
"eslint-plugin-you-dont-need-lodash-underscore": "^6.14.0",
"html-webpack-plugin": "^5.5.0",
"http-server": "^14.1.1",
- "husky": "^9.1.5",
"jest": "29.4.1",
"jest-circus": "29.4.1",
"jest-cli": "29.4.1",
@@ -326,8 +319,7 @@
"webpack-bundle-analyzer": "^4.5.0",
"webpack-cli": "^5.0.4",
"webpack-dev-server": "^5.0.4",
- "webpack-merge": "^5.8.0",
- "yaml": "^2.2.1"
+ "webpack-merge": "^5.8.0"
},
"overrides": {
"react-native": "0.75.2",
diff --git a/patches/@expensify+react-native-live-markdown+0.1.120+001+intial.patch b/patches/@expensify+react-native-live-markdown+0.1.120+001+intial.patch
deleted file mode 100644
index 4e59d206268e..000000000000
--- a/patches/@expensify+react-native-live-markdown+0.1.120+001+intial.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-diff --git a/node_modules/@expensify/react-native-live-markdown/android/src/main/java/com/expensify/livemarkdown/LiveMarkdownModule.java b/node_modules/@expensify/react-native-live-markdown/android/src/main/java/com/expensify/livemarkdown/LiveMarkdownModule.java
-index ed1428b..80641ce 100644
---- a/node_modules/@expensify/react-native-live-markdown/android/src/main/java/com/expensify/livemarkdown/LiveMarkdownModule.java
-+++ b/node_modules/@expensify/react-native-live-markdown/android/src/main/java/com/expensify/livemarkdown/LiveMarkdownModule.java
-@@ -1,7 +1,6 @@
- package com.expensify.livemarkdown;
-
- import com.facebook.react.bridge.ReactApplicationContext;
--import com.facebook.react.bridge.UIManager;
- import com.facebook.react.fabric.FabricUIManager;
- import com.facebook.react.uimanager.UIManagerHelper;
- import com.facebook.react.uimanager.common.UIManagerType;
-diff --git a/node_modules/@expensify/react-native-live-markdown/android/src/main/java/com/expensify/livemarkdown/MarkdownUtils.java b/node_modules/@expensify/react-native-live-markdown/android/src/main/java/com/expensify/livemarkdown/MarkdownUtils.java
-index 7711d8b..0000caa 100644
---- a/node_modules/@expensify/react-native-live-markdown/android/src/main/java/com/expensify/livemarkdown/MarkdownUtils.java
-+++ b/node_modules/@expensify/react-native-live-markdown/android/src/main/java/com/expensify/livemarkdown/MarkdownUtils.java
-@@ -1,6 +1,5 @@
- package com.expensify.livemarkdown;
-
--import static com.facebook.infer.annotation.ThreadConfined.UI;
-
- import android.content.res.AssetManager;
- import android.text.SpannableStringBuilder;
-@@ -8,10 +7,7 @@ import android.text.Spanned;
-
- import androidx.annotation.NonNull;
-
--import com.facebook.infer.annotation.Assertions;
--import com.facebook.infer.annotation.ThreadConfined;
--import com.facebook.react.bridge.UiThreadUtil;
--import com.facebook.react.views.text.CustomLineHeightSpan;
-+import com.facebook.react.views.text.internal.span.CustomLineHeightSpan;
- import com.facebook.soloader.SoLoader;
-
- import org.json.JSONArray;
-diff --git a/node_modules/@expensify/react-native-live-markdown/android/src/main/new_arch/CMakeLists.txt b/node_modules/@expensify/react-native-live-markdown/android/src/main/new_arch/CMakeLists.txt
-index f5dfedf..1609c60 100644
---- a/node_modules/@expensify/react-native-live-markdown/android/src/main/new_arch/CMakeLists.txt
-+++ b/node_modules/@expensify/react-native-live-markdown/android/src/main/new_arch/CMakeLists.txt
-@@ -42,6 +42,8 @@ target_link_libraries(
- ReactAndroid::rrc_textinput
- ReactAndroid::react_render_textlayoutmanager
- ReactAndroid::react_render_imagemanager
-+ ReactAndroid::reactnativejni
-+ ReactAndroid::mapbufferjni
- fabricjni
- fbjni
- folly_runtime
diff --git a/patches/@expensify+react-native-live-markdown+0.1.120+002+text-layout-manager.patch b/patches/@expensify+react-native-live-markdown+0.1.120+002+text-layout-manager.patch
deleted file mode 100644
index 24f327649253..000000000000
--- a/patches/@expensify+react-native-live-markdown+0.1.120+002+text-layout-manager.patch
+++ /dev/null
@@ -1,102 +0,0 @@
-diff --git a/node_modules/@expensify/react-native-live-markdown/android/src/main/java/com/expensify/livemarkdown/CustomMountingManager.java b/node_modules/@expensify/react-native-live-markdown/android/src/main/java/com/expensify/livemarkdown/CustomMountingManager.java
-index 1b4381b..7e3aebe 100644
---- a/node_modules/@expensify/react-native-live-markdown/android/src/main/java/com/expensify/livemarkdown/CustomMountingManager.java
-+++ b/node_modules/@expensify/react-native-live-markdown/android/src/main/java/com/expensify/livemarkdown/CustomMountingManager.java
-@@ -13,7 +13,6 @@ import android.text.TextPaint;
- import androidx.annotation.NonNull;
- import androidx.annotation.Nullable;
-
--import com.facebook.common.logging.FLog;
- import com.facebook.react.bridge.ReactContext;
- import com.facebook.react.bridge.ReadableMap;
- import com.facebook.react.common.mapbuffer.MapBuffer;
-@@ -21,8 +20,8 @@ import com.facebook.react.fabric.mounting.MountingManager;
- import com.facebook.react.uimanager.PixelUtil;
- import com.facebook.react.uimanager.ViewManagerRegistry;
- import com.facebook.react.views.text.TextAttributeProps;
--import com.facebook.react.views.text.TextInlineViewPlaceholderSpan;
--import com.facebook.react.views.text.TextLayoutManagerMapBuffer;
-+import com.facebook.react.views.text.internal.span.TextInlineViewPlaceholderSpan;
-+import com.facebook.react.views.text.TextLayoutManager;
- import com.facebook.yoga.YogaMeasureMode;
- import com.facebook.yoga.YogaMeasureOutput;
-
-@@ -63,7 +62,7 @@ public class CustomMountingManager extends MountingManager {
- @Nullable float[] attachmentsPositions) {
-
- Spannable text =
-- TextLayoutManagerMapBuffer.getOrCreateSpannableForText(context, attributedString, null);
-+ TextLayoutManager.getOrCreateSpannableForText(context, attributedString, null);
-
- if (text == null) {
- return 0;
-@@ -71,14 +70,14 @@ public class CustomMountingManager extends MountingManager {
-
- int textBreakStrategy =
- TextAttributeProps.getTextBreakStrategy(
-- paragraphAttributes.getString(TextLayoutManagerMapBuffer.PA_KEY_TEXT_BREAK_STRATEGY));
-+ paragraphAttributes.getString(TextLayoutManager.PA_KEY_TEXT_BREAK_STRATEGY));
- boolean includeFontPadding =
-- paragraphAttributes.contains(TextLayoutManagerMapBuffer.PA_KEY_INCLUDE_FONT_PADDING)
-- ? paragraphAttributes.getBoolean(TextLayoutManagerMapBuffer.PA_KEY_INCLUDE_FONT_PADDING)
-+ paragraphAttributes.contains(TextLayoutManager.PA_KEY_INCLUDE_FONT_PADDING)
-+ ? paragraphAttributes.getBoolean(TextLayoutManager.PA_KEY_INCLUDE_FONT_PADDING)
- : DEFAULT_INCLUDE_FONT_PADDING;
- int hyphenationFrequency =
- TextAttributeProps.getHyphenationFrequency(
-- paragraphAttributes.getString(TextLayoutManagerMapBuffer.PA_KEY_HYPHENATION_FREQUENCY));
-+ paragraphAttributes.getString(TextLayoutManager.PA_KEY_HYPHENATION_FREQUENCY));
-
- // StaticLayout returns wrong metrics for the last line if it's empty, add something to the
- // last line so it's measured correctly
-@@ -89,13 +88,15 @@ public class CustomMountingManager extends MountingManager {
- text = sb;
- }
-
-+ Layout.Alignment alignment = TextLayoutManager.getTextAlignment(attributedString, text);
-+
- markdownUtils.applyMarkdownFormatting((SpannableStringBuilder)text);
-
- BoringLayout.Metrics boring = BoringLayout.isBoring(text, sTextPaintInstance);
-
-- Class mapBufferClass = TextLayoutManagerMapBuffer.class;
-+ Class mapBufferClass = TextLayoutManager.class;
- try {
-- Method createLayoutMethod = mapBufferClass.getDeclaredMethod("createLayout", Spannable.class, BoringLayout.Metrics.class, float.class, YogaMeasureMode.class, boolean.class, int.class, int.class);
-+ Method createLayoutMethod = mapBufferClass.getDeclaredMethod("createLayout", Spannable.class, BoringLayout.Metrics.class, float.class, YogaMeasureMode.class, boolean.class, int.class, int.class, Layout.Alignment.class);
- createLayoutMethod.setAccessible(true);
-
- Layout layout = (Layout)createLayoutMethod.invoke(
-@@ -106,11 +107,12 @@ public class CustomMountingManager extends MountingManager {
- widthYogaMeasureMode,
- includeFontPadding,
- textBreakStrategy,
-- hyphenationFrequency);
-+ hyphenationFrequency,
-+ alignment);
-
- int maximumNumberOfLines =
-- paragraphAttributes.contains(TextLayoutManagerMapBuffer.PA_KEY_MAX_NUMBER_OF_LINES)
-- ? paragraphAttributes.getInt(TextLayoutManagerMapBuffer.PA_KEY_MAX_NUMBER_OF_LINES)
-+ paragraphAttributes.contains(TextLayoutManager.PA_KEY_MAX_NUMBER_OF_LINES)
-+ ? paragraphAttributes.getInt(TextLayoutManager.PA_KEY_MAX_NUMBER_OF_LINES)
- : UNSET;
-
- int calculatedLineCount =
-diff --git a/node_modules/@expensify/react-native-live-markdown/android/src/main/new_arch/CMakeLists.txt b/node_modules/@expensify/react-native-live-markdown/android/src/main/new_arch/CMakeLists.txt
-index 1609c60..1888eea 100644
---- a/node_modules/@expensify/react-native-live-markdown/android/src/main/new_arch/CMakeLists.txt
-+++ b/node_modules/@expensify/react-native-live-markdown/android/src/main/new_arch/CMakeLists.txt
-@@ -65,6 +65,12 @@ target_link_libraries(
- yoga
- android
- log
-+ mapbufferjni
-+ reactnativejni
-+ react_render_consistency
-+ react_performance_timeline
-+ react_render_observers_events
-+ react_featureflags
- )
-
- target_compile_options(
diff --git a/patches/@expensify+react-native-live-markdown+0.1.120+003+shadow-node.patch b/patches/@expensify+react-native-live-markdown+0.1.120+003+shadow-node.patch
deleted file mode 100644
index d3ff41b29249..000000000000
--- a/patches/@expensify+react-native-live-markdown+0.1.120+003+shadow-node.patch
+++ /dev/null
@@ -1,72 +0,0 @@
-diff --git a/node_modules/@expensify/react-native-live-markdown/cpp/react/renderer/components/RNLiveMarkdownSpec/MarkdownTextInputDecoratorShadowNode.cpp b/node_modules/@expensify/react-native-live-markdown/cpp/react/renderer/components/RNLiveMarkdownSpec/MarkdownTextInputDecoratorShadowNode.cpp
-index 104363d..9240e9e 100644
---- a/node_modules/@expensify/react-native-live-markdown/cpp/react/renderer/components/RNLiveMarkdownSpec/MarkdownTextInputDecoratorShadowNode.cpp
-+++ b/node_modules/@expensify/react-native-live-markdown/cpp/react/renderer/components/RNLiveMarkdownSpec/MarkdownTextInputDecoratorShadowNode.cpp
-@@ -11,7 +11,7 @@ namespace react {
- extern const char MarkdownTextInputDecoratorViewComponentName[] =
- "MarkdownTextInputDecoratorView";
-
--const ShadowNodeFragment::Value
-+const OwningShadowNodeFragment
- MarkdownTextInputDecoratorShadowNode::updateFragmentState(
- ShadowNodeFragment const &fragment,
- ShadowNodeFamily::Shared const &family) {
-@@ -24,12 +24,12 @@ MarkdownTextInputDecoratorShadowNode::updateFragmentState(
- // propagated on every clone we need it to clear the reference in the registry
- // when the view is removed from window it cannot be done in the destructor,
- // as multiple shadow nodes for the same family may be created
-- return ShadowNodeFragment::Value({
-+ return OwningShadowNodeFragment{
- .props = fragment.props,
- .children = fragment.children,
- .state =
- std::make_shared(newStateData, *fragment.state),
-- });
-+ };
- }
-
- } // namespace react
-diff --git a/node_modules/@expensify/react-native-live-markdown/cpp/react/renderer/components/RNLiveMarkdownSpec/MarkdownTextInputDecoratorShadowNode.h b/node_modules/@expensify/react-native-live-markdown/cpp/react/renderer/components/RNLiveMarkdownSpec/MarkdownTextInputDecoratorShadowNode.h
-index 294e0d3..597752c 100644
---- a/node_modules/@expensify/react-native-live-markdown/cpp/react/renderer/components/RNLiveMarkdownSpec/MarkdownTextInputDecoratorShadowNode.h
-+++ b/node_modules/@expensify/react-native-live-markdown/cpp/react/renderer/components/RNLiveMarkdownSpec/MarkdownTextInputDecoratorShadowNode.h
-@@ -11,6 +11,20 @@
- namespace facebook {
- namespace react {
-
-+struct OwningShadowNodeFragment {
-+ Props::Shared props;
-+ ShadowNode::SharedListOfShared children;
-+ State::Shared state;
-+
-+ operator ShadowNodeFragment() const {
-+ return ShadowNodeFragment {
-+ .props = props,
-+ .children = children,
-+ .state = state
-+ };
-+ }
-+};
-+
- JSI_EXPORT extern const char MarkdownTextInputDecoratorViewComponentName[];
-
- class JSI_EXPORT MarkdownTextInputDecoratorShadowNode final
-@@ -22,8 +36,7 @@ public:
- MarkdownTextInputDecoratorShadowNode(ShadowNodeFragment const &fragment,
- ShadowNodeFamily::Shared const &family,
- ShadowNodeTraits traits)
-- : ConcreteViewShadowNode(static_cast(
-- updateFragmentState(fragment, family)),
-+ : ConcreteViewShadowNode(updateFragmentState(fragment, family),
- family, traits) {}
-
- MarkdownTextInputDecoratorShadowNode(ShadowNode const &sourceShadowNode,
-@@ -37,7 +50,7 @@ public:
- }
-
- private:
-- static const ShadowNodeFragment::Value
-+ static const OwningShadowNodeFragment
- updateFragmentState(ShadowNodeFragment const &fragment,
- ShadowNodeFamily::Shared const &family);
- };
diff --git a/patches/@expensify+react-native-live-markdown+0.1.120+004+hybrid-app.patch b/patches/@expensify+react-native-live-markdown+0.1.120+004+hybrid-app.patch
deleted file mode 100644
index 00f87066c9fa..000000000000
--- a/patches/@expensify+react-native-live-markdown+0.1.120+004+hybrid-app.patch
+++ /dev/null
@@ -1,18 +0,0 @@
-diff --git a/node_modules/@expensify/react-native-live-markdown/RNLiveMarkdown.podspec b/node_modules/@expensify/react-native-live-markdown/RNLiveMarkdown.podspec
-index b1620ad..b3ea39c 100644
---- a/node_modules/@expensify/react-native-live-markdown/RNLiveMarkdown.podspec
-+++ b/node_modules/@expensify/react-native-live-markdown/RNLiveMarkdown.podspec
-@@ -23,10 +23,10 @@ Pod::Spec.new do |s|
- install_modules_dependencies(s)
-
- if ENV['USE_FRAMEWORKS'] && ENV['RCT_NEW_ARCH_ENABLED']
-- add_dependency(s, "React-Fabric", :additional_framework_paths => [
-+ add_dependency(s, "React-FabricComponents", :additional_framework_paths => [
- "react/renderer/textlayoutmanager/platform/ios",
-- "react/renderer/components/textinput/iostextinput",
-- ])
-+ "react/renderer/components/textinput/platform/ios",
-+ ]);
- end
-
- s.subspec "common" do |ss|
diff --git a/patches/react-native-keyboard-controller+1.12.2+002+rn-75-fixes.patch b/patches/react-native-keyboard-controller+1.12.2+002+rn-75-fixes.patch
deleted file mode 100644
index f7ab542a2a2b..000000000000
--- a/patches/react-native-keyboard-controller+1.12.2+002+rn-75-fixes.patch
+++ /dev/null
@@ -1,13 +0,0 @@
-diff --git a/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt b/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt
-index 50252f0..28a70d6 100644
---- a/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt
-+++ b/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt
-@@ -13,7 +13,7 @@ val ThemedReactContext.rootView: View?
-
- fun ThemedReactContext?.dispatchEvent(viewId: Int, event: Event<*>) {
- val eventDispatcher: EventDispatcher? =
-- UIManagerHelper.getEventDispatcherForReactTag(this, viewId)
-+ UIManagerHelper.getEventDispatcherForReactTag(this!!, viewId)
- eventDispatcher?.dispatchEvent(event)
- }
-
diff --git a/patches/react-native-keyboard-controller+1.12.2+001+initial.patch b/patches/react-native-keyboard-controller+1.12.7.patch
similarity index 100%
rename from patches/react-native-keyboard-controller+1.12.2+001+initial.patch
rename to patches/react-native-keyboard-controller+1.12.7.patch
diff --git a/patches/react-native-share+10.0.2+001+hybrid-app.patch b/patches/react-native-share+10.0.2+001+hybrid-app.patch
deleted file mode 100644
index 13dcc1e8438b..000000000000
--- a/patches/react-native-share+10.0.2+001+hybrid-app.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-diff --git a/node_modules/react-native-share/RNShare.podspec b/node_modules/react-native-share/RNShare.podspec
-index 124a721..636a1f7 100644
---- a/node_modules/react-native-share/RNShare.podspec
-+++ b/node_modules/react-native-share/RNShare.podspec
-@@ -20,20 +20,6 @@ Pod::Spec.new do |s|
-
- s.ios.weak_framework = 'LinkPresentation'
-
-- if ENV["RCT_NEW_ARCH_ENABLED"] == "1"
-- s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
-- s.pod_target_xcconfig = {
-- "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
-- "OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
-- "CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
-- }
--
-- s.dependency "React-Codegen"
-- s.dependency "React-RCTFabric"
-- s.dependency "RCT-Folly"
-- s.dependency "RCTRequired"
-- s.dependency "RCTTypeSafety"
-- s.dependency "ReactCommon/turbomodule/core"
-- end
-+ install_modules_dependencies(s)
-
- end
diff --git a/src/CONST.ts b/src/CONST.ts
index cd4d2b24e97a..86cbd4c28fc9 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -189,6 +189,8 @@ const CONST = {
},
// Multiplier for gyroscope animation in order to make it a bit more subtle
ANIMATION_GYROSCOPE_VALUE: 0.4,
+ ANIMATION_PAY_BUTTON_DURATION: 200,
+ ANIMATION_PAY_BUTTON_HIDE_DELAY: 1000,
BACKGROUND_IMAGE_TRANSITION_DURATION: 1000,
SCREEN_TRANSITION_END_TIMEOUT: 1000,
ARROW_HIDE_DELAY: 3000,
@@ -2208,6 +2210,7 @@ const CONST = {
REMOVE: 'remove',
MAKE_MEMBER: 'makeMember',
MAKE_ADMIN: 'makeAdmin',
+ MAKE_AUDITOR: 'makeAuditor',
},
BULK_ACTION_TYPES: {
DELETE: 'delete',
@@ -2340,6 +2343,8 @@ const CONST = {
NETSUITE_SYNC_UPDATE_DATA: 'netSuiteSyncUpdateConnectionData',
NETSUITE_SYNC_NETSUITE_REIMBURSED_REPORTS: 'netSuiteSyncNetSuiteReimbursedReports',
NETSUITE_SYNC_EXPENSIFY_REIMBURSED_REPORTS: 'netSuiteSyncExpensifyReimbursedReports',
+ NETSUITE_SYNC_IMPORT_VENDORS_TITLE: 'netSuiteImportVendorsTitle',
+ NETSUITE_SYNC_IMPORT_CUSTOM_LISTS_TITLE: 'netSuiteImportCustomListsTitle',
SAGE_INTACCT_SYNC_CHECK_CONNECTION: 'intacctCheckConnection',
SAGE_INTACCT_SYNC_IMPORT_TITLE: 'intacctImportTitle',
SAGE_INTACCT_SYNC_IMPORT_DATA: 'intacctImportData',
@@ -2488,6 +2493,90 @@ const CONST = {
RESTRICT: 'corporate',
ALLOW: 'personal',
},
+ EXPORT_CARD_TYPES: {
+ /**
+ * Name of Card NVP for QBO custom export accounts
+ */
+ NVP_QUICKBOOKS_ONLINE_EXPORT_ACCOUNT: 'quickbooks_online_export_account',
+ NVP_QUICKBOOKS_ONLINE_EXPORT_ACCOUNT_DEBIT: 'quickbooks_online_export_account_debit',
+
+ /**
+ * Name of Card NVP for NetSuite custom export accounts
+ */
+ NVP_NETSUITE_EXPORT_ACCOUNT: 'netsuite_export_payable_account',
+
+ /**
+ * Name of Card NVP for NetSuite custom vendors
+ */
+ NVP_NETSUITE_EXPORT_VENDOR: 'netsuite_export_vendor',
+
+ /**
+ * Name of Card NVP for Xero custom export accounts
+ */
+ NVP_XERO_EXPORT_BANK_ACCOUNT: 'xero_export_bank_account',
+
+ /**
+ * Name of Card NVP for Intacct custom export accounts
+ */
+ NVP_INTACCT_EXPORT_CHARGE_CARD: 'intacct_export_charge_card',
+
+ /**
+ * Name of card NVP for Intacct custom vendors
+ */
+ NVP_INTACCT_EXPORT_VENDOR: 'intacct_export_vendor',
+
+ /**
+ * Name of Card NVP for QuickBooks Desktop custom export accounts
+ */
+ NVP_QUICKBOOKS_DESKTOP_EXPORT_ACCOUNT_CREDIT: 'quickbooks_desktop_export_account_credit',
+
+ /**
+ * Name of Card NVP for QuickBooks Desktop custom export accounts
+ */
+ NVP_FINANCIALFORCE_EXPORT_VENDOR: 'financialforce_export_vendor',
+ },
+ EXPORT_CARD_POLICY_TYPES: {
+ /**
+ * Name of Card NVP for QBO custom export accounts
+ */
+ NVP_QUICKBOOKS_ONLINE_EXPORT_ACCOUNT_POLICY_ID: 'quickbooks_online_export_account_policy_id',
+ NVP_QUICKBOOKS_ONLINE_EXPORT_ACCOUNT_DEBIT_POLICY_ID: 'quickbooks_online_export_account_debit_policy_id',
+
+ /**
+ * Name of Card NVP for NetSuite custom export accounts
+ */
+ NVP_NETSUITE_EXPORT_ACCOUNT_POLICY_ID: 'netsuite_export_payable_account_policy_id',
+
+ /**
+ * Name of Card NVP for NetSuite custom vendors
+ */
+ NVP_NETSUITE_EXPORT_VENDOR_POLICY_ID: 'netsuite_export_vendor_policy_id',
+
+ /**
+ * Name of Card NVP for Xero custom export accounts
+ */
+ NVP_XERO_EXPORT_BANK_ACCOUNT_POLICY_ID: 'xero_export_bank_account_policy_id',
+
+ /**
+ * Name of Card NVP for Intacct custom export accounts
+ */
+ NVP_INTACCT_EXPORT_CHARGE_CARD_POLICY_ID: 'intacct_export_charge_card_policy_id',
+
+ /**
+ * Name of card NVP for Intacct custom vendors
+ */
+ NVP_INTACCT_EXPORT_VENDOR_POLICY_ID: 'intacct_export_vendor_policy_id',
+
+ /**
+ * Name of Card NVP for QuickBooks Desktop custom export accounts
+ */
+ NVP_QUICKBOOKS_DESKTOP_EXPORT_ACCOUNT_CREDIT_POLICY_ID: 'quickbooks_desktop_export_account_credit_policy_id',
+
+ /**
+ * Name of Card NVP for QuickBooks Desktop custom export accounts
+ */
+ NVP_FINANCIALFORCE_EXPORT_VENDOR_POLICY_ID: 'financialforce_export_vendor_policy_id',
+ },
},
AVATAR_ROW_SIZE: {
DEFAULT: 4,
@@ -2544,10 +2633,8 @@ const CONST = {
ATTACHMENT_ID: /chat-attachments\/(\d+)/,
HAS_COLON_ONLY_AT_THE_BEGINNING: /^:[^:]+$/,
HAS_AT_MOST_TWO_AT_SIGNS: /^@[^@]*@?[^@]*$/,
-
EMPTY_COMMENT: /^(\s)*$/,
SPECIAL_CHAR: /[,/?"{}[\]()&^%;`$=#<>!*]/g,
-
FIRST_SPACE: /.+?(?=\s)/,
get SPECIAL_CHAR_OR_EMOJI() {
@@ -2565,33 +2652,24 @@ const CONST = {
},
MERGED_ACCOUNT_PREFIX: /^(MERGED_\d+@)/,
-
ROUTES: {
VALIDATE_LOGIN: /\/v($|(\/\/*))/,
UNLINK_LOGIN: /\/u($|(\/\/*))/,
REDUNDANT_SLASHES: /(\/{2,})|(\/$)/g,
},
-
TIME_STARTS_01: /^01:\d{2} [AP]M$/,
TIME_FORMAT: /^\d{2}:\d{2} [AP]M$/,
DATE_TIME_FORMAT: /^\d{2}-\d{2} \d{2}:\d{2} [AP]M$/,
ILLEGAL_FILENAME_CHARACTERS: /\/|<|>|\*|"|:|\?|\\|\|/g,
-
ENCODE_PERCENT_CHARACTER: /%(25)+/g,
-
INVISIBLE_CHARACTERS_GROUPS: /[\p{C}\p{Z}]/gu,
-
OTHER_INVISIBLE_CHARACTERS: /[\u3164]/g,
-
REPORT_FIELD_TITLE: /{report:([a-zA-Z]+)}/g,
-
PATH_WITHOUT_POLICY_ID: /\/w\/[a-zA-Z0-9]+(\/|$)/,
-
POLICY_ID_FROM_PATH: /\/w\/([a-zA-Z0-9]+)(\/|$)/,
-
SHORT_MENTION: new RegExp(`@[\\w\\-\\+\\'#@]+(?:\\.[\\w\\-\\'\\+]+)*(?![^\`]*\`)`, 'gim'),
-
REPORT_ID_FROM_PATH: /\/r\/(\d+)/,
+ DISTANCE_MERCHANT: /^[0-9.]+ \w+ @ (-|-\()?[^0-9.\s]{1,3} ?[0-9.]+\)? \/ \w+$/,
get EXPENSIFY_POLICY_DOMAIN_NAME() {
return new RegExp(`${EXPENSIFY_POLICY_DOMAIN}([a-zA-Z0-9]+)\\${EXPENSIFY_POLICY_DOMAIN_EXTENSION}`);
diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts
index 1d33b6892d51..38affd97c637 100755
--- a/src/ONYXKEYS.ts
+++ b/src/ONYXKEYS.ts
@@ -215,6 +215,9 @@ const ONYXKEYS = {
/** The NVP containing all information related to educational tooltip in workspace chat */
NVP_WORKSPACE_TOOLTIP: 'workspaceTooltip',
+ /** Whether to hide save search rename tooltip */
+ NVP_SHOULD_HIDE_SAVED_SEARCH_RENAME_TOOLTIP: 'nvp_should_hide_saved_search_rename_tooltip',
+
/** Whether to hide gbr tooltip */
NVP_SHOULD_HIDE_GBR_TOOLTIP: 'nvp_should_hide_gbr_tooltip',
@@ -424,6 +427,9 @@ const ONYXKEYS = {
/** Stores the route to open after changing app permission from settings */
LAST_ROUTE: 'lastRoute',
+ /** Stores the information about the saved searches */
+ SAVED_SEARCHES: 'nvp_savedSearches',
+
/** Stores recently used currencies */
RECENTLY_USED_CURRENCIES: 'nvp_recentlyUsedCurrencies',
@@ -530,6 +536,8 @@ const ONYXKEYS = {
WORKSPACE_TAX_CUSTOM_NAME_DRAFT: 'workspaceTaxCustomNameDraft',
WORKSPACE_COMPANY_CARD_FEED_NAME: 'workspaceCompanyCardFeedName',
WORKSPACE_COMPANY_CARD_FEED_NAME_DRAFT: 'workspaceCompanyCardFeedNameDraft',
+ EDIT_WORKSPACE_COMPANY_CARD_NAME_FORM: 'editCompanyCardName',
+ EDIT_WORKSPACE_COMPANY_CARD_NAME_DRAFT_FORM: 'editCompanyCardNameDraft',
WORKSPACE_REPORT_FIELDS_FORM: 'workspaceReportFieldForm',
WORKSPACE_REPORT_FIELDS_FORM_DRAFT: 'workspaceReportFieldFormDraft',
POLICY_CREATE_DISTANCE_RATE_FORM: 'policyCreateDistanceRateForm',
@@ -666,6 +674,8 @@ const ONYXKEYS = {
SAGE_INTACCT_DIMENSION_TYPE_FORM_DRAFT: 'sageIntacctDimensionTypeFormDraft',
SEARCH_ADVANCED_FILTERS_FORM: 'searchAdvancedFiltersForm',
SEARCH_ADVANCED_FILTERS_FORM_DRAFT: 'searchAdvancedFiltersFormDraft',
+ SEARCH_SAVED_SEARCH_RENAME_FORM: 'searchSavedSearchRenameForm',
+ SEARCH_SAVED_SEARCH_RENAME_FORM_DRAFT: 'searchSavedSearchRenameFormDraft',
TEXT_PICKER_MODAL_FORM: 'textPickerModalForm',
TEXT_PICKER_MODAL_FORM_DRAFT: 'textPickerModalFormDraft',
RULES_CUSTOM_NAME_MODAL_FORM: 'rulesCustomNameModalForm',
@@ -695,6 +705,7 @@ type OnyxFormValuesMapping = {
[ONYXKEYS.FORMS.WORKSPACE_RATE_AND_UNIT_FORM]: FormTypes.WorkspaceRateAndUnitForm;
[ONYXKEYS.FORMS.WORKSPACE_TAX_CUSTOM_NAME]: FormTypes.WorkspaceTaxCustomName;
[ONYXKEYS.FORMS.WORKSPACE_COMPANY_CARD_FEED_NAME]: FormTypes.WorkspaceCompanyCardFeedName;
+ [ONYXKEYS.FORMS.EDIT_WORKSPACE_COMPANY_CARD_NAME_FORM]: FormTypes.WorkspaceCompanyCardEditName;
[ONYXKEYS.FORMS.WORKSPACE_REPORT_FIELDS_FORM]: FormTypes.WorkspaceReportFieldForm;
[ONYXKEYS.FORMS.WORKSPACE_CATEGORY_DESCRIPTION_HINT_FORM]: FormTypes.WorkspaceCategoryDescriptionHintForm;
[ONYXKEYS.FORMS.WORKSPACE_CATEGORY_FLAG_AMOUNTS_OVER_FORM]: FormTypes.WorkspaceCategoryFlagAmountsOverForm;
@@ -774,6 +785,7 @@ type OnyxFormValuesMapping = {
[ONYXKEYS.FORMS.RULES_REQUIRED_RECEIPT_AMOUNT_FORM]: FormTypes.RulesRequiredReceiptAmountForm;
[ONYXKEYS.FORMS.RULES_MAX_EXPENSE_AMOUNT_FORM]: FormTypes.RulesMaxExpenseAmountForm;
[ONYXKEYS.FORMS.RULES_MAX_EXPENSE_AGE_FORM]: FormTypes.RulesMaxExpenseAgeForm;
+ [ONYXKEYS.FORMS.SEARCH_SAVED_SEARCH_RENAME_FORM]: FormTypes.SearchSavedSearchRenameForm;
};
type OnyxFormDraftValuesMapping = {
@@ -839,6 +851,7 @@ type OnyxValuesMapping = {
// ONYXKEYS.NVP_TRYNEWDOT is HybridApp onboarding data
[ONYXKEYS.NVP_TRYNEWDOT]: OnyxTypes.TryNewDot;
+ [ONYXKEYS.SAVED_SEARCHES]: OnyxTypes.SaveSearch[];
[ONYXKEYS.RECENTLY_USED_CURRENCIES]: string[];
[ONYXKEYS.ACTIVE_CLIENTS]: string[];
[ONYXKEYS.DEVICE_ID]: string;
@@ -973,6 +986,7 @@ type OnyxValuesMapping = {
[ONYXKEYS.APPROVAL_WORKFLOW]: OnyxTypes.ApprovalWorkflowOnyx;
[ONYXKEYS.IMPORTED_SPREADSHEET]: OnyxTypes.ImportedSpreadsheet;
[ONYXKEYS.LAST_ROUTE]: string;
+ [ONYXKEYS.NVP_SHOULD_HIDE_SAVED_SEARCH_RENAME_TOOLTIP]: boolean;
};
type OnyxValues = OnyxValuesMapping & OnyxCollectionValuesMapping & OnyxFormValuesMapping & OnyxFormDraftValuesMapping;
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index 2b6268c05b3a..27504998c49c 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -37,6 +37,10 @@ const ROUTES = {
route: 'search',
getRoute: ({query}: {query: SearchQueryString}) => `search?q=${encodeURIComponent(query)}` as const,
},
+ SEARCH_SAVED_SEARCH_RENAME: {
+ route: 'search/saved-search/rename',
+ getRoute: ({name, jsonQuery}: {name: string; jsonQuery: SearchQueryString}) => `search/saved-search/rename?name=${name}&q=${jsonQuery}` as const,
+ },
SEARCH_ADVANCED_FILTERS: 'search/filters',
SEARCH_ADVANCED_FILTERS_DATE: 'search/filters/date',
SEARCH_ADVANCED_FILTERS_CURRENCY: 'search/filters/currency',
@@ -973,6 +977,18 @@ const ROUTES = {
route: 'settings/workspaces/:policyID/company-cards/:feed/assign-card',
getRoute: (policyID: string, feed: string) => `settings/workspaces/${policyID}/company-cards/${feed}/assign-card` as const,
},
+ WORKSPACE_COMPANY_CARD_DETAILS: {
+ route: 'settings/workspaces/:policyID/company-cards/:bank/:cardID',
+ getRoute: (policyID: string, cardID: string, bank: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/company-cards/${bank}/${cardID}`, backTo),
+ },
+ WORKSPACE_COMPANY_CARD_NAME: {
+ route: 'settings/workspaces/:policyID/company-cards/:bank/:cardID/edit/name',
+ getRoute: (policyID: string, cardID: string, bank: string) => `settings/workspaces/${policyID}/company-cards/${bank}/${cardID}/edit/name` as const,
+ },
+ WORKSPACE_COMPANY_CARD_EXPORT: {
+ route: 'settings/workspaces/:policyID/company-cards/:bank/:cardID/edit/export',
+ getRoute: (policyID: string, cardID: string, bank: string) => `settings/workspaces/${policyID}/company-cards/${bank}/${cardID}/edit/export` as const,
+ },
WORKSPACE_EXPENSIFY_CARD_DETAILS: {
route: 'settings/workspaces/:policyID/expensify-card/:cardID',
getRoute: (policyID: string, cardID: string, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/expensify-card/${cardID}`, backTo),
diff --git a/src/SCREENS.ts b/src/SCREENS.ts
index e8a3698e9a95..8168afba89ab 100644
--- a/src/SCREENS.ts
+++ b/src/SCREENS.ts
@@ -46,6 +46,7 @@ const SCREENS = {
ADVANCED_FILTERS_TAG_RHP: 'Search_Advanced_Filters_Tag_RHP',
ADVANCED_FILTERS_FROM_RHP: 'Search_Advanced_Filters_From_RHP',
ADVANCED_FILTERS_TO_RHP: 'Search_Advanced_Filters_To_RHP',
+ SAVED_SEARCH_RENAME_RHP: 'Search_Saved_Search_Rename_RHP',
ADVANCED_FILTERS_IN_RHP: 'Search_Advanced_Filters_In_RHP',
TRANSACTION_HOLD_REASON_RHP: 'Search_Transaction_Hold_Reason_RHP',
BOTTOM_TAB: 'Search_Bottom_Tab',
@@ -172,6 +173,7 @@ const SCREENS = {
TRAVEL: 'Travel',
SEARCH_REPORT: 'SearchReport',
SEARCH_ADVANCED_FILTERS: 'SearchAdvancedFilters',
+ SEARCH_SAVED_SEARCH: 'SearchSavedSearch',
SETTINGS_CATEGORIES: 'SettingsCategories',
RESTRICTED_ACTION: 'RestrictedAction',
REPORT_EXPORT: 'Report_Export',
@@ -384,6 +386,9 @@ const SCREENS = {
COMPANY_CARDS_DETAILS: 'Workspace_CompanyCards_Details',
COMPANY_CARDS_SETTINGS: 'Workspace_CompanyCards_Settings',
COMPANY_CARDS_SETTINGS_FEED_NAME: 'Workspace_CompanyCards_Settings_Feed_Name',
+ COMPANY_CARD_DETAILS: 'Workspace_CompanyCard_Details',
+ COMPANY_CARD_NAME: 'Workspace_CompanyCard_Name',
+ COMPANY_CARD_EXPORT: 'Workspace_CompanyCard_Export',
EXPENSIFY_CARD: 'Workspace_ExpensifyCard',
EXPENSIFY_CARD_DETAILS: 'Workspace_ExpensifyCard_Details',
EXPENSIFY_CARD_LIMIT: 'Workspace_ExpensifyCard_Limit',
diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx
index 9e67de51c47f..34710a2d897e 100644
--- a/src/components/Button/index.tsx
+++ b/src/components/Button/index.tsx
@@ -80,6 +80,9 @@ type ButtonProps = Partial & {
/** Additional styles to add after local styles. Applied to Pressable portion of button */
style?: StyleProp;
+ /** Additional styles to add to the component when it's disabled */
+ disabledStyle?: StyleProp;
+
/** Additional button styles. Specific to the OpacityView of the button */
innerStyles?: StyleProp;
@@ -206,6 +209,7 @@ function Button(
enterKeyEventListenerPriority = 0,
style = [],
+ disabledStyle,
innerStyles = [],
textStyles = [],
textHoverStyles = [],
@@ -381,6 +385,7 @@ function Button(
danger && !isDisabled ? styles.buttonDangerHovered : undefined,
hoverStyles,
]}
+ disabledStyle={disabledStyle}
id={id}
accessibilityLabel={accessibilityLabel}
role={CONST.ROLE.BUTTON}
diff --git a/src/components/ButtonWithDropdownMenu/index.tsx b/src/components/ButtonWithDropdownMenu/index.tsx
index 1ff0eaae4726..c1b662c3680e 100644
--- a/src/components/ButtonWithDropdownMenu/index.tsx
+++ b/src/components/ButtonWithDropdownMenu/index.tsx
@@ -25,6 +25,7 @@ function ButtonWithDropdownMenu({
menuHeaderText = '',
customText,
style,
+ disabledStyle,
buttonSize = CONST.DROPDOWN_BUTTON_SIZE.MEDIUM,
anchorAlignment = {
horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT,
@@ -157,6 +158,7 @@ function ButtonWithDropdownMenu({
pressOnEnter={pressOnEnter}
isDisabled={isDisabled || !!options[0].disabled}
style={[styles.w100, style]}
+ disabledStyle={disabledStyle}
isLoading={isLoading}
text={selectedItem.text}
onPress={(event) => onPress(event, options[0].value)}
diff --git a/src/components/ButtonWithDropdownMenu/types.ts b/src/components/ButtonWithDropdownMenu/types.ts
index 59bfd74cd55d..da6b3d93433d 100644
--- a/src/components/ButtonWithDropdownMenu/types.ts
+++ b/src/components/ButtonWithDropdownMenu/types.ts
@@ -68,6 +68,9 @@ type ButtonWithDropdownMenuProps = {
/** Additional styles to add to the component */
style?: StyleProp;
+ /** Additional styles to add to the component when it's disabled */
+ disabledStyle?: StyleProp;
+
/** Menu options to display */
/** e.g. [{text: 'Pay with Expensify', icon: Wallet}] */
options: Array>;
diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts
index 698dc33b4a03..569e467dfad7 100644
--- a/src/components/Icon/Expensicons.ts
+++ b/src/components/Icon/Expensicons.ts
@@ -24,6 +24,7 @@ import Bell from '@assets/images/bell.svg';
import BellSlash from '@assets/images/bellSlash.svg';
import Bill from '@assets/images/bill.svg';
import Bolt from '@assets/images/bolt.svg';
+import Bookmark from '@assets/images/bookmark.svg';
import Box from '@assets/images/box.svg';
import Briefcase from '@assets/images/briefcase.svg';
import Bug from '@assets/images/bug.svg';
@@ -186,6 +187,7 @@ import Unlock from '@assets/images/unlock.svg';
import UploadAlt from '@assets/images/upload-alt.svg';
import Upload from '@assets/images/upload.svg';
import UserCheck from '@assets/images/user-check.svg';
+import UserEye from '@assets/images/user-eye.svg';
import UserPlus from '@assets/images/user-plus.svg';
import User from '@assets/images/user.svg';
import Users from '@assets/images/users.svg';
@@ -392,10 +394,12 @@ export {
Filters,
CalendarSolid,
Filter,
+ UserEye,
CaretUpDown,
UserPlus,
Feed,
Table,
SpreadsheetComputer,
+ Bookmark,
Star,
};
diff --git a/src/components/Icon/Illustrations.ts b/src/components/Icon/Illustrations.ts
index 446137c3749b..954f9fa5caa7 100644
--- a/src/components/Icon/Illustrations.ts
+++ b/src/components/Icon/Illustrations.ts
@@ -1,5 +1,8 @@
import AmexCompanyCards from '@assets/images/companyCards/amex.svg';
import AmexBlueCompanyCards from '@assets/images/companyCards/card-amex-blue.svg';
+import AmexCardCompanyCardDetail from '@assets/images/companyCards/card-amex.svg';
+import MasterCardCompanyCardDetail from '@assets/images/companyCards/card-mastercard.svg';
+import VisaCompanyCardDetail from '@assets/images/companyCards/card-visa.svg';
import CompanyCardsEmptyState from '@assets/images/companyCards/emptystate__card-pos.svg';
import MasterCardCompanyCards from '@assets/images/companyCards/mastercard.svg';
import VisaCompanyCards from '@assets/images/companyCards/visa.svg';
@@ -237,5 +240,8 @@ export {
MasterCardCompanyCards,
VisaCompanyCards,
AmexBlueCompanyCards,
+ VisaCompanyCardDetail,
+ MasterCardCompanyCardDetail,
+ AmexCardCompanyCardDetail,
TurtleInShell,
};
diff --git a/src/components/LinearGradient/index.native.ts b/src/components/LinearGradient/index.native.ts
deleted file mode 100644
index 29af26b96b64..000000000000
--- a/src/components/LinearGradient/index.native.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import LinearGradientNative from 'react-native-linear-gradient';
-import type LinearGradient from './types';
-
-const LinearGradientImplementation: LinearGradient = LinearGradientNative;
-
-export default LinearGradientImplementation;
diff --git a/src/components/LinearGradient/index.ts b/src/components/LinearGradient/index.ts
deleted file mode 100644
index 84d0fc2ce5c8..000000000000
--- a/src/components/LinearGradient/index.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import LinearGradientWeb from 'react-native-web-linear-gradient';
-import type LinearGradient from './types';
-
-const LinearGradientImplementation: LinearGradient = LinearGradientWeb;
-
-export default LinearGradientImplementation;
diff --git a/src/components/LinearGradient/types.ts b/src/components/LinearGradient/types.ts
deleted file mode 100644
index 9e238ef71f12..000000000000
--- a/src/components/LinearGradient/types.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import type LinearGradientNative from 'react-native-linear-gradient';
-
-type LinearGradient = typeof LinearGradientNative;
-
-export default LinearGradient;
diff --git a/src/components/MenuItemList.tsx b/src/components/MenuItemList.tsx
index 623198498dd1..2e732c691140 100644
--- a/src/components/MenuItemList.tsx
+++ b/src/components/MenuItemList.tsx
@@ -1,9 +1,10 @@
import React, {useRef} from 'react';
-import type {GestureResponderEvent, View} from 'react-native';
+import type {GestureResponderEvent, StyleProp, View, ViewStyle} from 'react-native';
import useSingleExecution from '@hooks/useSingleExecution';
import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu';
import CONST from '@src/CONST';
import type * as OnyxCommon from '@src/types/onyx/OnyxCommon';
+import type IconAsset from '@src/types/utils/IconAsset';
import type {MenuItemProps} from './MenuItem';
import MenuItem from './MenuItem';
import OfflineWithFeedback from './OfflineWithFeedback';
@@ -36,9 +37,32 @@ type MenuItemListProps = {
/** Whether or not to use the single execution hook */
shouldUseSingleExecution?: boolean;
+
+ /** Any additional styles to apply for each item */
+ wrapperStyle?: StyleProp;
+
+ /** Icon to display on the left side of each item */
+ icon?: IconAsset;
+
+ /** Icon Width */
+ iconWidth?: number;
+
+ /** Icon Height */
+ iconHeight?: number;
+
+ /** Is this in the Pane */
+ isPaneMenu?: boolean;
};
-function MenuItemList({menuItems = [], shouldUseSingleExecution = false}: MenuItemListProps) {
+function MenuItemList({
+ menuItems = [],
+ shouldUseSingleExecution = false,
+ wrapperStyle = {},
+ icon = undefined,
+ iconWidth = undefined,
+ iconHeight = undefined,
+ isPaneMenu = false,
+}: MenuItemListProps) {
const popoverAnchor = useRef(null);
const {isExecuting, singleExecution} = useSingleExecution();
@@ -67,9 +91,14 @@ function MenuItemList({menuItems = [], shouldUseSingleExecution = false}: MenuIt
>
{shouldShowSettlementButton && (
- void;
+};
+
+function AnimatedSettlementButton({isPaidAnimationRunning, onAnimationFinish, isDisabled, ...settlementButtonProps}: AnimatedSettlementButtonProps) {
+ const styles = useThemeStyles();
+ const buttonScale = useSharedValue(1);
+ const buttonOpacity = useSharedValue(1);
+ const paymentCompleteTextScale = useSharedValue(0);
+ const paymentCompleteTextOpacity = useSharedValue(1);
+ const height = useSharedValue(variables.componentSizeNormal);
+ const buttonStyles = useAnimatedStyle(() => ({
+ transform: [{scale: buttonScale.value}],
+ opacity: buttonOpacity.value,
+ }));
+ const paymentCompleteTextStyles = useAnimatedStyle(() => ({
+ transform: [{scale: paymentCompleteTextScale.value}],
+ opacity: paymentCompleteTextOpacity.value,
+ position: 'absolute',
+ alignSelf: 'center',
+ }));
+ const containerStyles = useAnimatedStyle(() => ({
+ height: height.value,
+ justifyContent: 'center',
+ overflow: 'hidden',
+ }));
+ const buttonDisabledStyle = isPaidAnimationRunning
+ ? {
+ opacity: 1,
+ ...styles.cursorDefault,
+ }
+ : undefined;
+
+ const resetAnimation = useCallback(() => {
+ // eslint-disable-next-line react-compiler/react-compiler
+ buttonScale.value = 1;
+ buttonOpacity.value = 1;
+ paymentCompleteTextScale.value = 0;
+ paymentCompleteTextOpacity.value = 1;
+ height.value = variables.componentSizeNormal;
+ }, [buttonScale, buttonOpacity, paymentCompleteTextScale, paymentCompleteTextOpacity, height]);
+
+ useEffect(() => {
+ if (!isPaidAnimationRunning) {
+ resetAnimation();
+ return;
+ }
+ // eslint-disable-next-line react-compiler/react-compiler
+ buttonScale.value = withTiming(0, {duration: CONST.ANIMATION_PAY_BUTTON_DURATION});
+ buttonOpacity.value = withTiming(0, {duration: CONST.ANIMATION_PAY_BUTTON_DURATION});
+ paymentCompleteTextScale.value = withTiming(1, {duration: CONST.ANIMATION_PAY_BUTTON_DURATION});
+
+ // Wait for the above animation + 1s delay before hiding the component
+ const totalDelay = CONST.ANIMATION_PAY_BUTTON_DURATION + CONST.ANIMATION_PAY_BUTTON_HIDE_DELAY;
+ height.value = withDelay(
+ totalDelay,
+ withTiming(0, {duration: CONST.ANIMATION_PAY_BUTTON_DURATION}, () => runOnJS(onAnimationFinish)()),
+ );
+ paymentCompleteTextOpacity.value = withDelay(totalDelay, withTiming(0, {duration: CONST.ANIMATION_PAY_BUTTON_DURATION}));
+ }, [isPaidAnimationRunning, onAnimationFinish, buttonOpacity, buttonScale, height, paymentCompleteTextOpacity, paymentCompleteTextScale, resetAnimation]);
+
+ return (
+
+ {isPaidAnimationRunning && (
+
+ Payment complete
+
+ )}
+
+
+
+
+ );
+}
+
+AnimatedSettlementButton.displayName = 'AnimatedSettlementButton';
+
+export default AnimatedSettlementButton;
diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton/index.tsx
similarity index 66%
rename from src/components/SettlementButton.tsx
rename to src/components/SettlementButton/index.tsx
index aa9f7712d537..2d24f9102aab 100644
--- a/src/components/SettlementButton.tsx
+++ b/src/components/SettlementButton/index.tsx
@@ -1,7 +1,10 @@
import React, {useMemo} from 'react';
-import type {GestureResponderEvent, StyleProp, ViewStyle} from 'react-native';
-import type {OnyxEntry} from 'react-native-onyx';
-import {useOnyx, withOnyx} from 'react-native-onyx';
+import type {GestureResponderEvent} from 'react-native';
+import {useOnyx} from 'react-native-onyx';
+import ButtonWithDropdownMenu from '@components/ButtonWithDropdownMenu';
+import type {PaymentType} from '@components/ButtonWithDropdownMenu/types';
+import * as Expensicons from '@components/Icon/Expensicons';
+import KYCWall from '@components/KYCWall';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import Navigation from '@libs/Navigation/Navigation';
@@ -12,109 +15,15 @@ import * as BankAccounts from '@userActions/BankAccounts';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
-import type {Route} from '@src/ROUTES';
import ROUTES from '@src/ROUTES';
-import type {ButtonSizeValue} from '@src/styles/utils/types';
-import type {LastPaymentMethod, Policy, Report} from '@src/types/onyx';
import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage';
-import type AnchorAlignment from '@src/types/utils/AnchorAlignment';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
-import ButtonWithDropdownMenu from './ButtonWithDropdownMenu';
-import type {PaymentType} from './ButtonWithDropdownMenu/types';
-import * as Expensicons from './Icon/Expensicons';
-import KYCWall from './KYCWall';
+import type SettlementButtonProps from './types';
type KYCFlowEvent = GestureResponderEvent | KeyboardEvent | undefined;
type TriggerKYCFlow = (event: KYCFlowEvent, iouPaymentType: PaymentMethodType) => void;
-type EnablePaymentsRoute = typeof ROUTES.ENABLE_PAYMENTS | typeof ROUTES.IOU_SEND_ENABLE_PAYMENTS | typeof ROUTES.SETTINGS_ENABLE_PAYMENTS;
-
-type SettlementButtonOnyxProps = {
- /** The last payment method used per policy */
- nvpLastPaymentMethod?: OnyxEntry;
-
- /** The policy of the report */
- policy: OnyxEntry;
-};
-
-type SettlementButtonProps = SettlementButtonOnyxProps & {
- /** Callback to execute when this button is pressed. Receives a single payment type argument. */
- onPress: (paymentType?: PaymentMethodType, payAsBusiness?: boolean) => void;
-
- /** Callback when the payment options popover is shown */
- onPaymentOptionsShow?: () => void;
-
- /** Callback when the payment options popover is closed */
- onPaymentOptionsHide?: () => void;
-
- /** The route to redirect if user does not have a payment method setup */
- enablePaymentsRoute: EnablePaymentsRoute;
-
- /** Call the onPress function on main button when Enter key is pressed */
- pressOnEnter?: boolean;
-
- /** Settlement currency type */
- currency?: string;
-
- /** When the button is opened via an IOU, ID for the chatReport that the IOU is linked to */
- chatReportID?: string;
-
- /** The IOU/Expense report we are paying */
- iouReport?: OnyxEntry;
-
- /** Should we show the payment options? */
- shouldHidePaymentOptions?: boolean;
-
- /** Should we show the payment options? */
- shouldShowApproveButton?: boolean;
-
- /** Should approve button be disabled? */
- shouldDisableApproveButton?: boolean;
-
- /** The policyID of the report we are paying */
- policyID?: string;
-
- /** Additional styles to add to the component */
- style?: StyleProp;
-
- /** Total money amount in form */
- formattedAmount?: string;
-
- /** The size of button size */
- buttonSize?: ButtonSizeValue;
-
- /** Route for the Add Bank Account screen for a given navigation stack */
- addBankAccountRoute?: Route;
-
- /** Route for the Add Debit Card screen for a given navigation stack */
- addDebitCardRoute?: Route;
-
- /** Whether the button should be disabled */
- isDisabled?: boolean;
-
- /** Whether we should show a loading state for the main button */
- isLoading?: boolean;
-
- /** The anchor alignment of the popover menu for payment method dropdown */
- paymentMethodDropdownAnchorAlignment?: AnchorAlignment;
-
- /** The anchor alignment of the popover menu for KYC wall popover */
- kycWallAnchorAlignment?: AnchorAlignment;
-
- /** Whether the personal bank account option should be shown */
- shouldShowPersonalBankAccountOption?: boolean;
-
- /** The priority to assign the enter key event listener to buttons. 0 is the highest priority. */
- enterKeyEventListenerPriority?: number;
-
- /** Callback to open confirmation modal if any of the transactions is on HOLD */
- confirmApproval?: () => void;
-
- /** Whether to use keyboard shortcuts for confirmation or not */
- useKeyboardShortcuts?: boolean;
-};
-
function SettlementButton({
addDebitCardRoute = ROUTES.IOU_SEND_ADD_DEBIT_CARD,
addBankAccountRoute = '',
@@ -131,23 +40,20 @@ function SettlementButton({
currency = CONST.CURRENCY.USD,
enablePaymentsRoute,
iouReport,
- // The "nvpLastPaymentMethod" object needs to be stable to prevent the "useMemo"
- // hook from being recreated unnecessarily, hence the use of CONST.EMPTY_OBJECT
- nvpLastPaymentMethod = CONST.EMPTY_OBJECT,
isDisabled = false,
isLoading = false,
formattedAmount = '',
onPress,
pressOnEnter = false,
- policyID = '',
+ policyID = '-1',
shouldHidePaymentOptions = false,
shouldShowApproveButton = false,
shouldDisableApproveButton = false,
style,
+ disabledStyle,
shouldShowPersonalBankAccountOption = false,
enterKeyEventListenerPriority = 0,
confirmApproval,
- policy,
useKeyboardShortcuts = false,
onPaymentOptionsShow,
onPaymentOptionsHide,
@@ -156,6 +62,8 @@ function SettlementButton({
const {isOffline} = useNetwork();
// The app would crash due to subscribing to the entire report collection if chatReportID is an empty string. So we should have a fallback ID here.
const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID || -1}`);
+ const [lastPaymentMethod = '-1'] = useOnyx(ONYXKEYS.NVP_LAST_PAYMENT_METHOD, {selector: (paymentMethod) => paymentMethod?.[policyID]});
+ const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`);
const isInvoiceReport = (!isEmptyObject(iouReport) && ReportUtils.isInvoiceReport(iouReport)) || false;
const shouldShowPaywithExpensifyOption = !shouldHidePaymentOptions && policy?.reimbursementChoice !== CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_MANUAL;
const shouldShowPayElsewhereOption = !shouldHidePaymentOptions && !isInvoiceReport;
@@ -193,9 +101,6 @@ function SettlementButton({
}
// To achieve the one tap pay experience we need to choose the correct payment type as default.
- // If the user has previously chosen a specific payment option or paid for some expense,
- // let's use the last payment method or use default.
- const paymentMethod = nvpLastPaymentMethod?.[policyID] ?? '-1';
if (canUseWallet) {
buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.EXPENSIFY]);
}
@@ -244,14 +149,27 @@ function SettlementButton({
buttonOptions.push(approveButtonOption);
}
- // Put the preferred payment method to the front of the array, so it's shown as default
- if (paymentMethod) {
- return buttonOptions.sort((method) => (method.value === paymentMethod ? -1 : 0));
+ // Put the preferred payment method to the front of the array, so it's shown as default. We assume their last payment method is their preferred.
+ if (lastPaymentMethod) {
+ return buttonOptions.sort((method) => (method.value === lastPaymentMethod ? -1 : 0));
}
return buttonOptions;
// We don't want to reorder the options when the preferred payment method changes while the button is still visible
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
- }, [currency, formattedAmount, iouReport, chatReport, policyID, translate, shouldHidePaymentOptions, shouldShowApproveButton, shouldDisableApproveButton]);
+ }, [
+ iouReport,
+ translate,
+ formattedAmount,
+ shouldDisableApproveButton,
+ isInvoiceReport,
+ currency,
+ shouldHidePaymentOptions,
+ shouldShowApproveButton,
+ shouldShowPaywithExpensifyOption,
+ shouldShowPayElsewhereOption,
+ chatReport,
+ onPress,
+ ]);
const selectPaymentType = (event: KYCFlowEvent, iouPaymentType: PaymentMethodType, triggerKYCFlow: TriggerKYCFlow) => {
if (policy && SubscriptionUtils.shouldRestrictUserBillableActions(policy.id)) {
@@ -274,7 +192,7 @@ function SettlementButton({
return;
}
- playSound(SOUNDS.DONE);
+ playSound(SOUNDS.SUCCESS);
onPress(iouPaymentType);
};
@@ -310,8 +228,14 @@ function SettlementButton({
onPress={(event, iouPaymentType) => selectPaymentType(event, iouPaymentType, triggerKYCFlow)}
pressOnEnter={pressOnEnter}
options={paymentButtonOptions}
- onOptionSelected={(option) => savePreferredPaymentMethod(policyID, option.value)}
+ onOptionSelected={(option) => {
+ if (policyID === '-1') {
+ return;
+ }
+ savePreferredPaymentMethod(policyID, option.value);
+ }}
style={style}
+ disabledStyle={disabledStyle}
buttonSize={buttonSize}
anchorAlignment={paymentMethodDropdownAnchorAlignment}
enterKeyEventListenerPriority={enterKeyEventListenerPriority}
@@ -324,12 +248,4 @@ function SettlementButton({
SettlementButton.displayName = 'SettlementButton';
-export default withOnyx({
- nvpLastPaymentMethod: {
- key: ONYXKEYS.NVP_LAST_PAYMENT_METHOD,
- selector: (paymentMethod) => paymentMethod ?? {},
- },
- policy: {
- key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
- },
-})(SettlementButton);
+export default SettlementButton;
diff --git a/src/components/SettlementButton/types.ts b/src/components/SettlementButton/types.ts
new file mode 100644
index 000000000000..0a26aec914e0
--- /dev/null
+++ b/src/components/SettlementButton/types.ts
@@ -0,0 +1,92 @@
+import type {StyleProp, ViewStyle} from 'react-native';
+import type {OnyxEntry} from 'react-native-onyx';
+import type ROUTES from '@src/ROUTES';
+import type {Route} from '@src/ROUTES';
+import type {ButtonSizeValue} from '@src/styles/utils/types';
+import type {Report} from '@src/types/onyx';
+import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage';
+import type AnchorAlignment from '@src/types/utils/AnchorAlignment';
+
+type EnablePaymentsRoute = typeof ROUTES.ENABLE_PAYMENTS | typeof ROUTES.IOU_SEND_ENABLE_PAYMENTS | typeof ROUTES.SETTINGS_ENABLE_PAYMENTS;
+
+type SettlementButtonProps = {
+ /** Callback to execute when this button is pressed. Receives a single payment type argument. */
+ onPress: (paymentType?: PaymentMethodType, payAsBusiness?: boolean) => void;
+
+ /** Callback when the payment options popover is shown */
+ onPaymentOptionsShow?: () => void;
+
+ /** Callback when the payment options popover is closed */
+ onPaymentOptionsHide?: () => void;
+
+ /** The route to redirect if user does not have a payment method setup */
+ enablePaymentsRoute: EnablePaymentsRoute;
+
+ /** Call the onPress function on main button when Enter key is pressed */
+ pressOnEnter?: boolean;
+
+ /** Settlement currency type */
+ currency?: string;
+
+ /** When the button is opened via an IOU, ID for the chatReport that the IOU is linked to */
+ chatReportID?: string;
+
+ /** The IOU/Expense report we are paying */
+ iouReport?: OnyxEntry;
+
+ /** Should we show the payment options? */
+ shouldHidePaymentOptions?: boolean;
+
+ /** Should we show the payment options? */
+ shouldShowApproveButton?: boolean;
+
+ /** Should approve button be disabled? */
+ shouldDisableApproveButton?: boolean;
+
+ /** The policyID of the report we are paying */
+ policyID?: string;
+
+ /** Additional styles to add to the component */
+ style?: StyleProp;
+
+ /** Additional styles to add to the component when it's disabled */
+ disabledStyle?: StyleProp;
+
+ /** Total money amount in form */
+ formattedAmount?: string;
+
+ /** The size of button size */
+ buttonSize?: ButtonSizeValue;
+
+ /** Route for the Add Bank Account screen for a given navigation stack */
+ addBankAccountRoute?: Route;
+
+ /** Route for the Add Debit Card screen for a given navigation stack */
+ addDebitCardRoute?: Route;
+
+ /** Whether the button should be disabled */
+ isDisabled?: boolean;
+
+ /** Whether we should show a loading state for the main button */
+ isLoading?: boolean;
+
+ /** The anchor alignment of the popover menu for payment method dropdown */
+ paymentMethodDropdownAnchorAlignment?: AnchorAlignment;
+
+ /** The anchor alignment of the popover menu for KYC wall popover */
+ kycWallAnchorAlignment?: AnchorAlignment;
+
+ /** Whether the personal bank account option should be shown */
+ shouldShowPersonalBankAccountOption?: boolean;
+
+ /** The priority to assign the enter key event listener to buttons. 0 is the highest priority. */
+ enterKeyEventListenerPriority?: number;
+
+ /** Callback to open confirmation modal if any of the transactions is on HOLD */
+ confirmApproval?: () => void;
+
+ /** Whether to use keyboard shortcuts for confirmation or not */
+ useKeyboardShortcuts?: boolean;
+};
+
+export default SettlementButtonProps;
diff --git a/src/hooks/useDeleteSavedSearch.tsx b/src/hooks/useDeleteSavedSearch.tsx
new file mode 100644
index 000000000000..ddd2b83f5bc9
--- /dev/null
+++ b/src/hooks/useDeleteSavedSearch.tsx
@@ -0,0 +1,47 @@
+import React, {useState} from 'react';
+import ConfirmModal from '@components/ConfirmModal';
+import Navigation from '@libs/Navigation/Navigation';
+import * as SearchUtils from '@libs/SearchUtils';
+import * as SearchActions from '@userActions/Search';
+import CONST from '@src/CONST';
+import ROUTES from '@src/ROUTES';
+import useLocalize from './useLocalize';
+
+export default function useDeleteSavedSearch() {
+ const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false);
+ const [hashToDelete, setHashToDelete] = useState(0);
+ const {translate} = useLocalize();
+
+ const showDeleteModal = (hash: number) => {
+ setIsDeleteModalVisible(true);
+ setHashToDelete(hash);
+ };
+
+ const handleDelete = () => {
+ SearchActions.deleteSavedSearch(hashToDelete);
+ setIsDeleteModalVisible(false);
+ SearchActions.clearAdvancedFilters();
+ Navigation.navigate(
+ ROUTES.SEARCH_CENTRAL_PANE.getRoute({
+ query: SearchUtils.buildCannedSearchQuery(CONST.SEARCH.DATA_TYPES.EXPENSE, CONST.SEARCH.STATUS.EXPENSE.ALL),
+ }),
+ );
+ };
+
+ function DeleteConfirmModal() {
+ return (
+ setIsDeleteModalVisible(false)}
+ isVisible={isDeleteModalVisible}
+ prompt={translate('search.deleteSavedSearchConfirm')}
+ confirmText={translate('common.delete')}
+ cancelText={translate('common.cancel')}
+ danger
+ />
+ );
+ }
+
+ return {showDeleteModal, DeleteConfirmModal};
+}
diff --git a/src/hooks/useViolations.ts b/src/hooks/useViolations.ts
index 44b8e982139e..f645b03a99c3 100644
--- a/src/hooks/useViolations.ts
+++ b/src/hooks/useViolations.ts
@@ -5,7 +5,7 @@ import type {TransactionViolation, ViolationName} from '@src/types/onyx';
/**
* Names of Fields where violations can occur.
*/
-type ViolationField = 'amount' | 'billable' | 'category' | 'comment' | 'date' | 'merchant' | 'receipt' | 'tag' | 'tax' | 'none';
+type ViolationField = 'amount' | 'billable' | 'category' | 'comment' | 'date' | 'merchant' | 'receipt' | 'tag' | 'tax' | 'customUnitRateID' | 'none';
/**
* Map from Violation Names to the field where that violation can occur.
@@ -17,7 +17,7 @@ const violationFields: Record = {
cashExpenseWithNoReceipt: 'receipt',
categoryOutOfPolicy: 'category',
conversionSurcharge: 'amount',
- customUnitOutOfPolicy: 'merchant',
+ customUnitOutOfPolicy: 'customUnitRateID',
duplicatedTransaction: 'merchant',
fieldRequired: 'merchant',
futureDate: 'date',
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 5a1e242519c2..f36db113a2aa 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -78,7 +78,7 @@ import type {
ResolutionConstraintsParams,
RoomNameReservedErrorParams,
RoomRenamedToParams,
- SetTheDistanceParams,
+ SetTheDistanceMerchantParams,
SetTheRequestParams,
SettledAfterAddedBankAccountParams,
SettleExpensifyCardParams,
@@ -98,7 +98,7 @@ import type {
UnapprovedParams,
UnshareParams,
UntilTimeParams,
- UpdatedTheDistanceParams,
+ UpdatedTheDistanceMerchantParams,
UpdatedTheRequestParams,
UsePlusButtonParams,
UserIsAlreadyMemberParams,
@@ -352,6 +352,7 @@ export default {
default: 'Default',
update: 'Update',
member: 'Member',
+ auditor: 'Auditor',
role: 'Role',
currency: 'Currency',
rate: 'Rate',
@@ -396,6 +397,7 @@ export default {
sent: 'Sent',
links: 'Links',
days: 'days',
+ rename: 'Rename',
},
location: {
useCurrent: 'Use current location',
@@ -841,11 +843,12 @@ export default {
pendingConversionMessage: "Total will update when you're back online",
changedTheExpense: 'changed the expense',
setTheRequest: ({valueName, newValueToDisplay}: SetTheRequestParams) => `the ${valueName} to ${newValueToDisplay}`,
- setTheDistance: ({newDistanceToDisplay, newAmountToDisplay}: SetTheDistanceParams) => `set the distance to ${newDistanceToDisplay}, which set the amount to ${newAmountToDisplay}`,
+ setTheDistanceMerchant: ({translatedChangedField, newMerchant, newAmountToDisplay}: SetTheDistanceMerchantParams) =>
+ `set the ${translatedChangedField} to ${newMerchant}, which set the amount to ${newAmountToDisplay}`,
removedTheRequest: ({valueName, oldValueToDisplay}: RemovedTheRequestParams) => `the ${valueName} (previously ${oldValueToDisplay})`,
updatedTheRequest: ({valueName, newValueToDisplay, oldValueToDisplay}: UpdatedTheRequestParams) => `the ${valueName} to ${newValueToDisplay} (previously ${oldValueToDisplay})`,
- updatedTheDistance: ({newDistanceToDisplay, oldDistanceToDisplay, newAmountToDisplay, oldAmountToDisplay}: UpdatedTheDistanceParams) =>
- `changed the distance to ${newDistanceToDisplay} (previously ${oldDistanceToDisplay}), which updated the amount to ${newAmountToDisplay} (previously ${oldAmountToDisplay})`,
+ updatedTheDistanceMerchant: ({translatedChangedField, newMerchant, oldMerchant, newAmountToDisplay, oldAmountToDisplay}: UpdatedTheDistanceMerchantParams) =>
+ `changed the ${translatedChangedField} to ${newMerchant} (previously ${oldMerchant}), which updated the amount to ${newAmountToDisplay} (previously ${oldAmountToDisplay})`,
threadExpenseReportName: ({formattedAmount, comment}: ThreadRequestReportNameParams) => `${formattedAmount} ${comment ? `for ${comment}` : 'expense'}`,
threadTrackReportName: ({formattedAmount, comment}: ThreadRequestReportNameParams) => `Tracking ${formattedAmount} ${comment ? `for ${comment}` : ''}`,
threadPaySomeoneReportName: ({formattedAmount, comment}: ThreadSentMoneyReportNameParams) => `${formattedAmount} sent${comment ? ` for ${comment}` : ''}`,
@@ -2225,6 +2228,21 @@ export default {
reuseExistingConnection: 'Reuse existing connection',
existingConnections: 'Existing connections',
lastSyncDate: (connectionName: string, formattedDate: string) => `${connectionName} - Last synced ${formattedDate}`,
+ memberAlternateText: 'Members can submit and approve reports.',
+ adminAlternateText: 'Admins have full edit access to all reports and workspace settings.',
+ auditorAlternateText: 'Auditors can view and comment on reports.',
+ roleName: (role?: string): string => {
+ switch (role) {
+ case CONST.POLICY.ROLE.ADMIN:
+ return 'Admin';
+ case CONST.POLICY.ROLE.AUDITOR:
+ return 'Auditor';
+ case CONST.POLICY.ROLE.USER:
+ return 'Member';
+ default:
+ return 'Member';
+ }
+ },
},
qbo: {
importDescription: 'Choose which coding configurations to import from QuickBooks Online to Expensify.',
@@ -3010,6 +3028,20 @@ export default {
disableCardTitle: 'Disable company cards',
disableCardPrompt: 'You can’t disable company cards because this feature is in use. Reach out to the Concierge for next steps.',
disableCardButton: 'Chat with Concierge',
+ cardDetails: 'Card details',
+ cardNumber: 'Card number',
+ cardholder: 'Cardholder',
+ cardName: 'Card name',
+ integrationExport: (integration: string, type: string) => `${integration} ${type} export`,
+ integrationExportTitleFirstPart: (integration: string) => `Choose the ${integration} account where transactions should be exported. Select a different`,
+ integrationExportTitleLinkPart: 'export option',
+ integrationExportTitleSecondPart: 'to change the available accounts.',
+ lastUpdated: 'Last updated',
+ transactionStartDate: 'Transaction start date',
+ updateCard: 'Update card',
+ unassignCard: 'Unassign card',
+ unassign: 'Unassign',
+ unassignCardDescription: 'Unassign this card will remove all transactions on draft reports from the cardholder’s account.',
assignCard: 'Assign card',
cardFeedName: 'Card feed name',
cardFeedNameDescription: 'Give the card feed a unique name so you can tell it apart from the others.',
@@ -3029,6 +3061,10 @@ export default {
setTransactionLiabilityDescription: 'When enabled, cardholders can delete card transactions. New transactions will follow this rule.',
emptyAddedFeedTitle: 'Assign company cards',
emptyAddedFeedDescription: 'Get started by assigning your first card to a member.',
+ giveItNameInstruction: 'Give the card a name that sets it apart from the others.',
+ updating: 'Updating...',
+ noAccountsFound: 'No accounts found',
+ noAccountsFoundDescription: (connection: string) => `Please add the account in ${connection} and sync the connection again.`,
},
workflows: {
title: 'Workflows',
@@ -3214,6 +3250,7 @@ export default {
transferOwner: 'Transfer owner',
makeMember: 'Make member',
makeAdmin: 'Make admin',
+ makeAuditor: 'Make auditor',
selectAll: 'Select all',
error: {
genericAdd: 'There was a problem adding this workspace member.',
@@ -3461,6 +3498,10 @@ export default {
return 'Marking Expensify reports as reimbursed';
case 'netSuiteSyncExpensifyReimbursedReports':
return 'Marking NetSuite bills and invoices as paid';
+ case 'netSuiteImportVendorsTitle':
+ return 'Importing vendors';
+ case 'netSuiteImportCustomListsTitle':
+ return 'Importing custom lists';
case 'netSuiteSyncImportCustomLists':
return 'Importing custom lists';
case 'netSuiteSyncImportSubsidiaries':
@@ -3982,6 +4023,12 @@ export default {
buttonText: 'Book a trip',
},
},
+ saveSearch: 'Save search',
+ saveSearchTooltipText: 'You can rename your saved search',
+ deleteSavedSearch: 'Delete saved search',
+ deleteSavedSearchConfirm: 'Are you sure you want to delete this search?',
+ searchName: 'Search name',
+ savedSearchesMenuItemTitle: 'Saved',
groupedExpenses: 'grouped expenses',
bulkActions: {
delete: 'Delete',
@@ -4369,7 +4416,7 @@ export default {
cashExpenseWithNoReceipt: ({formattedLimit}: ViolationsCashExpenseWithNoReceiptParams) => `Receipt required${formattedLimit ? ` over ${formattedLimit}` : ''}`,
categoryOutOfPolicy: 'Category no longer valid',
conversionSurcharge: ({surcharge}: ViolationsConversionSurchargeParams) => `Applied ${surcharge}% conversion surcharge`,
- customUnitOutOfPolicy: 'Unit no longer valid',
+ customUnitOutOfPolicy: 'Rate not valid for this workspace',
duplicatedTransaction: 'Duplicate',
fieldRequired: 'Report fields are required',
futureDate: 'Future date not allowed',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 0ce9c70a9b56..8a18c6c002e0 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -77,7 +77,7 @@ import type {
ResolutionConstraintsParams,
RoomNameReservedErrorParams,
RoomRenamedToParams,
- SetTheDistanceParams,
+ SetTheDistanceMerchantParams,
SetTheRequestParams,
SettledAfterAddedBankAccountParams,
SettleExpensifyCardParams,
@@ -96,7 +96,7 @@ import type {
UnapprovedParams,
UnshareParams,
UntilTimeParams,
- UpdatedTheDistanceParams,
+ UpdatedTheDistanceMerchantParams,
UpdatedTheRequestParams,
UsePlusButtonParams,
UserIsAlreadyMemberParams,
@@ -269,6 +269,7 @@ export default {
comma: 'la coma',
semicolon: 'el punto y coma',
please: 'Por favor',
+ rename: 'Renombrar',
contactUs: 'contáctenos',
pleaseEnterEmailOrPhoneNumber: 'Por favor, escribe un email o número de teléfono',
fixTheErrors: 'corrige los errores',
@@ -342,6 +343,7 @@ export default {
default: 'Predeterminado',
update: 'Actualizar',
member: 'Miembro',
+ auditor: 'Auditor',
role: 'Role',
currency: 'Divisa',
rate: 'Tarifa',
@@ -834,13 +836,13 @@ export default {
pendingConversionMessage: 'El total se actualizará cuando estés online',
changedTheExpense: 'cambió el gasto',
setTheRequest: ({valueName, newValueToDisplay}: SetTheRequestParams) => `${valueName === 'comerciante' ? 'el' : 'la'} ${valueName} a ${newValueToDisplay}`,
- setTheDistance: ({newDistanceToDisplay, newAmountToDisplay}: SetTheDistanceParams) =>
- `estableció la distancia a ${newDistanceToDisplay}, lo que estableció el importe a ${newAmountToDisplay}`,
+ setTheDistanceMerchant: ({translatedChangedField, newMerchant, newAmountToDisplay}: SetTheDistanceMerchantParams) =>
+ `estableció la ${translatedChangedField} a ${newMerchant}, lo que estableció el importe a ${newAmountToDisplay}`,
removedTheRequest: ({valueName, oldValueToDisplay}: RemovedTheRequestParams) => `${valueName === 'comerciante' ? 'el' : 'la'} ${valueName} (previamente ${oldValueToDisplay})`,
updatedTheRequest: ({valueName, newValueToDisplay, oldValueToDisplay}: UpdatedTheRequestParams) =>
`${valueName === 'comerciante' || valueName === 'importe' || valueName === 'gasto' ? 'el' : 'la'} ${valueName} a ${newValueToDisplay} (previamente ${oldValueToDisplay})`,
- updatedTheDistance: ({newDistanceToDisplay, oldDistanceToDisplay, newAmountToDisplay, oldAmountToDisplay}: UpdatedTheDistanceParams) =>
- `cambió la distancia a ${newDistanceToDisplay} (previamente ${oldDistanceToDisplay}), lo que cambió el importe a ${newAmountToDisplay} (previamente ${oldAmountToDisplay})`,
+ updatedTheDistanceMerchant: ({translatedChangedField, newMerchant, oldMerchant, newAmountToDisplay, oldAmountToDisplay}: UpdatedTheDistanceMerchantParams) =>
+ `cambió la ${translatedChangedField} a ${newMerchant} (previamente ${oldMerchant}), lo que cambió el importe a ${newAmountToDisplay} (previamente ${oldAmountToDisplay})`,
threadExpenseReportName: ({formattedAmount, comment}: ThreadRequestReportNameParams) => `${comment ? `${formattedAmount} para ${comment}` : `Gasto de ${formattedAmount}`}`,
threadTrackReportName: ({formattedAmount, comment}: ThreadRequestReportNameParams) => `Seguimiento ${formattedAmount} ${comment ? `para ${comment}` : ''}`,
threadPaySomeoneReportName: ({formattedAmount, comment}: ThreadSentMoneyReportNameParams) => `${formattedAmount} enviado${comment ? ` para ${comment}` : ''}`,
@@ -2256,6 +2258,21 @@ export default {
existingConnections: 'Conexiones existentes',
lastSyncDate: (connectionName: string, formattedDate: string) => `${connectionName} - Última sincronización ${formattedDate}`,
topLevel: 'Nivel superior',
+ memberAlternateText: 'Los miembros pueden presentar y aprobar informes.',
+ adminAlternateText: 'Los administradores tienen acceso total para editar todos los informes y la configuración del área de trabajo.',
+ auditorAlternateText: 'Los auditores pueden ver y comentar los informes.',
+ roleName: (role?: string): string => {
+ switch (role) {
+ case CONST.POLICY.ROLE.ADMIN:
+ return 'Administrador';
+ case CONST.POLICY.ROLE.AUDITOR:
+ return 'Auditor';
+ case CONST.POLICY.ROLE.USER:
+ return 'Miembro';
+ default:
+ return 'Miembro';
+ }
+ },
},
qbo: {
importDescription: 'Elige que configuraciónes de codificación son importadas desde QuickBooks Online a Expensify.',
@@ -3059,6 +3076,20 @@ export default {
disableCardTitle: 'Deshabilitar tarjetas de empresa',
disableCardPrompt: 'No puedes deshabilitar las tarjetas de empresa porque esta función está en uso. Por favor, contacta a Concierge para los próximos pasos.',
disableCardButton: 'Chatear con Concierge',
+ cardDetails: 'Datos de la tarjeta',
+ cardNumber: 'Número de la tarjeta',
+ cardholder: 'Titular de la tarjeta',
+ cardName: 'Nombre de la tarjeta',
+ integrationExport: (integration: string, type: string) => `Exportación a ${integration} ${type}`,
+ integrationExportTitleFirstPart: (integration: string) => `Seleccione la cuenta ${integration} donde se deben exportar las transacciones. Seleccione una cuenta diferente`,
+ integrationExportTitleLinkPart: 'opción de exportación',
+ integrationExportTitleSecondPart: 'para cambiar las cuentas disponibles.',
+ lastUpdated: 'Última actualización',
+ transactionStartDate: 'Fecha de inicio de transacciones',
+ updateCard: 'Actualizar tarjeta',
+ unassignCard: 'Desasignar tarjeta',
+ unassign: 'Desasignar',
+ unassignCardDescription: 'Desasignar esta tarjeta eliminará todas las transacciones en informes en borrador de la cuenta del titular.',
assignCard: 'Asignar tarjeta',
cardFeedName: 'Nombre del feed de tarjeta',
cardFeedNameDescription: 'Dale al feed de tarjeta un nombre único para que puedas distinguirlo de los demás.',
@@ -3079,6 +3110,10 @@ export default {
'Cuando está habilitada, los titulares de tarjetas pueden eliminar transacciones con tarjeta. Las transacciones nuevas seguirán esta regla.',
emptyAddedFeedTitle: 'Asignar tarjetas de empresa',
emptyAddedFeedDescription: 'Comienza asignando tu primera tarjeta a un miembro.',
+ giveItNameInstruction: 'Dale a la tarjeta un nombre que la distinga de las demás.',
+ updating: 'Actualizando...',
+ noAccountsFound: 'No se han encontrado cuentas',
+ noAccountsFoundDescription: (connection: string) => `Añade la cuenta en ${connection} y sincroniza la conexión de nuevo.`,
},
workflows: {
title: 'Flujos de trabajo',
@@ -3264,6 +3299,7 @@ export default {
transferOwner: 'Transferir la propiedad',
makeMember: 'Hacer miembro',
makeAdmin: 'Hacer administrador',
+ makeAuditor: 'Hacer auditor',
selectAll: 'Seleccionar todo',
error: {
genericAdd: 'Ha ocurrido un problema al añadir el miembro al espacio de trabajo.',
@@ -3447,6 +3483,10 @@ export default {
return 'Importando proveedores';
case 'netSuiteSyncExpensifyReimbursedReports':
return 'Marcando facturas y recibos de NetSuite como pagados';
+ case 'netSuiteImportVendorsTitle':
+ return 'Importando proveedores';
+ case 'netSuiteImportCustomListsTitle':
+ return 'Importando listas personalizadas';
case 'intacctCheckConnection':
return 'Comprobando la conexión a Sage Intacct';
case 'intacctImportDimensions':
@@ -4034,6 +4074,12 @@ export default {
buttonText: 'Reserva un viaje',
},
},
+ saveSearch: 'Guardar búsqueda',
+ saveSearchTooltipText: 'Puedes cambiar el nombre de tu búsqueda guardada',
+ savedSearchesMenuItemTitle: 'Guardadas',
+ searchName: 'Nombre de la búsqueda',
+ deleteSavedSearch: 'Eliminar búsqueda guardada',
+ deleteSavedSearchConfirm: '¿Estás seguro de que quieres eliminar esta búsqueda?',
groupedExpenses: 'gastos agrupados',
bulkActions: {
delete: 'Eliminar',
@@ -4883,7 +4929,7 @@ export default {
cashExpenseWithNoReceipt: ({formattedLimit}: ViolationsCashExpenseWithNoReceiptParams) => `Recibo obligatorio para cantidades mayores de ${formattedLimit}`,
categoryOutOfPolicy: 'La categoría ya no es válida',
conversionSurcharge: ({surcharge}: ViolationsConversionSurchargeParams = {}) => `${surcharge}% de recargo aplicado`,
- customUnitOutOfPolicy: 'La unidad ya no es válida',
+ customUnitOutOfPolicy: 'Tasa inválida para este espacio de trabajo',
duplicatedTransaction: 'Duplicado',
fieldRequired: 'Los campos del informe son obligatorios',
futureDate: 'Fecha futura no permitida',
diff --git a/src/languages/types.ts b/src/languages/types.ts
index ca2b70f6ac61..a7a11fafb27b 100644
--- a/src/languages/types.ts
+++ b/src/languages/types.ts
@@ -207,13 +207,13 @@ type ParentNavigationSummaryParams = {reportName?: string; workspaceName?: strin
type SetTheRequestParams = {valueName: string; newValueToDisplay: string};
-type SetTheDistanceParams = {newDistanceToDisplay: string; newAmountToDisplay: string};
+type SetTheDistanceMerchantParams = {translatedChangedField: string; newMerchant: string; newAmountToDisplay: string};
type RemovedTheRequestParams = {valueName: string; oldValueToDisplay: string};
type UpdatedTheRequestParams = {valueName: string; newValueToDisplay: string; oldValueToDisplay: string};
-type UpdatedTheDistanceParams = {newDistanceToDisplay: string; oldDistanceToDisplay: string; newAmountToDisplay: string; oldAmountToDisplay: string};
+type UpdatedTheDistanceMerchantParams = {translatedChangedField: string; newMerchant: string; oldMerchant: string; newAmountToDisplay: string; oldAmountToDisplay: string};
type FormattedMaxLengthParams = {formattedMaxLength: string};
@@ -437,7 +437,7 @@ export type {
ResolutionConstraintsParams,
RoomNameReservedErrorParams,
RoomRenamedToParams,
- SetTheDistanceParams,
+ SetTheDistanceMerchantParams,
SetTheRequestParams,
SettleExpensifyCardParams,
SettledAfterAddedBankAccountParams,
@@ -454,7 +454,7 @@ export type {
TranslationFlatObject,
TranslationPaths,
UntilTimeParams,
- UpdatedTheDistanceParams,
+ UpdatedTheDistanceMerchantParams,
UpdatedTheRequestParams,
UsePlusButtonParams,
UserIsAlreadyMemberParams,
diff --git a/src/libs/API/parameters/DeleteSavedSearch.ts b/src/libs/API/parameters/DeleteSavedSearch.ts
new file mode 100644
index 000000000000..23b20204bcd2
--- /dev/null
+++ b/src/libs/API/parameters/DeleteSavedSearch.ts
@@ -0,0 +1,5 @@
+type DeleteSavedSearchParams = {
+ hash: number;
+};
+
+export default DeleteSavedSearchParams;
diff --git a/src/libs/API/parameters/SaveSearch.ts b/src/libs/API/parameters/SaveSearch.ts
new file mode 100644
index 000000000000..e0ad38dd8363
--- /dev/null
+++ b/src/libs/API/parameters/SaveSearch.ts
@@ -0,0 +1,8 @@
+import type {SearchQueryString} from '@components/Search/types';
+
+type SaveSearchParams = {
+ jsonQuery: SearchQueryString;
+ name?: string;
+};
+
+export default SaveSearchParams;
diff --git a/src/libs/API/parameters/SetCompanyCardExportAccountParams.ts b/src/libs/API/parameters/SetCompanyCardExportAccountParams.ts
new file mode 100644
index 000000000000..861345ff9c55
--- /dev/null
+++ b/src/libs/API/parameters/SetCompanyCardExportAccountParams.ts
@@ -0,0 +1,7 @@
+type SetCompanyCardExportAccountParams = {
+ authToken?: string | null;
+ cardID: number;
+ exportAccountDetails: Record;
+};
+
+export default SetCompanyCardExportAccountParams;
diff --git a/src/libs/API/parameters/UnassignCompanyCard.ts b/src/libs/API/parameters/UnassignCompanyCard.ts
new file mode 100644
index 000000000000..10e04aa13f82
--- /dev/null
+++ b/src/libs/API/parameters/UnassignCompanyCard.ts
@@ -0,0 +1,6 @@
+type UnassignCompanyCard = {
+ authToken?: string | null;
+ cardID: string;
+};
+
+export default UnassignCompanyCard;
diff --git a/src/libs/API/parameters/UpdateCompanyCard.ts b/src/libs/API/parameters/UpdateCompanyCard.ts
new file mode 100644
index 000000000000..3d5eb3c580cb
--- /dev/null
+++ b/src/libs/API/parameters/UpdateCompanyCard.ts
@@ -0,0 +1,6 @@
+type UpdateCompanyCard = {
+ authToken?: string | null;
+ cardID: string;
+};
+
+export default UpdateCompanyCard;
diff --git a/src/libs/API/parameters/UpdateCompanyCardNameParams.ts b/src/libs/API/parameters/UpdateCompanyCardNameParams.ts
new file mode 100644
index 000000000000..243cffe94b06
--- /dev/null
+++ b/src/libs/API/parameters/UpdateCompanyCardNameParams.ts
@@ -0,0 +1,7 @@
+type UpdateCompanyCardNameParams = {
+ authToken?: string | null;
+ cardID: number;
+ cardName: string;
+};
+
+export default UpdateCompanyCardNameParams;
diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts
index dcf8bfc9de66..c780003c3e3f 100644
--- a/src/libs/API/parameters/index.ts
+++ b/src/libs/API/parameters/index.ts
@@ -305,6 +305,8 @@ export type {default as EnablePolicyCompanyCardsParams} from './EnablePolicyComp
export type {default as ToggleCardContinuousReconciliationParams} from './ToggleCardContinuousReconciliationParams';
export type {default as CardDeactivateParams} from './CardDeactivateParams';
export type {default as UpdateExpensifyCardLimitTypeParams} from './UpdateExpensifyCardLimitTypeParams';
+export type {default as SaveSearchParams} from './SaveSearch';
+export type {default as DeleteSavedSearchParams} from './DeleteSavedSearch';
export type {default as SetPolicyCategoryReceiptsRequiredParams} from './SetPolicyCategoryReceiptsRequiredParams';
export type {default as RemovePolicyCategoryReceiptsRequiredParams} from './RemovePolicyCategoryReceiptsRequiredParams';
export type {default as UpdateQuickbooksOnlineAutoCreateVendorParams} from './UpdateQuickbooksOnlineAutoCreateVendorParams';
@@ -316,4 +318,8 @@ export type {default as UpdateCardSettlementAccountParams} from './UpdateCardSet
export type {default as SetCompanyCardFeedName} from './SetCompanyCardFeedName';
export type {default as DeleteCompanyCardFeed} from './DeleteCompanyCardFeed';
export type {default as SetCompanyCardTransactionLiability} from './SetCompanyCardTransactionLiability';
+export type {default as UnassignCompanyCard} from './UnassignCompanyCard';
+export type {default as UpdateCompanyCard} from './UpdateCompanyCard';
+export type {default as UpdateCompanyCardNameParams} from './UpdateCompanyCardNameParams';
+export type {default as SetCompanyCardExportAccountParams} from './SetCompanyCardExportAccountParams';
export type {default as SetMissingPersonalDetailsAndShipExpensifyCardParams} from './SetMissingPersonalDetailsAndShipExpensifyCardParams';
diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts
index 2db83a89bbe4..81b748ce6edb 100644
--- a/src/libs/API/types.ts
+++ b/src/libs/API/types.ts
@@ -369,15 +369,31 @@ const WRITE_COMMANDS = {
CREATE_ADMIN_ISSUED_VIRTUAL_CARD: 'CreateAdminIssuedVirtualCard',
ADD_DELEGATE: 'AddDelegate',
TOGGLE_CARD_CONTINUOUS_RECONCILIATION: 'ToggleCardContinuousReconciliation',
+ SAVE_SEARCH: 'SaveSearch',
+ DELETE_SAVED_SEARCH: 'DeleteSavedSearch',
UPDATE_CARD_SETTLEMENT_FREQUENCY: 'UpdateCardSettlementFrequency',
UPDATE_CARD_SETTLEMENT_ACCOUNT: 'UpdateCardSettlementAccount',
UPDATE_XERO_IMPORT_TRACKING_CATEGORIES: 'UpdateXeroImportTrackingCategories',
UPDATE_XERO_IMPORT_TAX_RATES: 'UpdateXeroImportTaxRates',
UPDATE_XERO_TENANT_ID: 'UpdateXeroTenantID',
UPDATE_XERO_MAPPING: 'UpdateXeroMappings',
+ UPDATE_XERO_IMPORT_CUSTOMERS: 'UpdateXeroImportCustomers',
+ UPDATE_XERO_ENABLE_NEW_CATEGORIES: 'UpdateXeroEnableNewCategories',
+ UPDATE_XERO_AUTO_SYNC: 'UpdateXeroAutoSync',
+ UPDATE_XERO_EXPORT_BILL_STATUS: 'UpdateXeroExportBillStatus',
+ UPDATE_XERO_EXPORT_BILL_DATE: 'UpdateXeroExportBillDate',
+ UPDATE_XERO_EXPORT_EXPORTER: 'UpdateXeroExportExporter',
+ UPDATE_XERO_EXPORT_NON_REIMBURSABLE_ACCOUNT: 'UpdateXeroExportNonReimbursableAccount',
+ UPDATE_XERO_SYNC_INVOICE_COLLECTIONS_ACCOUNT_ID: 'UpdateXeroSyncInvoiceCollectionsAccountID',
+ UPDATE_XERO_SYNC_SYNC_REIMBURSED_REPORTS: 'UpdateXeroSyncSyncReimbursedReports',
+ UPDATE_XERO_SYNC_REIMBURSEMENT_ACCOUNT_ID: 'UpdateXeroSyncReimbursementAccountID',
SET_COMPANY_CARD_FEED_NAME: 'SetFeedName',
DELETE_COMPANY_CARD_FEED: 'RemoveFeed',
SET_COMPANY_CARD_TRANSACTION_LIABILITY: 'SetFeedTransactionLiability',
+ UNASSIGN_COMPANY_CARD: 'UnassignCard',
+ UPDATE_COMPANY_CARD: 'SyncCard',
+ UPDATE_COMPANY_CARD_NAME: 'SetCardName',
+ SET_CARD_EXPORT_ACCOUNT: 'SetCardExportAccount',
SET_MISSING_PERSONAL_DETAILS_AND_SHIP_EXPENSIFY_CARD: 'SetMissingPersonalDetailsAndShipExpensifyCard',
} as const;
@@ -438,6 +454,10 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.UPDATE_PERSONAL_DETAILS_FOR_WALLET]: Parameters.UpdatePersonalDetailsForWalletParams;
[WRITE_COMMANDS.SET_COMPANY_CARD_FEED_NAME]: Parameters.SetCompanyCardFeedName;
[WRITE_COMMANDS.DELETE_COMPANY_CARD_FEED]: Parameters.DeleteCompanyCardFeed;
+ [WRITE_COMMANDS.UNASSIGN_COMPANY_CARD]: Parameters.UnassignCompanyCard;
+ [WRITE_COMMANDS.UPDATE_COMPANY_CARD]: Parameters.UpdateCompanyCard;
+ [WRITE_COMMANDS.UPDATE_COMPANY_CARD_NAME]: Parameters.UpdateCompanyCardNameParams;
+ [WRITE_COMMANDS.SET_CARD_EXPORT_ACCOUNT]: Parameters.SetCompanyCardExportAccountParams;
[WRITE_COMMANDS.SET_COMPANY_CARD_TRANSACTION_LIABILITY]: Parameters.SetCompanyCardTransactionLiability;
[WRITE_COMMANDS.VERIFY_IDENTITY]: Parameters.VerifyIdentityParams;
[WRITE_COMMANDS.ACCEPT_WALLET_TERMS]: Parameters.AcceptWalletTermsParams;
@@ -757,6 +777,8 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.CREATE_ADMIN_ISSUED_VIRTUAL_CARD]: Omit;
[WRITE_COMMANDS.ADD_DELEGATE]: Parameters.AddDelegateParams;
[WRITE_COMMANDS.TOGGLE_CARD_CONTINUOUS_RECONCILIATION]: Parameters.ToggleCardContinuousReconciliationParams;
+ [WRITE_COMMANDS.SAVE_SEARCH]: Parameters.SaveSearchParams;
+ [WRITE_COMMANDS.DELETE_SAVED_SEARCH]: Parameters.DeleteSavedSearchParams;
[WRITE_COMMANDS.UPDATE_CARD_SETTLEMENT_FREQUENCY]: Parameters.UpdateCardSettlementFrequencyParams;
[WRITE_COMMANDS.UPDATE_CARD_SETTLEMENT_ACCOUNT]: Parameters.UpdateCardSettlementAccountParams;
[WRITE_COMMANDS.SET_MISSING_PERSONAL_DETAILS_AND_SHIP_EXPENSIFY_CARD]: Parameters.SetMissingPersonalDetailsAndShipExpensifyCardParams;
@@ -766,6 +788,17 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.UPDATE_XERO_IMPORT_TAX_RATES]: Parameters.UpdateXeroGenericTypeParams;
[WRITE_COMMANDS.UPDATE_XERO_MAPPING]: Parameters.UpdateXeroGenericTypeParams;
[WRITE_COMMANDS.UPDATE_XERO_IMPORT_TRACKING_CATEGORIES]: Parameters.UpdateXeroGenericTypeParams;
+ [WRITE_COMMANDS.UPDATE_XERO_IMPORT_CUSTOMERS]: Parameters.UpdateXeroGenericTypeParams;
+ [WRITE_COMMANDS.UPDATE_XERO_ENABLE_NEW_CATEGORIES]: Parameters.UpdateXeroGenericTypeParams;
+ [WRITE_COMMANDS.UPDATE_XERO_AUTO_SYNC]: Parameters.UpdateXeroGenericTypeParams;
+ [WRITE_COMMANDS.UPDATE_XERO_AUTO_SYNC]: Parameters.UpdateXeroGenericTypeParams;
+ [WRITE_COMMANDS.UPDATE_XERO_EXPORT_BILL_STATUS]: Parameters.UpdateXeroGenericTypeParams;
+ [WRITE_COMMANDS.UPDATE_XERO_EXPORT_BILL_DATE]: Parameters.UpdateXeroGenericTypeParams;
+ [WRITE_COMMANDS.UPDATE_XERO_EXPORT_EXPORTER]: Parameters.UpdateXeroGenericTypeParams;
+ [WRITE_COMMANDS.UPDATE_XERO_EXPORT_NON_REIMBURSABLE_ACCOUNT]: Parameters.UpdateXeroGenericTypeParams;
+ [WRITE_COMMANDS.UPDATE_XERO_SYNC_INVOICE_COLLECTIONS_ACCOUNT_ID]: Parameters.UpdateXeroGenericTypeParams;
+ [WRITE_COMMANDS.UPDATE_XERO_SYNC_SYNC_REIMBURSED_REPORTS]: Parameters.UpdateXeroGenericTypeParams;
+ [WRITE_COMMANDS.UPDATE_XERO_SYNC_REIMBURSEMENT_ACCOUNT_ID]: Parameters.UpdateXeroGenericTypeParams;
};
const READ_COMMANDS = {
diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts
index 2398bc1e729a..58dbc07aae8d 100644
--- a/src/libs/CardUtils.ts
+++ b/src/libs/CardUtils.ts
@@ -1,6 +1,6 @@
import lodash from 'lodash';
import Onyx from 'react-native-onyx';
-import type {OnyxEntry} from 'react-native-onyx';
+import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import * as Illustrations from '@src/components/Icon/Illustrations';
import CONST from '@src/CONST';
@@ -8,6 +8,7 @@ import type {TranslationPaths} from '@src/languages/types';
import type {OnyxValues} from '@src/ONYXKEYS';
import ONYXKEYS from '@src/ONYXKEYS';
import type {BankAccountList, Card, CardList, PersonalDetailsList, WorkspaceCardsList} from '@src/types/onyx';
+import type Policy from '@src/types/onyx/Policy';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import type IconAsset from '@src/types/utils/IconAsset';
import localeCompare from './LocaleCompare';
@@ -26,6 +27,14 @@ Onyx.connect({
},
});
+let allCardsLists: OnyxCollection;
+
+Onyx.connect({
+ key: ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST,
+ waitForCollectionCallback: true,
+ callback: (value) => (allCardsLists = value),
+});
+
/**
* @returns string with a month in MM format
*/
@@ -194,6 +203,49 @@ function getCardFeedIcon(cardFeed: string): IconAsset {
return Illustrations.AmexCompanyCards;
}
+/** Checks if the Expensify Card toggle should be disabled */
+function shouldExpensifyCardToggleBeDisabled(workspaceAccountID?: number, areExpensifyCardsEnabled?: boolean): boolean {
+ if (!areExpensifyCardsEnabled || !workspaceAccountID) {
+ return false;
+ }
+
+ const cardsList = allCardsLists?.[`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${CONST.EXPENSIFY_CARD.BANK}`];
+
+ return !isEmptyObject(cardsList);
+}
+
+function getCardDetailsImage(cardFeed: string): IconAsset {
+ if (cardFeed.startsWith(CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD)) {
+ return Illustrations.MasterCardCompanyCardDetail;
+ }
+
+ if (cardFeed.startsWith(CONST.COMPANY_CARD.FEED_BANK_NAME.VISA)) {
+ return Illustrations.VisaCompanyCardDetail;
+ }
+
+ return Illustrations.AmexCardCompanyCardDetail;
+}
+
+function getMemberCards(policy: OnyxEntry, allCardsList: OnyxCollection, accountID?: number) {
+ const workspaceId = policy?.workspaceAccountID ? policy.workspaceAccountID.toString() : '';
+ const cards: WorkspaceCardsList = {};
+ const mockedCardsList = allCardsList ?? {};
+ Object.keys(mockedCardsList)
+ .filter((key) => key !== `${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceId}_${CONST.EXPENSIFY_CARD.BANK}` && key.includes(workspaceId))
+ .forEach((key) => {
+ const feedCards = mockedCardsList?.[key];
+ if (feedCards && Object.keys(feedCards).length > 0) {
+ Object.keys(feedCards).forEach((feedCardKey) => {
+ if (feedCards?.[feedCardKey].accountID !== accountID) {
+ return;
+ }
+ cards[feedCardKey] = feedCards[feedCardKey];
+ });
+ }
+ });
+ return cards;
+}
+
export {
isExpensifyCard,
isCorporateCard,
@@ -210,4 +262,7 @@ export {
getEligibleBankAccountsForCard,
sortCardsByCardholderName,
getCardFeedIcon,
+ shouldExpensifyCardToggleBeDisabled,
+ getCardDetailsImage,
+ getMemberCards,
};
diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts
index 798bc74869e7..99f483cf6ef6 100644
--- a/src/libs/ModifiedExpenseMessage.ts
+++ b/src/libs/ModifiedExpenseMessage.ts
@@ -6,6 +6,7 @@ import type {PolicyTagLists, ReportAction} from '@src/types/onyx';
import * as CurrencyUtils from './CurrencyUtils';
import DateUtils from './DateUtils';
import * as Localize from './Localize';
+import Log from './Log';
import * as PolicyUtils from './PolicyUtils';
import * as ReportActionsUtils from './ReportActionsUtils';
import * as ReportConnection from './ReportConnection';
@@ -94,13 +95,32 @@ function getMessageLine(prefix: string, messageFragments: string[]): string {
}, prefix);
}
-function getForDistanceRequest(newDistance: string, oldDistance: string, newAmount: string, oldAmount: string): string {
- if (!oldDistance) {
- return Localize.translateLocal('iou.setTheDistance', {newDistanceToDisplay: newDistance, newAmountToDisplay: newAmount});
+function getForDistanceRequest(newMerchant: string, oldMerchant: string, newAmount: string, oldAmount: string): string {
+ let changedField: 'distance' | 'rate' = 'distance';
+
+ if (CONST.REGEX.DISTANCE_MERCHANT.test(newMerchant) && CONST.REGEX.DISTANCE_MERCHANT.test(oldMerchant)) {
+ const oldValues = oldMerchant.split('@');
+ const oldDistance = oldValues[0]?.trim() || '';
+ const oldRate = oldValues[1]?.trim() || '';
+ const newValues = newMerchant.split('@');
+ const newDistance = newValues[0]?.trim() || '';
+ const newRate = newValues[1]?.trim() || '';
+
+ if (oldDistance === newDistance && oldRate !== newRate) {
+ changedField = 'rate';
+ }
+ } else {
+ Log.hmmm("Distance request merchant doesn't match NewDot format. Defaulting to showing as distance changed.", {newMerchant, oldMerchant});
+ }
+
+ const translatedChangedField = Localize.translateLocal(`common.${changedField}`).toLowerCase();
+ if (!oldMerchant.length) {
+ return Localize.translateLocal('iou.setTheDistanceMerchant', {translatedChangedField, newMerchant, newAmountToDisplay: newAmount});
}
- return Localize.translateLocal('iou.updatedTheDistance', {
- newDistanceToDisplay: newDistance,
- oldDistanceToDisplay: oldDistance,
+ return Localize.translateLocal('iou.updatedTheDistanceMerchant', {
+ translatedChangedField,
+ newMerchant,
+ oldMerchant,
newAmountToDisplay: newAmount,
oldAmountToDisplay: oldAmount,
});
diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
index f6316408e008..b41b58530a6b 100644
--- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
+++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
@@ -22,6 +22,7 @@ import type {
RoomMembersNavigatorParamList,
SearchAdvancedFiltersParamList,
SearchReportParamList,
+ SearchSavedSearchParamList,
SettingsNavigatorParamList,
SignInNavigatorParamList,
SplitDetailsNavigatorParamList,
@@ -430,6 +431,9 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/companyCards/assignCard/AssignCardFeedPage').default,
[SCREENS.WORKSPACE.COMPANY_CARDS_SELECT_FEED]: () => require('../../../../pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage').default,
[SCREENS.WORKSPACE.COMPANY_CARDS_ADD_NEW]: () => require('../../../../pages/workspace/companyCards/addNew/AddNewCardPage').default,
+ [SCREENS.WORKSPACE.COMPANY_CARD_DETAILS]: () => require('../../../../pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage').default,
+ [SCREENS.WORKSPACE.COMPANY_CARD_NAME]: () => require('../../../../pages/workspace/companyCards/WorkspaceCompanyCardEditCardNamePage').default,
+ [SCREENS.WORKSPACE.COMPANY_CARD_EXPORT]: () => require('../../../../pages/workspace/companyCards/WorkspaceCompanyCardAccountSelectCardPage').default,
[SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW]: () => require('../../../../pages/workspace/expensifyCard/issueNew/IssueNewCardPage').default,
[SCREENS.WORKSPACE.EXPENSIFY_CARD_SETTINGS]: () => require('../../../../pages/workspace/expensifyCard/WorkspaceCardSettingsPage').default,
[SCREENS.WORKSPACE.EXPENSIFY_CARD_SETTINGS_ACCOUNT]: () => require('../../../../pages/workspace/expensifyCard/WorkspaceSettlementAccountPage').default,
@@ -554,6 +558,10 @@ const SearchAdvancedFiltersModalStackNavigator = createModalStackNavigator require('@pages/Search/SearchAdvancedFiltersPage/SearchFiltersInPage').default,
});
+const SearchSavedSearchModalStackNavigator = createModalStackNavigator({
+ [SCREENS.SEARCH.SAVED_SEARCH_RENAME_RHP]: () => require('../../../../pages/Search/SavedSearchRenamePage').default,
+});
+
const RestrictedActionModalStackNavigator = createModalStackNavigator({
[SCREENS.RESTRICTED_ACTION_ROOT]: () => require('../../../../pages/RestrictedAction/Workspace/WorkspaceRestrictedActionPage').default,
});
@@ -592,5 +600,6 @@ export {
SearchReportModalStackNavigator,
RestrictedActionModalStackNavigator,
SearchAdvancedFiltersModalStackNavigator,
+ SearchSavedSearchModalStackNavigator,
MissingPersonalDetailsModalStackNavigator,
};
diff --git a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx
index 70bbd9f0182d..481df0b1c9ad 100644
--- a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx
+++ b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx
@@ -183,6 +183,10 @@ function RightModalNavigator({navigation, route}: RightModalNavigatorProps) {
name={SCREENS.RIGHT_MODAL.SEARCH_ADVANCED_FILTERS}
component={ModalStackNavigators.SearchAdvancedFiltersModalStackNavigator}
/>
+
{displaySignIn && }
+ {isCustomSearchQuery && (
+ {
+ Navigation.goBack(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: SearchUtils.buildCannedSearchQuery()}));
+ }}
+ >
+ {translate('common.cancel')}
+
+ )}
{displaySearch && (
> =
SCREENS.SEARCH.ADVANCED_FILTERS_TO_RHP,
SCREENS.SEARCH.ADVANCED_FILTERS_IN_RHP,
SCREENS.SEARCH.ADVANCED_FILTERS_CARD_RHP,
+ SCREENS.SEARCH.SAVED_SEARCH_RENAME_RHP,
],
[SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: [
SCREENS.SETTINGS.SUBSCRIPTION.ADD_PAYMENT_CARD,
diff --git a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts
index 72d513844d81..a42427cf0f60 100755
--- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts
+++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts
@@ -180,6 +180,9 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = {
SCREENS.WORKSPACE.COMPANY_CARDS_SETTINGS,
SCREENS.WORKSPACE.COMPANY_CARDS_SETTINGS_FEED_NAME,
SCREENS.WORKSPACE.COMPANY_CARDS_ASSIGN_CARD,
+ SCREENS.WORKSPACE.COMPANY_CARD_DETAILS,
+ SCREENS.WORKSPACE.COMPANY_CARD_NAME,
+ SCREENS.WORKSPACE.COMPANY_CARD_EXPORT,
],
[SCREENS.WORKSPACE.EXPENSIFY_CARD]: [
SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW,
diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts
index 87656c5997db..2ca2db10a1a7 100644
--- a/src/libs/Navigation/linkingConfig/config.ts
+++ b/src/libs/Navigation/linkingConfig/config.ts
@@ -507,6 +507,15 @@ const config: LinkingOptions['config'] = {
[SCREENS.WORKSPACE.COMPANY_CARDS_SELECT_FEED]: {
path: ROUTES.WORKSPACE_COMPANY_CARDS_SELECT_FEED.route,
},
+ [SCREENS.WORKSPACE.COMPANY_CARD_DETAILS]: {
+ path: ROUTES.WORKSPACE_COMPANY_CARD_DETAILS.route,
+ },
+ [SCREENS.WORKSPACE.COMPANY_CARD_NAME]: {
+ path: ROUTES.WORKSPACE_COMPANY_CARD_NAME.route,
+ },
+ [SCREENS.WORKSPACE.COMPANY_CARD_EXPORT]: {
+ path: ROUTES.WORKSPACE_COMPANY_CARD_EXPORT.route,
+ },
[SCREENS.WORKSPACE.EXPENSIFY_CARD_LIMIT]: {
path: ROUTES.WORKSPACE_EXPENSIFY_CARD_LIMIT.route,
},
@@ -1136,6 +1145,11 @@ const config: LinkingOptions['config'] = {
[SCREENS.SEARCH.ADVANCED_FILTERS_IN_RHP]: ROUTES.SEARCH_ADVANCED_FILTERS_IN,
},
},
+ [SCREENS.RIGHT_MODAL.SEARCH_SAVED_SEARCH]: {
+ screens: {
+ [SCREENS.SEARCH.SAVED_SEARCH_RENAME_RHP]: ROUTES.SEARCH_SAVED_SEARCH_RENAME.route,
+ },
+ },
[SCREENS.RIGHT_MODAL.RESTRICTED_ACTION]: {
screens: {
[SCREENS.RESTRICTED_ACTION_ROOT]: ROUTES.RESTRICTED_ACTION.route,
diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts
index 3dc7c708f0b6..c6a4ce90c214 100644
--- a/src/libs/Navigation/types.ts
+++ b/src/libs/Navigation/types.ts
@@ -13,6 +13,7 @@ import type {
import type {TupleToUnion, ValueOf} from 'type-fest';
import type {SearchQueryString} from '@components/Search/types';
import type {IOURequestType} from '@libs/actions/IOU';
+import type {SaveSearchParams} from '@libs/API/parameters';
import type CONST from '@src/CONST';
import type {Country, IOUAction, IOUType} from '@src/CONST';
import type NAVIGATORS from '@src/NAVIGATORS';
@@ -706,6 +707,22 @@ type SettingsNavigatorParamList = {
[SCREENS.WORKSPACE.COMPANY_CARDS_SELECT_FEED]: {
policyID: string;
};
+ [SCREENS.WORKSPACE.COMPANY_CARD_DETAILS]: {
+ policyID: string;
+ bank: string;
+ cardID: string;
+ backTo?: Routes;
+ };
+ [SCREENS.WORKSPACE.COMPANY_CARD_NAME]: {
+ policyID: string;
+ cardID: string;
+ bank: string;
+ };
+ [SCREENS.WORKSPACE.COMPANY_CARD_EXPORT]: {
+ policyID: string;
+ cardID: string;
+ bank: string;
+ };
[SCREENS.WORKSPACE.EXPENSIFY_CARD_ISSUE_NEW]: {
policyID: string;
backTo?: Routes;
@@ -1165,6 +1182,7 @@ type RightModalNavigatorParamList = {
[SCREENS.RIGHT_MODAL.SEARCH_REPORT]: NavigatorScreenParams;
[SCREENS.RIGHT_MODAL.RESTRICTED_ACTION]: NavigatorScreenParams;
[SCREENS.RIGHT_MODAL.SEARCH_ADVANCED_FILTERS]: NavigatorScreenParams;
+ [SCREENS.RIGHT_MODAL.SEARCH_SAVED_SEARCH]: NavigatorScreenParams;
[SCREENS.RIGHT_MODAL.MISSING_PERSONAL_DETAILS]: NavigatorScreenParams;
};
@@ -1389,6 +1407,10 @@ type SearchAdvancedFiltersParamList = {
[SCREENS.SEARCH.ADVANCED_FILTERS_RHP]: Record;
};
+type SearchSavedSearchParamList = {
+ [SCREENS.SEARCH.SAVED_SEARCH_RENAME_RHP]: SaveSearchParams;
+};
+
type RestrictedActionParamList = {
[SCREENS.RESTRICTED_ACTION_ROOT]: {
policyID: string;
@@ -1471,6 +1493,7 @@ export type {
TransactionDuplicateNavigatorParamList,
SearchReportParamList,
SearchAdvancedFiltersParamList,
+ SearchSavedSearchParamList,
RestrictedActionParamList,
MissingPersonalDetailsParamList,
};
diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts
index f62935fc6721..b98188567178 100644
--- a/src/libs/PolicyUtils.ts
+++ b/src/libs/PolicyUtils.ts
@@ -201,6 +201,12 @@ const isPolicyAdmin = (policy: OnyxInputOrEntry, currentUserLogin?: stri
*/
const isPolicyUser = (policy: OnyxInputOrEntry, currentUserLogin?: string): boolean => getPolicyRole(policy, currentUserLogin) === CONST.POLICY.ROLE.USER;
+/**
+ * Checks if the current user is an auditor of the policy
+ */
+const isPolicyAuditor = (policy: OnyxInputOrEntry, currentUserLogin?: string): boolean =>
+ (policy?.role ?? (currentUserLogin && policy?.employeeList?.[currentUserLogin]?.role)) === CONST.POLICY.ROLE.AUDITOR;
+
/**
* Checks if the policy is a free group policy.
*/
@@ -1035,6 +1041,7 @@ export {
isPendingDeletePolicy,
isPolicyAdmin,
isPolicyUser,
+ isPolicyAuditor,
isPolicyEmployee,
isPolicyFeatureEnabled,
isPolicyOwner,
diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts
index 7001dedea2f1..f35ab637aea1 100644
--- a/src/libs/ReportUtils.ts
+++ b/src/libs/ReportUtils.ts
@@ -1415,12 +1415,28 @@ function isJoinRequestInAdminRoom(report: OnyxEntry): boolean {
return ReportActionsUtils.isActionableJoinRequestPending(report.reportID);
}
+/**
+ * Checks if the user has auditor permission in the provided report
+ */
+function isAuditor(report: OnyxEntry): boolean {
+ if (report?.policyID) {
+ const policy = getPolicy(report.policyID);
+ return PolicyUtils.isPolicyAuditor(policy);
+ }
+
+ if (Array.isArray(report?.permissions) && report?.permissions.length > 0) {
+ return report?.permissions?.includes(CONST.REPORT.PERMISSIONS.AUDITOR);
+ }
+
+ return false;
+}
+
/**
* Checks if the user can write in the provided report
*/
function canWriteInReport(report: OnyxEntry): boolean {
- if (Array.isArray(report?.permissions) && report?.permissions.length > 0) {
- return report?.permissions?.includes(CONST.REPORT.PERMISSIONS.WRITE) || (report?.permissions?.includes(CONST.REPORT.PERMISSIONS.AUDITOR) && isExpenseReport(report));
+ if (Array.isArray(report?.permissions) && report?.permissions.length > 0 && !report?.permissions?.includes(CONST.REPORT.PERMISSIONS.AUDITOR)) {
+ return report?.permissions?.includes(CONST.REPORT.PERMISSIONS.WRITE);
}
return true;
@@ -1430,6 +1446,10 @@ function canWriteInReport(report: OnyxEntry): boolean {
* Checks if the current user is allowed to comment on the given report.
*/
function isAllowedToComment(report: OnyxEntry): boolean {
+ if (isAuditor(report)) {
+ return true;
+ }
+
if (!canWriteInReport(report)) {
return false;
}
@@ -3431,6 +3451,7 @@ function getModifiedExpenseOriginalMessage(
transactionChanges: TransactionChanges,
isFromExpenseReport: boolean,
policy: OnyxInputOrEntry,
+ updatedTransaction?: OnyxInputOrEntry,
): OriginalMessageModifiedExpense {
const originalMessage: OriginalMessageModifiedExpense = {};
// Remark: Comment field is the only one which has new/old prefixes for the keys (newComment/ oldComment),
@@ -3490,12 +3511,12 @@ function getModifiedExpenseOriginalMessage(
originalMessage.billable = transactionChanges?.billable ? Localize.translateLocal('common.billable').toLowerCase() : Localize.translateLocal('common.nonBillable').toLowerCase();
}
- if ('customUnitRateID' in transactionChanges) {
+ if ('customUnitRateID' in transactionChanges && updatedTransaction?.comment?.customUnit?.customUnitRateID) {
originalMessage.oldAmount = TransactionUtils.getAmount(oldTransaction, isFromExpenseReport);
originalMessage.oldCurrency = TransactionUtils.getCurrency(oldTransaction);
originalMessage.oldMerchant = TransactionUtils.getMerchant(oldTransaction);
- const modifiedDistanceFields = TransactionUtils.calculateAmountForUpdatedWaypointOrRate(oldTransaction, transactionChanges, policy, isFromExpenseReport);
+ const modifiedDistanceFields = TransactionUtils.calculateAmountForUpdatedWaypointOrRate(updatedTransaction, transactionChanges, policy, isFromExpenseReport);
// For the originalMessage, we should use the non-negative amount, similar to what TransactionUtils.getAmount does for oldAmount
originalMessage.amount = Math.abs(modifiedDistanceFields.modifiedAmount);
@@ -4867,8 +4888,9 @@ function buildOptimisticModifiedExpenseReportAction(
transactionChanges: TransactionChanges,
isFromExpenseReport: boolean,
policy: OnyxInputOrEntry,
+ updatedTransaction?: OnyxInputOrEntry,
): OptimisticModifiedExpenseReportAction {
- const originalMessage = getModifiedExpenseOriginalMessage(oldTransaction, transactionChanges, isFromExpenseReport, policy);
+ const originalMessage = getModifiedExpenseOriginalMessage(oldTransaction, transactionChanges, isFromExpenseReport, policy, updatedTransaction);
return {
actionName: CONST.REPORT.ACTIONS.TYPE.MODIFIED_EXPENSE,
actorAccountID: currentUserAccountID,
@@ -7075,7 +7097,8 @@ function canEditReportDescription(report: OnyxEntry, policy: OnyxEntry