diff --git a/.eslintrc.js b/.eslintrc.js
index 5f450f3ae6c2..cfbfdcc8fe91 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -109,7 +109,6 @@ module.exports = {
'plugin:prettier/recommended',
],
plugins: ['@typescript-eslint', 'jsdoc', 'you-dont-need-lodash-underscore', 'react-native-a11y', 'react', 'testing-library', 'eslint-plugin-react-compiler', 'lodash', 'deprecation'],
- ignorePatterns: ['lib/**'],
parser: '@typescript-eslint/parser',
parserOptions: {
project: path.resolve(__dirname, './tsconfig.json'),
diff --git a/.github/workflows/e2ePerformanceTests.yml b/.github/workflows/e2ePerformanceTests.yml
index b48c7b2175eb..c92ab83d1178 100644
--- a/.github/workflows/e2ePerformanceTests.yml
+++ b/.github/workflows/e2ePerformanceTests.yml
@@ -35,8 +35,16 @@ jobs:
- name: Determine "baseline ref" (prev merge commit)
id: getBaselineRef
run: |
- previous_merge=$(git rev-list --merges HEAD~1 | head -n 1)
- git checkout "$previous_merge"
+ # Get the name of the current branch
+ current_branch=$(git rev-parse --abbrev-ref HEAD)
+
+ if [ "$current_branch" = "main" ]; then
+ # On the main branch, find the previous merge commit
+ previous_merge=$(git rev-list --merges HEAD~1 | head -n 1)
+ else
+ # On a feature branch, find the common ancestor of the current branch and main
+ previous_merge=$(git merge-base HEAD main)
+ fi
echo "$previous_merge"
echo "BASELINE_REF=$previous_merge" >> "$GITHUB_OUTPUT"
diff --git a/.prettierignore b/.prettierignore
index c4c88bd11d3e..b428978a1563 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -18,8 +18,6 @@ package-lock.json
*.markdown
# We need to modify the import here specifically, hence we disable prettier to get rid of the sorted imports
src/libs/E2E/reactNativeLaunchingTest.ts
-# Temporary while we keep react-compiler in our repo
-lib/**
# Automatically generated files
src/libs/SearchParser/searchParser.js
diff --git a/README.md b/README.md
index 4a691045e7c2..730e745e368a 100644
--- a/README.md
+++ b/README.md
@@ -274,7 +274,7 @@ web: `npm run symbolicate-release:web`
- Perfetto UI (https://ui.perfetto.dev/)
- Google Chrome's Tracing UI (chrome://tracing)
----
+----
# App Structure and Conventions
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 802b04d71613..5ce4d0e5fddf 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 1009005400
- versionName "9.0.54-0"
+ versionCode 1009005401
+ versionName "9.0.54-1"
// 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/android/app/src/main/java/com/expensify/chat/MainApplication.kt b/android/app/src/main/java/com/expensify/chat/MainApplication.kt
index f476ad89c5b4..942304c80445 100644
--- a/android/app/src/main/java/com/expensify/chat/MainApplication.kt
+++ b/android/app/src/main/java/com/expensify/chat/MainApplication.kt
@@ -64,7 +64,7 @@ class MainApplication : MultiDexApplication(), ReactApplication {
SoLoader.init(this, /* native exopackage */false)
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// If you opted-in for the New Architecture, we load the native entry point for this app.
- load()
+ load(bridgelessEnabled = false)
}
if (BuildConfig.DEBUG) {
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(false)
diff --git a/babel.config.js b/babel.config.js
index 663eb29d5d2f..3f0fff03736d 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -3,11 +3,14 @@ require('dotenv').config();
const IS_E2E_TESTING = process.env.E2E_TESTING === 'true';
const ReactCompilerConfig = {
- runtimeModule: 'react-compiler-runtime',
+ target: '18',
environment: {
enableTreatRefLikeIdentifiersAsRefs: true,
},
+ // We exclude 'tests' directory from compilation, but still compile components imported in test files.
+ sources: (filename) => !filename.includes('tests/') && !filename.includes('node_modules/'),
};
+
/**
* Setting targets to node 20 to reduce JS bundle size
* It is also recommended by babel:
@@ -52,6 +55,8 @@ const webpack = {
const metro = {
presets: [require('@react-native/babel-preset')],
plugins: [
+ ['babel-plugin-react-compiler', ReactCompilerConfig], // must run first!
+
// This is needed due to a react-native bug: https://github.com/facebook/react-native/issues/29084#issuecomment-1030732709
// It is included in metro-react-native-babel-preset but needs to be before plugin-proposal-class-properties or FlatList will break
'@babel/plugin-transform-flow-strip-types',
@@ -154,11 +159,5 @@ module.exports = (api) => {
const runningIn = api.caller((args = {}) => args.name);
console.debug(' - running in: ', runningIn);
- // don't include react-compiler in jest, because otherwise tests will fail
- if (runningIn !== 'babel-jest') {
- // must run first!
- metro.plugins.unshift(['babel-plugin-react-compiler', ReactCompilerConfig]);
- }
-
return ['metro', 'babel-jest'].includes(runningIn) ? metro : webpack;
};
diff --git a/docs/assets/images/ExpensifyHelp_OldDot_PayInvoice_1.png b/docs/assets/images/ExpensifyHelp_OldDot_PayInvoice_1.png
new file mode 100644
index 000000000000..53c637736c95
Binary files /dev/null and b/docs/assets/images/ExpensifyHelp_OldDot_PayInvoice_1.png differ
diff --git a/docs/assets/images/ExpensifyHelp_OldDot_PayInvoice_2.png b/docs/assets/images/ExpensifyHelp_OldDot_PayInvoice_2.png
new file mode 100644
index 000000000000..92e607756de2
Binary files /dev/null and b/docs/assets/images/ExpensifyHelp_OldDot_PayInvoice_2.png differ
diff --git a/docs/redirects.csv b/docs/redirects.csv
index a7d4d94adb5d..d3672618cfad 100644
--- a/docs/redirects.csv
+++ b/docs/redirects.csv
@@ -585,7 +585,8 @@ https://community.expensify.com/discussion/6699/faq-troubleshooting-known-bank-s
https://community.expensify.com/discussion/4730/faq-expenses-are-exporting-to-the-wrong-accounts-whys-that,https://help.expensify.com/articles/expensify-classic/connect-credit-cards/company-cards/Company-Card-Settings
https://community.expensify.com/discussion/9000/how-to-integrate-with-deel,https://help.expensify.com/articles/expensify-classic/connections/Deel
https://community.expensify.com/categories/expensify-classroom,https://use.expensify.com
-https://help.expensify.com/articles/new-expensify/billing-and-subscriptions/adding-payment-card-subscription-overview,https://help.expensify.com/articles/new-expensify/billing-and-subscriptions/add-a-payment-card-and-view-your-subscription
+https://help.expensify.com/articles/new-expensify/billing-and-subscriptions/adding-payment-card-subscription-overview,https://help.expensify.com/articles/new-expensify/billing-and-subscriptions/Add-a-payment-card-and-view-your-subscription
https://help.expensify.com/articles/expensify-classic/articles/expensify-classic/expenses/Send-Receive-for-Invoices,https://help.expensify.com/articles/expensify-classic/articles/expensify-classic/expenses/Send-and-Receive-Payment-for-Invoices.md
https://help.expensify.com/articles/expensify-classic/articles/expensify-classic/expenses/Bulk-Upload-Multiple-Invoices,https://help.expensify.com/articles/expensify-classic/articles/expensify-classic/expenses/Add-Invoices-in-Bulk
-https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/payments/Pay-Bills,https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/payments/Create-and-Pay-Bills
\ No newline at end of file
+https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/payments/Pay-Bills,https://help.expensify.com/articles/expensify-classic/bank-accounts-and-payments/payments/Create-and-Pay-Bills
+https://help.expensify.com/articles/new-expensify/billing-and-subscriptions/add-a-payment-card-and-view-your-subscription,https://help.expensify.com/articles/new-expensify/billing-and-subscriptions/Add-a-payment-card-and-view-your-subscription
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index 74a45675a3be..361113013ae4 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -40,7 +40,7 @@
CFBundleVersion
- 9.0.54.0
+ 9.0.54.1
FullStory
OrgId
diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist
index a4a8f2de743b..cb867d7af0b5 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -19,6 +19,6 @@
CFBundleSignature
????
CFBundleVersion
- 9.0.54.0
+ 9.0.54.1
diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist
index aa916c957ebd..c7c9879bb2ab 100644
--- a/ios/NotificationServiceExtension/Info.plist
+++ b/ios/NotificationServiceExtension/Info.plist
@@ -13,7 +13,7 @@
CFBundleShortVersionString
9.0.54
CFBundleVersion
- 9.0.54.0
+ 9.0.54.1
NSExtension
NSExtensionPointIdentifier
diff --git a/lib/react-compiler-runtime/index.js b/lib/react-compiler-runtime/index.js
deleted file mode 100644
index 54e88d2b703a..000000000000
--- a/lib/react-compiler-runtime/index.js
+++ /dev/null
@@ -1,21 +0,0 @@
-// lib/react-compiler-runtime.js
-const $empty = Symbol.for("react.memo_cache_sentinel");
-const React = require('react');
-/**
- * DANGER: this hook is NEVER meant to be called directly!
- *
- * Note that this is a temporary userspace implementation of this function
- * from React 19. It is not as efficient and may invalidate more frequently
- * than the official API. Better to upgrade to React 19 as soon as we can.
- **/
-export function c(size) {
- return React.useState(() => {
- const $ = new Array(size);
- for (let ii = 0; ii < size; ii++) {
- $[ii] = $empty;
- }
- // @ts-ignore
- $[$empty] = true;
- return $;
- })[0];
-}
diff --git a/lib/react-compiler-runtime/package.json b/lib/react-compiler-runtime/package.json
deleted file mode 100644
index 3a0323538b6e..000000000000
--- a/lib/react-compiler-runtime/package.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "name": "react-compiler-runtime",
- "version": "0.0.1",
- "description": "Runtime for React Compiler",
- "license": "MIT",
- "main": "index.js",
- "dependencies": {
- "react": "18.3.1"
- }
-}
diff --git a/package-lock.json b/package-lock.json
index 97efca05f12f..7202ef76ab66 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "new.expensify",
- "version": "9.0.54-0",
+ "version": "9.0.54-1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "new.expensify",
- "version": "9.0.54-0",
+ "version": "9.0.54-1",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@@ -204,7 +204,7 @@
"babel-jest": "29.4.1",
"babel-loader": "^9.1.3",
"babel-plugin-module-resolver": "^5.0.0",
- "babel-plugin-react-compiler": "0.0.0-experimental-334f00b-20240725",
+ "babel-plugin-react-compiler": "^19.0.0-beta-8a03594-20241020",
"babel-plugin-react-native-web": "^0.18.7",
"babel-plugin-transform-remove-console": "^6.9.4",
"clean-webpack-plugin": "^4.0.0",
@@ -225,7 +225,7 @@
"eslint-plugin-jest": "^28.6.0",
"eslint-plugin-jsdoc": "^46.2.6",
"eslint-plugin-lodash": "^7.4.0",
- "eslint-plugin-react-compiler": "0.0.0-experimental-9ed098e-20240725",
+ "eslint-plugin-react-compiler": "^19.0.0-beta-8a03594-20241020",
"eslint-plugin-react-native-a11y": "^3.3.0",
"eslint-plugin-storybook": "^0.8.0",
"eslint-plugin-testing-library": "^6.2.2",
@@ -248,8 +248,8 @@
"portfinder": "^1.0.28",
"prettier": "^2.8.8",
"pusher-js-mock": "^0.3.3",
- "react-compiler-healthcheck": "^0.0.0-experimental-ab3118d-20240725",
- "react-compiler-runtime": "file:./lib/react-compiler-runtime",
+ "react-compiler-healthcheck": "^19.0.0-beta-8a03594-20241020",
+ "react-compiler-runtime": "^19.0.0-beta-8a03594-20241020",
"react-is": "^18.3.1",
"react-native-clean-project": "^4.0.0-alpha4.0",
"react-test-renderer": "18.3.1",
@@ -279,14 +279,6 @@
"npm": "10.8.2"
}
},
- "lib/react-compiler-runtime": {
- "version": "0.0.1",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "react": "18.3.1"
- }
- },
"node_modules/@actions/core": {
"version": "1.10.0",
"dev": true,
@@ -18346,9 +18338,10 @@
}
},
"node_modules/babel-plugin-react-compiler": {
- "version": "0.0.0-experimental-334f00b-20240725",
+ "version": "19.0.0-beta-8a03594-20241020",
+ "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-19.0.0-beta-8a03594-20241020.tgz",
+ "integrity": "sha512-Wk0748DZzQEmjkEN4SbBujM5al4q5TfRBapA32ax0AID/Yek3emS+eyCvPvb4zPddYJTAF4LaJNLt8uHYfdKAQ==",
"dev": true,
- "license": "MIT",
"dependencies": {
"@babel/generator": "7.2.0",
"@babel/types": "^7.19.0",
@@ -23532,9 +23525,10 @@
}
},
"node_modules/eslint-plugin-react-compiler": {
- "version": "0.0.0-experimental-9ed098e-20240725",
+ "version": "19.0.0-beta-8a03594-20241020",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-compiler/-/eslint-plugin-react-compiler-19.0.0-beta-8a03594-20241020.tgz",
+ "integrity": "sha512-bYg1COih1s3r14IV/AKdQs/SN7CQmNI0ZaMtPdgZ6gp1S1Q/KGP9P43w7R6dHJ4wYpuMBvekNJHQdVu+x6UM+A==",
"dev": true,
- "license": "MIT",
"dependencies": {
"@babel/core": "^7.24.4",
"@babel/parser": "^7.24.4",
@@ -34077,9 +34071,10 @@
}
},
"node_modules/react-compiler-healthcheck": {
- "version": "0.0.0-experimental-b130d5f-20240625",
+ "version": "19.0.0-beta-8a03594-20241020",
+ "resolved": "https://registry.npmjs.org/react-compiler-healthcheck/-/react-compiler-healthcheck-19.0.0-beta-8a03594-20241020.tgz",
+ "integrity": "sha512-wupgZ4fASQ+oRI88V6QIERKCHZUo6322LXlH8EotsWQDc8c4EXgPdkZHO/zH+zDh4Np4qZM36bFbZgHPXtI0FA==",
"dev": true,
- "license": "MIT",
"dependencies": {
"@babel/core": "^7.24.4",
"@babel/parser": "^7.24.4",
@@ -34162,8 +34157,13 @@
}
},
"node_modules/react-compiler-runtime": {
- "resolved": "lib/react-compiler-runtime",
- "link": true
+ "version": "19.0.0-beta-8a03594-20241020",
+ "resolved": "https://registry.npmjs.org/react-compiler-runtime/-/react-compiler-runtime-19.0.0-beta-8a03594-20241020.tgz",
+ "integrity": "sha512-YWl8SjxsWGU1dpxHvWS0vxTkpeLXTZ/Y7IVzwZGj6yAfXOEie1MduuAR0TFiGeV0RxFLp5jKUIWl+ZglN4dMQw==",
+ "dev": true,
+ "peerDependencies": {
+ "react": "^18.2.0 || ^19.0.0"
+ }
},
"node_modules/react-content-loader": {
"version": "7.0.0",
diff --git a/package.json b/package.json
index 96efa856e16f..f0425a747967 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "9.0.54-0",
+ "version": "9.0.54-1",
"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.",
@@ -260,7 +260,7 @@
"babel-jest": "29.4.1",
"babel-loader": "^9.1.3",
"babel-plugin-module-resolver": "^5.0.0",
- "babel-plugin-react-compiler": "0.0.0-experimental-334f00b-20240725",
+ "babel-plugin-react-compiler": "^19.0.0-beta-8a03594-20241020",
"babel-plugin-react-native-web": "^0.18.7",
"babel-plugin-transform-remove-console": "^6.9.4",
"clean-webpack-plugin": "^4.0.0",
@@ -281,7 +281,7 @@
"eslint-plugin-jest": "^28.6.0",
"eslint-plugin-jsdoc": "^46.2.6",
"eslint-plugin-lodash": "^7.4.0",
- "eslint-plugin-react-compiler": "0.0.0-experimental-9ed098e-20240725",
+ "eslint-plugin-react-compiler": "^19.0.0-beta-8a03594-20241020",
"eslint-plugin-react-native-a11y": "^3.3.0",
"eslint-plugin-storybook": "^0.8.0",
"eslint-plugin-testing-library": "^6.2.2",
@@ -304,8 +304,8 @@
"portfinder": "^1.0.28",
"prettier": "^2.8.8",
"pusher-js-mock": "^0.3.3",
- "react-compiler-healthcheck": "^0.0.0-experimental-ab3118d-20240725",
- "react-compiler-runtime": "file:./lib/react-compiler-runtime",
+ "react-compiler-healthcheck": "^19.0.0-beta-8a03594-20241020",
+ "react-compiler-runtime": "^19.0.0-beta-8a03594-20241020",
"react-is": "^18.3.1",
"react-native-clean-project": "^4.0.0-alpha4.0",
"react-test-renderer": "18.3.1",
diff --git a/patches/@react-native-firebase+app+12.9.3+002+bridgeless.patch b/patches/@react-native-firebase+app+12.9.3+002+bridgeless.patch
index a085cdbcfbe2..54ae4d9a1c58 100644
--- a/patches/@react-native-firebase+app+12.9.3+002+bridgeless.patch
+++ b/patches/@react-native-firebase+app+12.9.3+002+bridgeless.patch
@@ -1,5 +1,5 @@
diff --git a/node_modules/@react-native-firebase/app/lib/internal/registry/nativeModule.js b/node_modules/@react-native-firebase/app/lib/internal/registry/nativeModule.js
-index 03f001c..23d467d 100644
+index 03f001c..358c795 100644
--- a/node_modules/@react-native-firebase/app/lib/internal/registry/nativeModule.js
+++ b/node_modules/@react-native-firebase/app/lib/internal/registry/nativeModule.js
@@ -65,7 +65,7 @@ function nativeModuleWrapped(namespace, NativeModule, argToPrepend) {
@@ -7,7 +7,7 @@ index 03f001c..23d467d 100644
}
- const properties = Object.keys(NativeModule);
-+ const properties = Object.keys(Object.getPrototypeOf(NativeModule));
++ const properties = [...Object.keys(Object.getPrototypeOf(NativeModule)), ...Object.keys(NativeModule)];
for (let i = 0, len = properties.length; i < len; i++) {
const property = properties[i];
diff --git a/patches/react-compiler-healthcheck+0.0.0-experimental-b130d5f-20240625+001+initial.patch b/patches/react-compiler-healthcheck+19.0.0-beta-8a03594-20241020+001+initial.patch
similarity index 66%
rename from patches/react-compiler-healthcheck+0.0.0-experimental-b130d5f-20240625+001+initial.patch
rename to patches/react-compiler-healthcheck+19.0.0-beta-8a03594-20241020+001+initial.patch
index d7c02701a636..03b386587338 100644
--- a/patches/react-compiler-healthcheck+0.0.0-experimental-b130d5f-20240625+001+initial.patch
+++ b/patches/react-compiler-healthcheck+19.0.0-beta-8a03594-20241020+001+initial.patch
@@ -1,8 +1,8 @@
diff --git a/node_modules/react-compiler-healthcheck/dist/index.js b/node_modules/react-compiler-healthcheck/dist/index.js
-index b427385..4bf23db 100755
+index 5a4060d..460339b 100755
--- a/node_modules/react-compiler-healthcheck/dist/index.js
+++ b/node_modules/react-compiler-healthcheck/dist/index.js
-@@ -69154,7 +69154,7 @@ var reactCompilerCheck = {
+@@ -56969,7 +56969,7 @@ var reactCompilerCheck = {
compile(source, path);
}
},
@@ -11,11 +11,11 @@ index b427385..4bf23db 100755
const totalComponents =
SucessfulCompilation.length +
countUniqueLocInEvents(OtherFailures) +
-@@ -69164,6 +69164,50 @@ var reactCompilerCheck = {
+@@ -56979,6 +56979,50 @@ var reactCompilerCheck = {
`Successfully compiled ${SucessfulCompilation.length} out of ${totalComponents} components.`
)
);
-+
++
+ if (verbose) {
+ for (const compilation of [...SucessfulCompilation, ...ActionableFailures, ...OtherFailures]) {
+ const filename = compilation.fnLoc?.filename;
@@ -38,33 +38,33 @@ index b427385..4bf23db 100755
+ if (compilation.kind === "CompileError") {
+ const { reason, severity, loc } = compilation.detail;
+
-+ const lnNo = loc.start?.line;
-+ const colNo = loc.start?.column;
++ const lnNo = loc.start?.line;
++ const colNo = loc.start?.column;
+
-+ const isTodo = severity === ErrorSeverity.Todo;
++ const isTodo = severity === ErrorSeverity.Todo;
+
-+ console.log(
-+ chalk[isTodo ? 'yellow' : 'red'](
-+ `Failed to compile ${
-+ filename
-+ }${
-+ lnNo !== undefined ? `:${lnNo}${
-+ colNo !== undefined ? `:${colNo}` : ""
-+ }.` : ""
-+ }`
-+ ),
-+ chalk[isTodo ? 'yellow' : 'red'](reason? `Reason: ${reason}` : "")
-+ );
-+ console.log("\n");
++ console.log(
++ chalk[isTodo ? 'yellow' : 'red'](
++ `Failed to compile ${
++ filename
++ }${
++ lnNo !== undefined ? `:${lnNo}${
++ colNo !== undefined ? `:${colNo}` : ""
++ }.` : ""
++ }`
++ ),
++ chalk[isTodo ? 'yellow' : 'red'](reason? `Reason: ${reason}` : "")
++ );
++ console.log("\n");
+ }
+ }
+ }
},
};
const JsFileExtensionRE = /(js|ts|jsx|tsx)$/;
-@@ -69200,9 +69244,16 @@ function main() {
- type: "string",
- default: "**/+(*.{js,mjs,jsx,ts,tsx}|package.json)",
+@@ -57015,9 +57059,16 @@ function main() {
+ type: 'string',
+ default: '**/+(*.{js,mjs,jsx,ts,tsx}|package.json)',
})
+ .option('verbose', {
+ description: 'run with verbose logging',
@@ -73,13 +73,13 @@ index b427385..4bf23db 100755
+ alias: 'v',
+ })
.parseSync();
- const spinner = ora("Checking").start();
+ const spinner = ora('Checking').start();
let src = argv.src;
+ let verbose = argv.verbose;
const globOptions = {
onlyFiles: true,
ignore: [
-@@ -69222,7 +69273,7 @@ function main() {
+@@ -57037,7 +57088,7 @@ function main() {
libraryCompatCheck.run(source, path);
}
spinner.stop();
diff --git a/patches/react-compiler-healthcheck+0.0.0-experimental-b130d5f-20240625+002+enable-ref-identifiers.patch b/patches/react-compiler-healthcheck+19.0.0-beta-8a03594-20241020+002+enable-ref-identifiers.patch
similarity index 65%
rename from patches/react-compiler-healthcheck+0.0.0-experimental-b130d5f-20240625+002+enable-ref-identifiers.patch
rename to patches/react-compiler-healthcheck+19.0.0-beta-8a03594-20241020+002+enable-ref-identifiers.patch
index 6caa4ad4c373..8ae46e379619 100644
--- a/patches/react-compiler-healthcheck+0.0.0-experimental-b130d5f-20240625+002+enable-ref-identifiers.patch
+++ b/patches/react-compiler-healthcheck+19.0.0-beta-8a03594-20241020+002+enable-ref-identifiers.patch
@@ -1,28 +1,28 @@
diff --git a/node_modules/react-compiler-healthcheck/dist/index.js b/node_modules/react-compiler-healthcheck/dist/index.js
-index 4bf23db..fa2ab22 100755
+index 460339b..17b0f96 100755
--- a/node_modules/react-compiler-healthcheck/dist/index.js
+++ b/node_modules/react-compiler-healthcheck/dist/index.js
-@@ -69088,6 +69088,9 @@ const COMPILER_OPTIONS = {
- compilationMode: "infer",
- panicThreshold: "critical_errors",
- logger: logger,
+@@ -56902,6 +56902,9 @@ const COMPILER_OPTIONS = {
+ noEmit: true,
+ compilationMode: 'infer',
+ panicThreshold: 'critical_errors',
+ environment: {
+ enableTreatRefLikeIdentifiersAsRefs: true,
+ },
+ logger: logger,
};
function isActionableDiagnostic(detail) {
- switch (detail.severity) {
diff --git a/node_modules/react-compiler-healthcheck/src/checks/reactCompiler.ts b/node_modules/react-compiler-healthcheck/src/checks/reactCompiler.ts
-index 09c9b9b..d2418e0 100644
+index 3094548..fd05b76 100644
--- a/node_modules/react-compiler-healthcheck/src/checks/reactCompiler.ts
+++ b/node_modules/react-compiler-healthcheck/src/checks/reactCompiler.ts
-@@ -51,6 +51,9 @@ const COMPILER_OPTIONS: Partial = {
- compilationMode: "infer",
- panicThreshold: "critical_errors",
- logger,
+@@ -50,6 +50,9 @@ const COMPILER_OPTIONS: Partial = {
+ noEmit: true,
+ compilationMode: 'infer',
+ panicThreshold: 'critical_errors',
+ environment: {
+ enableTreatRefLikeIdentifiersAsRefs: true,
+ },
+ logger,
};
- function isActionableDiagnostic(detail: CompilerErrorDetailOptions) {
diff --git a/patches/react-compiler-healthcheck+0.0.0-experimental-b130d5f-20240625+003+json.patch b/patches/react-compiler-healthcheck+19.0.0-beta-8a03594-20241020+003+json.patch
similarity index 88%
rename from patches/react-compiler-healthcheck+0.0.0-experimental-b130d5f-20240625+003+json.patch
rename to patches/react-compiler-healthcheck+19.0.0-beta-8a03594-20241020+003+json.patch
index a3de7a365889..246351351195 100644
--- a/patches/react-compiler-healthcheck+0.0.0-experimental-b130d5f-20240625+003+json.patch
+++ b/patches/react-compiler-healthcheck+19.0.0-beta-8a03594-20241020+003+json.patch
@@ -1,8 +1,8 @@
diff --git a/node_modules/react-compiler-healthcheck/dist/index.js b/node_modules/react-compiler-healthcheck/dist/index.js
-index fa2ab22..93be1fb 100755
+index 17b0f96..e386e34 100755
--- a/node_modules/react-compiler-healthcheck/dist/index.js
+++ b/node_modules/react-compiler-healthcheck/dist/index.js
-@@ -69157,16 +69157,28 @@ var reactCompilerCheck = {
+@@ -56972,16 +56972,28 @@ var reactCompilerCheck = {
compile(source, path);
}
},
@@ -24,7 +24,7 @@ index fa2ab22..93be1fb 100755
+ )
+ );
+ }
-+
++
+ if (json) {
+ const extractFileName = (output) => output.fnLoc.filename;
+ const successfulFiles = SucessfulCompilation.map(extractFileName);
@@ -34,10 +34,10 @@ index fa2ab22..93be1fb 100755
+ failure: unsuccessfulFiles,
+ }));
+ }
-
+
if (verbose) {
for (const compilation of [...SucessfulCompilation, ...ActionableFailures, ...OtherFailures]) {
-@@ -69253,10 +69265,17 @@ function main() {
+@@ -57068,10 +57080,17 @@ function main() {
default: false,
alias: 'v',
})
@@ -48,14 +48,14 @@ index fa2ab22..93be1fb 100755
+ alias: 'j',
+ })
.parseSync();
- const spinner = ora("Checking").start();
+ const spinner = ora('Checking').start();
let src = argv.src;
let verbose = argv.verbose;
+ let json = argv.json;
const globOptions = {
onlyFiles: true,
ignore: [
-@@ -69276,9 +69295,12 @@ function main() {
+@@ -57091,9 +57110,11 @@ function main() {
libraryCompatCheck.run(source, path);
}
spinner.stop();
@@ -63,7 +63,6 @@ index fa2ab22..93be1fb 100755
- strictModeCheck.report();
- libraryCompatCheck.report();
+ reactCompilerCheck.report(verbose, json);
-+ // using json option we only want to get list of files
+ if (!json) {
+ strictModeCheck.report();
+ libraryCompatCheck.report();
diff --git a/src/CONST.ts b/src/CONST.ts
index 3c8ca0b40b1a..dce6f53d47f0 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -1581,6 +1581,7 @@ const CONST = {
TRACKING_CATEGORY_OPTIONS: {
DEFAULT: 'DEFAULT',
TAG: 'TAG',
+ REPORT_FIELD: 'REPORT_FIELD',
},
},
@@ -1633,6 +1634,12 @@ const CONST = {
JOURNAL_ENTRY: 'journal_entry',
},
+ QUICKBOOKS_NON_REIMBURSABLE_ACCOUNT_TYPE: {
+ CREDIT_CARD: 'credit_card',
+ DEBIT_CARD: 'debit_card',
+ VENDOR_BILL: 'bill',
+ },
+
QUICKBOOKS_DESKTOP_REIMBURSABLE_ACCOUNT_TYPE: {
VENDOR_BILL: 'VENDOR_BILL',
CHECK: 'CHECK',
@@ -4834,10 +4841,11 @@ const CONST = {
'\n' +
'Here’s how to request money:\n' +
'\n' +
- '1. Click the green *+* button.\n' +
- '2. Choose *Split expense*.\n' +
- '3. Scan a receipt or enter an amount.\n' +
- '4. Add your friend(s) to the request.\n' +
+ '1. Hit the green *+* button.\n' +
+ '2. Choose *Start chat*.\n' +
+ '3. Enter any email, SMS, or name of who you want to split with.\n' +
+ '4. From within the chat, hit the *+* button on the message bar, and hit *Split expense*.\n' +
+ '5. Create the expense by selecting Manual, Scan or Distance.\n' +
'\n' +
'Feel free to add more details if you want, or just send it off. Let’s get you paid back!',
},
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index e6984d6c757b..c346da6cadcb 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -1450,14 +1450,26 @@ const ROUTES = {
route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import/classes',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import/classes` as const,
},
+ POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_CLASSES_DISPLAYED_AS: {
+ route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import/classes/displayed-as',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import/classes/displayed-as` as const,
+ },
POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_CUSTOMERS: {
route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import/customers',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import/customers` as const,
},
+ POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_CUSTOMERS_DISPLAYED_AS: {
+ route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import/customers/displayed-as',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import/customers/displayed-as` as const,
+ },
POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_LOCATIONS: {
route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import/locations',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import/locations` as const,
},
+ POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_LOCATIONS_DISPLAYED_AS: {
+ route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import/locations/displayed-as',
+ getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import/locations/displayed-as` as const,
+ },
POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_TAXES: {
route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import/taxes',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import/taxes` as const,
diff --git a/src/SCREENS.ts b/src/SCREENS.ts
index 7f04b861dec5..2e44c5ed5695 100644
--- a/src/SCREENS.ts
+++ b/src/SCREENS.ts
@@ -332,6 +332,9 @@ const SCREENS = {
QUICKBOOKS_ONLINE_ADVANCED: 'Policy_Accounting_Quickbooks_Online_Advanced',
QUICKBOOKS_ONLINE_ACCOUNT_SELECTOR: 'Policy_Accounting_Quickbooks_Online_Account_Selector',
QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECTOR: 'Policy_Accounting_Quickbooks_Online_Invoice_Account_Selector',
+ QUICKBOOKS_ONLINE_CLASSES_DISPLAYED_AS: 'Policy_Accounting_Quickbooks_Online_Import_Classes_Displayed_As',
+ QUICKBOOKS_ONLINE_CUSTOMERS_DISPLAYED_AS: 'Policy_Accounting_Quickbooks_Online_Import_Customers_Displayed_As',
+ QUICKBOOKS_ONLINE_LOCATIONS_DISPLAYED_AS: 'Policy_Accounting_Quickbooks_Online_Import_Locations_Displayed_As',
QUICKBOOKS_DESKTOP_COMPANY_CARD_EXPENSE_ACCOUNT_SELECT: 'Workspace_Accounting_Quickbooks_Desktop_Export_Company_Card_Expense_Account_Select',
QUICKBOOKS_DESKTOP_COMPANY_CARD_EXPENSE_ACCOUNT_COMPANY_CARD_SELECT: 'Workspace_Accounting_Quickbooks_Desktop_Export_Company_Card_Expense_Select',
QUICKBOOKS_DESKTOP_COMPANY_CARD_EXPENSE_ACCOUNT: 'Workspace_Accounting_Quickbooks_Desktop_Export_Company_Card_Expense',
diff --git a/src/components/AttachmentPicker/index.native.tsx b/src/components/AttachmentPicker/index.native.tsx
index 975ea6c548c0..d3a51c7fc0f0 100644
--- a/src/components/AttachmentPicker/index.native.tsx
+++ b/src/components/AttachmentPicker/index.native.tsx
@@ -417,6 +417,7 @@ function AttachmentPicker({
}}
isVisible={isVisible}
anchorRef={popoverRef}
+ // eslint-disable-next-line react-compiler/react-compiler
onModalHide={onModalHide.current}
>
@@ -431,6 +432,7 @@ function AttachmentPicker({
))}
+ {/* eslint-disable-next-line react-compiler/react-compiler */}
{renderChildren()}
>
);
diff --git a/src/components/AttachmentPicker/index.tsx b/src/components/AttachmentPicker/index.tsx
index c4979f544080..f3c880fcb835 100644
--- a/src/components/AttachmentPicker/index.tsx
+++ b/src/components/AttachmentPicker/index.tsx
@@ -98,6 +98,7 @@ function AttachmentPicker({children, type = CONST.ATTACHMENT_PICKER_TYPE.FILE, a
}}
accept={acceptedFileTypes ? getAcceptableFileTypesFromAList(acceptedFileTypes) : getAcceptableFileTypes(type)}
/>
+ {/* eslint-disable-next-line react-compiler/react-compiler */}
{children({
openPicker: ({onPicked: newOnPicked, onCanceled: newOnCanceled = () => {}}) => {
onPicked.current = newOnPicked;
diff --git a/src/components/Attachments/AttachmentCarousel/index.tsx b/src/components/Attachments/AttachmentCarousel/index.tsx
index a1408aaf400e..d9c4f7e93fbe 100644
--- a/src/components/Attachments/AttachmentCarousel/index.tsx
+++ b/src/components/Attachments/AttachmentCarousel/index.tsx
@@ -255,6 +255,7 @@ function AttachmentCarousel({report, source, onNavigate, setDownloadButtonVisibi
scrollTo(scrollRef, newIndex * cellWidth, 0, true);
})
+ // eslint-disable-next-line react-compiler/react-compiler
.withRef(pagerRef as MutableRefObject),
[attachments.length, canUseTouchScreen, cellWidth, page, scale, scrollRef],
);
diff --git a/src/components/AvatarCropModal/AvatarCropModal.tsx b/src/components/AvatarCropModal/AvatarCropModal.tsx
index 1a606b35f6d2..dca0d08d11d5 100644
--- a/src/components/AvatarCropModal/AvatarCropModal.tsx
+++ b/src/components/AvatarCropModal/AvatarCropModal.tsx
@@ -336,6 +336,7 @@ function AvatarCropModal({imageUri = '', imageName = '', imageType = '', onClose
}
const newSliderValue = clamp(locationX, [0, sliderContainerSize]);
const newScale = newScaleValue(newSliderValue, sliderContainerSize);
+ // eslint-disable-next-line react-compiler/react-compiler
translateSlider.value = newSliderValue;
const differential = newScale / scale.value;
scale.value = newScale;
diff --git a/src/components/ButtonWithDropdownMenu/index.tsx b/src/components/ButtonWithDropdownMenu/index.tsx
index e1d7beb850d0..c44054c15445 100644
--- a/src/components/ButtonWithDropdownMenu/index.tsx
+++ b/src/components/ButtonWithDropdownMenu/index.tsx
@@ -53,6 +53,7 @@ function ButtonWithDropdownMenu({
const [popoverAnchorPosition, setPopoverAnchorPosition] = useState(null);
const {windowWidth, windowHeight} = useWindowDimensions();
const dropdownAnchor = useRef(null);
+ // eslint-disable-next-line react-compiler/react-compiler
const dropdownButtonRef = isSplitButton ? buttonRef : mergeRefs(buttonRef, dropdownAnchor);
const selectedItem = options.at(selectedItemIndex) ?? options.at(0);
const innerStyleDropButton = StyleUtils.getDropDownButtonHeight(buttonSize);
@@ -200,6 +201,7 @@ function ButtonWithDropdownMenu({
onItemSelected={() => setIsMenuVisible(false)}
anchorPosition={shouldUseStyleUtilityForAnchorPosition ? styles.popoverButtonDropdownMenuOffset(windowWidth) : popoverAnchorPosition}
shouldShowSelectedItemCheck={shouldShowSelectedItemCheck}
+ // eslint-disable-next-line react-compiler/react-compiler
anchorRef={nullCheckRef(dropdownAnchor)}
withoutOverlay
anchorAlignment={anchorAlignment}
diff --git a/src/components/ButtonWithDropdownMenu/types.ts b/src/components/ButtonWithDropdownMenu/types.ts
index da6b3d93433d..766c0df950b4 100644
--- a/src/components/ButtonWithDropdownMenu/types.ts
+++ b/src/components/ButtonWithDropdownMenu/types.ts
@@ -25,6 +25,7 @@ type DropdownOption = {
iconWidth?: number;
iconHeight?: number;
iconDescription?: string;
+ additionalIconStyles?: StyleProp;
onSelected?: () => void;
disabled?: boolean;
iconFill?: string;
diff --git a/src/components/DisplayNames/DisplayNamesWithTooltip.tsx b/src/components/DisplayNames/DisplayNamesWithTooltip.tsx
index 86edbb3b4c5e..acc1a7f40b47 100644
--- a/src/components/DisplayNames/DisplayNamesWithTooltip.tsx
+++ b/src/components/DisplayNames/DisplayNamesWithTooltip.tsx
@@ -15,6 +15,7 @@ function DisplayNamesWithToolTip({shouldUseFullTitle, fullTitle, displayNamesWit
const styles = useThemeStyles();
const containerRef = useRef(null);
const childRefs = useRef([]);
+ // eslint-disable-next-line react-compiler/react-compiler
const isEllipsisActive = !!containerRef.current?.offsetWidth && !!containerRef.current?.scrollWidth && containerRef.current.offsetWidth < containerRef.current.scrollWidth;
/**
diff --git a/src/components/DragAndDrop/NoDropZone/index.tsx b/src/components/DragAndDrop/NoDropZone/index.tsx
index 3438bfff7c05..b55db0e6c212 100644
--- a/src/components/DragAndDrop/NoDropZone/index.tsx
+++ b/src/components/DragAndDrop/NoDropZone/index.tsx
@@ -11,12 +11,14 @@ function NoDropZone({children}: NoDropZoneProps) {
const noDropZone = useRef(null);
useDragAndDrop({
+ // eslint-disable-next-line react-compiler/react-compiler
dropZone: htmlDivElementRef(noDropZone),
shouldAllowDrop: false,
});
return (
diff --git a/src/components/DragAndDrop/Provider/index.tsx b/src/components/DragAndDrop/Provider/index.tsx
index 1011fa161312..a403c7ecca0d 100644
--- a/src/components/DragAndDrop/Provider/index.tsx
+++ b/src/components/DragAndDrop/Provider/index.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable react-compiler/react-compiler */
import {PortalHost} from '@gorhom/portal';
import {Str} from 'expensify-common';
import React, {useCallback, useEffect, useMemo, useRef} from 'react';
diff --git a/src/components/DraggableList/useDraggableInPortal.ts b/src/components/DraggableList/useDraggableInPortal.ts
index 3b4610ce1e5e..e4e01ee4b133 100644
--- a/src/components/DraggableList/useDraggableInPortal.ts
+++ b/src/components/DraggableList/useDraggableInPortal.ts
@@ -7,6 +7,7 @@ type DraggableInPortal = {
};
export default function useDraggableInPortal({shouldUsePortal}: DraggableInPortal): (render: DraggableChildrenFn) => DraggableChildrenFn {
+ // eslint-disable-next-line react-compiler/react-compiler
const element = useRef(document.createElement('div')).current;
useEffect(() => {
diff --git a/src/components/EmojiPicker/EmojiPicker.tsx b/src/components/EmojiPicker/EmojiPicker.tsx
index 706265f2e11a..79af5bc0a4f2 100644
--- a/src/components/EmojiPicker/EmojiPicker.tsx
+++ b/src/components/EmojiPicker/EmojiPicker.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable react-compiler/react-compiler */
import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react';
import type {ForwardedRef, RefObject} from 'react';
import {Dimensions, View} from 'react-native';
@@ -117,7 +118,7 @@ function EmojiPicker({viewportOffsetTop}: EmojiPickerProps, ref: ForwardedRef open(onPicked, newOnCanceled),
});
+ // eslint-disable-next-line react-compiler/react-compiler
return <>{renderChildren()}>;
}
diff --git a/src/components/FilePicker/index.tsx b/src/components/FilePicker/index.tsx
index 2514a16053bd..3d4242d22420 100644
--- a/src/components/FilePicker/index.tsx
+++ b/src/components/FilePicker/index.tsx
@@ -65,6 +65,7 @@ function FilePicker({children, acceptableFileTypes = ''}: FilePickerProps): Reac
}}
accept={acceptableFileTypes}
/>
+ {/* eslint-disable-next-line react-compiler/react-compiler */}
{children({
openPicker: ({onPicked: newOnPicked, onCanceled: newOnCanceled = () => {}}) => {
onPicked.current = newOnPicked;
diff --git a/src/components/FlatList/index.tsx b/src/components/FlatList/index.tsx
index 9bca23efb384..be0227375470 100644
--- a/src/components/FlatList/index.tsx
+++ b/src/components/FlatList/index.tsx
@@ -50,6 +50,7 @@ function MVCPFlatList({maintainVisibleContentPosition, horizontal = false
const lastScrollOffsetRef = useRef(0);
const isListRenderedRef = useRef(false);
const mvcpAutoscrollToTopThresholdRef = useRef(mvcpAutoscrollToTopThreshold);
+ // eslint-disable-next-line react-compiler/react-compiler
mvcpAutoscrollToTopThresholdRef.current = mvcpAutoscrollToTopThreshold;
const getScrollOffset = useCallback((): number => {
diff --git a/src/components/Form/InputWrapper.tsx b/src/components/Form/InputWrapper.tsx
index f54009852b22..a01c1ca3c136 100644
--- a/src/components/Form/InputWrapper.tsx
+++ b/src/components/Form/InputWrapper.tsx
@@ -76,6 +76,7 @@ function InputWrapper(p
const {registerInput} = useContext(FormContext);
const {shouldSetTouchedOnBlurOnly, blurOnSubmit, shouldSubmitForm} = computeComponentSpecificRegistrationParams(props as InputComponentBaseProps);
+ // eslint-disable-next-line react-compiler/react-compiler
const {key, ...registerInputProps} = registerInput(inputID, shouldSubmitForm, {ref, valueType, ...rest, shouldSetTouchedOnBlurOnly, blurOnSubmit});
return (
diff --git a/src/components/FormElement/index.tsx b/src/components/FormElement/index.tsx
index 9a344eb3c39c..a4b864d69466 100644
--- a/src/components/FormElement/index.tsx
+++ b/src/components/FormElement/index.tsx
@@ -13,6 +13,7 @@ const preventFormDefault = (event: SubmitEvent) => {
function FormElement(props: ViewProps, outerRef: ForwardedRef) {
const formRef = useRef(null);
+ // eslint-disable-next-line react-compiler/react-compiler
const mergedRef = mergeRefs(formRef, outerRef);
useEffect(() => {
diff --git a/src/components/Hoverable/ActiveHoverable.tsx b/src/components/Hoverable/ActiveHoverable.tsx
index fd3d4f3d19e8..9bc0e846aaf1 100644
--- a/src/components/Hoverable/ActiveHoverable.tsx
+++ b/src/components/Hoverable/ActiveHoverable.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable react-compiler/react-compiler */
import type {Ref} from 'react';
import {cloneElement, forwardRef, useCallback, useEffect, useMemo, useRef, useState} from 'react';
import {DeviceEventEmitter} from 'react-native';
diff --git a/src/components/Hoverable/index.tsx b/src/components/Hoverable/index.tsx
index e3357fd963c4..3ff28a8da451 100644
--- a/src/components/Hoverable/index.tsx
+++ b/src/components/Hoverable/index.tsx
@@ -16,6 +16,7 @@ function Hoverable({isDisabled, ...props}: HoverableProps, ref: Ref
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
if (isDisabled || !hasHoverSupport()) {
const child = getReturnValue(props.children, false);
+ // eslint-disable-next-line react-compiler/react-compiler
return cloneElement(child, {ref: mergeRefs(ref, child.ref)});
}
diff --git a/src/components/ImageView/index.tsx b/src/components/ImageView/index.tsx
index e12be53d01ae..266ed2eed16a 100644
--- a/src/components/ImageView/index.tsx
+++ b/src/components/ImageView/index.tsx
@@ -210,6 +210,7 @@ function ImageView({isAuthTokenRequired = false, url, fileName, onError}: ImageV
}
return (
{translate('spreadsheet.upload')}
diff --git a/src/components/KYCWall/BaseKYCWall.tsx b/src/components/KYCWall/BaseKYCWall.tsx
index 7d1e6614c716..b846449faafd 100644
--- a/src/components/KYCWall/BaseKYCWall.tsx
+++ b/src/components/KYCWall/BaseKYCWall.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable react-compiler/react-compiler */
import React, {useCallback, useEffect, useRef, useState} from 'react';
import {Dimensions} from 'react-native';
import type {EmitterSubscription, GestureResponderEvent, View} from 'react-native';
diff --git a/src/components/LHNOptionsList/OptionRowLHNData.tsx b/src/components/LHNOptionsList/OptionRowLHNData.tsx
index 8253a1708c81..3c40210a5d99 100644
--- a/src/components/LHNOptionsList/OptionRowLHNData.tsx
+++ b/src/components/LHNOptionsList/OptionRowLHNData.tsx
@@ -54,10 +54,13 @@ function OptionRowLHNData({
transactionViolations,
invoiceReceiverPolicy,
});
+ // eslint-disable-next-line react-compiler/react-compiler
if (deepEqual(item, optionItemRef.current)) {
+ // eslint-disable-next-line react-compiler/react-compiler
return optionItemRef.current;
}
+ // eslint-disable-next-line react-compiler/react-compiler
optionItemRef.current = item;
return item;
diff --git a/src/components/MagicCodeInput.tsx b/src/components/MagicCodeInput.tsx
index ce4f3380a9b7..ff2db66dbc4a 100644
--- a/src/components/MagicCodeInput.tsx
+++ b/src/components/MagicCodeInput.tsx
@@ -212,6 +212,7 @@ function MagicCodeInput(
*/
const tapGesture = Gesture.Tap()
.runOnJS(true)
+ // eslint-disable-next-line react-compiler/react-compiler
.onBegin((event) => {
const index = Math.floor(event.x / (inputWidth.current / maxLength));
shouldFocusLast.current = false;
diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx
index 526a090afb73..a40fd925cd2e 100644
--- a/src/components/MenuItem.tsx
+++ b/src/components/MenuItem.tsx
@@ -114,6 +114,9 @@ type MenuItemBaseProps = {
/** Any additional styles to pass to the icon container. */
iconStyles?: StyleProp;
+ /** Additional styles to pass to the icon itself */
+ additionalIconStyles?: StyleProp;
+
/** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */
fallbackIcon?: IconAsset;
@@ -433,6 +436,7 @@ function MenuItem(
tooltipShiftHorizontal = 0,
tooltipShiftVertical = 0,
renderTooltipContent,
+ additionalIconStyles,
shouldShowSelectedItemCheck = false,
onHideTooltip,
shouldIconUseAutoWidthStyle = false,
@@ -663,6 +667,7 @@ function MenuItem(
isPaneMenu,
)
}
+ additionalStyles={additionalIconStyles}
/>
) : (
({options, initializeOptions, areOptionsInitialized: areOptionsInitialized.current}), [options, initializeOptions])}>
{children}
diff --git a/src/components/PopoverProvider/index.tsx b/src/components/PopoverProvider/index.tsx
index 82f3c6c7d61a..b59d1604a5aa 100644
--- a/src/components/PopoverProvider/index.tsx
+++ b/src/components/PopoverProvider/index.tsx
@@ -117,6 +117,7 @@ function PopoverContextProvider(props: PopoverContextProps) {
() => ({
onOpen,
close: closePopover,
+ // eslint-disable-next-line react-compiler/react-compiler
popover: activePopoverRef.current,
isOpen,
}),
diff --git a/src/components/Pressable/GenericPressable/implementation/BaseGenericPressable.tsx b/src/components/Pressable/GenericPressable/implementation/BaseGenericPressable.tsx
index 84a595a7bf05..1765560eaae3 100644
--- a/src/components/Pressable/GenericPressable/implementation/BaseGenericPressable.tsx
+++ b/src/components/Pressable/GenericPressable/implementation/BaseGenericPressable.tsx
@@ -148,6 +148,7 @@ function GenericPressable(
onLayout={shouldUseAutoHitSlop ? onLayout : undefined}
ref={ref as ForwardedRef}
disabled={fullDisabled}
+ // eslint-disable-next-line react-compiler/react-compiler
onPress={!isDisabled ? singleExecution(onPressHandler) : undefined}
onLongPress={!isDisabled && onLongPress ? onLongPressHandler : undefined}
onKeyDown={!isDisabled ? onKeyDown : undefined}
diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.tsx b/src/components/Reactions/ReportActionItemEmojiReactions.tsx
index 943158607db4..bc5f48f9001c 100644
--- a/src/components/Reactions/ReportActionItemEmojiReactions.tsx
+++ b/src/components/Reactions/ReportActionItemEmojiReactions.tsx
@@ -104,6 +104,7 @@ function ReportActionItemEmojiReactions({
if (reactionCount === 0) {
return null;
}
+ // eslint-disable-next-line react-compiler/react-compiler
totalReactionCount += reactionCount;
const onPress = () => {
diff --git a/src/components/ReportActionItem/ExportWithDropdownMenu.tsx b/src/components/ReportActionItem/ExportWithDropdownMenu.tsx
index 2f96b9d7dfaf..50e753b25755 100644
--- a/src/components/ReportActionItem/ExportWithDropdownMenu.tsx
+++ b/src/components/ReportActionItem/ExportWithDropdownMenu.tsx
@@ -12,6 +12,7 @@ import * as ReportActions from '@libs/actions/Report';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReportUtils from '@libs/ReportUtils';
import type {ExportType} from '@pages/home/report/ReportDetailsExportPage';
+import variables from '@styles/variables';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Policy, Report} from '@src/types/onyx';
@@ -55,6 +56,9 @@ function ExportWithDropdownMenu({
icon: iconToDisplay,
disabled: !canBeExported,
displayInDefaultIconColor: true,
+ iconWidth: variables.iconSizeMenuItem,
+ iconHeight: variables.iconSizeMenuItem,
+ additionalIconStyles: styles.integrationIcon,
};
const options = [
{
diff --git a/src/components/ScreenWrapper.tsx b/src/components/ScreenWrapper.tsx
index 152a14fc3eb7..4a7aab4ad299 100644
--- a/src/components/ScreenWrapper.tsx
+++ b/src/components/ScreenWrapper.tsx
@@ -162,6 +162,7 @@ function ScreenWrapper(
const isKeyboardShownRef = useRef(false);
+ // eslint-disable-next-line react-compiler/react-compiler
isKeyboardShownRef.current = keyboardState?.isKeyboardShown ?? false;
const panResponder = useRef(
diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx
index 5ff0882ea026..4a1a67505d91 100644
--- a/src/components/Search/index.tsx
+++ b/src/components/Search/index.tsx
@@ -172,10 +172,13 @@ function Search({queryJSON, onSearchListScroll, contentContainerStyle}: SearchPr
});
// save last non-empty search results to avoid ugly flash of loading screen when hash changes and onyx returns empty data
+ // eslint-disable-next-line react-compiler/react-compiler
if (currentSearchResults?.data && currentSearchResults !== lastSearchResultsRef.current) {
+ // eslint-disable-next-line react-compiler/react-compiler
lastSearchResultsRef.current = currentSearchResults;
}
+ // eslint-disable-next-line react-compiler/react-compiler
const searchResults = currentSearchResults?.data ? currentSearchResults : lastSearchResultsRef.current;
const {newSearchResultKey, handleSelectionListScroll} = useSearchHighlightAndScroll({
diff --git a/src/components/Search/types.ts b/src/components/Search/types.ts
index 4f96090be9d0..5db88ae47e8d 100644
--- a/src/components/Search/types.ts
+++ b/src/components/Search/types.ts
@@ -58,9 +58,10 @@ type QueryFilter = {
type AdvancedFiltersKeys = ValueOf;
-type QueryFilters = {
- [K in AdvancedFiltersKeys]?: QueryFilter[];
-};
+type QueryFilters = Array<{
+ key: AdvancedFiltersKeys;
+ filters: QueryFilter[];
+}>;
type SearchQueryString = string;
diff --git a/src/components/SwipeableView/index.native.tsx b/src/components/SwipeableView/index.native.tsx
index e5b6d371e606..4376585c6f0a 100644
--- a/src/components/SwipeableView/index.native.tsx
+++ b/src/components/SwipeableView/index.native.tsx
@@ -7,6 +7,7 @@ function SwipeableView({children, onSwipeDown}: SwipeableViewProps) {
const minimumPixelDistance = CONST.COMPOSER_MAX_HEIGHT;
const oldYRef = useRef(0);
const panResponder = useRef(
+ // eslint-disable-next-line react-compiler/react-compiler
PanResponder.create({
// The PanResponder gets focus only when the y-axis movement is over minimumPixelDistance & swipe direction is downwards
onMoveShouldSetPanResponderCapture: (_event, gestureState) => {
@@ -22,10 +23,8 @@ function SwipeableView({children, onSwipeDown}: SwipeableViewProps) {
}),
).current;
- return (
- // eslint-disable-next-line react/jsx-props-no-spreading
- {children}
- );
+ // eslint-disable-next-line react/jsx-props-no-spreading, react-compiler/react-compiler
+ return {children};
}
SwipeableView.displayName = 'SwipeableView';
diff --git a/src/components/Switch.tsx b/src/components/Switch.tsx
index 1ddc65bbd0fc..d2b3f2c3a4ac 100644
--- a/src/components/Switch.tsx
+++ b/src/components/Switch.tsx
@@ -69,6 +69,7 @@ function Switch({isOn, onToggle, accessibilityLabel, disabled, showLockIcon, dis
hoverDimmingValue={1}
pressDimmingValue={0.8}
>
+ {/* eslint-disable-next-line react-compiler/react-compiler */}
{(!!disabled || !!showLockIcon) && (
StyleUtils.getTooltipStyles({
+ // eslint-disable-next-line react-compiler/react-compiler
tooltip: rootWrapper.current,
currentSize: animation,
windowWidth,
diff --git a/src/components/Tooltip/BaseGenericTooltip/index.tsx b/src/components/Tooltip/BaseGenericTooltip/index.tsx
index 4477c991e3ac..28f2458699b7 100644
--- a/src/components/Tooltip/BaseGenericTooltip/index.tsx
+++ b/src/components/Tooltip/BaseGenericTooltip/index.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable react-compiler/react-compiler */
import React, {useLayoutEffect, useMemo, useRef, useState} from 'react';
import ReactDOM from 'react-dom';
import {Animated, View} from 'react-native';
diff --git a/src/components/Tooltip/GenericTooltip.tsx b/src/components/Tooltip/GenericTooltip.tsx
index a59819a77f6c..7309359b8e0c 100644
--- a/src/components/Tooltip/GenericTooltip.tsx
+++ b/src/components/Tooltip/GenericTooltip.tsx
@@ -157,6 +157,7 @@ function GenericTooltip({
// Skip the tooltip and return the children if the text is empty, we don't have a render function.
if (StringUtils.isEmptyString(text) && renderTooltipContent == null) {
+ // eslint-disable-next-line react-compiler/react-compiler
return children({isVisible, showTooltip, hideTooltip, updateTargetBounds});
}
@@ -164,6 +165,7 @@ function GenericTooltip({
<>
{isRendered && (
)}
-
+ {/* eslint-disable-next-line react-compiler/react-compiler */}
{children({isVisible, showTooltip, hideTooltip, updateTargetBounds})}
>
);
diff --git a/src/components/Tooltip/PopoverAnchorTooltip.tsx b/src/components/Tooltip/PopoverAnchorTooltip.tsx
index 5eb1f45dafcc..1af0f01cf957 100644
--- a/src/components/Tooltip/PopoverAnchorTooltip.tsx
+++ b/src/components/Tooltip/PopoverAnchorTooltip.tsx
@@ -9,7 +9,7 @@ function PopoverAnchorTooltip({shouldRender = true, children, ...props}: Tooltip
const tooltipRef = useRef(null);
const isPopoverRelatedToTooltipOpen = useMemo(() => {
- // eslint-disable-next-line @typescript-eslint/dot-notation
+ // eslint-disable-next-line @typescript-eslint/dot-notation, react-compiler/react-compiler
const tooltipNode = (tooltipRef.current?.['_childNode'] as Node | undefined) ?? null;
if (
diff --git a/src/components/VideoPlayerContexts/VideoPopoverMenuContext.tsx b/src/components/VideoPlayerContexts/VideoPopoverMenuContext.tsx
index 966f49e45a93..42373da91789 100644
--- a/src/components/VideoPlayerContexts/VideoPopoverMenuContext.tsx
+++ b/src/components/VideoPlayerContexts/VideoPopoverMenuContext.tsx
@@ -43,6 +43,7 @@ function VideoPopoverMenuContextProvider({children}: ChildrenProps) {
const items: PopoverMenuItem[] = [];
if (!isOffline && !isLocalFile) {
+ // eslint-disable-next-line react-compiler/react-compiler
items.push({
icon: Expensicons.Download,
text: translate('common.download'),
diff --git a/src/hooks/useCancellationType.ts b/src/hooks/useCancellationType.ts
index bc34f5feea6f..2a77bfd8ddc1 100644
--- a/src/hooks/useCancellationType.ts
+++ b/src/hooks/useCancellationType.ts
@@ -21,6 +21,7 @@ function useCancellationType(): CancellationType | undefined {
}
// There are no new items in the cancellation details NVP
+ // eslint-disable-next-line react-compiler/react-compiler
if (previousCancellationDetails.current?.length === cancellationDetails?.length) {
return;
}
diff --git a/src/hooks/useDebounce.ts b/src/hooks/useDebounce.ts
index b5e3f333c44a..458949264ff0 100644
--- a/src/hooks/useDebounce.ts
+++ b/src/hooks/useDebounce.ts
@@ -42,5 +42,6 @@ export default function useDebounce(func: T, wait: nu
}
}, []);
+ // eslint-disable-next-line react-compiler/react-compiler
return debounceCallback as T;
}
diff --git a/src/hooks/useDebouncedState.ts b/src/hooks/useDebouncedState.ts
index 8d7d43cb6f9c..b004c308a375 100644
--- a/src/hooks/useDebouncedState.ts
+++ b/src/hooks/useDebouncedState.ts
@@ -20,6 +20,7 @@ import CONST from '@src/CONST';
function useDebouncedState(initialValue: T, delay: number = CONST.TIMING.USE_DEBOUNCED_STATE_DELAY): [T, T, (value: T) => void] {
const [value, setValue] = useState(initialValue);
const [debouncedValue, setDebouncedValue] = useState(initialValue);
+ // eslint-disable-next-line react-compiler/react-compiler
const debouncedSetDebouncedValue = useRef(debounce(setDebouncedValue, delay)).current;
useEffect(() => () => debouncedSetDebouncedValue.cancel(), [debouncedSetDebouncedValue]);
diff --git a/src/hooks/useDeepCompareRef.ts b/src/hooks/useDeepCompareRef.ts
index 7511c1516a1d..9a226da44767 100644
--- a/src/hooks/useDeepCompareRef.ts
+++ b/src/hooks/useDeepCompareRef.ts
@@ -17,8 +17,11 @@ import {useRef} from 'react';
*/
export default function useDeepCompareRef(value: T): T | undefined {
const ref = useRef();
+ // eslint-disable-next-line react-compiler/react-compiler
if (!isEqual(value, ref.current)) {
+ // eslint-disable-next-line react-compiler/react-compiler
ref.current = value;
}
+ // eslint-disable-next-line react-compiler/react-compiler
return ref.current;
}
diff --git a/src/hooks/useNetwork.ts b/src/hooks/useNetwork.ts
index 950d0592b59c..69aaebc415a5 100644
--- a/src/hooks/useNetwork.ts
+++ b/src/hooks/useNetwork.ts
@@ -10,6 +10,7 @@ type UseNetwork = {isOffline: boolean};
export default function useNetwork({onReconnect = () => {}}: UseNetworkProps = {}): UseNetwork {
const callback = useRef(onReconnect);
+ // eslint-disable-next-line react-compiler/react-compiler
callback.current = onReconnect;
const {isOffline, networkStatus} = useContext(NetworkContext) ?? {...CONST.DEFAULT_NETWORK_DATA, networkStatus: CONST.NETWORK.NETWORK_STATUS.UNKNOWN};
diff --git a/src/hooks/usePrevious.ts b/src/hooks/usePrevious.ts
index 279e8e4a3bf4..e5db9bffd39c 100644
--- a/src/hooks/usePrevious.ts
+++ b/src/hooks/usePrevious.ts
@@ -8,5 +8,6 @@ export default function usePrevious(value: T): T {
useEffect(() => {
ref.current = value;
}, [value]);
+ // eslint-disable-next-line react-compiler/react-compiler
return ref.current;
}
diff --git a/src/hooks/useSingleExecution/index.native.ts b/src/hooks/useSingleExecution/index.native.ts
index 16a98152def1..736a79ab1810 100644
--- a/src/hooks/useSingleExecution/index.native.ts
+++ b/src/hooks/useSingleExecution/index.native.ts
@@ -10,6 +10,7 @@ export default function useSingleExecution() {
const [isExecuting, setIsExecuting] = useState(false);
const isExecutingRef = useRef();
+ // eslint-disable-next-line react-compiler/react-compiler
isExecutingRef.current = isExecuting;
const singleExecution = useCallback(
diff --git a/src/hooks/useSubStep/index.ts b/src/hooks/useSubStep/index.ts
index eb4a30037ab0..e59e18cf85b5 100644
--- a/src/hooks/useSubStep/index.ts
+++ b/src/hooks/useSubStep/index.ts
@@ -59,9 +59,11 @@ export default function useSubStep({bodyContent, on
setScreenIndex(bodyContent.length - 1);
}, [bodyContent]);
+ // eslint-disable-next-line react-compiler/react-compiler
return {
// eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style
componentToRender: bodyContent.at(screenIndex) as ComponentType,
+ // eslint-disable-next-line react-compiler/react-compiler
isEditing: isEditing.current,
screenIndex,
prevScreen,
diff --git a/src/languages/en.ts b/src/languages/en.ts
index d32d52ca5688..5daecbc98e5f 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -2431,12 +2431,9 @@ const translations = {
"We'll create an itemized vendor bill for each Expensify report and add it to the account below. If this period is closed, we'll post to the 1st of the next open period.",
deepDiveExpensifyCard: 'Expensify Card transactions will automatically export to an "Expensify Card Liability Account" created with',
deepDiveExpensifyCardIntegration: 'our integration.',
- outOfPocketLocationEnabledDescription:
- 'QuickBooks Desktop doesn’t support locations on vendor bills or checks. As you have locations enabled on your workspace, these export options are unavailable.',
outOfPocketTaxEnabledDescription:
"QuickBooks Desktop doesn't support taxes on journal entry exports. As you have taxes enabled on your workspace, this export option is unavailable.",
outOfPocketTaxEnabledError: 'Journal entries are unavailable when taxes are enabled. Please choose a different export option.',
- outOfPocketLocationEnabledError: 'Vendor bills are unavailable when locations are enabled. Please choose a different export option.',
accounts: {
[CONST.QUICKBOOKS_DESKTOP_NON_REIMBURSABLE_EXPORT_ACCOUNT_TYPE.CREDIT_CARD]: 'Credit card',
[CONST.QUICKBOOKS_DESKTOP_REIMBURSABLE_ACCOUNT_TYPE.VENDOR_BILL]: 'Vendor bill',
@@ -2504,10 +2501,8 @@ const translations = {
customersDescription: 'Choose how to handle QuickBooks Online customers/projects in Expensify.',
locationsDescription: 'Choose how to handle QuickBooks Online locations in Expensify.',
taxesDescription: 'Choose how to handle QuickBooks Online taxes in Expensify.',
- locationsAdditionalDescription:
- 'QuickBooks Online doesn’t support locations on vendor bills or checks. As you have locations enabled on your workspace, these export options are unavailable.',
- outOfPocketLocationEnabledDescription:
- 'QuickBooks Online doesn’t support locations on vendor bills or checks. As you have locations enabled on your workspace, these export options are unavailable.',
+ locationsLineItemsRestrictionDescription:
+ "QuickBooks Online does not support Locations at the line-level for Checks or Vendor Bills. If you'd like to have locations at the line-level, make sure you are using Journal Entries and Credit/Debit Card expenses.",
taxesJournalEntrySwitchNote: "QuickBooks Online doesn't support taxes on journal entries. Please change your export option to vendor bill or check.",
exportDescription: 'Configure how Expensify data exports to QuickBooks Online.',
date: 'Export date',
@@ -2557,7 +2552,6 @@ const translations = {
outOfPocketTaxEnabledDescription:
"QuickBooks Online doesn't support taxes on journal entry exports. As you have taxes enabled on your workspace, this export option is unavailable.",
outOfPocketTaxEnabledError: 'Journal entries are unavailable when taxes are enabled. Please choose a different export option.',
- outOfPocketLocationEnabledError: 'Vendor bills are unavailable when locations are enabled. Please choose a different export option.',
advancedConfig: {
autoSyncDescription: 'Expensify will automatically sync with QuickBooks Online every day.',
inviteEmployees: 'Invite employees',
@@ -2612,8 +2606,9 @@ const translations = {
notImported: 'Not imported',
notConfigured: 'Not configured',
trackingCategoriesOptions: {
- default: 'Xero contact default',
- tag: 'Tags',
+ [CONST.XERO_CONFIG.TRACKING_CATEGORY_OPTIONS.DEFAULT]: 'Xero contact default',
+ [CONST.XERO_CONFIG.TRACKING_CATEGORY_OPTIONS.TAG]: 'Tags',
+ [CONST.XERO_CONFIG.TRACKING_CATEGORY_OPTIONS.REPORT_FIELD]: 'Report fields',
},
exportDescription: 'Configure how Expensify data exports to Xero.',
purchaseBill: 'Purchase bill',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index b7725389e18f..c38e9052bd60 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -2454,12 +2454,9 @@ const translations = {
'Crearemos una factura de proveedor desglosada para cada informe de Expensify y la añadiremos a la cuenta a continuación. Si este periodo está cerrado, lo contabilizaremos el 1º del siguiente periodo abierto.',
deepDiveExpensifyCard: 'Las transacciones de la Tarjeta Expensify se exportarán automáticamente a una "Cuenta de Responsabilidad de la Tarjeta Expensify" creada con',
deepDiveExpensifyCardIntegration: 'nuestra integración.',
- outOfPocketLocationEnabledDescription:
- 'QuickBooks Desktop no permite lugares en facturas de proveedores o cheques. Como tienes activadas los lugares en tu espacio de trabajo, estas opciones de exportación no están disponibles.',
outOfPocketTaxEnabledDescription:
'QuickBooks Desktop no admite impuestos en las exportaciones de asientos contables. Como tienes impuestos habilitados en tu espacio de trabajo, esta opción de exportación no está disponible.',
outOfPocketTaxEnabledError: 'Los asientos contables no están disponibles cuando los impuestos están habilitados. Por favor, selecciona otra opción de exportación.',
- outOfPocketLocationEnabledError: 'Las facturas de proveedores no están disponibles cuando las ubicaciones están habilitadas. Por favor, selecciona otra opción de exportación.',
accounts: {
[CONST.QUICKBOOKS_DESKTOP_NON_REIMBURSABLE_EXPORT_ACCOUNT_TYPE.CREDIT_CARD]: 'Tarjeta de crédito',
[CONST.QUICKBOOKS_DESKTOP_REIMBURSABLE_ACCOUNT_TYPE.VENDOR_BILL]: 'Factura del proveedor',
@@ -2528,10 +2525,10 @@ const translations = {
classesDescription: 'Elige cómo gestionar las clases de QuickBooks Online en Expensify.',
customersDescription: 'Elige cómo gestionar los clientes/proyectos de QuickBooks Online en Expensify.',
locationsDescription: 'Elige cómo gestionar los lugares de QuickBooks Online en Expensify.',
+ locationsLineItemsRestrictionDescription:
+ 'QuickBooks Online no admite Ubicaciones a nivel de línea para cheques o facturas de proveedores. Si deseas tener ubicaciones a nivel de línea, asegúrate de estar usando asientos contables y gastos con tarjetas de crédito/débito.',
taxesDescription: 'Elige cómo gestionar los impuestos de QuickBooks Online en Expensify.',
taxesJournalEntrySwitchNote: 'QuickBooks Online no permite impuestos en los asientos contables. Por favor, cambia la opción de exportación a factura de proveedor o cheque.',
- locationsAdditionalDescription:
- 'QuickBooks Online no permite lugares en facturas de proveedores o cheques. Como tienes activadas los lugares en tu espacio de trabajo, estas opciones de exportación no están disponibles.',
exportInvoices: 'Exportar facturas a',
exportDescription: 'Configura cómo se exportan los datos de Expensify a QuickBooks Online.',
date: 'Fecha de exportación',
@@ -2580,10 +2577,6 @@ const translations = {
outOfPocketTaxEnabledDescription:
'QuickBooks Online no permite impuestos en las exportaciones de entradas a los asientos contables. Como tienes los impuestos activados en tu espacio de trabajo, esta opción de exportación no está disponible.',
outOfPocketTaxEnabledError: 'La anotacion en el diario no está disponible cuando los impuestos están activados. Por favor, selecciona otra opción de exportación diferente.',
- outOfPocketLocationEnabledError:
- 'Las facturas de proveedores no están disponibles cuando las ubicaciones están activadas. Por favor, selecciona otra opción de exportación diferente.',
- outOfPocketLocationEnabledDescription:
- 'QuickBooks Online no permite lugares en facturas de proveedores o cheques. Como tienes activadas los lugares en tu espacio de trabajo, estas opciones de exportación no están disponibles.',
advancedConfig: {
autoSyncDescription: 'Expensify se sincronizará automáticamente con QuickBooks Online todos los días.',
@@ -2644,8 +2637,9 @@ const translations = {
notImported: 'No importado',
notConfigured: 'No configurado',
trackingCategoriesOptions: {
- default: 'Contacto de Xero por defecto',
- tag: 'Etiquetas',
+ [CONST.XERO_CONFIG.TRACKING_CATEGORY_OPTIONS.DEFAULT]: 'Contacto de Xero por defecto',
+ [CONST.XERO_CONFIG.TRACKING_CATEGORY_OPTIONS.TAG]: 'Etiquetas',
+ [CONST.XERO_CONFIG.TRACKING_CATEGORY_OPTIONS.REPORT_FIELD]: 'Campos de informes',
},
exportDescription: 'Configura cómo se exportan los datos de Expensify a Xero.',
purchaseBill: 'Factura de compra',
diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx
index 68cc7e748413..85c027f06d95 100644
--- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx
+++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx
@@ -244,6 +244,8 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
let initialReportID: string | undefined;
const isInitialRender = useRef(true);
+
+ // eslint-disable-next-line react-compiler/react-compiler
if (isInitialRender.current) {
Timing.start(CONST.TIMING.HOMEPAGE_INITIAL_RENDER);
@@ -257,6 +259,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
initialReportID = initialReport?.reportID ?? '';
}
+ // eslint-disable-next-line react-compiler/react-compiler
isInitialRender.current = false;
}
diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
index 2ee1f7d19be9..35f67e0253c6 100644
--- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
+++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
@@ -381,6 +381,12 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/accounting/qbo/import/QuickbooksTaxesPage').default,
[SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_LOCATIONS]: () => require('../../../../pages/workspace/accounting/qbo/import/QuickbooksLocationsPage').default,
[SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_CLASSES]: () => require('../../../../pages/workspace/accounting/qbo/import/QuickbooksClassesPage').default,
+ [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_CLASSES_DISPLAYED_AS]: () =>
+ require('../../../../pages/workspace/accounting/qbo/import/QuickbooksClassesDisplayedAsPage').default,
+ [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_CUSTOMERS_DISPLAYED_AS]: () =>
+ require('../../../../pages/workspace/accounting/qbo/import/QuickbooksCustomersDisplayedAsPage').default,
+ [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_LOCATIONS_DISPLAYED_AS]: () =>
+ require('../../../../pages/workspace/accounting/qbo/import/QuickbooksLocationsDisplayedAsPage').default,
[SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_ADVANCED]: () => require('../../../../pages/workspace/accounting/qbo/advanced/QuickbooksAdvancedPage').default,
[SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_ACCOUNT_SELECTOR]: () =>
require('../../../../pages/workspace/accounting/qbo/advanced/QuickbooksAccountSelectPage').default,
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 39fa05cf87d4..60cb6f53f697 100755
--- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts
+++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts
@@ -45,6 +45,9 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = {
SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_ADVANCED,
SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_ACCOUNT_SELECTOR,
SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECTOR,
+ SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_CLASSES_DISPLAYED_AS,
+ SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_CUSTOMERS_DISPLAYED_AS,
+ SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_LOCATIONS_DISPLAYED_AS,
SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_DESKTOP_COMPANY_CARD_EXPENSE_ACCOUNT_SELECT,
SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_DESKTOP_COMPANY_CARD_EXPENSE_ACCOUNT_COMPANY_CARD_SELECT,
SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_DESKTOP_COMPANY_CARD_EXPENSE_ACCOUNT,
diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts
index 39da189af13c..6f551d7cc41c 100644
--- a/src/libs/Navigation/linkingConfig/config.ts
+++ b/src/libs/Navigation/linkingConfig/config.ts
@@ -383,6 +383,15 @@ const config: LinkingOptions['config'] = {
[SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECTOR]: {
path: ROUTES.WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECTOR.route,
},
+ [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_CLASSES_DISPLAYED_AS]: {
+ path: ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_CLASSES_DISPLAYED_AS.route,
+ },
+ [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_CUSTOMERS_DISPLAYED_AS]: {
+ path: ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_CUSTOMERS_DISPLAYED_AS.route,
+ },
+ [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_LOCATIONS_DISPLAYED_AS]: {
+ path: ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_LOCATIONS_DISPLAYED_AS.route,
+ },
[SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_DESKTOP_COMPANY_CARD_EXPENSE_ACCOUNT_SELECT]: {
path: ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_COMPANY_CARD_EXPENSE_ACCOUNT_SELECT.route,
},
diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts
index 087df82c684f..3de07f2c801f 100644
--- a/src/libs/Navigation/types.ts
+++ b/src/libs/Navigation/types.ts
@@ -1455,6 +1455,15 @@ type FullScreenNavigatorParamList = {
[SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECTOR]: {
policyID: string;
};
+ [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_CLASSES_DISPLAYED_AS]: {
+ policyID: string;
+ };
+ [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_CUSTOMERS_DISPLAYED_AS]: {
+ policyID: string;
+ };
+ [SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_LOCATIONS_DISPLAYED_AS]: {
+ policyID: string;
+ };
[SCREENS.WORKSPACE.EXPENSIFY_CARD]: {
policyID: string;
};
diff --git a/src/libs/Network/enhanceParameters.ts b/src/libs/Network/enhanceParameters.ts
index 01d2185a34c6..1806726fdabb 100644
--- a/src/libs/Network/enhanceParameters.ts
+++ b/src/libs/Network/enhanceParameters.ts
@@ -20,6 +20,15 @@ Onyx.connect({
},
});
+// Check if the user is logged in as a delegate and send that if so
+let delegate = '';
+Onyx.connect({
+ key: ONYXKEYS.ACCOUNT,
+ callback: (val) => {
+ delegate = val?.delegatedAccess?.delegate ?? '';
+ },
+});
+
/**
* Does this command require an authToken?
*/
@@ -57,5 +66,9 @@ export default function enhanceParameters(command: string, parameters: Record): str
function getIntegrationIcon(connectionName?: ConnectionName) {
if (connectionName === CONST.POLICY.CONNECTIONS.NAME.XERO) {
- return XeroCircle;
+ return XeroSquare;
}
if (connectionName === CONST.POLICY.CONNECTIONS.NAME.QBO) {
- return QBOCircle;
+ return QBOSquare;
}
+ if (connectionName === CONST.POLICY.CONNECTIONS.NAME.NETSUITE) {
+ return NetSuiteSquare;
+ }
+ if (connectionName === CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT) {
+ return IntacctSquare;
+ }
+
return undefined;
}
diff --git a/src/libs/SearchParser/autocompleteParser.js b/src/libs/SearchParser/autocompleteParser.js
index a9c8870e2f79..5fc13ac07f17 100644
--- a/src/libs/SearchParser/autocompleteParser.js
+++ b/src/libs/SearchParser/autocompleteParser.js
@@ -185,17 +185,19 @@ function peg$parse(input, options) {
var peg$c7 = "expenseType";
var peg$c8 = "type";
var peg$c9 = "status";
- var peg$c10 = "!=";
- var peg$c11 = ">=";
- var peg$c12 = ">";
- var peg$c13 = "<=";
- var peg$c14 = "<";
- var peg$c15 = "\"";
+ var peg$c10 = ",";
+ var peg$c11 = "!=";
+ var peg$c12 = ">=";
+ var peg$c13 = ">";
+ var peg$c14 = "<=";
+ var peg$c15 = "<";
+ var peg$c16 = "\"";
var peg$r0 = /^[:=]/;
- var peg$r1 = /^[^"\r\n]/;
- var peg$r2 = /^[A-Za-z0-9_@.\/#&+\-\\',;%]/;
- var peg$r3 = /^[ \t\r\n]/;
+ var peg$r1 = /^[^ ,"\t\n\r]/;
+ var peg$r2 = /^[^"\r\n]/;
+ var peg$r3 = /^[^ ,\t\n\r]/;
+ var peg$r4 = /^[ \t\r\n]/;
var peg$e0 = peg$otherExpectation("key");
var peg$e1 = peg$literalExpectation("in", false);
@@ -208,20 +210,22 @@ function peg$parse(input, options) {
var peg$e8 = peg$literalExpectation("expenseType", false);
var peg$e9 = peg$literalExpectation("type", false);
var peg$e10 = peg$literalExpectation("status", false);
- var peg$e11 = peg$otherExpectation("operator");
- var peg$e12 = peg$classExpectation([":", "="], false, false);
- var peg$e13 = peg$literalExpectation("!=", false);
- var peg$e14 = peg$literalExpectation(">=", false);
- var peg$e15 = peg$literalExpectation(">", false);
- var peg$e16 = peg$literalExpectation("<=", false);
- var peg$e17 = peg$literalExpectation("<", false);
- var peg$e18 = peg$otherExpectation("quote");
- var peg$e19 = peg$literalExpectation("\"", false);
- var peg$e20 = peg$classExpectation(["\"", "\r", "\n"], true, false);
- var peg$e21 = peg$otherExpectation("word");
- var peg$e22 = peg$classExpectation([["A", "Z"], ["a", "z"], ["0", "9"], "_", "@", ".", "/", "#", "&", "+", "-", "\\", "'", ",", ";", "%"], false, false);
- var peg$e23 = peg$otherExpectation("whitespace");
- var peg$e24 = peg$classExpectation([" ", "\t", "\r", "\n"], false, false);
+ var peg$e11 = peg$literalExpectation(",", false);
+ var peg$e12 = peg$otherExpectation("operator");
+ var peg$e13 = peg$classExpectation([":", "="], false, false);
+ var peg$e14 = peg$literalExpectation("!=", false);
+ var peg$e15 = peg$literalExpectation(">=", false);
+ var peg$e16 = peg$literalExpectation(">", false);
+ var peg$e17 = peg$literalExpectation("<=", false);
+ var peg$e18 = peg$literalExpectation("<", false);
+ var peg$e19 = peg$otherExpectation("quote");
+ var peg$e20 = peg$classExpectation([" ", ",", "\"", "\t", "\n", "\r"], true, false);
+ var peg$e21 = peg$literalExpectation("\"", false);
+ var peg$e22 = peg$classExpectation(["\"", "\r", "\n"], true, false);
+ var peg$e23 = peg$classExpectation([" ", ",", "\t", "\n", "\r"], true, false);
+ var peg$e24 = peg$otherExpectation("word");
+ var peg$e25 = peg$otherExpectation("whitespace");
+ var peg$e26 = peg$classExpectation([" ", "\t", "\r", "\n"], false, false);
var peg$f0 = function(ranges) { return { autocomplete, ranges }; };
var peg$f1 = function(filters) { return filters.filter(Boolean).flat(); };
@@ -241,21 +245,30 @@ function peg$parse(input, options) {
...value[value.length - 1],
};
- return value.map(({ start, length }) => ({
- key,
- start,
- length,
- }));
+ return value
+ .filter((filter) => filter.length > 0)
+ .map(({ start, length }) => ({
+ key,
+ start,
+ length,
+ }));
};
var peg$f3 = function() { autocomplete = null; };
- var peg$f4 = function(parts) {
+ var peg$f4 = function(parts, empty) {
const ends = location();
const value = parts.flat();
+ if (empty) {
+ value.push("");
+ }
let count = ends.start.offset;
const result = [];
value.forEach((filter) => {
+ let word = filter;
+ if (word.startsWith('"') && word.endsWith('"') && word.length >= 2) {
+ word = word.slice(1, -1);
+ }
result.push({
- value: filter,
+ value: word,
start: count,
length: filter.length,
});
@@ -269,10 +282,10 @@ function peg$parse(input, options) {
var peg$f8 = function() { return "gt"; };
var peg$f9 = function() { return "lte"; };
var peg$f10 = function() { return "lt"; };
- var peg$f11 = function(chars) { return chars.join(""); };
- var peg$f12 = function(chars) {
- return chars.join("").trim().split(",").filter(Boolean);
+ var peg$f11 = function(start, inner, end) {
+ return [...start, '"', ...inner, '"', ...end].join("");
};
+ var peg$f12 = function(chars) { return chars.join("").trim(); };
var peg$f13 = function() { return "and"; };
var peg$currPos = options.peg$currPos | 0;
var peg$savedPos = peg$currPos;
@@ -648,30 +661,63 @@ function peg$parse(input, options) {
}
function peg$parseidentifier() {
- var s0, s1, s2;
+ var s0, s1, s2, s3, s4;
s0 = peg$currPos;
- s1 = [];
- s2 = peg$parsequotedString();
- if (s2 === peg$FAILED) {
- s2 = peg$parsealphanumeric();
+ s1 = peg$currPos;
+ s2 = [];
+ s3 = peg$parsequotedString();
+ if (s3 === peg$FAILED) {
+ s3 = peg$parsealphanumeric();
}
- if (s2 !== peg$FAILED) {
- while (s2 !== peg$FAILED) {
- s1.push(s2);
- s2 = peg$parsequotedString();
- if (s2 === peg$FAILED) {
- s2 = peg$parsealphanumeric();
+ while (s3 !== peg$FAILED) {
+ s2.push(s3);
+ s3 = peg$currPos;
+ if (input.charCodeAt(peg$currPos) === 44) {
+ s4 = peg$c10;
+ peg$currPos++;
+ } else {
+ s4 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$e11); }
+ }
+ if (s4 !== peg$FAILED) {
+ s4 = peg$parsequotedString();
+ if (s4 === peg$FAILED) {
+ s4 = peg$parsealphanumeric();
}
+ if (s4 === peg$FAILED) {
+ peg$currPos = s3;
+ s3 = peg$FAILED;
+ } else {
+ s3 = s4;
+ }
+ } else {
+ s3 = s4;
}
- } else {
+ }
+ if (s2.length < 1) {
+ peg$currPos = s1;
s1 = peg$FAILED;
+ } else {
+ s1 = s2;
}
if (s1 !== peg$FAILED) {
+ if (input.charCodeAt(peg$currPos) === 44) {
+ s2 = peg$c10;
+ peg$currPos++;
+ } else {
+ s2 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$e11); }
+ }
+ if (s2 === peg$FAILED) {
+ s2 = null;
+ }
peg$savedPos = s0;
- s1 = peg$f4(s1);
+ s0 = peg$f4(s1, s2);
+ } else {
+ peg$currPos = s0;
+ s0 = peg$FAILED;
}
- s0 = s1;
return s0;
}
@@ -686,7 +732,7 @@ function peg$parse(input, options) {
peg$currPos++;
} else {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e12); }
+ if (peg$silentFails === 0) { peg$fail(peg$e13); }
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
@@ -695,12 +741,12 @@ function peg$parse(input, options) {
s0 = s1;
if (s0 === peg$FAILED) {
s0 = peg$currPos;
- if (input.substr(peg$currPos, 2) === peg$c10) {
- s1 = peg$c10;
+ if (input.substr(peg$currPos, 2) === peg$c11) {
+ s1 = peg$c11;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e13); }
+ if (peg$silentFails === 0) { peg$fail(peg$e14); }
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
@@ -709,12 +755,12 @@ function peg$parse(input, options) {
s0 = s1;
if (s0 === peg$FAILED) {
s0 = peg$currPos;
- if (input.substr(peg$currPos, 2) === peg$c11) {
- s1 = peg$c11;
+ if (input.substr(peg$currPos, 2) === peg$c12) {
+ s1 = peg$c12;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e14); }
+ if (peg$silentFails === 0) { peg$fail(peg$e15); }
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
@@ -724,11 +770,11 @@ function peg$parse(input, options) {
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 62) {
- s1 = peg$c12;
+ s1 = peg$c13;
peg$currPos++;
} else {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e15); }
+ if (peg$silentFails === 0) { peg$fail(peg$e16); }
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
@@ -737,12 +783,12 @@ function peg$parse(input, options) {
s0 = s1;
if (s0 === peg$FAILED) {
s0 = peg$currPos;
- if (input.substr(peg$currPos, 2) === peg$c13) {
- s1 = peg$c13;
+ if (input.substr(peg$currPos, 2) === peg$c14) {
+ s1 = peg$c14;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e16); }
+ if (peg$silentFails === 0) { peg$fail(peg$e17); }
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
@@ -752,11 +798,11 @@ function peg$parse(input, options) {
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 60) {
- s1 = peg$c14;
+ s1 = peg$c15;
peg$currPos++;
} else {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e17); }
+ if (peg$silentFails === 0) { peg$fail(peg$e18); }
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
@@ -771,53 +817,89 @@ function peg$parse(input, options) {
peg$silentFails--;
if (s0 === peg$FAILED) {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e11); }
+ if (peg$silentFails === 0) { peg$fail(peg$e12); }
}
return s0;
}
function peg$parsequotedString() {
- var s0, s1, s2, s3;
+ var s0, s1, s2, s3, s4, s5, s6;
peg$silentFails++;
s0 = peg$currPos;
- if (input.charCodeAt(peg$currPos) === 34) {
- s1 = peg$c15;
+ s1 = [];
+ s2 = input.charAt(peg$currPos);
+ if (peg$r1.test(s2)) {
peg$currPos++;
} else {
- s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e19); }
+ s2 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$e20); }
}
- if (s1 !== peg$FAILED) {
- s2 = [];
- s3 = input.charAt(peg$currPos);
- if (peg$r1.test(s3)) {
+ while (s2 !== peg$FAILED) {
+ s1.push(s2);
+ s2 = input.charAt(peg$currPos);
+ if (peg$r1.test(s2)) {
peg$currPos++;
} else {
- s3 = peg$FAILED;
+ s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e20); }
}
- while (s3 !== peg$FAILED) {
- s2.push(s3);
- s3 = input.charAt(peg$currPos);
- if (peg$r1.test(s3)) {
+ }
+ if (input.charCodeAt(peg$currPos) === 34) {
+ s2 = peg$c16;
+ peg$currPos++;
+ } else {
+ s2 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$e21); }
+ }
+ if (s2 !== peg$FAILED) {
+ s3 = [];
+ s4 = input.charAt(peg$currPos);
+ if (peg$r2.test(s4)) {
+ peg$currPos++;
+ } else {
+ s4 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$e22); }
+ }
+ while (s4 !== peg$FAILED) {
+ s3.push(s4);
+ s4 = input.charAt(peg$currPos);
+ if (peg$r2.test(s4)) {
peg$currPos++;
} else {
- s3 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e20); }
+ s4 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$e22); }
}
}
if (input.charCodeAt(peg$currPos) === 34) {
- s3 = peg$c15;
+ s4 = peg$c16;
peg$currPos++;
} else {
- s3 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e19); }
+ s4 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$e21); }
}
- if (s3 !== peg$FAILED) {
+ if (s4 !== peg$FAILED) {
+ s5 = [];
+ s6 = input.charAt(peg$currPos);
+ if (peg$r3.test(s6)) {
+ peg$currPos++;
+ } else {
+ s6 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$e23); }
+ }
+ while (s6 !== peg$FAILED) {
+ s5.push(s6);
+ s6 = input.charAt(peg$currPos);
+ if (peg$r3.test(s6)) {
+ peg$currPos++;
+ } else {
+ s6 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$e23); }
+ }
+ }
peg$savedPos = s0;
- s0 = peg$f11(s2);
+ s0 = peg$f11(s1, s3, s5);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -829,7 +911,7 @@ function peg$parse(input, options) {
peg$silentFails--;
if (s0 === peg$FAILED) {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e18); }
+ if (peg$silentFails === 0) { peg$fail(peg$e19); }
}
return s0;
@@ -842,21 +924,21 @@ function peg$parse(input, options) {
s0 = peg$currPos;
s1 = [];
s2 = input.charAt(peg$currPos);
- if (peg$r2.test(s2)) {
+ if (peg$r3.test(s2)) {
peg$currPos++;
} else {
s2 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e22); }
+ if (peg$silentFails === 0) { peg$fail(peg$e23); }
}
if (s2 !== peg$FAILED) {
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = input.charAt(peg$currPos);
- if (peg$r2.test(s2)) {
+ if (peg$r3.test(s2)) {
peg$currPos++;
} else {
s2 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e22); }
+ if (peg$silentFails === 0) { peg$fail(peg$e23); }
}
}
} else {
@@ -870,7 +952,7 @@ function peg$parse(input, options) {
peg$silentFails--;
if (s0 === peg$FAILED) {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e21); }
+ if (peg$silentFails === 0) { peg$fail(peg$e24); }
}
return s0;
@@ -894,25 +976,25 @@ function peg$parse(input, options) {
peg$silentFails++;
s0 = [];
s1 = input.charAt(peg$currPos);
- if (peg$r3.test(s1)) {
+ if (peg$r4.test(s1)) {
peg$currPos++;
} else {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e24); }
+ if (peg$silentFails === 0) { peg$fail(peg$e26); }
}
while (s1 !== peg$FAILED) {
s0.push(s1);
s1 = input.charAt(peg$currPos);
- if (peg$r3.test(s1)) {
+ if (peg$r4.test(s1)) {
peg$currPos++;
} else {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e24); }
+ if (peg$silentFails === 0) { peg$fail(peg$e26); }
}
}
peg$silentFails--;
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e23); }
+ if (peg$silentFails === 0) { peg$fail(peg$e25); }
return s0;
}
diff --git a/src/libs/SearchParser/autocompleteParser.peggy b/src/libs/SearchParser/autocompleteParser.peggy
index 003d35485d69..bc96c86eb74d 100644
--- a/src/libs/SearchParser/autocompleteParser.peggy
+++ b/src/libs/SearchParser/autocompleteParser.peggy
@@ -39,11 +39,13 @@ defaultFilter
...value[value.length - 1],
};
- return value.map(({ start, length }) => ({
- key,
- start,
- length,
- }));
+ return value
+ .filter((filter) => filter.length > 0)
+ .map(({ start, length }) => ({
+ key,
+ start,
+ length,
+ }));
}
freeTextFilter = _ identifier _ { autocomplete = null; }
@@ -63,14 +65,21 @@ autocompleteKey "key"
)
identifier
- = parts:(quotedString / alphanumeric)+ {
+ = parts:(quotedString / alphanumeric)|1.., ","| empty:","? {
const ends = location();
const value = parts.flat();
+ if (empty) {
+ value.push("");
+ }
let count = ends.start.offset;
const result = [];
value.forEach((filter) => {
+ let word = filter;
+ if (word.startsWith('"') && word.endsWith('"') && word.length >= 2) {
+ word = word.slice(1, -1);
+ }
result.push({
- value: filter,
+ value: word,
start: count,
length: filter.length,
});
diff --git a/src/libs/SearchParser/baseRules.peggy b/src/libs/SearchParser/baseRules.peggy
index 62948fdb573b..7605d888ba43 100644
--- a/src/libs/SearchParser/baseRules.peggy
+++ b/src/libs/SearchParser/baseRules.peggy
@@ -16,13 +16,13 @@ operator "operator"
/ "<=" { return "lte"; }
/ "<" { return "lt"; }
-quotedString "quote" = "\"" chars:[^"\r\n]* "\"" { return chars.join(""); }
-
-alphanumeric "word"
- = chars:[A-Za-z0-9_@./#&+\-\\',;%]+ {
- return chars.join("").trim().split(",").filter(Boolean);
+quotedString "quote"
+ = start:[^ ,"\t\n\r]* "\"" inner:[^"\r\n]* "\"" end:[^ ,\t\n\r]* {
+ return [...start, '"', ...inner, '"', ...end].join("");
}
+alphanumeric "word" = chars:[^ ,\t\n\r]+ { return chars.join("").trim(); }
+
logicalAnd = _ { return "and"; }
_ "whitespace" = [ \t\r\n]*
diff --git a/src/libs/SearchParser/searchParser.js b/src/libs/SearchParser/searchParser.js
index 49e819ada3e5..1e8d12f16a32 100644
--- a/src/libs/SearchParser/searchParser.js
+++ b/src/libs/SearchParser/searchParser.js
@@ -200,17 +200,19 @@ function peg$parse(input, options) {
var peg$c17 = "sortBy";
var peg$c18 = "sortOrder";
var peg$c19 = "policyID";
- var peg$c20 = "!=";
- var peg$c21 = ">=";
- var peg$c22 = ">";
- var peg$c23 = "<=";
- var peg$c24 = "<";
- var peg$c25 = "\"";
+ var peg$c20 = ",";
+ var peg$c21 = "!=";
+ var peg$c22 = ">=";
+ var peg$c23 = ">";
+ var peg$c24 = "<=";
+ var peg$c25 = "<";
+ var peg$c26 = "\"";
var peg$r0 = /^[:=]/;
- var peg$r1 = /^[^"\r\n]/;
- var peg$r2 = /^[A-Za-z0-9_@.\/#&+\-\\',;%]/;
- var peg$r3 = /^[ \t\r\n]/;
+ var peg$r1 = /^[^ ,"\t\n\r]/;
+ var peg$r2 = /^[^"\r\n]/;
+ var peg$r3 = /^[^ ,\t\n\r]/;
+ var peg$r4 = /^[ \t\r\n]/;
var peg$e0 = peg$otherExpectation("key");
var peg$e1 = peg$literalExpectation("date", false);
@@ -234,20 +236,22 @@ function peg$parse(input, options) {
var peg$e19 = peg$literalExpectation("sortBy", false);
var peg$e20 = peg$literalExpectation("sortOrder", false);
var peg$e21 = peg$literalExpectation("policyID", false);
- var peg$e22 = peg$otherExpectation("operator");
- var peg$e23 = peg$classExpectation([":", "="], false, false);
- var peg$e24 = peg$literalExpectation("!=", false);
- var peg$e25 = peg$literalExpectation(">=", false);
- var peg$e26 = peg$literalExpectation(">", false);
- var peg$e27 = peg$literalExpectation("<=", false);
- var peg$e28 = peg$literalExpectation("<", false);
- var peg$e29 = peg$otherExpectation("quote");
- var peg$e30 = peg$literalExpectation("\"", false);
- var peg$e31 = peg$classExpectation(["\"", "\r", "\n"], true, false);
- var peg$e32 = peg$otherExpectation("word");
- var peg$e33 = peg$classExpectation([["A", "Z"], ["a", "z"], ["0", "9"], "_", "@", ".", "/", "#", "&", "+", "-", "\\", "'", ",", ";", "%"], false, false);
- var peg$e34 = peg$otherExpectation("whitespace");
- var peg$e35 = peg$classExpectation([" ", "\t", "\r", "\n"], false, false);
+ var peg$e22 = peg$literalExpectation(",", false);
+ var peg$e23 = peg$otherExpectation("operator");
+ var peg$e24 = peg$classExpectation([":", "="], false, false);
+ var peg$e25 = peg$literalExpectation("!=", false);
+ var peg$e26 = peg$literalExpectation(">=", false);
+ var peg$e27 = peg$literalExpectation(">", false);
+ var peg$e28 = peg$literalExpectation("<=", false);
+ var peg$e29 = peg$literalExpectation("<", false);
+ var peg$e30 = peg$otherExpectation("quote");
+ var peg$e31 = peg$classExpectation([" ", ",", "\"", "\t", "\n", "\r"], true, false);
+ var peg$e32 = peg$literalExpectation("\"", false);
+ var peg$e33 = peg$classExpectation(["\"", "\r", "\n"], true, false);
+ var peg$e34 = peg$classExpectation([" ", ",", "\t", "\n", "\r"], true, false);
+ var peg$e35 = peg$otherExpectation("word");
+ var peg$e36 = peg$otherExpectation("whitespace");
+ var peg$e37 = peg$classExpectation([" ", "\t", "\r", "\n"], false, false);
var peg$f0 = function(filters) { return applyDefaults(filters); };
var peg$f1 = function(head, tail) {
@@ -287,9 +291,14 @@ function peg$parse(input, options) {
return buildFilter(op, field, values);
};
var peg$f5 = function(parts) {
- const value = parts.flat();
+ const value = parts.flat().map((word) => {
+ if (word.startsWith('"') && word.endsWith('"') && word.length >= 2) {
+ return word.slice(1,-1);
+ }
+ return word;
+ });
if (value.length > 1) {
- return value;
+ return value.filter((word) => word.length > 0);
}
return value[0];
};
@@ -299,10 +308,10 @@ function peg$parse(input, options) {
var peg$f9 = function() { return "gt"; };
var peg$f10 = function() { return "lte"; };
var peg$f11 = function() { return "lt"; };
- var peg$f12 = function(chars) { return chars.join(""); };
- var peg$f13 = function(chars) {
- return chars.join("").trim().split(",").filter(Boolean);
+ var peg$f12 = function(start, inner, end) {
+ return [...start, '"', ...inner, '"', ...end].join("");
};
+ var peg$f13 = function(chars) { return chars.join("").trim(); };
var peg$f14 = function() { return "and"; };
var peg$currPos = options.peg$currPos | 0;
var peg$savedPos = peg$currPos;
@@ -747,15 +756,6 @@ function peg$parse(input, options) {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e15); }
}
- if (s1 === peg$FAILED) {
- if (input.substr(peg$currPos, 2) === peg$c6) {
- s1 = peg$c6;
- peg$currPos += 2;
- } else {
- s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e7); }
- }
- }
}
}
}
@@ -849,24 +849,45 @@ function peg$parse(input, options) {
}
function peg$parseidentifier() {
- var s0, s1, s2;
+ var s0, s1, s2, s3, s4;
s0 = peg$currPos;
- s1 = [];
- s2 = peg$parsequotedString();
- if (s2 === peg$FAILED) {
- s2 = peg$parsealphanumeric();
+ s1 = peg$currPos;
+ s2 = [];
+ s3 = peg$parsequotedString();
+ if (s3 === peg$FAILED) {
+ s3 = peg$parsealphanumeric();
}
- if (s2 !== peg$FAILED) {
- while (s2 !== peg$FAILED) {
- s1.push(s2);
- s2 = peg$parsequotedString();
- if (s2 === peg$FAILED) {
- s2 = peg$parsealphanumeric();
+ while (s3 !== peg$FAILED) {
+ s2.push(s3);
+ s3 = peg$currPos;
+ if (input.charCodeAt(peg$currPos) === 44) {
+ s4 = peg$c20;
+ peg$currPos++;
+ } else {
+ s4 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$e22); }
+ }
+ if (s4 !== peg$FAILED) {
+ s4 = peg$parsequotedString();
+ if (s4 === peg$FAILED) {
+ s4 = peg$parsealphanumeric();
}
+ if (s4 === peg$FAILED) {
+ peg$currPos = s3;
+ s3 = peg$FAILED;
+ } else {
+ s3 = s4;
+ }
+ } else {
+ s3 = s4;
}
- } else {
+ }
+ if (s2.length < 1) {
+ peg$currPos = s1;
s1 = peg$FAILED;
+ } else {
+ s1 = s2;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
@@ -887,7 +908,7 @@ function peg$parse(input, options) {
peg$currPos++;
} else {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e23); }
+ if (peg$silentFails === 0) { peg$fail(peg$e24); }
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
@@ -896,12 +917,12 @@ function peg$parse(input, options) {
s0 = s1;
if (s0 === peg$FAILED) {
s0 = peg$currPos;
- if (input.substr(peg$currPos, 2) === peg$c20) {
- s1 = peg$c20;
+ if (input.substr(peg$currPos, 2) === peg$c21) {
+ s1 = peg$c21;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e24); }
+ if (peg$silentFails === 0) { peg$fail(peg$e25); }
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
@@ -910,12 +931,12 @@ function peg$parse(input, options) {
s0 = s1;
if (s0 === peg$FAILED) {
s0 = peg$currPos;
- if (input.substr(peg$currPos, 2) === peg$c21) {
- s1 = peg$c21;
+ if (input.substr(peg$currPos, 2) === peg$c22) {
+ s1 = peg$c22;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e25); }
+ if (peg$silentFails === 0) { peg$fail(peg$e26); }
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
@@ -925,11 +946,11 @@ function peg$parse(input, options) {
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 62) {
- s1 = peg$c22;
+ s1 = peg$c23;
peg$currPos++;
} else {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e26); }
+ if (peg$silentFails === 0) { peg$fail(peg$e27); }
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
@@ -938,12 +959,12 @@ function peg$parse(input, options) {
s0 = s1;
if (s0 === peg$FAILED) {
s0 = peg$currPos;
- if (input.substr(peg$currPos, 2) === peg$c23) {
- s1 = peg$c23;
+ if (input.substr(peg$currPos, 2) === peg$c24) {
+ s1 = peg$c24;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e27); }
+ if (peg$silentFails === 0) { peg$fail(peg$e28); }
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
@@ -953,11 +974,11 @@ function peg$parse(input, options) {
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 60) {
- s1 = peg$c24;
+ s1 = peg$c25;
peg$currPos++;
} else {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e28); }
+ if (peg$silentFails === 0) { peg$fail(peg$e29); }
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
@@ -972,53 +993,89 @@ function peg$parse(input, options) {
peg$silentFails--;
if (s0 === peg$FAILED) {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e22); }
+ if (peg$silentFails === 0) { peg$fail(peg$e23); }
}
return s0;
}
function peg$parsequotedString() {
- var s0, s1, s2, s3;
+ var s0, s1, s2, s3, s4, s5, s6;
peg$silentFails++;
s0 = peg$currPos;
- if (input.charCodeAt(peg$currPos) === 34) {
- s1 = peg$c25;
+ s1 = [];
+ s2 = input.charAt(peg$currPos);
+ if (peg$r1.test(s2)) {
peg$currPos++;
} else {
- s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e30); }
+ s2 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$e31); }
}
- if (s1 !== peg$FAILED) {
- s2 = [];
- s3 = input.charAt(peg$currPos);
- if (peg$r1.test(s3)) {
+ while (s2 !== peg$FAILED) {
+ s1.push(s2);
+ s2 = input.charAt(peg$currPos);
+ if (peg$r1.test(s2)) {
peg$currPos++;
} else {
- s3 = peg$FAILED;
+ s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e31); }
}
- while (s3 !== peg$FAILED) {
- s2.push(s3);
- s3 = input.charAt(peg$currPos);
- if (peg$r1.test(s3)) {
+ }
+ if (input.charCodeAt(peg$currPos) === 34) {
+ s2 = peg$c26;
+ peg$currPos++;
+ } else {
+ s2 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$e32); }
+ }
+ if (s2 !== peg$FAILED) {
+ s3 = [];
+ s4 = input.charAt(peg$currPos);
+ if (peg$r2.test(s4)) {
+ peg$currPos++;
+ } else {
+ s4 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$e33); }
+ }
+ while (s4 !== peg$FAILED) {
+ s3.push(s4);
+ s4 = input.charAt(peg$currPos);
+ if (peg$r2.test(s4)) {
peg$currPos++;
} else {
- s3 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e31); }
+ s4 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$e33); }
}
}
if (input.charCodeAt(peg$currPos) === 34) {
- s3 = peg$c25;
+ s4 = peg$c26;
peg$currPos++;
} else {
- s3 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e30); }
+ s4 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$e32); }
}
- if (s3 !== peg$FAILED) {
+ if (s4 !== peg$FAILED) {
+ s5 = [];
+ s6 = input.charAt(peg$currPos);
+ if (peg$r3.test(s6)) {
+ peg$currPos++;
+ } else {
+ s6 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$e34); }
+ }
+ while (s6 !== peg$FAILED) {
+ s5.push(s6);
+ s6 = input.charAt(peg$currPos);
+ if (peg$r3.test(s6)) {
+ peg$currPos++;
+ } else {
+ s6 = peg$FAILED;
+ if (peg$silentFails === 0) { peg$fail(peg$e34); }
+ }
+ }
peg$savedPos = s0;
- s0 = peg$f12(s2);
+ s0 = peg$f12(s1, s3, s5);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1030,7 +1087,7 @@ function peg$parse(input, options) {
peg$silentFails--;
if (s0 === peg$FAILED) {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e29); }
+ if (peg$silentFails === 0) { peg$fail(peg$e30); }
}
return s0;
@@ -1043,21 +1100,21 @@ function peg$parse(input, options) {
s0 = peg$currPos;
s1 = [];
s2 = input.charAt(peg$currPos);
- if (peg$r2.test(s2)) {
+ if (peg$r3.test(s2)) {
peg$currPos++;
} else {
s2 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e33); }
+ if (peg$silentFails === 0) { peg$fail(peg$e34); }
}
if (s2 !== peg$FAILED) {
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = input.charAt(peg$currPos);
- if (peg$r2.test(s2)) {
+ if (peg$r3.test(s2)) {
peg$currPos++;
} else {
s2 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e33); }
+ if (peg$silentFails === 0) { peg$fail(peg$e34); }
}
}
} else {
@@ -1071,7 +1128,7 @@ function peg$parse(input, options) {
peg$silentFails--;
if (s0 === peg$FAILED) {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e32); }
+ if (peg$silentFails === 0) { peg$fail(peg$e35); }
}
return s0;
@@ -1095,25 +1152,25 @@ function peg$parse(input, options) {
peg$silentFails++;
s0 = [];
s1 = input.charAt(peg$currPos);
- if (peg$r3.test(s1)) {
+ if (peg$r4.test(s1)) {
peg$currPos++;
} else {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e35); }
+ if (peg$silentFails === 0) { peg$fail(peg$e37); }
}
while (s1 !== peg$FAILED) {
s0.push(s1);
s1 = input.charAt(peg$currPos);
- if (peg$r3.test(s1)) {
+ if (peg$r4.test(s1)) {
peg$currPos++;
} else {
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e35); }
+ if (peg$silentFails === 0) { peg$fail(peg$e37); }
}
}
peg$silentFails--;
s1 = peg$FAILED;
- if (peg$silentFails === 0) { peg$fail(peg$e34); }
+ if (peg$silentFails === 0) { peg$fail(peg$e36); }
return s0;
}
diff --git a/src/libs/SearchParser/searchParser.peggy b/src/libs/SearchParser/searchParser.peggy
index f775117b5f4e..7d5815d41459 100644
--- a/src/libs/SearchParser/searchParser.peggy
+++ b/src/libs/SearchParser/searchParser.peggy
@@ -108,17 +108,21 @@ key "key"
/ "cardID"
/ "from"
/ "expenseType"
- / "in"
)
defaultKey "default key"
= @("type" / "status" / "sortBy" / "sortOrder" / "policyID")
identifier
- = parts:(quotedString / alphanumeric)+ {
- const value = parts.flat();
+ = parts:(quotedString / alphanumeric)|1.., ","| {
+ const value = parts.flat().map((word) => {
+ if (word.startsWith('"') && word.endsWith('"') && word.length >= 2) {
+ return word.slice(1,-1);
+ }
+ return word;
+ });
if (value.length > 1) {
- return value;
+ return value.filter((word) => word.length > 0);
}
return value[0];
}
diff --git a/src/libs/SearchQueryUtils.ts b/src/libs/SearchQueryUtils.ts
index 32c2eff72007..51db9fd56ea6 100644
--- a/src/libs/SearchQueryUtils.ts
+++ b/src/libs/SearchQueryUtils.ts
@@ -1,13 +1,15 @@
import cloneDeep from 'lodash/cloneDeep';
import type {OnyxCollection} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
-import type {AdvancedFiltersKeys, ASTNode, QueryFilter, QueryFilters, SearchQueryJSON, SearchQueryString, SearchStatus} from '@components/Search/types';
+import type {ASTNode, QueryFilter, QueryFilters, SearchQueryJSON, SearchQueryString, SearchStatus} from '@components/Search/types';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {SearchAdvancedFiltersForm} from '@src/types/form';
import FILTER_KEYS from '@src/types/form/SearchAdvancedFiltersForm';
import type * as OnyxTypes from '@src/types/onyx';
import type {SearchDataTypes} from '@src/types/onyx/SearchResults';
+import * as CurrencyUtils from './CurrencyUtils';
+import localeCompare from './LocaleCompare';
import {validateAmount} from './MoneyRequestUtils';
import * as PersonalDetailsUtils from './PersonalDetailsUtils';
import {getTagNamesFromTagsLists} from './PolicyUtils';
@@ -115,7 +117,7 @@ function buildFilterValuesString(filterName: string, queryFilters: QueryFilter[]
* Traverses the AST and returns filters as a QueryFilters object.
*/
function getFilters(queryJSON: SearchQueryJSON) {
- const filters = {} as QueryFilters;
+ const filters = [] as QueryFilters;
const filterKeys = Object.values(CONST.SEARCH.SYNTAX_FILTER_KEYS);
function traverse(node: ASTNode) {
@@ -136,12 +138,7 @@ function getFilters(queryJSON: SearchQueryJSON) {
return;
}
- if (!filters[nodeKey]) {
- filters[nodeKey] = [];
- }
-
- // the "?? []" is added only for typescript because otherwise TS throws an error, in newer TS versions this should be fixed
- const filterArray = filters[nodeKey] ?? [];
+ const filterArray = [];
if (!Array.isArray(node.right)) {
filterArray.push({
operator: node.operator,
@@ -155,6 +152,7 @@ function getFilters(queryJSON: SearchQueryJSON) {
});
});
}
+ filters.push({key: nodeKey, filters: filterArray});
}
if (queryJSON.filters) {
@@ -200,6 +198,16 @@ function findIDFromDisplayValue(filterName: ValueOf {
+ const backendAmount = CurrencyUtils.convertToBackendAmount(Number(amount));
+ return Number.isNaN(backendAmount) ? amount : backendAmount.toString();
+ });
+ }
return filter;
}
@@ -218,18 +226,14 @@ function getQueryHash(query: SearchQueryJSON): number {
orderedQuery += ` ${CONST.SEARCH.SYNTAX_ROOT_KEYS.SORT_BY}:${query.sortBy}`;
orderedQuery += ` ${CONST.SEARCH.SYNTAX_ROOT_KEYS.SORT_ORDER}:${query.sortOrder}`;
- Object.keys(query.flatFilters)
+ query.flatFilters.forEach((filter) => {
+ filter.filters.sort((a, b) => localeCompare(a.value.toString(), b.value.toString()));
+ });
+
+ query.flatFilters
+ .map((filter) => buildFilterValuesString(filter.key, filter.filters))
.sort()
- .forEach((key) => {
- const filterValues = query.flatFilters?.[key as AdvancedFiltersKeys];
- const sortedFilterValues = filterValues?.sort((queryFilter1, queryFilter2) => {
- if (queryFilter1.value > queryFilter2.value) {
- return 1;
- }
- return -1;
- });
- orderedQuery += ` ${buildFilterValuesString(key, sortedFilterValues ?? [])}`;
- });
+ .forEach((filterString) => (orderedQuery += ` ${filterString}`));
return UserUtils.hashText(orderedQuery, 2 ** 32);
}
@@ -281,13 +285,9 @@ function buildSearchQueryString(queryJSON?: SearchQueryJSON) {
const filters = queryJSON.flatFilters;
- for (const [, filterKey] of Object.entries(CONST.SEARCH.SYNTAX_FILTER_KEYS)) {
- const queryFilter = filters[filterKey];
-
- if (queryFilter) {
- const filterValueString = buildFilterValuesString(filterKey, queryFilter);
- queryParts.push(filterValueString);
- }
+ for (const filter of filters) {
+ const filterValueString = buildFilterValuesString(filter.key, filter.filters);
+ queryParts.push(filterValueString);
}
return queryParts.join(' ');
@@ -388,32 +388,35 @@ function buildFilterFormValuesFromQuery(
taxRates: Record,
) {
const filters = queryJSON.flatFilters;
- const filterKeys = Object.keys(filters);
const filtersForm = {} as Partial;
const policyID = queryJSON.policyID;
- for (const filterKey of filterKeys) {
+ for (const queryFilter of filters) {
+ const filterKey = queryFilter.key;
+ const filterList = queryFilter.filters;
+ const filterValues = filterList.map((item) => item.value.toString());
if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.REPORT_ID || filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.MERCHANT || filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.DESCRIPTION) {
- filtersForm[filterKey] = filters[filterKey]?.[0]?.value.toString();
+ filtersForm[filterKey] = filterValues.at(0);
}
if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.EXPENSE_TYPE) {
- filtersForm[filterKey] = filters[filterKey]
- ?.map((expenseType) => expenseType.value.toString())
- .filter((expenseType) => Object.values(CONST.SEARCH.TRANSACTION_TYPE).includes(expenseType as ValueOf));
+ const validExpenseTypes = new Set(Object.values(CONST.SEARCH.TRANSACTION_TYPE));
+ filtersForm[filterKey] = filterValues.filter((expenseType) => validExpenseTypes.has(expenseType as ValueOf));
}
if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.CARD_ID) {
- filtersForm[filterKey] = filters[filterKey]?.map((card) => card.value.toString()).filter((card) => Object.keys(cardList).includes(card));
+ filtersForm[filterKey] = filterValues.filter((card) => cardList[card]);
}
if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE) {
- filtersForm[filterKey] = filters[filterKey]?.map((tax) => tax.value.toString()).filter((tax) => [...Object.values(taxRates)].flat().includes(tax));
+ const allTaxRates = new Set(Object.values(taxRates).flat());
+ filtersForm[filterKey] = filterValues.filter((tax) => allTaxRates.has(tax));
}
if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.IN) {
- filtersForm[filterKey] = filters[filterKey]?.map((report) => report.value.toString()).filter((id) => reports?.[`${ONYXKEYS.COLLECTION.REPORT}${id}`]);
+ filtersForm[filterKey] = filterValues.filter((id) => reports?.[`${ONYXKEYS.COLLECTION.REPORT}${id}`]);
}
if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM || filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO) {
- filtersForm[filterKey] = filters[filterKey]?.map((id) => id.value.toString()).filter((id) => Object.keys(personalDetails).includes(id));
+ filtersForm[filterKey] = filterValues.filter((id) => personalDetails[id]);
}
if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.CURRENCY) {
- filtersForm[filterKey] = filters[filterKey]?.filter((currency) => Object.keys(currencyList).includes(currency.value.toString())).map((currency) => currency.value.toString());
+ const validCurrency = new Set(Object.keys(currencyList));
+ filtersForm[filterKey] = filterValues.filter((currency) => validCurrency.has(currency));
}
if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAG) {
const tags = policyID
@@ -422,8 +425,9 @@ function buildFilterFormValuesFromQuery(
.filter((item) => !!item)
.map((tagList) => getTagNamesFromTagsLists(tagList ?? {}))
.flat();
- tags.push(CONST.SEARCH.EMPTY_VALUE);
- filtersForm[filterKey] = filters[filterKey]?.map((tag) => tag.value.toString()).filter((name) => tags.includes(name));
+ const uniqueTags = new Set(tags);
+ uniqueTags.add(CONST.SEARCH.EMPTY_VALUE);
+ filtersForm[filterKey] = filterValues.filter((name) => uniqueTags.has(name));
}
if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.CATEGORY) {
const categories = policyID
@@ -431,13 +435,13 @@ function buildFilterFormValuesFromQuery(
: Object.values(policyCategories ?? {})
.map((item) => Object.values(item ?? {}).map((category) => category.name))
.flat();
- categories.push(CONST.SEARCH.EMPTY_VALUE);
- filtersForm[filterKey] = filters[filterKey]?.map((category) => category.value.toString()).filter((name) => categories.includes(name));
+ const uniqueCategories = new Set(categories);
+ uniqueCategories.add(CONST.SEARCH.EMPTY_VALUE);
+ filtersForm[filterKey] = filterValues.filter((name) => uniqueCategories.has(name));
}
if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.KEYWORD) {
- filtersForm[filterKey] = filters[filterKey]
- ?.map((filter) => filter.value.toString())
- .map((filter) => {
+ filtersForm[filterKey] = filterValues
+ ?.map((filter) => {
if (filter.includes(' ')) {
return `"${filter}"`;
}
@@ -446,12 +450,19 @@ function buildFilterFormValuesFromQuery(
.join(' ');
}
if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.DATE) {
- filtersForm[FILTER_KEYS.DATE_BEFORE] = filters[filterKey]?.find((filter) => filter.operator === 'lt' && ValidationUtils.isValidDate(filter.value.toString()))?.value.toString();
- filtersForm[FILTER_KEYS.DATE_AFTER] = filters[filterKey]?.find((filter) => filter.operator === 'gt' && ValidationUtils.isValidDate(filter.value.toString()))?.value.toString();
+ filtersForm[FILTER_KEYS.DATE_BEFORE] =
+ filterList.find((filter) => filter.operator === 'lt' && ValidationUtils.isValidDate(filter.value.toString()))?.value.toString() ?? filtersForm[FILTER_KEYS.DATE_BEFORE];
+ filtersForm[FILTER_KEYS.DATE_AFTER] =
+ filterList.find((filter) => filter.operator === 'gt' && ValidationUtils.isValidDate(filter.value.toString()))?.value.toString() ?? filtersForm[FILTER_KEYS.DATE_AFTER];
}
if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT) {
- filtersForm[FILTER_KEYS.LESS_THAN] = filters[filterKey]?.find((filter) => filter.operator === 'lt' && validateAmount(filter.value.toString(), 2))?.value.toString();
- filtersForm[FILTER_KEYS.GREATER_THAN] = filters[filterKey]?.find((filter) => filter.operator === 'gt' && validateAmount(filter.value.toString(), 2))?.value.toString();
+ // backend amount is an integer and is 2 digits longer than frontend amount
+ filtersForm[FILTER_KEYS.LESS_THAN] =
+ filterList.find((filter) => filter.operator === 'lt' && validateAmount(filter.value.toString(), 0, CONST.IOU.AMOUNT_MAX_LENGTH + 2))?.value.toString() ??
+ filtersForm[FILTER_KEYS.LESS_THAN];
+ filtersForm[FILTER_KEYS.GREATER_THAN] =
+ filterList.find((filter) => filter.operator === 'gt' && validateAmount(filter.value.toString(), 0, CONST.IOU.AMOUNT_MAX_LENGTH + 2))?.value.toString() ??
+ filtersForm[FILTER_KEYS.GREATER_THAN];
}
}
@@ -503,6 +514,10 @@ function getDisplayValue(filterName: string, filter: string, personalDetails: On
if (filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.IN) {
return ReportUtils.getReportName(reports?.[`${ONYXKEYS.COLLECTION.REPORT}${filter}`]) || filter;
}
+ if (filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT) {
+ const frontendAmount = CurrencyUtils.convertToFrontendAmountAsInteger(Number(filter));
+ return Number.isNaN(frontendAmount) ? filter : frontendAmount.toString();
+ }
return filter;
}
@@ -524,8 +539,10 @@ function buildUserReadableQueryString(
let title = `type:${type} status:${status}`;
- Object.keys(filters).forEach((key) => {
- const queryFilter = filters[key as ValueOf] ?? [];
+ for (const filterObject of filters) {
+ const key = filterObject.key;
+ const queryFilter = filterObject.filters;
+
let displayQueryFilters: QueryFilter[] = [];
if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE) {
const taxRateIDs = queryFilter.map((filter) => filter.value.toString());
@@ -549,7 +566,7 @@ function buildUserReadableQueryString(
}));
}
title += buildFilterValuesString(key, displayQueryFilters);
- });
+ }
return title;
}
diff --git a/src/pages/LogInWithShortLivedAuthTokenPage.tsx b/src/pages/LogInWithShortLivedAuthTokenPage.tsx
index fcbeadaa4a47..e604f2ccf847 100644
--- a/src/pages/LogInWithShortLivedAuthTokenPage.tsx
+++ b/src/pages/LogInWithShortLivedAuthTokenPage.tsx
@@ -1,8 +1,7 @@
import type {StackScreenProps} from '@react-navigation/stack';
import React, {useEffect} from 'react';
import {NativeModules} from 'react-native';
-import type {OnyxEntry} from 'react-native-onyx';
-import {withOnyx} from 'react-native-onyx';
+import {useOnyx} from 'react-native-onyx';
import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
import Log from '@libs/Log';
import Navigation from '@libs/Navigation/Navigation';
@@ -13,18 +12,13 @@ import ONYXKEYS from '@src/ONYXKEYS';
import type {Route} from '@src/ROUTES';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
-import type {Account} from '@src/types/onyx';
import SessionExpiredPage from './ErrorPage/SessionExpiredPage';
-type LogInWithShortLivedAuthTokenPageOnyxProps = {
- /** The details about the account that the user is signing in with */
- account: OnyxEntry;
-};
+type LogInWithShortLivedAuthTokenPageProps = StackScreenProps;
-type LogInWithShortLivedAuthTokenPageProps = LogInWithShortLivedAuthTokenPageOnyxProps & StackScreenProps;
-
-function LogInWithShortLivedAuthTokenPage({route, account}: LogInWithShortLivedAuthTokenPageProps) {
+function LogInWithShortLivedAuthTokenPage({route}: LogInWithShortLivedAuthTokenPageProps) {
const {email = '', shortLivedAuthToken = '', shortLivedToken = '', authTokenType, exitTo, error} = route?.params ?? {};
+ const [account] = useOnyx(ONYXKEYS.ACCOUNT);
useEffect(() => {
// We have to check for both shortLivedAuthToken and shortLivedToken, as the old mobile app uses shortLivedToken, and is not being actively updated.
@@ -71,6 +65,4 @@ function LogInWithShortLivedAuthTokenPage({route, account}: LogInWithShortLivedA
LogInWithShortLivedAuthTokenPage.displayName = 'LogInWithShortLivedAuthTokenPage';
-export default withOnyx({
- account: {key: ONYXKEYS.ACCOUNT},
-})(LogInWithShortLivedAuthTokenPage);
+export default LogInWithShortLivedAuthTokenPage;
diff --git a/src/pages/NewChatConfirmPage.tsx b/src/pages/NewChatConfirmPage.tsx
index d6b199def243..85e33cf0c598 100644
--- a/src/pages/NewChatConfirmPage.tsx
+++ b/src/pages/NewChatConfirmPage.tsx
@@ -140,6 +140,7 @@ function NewChatConfirmPage({newGroupDraft, allPersonalDetails}: NewChatConfirmP
{
setAvatarFile(image);
diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx
index 47c1aadf493a..a6b4d2f88a76 100644
--- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx
+++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx
@@ -414,6 +414,7 @@ function ReimbursementAccountPage({route, policy}: ReimbursementAccountPageProps
// Show loading indicator when page is first time being opened and props.reimbursementAccount yet to be loaded from the server
// or when data is being loaded. Don't show the loading indicator if we're offline and restarted the bank account setup process
// On Android, when we open the app from the background, Onfido activity gets destroyed, so we need to reopen it.
+ // eslint-disable-next-line react-compiler/react-compiler
if ((!hasACHDataBeenLoaded || isLoading) && shouldShowOfflineLoader && (shouldReopenOnfido || !requestorStepRef.current)) {
return ;
}
diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx
index baf3d02d7af3..d141920ac99b 100644
--- a/src/pages/home/ReportScreen.tsx
+++ b/src/pages/home/ReportScreen.tsx
@@ -228,6 +228,7 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro
const [scrollPosition, setScrollPosition] = useState({});
const wasReportAccessibleRef = useRef(false);
+ // eslint-disable-next-line react-compiler/react-compiler
if (firstRenderRef.current) {
Timing.start(CONST.TIMING.CHAT_RENDER);
Performance.markStart(CONST.TIMING.CHAT_RENDER);
@@ -384,7 +385,9 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro
return false;
}
+ // eslint-disable-next-line react-compiler/react-compiler
if (!wasReportAccessibleRef.current && !firstRenderRef.current && !reportID && !isOptimisticDelete && !reportMetadata?.isLoadingInitialReportActions && !userLeavingStatus) {
+ // eslint-disable-next-line react-compiler/react-compiler
return true;
}
diff --git a/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx
index 3a4c8beb56c9..f387f09d3880 100644
--- a/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx
+++ b/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable react-compiler/react-compiler */
import type {ForwardedRef} from 'react';
import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react';
diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/index.e2e.tsx b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/index.e2e.tsx
index f325ef10b56f..f3390fe10694 100644
--- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/index.e2e.tsx
+++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/index.e2e.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable react-compiler/react-compiler */
import type {ForwardedRef} from 'react';
import React, {forwardRef, useCallback, useRef} from 'react';
import type {LayoutChangeEvent} from 'react-native';
diff --git a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx
index 2850ed0538db..ef54f2893660 100644
--- a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx
+++ b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx
@@ -61,6 +61,7 @@ function SuggestionEmoji(
) {
const [suggestionValues, setSuggestionValues] = useState(defaultSuggestionsValues);
const suggestionValuesRef = useRef(suggestionValues);
+ // eslint-disable-next-line react-compiler/react-compiler
suggestionValuesRef.current = suggestionValues;
const isEmojiSuggestionsMenuVisible = suggestionValues.suggestedEmojis.length > 0 && suggestionValues.shouldShowSuggestionMenu;
diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx
index 0cb87156cdf2..7a7230fef333 100644
--- a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx
+++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx
@@ -89,6 +89,7 @@ function SuggestionMention(
const {translate, formatPhoneNumber} = useLocalize();
const [suggestionValues, setSuggestionValues] = useState(defaultSuggestionsValues);
const suggestionValuesRef = useRef(suggestionValues);
+ // eslint-disable-next-line react-compiler/react-compiler
suggestionValuesRef.current = suggestionValues;
const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT);
@@ -137,8 +138,10 @@ function SuggestionMention(
// Used to detect if the selection has changed since the last suggestion insertion
// If so, we reset the suggestionInsertionIndexRef
+ // eslint-disable-next-line react-compiler/react-compiler
const hasSelectionChanged = !(selection.end === selection.start && selection.start === suggestionInsertionIndexRef.current);
if (hasSelectionChanged) {
+ // eslint-disable-next-line react-compiler/react-compiler
suggestionInsertionIndexRef.current = null;
}
diff --git a/src/pages/home/report/ReportActionItemMessageEdit.tsx b/src/pages/home/report/ReportActionItemMessageEdit.tsx
index 84bea2600ddf..fd2dc2d44d4b 100644
--- a/src/pages/home/report/ReportActionItemMessageEdit.tsx
+++ b/src/pages/home/report/ReportActionItemMessageEdit.tsx
@@ -243,6 +243,7 @@ function ReportActionItemMessageEdit(
*/
const debouncedSaveDraft = useMemo(
() =>
+ // eslint-disable-next-line react-compiler/react-compiler
lodashDebounce((newDraft: string) => {
Report.saveReportActionDraft(reportID, action, newDraft);
isCommentPendingSaved.current = false;
@@ -559,6 +560,7 @@ function ReportActionItemMessageEdit(
+ {/* eslint-disable-next-line react-compiler/react-compiler */}
{allAncestors.map((ancestor) => (
= useMemo(
@@ -569,6 +572,7 @@ function ReportActionsList({
loadOlderChats(true);
}, [loadOlderChats]);
+ // eslint-disable-next-line react-compiler/react-compiler
const listFooterComponent = useMemo(() => {
// Skip this hook on the first render (when online), as we are not sure if more actions are going to be loaded,
// Therefore showing the skeleton on footer might be misleading.
@@ -604,6 +608,7 @@ function ReportActionsList({
[onContentSizeChange],
);
+ // eslint-disable-next-line react-compiler/react-compiler
const retryLoadNewerChatsError = useCallback(() => {
loadNewerChats(true);
}, [loadNewerChats]);
@@ -611,6 +616,7 @@ function ReportActionsList({
const listHeaderComponent = useMemo(() => {
// In case of an error we want to display the header no matter what.
if (!canShowHeader && !hasLoadingNewerReportActionsError) {
+ // eslint-disable-next-line react-compiler/react-compiler
hasHeaderRendered.current = true;
return null;
}
diff --git a/src/pages/home/report/withReportOrNotFound.tsx b/src/pages/home/report/withReportOrNotFound.tsx
index 8c0f4acbbe39..d74dc84249d4 100644
--- a/src/pages/home/report/withReportOrNotFound.tsx
+++ b/src/pages/home/report/withReportOrNotFound.tsx
@@ -85,6 +85,7 @@ export default function (
// If the content was shown, but it's not anymore, that means the report was deleted, and we are probably navigating out of this screen.
// Return null for this case to avoid rendering FullScreenLoadingIndicator or NotFoundPage when animating transition.
+ // eslint-disable-next-line react-compiler/react-compiler
if (shouldShowNotFoundPage && contentShown.current) {
return null;
}
@@ -98,7 +99,9 @@ export default function (
}
}
+ // eslint-disable-next-line react-compiler/react-compiler
if (!contentShown.current) {
+ // eslint-disable-next-line react-compiler/react-compiler
contentShown.current = true;
}
diff --git a/src/pages/home/sidebar/SidebarLinksData.tsx b/src/pages/home/sidebar/SidebarLinksData.tsx
index e5a74db796d8..7dfbdbaf7299 100644
--- a/src/pages/home/sidebar/SidebarLinksData.tsx
+++ b/src/pages/home/sidebar/SidebarLinksData.tsx
@@ -50,6 +50,7 @@ function SidebarLinksData({insets, isLoadingApp = true, onLinkClick, priorityMod
const isLoading = isLoadingApp;
const currentReportIDRef = useRef(currentReportID);
+ // eslint-disable-next-line react-compiler/react-compiler
currentReportIDRef.current = currentReportID;
const isActiveReport = useCallback((reportID: string): boolean => currentReportIDRef.current === reportID, []);
diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx
index db7aac8268e6..d9f65c451f3c 100644
--- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx
+++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx
@@ -139,6 +139,7 @@ function IOURequestStepScan({
const tapGesture = Gesture.Tap()
.enabled(device?.supportsFocus ?? false)
+ // eslint-disable-next-line react-compiler/react-compiler
.onStart((ev: {x: number; y: number}) => {
const point = {x: ev.x, y: ev.y};
diff --git a/src/pages/settings/AboutPage/ConsolePage.tsx b/src/pages/settings/AboutPage/ConsolePage.tsx
index b156a6c7b2f1..c9124fe814c3 100644
--- a/src/pages/settings/AboutPage/ConsolePage.tsx
+++ b/src/pages/settings/AboutPage/ConsolePage.tsx
@@ -96,6 +96,7 @@ function ConsolePage() {
.reverse();
}, [capturedLogs, shouldStoreLogs]);
+ // eslint-disable-next-line react-compiler/react-compiler
const logsList = useMemo(() => getLogs(), [getLogs]);
const filteredLogsList = useMemo(() => logsList.filter((log) => log.message.includes(activeFilterIndex)), [activeFilterIndex, logsList]);
diff --git a/src/pages/settings/Security/AddDelegate/AddDelegatePage.tsx b/src/pages/settings/Security/AddDelegate/AddDelegatePage.tsx
index 6f433957015f..a6ed5ca1b53e 100644
--- a/src/pages/settings/Security/AddDelegate/AddDelegatePage.tsx
+++ b/src/pages/settings/Security/AddDelegate/AddDelegatePage.tsx
@@ -40,6 +40,7 @@ function useOptions() {
const headerMessage = OptionsListUtils.getHeaderMessage((recentReports?.length || 0) + (personalDetails?.length || 0) !== 0, !!userToInvite, '');
if (isLoading) {
+ // eslint-disable-next-line react-compiler/react-compiler
setIsLoading(false);
}
diff --git a/src/pages/signin/SignInPage.tsx b/src/pages/signin/SignInPage.tsx
index 729faae5e90b..1068cf97197e 100644
--- a/src/pages/signin/SignInPage.tsx
+++ b/src/pages/signin/SignInPage.tsx
@@ -315,6 +315,7 @@ function SignInPage({credentials, account, activeClients = [], preferredLocale,
login={login}
onLoginChanged={setLogin}
blurOnSubmit={account?.validated === false}
+ // eslint-disable-next-line react-compiler/react-compiler
scrollPageToTop={signInPageLayoutRef.current?.scrollPageToTop}
/>
{shouldShouldSignUpWelcomeForm && }
diff --git a/src/pages/tasks/TaskAssigneeSelectorModal.tsx b/src/pages/tasks/TaskAssigneeSelectorModal.tsx
index 5a0912de45a5..59ebe08e41a4 100644
--- a/src/pages/tasks/TaskAssigneeSelectorModal.tsx
+++ b/src/pages/tasks/TaskAssigneeSelectorModal.tsx
@@ -52,6 +52,7 @@ function useOptions() {
const headerMessage = OptionsListUtils.getHeaderMessage((recentReports?.length || 0) + (personalDetails?.length || 0) !== 0 || !!currentUserOption, !!userToInvite, '');
if (isLoading) {
+ // eslint-disable-next-line react-compiler/react-compiler
setIsLoading(false);
}
diff --git a/src/pages/workspace/accounting/qbo/export/QuickbooksOutOfPocketExpenseEntitySelectPage.tsx b/src/pages/workspace/accounting/qbo/export/QuickbooksOutOfPocketExpenseEntitySelectPage.tsx
index 0fd977fb5b13..53114de806ea 100644
--- a/src/pages/workspace/accounting/qbo/export/QuickbooksOutOfPocketExpenseEntitySelectPage.tsx
+++ b/src/pages/workspace/accounting/qbo/export/QuickbooksOutOfPocketExpenseEntitySelectPage.tsx
@@ -18,18 +18,17 @@ import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import type {Account, QBOReimbursableExportAccountType} from '@src/types/onyx/Policy';
-function Footer({isTaxEnabled, isLocationsEnabled}: {isTaxEnabled: boolean; isLocationsEnabled: boolean}) {
+function Footer({isTaxEnabled}: {isTaxEnabled: boolean}) {
const styles = useThemeStyles();
const {translate} = useLocalize();
- if (!isTaxEnabled && !isLocationsEnabled) {
+ if (!isTaxEnabled) {
return null;
}
return (
{isTaxEnabled && {translate('workspace.qbo.outOfPocketTaxEnabledDescription')}}
- {isLocationsEnabled && {translate('workspace.qbo.outOfPocketLocationEnabledDescription')}}
);
}
@@ -43,11 +42,9 @@ function QuickbooksOutOfPocketExpenseEntitySelectPage({policy}: WithPolicyConnec
const styles = useThemeStyles();
const qboConfig = policy?.connections?.quickbooksOnline?.config;
const {bankAccounts, accountPayable, journalEntryAccounts} = policy?.connections?.quickbooksOnline?.data ?? {};
- const isLocationsEnabled = !!(qboConfig?.syncLocations && qboConfig?.syncLocations !== CONST.INTEGRATION_ENTITY_MAP_TYPES.NONE);
const isTaxesEnabled = !!qboConfig?.syncTax;
const shouldShowTaxError = isTaxesEnabled && qboConfig?.reimbursableExpensesExportDestination === CONST.QUICKBOOKS_REIMBURSABLE_ACCOUNT_TYPE.JOURNAL_ENTRY;
- const shouldShowLocationError = isLocationsEnabled && qboConfig?.reimbursableExpensesExportDestination !== CONST.QUICKBOOKS_REIMBURSABLE_ACCOUNT_TYPE.JOURNAL_ENTRY;
- const hasErrors = !!qboConfig?.errorFields?.reimbursableExpensesExportDestination && (shouldShowTaxError || shouldShowLocationError);
+ const hasErrors = !!qboConfig?.errorFields?.reimbursableExpensesExportDestination && shouldShowTaxError;
const policyID = policy?.id ?? '-1';
const data: MenuItem[] = useMemo(
@@ -57,7 +54,7 @@ function QuickbooksOutOfPocketExpenseEntitySelectPage({policy}: WithPolicyConnec
text: translate(`workspace.qbo.accounts.check`),
keyForList: CONST.QUICKBOOKS_REIMBURSABLE_ACCOUNT_TYPE.CHECK,
isSelected: qboConfig?.reimbursableExpensesExportDestination === CONST.QUICKBOOKS_REIMBURSABLE_ACCOUNT_TYPE.CHECK,
- isShown: !isLocationsEnabled,
+ isShown: qboConfig?.syncLocations !== CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG,
accounts: bankAccounts ?? [],
},
{
@@ -73,11 +70,11 @@ function QuickbooksOutOfPocketExpenseEntitySelectPage({policy}: WithPolicyConnec
text: translate(`workspace.qbo.accounts.bill`),
keyForList: CONST.QUICKBOOKS_REIMBURSABLE_ACCOUNT_TYPE.VENDOR_BILL,
isSelected: qboConfig?.reimbursableExpensesExportDestination === CONST.QUICKBOOKS_REIMBURSABLE_ACCOUNT_TYPE.VENDOR_BILL,
- isShown: !isLocationsEnabled,
+ isShown: qboConfig?.syncLocations !== CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG,
accounts: accountPayable ?? [],
},
],
- [qboConfig?.reimbursableExpensesExportDestination, isTaxesEnabled, translate, isLocationsEnabled, bankAccounts, accountPayable, journalEntryAccounts],
+ [qboConfig?.reimbursableExpensesExportDestination, qboConfig?.syncLocations, translate, bankAccounts, accountPayable, journalEntryAccounts, isTaxesEnabled],
);
const sections = useMemo(() => [{data: data.filter((item) => item.isShown)}], [data]);
@@ -128,12 +125,7 @@ function QuickbooksOutOfPocketExpenseEntitySelectPage({policy}: WithPolicyConnec
}
errorRowStyles={[styles.ph5, styles.pv3]}
onClose={() => clearQBOErrorField(policyID, CONST.QUICKBOOKS_CONFIG.REIMBURSABLE_EXPENSES_EXPORT_DESTINATION)}
- listFooterContent={
-
- }
+ listFooterContent={}
/>
);
}
diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksClassesDisplayedAsPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksClassesDisplayedAsPage.tsx
new file mode 100644
index 000000000000..808fd924bbd0
--- /dev/null
+++ b/src/pages/workspace/accounting/qbo/import/QuickbooksClassesDisplayedAsPage.tsx
@@ -0,0 +1,79 @@
+import React, {useCallback, useMemo} from 'react';
+import RadioListItem from '@components/SelectionList/RadioListItem';
+import type {ListItem} from '@components/SelectionList/types';
+import SelectionScreen from '@components/SelectionScreen';
+import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
+import * as QuickbooksOnline from '@libs/actions/connections/QuickbooksOnline';
+import * as ErrorUtils from '@libs/ErrorUtils';
+import Navigation from '@libs/Navigation/Navigation';
+import {settingsPendingAction} from '@libs/PolicyUtils';
+import type {WithPolicyProps} from '@pages/workspace/withPolicy';
+import withPolicyConnections from '@pages/workspace/withPolicyConnections';
+import {clearQBOErrorField} from '@userActions/Policy/Policy';
+import CONST from '@src/CONST';
+import ROUTES from '@src/ROUTES';
+
+type CardListItem = ListItem & {
+ value: keyof typeof CONST.INTEGRATION_ENTITY_MAP_TYPES;
+};
+
+function QuickbooksClassesDisplayedAsPage({policy}: WithPolicyProps) {
+ const {translate} = useLocalize();
+ const styles = useThemeStyles();
+ const policyID = policy?.id ?? '-1';
+ const qboConfig = policy?.connections?.quickbooksOnline?.config;
+
+ const data: CardListItem[] = useMemo(
+ () => [
+ {
+ value: CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG,
+ text: translate('workspace.common.tags'),
+ keyForList: CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG,
+ isSelected: qboConfig?.syncClasses === CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG,
+ },
+ {
+ value: CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD,
+ text: translate('workspace.common.reportFields'),
+ keyForList: CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD,
+ isSelected: qboConfig?.syncClasses === CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD,
+ },
+ ],
+ [qboConfig?.syncClasses, translate],
+ );
+
+ const selectDisplayedAs = useCallback(
+ (row: CardListItem) => {
+ if (row.value !== qboConfig?.syncClasses) {
+ QuickbooksOnline.updateQuickbooksOnlineSyncClasses(policyID, row.value, qboConfig?.syncClasses);
+ }
+ Navigation.goBack(ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_CLASSES.getRoute(policyID));
+ },
+ [qboConfig?.syncClasses, policyID],
+ );
+
+ return (
+ Navigation.goBack(ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_CLASSES.getRoute(policyID))}
+ onSelectRow={selectDisplayedAs}
+ shouldSingleExecuteRowSelect
+ initiallyFocusedOptionKey={data.find((item) => item.isSelected)?.keyForList}
+ title="workspace.common.displayedAs"
+ connectionName={CONST.POLICY.CONNECTIONS.NAME.QBO}
+ pendingAction={settingsPendingAction([CONST.QUICKBOOKS_CONFIG.SYNC_CLASSES], qboConfig?.pendingFields)}
+ errors={ErrorUtils.getLatestErrorField(qboConfig, CONST.QUICKBOOKS_CONFIG.SYNC_CLASSES)}
+ errorRowStyles={[styles.ph5, styles.pv3]}
+ onClose={() => clearQBOErrorField(policyID, CONST.QUICKBOOKS_CONFIG.SYNC_CLASSES)}
+ />
+ );
+}
+
+QuickbooksClassesDisplayedAsPage.displayName = 'QuickbooksClassesDisplayedAsPage';
+
+export default withPolicyConnections(QuickbooksClassesDisplayedAsPage);
diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx
index 70eb9bf8cdb5..ae72999ad975 100644
--- a/src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx
+++ b/src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx
@@ -1,12 +1,13 @@
import React from 'react';
import ConnectionLayout from '@components/ConnectionLayout';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
+import OfflineWithFeedback from '@components/OfflineWithFeedback';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as QuickbooksOnline from '@libs/actions/connections/QuickbooksOnline';
import * as ErrorUtils from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
-import {settingsPendingAction} from '@libs/PolicyUtils';
+import {areSettingsInErrorFields, settingsPendingAction} from '@libs/PolicyUtils';
import type {WithPolicyProps} from '@pages/workspace/withPolicy';
import withPolicyConnections from '@pages/workspace/withPolicyConnections';
import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow';
@@ -50,12 +51,16 @@ function QuickbooksClassesPage({policy}: WithPolicyProps) {
onCloseError={() => clearQBOErrorField(policyID, CONST.QUICKBOOKS_CONFIG.SYNC_CLASSES)}
/>
{isSwitchOn && (
-
+
+ Navigation.navigate(ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_CLASSES_DISPLAYED_AS.getRoute(policyID))}
+ shouldShowRightIcon
+ wrapperStyle={[styles.sectionMenuItemTopDescription, styles.mt4]}
+ brickRoadIndicator={areSettingsInErrorFields([CONST.QUICKBOOKS_CONFIG.SYNC_CLASSES], qboConfig?.errorFields) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
+ />
+
)}
);
diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksCustomersDisplayedAsPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksCustomersDisplayedAsPage.tsx
new file mode 100644
index 000000000000..9c05247d9d98
--- /dev/null
+++ b/src/pages/workspace/accounting/qbo/import/QuickbooksCustomersDisplayedAsPage.tsx
@@ -0,0 +1,79 @@
+import React, {useCallback, useMemo} from 'react';
+import RadioListItem from '@components/SelectionList/RadioListItem';
+import type {ListItem} from '@components/SelectionList/types';
+import SelectionScreen from '@components/SelectionScreen';
+import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
+import * as QuickbooksOnline from '@libs/actions/connections/QuickbooksOnline';
+import * as ErrorUtils from '@libs/ErrorUtils';
+import Navigation from '@libs/Navigation/Navigation';
+import {settingsPendingAction} from '@libs/PolicyUtils';
+import type {WithPolicyProps} from '@pages/workspace/withPolicy';
+import withPolicyConnections from '@pages/workspace/withPolicyConnections';
+import {clearQBOErrorField} from '@userActions/Policy/Policy';
+import CONST from '@src/CONST';
+import ROUTES from '@src/ROUTES';
+
+type CardListItem = ListItem & {
+ value: keyof typeof CONST.INTEGRATION_ENTITY_MAP_TYPES;
+};
+
+function QuickbooksCustomersDisplayedAsPage({policy}: WithPolicyProps) {
+ const {translate} = useLocalize();
+ const styles = useThemeStyles();
+ const policyID = policy?.id ?? '-1';
+ const qboConfig = policy?.connections?.quickbooksOnline?.config;
+
+ const data: CardListItem[] = useMemo(
+ () => [
+ {
+ value: CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG,
+ text: translate('workspace.common.tags'),
+ keyForList: CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG,
+ isSelected: qboConfig?.syncCustomers === CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG,
+ },
+ {
+ value: CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD,
+ text: translate('workspace.common.reportFields'),
+ keyForList: CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD,
+ isSelected: qboConfig?.syncCustomers === CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD,
+ },
+ ],
+ [qboConfig?.syncCustomers, translate],
+ );
+
+ const selectDisplayedAs = useCallback(
+ (row: CardListItem) => {
+ if (row.value !== qboConfig?.syncCustomers) {
+ QuickbooksOnline.updateQuickbooksOnlineSyncCustomers(policyID, row.value, qboConfig?.syncCustomers);
+ }
+ Navigation.goBack(ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_CUSTOMERS.getRoute(policyID));
+ },
+ [qboConfig?.syncCustomers, policyID],
+ );
+
+ return (
+ Navigation.goBack(ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_CUSTOMERS.getRoute(policyID))}
+ onSelectRow={(selection) => selectDisplayedAs(selection as CardListItem)}
+ shouldSingleExecuteRowSelect
+ initiallyFocusedOptionKey={data.find((item) => item.isSelected)?.keyForList}
+ title="workspace.common.displayedAs"
+ connectionName={CONST.POLICY.CONNECTIONS.NAME.QBO}
+ pendingAction={settingsPendingAction([CONST.QUICKBOOKS_CONFIG.SYNC_CUSTOMERS], qboConfig?.pendingFields)}
+ errors={ErrorUtils.getLatestErrorField(qboConfig, CONST.QUICKBOOKS_CONFIG.SYNC_CUSTOMERS)}
+ errorRowStyles={[styles.ph5, styles.pv3]}
+ onClose={() => clearQBOErrorField(policyID, CONST.QUICKBOOKS_CONFIG.SYNC_CUSTOMERS)}
+ />
+ );
+}
+
+QuickbooksCustomersDisplayedAsPage.displayName = 'QuickbooksCustomersDisplayedAsPage';
+
+export default withPolicyConnections(QuickbooksCustomersDisplayedAsPage);
diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksCustomersPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksCustomersPage.tsx
index 67a326858379..8b842e737de6 100644
--- a/src/pages/workspace/accounting/qbo/import/QuickbooksCustomersPage.tsx
+++ b/src/pages/workspace/accounting/qbo/import/QuickbooksCustomersPage.tsx
@@ -1,12 +1,13 @@
import React from 'react';
import ConnectionLayout from '@components/ConnectionLayout';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
+import OfflineWithFeedback from '@components/OfflineWithFeedback';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as QuickbooksOnline from '@libs/actions/connections/QuickbooksOnline';
import * as ErrorUtils from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
-import {settingsPendingAction} from '@libs/PolicyUtils';
+import {areSettingsInErrorFields, settingsPendingAction} from '@libs/PolicyUtils';
import type {WithPolicyProps} from '@pages/workspace/withPolicy';
import withPolicyConnections from '@pages/workspace/withPolicyConnections';
import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow';
@@ -49,12 +50,16 @@ function QuickbooksCustomersPage({policy}: WithPolicyProps) {
onCloseError={() => clearQBOErrorField(policyID, CONST.QUICKBOOKS_CONFIG.SYNC_CUSTOMERS)}
/>
{isSwitchOn && (
-
+
+ Navigation.navigate(ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_CUSTOMERS_DISPLAYED_AS.getRoute(policyID))}
+ shouldShowRightIcon
+ wrapperStyle={[styles.sectionMenuItemTopDescription, styles.mt4]}
+ brickRoadIndicator={areSettingsInErrorFields([CONST.QUICKBOOKS_CONFIG.SYNC_CUSTOMERS], qboConfig?.errorFields) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
+ />
+
)}
);
diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksImportPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksImportPage.tsx
index 56089d9d25d5..aa2a3156038f 100644
--- a/src/pages/workspace/accounting/qbo/import/QuickbooksImportPage.tsx
+++ b/src/pages/workspace/accounting/qbo/import/QuickbooksImportPage.tsx
@@ -1,11 +1,13 @@
-import React from 'react';
+import React, {useEffect} from 'react';
import ConnectionLayout from '@components/ConnectionLayout';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
+import * as QuickbooksOnline from '@libs/actions/connections/QuickbooksOnline';
import * as PolicyUtils from '@libs/PolicyUtils';
import Navigation from '@navigation/Navigation';
+import {shouldSwitchLocationsToReportFields} from '@pages/workspace/accounting/qbo/utils';
import type {WithPolicyProps} from '@pages/workspace/withPolicy';
import withPolicyConnections from '@pages/workspace/withPolicyConnections';
import CONST from '@src/CONST';
@@ -22,7 +24,16 @@ function QuickbooksImportPage({policy}: WithPolicyProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const policyID = policy?.id ?? '-1';
- const {syncClasses, syncCustomers, syncLocations, syncTax, pendingFields, errorFields} = policy?.connections?.quickbooksOnline?.config ?? {};
+ const qboConfig = policy?.connections?.quickbooksOnline?.config;
+ const {syncClasses, syncCustomers, syncLocations, syncTax, pendingFields, errorFields} = qboConfig ?? {};
+
+ // If we previously selected tags but now we have the line items restriction for locations, we need to switch to report fields
+ useEffect(() => {
+ if (!shouldSwitchLocationsToReportFields(qboConfig)) {
+ return;
+ }
+ QuickbooksOnline.updateQuickbooksOnlineSyncLocations(policyID, CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD, qboConfig?.syncLocations);
+ }, [qboConfig, policyID]);
const sections: QBOSectionType[] = [
{
diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksLocationsDisplayedAsPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksLocationsDisplayedAsPage.tsx
new file mode 100644
index 000000000000..a974ac64b6ec
--- /dev/null
+++ b/src/pages/workspace/accounting/qbo/import/QuickbooksLocationsDisplayedAsPage.tsx
@@ -0,0 +1,86 @@
+import React, {useCallback, useMemo} from 'react';
+import RadioListItem from '@components/SelectionList/RadioListItem';
+import type {ListItem} from '@components/SelectionList/types';
+import SelectionScreen from '@components/SelectionScreen';
+import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
+import * as QuickbooksOnline from '@libs/actions/connections/QuickbooksOnline';
+import * as ErrorUtils from '@libs/ErrorUtils';
+import Navigation from '@libs/Navigation/Navigation';
+import {settingsPendingAction} from '@libs/PolicyUtils';
+import type {WithPolicyProps} from '@pages/workspace/withPolicy';
+import withPolicyConnections from '@pages/workspace/withPolicyConnections';
+import {clearQBOErrorField} from '@userActions/Policy/Policy';
+import CONST from '@src/CONST';
+import ROUTES from '@src/ROUTES';
+
+type CardListItem = ListItem & {
+ value: keyof typeof CONST.INTEGRATION_ENTITY_MAP_TYPES;
+};
+
+function QuickbooksLocationsDisplayedAsPage({policy}: WithPolicyProps) {
+ const {translate} = useLocalize();
+ const styles = useThemeStyles();
+ const policyID = policy?.id ?? '-1';
+ const qboConfig = policy?.connections?.quickbooksOnline?.config;
+
+ const data: CardListItem[] = useMemo(() => {
+ const items: CardListItem[] = [];
+
+ if (
+ qboConfig?.reimbursableExpensesExportDestination === CONST.QUICKBOOKS_REIMBURSABLE_ACCOUNT_TYPE.JOURNAL_ENTRY &&
+ qboConfig?.nonReimbursableExpensesExportDestination === CONST.QUICKBOOKS_NON_REIMBURSABLE_ACCOUNT_TYPE.CREDIT_CARD
+ ) {
+ items.push({
+ value: CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG,
+ text: translate('workspace.common.tags'),
+ keyForList: CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG,
+ isSelected: qboConfig?.syncLocations === CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG,
+ });
+ }
+
+ items.push({
+ value: CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD,
+ text: translate('workspace.common.reportFields'),
+ keyForList: CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD,
+ isSelected: qboConfig?.syncLocations === CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD,
+ });
+
+ return items;
+ }, [qboConfig?.syncLocations, qboConfig?.reimbursableExpensesExportDestination, qboConfig?.nonReimbursableExpensesExportDestination, translate]);
+
+ const selectDisplayedAs = useCallback(
+ (row: CardListItem) => {
+ if (row.value !== qboConfig?.syncLocations) {
+ QuickbooksOnline.updateQuickbooksOnlineSyncLocations(policyID, row.value, qboConfig?.syncLocations);
+ }
+ Navigation.goBack(ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_LOCATIONS.getRoute(policyID));
+ },
+ [qboConfig?.syncLocations, policyID],
+ );
+
+ return (
+ Navigation.goBack(ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_LOCATIONS.getRoute(policyID))}
+ onSelectRow={(selection) => selectDisplayedAs(selection as CardListItem)}
+ shouldSingleExecuteRowSelect
+ initiallyFocusedOptionKey={data.find((item) => item.isSelected)?.keyForList}
+ title="workspace.common.displayedAs"
+ connectionName={CONST.POLICY.CONNECTIONS.NAME.QBO}
+ pendingAction={settingsPendingAction([CONST.QUICKBOOKS_CONFIG.SYNC_LOCATIONS], qboConfig?.pendingFields)}
+ errors={ErrorUtils.getLatestErrorField(qboConfig, CONST.QUICKBOOKS_CONFIG.SYNC_LOCATIONS)}
+ errorRowStyles={[styles.ph5, styles.pv3]}
+ onClose={() => clearQBOErrorField(policyID, CONST.QUICKBOOKS_CONFIG.SYNC_LOCATIONS)}
+ />
+ );
+}
+
+QuickbooksLocationsDisplayedAsPage.displayName = 'QuickbooksLocationsDisplayedAsPage';
+
+export default withPolicyConnections(QuickbooksLocationsDisplayedAsPage);
diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksLocationsPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksLocationsPage.tsx
index f619dfc2772e..c84421382f82 100644
--- a/src/pages/workspace/accounting/qbo/import/QuickbooksLocationsPage.tsx
+++ b/src/pages/workspace/accounting/qbo/import/QuickbooksLocationsPage.tsx
@@ -1,7 +1,8 @@
-import React from 'react';
+import React, {useEffect} from 'react';
import {View} from 'react-native';
import ConnectionLayout from '@components/ConnectionLayout';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
+import OfflineWithFeedback from '@components/OfflineWithFeedback';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
@@ -9,6 +10,7 @@ import * as QuickbooksOnline from '@libs/actions/connections/QuickbooksOnline';
import * as ErrorUtils from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as PolicyUtils from '@libs/PolicyUtils';
+import {shouldShowLocationsLineItemsRestriction, shouldSwitchLocationsToReportFields} from '@pages/workspace/accounting/qbo/utils';
import type {WithPolicyProps} from '@pages/workspace/withPolicy';
import withPolicyConnections from '@pages/workspace/withPolicyConnections';
import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow';
@@ -22,11 +24,16 @@ function QuickbooksLocationsPage({policy}: WithPolicyProps) {
const policyID = policy?.id ?? '-1';
const qboConfig = policy?.connections?.quickbooksOnline?.config;
const isSwitchOn = !!(qboConfig?.syncLocations && qboConfig?.syncLocations !== CONST.INTEGRATION_ENTITY_MAP_TYPES.NONE);
- const canImportLocation =
- qboConfig?.reimbursableExpensesExportDestination === CONST.QUICKBOOKS_REIMBURSABLE_ACCOUNT_TYPE.JOURNAL_ENTRY &&
- qboConfig?.nonReimbursableExpensesExportDestination !== CONST.QUICKBOOKS_NON_REIMBURSABLE_EXPORT_ACCOUNT_TYPE.VENDOR_BILL;
- const shouldBeDisabled = !canImportLocation && !isSwitchOn;
- const isReportFieldsSelected = qboConfig?.syncLocations === CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD;
+ const isTagsSelected = qboConfig?.syncLocations === CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG;
+ const shouldShowLineItemsRestriction = shouldShowLocationsLineItemsRestriction(qboConfig);
+
+ // If we previously selected tags but now we have the line items restriction, we need to switch to report fields
+ useEffect(() => {
+ if (!shouldSwitchLocationsToReportFields(qboConfig)) {
+ return;
+ }
+ QuickbooksOnline.updateQuickbooksOnlineSyncLocations(policyID, CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD, qboConfig?.syncLocations);
+ }, [qboConfig, policyID]);
return (
QuickbooksOnline.updateQuickbooksOnlineSyncLocations(
policyID,
- isSwitchOn ? CONST.INTEGRATION_ENTITY_MAP_TYPES.NONE : CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG,
+ // eslint-disable-next-line no-nested-ternary
+ isSwitchOn
+ ? CONST.INTEGRATION_ENTITY_MAP_TYPES.NONE
+ : shouldShowLineItemsRestriction
+ ? CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD
+ : CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG,
qboConfig?.syncLocations,
)
}
@@ -57,16 +68,24 @@ function QuickbooksLocationsPage({policy}: WithPolicyProps) {
pendingAction={PolicyUtils.settingsPendingAction([CONST.QUICKBOOKS_CONFIG.SYNC_LOCATIONS], qboConfig?.pendingFields)}
/>
{isSwitchOn && (
-
+
+ Navigation.navigate(ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_LOCATIONS_DISPLAYED_AS.getRoute(policyID))}
+ shouldShowRightIcon={!shouldShowLineItemsRestriction}
+ wrapperStyle={[styles.sectionMenuItemTopDescription, styles.mt4]}
+ brickRoadIndicator={
+ PolicyUtils.areSettingsInErrorFields([CONST.QUICKBOOKS_CONFIG.SYNC_LOCATIONS], qboConfig?.errorFields) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined
+ }
+ />
+
)}
- {shouldBeDisabled && (
+
+ {shouldShowLineItemsRestriction && isSwitchOn && (
- {translate('workspace.qbo.locationsAdditionalDescription')}
+ {translate('workspace.qbo.locationsLineItemsRestrictionDescription')}
)}
diff --git a/src/pages/workspace/accounting/qbo/utils.ts b/src/pages/workspace/accounting/qbo/utils.ts
new file mode 100644
index 000000000000..1b5d5b8ed66d
--- /dev/null
+++ b/src/pages/workspace/accounting/qbo/utils.ts
@@ -0,0 +1,15 @@
+import CONST from '@src/CONST';
+import type {QBOConnectionConfig} from '@src/types/onyx/Policy';
+
+function shouldShowLocationsLineItemsRestriction(config?: QBOConnectionConfig): boolean {
+ return !(
+ config?.reimbursableExpensesExportDestination === CONST.QUICKBOOKS_REIMBURSABLE_ACCOUNT_TYPE.JOURNAL_ENTRY &&
+ config?.nonReimbursableExpensesExportDestination === CONST.QUICKBOOKS_NON_REIMBURSABLE_ACCOUNT_TYPE.CREDIT_CARD
+ );
+}
+
+function shouldSwitchLocationsToReportFields(config?: QBOConnectionConfig): boolean {
+ return config?.syncLocations === CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG && shouldShowLocationsLineItemsRestriction(config);
+}
+
+export {shouldShowLocationsLineItemsRestriction, shouldSwitchLocationsToReportFields};
diff --git a/src/pages/workspace/accounting/xero/XeroMapTrackingCategoryConfigurationPage.tsx b/src/pages/workspace/accounting/xero/XeroMapTrackingCategoryConfigurationPage.tsx
index dae46f32dc54..6585e89011e0 100644
--- a/src/pages/workspace/accounting/xero/XeroMapTrackingCategoryConfigurationPage.tsx
+++ b/src/pages/workspace/accounting/xero/XeroMapTrackingCategoryConfigurationPage.tsx
@@ -41,7 +41,7 @@ function XeroMapTrackingCategoryConfigurationPage({policy}: WithPolicyProps) {
() =>
Object.values(CONST.XERO_CONFIG.TRACKING_CATEGORY_OPTIONS).map((option) => ({
value: option,
- text: translate(`workspace.xero.trackingCategoriesOptions.${option.toLowerCase()}` as TranslationPaths),
+ text: translate(`workspace.xero.trackingCategoriesOptions.${option.toUpperCase()}` as TranslationPaths),
keyForList: option,
isSelected: option === currentTrackingCategoryValue,
})),
diff --git a/src/pages/workspace/accounting/xero/XeroTrackingCategoryConfigurationPage.tsx b/src/pages/workspace/accounting/xero/XeroTrackingCategoryConfigurationPage.tsx
index 5aa6b2cd4efe..1e9f7382f51f 100644
--- a/src/pages/workspace/accounting/xero/XeroTrackingCategoryConfigurationPage.tsx
+++ b/src/pages/workspace/accounting/xero/XeroTrackingCategoryConfigurationPage.tsx
@@ -32,7 +32,11 @@ function XeroTrackingCategoryConfigurationPage({policy}: WithPolicyProps) {
id: category.id,
description: translate('workspace.xero.mapTrackingCategoryTo', {categoryName: category.name}) as TranslationPaths,
onPress: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_TRACKING_CATEGORIES_MAP.getRoute(policyID, category.id, category.name)),
- title: translate(`workspace.xero.trackingCategoriesOptions.${!StringUtils.isEmptyString(category.value) ? category.value.toLowerCase() : 'default'}` as TranslationPaths),
+ title: translate(
+ `workspace.xero.trackingCategoriesOptions.${
+ !StringUtils.isEmptyString(category.value) ? category.value.toUpperCase() : CONST.XERO_CONFIG.TRACKING_CATEGORY_OPTIONS.DEFAULT
+ }` as TranslationPaths,
+ ),
}));
}, [translate, policy, policyID]);
diff --git a/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsApproverPage.tsx b/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsApproverPage.tsx
index 56cf5a0eb90b..1c952617dab5 100644
--- a/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsApproverPage.tsx
+++ b/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsApproverPage.tsx
@@ -117,6 +117,7 @@ function WorkspaceWorkflowsApprovalsApproverPage({policy, personalDetails, isLoa
.filter((approver): approver is SelectionListApprover => !!approver);
approvers.push(...availableApprovers);
+ // eslint-disable-next-line react-compiler/react-compiler
setAllApprovers(approvers);
}
diff --git a/src/styles/index.ts b/src/styles/index.ts
index 1bd77a7974e5..5b04a7a8eace 100644
--- a/src/styles/index.ts
+++ b/src/styles/index.ts
@@ -5264,6 +5264,11 @@ const styles = (theme: ThemeColors) =>
backgroundColor: theme.border,
},
+ integrationIcon: {
+ overflow: 'hidden',
+ borderRadius: variables.buttonBorderRadius,
+ },
+
colorGreenSuccess: {
color: colors.green400,
},
diff --git a/src/styles/utils/generators/ReportActionContextMenuStyleUtils.ts b/src/styles/utils/generators/ReportActionContextMenuStyleUtils.ts
index 1894f95a44c1..0bf999e7a8d6 100644
--- a/src/styles/utils/generators/ReportActionContextMenuStyleUtils.ts
+++ b/src/styles/utils/generators/ReportActionContextMenuStyleUtils.ts
@@ -19,8 +19,6 @@ const getMiniWrapperStyle = (theme: ThemeColors, styles: ThemeStyles): ViewStyle
borderRadius: variables.buttonBorderRadius,
borderWidth: 1,
borderColor: theme.border,
- // In Safari, when welcome messages use a code block (triple backticks), they would overlap the context menu below when there is no scrollbar without the transform style.
- transform: 'translateZ(0)',
},
];
diff --git a/src/styles/variables.ts b/src/styles/variables.ts
index dc6655791489..4e9bf50e3c8e 100644
--- a/src/styles/variables.ts
+++ b/src/styles/variables.ts
@@ -83,6 +83,7 @@ export default {
iconSizeExtraLarge: 40,
iconSizeSuperLarge: 60,
iconSizeUltraLarge: 80,
+ iconSizeMenuItem: 32,
iconBottomBar: 24,
sidebarAvatarSize: 28,
iconHeader: 48,