diff --git a/Gemfile.lock b/Gemfile.lock
index b386f59b5c11..10acc25586ad 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -15,7 +15,7 @@ GEM
algoliasearch (1.27.5)
httpclient (~> 2.8, >= 2.8.3)
json (>= 1.5.1)
- apktools (0.7.4)
+ apktools (0.7.5)
rubyzip (~> 2.0)
artifactory (3.0.17)
atomos (0.1.3)
diff --git a/android/app/build.gradle b/android/app/build.gradle
index ff57b4515407..a46fe6672fe4 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 1009002515
- versionName "9.0.25-15"
+ versionCode 1009002605
+ versionName "9.0.26-5"
// Supported language variants must be declared here to avoid from being removed during the compilation.
// This also helps us to not include unnecessary language variants in the APK.
resConfigs "en", "es"
diff --git a/assets/images/companyCards/emptystate__card-pos.svg b/assets/images/companyCards/emptystate__card-pos.svg
new file mode 100644
index 000000000000..6a6fbae74a04
--- /dev/null
+++ b/assets/images/companyCards/emptystate__card-pos.svg
@@ -0,0 +1,643 @@
+
+
+
diff --git a/assets/images/simple-illustrations/simple-illustration__magnifyingglass-money.svg b/assets/images/simple-illustrations/simple-illustration__magnifyingglass-money.svg
new file mode 100644
index 000000000000..e7f64f69305a
--- /dev/null
+++ b/assets/images/simple-illustrations/simple-illustration__magnifyingglass-money.svg
@@ -0,0 +1,49 @@
+
+
+
diff --git a/config/webpack/webpack.common.ts b/config/webpack/webpack.common.ts
index 33fd9131eca0..b19fedca3871 100644
--- a/config/webpack/webpack.common.ts
+++ b/config/webpack/webpack.common.ts
@@ -123,7 +123,7 @@ const getCommonConfiguration = ({file = '.env', platform = 'web'}: Environment):
{from: 'node_modules/pdfjs-dist/cmaps/', to: 'cmaps/'},
],
}),
- new EnvironmentPlugin({JEST_WORKER_ID: null}),
+ new EnvironmentPlugin({JEST_WORKER_ID: ''}),
new IgnorePlugin({
resourceRegExp: /^\.\/locale$/,
contextRegExp: /moment$/,
diff --git a/docs/articles/expensify-classic/connections/quickbooks-desktop/Connect-To-QuickBooks-Desktop.md b/docs/articles/expensify-classic/connections/quickbooks-desktop/Connect-To-QuickBooks-Desktop.md
index 50e3e0971869..1fb1b09328b9 100644
--- a/docs/articles/expensify-classic/connections/quickbooks-desktop/Connect-To-QuickBooks-Desktop.md
+++ b/docs/articles/expensify-classic/connections/quickbooks-desktop/Connect-To-QuickBooks-Desktop.md
@@ -18,17 +18,17 @@ To connect QuickBooks Desktop to Expensify, you must log into QuickBooks Desktop
4. Click **Connect to QuickBooks Desktop**.
5. Click Copy to copy the link, then paste the link into the computer where QuickBooks Desktop is running.
-![QuickBooks Desktop Setup pop-up link, containing the URL to paste](https://help.expensify.com/assets/images/QBO_desktop_01.png){:width="100%"}
+ ![QuickBooks Desktop Setup pop-up link, containing the URL to paste](https://help.expensify.com/assets/images/QBO_desktop_01.png){:width="100%"}
6. Select the version of QuickBooks Desktop that you currently have.
-![The Web Connnector Pop-up to allow you to select the type of QuickBooks Desktop you have](https://help.expensify.com/assets/images/QBO_desktop_02.png){:width="100%"}
+ ![The Web Connnector Pop-up to allow you to select the type of QuickBooks Desktop you have](https://help.expensify.com/assets/images/QBO_desktop_02.png){:width="100%"}
7. Download the Web Connector and go through the guided installation process.
8. Open the Web Connector.
9. Click on **Add an Application**.
-![The Web Connnector Pop-up where you will need to click on Add an Application](https://help.expensify.com/assets/images/QBO_desktop_03.png){:width="100%"}
+ ![The Web Connnector Pop-up where you will need to click on Add an Application](https://help.expensify.com/assets/images/QBO_desktop_03.png){:width="100%"}
{% include info.html %}
For this step, it is key to ensure that the correct company file is open in QuickBooks Desktop and that it is the only one open.
@@ -36,23 +36,23 @@ For this step, it is key to ensure that the correct company file is open in Quic
10. In QuickBooks Desktop, select **"Yes, always allow access, even when QuickBooks is not running"** and click **Continue**.
-![The QuickBooks Desktop pop-up, where you will need to select "Yes, always allow access, even when QuickBooks is not running"](https://help.expensify.com/assets/images/QBO_desktop_04.png){:width="100%"}
+ ![The QuickBooks Desktop pop-up, where you will need to select "Yes, always allow access, even when QuickBooks is not running"](https://help.expensify.com/assets/images/QBO_desktop_04.png){:width="100%"}
11. Click **OK**, then click **Yes**.
-![The QuickBooks Desktop pop-up, where you will need to click "Ok" then select "Yes"](https://help.expensify.com/assets/images/QBO_desktop_05.png){:width="100%"}
+ ![The QuickBooks Desktop pop-up, where you will need to click "Ok" then select "Yes"](https://help.expensify.com/assets/images/QBO_desktop_05.png){:width="100%"}
12. Click **Copy** to copy the password.
-![The Web Connector pop-up, where you will need to click "Copy"](https://help.expensify.com/assets/images/QBO_desktop_06.png){:width="100%"}
+ ![The Web Connector pop-up, where you will need to click "Copy"](https://help.expensify.com/assets/images/QBO_desktop_06.png){:width="100%"}
13. Paste the password into the Password field of the Web Connector and press **Enter**.
-![The Web Connector pop-up, where you will need to paste the password into the password field](https://help.expensify.com/assets/images/QBO_desktop_08.png){:width="100%"}
+ ![The Web Connector pop-up, where you will need to paste the password into the password field](https://help.expensify.com/assets/images/QBO_desktop_08.png){:width="100%"}
14. Click **Yes** to save the password. The new connection now appears in the Web Connector.
-![The Web Connector pop-up, where you will need to click "Yes"](https://help.expensify.com/assets/images/QBO_desktop_07.png){:width="100%"}
+ ![The Web Connector pop-up, where you will need to click "Yes"](https://help.expensify.com/assets/images/QBO_desktop_07.png){:width="100%"}
# FAQ
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index 5085b33f9dc9..0935d32816a5 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -19,7 +19,7 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 9.0.25
+ 9.0.26
CFBundleSignature
????
CFBundleURLTypes
@@ -40,7 +40,7 @@
CFBundleVersion
- 9.0.25.15
+ 9.0.26.5
FullStory
OrgId
diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist
index 30ced177b94d..5399ef12ef27 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -15,10 +15,10 @@
CFBundlePackageType
BNDL
CFBundleShortVersionString
- 9.0.25
+ 9.0.26
CFBundleSignature
????
CFBundleVersion
- 9.0.25.15
+ 9.0.26.5
diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist
index 89729b998bbe..e28dfcd9c056 100644
--- a/ios/NotificationServiceExtension/Info.plist
+++ b/ios/NotificationServiceExtension/Info.plist
@@ -11,9 +11,9 @@
CFBundleName
$(PRODUCT_NAME)
CFBundleShortVersionString
- 9.0.25
+ 9.0.26
CFBundleVersion
- 9.0.25.15
+ 9.0.26.5
NSExtension
NSExtensionPointIdentifier
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index b3fb491d614a..a431bf59d438 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -1840,7 +1840,7 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- - react-native-quick-sqlite (8.0.6):
+ - react-native-quick-sqlite (8.1.0):
- DoubleConversion
- glog
- hermes-engine
@@ -3147,7 +3147,7 @@ SPEC CHECKSUMS:
react-native-pdf: dd6ae39a93607a80919bef9f3499e840c693989d
react-native-performance: 3c608307be10964f8a97d3af462f37125b6d8fa5
react-native-plaid-link-sdk: f91a22b45b7c3d4cd6c47273200dc57df35068b0
- react-native-quick-sqlite: cc2939134fbd404ac7d51d3dc8d69219eff242a8
+ react-native-quick-sqlite: 7c793c9f5834e756b336257a8d8b8239b7ceb451
react-native-release-profiler: 131ec5e4145d900b2be2a8d6641e2ce0dd784259
react-native-safe-area-context: 38fdd9b3c5561de7cabae64bd0cd2ce05d2768a1
react-native-view-shot: 6b7ed61d77d88580fed10954d45fad0eb2d47688
diff --git a/jest/setup.ts b/jest/setup.ts
index 19e20a6d0395..51385ad19e45 100644
--- a/jest/setup.ts
+++ b/jest/setup.ts
@@ -1,3 +1,4 @@
+/* eslint-disable max-classes-per-file */
import '@shopify/flash-list/jestSetup';
import 'react-native-gesture-handler/jestSetup';
import type * as RNKeyboardController from 'react-native-keyboard-controller';
@@ -78,3 +79,22 @@ jest.mock('@src/libs/actions/Timing', () => ({
start: jest.fn(),
end: jest.fn(),
}));
+
+// This makes FlatList render synchronously for easier testing.
+jest.mock(
+ '@react-native/virtualized-lists/Interaction/Batchinator',
+ () =>
+ class SyncBachinator {
+ #callback: () => void;
+
+ constructor(callback: () => void) {
+ this.#callback = callback;
+ }
+
+ schedule() {
+ this.#callback();
+ }
+
+ dispose() {}
+ },
+);
diff --git a/package-lock.json b/package-lock.json
index aff60a111545..2dab9a1fd21d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "new.expensify",
- "version": "9.0.25-15",
+ "version": "9.0.26-5",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "new.expensify",
- "version": "9.0.25-15",
+ "version": "9.0.26-5",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@@ -56,7 +56,7 @@
"date-fns-tz": "^2.0.0",
"dom-serializer": "^0.2.2",
"domhandler": "^4.3.0",
- "expensify-common": "2.0.77",
+ "expensify-common": "2.0.78",
"expo": "51.0.17",
"expo-av": "14.0.6",
"expo-image": "1.12.12",
@@ -111,7 +111,7 @@
"react-native-picker-select": "git+https://github.com/Expensify/react-native-picker-select.git#da50d2c5c54e268499047f9cc98b8df4196c1ddf",
"react-native-plaid-link-sdk": "11.11.0",
"react-native-qrcode-svg": "git+https://github.com/Expensify/react-native-qrcode-svg-old",
- "react-native-quick-sqlite": "git+https://github.com/margelo/react-native-quick-sqlite#abc91857d4b3efb2020ec43abd2a508563b64316",
+ "react-native-quick-sqlite": "git+https://github.com/margelo/react-native-quick-sqlite#99f34ebefa91698945f3ed26622e002bd79489e0",
"react-native-reanimated": "3.13.0",
"react-native-release-profiler": "^0.2.1",
"react-native-render-html": "6.3.1",
@@ -262,7 +262,7 @@
"type-fest": "4.20.0",
"typescript": "^5.4.5",
"wait-port": "^0.2.9",
- "webpack": "^5.76.0",
+ "webpack": "^5.94.0",
"webpack-bundle-analyzer": "^4.5.0",
"webpack-cli": "^5.0.4",
"webpack-dev-server": "^5.0.4",
@@ -17563,24 +17563,9 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@types/eslint": {
- "version": "8.4.6",
- "license": "MIT",
- "dependencies": {
- "@types/estree": "*",
- "@types/json-schema": "*"
- }
- },
- "node_modules/@types/eslint-scope": {
- "version": "3.7.4",
- "license": "MIT",
- "dependencies": {
- "@types/eslint": "*",
- "@types/estree": "*"
- }
- },
"node_modules/@types/estree": {
"version": "0.0.51",
+ "dev": true,
"license": "MIT"
},
"node_modules/@types/express": {
@@ -18688,12 +18673,38 @@
"webpack": "^5.20.0 || ^4.1.0"
}
},
- "node_modules/@webassemblyjs/helper-buffer": {
+ "node_modules/@webassemblyjs/ast": {
+ "version": "1.12.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz",
+ "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==",
+ "license": "MIT",
+ "dependencies": {
+ "@webassemblyjs/helper-numbers": "1.11.6",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.6"
+ }
+ },
+ "node_modules/@webassemblyjs/floating-point-hex-parser": {
"version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz",
+ "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==",
+ "license": "MIT"
+ },
+ "node_modules/@webassemblyjs/helper-api-error": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz",
+ "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==",
+ "license": "MIT"
+ },
+ "node_modules/@webassemblyjs/helper-buffer": {
+ "version": "1.12.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz",
+ "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==",
"license": "MIT"
},
"node_modules/@webassemblyjs/helper-numbers": {
"version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz",
+ "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==",
"license": "MIT",
"dependencies": {
"@webassemblyjs/floating-point-hex-parser": "1.11.6",
@@ -18701,38 +18712,28 @@
"@xtuc/long": "4.2.2"
}
},
- "node_modules/@webassemblyjs/helper-numbers/node_modules/@webassemblyjs/floating-point-hex-parser": {
- "version": "1.11.6",
- "license": "MIT"
- },
- "node_modules/@webassemblyjs/helper-numbers/node_modules/@webassemblyjs/helper-api-error": {
+ "node_modules/@webassemblyjs/helper-wasm-bytecode": {
"version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz",
+ "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==",
"license": "MIT"
},
"node_modules/@webassemblyjs/helper-wasm-section": {
- "version": "1.11.6",
+ "version": "1.12.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz",
+ "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==",
"license": "MIT",
"dependencies": {
- "@webassemblyjs/ast": "1.11.6",
- "@webassemblyjs/helper-buffer": "1.11.6",
+ "@webassemblyjs/ast": "1.12.1",
+ "@webassemblyjs/helper-buffer": "1.12.1",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
- "@webassemblyjs/wasm-gen": "1.11.6"
+ "@webassemblyjs/wasm-gen": "1.12.1"
}
},
- "node_modules/@webassemblyjs/helper-wasm-section/node_modules/@webassemblyjs/ast": {
- "version": "1.11.6",
- "license": "MIT",
- "dependencies": {
- "@webassemblyjs/helper-numbers": "1.11.6",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.6"
- }
- },
- "node_modules/@webassemblyjs/helper-wasm-section/node_modules/@webassemblyjs/helper-wasm-bytecode": {
- "version": "1.11.6",
- "license": "MIT"
- },
"node_modules/@webassemblyjs/ieee754": {
"version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz",
+ "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==",
"license": "MIT",
"dependencies": {
"@xtuc/ieee754": "^1.2.0"
@@ -18740,6 +18741,8 @@
},
"node_modules/@webassemblyjs/leb128": {
"version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz",
+ "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==",
"license": "Apache-2.0",
"dependencies": {
"@xtuc/long": "4.2.2"
@@ -18747,92 +18750,58 @@
},
"node_modules/@webassemblyjs/utf8": {
"version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz",
+ "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==",
"license": "MIT"
},
"node_modules/@webassemblyjs/wasm-edit": {
- "version": "1.11.6",
+ "version": "1.12.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz",
+ "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==",
"license": "MIT",
"dependencies": {
- "@webassemblyjs/ast": "1.11.6",
- "@webassemblyjs/helper-buffer": "1.11.6",
+ "@webassemblyjs/ast": "1.12.1",
+ "@webassemblyjs/helper-buffer": "1.12.1",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
- "@webassemblyjs/helper-wasm-section": "1.11.6",
- "@webassemblyjs/wasm-gen": "1.11.6",
- "@webassemblyjs/wasm-opt": "1.11.6",
- "@webassemblyjs/wasm-parser": "1.11.6",
- "@webassemblyjs/wast-printer": "1.11.6"
- }
- },
- "node_modules/@webassemblyjs/wasm-edit/node_modules/@webassemblyjs/ast": {
- "version": "1.11.6",
- "license": "MIT",
- "dependencies": {
- "@webassemblyjs/helper-numbers": "1.11.6",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.6"
- }
- },
- "node_modules/@webassemblyjs/wasm-edit/node_modules/@webassemblyjs/helper-wasm-bytecode": {
- "version": "1.11.6",
- "license": "MIT"
- },
- "node_modules/@webassemblyjs/wasm-edit/node_modules/@webassemblyjs/wast-printer": {
- "version": "1.11.6",
- "license": "MIT",
- "dependencies": {
- "@webassemblyjs/ast": "1.11.6",
- "@xtuc/long": "4.2.2"
+ "@webassemblyjs/helper-wasm-section": "1.12.1",
+ "@webassemblyjs/wasm-gen": "1.12.1",
+ "@webassemblyjs/wasm-opt": "1.12.1",
+ "@webassemblyjs/wasm-parser": "1.12.1",
+ "@webassemblyjs/wast-printer": "1.12.1"
}
},
"node_modules/@webassemblyjs/wasm-gen": {
- "version": "1.11.6",
+ "version": "1.12.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz",
+ "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==",
"license": "MIT",
"dependencies": {
- "@webassemblyjs/ast": "1.11.6",
+ "@webassemblyjs/ast": "1.12.1",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/ieee754": "1.11.6",
"@webassemblyjs/leb128": "1.11.6",
"@webassemblyjs/utf8": "1.11.6"
}
},
- "node_modules/@webassemblyjs/wasm-gen/node_modules/@webassemblyjs/ast": {
- "version": "1.11.6",
- "license": "MIT",
- "dependencies": {
- "@webassemblyjs/helper-numbers": "1.11.6",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.6"
- }
- },
- "node_modules/@webassemblyjs/wasm-gen/node_modules/@webassemblyjs/helper-wasm-bytecode": {
- "version": "1.11.6",
- "license": "MIT"
- },
"node_modules/@webassemblyjs/wasm-opt": {
- "version": "1.11.6",
+ "version": "1.12.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz",
+ "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==",
"license": "MIT",
"dependencies": {
- "@webassemblyjs/ast": "1.11.6",
- "@webassemblyjs/helper-buffer": "1.11.6",
- "@webassemblyjs/wasm-gen": "1.11.6",
- "@webassemblyjs/wasm-parser": "1.11.6"
+ "@webassemblyjs/ast": "1.12.1",
+ "@webassemblyjs/helper-buffer": "1.12.1",
+ "@webassemblyjs/wasm-gen": "1.12.1",
+ "@webassemblyjs/wasm-parser": "1.12.1"
}
},
- "node_modules/@webassemblyjs/wasm-opt/node_modules/@webassemblyjs/ast": {
- "version": "1.11.6",
- "license": "MIT",
- "dependencies": {
- "@webassemblyjs/helper-numbers": "1.11.6",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.6"
- }
- },
- "node_modules/@webassemblyjs/wasm-opt/node_modules/@webassemblyjs/helper-wasm-bytecode": {
- "version": "1.11.6",
- "license": "MIT"
- },
"node_modules/@webassemblyjs/wasm-parser": {
- "version": "1.11.6",
+ "version": "1.12.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz",
+ "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==",
"license": "MIT",
"dependencies": {
- "@webassemblyjs/ast": "1.11.6",
+ "@webassemblyjs/ast": "1.12.1",
"@webassemblyjs/helper-api-error": "1.11.6",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/ieee754": "1.11.6",
@@ -18840,22 +18809,16 @@
"@webassemblyjs/utf8": "1.11.6"
}
},
- "node_modules/@webassemblyjs/wasm-parser/node_modules/@webassemblyjs/ast": {
- "version": "1.11.6",
+ "node_modules/@webassemblyjs/wast-printer": {
+ "version": "1.12.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz",
+ "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==",
"license": "MIT",
"dependencies": {
- "@webassemblyjs/helper-numbers": "1.11.6",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.6"
+ "@webassemblyjs/ast": "1.12.1",
+ "@xtuc/long": "4.2.2"
}
},
- "node_modules/@webassemblyjs/wasm-parser/node_modules/@webassemblyjs/helper-api-error": {
- "version": "1.11.6",
- "license": "MIT"
- },
- "node_modules/@webassemblyjs/wasm-parser/node_modules/@webassemblyjs/helper-wasm-bytecode": {
- "version": "1.11.6",
- "license": "MIT"
- },
"node_modules/@webpack-cli/configtest": {
"version": "2.1.1",
"dev": true,
@@ -18917,10 +18880,14 @@
},
"node_modules/@xtuc/ieee754": {
"version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+ "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
"license": "BSD-3-Clause"
},
"node_modules/@xtuc/long": {
"version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
+ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
"license": "Apache-2.0"
},
"node_modules/@yarnpkg/esbuild-plugin-pnp": {
@@ -24644,7 +24611,9 @@
}
},
"node_modules/enhanced-resolve": {
- "version": "5.15.0",
+ "version": "5.17.1",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
+ "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==",
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.4",
@@ -26323,9 +26292,9 @@
}
},
"node_modules/expensify-common": {
- "version": "2.0.77",
- "resolved": "https://registry.npmjs.org/expensify-common/-/expensify-common-2.0.77.tgz",
- "integrity": "sha512-QMlIKYFIXQQ4bu1dgrUl0PdP+Nx7WgHlYq8nY8p5VhOIAzEt0LABC1xZDHxgDLyEW7skAKu+dDcadATpP1g1hA==",
+ "version": "2.0.78",
+ "resolved": "https://registry.npmjs.org/expensify-common/-/expensify-common-2.0.78.tgz",
+ "integrity": "sha512-Q4SLK+C8NfUq8cfu66O6Ap0Ak6xbQECIBpLVgqkkyTVL12sec6mQWs7X1gV9wwTE1f6cIIZ9oiBZ1lXtbAWYKw==",
"dependencies": {
"awesome-phonenumber": "^5.4.0",
"classnames": "2.5.0",
@@ -27988,7 +27957,9 @@
}
},
"node_modules/graceful-fs": {
- "version": "4.2.10",
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"license": "ISC"
},
"node_modules/graphemer": {
@@ -38312,9 +38283,9 @@
}
},
"node_modules/react-native-quick-sqlite": {
- "version": "8.0.6",
- "resolved": "git+ssh://git@github.com/margelo/react-native-quick-sqlite.git#abc91857d4b3efb2020ec43abd2a508563b64316",
- "integrity": "sha512-/tBM6Oh8ye3d+hIhURRA9hlBausKqQmscgyt4ZcKluPjBti0bgLb0cyL8Gyd0cbCakaVgym25VyGjaeicV/01A==",
+ "version": "8.1.0",
+ "resolved": "git+ssh://git@github.com/margelo/react-native-quick-sqlite.git#99f34ebefa91698945f3ed26622e002bd79489e0",
+ "integrity": "sha512-7uuHmOEnc6SOAVoAdvkQhvaYhUZMORM75qo+v6PZoH6Qk21j5CmrcxJE3gNh0FhMfxK73hQ3ZtugC/NI2jVhrw==",
"peerDependencies": {
"react": "*",
"react-native": "*"
@@ -44444,7 +44415,9 @@
}
},
"node_modules/watchpack": {
- "version": "2.4.0",
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
+ "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==",
"license": "MIT",
"dependencies": {
"glob-to-regexp": "^0.4.1",
@@ -44723,32 +44696,33 @@
}
},
"node_modules/webpack": {
- "version": "5.88.2",
+ "version": "5.94.0",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz",
+ "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==",
"license": "MIT",
"dependencies": {
- "@types/eslint-scope": "^3.7.3",
- "@types/estree": "^1.0.0",
- "@webassemblyjs/ast": "^1.11.5",
- "@webassemblyjs/wasm-edit": "^1.11.5",
- "@webassemblyjs/wasm-parser": "^1.11.5",
+ "@types/estree": "^1.0.5",
+ "@webassemblyjs/ast": "^1.12.1",
+ "@webassemblyjs/wasm-edit": "^1.12.1",
+ "@webassemblyjs/wasm-parser": "^1.12.1",
"acorn": "^8.7.1",
- "acorn-import-assertions": "^1.9.0",
- "browserslist": "^4.14.5",
+ "acorn-import-attributes": "^1.9.5",
+ "browserslist": "^4.21.10",
"chrome-trace-event": "^1.0.2",
- "enhanced-resolve": "^5.15.0",
+ "enhanced-resolve": "^5.17.1",
"es-module-lexer": "^1.2.1",
"eslint-scope": "5.1.1",
"events": "^3.2.0",
"glob-to-regexp": "^0.4.1",
- "graceful-fs": "^4.2.9",
+ "graceful-fs": "^4.2.11",
"json-parse-even-better-errors": "^2.3.1",
"loader-runner": "^4.2.0",
"mime-types": "^2.1.27",
"neo-async": "^2.6.2",
"schema-utils": "^3.2.0",
"tapable": "^2.1.1",
- "terser-webpack-plugin": "^5.3.7",
- "watchpack": "^2.4.0",
+ "terser-webpack-plugin": "^5.3.10",
+ "watchpack": "^2.4.1",
"webpack-sources": "^3.2.3"
},
"bin": {
@@ -45310,23 +45284,15 @@
"license": "MIT"
},
"node_modules/webpack/node_modules/@types/estree": {
- "version": "1.0.1",
- "license": "MIT"
- },
- "node_modules/webpack/node_modules/@webassemblyjs/ast": {
- "version": "1.11.6",
- "license": "MIT",
- "dependencies": {
- "@webassemblyjs/helper-numbers": "1.11.6",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.6"
- }
- },
- "node_modules/webpack/node_modules/@webassemblyjs/helper-wasm-bytecode": {
- "version": "1.11.6",
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
+ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
"license": "MIT"
},
"node_modules/webpack/node_modules/acorn": {
- "version": "8.10.0",
+ "version": "8.12.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
+ "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
@@ -45335,8 +45301,10 @@
"node": ">=0.4.0"
}
},
- "node_modules/webpack/node_modules/acorn-import-assertions": {
- "version": "1.9.0",
+ "node_modules/webpack/node_modules/acorn-import-attributes": {
+ "version": "1.9.5",
+ "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz",
+ "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==",
"license": "MIT",
"peerDependencies": {
"acorn": "^8"
diff --git a/package.json b/package.json
index 51384b6e8bf7..0246a78af204 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "9.0.25-15",
+ "version": "9.0.26-5",
"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.",
@@ -113,7 +113,7 @@
"date-fns-tz": "^2.0.0",
"dom-serializer": "^0.2.2",
"domhandler": "^4.3.0",
- "expensify-common": "2.0.77",
+ "expensify-common": "2.0.78",
"expo": "51.0.17",
"expo-av": "14.0.6",
"expo-image": "1.12.12",
@@ -168,7 +168,7 @@
"react-native-picker-select": "git+https://github.com/Expensify/react-native-picker-select.git#da50d2c5c54e268499047f9cc98b8df4196c1ddf",
"react-native-plaid-link-sdk": "11.11.0",
"react-native-qrcode-svg": "git+https://github.com/Expensify/react-native-qrcode-svg-old",
- "react-native-quick-sqlite": "git+https://github.com/margelo/react-native-quick-sqlite#abc91857d4b3efb2020ec43abd2a508563b64316",
+ "react-native-quick-sqlite": "git+https://github.com/margelo/react-native-quick-sqlite#99f34ebefa91698945f3ed26622e002bd79489e0",
"react-native-reanimated": "3.13.0",
"react-native-release-profiler": "^0.2.1",
"react-native-render-html": "6.3.1",
@@ -319,7 +319,7 @@
"type-fest": "4.20.0",
"typescript": "^5.4.5",
"wait-port": "^0.2.9",
- "webpack": "^5.76.0",
+ "webpack": "^5.94.0",
"webpack-bundle-analyzer": "^4.5.0",
"webpack-cli": "^5.0.4",
"webpack-dev-server": "^5.0.4",
diff --git a/patches/react-native-quick-sqlite+8.0.6.patch b/patches/react-native-quick-sqlite+8.0.6.patch
deleted file mode 100644
index 5d4dcfad8e35..000000000000
--- a/patches/react-native-quick-sqlite+8.0.6.patch
+++ /dev/null
@@ -1,13 +0,0 @@
-diff --git a/node_modules/react-native-quick-sqlite/android/build.gradle b/node_modules/react-native-quick-sqlite/android/build.gradle
-index afcda02..3faaf76 100644
---- a/node_modules/react-native-quick-sqlite/android/build.gradle
-+++ b/node_modules/react-native-quick-sqlite/android/build.gradle
-@@ -63,7 +63,7 @@ android {
- }
-
- defaultConfig {
-- minSdkVersion 21
-+ minSdkVersion 23
- targetSdkVersion safeExtGet('targetSdkVersion', 28)
- versionCode 1
- versionName "1.0"
diff --git a/patches/react-native-tab-view+3.5.2.patch b/patches/react-native-tab-view+3.5.2+001+initial.patch
similarity index 100%
rename from patches/react-native-tab-view+3.5.2.patch
rename to patches/react-native-tab-view+3.5.2+001+initial.patch
diff --git a/patches/react-native-tab-view+3.5.2+002+fixZeroWidthHeightPositionFlicker.patch b/patches/react-native-tab-view+3.5.2+002+fixZeroWidthHeightPositionFlicker.patch
new file mode 100644
index 000000000000..1f42ef2885b0
--- /dev/null
+++ b/patches/react-native-tab-view+3.5.2+002+fixZeroWidthHeightPositionFlicker.patch
@@ -0,0 +1,29 @@
+diff --git a/node_modules/react-native-tab-view/lib/module/TabView.js b/node_modules/react-native-tab-view/lib/module/TabView.js
+index ba28170..4c607a2 100644
+--- a/node_modules/react-native-tab-view/lib/module/TabView.js
++++ b/node_modules/react-native-tab-view/lib/module/TabView.js
+@@ -40,6 +40,9 @@ export function TabView(_ref) {
+ height,
+ width
+ } = e.nativeEvent.layout;
++ if(!!width || !!height) {
++ return;
++ }
+ setLayout(prevLayout => {
+ if (prevLayout.width === width && prevLayout.height === height) {
+ return prevLayout;
+diff --git a/node_modules/react-native-tab-view/src/TabView.tsx b/node_modules/react-native-tab-view/src/TabView.tsx
+index bb1f531..d70f9ba 100644
+--- a/node_modules/react-native-tab-view/src/TabView.tsx
++++ b/node_modules/react-native-tab-view/src/TabView.tsx
+@@ -70,6 +70,10 @@ export function TabView({
+ const handleLayout = (e: LayoutChangeEvent) => {
+ const { height, width } = e.nativeEvent.layout;
+
++ if(!!width || !!height) {
++ return;
++ }
++
+ setLayout((prevLayout) => {
+ if (prevLayout.width === width && prevLayout.height === height) {
+ return prevLayout;
diff --git a/patches/react-pdf+7.7.3.patch b/patches/react-pdf+7.7.3.patch
index 5b1b3ebb6f6e..0f9a2f47d3c7 100644
--- a/patches/react-pdf+7.7.3.patch
+++ b/patches/react-pdf+7.7.3.patch
@@ -1,3 +1,15 @@
+diff --git a/node_modules/react-pdf/dist/cjs/Document.js b/node_modules/react-pdf/dist/cjs/Document.js
+index 9bb0398..032d898 100644
+--- a/node_modules/react-pdf/dist/cjs/Document.js
++++ b/node_modules/react-pdf/dist/cjs/Document.js
+@@ -289,6 +289,7 @@ const Document = (0, react_1.forwardRef)(function Document(_a, ref) {
+ pdfDispatch({ type: 'REJECT', error });
+ });
+ return () => {
++ loadingTask._worker.destroy();
+ loadingTask.destroy();
+ };
+ }
diff --git a/node_modules/react-pdf/dist/esm/Document.js b/node_modules/react-pdf/dist/esm/Document.js
index b1c5a81..569769e 100644
--- a/node_modules/react-pdf/dist/esm/Document.js
diff --git a/src/CONST.ts b/src/CONST.ts
index 9e8416d0f21e..a494f01073c2 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -2502,6 +2502,10 @@ const CONST = {
get RESTRICTED_ACCOUNT_IDS() {
return [this.ACCOUNT_ID.NOTIFICATIONS];
},
+ // Account IDs that can't be added as a group member
+ get NON_ADDABLE_ACCOUNT_IDS() {
+ return [this.ACCOUNT_ID.NOTIFICATIONS, this.ACCOUNT_ID.CHRONOS];
+ },
// Auth limit is 60k for the column but we store edits and other metadata along the html so let's use a lower limit to accommodate for it.
MAX_COMMENT_LENGTH: 10000,
diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts
index 6a1fc8a629ed..e3e47be96e83 100755
--- a/src/ONYXKEYS.ts
+++ b/src/ONYXKEYS.ts
@@ -275,9 +275,6 @@ const ONYXKEYS = {
/** Is report data loading? */
IS_LOADING_APP: 'isLoadingApp',
- /** Is the user in the process of switching to OldDot? */
- IS_SWITCHING_TO_OLD_DOT: 'isSwitchingToOldDot',
-
/** Is the test tools modal open? */
IS_TEST_TOOLS_MODAL_OPEN: 'isTestToolsModalOpen',
@@ -862,7 +859,6 @@ type OnyxValuesMapping = {
[ONYXKEYS.IS_TEST_TOOLS_MODAL_OPEN]: boolean;
[ONYXKEYS.APP_PROFILING_IN_PROGRESS]: boolean;
[ONYXKEYS.IS_LOADING_APP]: boolean;
- [ONYXKEYS.IS_SWITCHING_TO_OLD_DOT]: boolean;
[ONYXKEYS.WALLET_TRANSFER]: OnyxTypes.WalletTransfer;
[ONYXKEYS.LAST_ACCESSED_WORKSPACE_POLICY_ID]: string;
[ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT]: boolean;
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index 97a86d272530..c532970824b0 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -895,6 +895,10 @@ const ROUTES = {
route: 'settings/workspaces/:policyID/expensify-card',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/expensify-card` as const,
},
+ WORKSPACE_COMPANY_CARDS: {
+ route: 'settings/workspaces/:policyID/company-cards',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/company-cards` 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),
@@ -931,10 +935,6 @@ const ROUTES = {
route: 'settings/workspaces/:policyID/expensify-card/settings/frequency',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/expensify-card/settings/frequency` as const,
},
- WORKSPACE_COMPANY_CARDS: {
- route: 'settings/workspaces/:policyID/company-cards',
- getRoute: (policyID: string) => `settings/workspaces/${policyID}/company-cards` as const,
- },
WORKSPACE_RULES: {
route: 'settings/workspaces/:policyID/rules',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/rules` as const,
diff --git a/src/components/AddPaymentCard/PaymentCardCurrencyModal.tsx b/src/components/AddPaymentCard/PaymentCardCurrencyModal.tsx
index c3c38c4aec72..dc27f115dc05 100644
--- a/src/components/AddPaymentCard/PaymentCardCurrencyModal.tsx
+++ b/src/components/AddPaymentCard/PaymentCardCurrencyModal.tsx
@@ -8,6 +8,7 @@ import RadioListItem from '@components/SelectionList/RadioListItem';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
+import Navigation from '@libs/Navigation/Navigation';
import CONST from '@src/CONST';
type PaymentCardCurrencyModalProps = {
@@ -55,6 +56,7 @@ function PaymentCardCurrencyModal({isVisible, currencies, currentCurrency = CONS
onModalHide={onClose}
hideModalContentWhileAnimating
innerContainerStyle={styles.RHPNavigatorContainer(isSmallScreenWidth)}
+ onBackdropPress={Navigation.dismissModal}
useNativeDriver
>
;
+
/** The style used for the title */
titleStyles?: StyleProp;
@@ -78,6 +81,7 @@ function FeatureList({
illustration,
illustrationStyle,
illustrationBackgroundColor,
+ illustrationContainerStyle,
titleStyles,
contentPaddingOnLargeScreens,
}: FeatureListProps) {
@@ -94,6 +98,7 @@ function FeatureList({
illustrationBackgroundColor={illustrationBackgroundColor}
illustrationStyle={illustrationStyle}
titleStyles={titleStyles}
+ illustrationContainerStyle={illustrationContainerStyle}
contentPaddingOnLargeScreens={contentPaddingOnLargeScreens}
>
diff --git a/src/components/Form/FormProvider.tsx b/src/components/Form/FormProvider.tsx
index 793154d95b02..48fea78da0dc 100644
--- a/src/components/Form/FormProvider.tsx
+++ b/src/components/Form/FormProvider.tsx
@@ -1,3 +1,4 @@
+import {useFocusEffect} from '@react-navigation/native';
import lodashIsEqual from 'lodash/isEqual';
import type {ForwardedRef, MutableRefObject, ReactNode, RefAttributes} from 'react';
import React, {createRef, forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react';
@@ -215,6 +216,19 @@ function FormProvider(
onSubmit(trimmedStringValues);
}, [enabledWhenOffline, formState?.isLoading, inputValues, network?.isOffline, onSubmit, onValidate, shouldTrimValues]);
+ // Keep track of the focus state of the current screen.
+ // This is used to prevent validating the form on blur before it has been interacted with.
+ const isFocusedRef = useRef(true);
+
+ useFocusEffect(
+ useCallback(() => {
+ isFocusedRef.current = true;
+ return () => {
+ isFocusedRef.current = false;
+ };
+ }, []),
+ );
+
const resetForm = useCallback(
(optionalValue: FormOnyxValues) => {
Object.keys(inputValues).forEach((inputID) => {
@@ -332,7 +346,8 @@ function FormProvider(
return;
}
setTouchedInput(inputID);
- if (shouldValidateOnBlur) {
+ // We don't validate the form on blur in case the current screen is not focused
+ if (shouldValidateOnBlur && isFocusedRef.current) {
onValidate(inputValues, !hasServerError);
}
}, VALIDATE_DELAY);
diff --git a/src/components/Icon/Illustrations.ts b/src/components/Icon/Illustrations.ts
index 060f31647b7c..e30fc8bce7b7 100644
--- a/src/components/Icon/Illustrations.ts
+++ b/src/components/Icon/Illustrations.ts
@@ -1,4 +1,5 @@
import AmexCompanyCards from '@assets/images/companyCards/amex.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';
import EmptyCardState from '@assets/images/emptystate__expensifycard.svg';
@@ -75,6 +76,7 @@ import LockClosed from '@assets/images/simple-illustrations/simple-illustration_
import LockClosedOrange from '@assets/images/simple-illustrations/simple-illustration__lockclosed_orange.svg';
import LockOpen from '@assets/images/simple-illustrations/simple-illustration__lockopen.svg';
import Luggage from '@assets/images/simple-illustrations/simple-illustration__luggage.svg';
+import MagnifyingGlassMoney from '@assets/images/simple-illustrations/simple-illustration__magnifyingglass-money.svg';
import Mailbox from '@assets/images/simple-illustrations/simple-illustration__mailbox.svg';
import MoneyReceipts from '@assets/images/simple-illustrations/simple-illustration__money-receipts.svg';
import MoneyBadge from '@assets/images/simple-illustrations/simple-illustration__moneybadge.svg';
@@ -222,7 +224,9 @@ export {
Tire,
BigVault,
Filters,
+ MagnifyingGlassMoney,
Rules,
+ CompanyCardsEmptyState,
AmexCompanyCards,
MasterCardCompanyCards,
VisaCompanyCards,
diff --git a/src/components/Lottie/index.tsx b/src/components/Lottie/index.tsx
index 0f3b81554c75..dd46b33a8400 100644
--- a/src/components/Lottie/index.tsx
+++ b/src/components/Lottie/index.tsx
@@ -1,4 +1,3 @@
-import {useNavigation} from '@react-navigation/native';
import type {AnimationObject, LottieViewProps} from 'lottie-react-native';
import LottieView from 'lottie-react-native';
import type {ForwardedRef} from 'react';
@@ -9,7 +8,6 @@ import useAppState from '@hooks/useAppState';
import useNetwork from '@hooks/useNetwork';
import useSplashScreen from '@hooks/useSplashScreen';
import useThemeStyles from '@hooks/useThemeStyles';
-import NAVIGATORS from '@src/NAVIGATORS';
type Props = {
source: DotLottieAnimation;
@@ -20,8 +18,6 @@ function Lottie({source, webStyle, ...props}: Props, ref: ForwardedRef setIsError(false)});
@@ -31,33 +27,13 @@ function Lottie({source, webStyle, ...props}: Props, ref: ForwardedRef {
- const unsubscribeNavigationFocus = navigation.addListener('focus', () => {
- setIsHidden(false);
- });
- return unsubscribeNavigationFocus;
- }, [navigation]);
-
- // Prevent the animation from running in the background after navigating to other pages.
- // See https://github.com/Expensify/App/issues/47273
- useEffect(() => {
- const unsubscribeNavigationBlur = navigation.addListener('blur', () => {
- const state = navigation.getState();
- const targetRouteName = state?.routes?.[state?.index ?? 0]?.name;
- if (targetRouteName !== NAVIGATORS.RIGHT_MODAL_NAVIGATOR) {
- setIsHidden(true);
- }
- });
- return unsubscribeNavigationBlur;
- }, [navigation]);
-
const aspectRatioStyle = styles.aspectRatioLottie(source);
// If the image fails to load, app is in background state, animation file isn't ready, or the splash screen isn't hidden yet,
// we'll just render an empty view as the fallback to prevent
// 1. memory leak, see issue: https://github.com/Expensify/App/issues/36645
// 2. heavy rendering, see issue: https://github.com/Expensify/App/issues/34696
- if (isError || isHidden || appState.isBackground || !animationFile || !isSplashHidden) {
+ if (isError || appState.isBackground || !animationFile || !isSplashHidden) {
return ;
}
diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx
index 974f58636977..0c472bac61a5 100644
--- a/src/components/MapView/MapView.tsx
+++ b/src/components/MapView/MapView.tsx
@@ -282,7 +282,6 @@ const MapView = forwardRef(
diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx
index 4222d9e7b809..56b9cf970d54 100644
--- a/src/pages/home/ReportScreen.tsx
+++ b/src/pages/home/ReportScreen.tsx
@@ -524,10 +524,9 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro
useEffect(() => {
// Call OpenReport only if we are not linking to a message or the report is not available yet
- if (isLoadingReportOnyx || (reportActionIDFromRoute && report.reportID && isLinkedMessagePageReady)) {
+ if (isLoadingReportOnyx || reportActionIDFromRoute) {
return;
}
-
fetchReportIfNeeded();
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
}, [isLoadingReportOnyx]);
diff --git a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx
index 9c61e0178a54..5d7b5b1390c2 100644
--- a/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx
+++ b/src/pages/home/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx
@@ -7,7 +7,6 @@ import type {FileObject} from '@components/AttachmentModal';
import AttachmentPicker from '@components/AttachmentPicker';
import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
-import {usePersonalDetails} from '@components/OnyxProvider';
import type {PopoverMenuItem} from '@components/PopoverMenu';
import PopoverMenu from '@components/PopoverMenu';
import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
@@ -123,7 +122,6 @@ function AttachmentPickerWithMenuItems({
const {translate} = useLocalize();
const {windowHeight, windowWidth} = useWindowDimensions();
const {shouldUseNarrowLayout} = useResponsiveLayout();
- const allPersonalDetails = usePersonalDetails();
/**
* Returns the list of IOU Options
@@ -169,8 +167,7 @@ function AttachmentPickerWithMenuItems({
return ReportUtils.temporary_getMoneyRequestOptions(report, policy, reportParticipantIDs ?? []).map((option) => ({
...options[option],
}));
- // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
- }, [translate, report, policy, reportParticipantIDs, allPersonalDetails]);
+ }, [translate, report, policy, reportParticipantIDs]);
/**
* Determines if we can show the task option
diff --git a/src/pages/home/report/ReportActionItemParentAction.tsx b/src/pages/home/report/ReportActionItemParentAction.tsx
index 41943485f171..ff7e9d4d7027 100644
--- a/src/pages/home/report/ReportActionItemParentAction.tsx
+++ b/src/pages/home/report/ReportActionItemParentAction.tsx
@@ -124,14 +124,14 @@ function ReportActionItemParentAction({
) : (
{
const isVisibleAction = ReportActionsUtils.shouldReportActionBeVisible(ancestor.reportAction, ancestor.reportAction.reportActionID ?? '-1');
// Pop the thread report screen before navigating to the chat report.
- Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(ancestor.report.parentReportID ?? '-1'));
+ Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(ancestor.report.reportID ?? '-1'));
if (isVisibleAction && !isOffline) {
// Pop the chat report screen before navigating to the linked report action.
- Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(ancestor.report.parentReportID ?? '-1', ancestor.reportAction.reportActionID));
+ Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(ancestor.report.reportID ?? '-1', ancestor.reportAction.reportActionID));
}
}
: undefined
diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx
index a7457c88280d..069f86ce0899 100644
--- a/src/pages/iou/request/step/IOURequestStepAmount.tsx
+++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx
@@ -10,7 +10,6 @@ import * as TransactionEdit from '@libs/actions/TransactionEdit';
import * as CurrencyUtils from '@libs/CurrencyUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as OptionsListUtils from '@libs/OptionsListUtils';
-import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReportUtils from '@libs/ReportUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
import {getRequestType} from '@libs/TransactionUtils';
@@ -80,7 +79,6 @@ function IOURequestStepAmount({
const isSaveButtonPressed = useRef(false);
const iouRequestType = getRequestType(transaction);
const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID ?? -1}`);
- const [allPersonalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST);
const isEditing = action === CONST.IOU.ACTION.EDIT;
const isSplitBill = iouType === CONST.IOU.TYPE.SPLIT;
@@ -255,9 +253,7 @@ function IOURequestStepAmount({
}
IOU.setMoneyRequestParticipantsFromReport(transactionID, report);
if (isSplitBill && !report.isOwnPolicyExpenseChat && report.participants) {
- const participantAccountIDs = Object.keys(report.participants)
- .map((accountID) => Number(accountID))
- .filter((accountID) => !PolicyUtils.isExpensifyTeam(allPersonalDetails?.[accountID]?.login));
+ const participantAccountIDs = Object.keys(report.participants).map((accountID) => Number(accountID));
IOU.setSplitShares(transaction, amountInSmallestCurrencyUnits, currency || CONST.CURRENCY.USD, participantAccountIDs);
}
navigateToConfirmationPage();
diff --git a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx
index 2c9fa879dcfc..2e567fe82974 100644
--- a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx
+++ b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx
@@ -1,6 +1,6 @@
import type {StackScreenProps} from '@react-navigation/stack';
import React, {useCallback, useEffect} from 'react';
-import {NativeModules, View} from 'react-native';
+import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import Icon from '@components//Icon';
@@ -28,12 +28,11 @@ import ExitSurveyOffline from './ExitSurveyOffline';
type ExitSurveyConfirmPageOnyxProps = {
exitReason?: ExitReason | null;
- isLoading: OnyxEntry;
};
type ExitSurveyConfirmPageProps = ExitSurveyConfirmPageOnyxProps & StackScreenProps;
-function ExitSurveyConfirmPage({exitReason, isLoading, route, navigation}: ExitSurveyConfirmPageProps) {
+function ExitSurveyConfirmPage({exitReason, route, navigation}: ExitSurveyConfirmPageProps) {
const {translate} = useLocalize();
const {isOffline} = useNetwork();
const styles = useThemeStyles();
@@ -84,18 +83,10 @@ function ExitSurveyConfirmPage({exitReason, isLoading, route, navigation}: ExitS
large
text={translate('exitSurvey.goToExpensifyClassic')}
onPress={() => {
- const promise = ExitSurvey.switchToOldDot();
- if (NativeModules.HybridAppModule) {
- promise.then(() => {
- Navigation.resetToHome();
- NativeModules.HybridAppModule.closeReactNativeApp(false);
- });
- return;
- }
+ ExitSurvey.switchToOldDot();
Navigation.dismissModal();
Link.openOldDotLink(CONST.OLDDOT_URLS.INBOX);
}}
- isLoading={isLoading ?? false}
isDisabled={isOffline}
/>
@@ -110,7 +101,4 @@ export default withOnyx) => value?.[EXIT_SURVEY_REASON_INPUT_IDS.REASON] ?? null,
},
- isLoading: {
- key: ONYXKEYS.IS_SWITCHING_TO_OLD_DOT,
- },
})(ExitSurveyConfirmPage);
diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx
index 0cfcad3d731d..8e9caad09676 100755
--- a/src/pages/settings/InitialSettingsPage.tsx
+++ b/src/pages/settings/InitialSettingsPage.tsx
@@ -2,7 +2,7 @@ import {useRoute} from '@react-navigation/native';
import React, {useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState} from 'react';
// eslint-disable-next-line no-restricted-imports
import type {GestureResponderEvent, ScrollView as RNScrollView, ScrollViewProps, StyleProp, ViewStyle} from 'react-native';
-import {View} from 'react-native';
+import {NativeModules, View} from 'react-native';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import {useOnyx, withOnyx} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
@@ -144,7 +144,15 @@ function InitialSettingsPage({userWallet, bankAccountList, fundList, walletTerms
{
translationKey: 'exitSurvey.goToExpensifyClassic',
icon: Expensicons.ExpensifyLogoNew,
- routeName: ROUTES.SETTINGS_EXIT_SURVEY_REASON,
+ ...(NativeModules.HybridAppModule
+ ? {
+ action: () => {
+ NativeModules.HybridAppModule.closeReactNativeApp(false, true);
+ },
+ }
+ : {
+ routeName: ROUTES.SETTINGS_EXIT_SURVEY_REASON,
+ }),
},
{
translationKey: 'common.profile',
diff --git a/src/pages/settings/Profile/Contacts/NewContactMethodPage.tsx b/src/pages/settings/Profile/Contacts/NewContactMethodPage.tsx
index 945a1155c528..3380766d0ecf 100644
--- a/src/pages/settings/Profile/Contacts/NewContactMethodPage.tsx
+++ b/src/pages/settings/Profile/Contacts/NewContactMethodPage.tsx
@@ -47,9 +47,9 @@ function NewContactMethodPage({loginList, route}: NewContactMethodPageProps) {
const validateIfnumber = LoginUtils.validateNumber(phoneLogin);
const submitDetail = (validateIfnumber || values.phoneOrEmail).trim().toLowerCase();
- User.addNewContactMethodAndNavigate(submitDetail, route.params.backTo);
+ User.addNewContactMethodAndNavigate(submitDetail, route.params?.backTo);
},
- [route.params.backTo],
+ [route.params?.backTo],
);
const validate = React.useCallback(
diff --git a/src/pages/settings/Profile/ProfilePage.tsx b/src/pages/settings/Profile/ProfilePage.tsx
index b62342aa56f4..cc3d93c3db25 100755
--- a/src/pages/settings/Profile/ProfilePage.tsx
+++ b/src/pages/settings/Profile/ProfilePage.tsx
@@ -155,25 +155,27 @@ function ProfilePage({
titleStyles={styles.accountSettingsSectionTitle}
>
- Navigation.navigate(ROUTES.PROFILE_AVATAR.getRoute(String(accountID)))}
- previewSource={UserUtils.getFullSizeAvatar(avatarURL, accountID)}
- originalFileName={currentUserPersonalDetails.originalFileName}
- headerTitle={translate('profilePage.profileAvatar')}
- fallbackIcon={currentUserPersonalDetails?.fallbackIcon}
- editIconStyle={styles.profilePageAvatar}
- />
+
+ Navigation.navigate(ROUTES.PROFILE_AVATAR.getRoute(String(accountID)))}
+ previewSource={UserUtils.getFullSizeAvatar(avatarURL, accountID)}
+ originalFileName={currentUserPersonalDetails.originalFileName}
+ headerTitle={translate('profilePage.profileAvatar')}
+ fallbackIcon={currentUserPersonalDetails?.fallbackIcon}
+ editIconStyle={styles.profilePageAvatar}
+ />
+
{publicOptions.map((detail, index) => (
{
- const reimbursementAccountBrickRoadIndicator = reimbursementAccount?.errors ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined;
+ const reimbursementAccountBrickRoadIndicator = !isEmptyObject(reimbursementAccount?.errors) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined;
if (isEmptyObject(policies)) {
return [];
}
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardPageEmptyState.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardPageEmptyState.tsx
new file mode 100644
index 000000000000..0846c8b9e179
--- /dev/null
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardPageEmptyState.tsx
@@ -0,0 +1,57 @@
+import React, {useCallback} from 'react';
+import {View} from 'react-native';
+import FeatureList from '@components/FeatureList';
+import type {FeatureListItem} from '@components/FeatureList';
+import * as Illustrations from '@components/Icon/Illustrations';
+import useLocalize from '@hooks/useLocalize';
+import useResponsiveLayout from '@hooks/useResponsiveLayout';
+import useThemeStyles from '@hooks/useThemeStyles';
+import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading';
+import colors from '@styles/theme/colors';
+
+const companyCardFeatures: FeatureListItem[] = [
+ {
+ icon: Illustrations.CreditCardsNew,
+ translationKey: 'workspace.moreFeatures.companyCards.feed.features.support',
+ },
+ {
+ icon: Illustrations.HandCard,
+ translationKey: 'workspace.moreFeatures.companyCards.feed.features.assignCards',
+ },
+ {
+ icon: Illustrations.MagnifyingGlassMoney,
+ translationKey: 'workspace.moreFeatures.companyCards.feed.features.automaticImport',
+ },
+];
+
+function WorkspaceCompanyCardPageEmptyState() {
+ const {translate} = useLocalize();
+ const styles = useThemeStyles();
+ const {shouldUseNarrowLayout} = useResponsiveLayout();
+
+ const startFlow = useCallback(() => {
+ // TODO: Add Card Feed Flow https://github.com/Expensify/App/issues/47376
+ }, []);
+
+ return (
+
+
+
+ );
+}
+
+WorkspaceCompanyCardPageEmptyState.displayName = 'WorkspaceCompanyCardPageEmptyState';
+
+export default withPolicyAndFullscreenLoading(WorkspaceCompanyCardPageEmptyState);
diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx
index 1945cf99a001..7209ede8467a 100644
--- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx
+++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx
@@ -1,22 +1,18 @@
import type {StackScreenProps} from '@react-navigation/stack';
import React from 'react';
-import {View} from 'react-native';
import * as Illustrations from '@components/Icon/Illustrations';
import useLocalize from '@hooks/useLocalize';
-import useResponsiveLayout from '@hooks/useResponsiveLayout';
-import useThemeStyles from '@hooks/useThemeStyles';
import type {FullScreenNavigatorParamList} from '@libs/Navigation/types';
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
import WorkspacePageWithSections from '@pages/workspace/WorkspacePageWithSections';
import CONST from '@src/CONST';
import type SCREENS from '@src/SCREENS';
+import WorkspaceCompanyCardPageEmptyState from './WorkspaceCompanyCardPageEmptyState';
type WorkspaceCompanyCardPageProps = StackScreenProps;
function WorkspaceCompanyCardPage({route}: WorkspaceCompanyCardPageProps) {
const {translate} = useLocalize();
- const styles = useThemeStyles();
- const {shouldUseNarrowLayout} = useResponsiveLayout();
return (
-
+
);
diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardDetailsPage.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardDetailsPage.tsx
index f3e1472820c8..5fc5bf8c186c 100644
--- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardDetailsPage.tsx
+++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardDetailsPage.tsx
@@ -12,6 +12,7 @@ import * as Expensicons from '@components/Icon/Expensicons';
import ImageSVG from '@components/ImageSVG';
import MenuItem from '@components/MenuItem';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
+import OfflineWithFeedback from '@components/OfflineWithFeedback';
import ScreenWrapper from '@components/ScreenWrapper';
import ScrollView from '@components/ScrollView';
import useLocalize from '@hooks/useLocalize';
@@ -117,31 +118,38 @@ function WorkspaceExpensifyCardDetailsPage({route}: WorkspaceExpensifyCardDetail
interactive={false}
titleStyle={styles.walletCardNumber}
/>
-
- Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_LIMIT.getRoute(policyID, cardID))}
- />
- Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_LIMIT_TYPE.getRoute(policyID, cardID))}
- />
- Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_NAME.getRoute(policyID, cardID))}
- />
+
+
+
+
+ Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_LIMIT.getRoute(policyID, cardID))}
+ />
+
+
+ Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_LIMIT_TYPE.getRoute(policyID, cardID))}
+ />
+
+
+ Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD_NAME.getRoute(policyID, cardID))}
+ />
+
setIsOfflineModalVisible(false)}
secondOptionText={translate('common.buttonConfirm')}
diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx
index c27d5a7168ba..30d917e7ffc5 100644
--- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx
+++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx
@@ -73,6 +73,7 @@ function WorkspaceExpensifyCardListPage({route, cardsList}: WorkspaceExpensifyCa
({item, index}: ListRenderItemInfo) => (
diff --git a/src/pages/workspace/expensifyCard/issueNew/CardNameStep.tsx b/src/pages/workspace/expensifyCard/issueNew/CardNameStep.tsx
index 556f512c725d..f90635d96f83 100644
--- a/src/pages/workspace/expensifyCard/issueNew/CardNameStep.tsx
+++ b/src/pages/workspace/expensifyCard/issueNew/CardNameStep.tsx
@@ -12,6 +12,7 @@ import TextInput from '@components/TextInput';
import useAutoFocusInput from '@hooks/useAutoFocusInput';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
+import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
import * as ValidationUtils from '@libs/ValidationUtils';
import * as Card from '@userActions/Card';
import CONST from '@src/CONST';
@@ -25,6 +26,10 @@ function CardNameStep() {
const [issueNewCard] = useOnyx(ONYXKEYS.ISSUE_NEW_EXPENSIFY_CARD);
const isEditing = issueNewCard?.isEditing;
+ const data = issueNewCard?.data;
+
+ const userName = PersonalDetailsUtils.getUserNameByEmail(data?.assigneeEmail ?? '', 'firstName');
+ const defaultCardTitle = `${userName}'s Card`;
const validate = useCallback(
(values: FormOnyxValues): FormInputErrors => {
@@ -88,8 +93,7 @@ function CardNameStep() {
hint={translate('workspace.card.issueNewCard.giveItNameInstruction')}
aria-label={translate('workspace.card.issueNewCard.cardName')}
role={CONST.ROLE.PRESENTATION}
- // TODO: default value for card name
- defaultValue={issueNewCard?.data?.cardTitle}
+ defaultValue={issueNewCard?.data?.cardTitle ?? defaultCardTitle}
containerStyles={[styles.mb6]}
ref={inputCallbackRef}
/>
diff --git a/src/pages/workspace/expensifyCard/issueNew/ConfirmationStep.tsx b/src/pages/workspace/expensifyCard/issueNew/ConfirmationStep.tsx
index aed246227d7d..3c82d9075fc4 100644
--- a/src/pages/workspace/expensifyCard/issueNew/ConfirmationStep.tsx
+++ b/src/pages/workspace/expensifyCard/issueNew/ConfirmationStep.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, {useEffect, useRef} from 'react';
import {View} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import Button from '@components/Button';
@@ -35,6 +35,12 @@ function ConfirmationStep({policyID}: ConfirmationStepProps) {
const data = issueNewCard?.data;
+ const submitButton = useRef(null);
+
+ useEffect(() => {
+ submitButton.current?.focus();
+ }, []);
+
const submit = () => {
Card.issueExpensifyCard(policyID, CONST.COUNTRY.US, data);
Navigation.navigate(ROUTES.WORKSPACE_EXPENSIFY_CARD.getRoute(policyID ?? '-1'));
@@ -76,7 +82,7 @@ function ConfirmationStep({policyID}: ConfirmationStepProps) {
{translate('workspace.card.issueNewCard.willBeReady')}
editStep(CONST.EXPENSIFY_CARD.STEP.ASSIGNEE)}
/>
@@ -106,6 +112,7 @@ function ConfirmationStep({policyID}: ConfirmationStepProps) {
/>
isControlPolicy(policy), [policy]);
+ const canPerformUpgrade = !!feature && !!policy && PolicyUtils.isPolicyAdmin(policy);
+ const isUpgraded = React.useMemo(() => PolicyUtils.isControlPolicy(policy), [policy]);
const upgradeToCorporate = () => {
- if (!policy || !feature) {
+ if (!canPerformUpgrade) {
return;
}
@@ -57,16 +58,16 @@ function WorkspaceUpgradePage({route}: WorkspaceUpgradePageProps) {
useEffect(() => {
const unsubscribeListener = navigation.addListener('blur', () => {
- if (!isUpgraded) {
+ if (!isUpgraded || !canPerformUpgrade) {
return;
}
confirmUpgrade();
});
return unsubscribeListener;
- }, [isUpgraded, confirmUpgrade, navigation]);
+ }, [isUpgraded, canPerformUpgrade, confirmUpgrade, navigation]);
- if (!feature || !policy) {
+ if (!canPerformUpgrade) {
return ;
}
diff --git a/src/styles/index.ts b/src/styles/index.ts
index e4f972dbec3b..0e320fc039e3 100644
--- a/src/styles/index.ts
+++ b/src/styles/index.ts
@@ -5083,6 +5083,15 @@ const styles = (theme: ThemeColors) =>
height: 220,
},
+ emptyStateCardIllustrationContainer: {
+ height: 220,
+ },
+
+ emptyStateCardIllustration: {
+ width: 164,
+ height: 190,
+ },
+
computerIllustrationContainer: {
width: 272,
height: 188,
@@ -5143,6 +5152,7 @@ const styles = (theme: ThemeColors) =>
backgroundColor: theme.cardBG,
borderRadius: variables.componentBorderRadiusLarge,
maxWidth: 400,
+ width: '100%',
},
emptyStateHeader: (isIllustration: boolean) => ({
diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts
index 2fc9402157e0..7e3f299ad938 100644
--- a/src/types/modules/react-native.d.ts
+++ b/src/types/modules/react-native.d.ts
@@ -5,7 +5,7 @@ import type {EnvironmentCheckerModule} from '@libs/Environment/betaChecker/types
import type StartupTimer from '@libs/StartupTimer/types';
type HybridAppModule = {
- closeReactNativeApp: (shouldSignOut: boolean) => void;
+ closeReactNativeApp: (shouldSignOut: boolean, shouldSetNVP: boolean) => void;
completeOnboarding: (status: boolean) => void;
exitApp: () => void;
};
diff --git a/src/types/onyx/Card.ts b/src/types/onyx/Card.ts
index 24640df9e2da..70af988a1175 100644
--- a/src/types/onyx/Card.ts
+++ b/src/types/onyx/Card.ts
@@ -3,7 +3,7 @@ import type CONST from '@src/CONST';
import type * as OnyxCommon from './OnyxCommon';
/** Model of Expensify card */
-type Card = {
+type Card = OnyxCommon.OnyxValueWithOfflineFeedback<{
/** Card ID number */
cardID: number;
@@ -41,7 +41,7 @@ type Card = {
accountID?: number;
/** Additional card data */
- nameValuePairs?: {
+ nameValuePairs?: OnyxCommon.OnyxValueWithOfflineFeedback<{
/** Type of card spending limits */
limitType?: ValueOf;
@@ -74,8 +74,8 @@ type Card = {
/** Card expiration date */
expirationDate?: string;
- };
-};
+ }>;
+}>;
/** Model of Expensify card details */
type ExpensifyCardDetails = {
diff --git a/tests/actions/PolicyTest.ts b/tests/actions/PolicyTest.ts
index 5b95d50eed52..a9af7b9da7d8 100644
--- a/tests/actions/PolicyTest.ts
+++ b/tests/actions/PolicyTest.ts
@@ -12,7 +12,7 @@ import waitForBatchedUpdates from '../utils/waitForBatchedUpdates';
const ESH_EMAIL = 'eshgupta1217@gmail.com';
const ESH_ACCOUNT_ID = 1;
-const ESH_PARTICIPANT: Participant = {hidden: false, role: 'admin'};
+const ESH_PARTICIPANT: Participant = {hidden: false};
const WORKSPACE_NAME = "Esh's Workspace";
OnyxUpdateManager();
diff --git a/tests/ui/PaginationTest.tsx b/tests/ui/PaginationTest.tsx
index 277f6b88e78f..44ad43e69953 100644
--- a/tests/ui/PaginationTest.tsx
+++ b/tests/ui/PaginationTest.tsx
@@ -309,9 +309,7 @@ describe('Pagination', () => {
expect(getReportActions()).toHaveLength(18);
});
- // Currently broken on main by https://github.com/Expensify/App/pull/42582.
- // TODO: Investigate and re-enable.
- it.skip('opens a chat and load newer messages', async () => {
+ it('opens a chat and load newer messages', async () => {
mockOpenReport(5, '5');
mockGetNewerActions(5);
@@ -325,26 +323,43 @@ describe('Pagination', () => {
});
// ReportScreen relies on the onLayout event to receive updates from onyx.
triggerListLayout();
+ await waitForBatchedUpdatesWithAct();
- expect(getReportActions()).toHaveLength(5);
+ // Here we have 5 messages from the initial OpenReport and 5 from the initial GetNewerActions.
+ expect(getReportActions()).toHaveLength(10);
// There is 1 extra call here because of the comment linking report.
TestHelper.expectAPICommandToHaveBeenCalled('OpenReport', 2);
TestHelper.expectAPICommandToHaveBeenCalledWith('OpenReport', 1, {reportID: REPORT_ID, reportActionID: '5'});
TestHelper.expectAPICommandToHaveBeenCalled('GetOlderActions', 0);
- TestHelper.expectAPICommandToHaveBeenCalled('GetNewerActions', 0);
+ TestHelper.expectAPICommandToHaveBeenCalledWith('GetNewerActions', 0, {reportID: REPORT_ID, reportActionID: '5'});
+ // Simulate the maintainVisibleContentPosition scroll adjustment, so it is now possible to scroll down more.
+ scrollToOffset(500);
scrollToOffset(0);
await waitForBatchedUpdatesWithAct();
TestHelper.expectAPICommandToHaveBeenCalled('OpenReport', 2);
TestHelper.expectAPICommandToHaveBeenCalled('GetOlderActions', 0);
- TestHelper.expectAPICommandToHaveBeenCalled('GetNewerActions', 1);
- TestHelper.expectAPICommandToHaveBeenCalledWith('GetNewerActions', 0, {reportID: REPORT_ID, reportActionID: '5'});
+ TestHelper.expectAPICommandToHaveBeenCalled('GetNewerActions', 2);
+ TestHelper.expectAPICommandToHaveBeenCalledWith('GetNewerActions', 1, {reportID: REPORT_ID, reportActionID: '10'});
+
+ // We now have 15 messages. 5 from the initial OpenReport and 10 from the 2 GetNewerActions calls.
+ expect(getReportActions()).toHaveLength(15);
+ // Simulate the backend returning no new messages to simulate reaching the start of the chat.
+ mockGetNewerActions(0);
+
+ scrollToOffset(500);
+ scrollToOffset(0);
await waitForBatchedUpdatesWithAct();
- // We now have 10 messages. 5 from the initial OpenReport and 5 from GetNewerActions.
- expect(getReportActions()).toHaveLength(10);
+ TestHelper.expectAPICommandToHaveBeenCalled('OpenReport', 2);
+ TestHelper.expectAPICommandToHaveBeenCalled('GetOlderActions', 0);
+ TestHelper.expectAPICommandToHaveBeenCalled('GetNewerActions', 3);
+ TestHelper.expectAPICommandToHaveBeenCalledWith('GetNewerActions', 2, {reportID: REPORT_ID, reportActionID: '15'});
+
+ // We still have 15 messages. 5 from the initial OpenReport and 10 from the 2 GetNewerActions calls.
+ expect(getReportActions()).toHaveLength(15);
});
});