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={ -