diff --git a/.github/workflows/reassurePerformanceTests.yml b/.github/workflows/reassurePerformanceTests.yml index 85fb866b05c4..9887943c77e0 100644 --- a/.github/workflows/reassurePerformanceTests.yml +++ b/.github/workflows/reassurePerformanceTests.yml @@ -69,9 +69,6 @@ jobs: uses: ./.github/actions/javascript/getGraphiteString - name: Send graphite data - env: - GRAPHITE_SERVER: ${{ vars.GRAPHITE_SERVER }} - GRAPHITE_PORT: ${{ vars.GRAPHITE_PORT }} # run only when merged to main if: github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'main' - run: echo -e "${{ steps.saveGraphiteString.outputs.GRAPHITE_STRING }}" | nc -q0 "$GRAPHITE_SERVER" "$GRAPHITE_PORT" + run: echo -e "${{ steps.saveGraphiteString.outputs.GRAPHITE_STRING }}" | nc -q0 stats.expensify.com 3003 diff --git a/android/app/build.gradle b/android/app/build.gradle index cb12616ee216..5f5a5a60b1fc 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -98,8 +98,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001045001 - versionName "1.4.50-1" + versionCode 1001045003 + versionName "1.4.50-3" } flavorDimensions "default" @@ -181,7 +181,6 @@ android { dependencies { // The version of react-native is set by the React Native Gradle Plugin implementation("com.facebook.react:react-android") - implementation("com.facebook.react:flipper-integration") if (hermesEnabled.toBoolean()) { implementation("com.facebook.react:hermes-android") diff --git a/assets/images/simple-illustrations/simple-illustration__accounting.svg b/assets/images/simple-illustrations/simple-illustration__accounting.svg new file mode 100644 index 000000000000..f7634141e966 --- /dev/null +++ b/assets/images/simple-illustrations/simple-illustration__accounting.svg @@ -0,0 +1,32 @@ + diff --git a/assets/images/simple-illustrations/simple-illustration__car.svg b/assets/images/simple-illustrations/simple-illustration__car.svg new file mode 100644 index 000000000000..2d420be6c3a9 --- /dev/null +++ b/assets/images/simple-illustrations/simple-illustration__car.svg @@ -0,0 +1,25 @@ + diff --git a/assets/images/simple-illustrations/simple-illustration__coins.svg b/assets/images/simple-illustrations/simple-illustration__coins.svg new file mode 100644 index 000000000000..5350886402c6 --- /dev/null +++ b/assets/images/simple-illustrations/simple-illustration__coins.svg @@ -0,0 +1,26 @@ + diff --git a/assets/images/simple-illustrations/simple-illustration__pencil.svg b/assets/images/simple-illustrations/simple-illustration__pencil.svg new file mode 100644 index 000000000000..8d9f06991612 --- /dev/null +++ b/assets/images/simple-illustrations/simple-illustration__pencil.svg @@ -0,0 +1,20 @@ + diff --git a/assets/images/simple-illustrations/simple-illustration__workflows.svg b/assets/images/simple-illustrations/simple-illustration__workflows.svg index 47d30d54310f..b684c58126f7 100644 --- a/assets/images/simple-illustrations/simple-illustration__workflows.svg +++ b/assets/images/simple-illustrations/simple-illustration__workflows.svg @@ -1 +1,153 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/babel.config.js b/babel.config.js index 2a09d086dc5c..7e90fca1c9be 100644 --- a/babel.config.js +++ b/babel.config.js @@ -82,7 +82,7 @@ const metro = { }; /* - * We use Flipper, and react-native-performance to capture/monitor stats + * We use and react-native-performance to capture/monitor stats * By default is disabled in production as it adds small overhead * When CAPTURE_METRICS is set we're explicitly saying that we want to capture metrics * To enable the for release builds we add these aliases */ diff --git a/config/webpack/webpack.common.js b/config/webpack/webpack.common.js index 170198987793..2fed8a477aab 100644 --- a/config/webpack/webpack.common.js +++ b/config/webpack/webpack.common.js @@ -18,7 +18,6 @@ const includeModules = [ '@react-native-picker', 'react-native-modal', 'react-native-gesture-handler', - 'react-native-flipper', 'react-native-google-places-autocomplete', 'react-native-qrcode-svg', 'react-native-view-shot', diff --git a/ios/NewExpensify.xcodeproj/project.pbxproj b/ios/NewExpensify.xcodeproj/project.pbxproj index acfc4d933954..e39542ef0303 100644 --- a/ios/NewExpensify.xcodeproj/project.pbxproj +++ b/ios/NewExpensify.xcodeproj/project.pbxproj @@ -543,13 +543,10 @@ "${PODS_ROOT}/Target Support Files/Pods-NewExpensify-NewExpensifyTests/Pods-NewExpensify-NewExpensifyTests-frameworks.sh", "${BUILT_PRODUCTS_DIR}/MapboxMaps/MapboxMaps.framework", "${BUILT_PRODUCTS_DIR}/Turf/Turf.framework", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/Flipper-DoubleConversion/double-conversion.framework/double-conversion", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/Flipper-Glog/glog.framework/glog", "${PODS_XCFRAMEWORKS_BUILD_DIR}/MapboxCommon/MapboxCommon.framework/MapboxCommon", "${PODS_XCFRAMEWORKS_BUILD_DIR}/MapboxCoreMaps/MapboxCoreMaps.framework/MapboxCoreMaps", "${PODS_XCFRAMEWORKS_BUILD_DIR}/MapboxMobileEvents/MapboxMobileEvents.framework/MapboxMobileEvents", "${PODS_XCFRAMEWORKS_BUILD_DIR}/Onfido/Onfido.framework/Onfido", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL-Universal/OpenSSL.framework/OpenSSL", "${PODS_XCFRAMEWORKS_BUILD_DIR}/Plaid/LinkKit.framework/LinkKit", "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", ); @@ -557,13 +554,10 @@ outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxMaps.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Turf.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/double-conversion.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/glog.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxCommon.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxCoreMaps.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxMobileEvents.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Onfido.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/LinkKit.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", ); @@ -613,13 +607,10 @@ "${PODS_ROOT}/Target Support Files/Pods-NewExpensify/Pods-NewExpensify-frameworks.sh", "${BUILT_PRODUCTS_DIR}/MapboxMaps/MapboxMaps.framework", "${BUILT_PRODUCTS_DIR}/Turf/Turf.framework", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/Flipper-DoubleConversion/double-conversion.framework/double-conversion", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/Flipper-Glog/glog.framework/glog", "${PODS_XCFRAMEWORKS_BUILD_DIR}/MapboxCommon/MapboxCommon.framework/MapboxCommon", "${PODS_XCFRAMEWORKS_BUILD_DIR}/MapboxCoreMaps/MapboxCoreMaps.framework/MapboxCoreMaps", "${PODS_XCFRAMEWORKS_BUILD_DIR}/MapboxMobileEvents/MapboxMobileEvents.framework/MapboxMobileEvents", "${PODS_XCFRAMEWORKS_BUILD_DIR}/Onfido/Onfido.framework/Onfido", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL-Universal/OpenSSL.framework/OpenSSL", "${PODS_XCFRAMEWORKS_BUILD_DIR}/Plaid/LinkKit.framework/LinkKit", "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", ); @@ -627,13 +618,10 @@ outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxMaps.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Turf.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/double-conversion.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/glog.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxCommon.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxCoreMaps.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MapboxMobileEvents.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Onfido.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/LinkKit.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", ); diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 97eb55747062..a82a1d598d95 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.50.1 + 1.4.50.3 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index af8e99100825..90f736a246fa 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 1.4.50.1 + 1.4.50.3 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index a4c7b52661b6..276cad0c9f9a 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 1.4.50 CFBundleVersion - 1.4.50.1 + 1.4.50.3 NSExtension NSExtensionPointIdentifier diff --git a/ios/Podfile b/ios/Podfile index aa87c3e295f3..83c21797bd0a 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -26,17 +26,6 @@ setup_permissions([ 'LocationWhenInUse' ]) -# If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set. -# because `react-native-flipper` depends on (FlipperKit,...) that will be excluded -# -# To fix this you can also exclude `react-native-flipper` using a `react-native.config.js` -# ```js -# module.exports = { -# dependencies: { -# ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}), -# ``` -flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled(['DebugProduction', 'DebugDevelopment', 'DebugAdHoc']) - linkage = ENV['USE_FRAMEWORKS'] if linkage != nil Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green @@ -86,11 +75,6 @@ target 'NewExpensify' do use_react_native!( :path => config[:reactNativePath], - # Enables Flipper. - # - # Note that if you have use_frameworks! enabled, Flipper will not work and - # you should disable the next line. - :flipper_configuration => flipper_config, # An absolute path to your application root. :app_path => "#{Pod::Config.instance.installation_root}/.." ) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index bb26b37d4015..d0007ec51668 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -30,7 +30,6 @@ PODS: - boost (1.83.0) - BVLinearGradient (2.8.1): - React-Core - - CocoaAsyncSocket (7.6.5) - DoubleConversion (1.1.6) - EXAV (13.10.4): - ExpoModulesCore @@ -127,62 +126,6 @@ PODS: - FirebaseInstallations (~> 8.0) - GoogleUtilities/Environment (~> 7.7) - "GoogleUtilities/NSData+zlib (~> 7.7)" - - Flipper (0.201.0): - - Flipper-Folly (~> 2.6) - - Flipper-Boost-iOSX (1.76.0.1.11) - - Flipper-DoubleConversion (3.2.0.1) - - Flipper-Fmt (7.1.7) - - Flipper-Folly (2.6.10): - - Flipper-Boost-iOSX - - Flipper-DoubleConversion - - Flipper-Fmt (= 7.1.7) - - Flipper-Glog - - libevent (~> 2.1.12) - - OpenSSL-Universal (= 1.1.1100) - - Flipper-Glog (0.5.0.5) - - Flipper-PeerTalk (0.0.4) - - FlipperKit (0.201.0): - - FlipperKit/Core (= 0.201.0) - - FlipperKit/Core (0.201.0): - - Flipper (~> 0.201.0) - - FlipperKit/CppBridge - - FlipperKit/FBCxxFollyDynamicConvert - - FlipperKit/FBDefines - - FlipperKit/FKPortForwarding - - SocketRocket (~> 0.6.0) - - FlipperKit/CppBridge (0.201.0): - - Flipper (~> 0.201.0) - - FlipperKit/FBCxxFollyDynamicConvert (0.201.0): - - Flipper-Folly (~> 2.6) - - FlipperKit/FBDefines (0.201.0) - - FlipperKit/FKPortForwarding (0.201.0): - - CocoaAsyncSocket (~> 7.6) - - Flipper-PeerTalk (~> 0.0.4) - - FlipperKit/FlipperKitHighlightOverlay (0.201.0) - - FlipperKit/FlipperKitLayoutHelpers (0.201.0): - - FlipperKit/Core - - FlipperKit/FlipperKitHighlightOverlay - - FlipperKit/FlipperKitLayoutTextSearchable - - FlipperKit/FlipperKitLayoutIOSDescriptors (0.201.0): - - FlipperKit/Core - - FlipperKit/FlipperKitHighlightOverlay - - FlipperKit/FlipperKitLayoutHelpers - - FlipperKit/FlipperKitLayoutPlugin (0.201.0): - - FlipperKit/Core - - FlipperKit/FlipperKitHighlightOverlay - - FlipperKit/FlipperKitLayoutHelpers - - FlipperKit/FlipperKitLayoutIOSDescriptors - - FlipperKit/FlipperKitLayoutTextSearchable - - FlipperKit/FlipperKitLayoutTextSearchable (0.201.0) - - FlipperKit/FlipperKitNetworkPlugin (0.201.0): - - FlipperKit/Core - - FlipperKit/FlipperKitReactPlugin (0.201.0): - - FlipperKit/Core - - FlipperKit/FlipperKitUserDefaultsPlugin (0.201.0): - - FlipperKit/Core - - FlipperKit/SKIOSNetworkPlugin (0.201.0): - - FlipperKit/Core - - FlipperKit/FlipperKitNetworkPlugin - fmt (6.2.1) - glog (0.3.5) - GoogleAppMeasurement (8.8.0): @@ -284,7 +227,6 @@ PODS: - onfido-react-native-sdk (10.6.0): - Onfido (~> 29.6.0) - React - - OpenSSL-Universal (1.1.1100) - Plaid (4.7.0) - PromisesObjC (2.3.1) - RCT-Folly (2022.05.16.00): @@ -1486,32 +1428,11 @@ DEPENDENCIES: - ExpoModulesCore (from `../node_modules/expo-modules-core`) - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`) - - Flipper (= 0.201.0) - - Flipper-Boost-iOSX (= 1.76.0.1.11) - - Flipper-DoubleConversion (= 3.2.0.1) - - Flipper-Fmt (= 7.1.7) - - Flipper-Folly (= 2.6.10) - - Flipper-Glog (= 0.5.0.5) - - Flipper-PeerTalk (= 0.0.4) - - FlipperKit (= 0.201.0) - - FlipperKit/Core (= 0.201.0) - - FlipperKit/CppBridge (= 0.201.0) - - FlipperKit/FBCxxFollyDynamicConvert (= 0.201.0) - - FlipperKit/FBDefines (= 0.201.0) - - FlipperKit/FKPortForwarding (= 0.201.0) - - FlipperKit/FlipperKitHighlightOverlay (= 0.201.0) - - FlipperKit/FlipperKitLayoutPlugin (= 0.201.0) - - FlipperKit/FlipperKitLayoutTextSearchable (= 0.201.0) - - FlipperKit/FlipperKitNetworkPlugin (= 0.201.0) - - FlipperKit/FlipperKitReactPlugin (= 0.201.0) - - FlipperKit/FlipperKitUserDefaultsPlugin (= 0.201.0) - - FlipperKit/SKIOSNetworkPlugin (= 0.201.0) - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) - libevent (~> 2.1.12) - lottie-react-native (from `../node_modules/lottie-react-native`) - "onfido-react-native-sdk (from `../node_modules/@onfido/react-native-sdk`)" - - OpenSSL-Universal (= 1.1.1100) - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - RCT-Folly/Fabric (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`) @@ -1520,7 +1441,6 @@ DEPENDENCIES: - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`) - React-Codegen (from `build/generated/ios`) - React-Core (from `../node_modules/react-native/`) - - React-Core/DevSupport (from `../node_modules/react-native/`) - React-Core/RCTWebSocket (from `../node_modules/react-native/`) - React-CoreModules (from `../node_modules/react-native/React/CoreModules`) - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`) @@ -1610,7 +1530,6 @@ SPEC REPOS: - AirshipFrameworkProxy - AirshipServiceExtension - AppAuth - - CocoaAsyncSocket - Firebase - FirebaseABTesting - FirebaseAnalytics @@ -1620,14 +1539,6 @@ SPEC REPOS: - FirebaseInstallations - FirebasePerformance - FirebaseRemoteConfig - - Flipper - - Flipper-Boost-iOSX - - Flipper-DoubleConversion - - Flipper-Fmt - - Flipper-Folly - - Flipper-Glog - - Flipper-PeerTalk - - FlipperKit - fmt - GoogleAppMeasurement - GoogleDataTransport @@ -1647,7 +1558,6 @@ SPEC REPOS: - MapboxMobileEvents - nanopb - Onfido - - OpenSSL-Universal - Plaid - PromisesObjC - SDWebImage @@ -1869,7 +1779,6 @@ SPEC CHECKSUMS: AppAuth: 3bb1d1cd9340bd09f5ed189fb00b1cc28e1e8570 boost: d3f49c53809116a5d38da093a8aa78bf551aed09 BVLinearGradient: 421743791a59d259aec53f4c58793aad031da2ca - CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 DoubleConversion: fea03f2699887d960129cc54bba7e52542b6f953 EXAV: 09a4d87fa6b113fbb0ada3aade6799f78271cb44 Expo: 1e3bcf9dd99de57a636127057f6b488f0609681a @@ -1886,14 +1795,6 @@ SPEC CHECKSUMS: FirebaseInstallations: 40bd9054049b2eae9a2c38ef1c3dd213df3605cd FirebasePerformance: 0c01a7a496657d7cea86d40c0b1725259d164c6c FirebaseRemoteConfig: 2d6e2cfdb49af79535c8af8a80a4a5009038ec2b - Flipper: c7a0093234c4bdd456e363f2f19b2e4b27652d44 - Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c - Flipper-DoubleConversion: 2dc99b02f658daf147069aad9dbd29d8feb06d30 - Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b - Flipper-Folly: 584845625005ff068a6ebf41f857f468decd26b3 - Flipper-Glog: 70c50ce58ddaf67dc35180db05f191692570f446 - Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 - FlipperKit: 37525a5d056ef9b93d1578e04bc3ea1de940094f fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 glog: c5d68082e772fa1c511173d6b30a9de2c05a69a2 GoogleAppMeasurement: 5ba1164e3c844ba84272555e916d0a6d3d977e91 @@ -1917,7 +1818,6 @@ SPEC CHECKSUMS: nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 Onfido: c52e797b10cc9e6d29ba91996cb62e501000bfdd onfido-react-native-sdk: 4e7f0a7a986ed93cb906d2e0b67a6aab9202de0b - OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c Plaid: 431ef9be5314a1345efb451bc5e6b067bfb3b4c6 PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4 RCT-Folly: 7169b2b1c44399c76a47b5deaaba715eeeb476c0 @@ -2013,8 +1913,8 @@ SPEC CHECKSUMS: SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 Turf: 13d1a92d969ca0311bbc26e8356cca178ce95da2 VisionCamera: 0a6794d1974aed5d653d0d0cb900493e2583e35a - Yoga: e64aa65de36c0832d04e8c7bd614396c77a80047 + Yoga: 13c8ef87792450193e117976337b8527b49e8c03 -PODFILE CHECKSUM: 0ccbb4f2406893c6e9f266dc1e7470dcd72885d2 +PODFILE CHECKSUM: a431c146e1501391834a2f299a74093bac53b530 COCOAPODS: 1.13.0 diff --git a/package-lock.json b/package-lock.json index ac65cd05115d..f86bdcd2c5cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.50-1", + "version": "1.4.50-3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.50-1", + "version": "1.4.50-3", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -64,7 +64,7 @@ "lodash": "4.17.21", "lottie-react-native": "6.4.1", "mapbox-gl": "^2.15.0", - "onfido-sdk-ui": "13.6.1", + "onfido-sdk-ui": "14.15.0", "patch-package": "^8.0.0", "process": "^0.11.10", "prop-types": "^15.7.2", @@ -229,7 +229,6 @@ "prettier": "^2.8.8", "pusher-js-mock": "^0.3.3", "react-native-clean-project": "^4.0.0-alpha4.0", - "react-native-performance-flipper-reporter": "^2.0.0", "react-test-renderer": "18.2.0", "reassure": "^0.10.1", "setimmediate": "^1.0.5", @@ -7715,16 +7714,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/@mediapipe/face_detection": { - "version": "0.4.1646425229", - "resolved": "https://registry.npmjs.org/@mediapipe/face_detection/-/face_detection-0.4.1646425229.tgz", - "integrity": "sha512-aeCN+fRAojv9ch3NXorP6r5tcGVLR3/gC1HmtqB0WEZBRXrdP6/3W/sGR0dHr1iT6ueiK95G9PVjbzFosf/hrg==" - }, - "node_modules/@mediapipe/face_mesh": { - "version": "0.4.1633559619", - "resolved": "https://registry.npmjs.org/@mediapipe/face_mesh/-/face_mesh-0.4.1633559619.tgz", - "integrity": "sha512-Vc8cdjxS5+O2gnjWH9KncYpUCVXT0h714KlWAsyqJvJbIgUJBqpppbIx8yWcAzBDxm/5cYSuBI5p5ySIPxzcEg==" - }, "node_modules/@mrmlnc/readdir-enhanced": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", @@ -10489,78 +10478,6 @@ "join-component": "^1.1.0" } }, - "node_modules/@sentry/browser": { - "version": "7.11.1", - "license": "BSD-3-Clause", - "dependencies": { - "@sentry/core": "7.11.1", - "@sentry/types": "7.11.1", - "@sentry/utils": "7.11.1", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/browser/node_modules/tslib": { - "version": "1.14.1", - "license": "0BSD" - }, - "node_modules/@sentry/core": { - "version": "7.11.1", - "license": "BSD-3-Clause", - "dependencies": { - "@sentry/hub": "7.11.1", - "@sentry/types": "7.11.1", - "@sentry/utils": "7.11.1", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/core/node_modules/tslib": { - "version": "1.14.1", - "license": "0BSD" - }, - "node_modules/@sentry/hub": { - "version": "7.11.1", - "license": "BSD-3-Clause", - "dependencies": { - "@sentry/types": "7.11.1", - "@sentry/utils": "7.11.1", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/hub/node_modules/tslib": { - "version": "1.14.1", - "license": "0BSD" - }, - "node_modules/@sentry/types": { - "version": "7.11.1", - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/utils": { - "version": "7.11.1", - "license": "BSD-3-Clause", - "dependencies": { - "@sentry/types": "7.11.1", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/utils/node_modules/tslib": { - "version": "1.14.1", - "license": "0BSD" - }, "node_modules/@shopify/flash-list": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/@shopify/flash-list/-/flash-list-1.6.3.tgz", @@ -10639,11 +10556,6 @@ "@sinonjs/commons": "^2.0.0" } }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" - }, "node_modules/@storybook/addon-a11y": { "version": "6.5.10", "dev": true, @@ -20007,88 +19919,6 @@ "node": ">=10" } }, - "node_modules/@tensorflow-models/face-detection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@tensorflow-models/face-detection/-/face-detection-1.0.2.tgz", - "integrity": "sha512-anjSxy3MnZdTiVluOEQZeaFWM30IPswFM+SltX6wseXKja/AbrHYqamGNZKUylAs2JAyudq+xqTRPS+nA2ourg==", - "dependencies": { - "rimraf": "^3.0.2", - "tslib": "2.4.0" - }, - "peerDependencies": { - "@mediapipe/face_detection": "~0.4.0", - "@tensorflow/tfjs-backend-webgl": "^4.4.0", - "@tensorflow/tfjs-converter": "^4.4.0", - "@tensorflow/tfjs-core": "^4.4.0" - } - }, - "node_modules/@tensorflow/tfjs-backend-cpu": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-backend-cpu/-/tfjs-backend-cpu-4.16.0.tgz", - "integrity": "sha512-bQFu7FTUgqgss1AwnqSwQ1f02IPrfLLc2lLn5pyyVrS6Ex7zA6Y4YkfktqoJSRE6LlRZv3vxSriUGE1avRe4qQ==", - "peer": true, - "dependencies": { - "@types/seedrandom": "^2.4.28", - "seedrandom": "^3.0.5" - }, - "engines": { - "yarn": ">= 1.3.2" - }, - "peerDependencies": { - "@tensorflow/tfjs-core": "4.16.0" - } - }, - "node_modules/@tensorflow/tfjs-backend-webgl": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-backend-webgl/-/tfjs-backend-webgl-4.16.0.tgz", - "integrity": "sha512-cIGZWuY892iwTRokbDj3qsLi0AlpQn+U7rzB1mddhHrWr9kBXrrnAvIq0h2aiFzRFNePWUcsbgK+HmYG32kosg==", - "peer": true, - "dependencies": { - "@tensorflow/tfjs-backend-cpu": "4.16.0", - "@types/offscreencanvas": "~2019.3.0", - "@types/seedrandom": "^2.4.28", - "seedrandom": "^3.0.5" - }, - "engines": { - "yarn": ">= 1.3.2" - }, - "peerDependencies": { - "@tensorflow/tfjs-core": "4.16.0" - } - }, - "node_modules/@tensorflow/tfjs-converter": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-converter/-/tfjs-converter-4.16.0.tgz", - "integrity": "sha512-gd8dHl9tqEPQOHZLAUza713nKr42rpvUXrtm7yUhk10THvJT6TXe9Q2AJKmni8J3vfR+ghsCh77F8D4RbShx1Q==", - "peer": true, - "peerDependencies": { - "@tensorflow/tfjs-core": "4.16.0" - } - }, - "node_modules/@tensorflow/tfjs-core": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-core/-/tfjs-core-4.16.0.tgz", - "integrity": "sha512-MarAtO+Up6wA8pI9QDpQOwwJgb/imYMN++tsoaalyOEE9+B5HS4lQldxDJKXO8Frf4DyXf4FItJktEXaiPfRHw==", - "peer": true, - "dependencies": { - "@types/long": "^4.0.1", - "@types/offscreencanvas": "~2019.7.0", - "@types/seedrandom": "^2.4.28", - "@webgpu/types": "0.1.38", - "long": "4.0.0", - "node-fetch": "~2.6.1", - "seedrandom": "^3.0.5" - }, - "engines": { - "yarn": ">= 1.3.2" - } - }, - "node_modules/@tensorflow/tfjs-core/node_modules/@types/offscreencanvas": { - "version": "2019.7.3", - "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz", - "integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==", - "peer": true - }, "node_modules/@testing-library/jest-native": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/@testing-library/jest-native/-/jest-native-5.4.1.tgz", @@ -20578,11 +20408,6 @@ "integrity": "sha512-w5jZ0ee+HaPOaX25X2/2oGR/7rgAQSYII7X7pp0m9KgBfMP7uKfMfTvcpl5Dj+eDBbpxKGiqE+flqDr6XTd2RA==", "dev": true }, - "node_modules/@types/emscripten": { - "version": "0.0.34", - "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-0.0.34.tgz", - "integrity": "sha512-QSb9ojDincskc+uKMI0KXp8e1NALFINCrMlp8VGKGcTSxeEyRTTKyjWw75NYrCZHUsVEEEpr1tYHpbtaC++/sQ==" - }, "node_modules/@types/eslint": { "version": "8.4.6", "license": "MIT", @@ -20827,11 +20652,6 @@ "integrity": "sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==", "dev": true }, - "node_modules/@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" - }, "node_modules/@types/mapbox-gl": { "version": "2.7.13", "resolved": "https://registry.npmjs.org/@types/mapbox-gl/-/mapbox-gl-2.7.13.tgz", @@ -20904,11 +20724,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/offscreencanvas": { - "version": "2019.3.0", - "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.3.0.tgz", - "integrity": "sha512-esIJx9bQg+QYF0ra8GnvfianIY8qWB0GBx54PK5Eps6m+xTj86KLavHv6qDhzKcu5UUOgNfJ2pWaIIV7TRUd9Q==" - }, "node_modules/@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", @@ -21066,11 +20881,6 @@ "version": "0.16.2", "license": "MIT" }, - "node_modules/@types/seedrandom": { - "version": "2.4.34", - "resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-2.4.34.tgz", - "integrity": "sha512-ytDiArvrn/3Xk6/vtylys5tlY6eo7Ane0hvcx++TKo6RxQXuVfW0AF/oeWqAj9dN29SyhtawuXstgmPlwNcv/A==" - }, "node_modules/@types/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", @@ -21170,16 +20980,6 @@ "dev": true, "optional": true }, - "node_modules/@types/webgl-ext": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/@types/webgl-ext/-/webgl-ext-0.0.30.tgz", - "integrity": "sha512-LKVgNmBxN0BbljJrVUwkxwRYqzsAEPcZOe6S2T6ZaBDIrFp0qu4FNlpc5sM1tGbXUYFgdVQIoeLk1Y1UoblyEg==" - }, - "node_modules/@types/webgl2": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/@types/webgl2/-/webgl2-0.0.6.tgz", - "integrity": "sha512-50GQhDVTq/herLMiqSQkdtRu+d5q/cWHn4VvKJtrj4DJAjo1MNkWYa2MA41BaBO1q1HgsUjuQvEOk0QHvlnAaQ==" - }, "node_modules/@types/webpack": { "version": "4.41.32", "dev": true, @@ -22197,12 +21997,6 @@ "@xtuc/long": "4.2.2" } }, - "node_modules/@webgpu/types": { - "version": "0.1.38", - "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.38.tgz", - "integrity": "sha512-7LrhVKz2PRh+DD7+S+PVaFd5HxaWQvoMqBbsV9fNJO1pjUs1P8bM2vQVNfk+3URTqbuTI7gkXi0rfsN0IadoBA==", - "peer": true - }, "node_modules/@webpack-cli/configtest": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", @@ -24631,12 +24425,6 @@ "bluebird": "^3.5.5" } }, - "node_modules/blueimp-load-image": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/blueimp-load-image/-/blueimp-load-image-2.29.0.tgz", - "integrity": "sha512-psm81GlZ0ffKxVT0QN9dvhpzXMv1KxgXSg8ars0XGAcEGsTwFT2IPo59HDXlw4Lo2oImdPzwrwkliZSiLLUpIw==", - "license": "MIT" - }, "node_modules/blueimp-md5": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz", @@ -28739,10 +28527,6 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/dompurify": { - "version": "2.3.10", - "license": "(MPL-2.0 OR Apache-2.0)" - }, "node_modules/domutils": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", @@ -29261,46 +29045,6 @@ "objectorarray": "^1.0.5" } }, - "node_modules/engine.io-client": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz", - "integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.11.0", - "xmlhttprequest-ssl": "~2.0.0" - } - }, - "node_modules/engine.io-client/node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/engine.io-parser": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", - "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==", - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/enhanced-resolve": { "version": "5.15.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", @@ -29341,12 +29085,6 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/enumerate-devices": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/enumerate-devices/-/enumerate-devices-1.1.1.tgz", - "integrity": "sha512-8zDbrc7ocusTL1ZGmvgy0cTwdyCaM7sGZoYLRmnWJalLQzmftDtce+uDU91gafOTo9MCtgjSIxyMv/F4+Hcchw==", - "license": "MIT" - }, "node_modules/env-editor": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/env-editor/-/env-editor-0.4.2.tgz", @@ -30743,12 +30481,6 @@ "node": ">=6" } }, - "node_modules/eventemitter2": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-2.2.2.tgz", - "integrity": "sha512-AmQ734LWUB9Iyk+2WIU3Z8iRhdL1XQihEE0iF/QC5Xp11zST0Z5tn5jRHa/PgIld2QIPSCys3CREqOQLUhNvkw==", - "license": "MIT" - }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -31825,14 +31557,6 @@ "url": "https://opencollective.com/ramda" } }, - "node_modules/file-type": { - "version": "12.4.2", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-12.4.2.tgz", - "integrity": "sha512-UssQP5ZgIOKelfsaB5CuGAL+Y+q7EmONuiwF3N5HAH0t27rvrttgi6Ra9k/+DVaY9UF6+ybxu5pOXLUdA8N7Vg==", - "engines": { - "node": ">=8" - } - }, "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -33408,19 +33132,6 @@ "node": ">= 8" } }, - "node_modules/history": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/history/-/history-4.5.1.tgz", - "integrity": "sha512-gfHeJeYeMzFtos61gdA1AloO0hGXPF2Yum+2FRdJvlylYQOz51OnT1zuwg9UYst1BRrONhcAh3Nmsg9iblgl6g==", - "license": "MIT", - "dependencies": { - "invariant": "^2.2.1", - "loose-envify": "^1.2.0", - "resolve-pathname": "^2.0.0", - "value-equal": "^0.2.0", - "warning": "^3.0.0" - } - }, "node_modules/hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -38489,13 +38200,6 @@ "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.5.tgz", "integrity": "sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA==" }, - "node_modules/js-cookie": { - "version": "3.0.1", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, "node_modules/js-string-escape": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", @@ -39503,11 +39207,6 @@ "node": ">=6" } }, - "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" - }, "node_modules/longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", @@ -41122,22 +40821,6 @@ "node": ">= 8" } }, - "node_modules/mirada": { - "version": "0.0.15", - "resolved": "https://registry.npmjs.org/mirada/-/mirada-0.0.15.tgz", - "integrity": "sha512-mbm4c+wjBVcmUzHRLv/TfOAq+iy03D24KwGxx8H+NSXkD5EOZV9zFWbVxTvZCc9XwR0FIUhryU/kQm12SMSQ3g==", - "dependencies": { - "buffer": "^5.4.3", - "cross-fetch": "^3.0.4", - "file-type": "^12.3.0", - "misc-utils-of-mine-generic": "^0.2.31" - } - }, - "node_modules/misc-utils-of-mine-generic": { - "version": "0.2.45", - "resolved": "https://registry.npmjs.org/misc-utils-of-mine-generic/-/misc-utils-of-mine-generic-0.2.45.tgz", - "integrity": "sha512-WsG2zYiui2cdEbHF2pXmJfnjHb4zL+cy+PaYcLgIpMju98hwX89VbjlvGIfamCfEodbQ0qjCEvD3ocgkCXfMOQ==" - }, "node_modules/mississippi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", @@ -42149,41 +41832,9 @@ } }, "node_modules/onfido-sdk-ui": { - "version": "13.6.1", - "resolved": "https://registry.npmjs.org/onfido-sdk-ui/-/onfido-sdk-ui-13.6.1.tgz", - "integrity": "sha512-EcFqTN9uaVINRUttSdt6ySUBlfg25dE9f2yxxXVUmrM9a4M1luv+aICej1zE3vRZPFEuFJ9mqJZQUTYo0YMFyg==", - "dependencies": { - "@onfido/active-video-capture": "^0.28.2", - "@onfido/opencv": "^2.0.0", - "@sentry/browser": "^7.2.0", - "blueimp-load-image": "~2.29.0", - "classnames": "~2.2.5", - "core-js": "^3.21.1", - "deepmerge": "^4.2.2", - "dompurify": "^2.2.6", - "enumerate-devices": "^1.1.1", - "eventemitter2": "~2.2.2", - "history": "~4.5.1", - "hoist-non-react-statics": "^3.3.2", - "js-cookie": "^3.0.1", - "pdfobject": "^2.2.7", - "preact": "10.11.3", - "redux": "^4.0.5", - "socket.io-client": "^4.2.0", - "supports-webp": "~1.0.3", - "uuid": "^8.3.2", - "visibilityjs": "~1.2.4", - "xstate": "^4.33.6" - }, - "bin": { - "migrate_locales": "scripts/migrate_locales.js" - } - }, - "node_modules/onfido-sdk-ui/node_modules/classnames": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", - "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==", - "license": "MIT" + "version": "14.15.0", + "resolved": "https://registry.npmjs.org/onfido-sdk-ui/-/onfido-sdk-ui-14.15.0.tgz", + "integrity": "sha512-4Z+tnH6pQjK4SyazlzJq17NXO8AnhGcwEACbA3PVbAo90LBpGu1WAZ1r6VidlxFr/oPbu6sg/hisYvfXiqOtTg==" }, "node_modules/open": { "version": "8.4.2", @@ -43033,10 +42684,6 @@ "canvas": "^2.11.2" } }, - "node_modules/pdfobject": { - "version": "2.2.8", - "license": "MIT" - }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -43423,15 +43070,6 @@ "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.0.0.tgz", "integrity": "sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw==" }, - "node_modules/preact": { - "version": "10.11.3", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.11.3.tgz", - "integrity": "sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/preact" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -44597,16 +44235,6 @@ "react-native-reanimated": ">=2.8.0" } }, - "node_modules/react-native-flipper": { - "version": "0.159.0", - "dev": true, - "license": "MIT", - "peer": true, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-native": ">0.62.0" - } - }, "node_modules/react-native-fs": { "version": "2.20.0", "resolved": "https://registry.npmjs.org/react-native-fs/-/react-native-fs-2.20.0.tgz", @@ -44839,17 +44467,6 @@ "react-native": "*" } }, - "node_modules/react-native-performance-flipper-reporter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/react-native-performance-flipper-reporter/-/react-native-performance-flipper-reporter-2.0.0.tgz", - "integrity": "sha512-ccOgq99eK3OvrNNhpJDC4ydNk/1JGgWZPo2FLrPDLUHXAR4EcE9cUAtb46oGOpvHk5ZOb5aEDofc/CS9OEGcag==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "react-native-flipper": "*", - "react-native-performance": "*" - } - }, "node_modules/react-native-permissions": { "version": "3.9.3", "resolved": "https://registry.npmjs.org/react-native-permissions/-/react-native-permissions-3.9.3.tgz", @@ -46951,12 +46568,6 @@ "node": ">=8" } }, - "node_modules/resolve-pathname": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz", - "integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg==", - "license": "MIT" - }, "node_modules/resolve-protobuf-schema": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", @@ -47325,6 +46936,7 @@ "version": "3.0.5", "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==", + "dev": true, "license": "MIT" }, "node_modules/select": { @@ -48223,32 +47835,6 @@ "node": ">=0.10.0" } }, - "node_modules/socket.io-client": { - "version": "4.7.4", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.4.tgz", - "integrity": "sha512-wh+OkeF0rAVCrABWQBaEjLfb7DVPotMbu0cgWgyR0v6eA4EoVnAwcIeIbcdTE3GT/H3kbdLl7OoH2+asoDRIIg==", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.2", - "engine.io-client": "~6.5.2", - "socket.io-parser": "~4.2.4" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-parser": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", - "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -49309,12 +48895,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/supports-webp": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/supports-webp/-/supports-webp-1.0.7.tgz", - "integrity": "sha512-ZlqT+sCgZKcykOLrk8DYR4t3Em+nyVSHpiV3q7uzOutLwKIYU23n88KibCLw3FzM4NCQeRorvZ55AV/77lQyOQ==", - "license": "MIT" - }, "node_modules/svg-parser": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", @@ -51451,12 +51031,6 @@ "builtins": "^1.0.3" } }, - "node_modules/value-equal": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.2.1.tgz", - "integrity": "sha512-yRL36Xb2K/HmFT5Fe3M86S7mu4+a12/3l7uytUh6eNPPjP77ldPBvsAvmnWff39sXn55naRMZN8LZWRO8PWaeQ==", - "license": "MIT" - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -51524,12 +51098,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/visibilityjs": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/visibilityjs/-/visibilityjs-1.2.8.tgz", - "integrity": "sha512-Y+aL3OUX88b+/VSmkmC2ApuLbf0grzbNLpCfIDSw3BzTU6PqcPsdgIOaw8b+eZoy+DdQqnVN3y/Evow9vQq9Ig==", - "license": "MIT" - }, "node_modules/vlq": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz", @@ -51591,15 +51159,6 @@ "integrity": "sha512-VkQZJbO8zVImzYFteBXvBOZEl1qL175WH8VmZcxF2fZAoudNhNDvHi+doCaAEdU2l2vtcIwa2zn0QK5+I1HQ3Q==", "license": "MIT" }, - "node_modules/warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", - "integrity": "sha512-jMBt6pUrKn5I+OGgtQ4YZLdhIeJmObddh6CsibPxyQ5yPZm1XExSyzC1LCNX7BzhxWgiHmizBWJTHJIjMjTQYQ==", - "license": "BSD-3-Clause", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, "node_modules/watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", @@ -53033,22 +52592,6 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "license": "MIT" }, - "node_modules/xmlhttprequest-ssl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", - "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/xstate": { - "version": "4.37.2", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/xstate" - } - }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index 7af92881d627..07464a5eb8b5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.50-1", + "version": "1.4.50-3", "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.", @@ -115,7 +115,7 @@ "lodash": "4.17.21", "lottie-react-native": "6.4.1", "mapbox-gl": "^2.15.0", - "onfido-sdk-ui": "13.6.1", + "onfido-sdk-ui": "14.15.0", "patch-package": "^8.0.0", "process": "^0.11.10", "prop-types": "^15.7.2", @@ -280,7 +280,6 @@ "prettier": "^2.8.8", "pusher-js-mock": "^0.3.3", "react-native-clean-project": "^4.0.0-alpha4.0", - "react-native-performance-flipper-reporter": "^2.0.0", "react-test-renderer": "18.2.0", "reassure": "^0.10.1", "setimmediate": "^1.0.5", diff --git a/patches/@react-native-community+cli-platform-ios+12.3.0.patch b/patches/@react-native-community+cli-platform-ios+12.3.0.patch index cfae504e44fa..e54ab17c43dd 100644 --- a/patches/@react-native-community+cli-platform-ios+12.3.0.patch +++ b/patches/@react-native-community+cli-platform-ios+12.3.0.patch @@ -1,5 +1,5 @@ diff --git a/node_modules/@react-native-community/cli-platform-ios/native_modules.rb b/node_modules/@react-native-community/cli-platform-ios/native_modules.rb -index 82f537c..f5e2cda 100644 +index 82f537c..df441e2 100644 --- a/node_modules/@react-native-community/cli-platform-ios/native_modules.rb +++ b/node_modules/@react-native-community/cli-platform-ios/native_modules.rb @@ -12,7 +12,7 @@ @@ -19,7 +19,7 @@ index 82f537c..f5e2cda 100644 if (!config) json = [] -@@ -36,10 +35,30 @@ def use_native_modules!(config = nil) +@@ -36,9 +35,24 @@ def use_native_modules!(config = nil) config = JSON.parse(json.join("\n")) end @@ -42,11 +42,5 @@ index 82f537c..f5e2cda 100644 + end + packages = config["dependencies"] -+ -+ if (ENV["NO_FLIPPER"]) -+ packages = {**packages, "react-native-flipper" => {"platforms" => {"ios" => nil}}} -+ end -+ found_pods = [] - packages.each do |package_name, package| diff --git a/src/CONST.ts b/src/CONST.ts index ce2029c78713..6c0487a9177f 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -98,6 +98,8 @@ const CONST = { AVATAR_MAX_WIDTH_PX: 4096, AVATAR_MAX_HEIGHT_PX: 4096, + LOGO_MAX_SCALE: 1.5, + BREADCRUMB_TYPE: { ROOT: 'root', STRONG: 'strong', @@ -1169,6 +1171,7 @@ const CONST = { MISSING_FIELD: 'Missing required additional details fields', WRONG_ANSWERS: 'Wrong answers', ONFIDO_FIXABLE_ERROR: 'Onfido returned a fixable error', + ONFIDO_USER_CONSENT_DENIED: 'user_consent_denied', // KBA stands for Knowledge Based Answers (requiring us to show Idology questions) KBA_NEEDED: 'KBA needed', diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 856a6fb89a3e..1c33e2ab5bab 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -557,6 +557,10 @@ const ROUTES = { route: 'workspace/:policyID/categories/settings', getRoute: (policyID: string) => `workspace/${policyID}/categories/settings` as const, }, + WORKSPACE_MORE_FEATURES: { + route: 'workspace/:policyID/more-features', + getRoute: (policyID: string) => `workspace/${policyID}/more-features` as const, + }, WORKSPACE_CATEGORY_CREATE: { route: 'workspace/:policyID/categories/new', getRoute: (policyID: string) => `workspace/${policyID}/categories/new` as const, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 8546f543b77a..8b653a8e22a1 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -227,6 +227,7 @@ const SCREENS = { CATEGORY_CREATE: 'Category_Create', CATEGORY_SETTINGS: 'Category_Settings', CATEGORIES_SETTINGS: 'Categories_Settings', + MORE_FEATURES: 'Workspace_More_Features', MEMBER_DETAILS: 'Workspace_Member_Details', MEMBER_DETAILS_ROLE_SELECTION: 'Workspace_Member_Details_Role_Selection', DISTANCE_RATES: 'Distance_Rates', diff --git a/src/components/BaseMiniContextMenuItem.tsx b/src/components/BaseMiniContextMenuItem.tsx index 7bed44cd8f13..6e1a1e0fd229 100644 --- a/src/components/BaseMiniContextMenuItem.tsx +++ b/src/components/BaseMiniContextMenuItem.tsx @@ -32,13 +32,20 @@ type BaseMiniContextMenuItemProps = { * Whether the button should be in the active state */ isDelayButtonStateComplete: boolean; + /** + * Can be used to control the click event, and for example whether or not to lose focus from the composer when pressing the item + */ + shouldPreventDefaultFocusOnPress?: boolean; }; /** * Component that renders a mini context menu item with a * pressable. Also renders a tooltip when hovering the item. */ -function BaseMiniContextMenuItem({tooltipText, onPress, children, isDelayButtonStateComplete = true}: BaseMiniContextMenuItemProps, ref: ForwardedRef) { +function BaseMiniContextMenuItem( + {tooltipText, onPress, children, isDelayButtonStateComplete = true, shouldPreventDefaultFocusOnPress = true}: BaseMiniContextMenuItemProps, + ref: ForwardedRef, +) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); return ( @@ -64,7 +71,9 @@ function BaseMiniContextMenuItem({tooltipText, onPress, children, isDelayButtonS } // Prevent text input blur on left click - event.preventDefault(); + if (shouldPreventDefaultFocusOnPress) { + event.preventDefault(); + } }} accessibilityLabel={tooltipText} role={CONST.ROLE.BUTTON} diff --git a/src/components/Breadcrumbs.tsx b/src/components/Breadcrumbs.tsx index 34bc3f7e30c8..e5eb09691eba 100644 --- a/src/components/Breadcrumbs.tsx +++ b/src/components/Breadcrumbs.tsx @@ -1,6 +1,6 @@ import React from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; -import {View} from 'react-native'; +import {PixelRatio, View} from 'react-native'; import LogoComponent from '@assets/images/expensify-wordmark.svg'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -36,7 +36,7 @@ function Breadcrumbs({breadcrumbs, style}: BreadcrumbsProps) { const theme = useTheme(); const styles = useThemeStyles(); const [primaryBreadcrumb, secondaryBreadcrumb] = breadcrumbs; - + const fontScale = PixelRatio.getFontScale() > CONST.LOGO_MAX_SCALE ? CONST.LOGO_MAX_SCALE : PixelRatio.getFontScale(); return ( {primaryBreadcrumb.type === CONST.BREADCRUMB_TYPE.ROOT ? ( @@ -47,8 +47,8 @@ function Breadcrumbs({breadcrumbs, style}: BreadcrumbsProps) { contentFit="contain" src={LogoComponent} fill={theme.text} - width={variables.lhnLogoWidth} - height={variables.lhnLogoHeight} + width={variables.lhnLogoWidth * fontScale} + height={variables.lhnLogoHeight * fontScale} /> } shouldShowEnvironmentBadge diff --git a/src/components/ContextMenuItem.tsx b/src/components/ContextMenuItem.tsx index d6c8fd973983..b80d6a138c9e 100644 --- a/src/components/ContextMenuItem.tsx +++ b/src/components/ContextMenuItem.tsx @@ -44,6 +44,8 @@ type ContextMenuItemProps = { /** Styles to apply to ManuItem wrapper */ wrapperStyle?: StyleProp; + + shouldPreventDefaultFocusOnPress?: boolean; }; type ContextMenuItemHandle = { @@ -63,6 +65,7 @@ function ContextMenuItem( isFocused = false, shouldLimitWidth = true, wrapperStyle, + shouldPreventDefaultFocusOnPress = true, }: ContextMenuItemProps, ref: ForwardedRef, ) { @@ -94,6 +97,7 @@ function ContextMenuItem( tooltipText={itemText} onPress={triggerPressAndUpdateSuccess} isDelayButtonStateComplete={!isThrottledButtonActive} + shouldPreventDefaultFocusOnPress={shouldPreventDefaultFocusOnPress} > {({hovered, pressed}) => ( { - const yearsList = searchText === '' ? years : years.filter((year) => year.text.includes(searchText)); + const yearsList = searchText === '' ? years : years.filter((year) => year.text?.includes(searchText)); return { headerMessage: !yearsList.length ? translate('common.noResultsFound') : '', sections: [{data: yearsList.sort((a, b) => b.value - a.value), indexOffset: 0}], diff --git a/src/components/DragAndDrop/Provider/types.ts b/src/components/DragAndDrop/Provider/types.ts index b4394056cac5..57d0fb47c637 100644 --- a/src/components/DragAndDrop/Provider/types.ts +++ b/src/components/DragAndDrop/Provider/types.ts @@ -8,7 +8,7 @@ type DragAndDropProviderProps = { isDisabled?: boolean; /** Indicate that users are dragging file or not */ - setIsDraggingOver: (value: boolean) => void; + setIsDraggingOver?: (value: boolean) => void; }; type SetOnDropHandlerCallback = (event: DragEvent) => void; diff --git a/src/components/FlatList/index.android.tsx b/src/components/FlatList/index.android.tsx index 1246367d29e8..863930203863 100644 --- a/src/components/FlatList/index.android.tsx +++ b/src/components/FlatList/index.android.tsx @@ -1,7 +1,7 @@ import {useFocusEffect} from '@react-navigation/native'; import type {ForwardedRef} from 'react'; import React, {forwardRef, useCallback, useContext} from 'react'; -import type {FlatListProps, NativeScrollEvent, NativeSyntheticEvent} from 'react-native'; +import type {FlatListProps} from 'react-native'; import {FlatList} from 'react-native'; import {ActionListContext} from '@pages/home/ReportScreenContext'; @@ -22,9 +22,6 @@ function CustomFlatList(props: FlatListProps, ref: ForwardedRef) } }, [scrollPosition?.offset, ref]); - // eslint-disable-next-line react-hooks/exhaustive-deps - const onMomentumScrollEnd = useCallback((event: NativeSyntheticEvent) => setScrollPosition({offset: event.nativeEvent.contentOffset.y}), []); - useFocusEffect( useCallback(() => { onScreenFocus(); @@ -35,8 +32,10 @@ function CustomFlatList(props: FlatListProps, ref: ForwardedRef) // eslint-disable-next-line react/jsx-props-no-spreading {...props} - onScroll={props.onScroll} - onMomentumScrollEnd={onMomentumScrollEnd} + onScroll={(event) => props.onScroll?.(event)} + onMomentumScrollEnd={(event) => { + setScrollPosition({offset: event.nativeEvent.contentOffset.y}); + }} ref={ref} /> ); diff --git a/src/components/HeaderWithBackButton/types.ts b/src/components/HeaderWithBackButton/types.ts index c3ffb500080b..83afbad8636b 100644 --- a/src/components/HeaderWithBackButton/types.ts +++ b/src/components/HeaderWithBackButton/types.ts @@ -1,9 +1,9 @@ import type {ReactNode} from 'react'; -import type {OnyxEntry} from 'react-native-onyx'; +import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import type {Action} from '@hooks/useSingleExecution'; import type {StepCounterParams} from '@src/languages/types'; import type {AnchorPosition} from '@src/styles'; -import type {Policy, Report} from '@src/types/onyx'; +import type {PersonalDetails, Policy, Report} from '@src/types/onyx'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; import type IconAsset from '@src/types/utils/IconAsset'; @@ -101,6 +101,9 @@ type HeaderWithBackButtonProps = Partial & { /** The report's policy, if we're showing the details for a report and need info about it for AvatarWithDisplay */ policy?: OnyxEntry; + /** Policies, if we're showing the details for a report and need participant details for AvatarWithDisplay */ + personalDetails?: OnyxCollection; + /** Single execution function to prevent concurrent navigation actions */ singleExecution?: (action: Action) => Action; diff --git a/src/components/Icon/Illustrations.ts b/src/components/Icon/Illustrations.ts index 58cefb1877ce..28d1d53ed60c 100644 --- a/src/components/Icon/Illustrations.ts +++ b/src/components/Icon/Illustrations.ts @@ -29,13 +29,16 @@ import TadaYellow from '@assets/images/product-illustrations/tada--yellow.svg'; import TeleScope from '@assets/images/product-illustrations/telescope.svg'; import ThreeLeggedLaptopWoman from '@assets/images/product-illustrations/three_legged_laptop_woman.svg'; import ToddBehindCloud from '@assets/images/product-illustrations/todd-behind-cloud.svg'; +import Accounting from '@assets/images/simple-illustrations/simple-illustration__accounting.svg'; import Approval from '@assets/images/simple-illustrations/simple-illustration__approval.svg'; import BankArrow from '@assets/images/simple-illustrations/simple-illustration__bank-arrow.svg'; import BigRocket from '@assets/images/simple-illustrations/simple-illustration__bigrocket.svg'; import PinkBill from '@assets/images/simple-illustrations/simple-illustration__bill.svg'; import CarIce from '@assets/images/simple-illustrations/simple-illustration__car-ice.svg'; +import Car from '@assets/images/simple-illustrations/simple-illustration__car.svg'; import ChatBubbles from '@assets/images/simple-illustrations/simple-illustration__chatbubbles.svg'; import CoffeeMug from '@assets/images/simple-illustrations/simple-illustration__coffeemug.svg'; +import Coins from '@assets/images/simple-illustrations/simple-illustration__coins.svg'; import CommentBubbles from '@assets/images/simple-illustrations/simple-illustration__commentbubbles.svg'; import ConciergeBubble from '@assets/images/simple-illustrations/simple-illustration__concierge-bubble.svg'; import ConciergeNew from '@assets/images/simple-illustrations/simple-illustration__concierge.svg'; @@ -59,6 +62,7 @@ import MoneyIntoWallet from '@assets/images/simple-illustrations/simple-illustra import MoneyWings from '@assets/images/simple-illustrations/simple-illustration__moneywings.svg'; import OpenSafe from '@assets/images/simple-illustrations/simple-illustration__opensafe.svg'; import PalmTree from '@assets/images/simple-illustrations/simple-illustration__palmtree.svg'; +import Pencil from '@assets/images/simple-illustrations/simple-illustration__pencil.svg'; import Profile from '@assets/images/simple-illustrations/simple-illustration__profile.svg'; import QRCode from '@assets/images/simple-illustrations/simple-illustration__qr-code.svg'; import ReceiptEnvelope from '@assets/images/simple-illustrations/simple-illustration__receipt-envelope.svg'; @@ -148,6 +152,10 @@ export { Workflows, ThreeLeggedLaptopWoman, House, + Accounting, + Car, + Coins, + Pencil, Tag, CarIce, }; diff --git a/src/components/InvertedFlatList/BaseInvertedFlatList.tsx b/src/components/InvertedFlatList/BaseInvertedFlatList.tsx index 0549e19c2eb4..e28400505280 100644 --- a/src/components/InvertedFlatList/BaseInvertedFlatList.tsx +++ b/src/components/InvertedFlatList/BaseInvertedFlatList.tsx @@ -6,11 +6,6 @@ import FlatList from '@components/FlatList'; const WINDOW_SIZE = 15; const AUTOSCROLL_TO_TOP_THRESHOLD = 128; -const maintainVisibleContentPosition = { - minIndexForVisible: 0, - autoscrollToTopThreshold: AUTOSCROLL_TO_TOP_THRESHOLD, -}; - function BaseInvertedFlatList(props: FlatListProps, ref: ForwardedRef) { return ( (props: FlatListProps, ref: ForwardedRef ); diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index 93eac30d5477..f5545f402b14 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -1,8 +1,9 @@ import {FlashList} from '@shopify/flash-list'; import type {ReactElement} from 'react'; -import React, {memo, useCallback, useMemo} from 'react'; +import React, {memo, useCallback} from 'react'; import {StyleSheet, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; +import withCurrentReportID from '@components/withCurrentReportID'; import usePermissions from '@hooks/usePermissions'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; @@ -27,6 +28,7 @@ function LHNOptionsList({ preferredLocale = CONST.LOCALES.DEFAULT, personalDetails = {}, transactions = {}, + currentReportID = '', draftComments = {}, transactionViolations = {}, onFirstItemRendered = () => {}, @@ -84,7 +86,7 @@ function LHNOptionsList({ lastReportActionTransaction={lastReportActionTransaction} receiptTransactions={transactions} viewMode={optionMode} - isFocused={!shouldDisableFocusOptions} + isFocused={!shouldDisableFocusOptions && reportID === currentReportID} onSelectRow={onSelectRow} preferredLocale={preferredLocale} comment={itemComment} @@ -96,6 +98,7 @@ function LHNOptionsList({ ); }, [ + currentReportID, draftComments, onSelectRow, optionMode, @@ -113,8 +116,6 @@ function LHNOptionsList({ ], ); - const extraData = useMemo(() => [reportActions, reports, policy, personalDetails], [reportActions, reports, policy, personalDetails]); - return ( @@ -135,31 +136,33 @@ function LHNOptionsList({ LHNOptionsList.displayName = 'LHNOptionsList'; -export default withOnyx({ - reports: { - key: ONYXKEYS.COLLECTION.REPORT, - }, - reportActions: { - key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, - }, - policy: { - key: ONYXKEYS.COLLECTION.POLICY, - }, - preferredLocale: { - key: ONYXKEYS.NVP_PREFERRED_LOCALE, - }, - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, - transactions: { - key: ONYXKEYS.COLLECTION.TRANSACTION, - }, - draftComments: { - key: ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT, - }, - transactionViolations: { - key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, - }, -})(memo(LHNOptionsList)); +export default withCurrentReportID( + withOnyx({ + reports: { + key: ONYXKEYS.COLLECTION.REPORT, + }, + reportActions: { + key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, + }, + policy: { + key: ONYXKEYS.COLLECTION.POLICY, + }, + preferredLocale: { + key: ONYXKEYS.NVP_PREFERRED_LOCALE, + }, + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + }, + transactions: { + key: ONYXKEYS.COLLECTION.TRANSACTION, + }, + draftComments: { + key: ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT, + }, + transactionViolations: { + key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, + }, + })(memo(LHNOptionsList)), +); export type {LHNOptionsListProps}; diff --git a/src/components/LHNOptionsList/OptionRowLHNData.tsx b/src/components/LHNOptionsList/OptionRowLHNData.tsx index 9b22b50b64fe..a3394190d0c1 100644 --- a/src/components/LHNOptionsList/OptionRowLHNData.tsx +++ b/src/components/LHNOptionsList/OptionRowLHNData.tsx @@ -1,6 +1,5 @@ import {deepEqual} from 'fast-equals'; import React, {useEffect, useMemo, useRef} from 'react'; -import useCurrentReportID from '@hooks/useCurrentReportID'; import * as ReportUtils from '@libs/ReportUtils'; import SidebarUtils from '@libs/SidebarUtils'; import * as Report from '@userActions/Report'; @@ -33,8 +32,6 @@ function OptionRowLHNData({ ...propsToForward }: OptionRowLHNDataProps) { const reportID = propsToForward.reportID; - const currentReportIDValue = useCurrentReportID(); - const isReportFocused = isFocused && currentReportIDValue?.currentReportID === reportID; const optionItemRef = useRef(); @@ -88,7 +85,7 @@ function OptionRowLHNData({ ); diff --git a/src/components/LHNOptionsList/types.ts b/src/components/LHNOptionsList/types.ts index 4ca30358f9b1..c122ab018392 100644 --- a/src/components/LHNOptionsList/types.ts +++ b/src/components/LHNOptionsList/types.ts @@ -3,6 +3,7 @@ import type {RefObject} from 'react'; import type {LayoutChangeEvent, StyleProp, TextStyle, View, ViewStyle} from 'react-native'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; +import type {CurrentReportIDContextValue} from '@components/withCurrentReportID'; import type CONST from '@src/CONST'; import type {OptionData} from '@src/libs/ReportUtils'; import type {Locale, PersonalDetailsList, Policy, Report, ReportAction, ReportActions, Transaction, TransactionViolation} from '@src/types/onyx'; @@ -63,7 +64,7 @@ type CustomLHNOptionsListProps = { reportIDsWithErrors: Record; }; -type LHNOptionsListProps = CustomLHNOptionsListProps & LHNOptionsListOnyxProps; +type LHNOptionsListProps = CustomLHNOptionsListProps & CurrentReportIDContextValue & LHNOptionsListOnyxProps; type OptionRowLHNDataProps = { /** Whether row should be focused */ diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 2520520fd467..e0d6c39623ed 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -20,6 +20,7 @@ import ConfirmModal from './ConfirmModal'; import HeaderWithBackButton from './HeaderWithBackButton'; import * as Expensicons from './Icon/Expensicons'; import MoneyReportHeaderStatusBar from './MoneyReportHeaderStatusBar'; +import {usePersonalDetails} from './OnyxProvider'; import SettlementButton from './SettlementButton'; type PaymentType = DeepValueOf; @@ -40,10 +41,11 @@ type MoneyReportHeaderProps = MoneyReportHeaderOnyxProps & { report: OnyxTypes.Report; /** The policy tied to the money request report */ - policy: OnyxTypes.Policy; + policy: OnyxEntry; }; function MoneyReportHeader({session, policy, chatReport, nextStep, report: moneyRequestReport}: MoneyReportHeaderProps) { + const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; const styles = useThemeStyles(); const {translate} = useLocalize(); const {windowWidth, isSmallScreenWidth} = useWindowDimensions(); @@ -79,8 +81,8 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money // The submit button should be success green colour only if the user is submitter and the policy does not have Scheduled Submit turned on const isWaitingForSubmissionFromCurrentUser = useMemo( - () => chatReport?.isOwnPolicyExpenseChat && !policy.harvesting?.enabled, - [chatReport?.isOwnPolicyExpenseChat, policy.harvesting?.enabled], + () => chatReport?.isOwnPolicyExpenseChat && !policy?.harvesting?.enabled, + [chatReport?.isOwnPolicyExpenseChat, policy?.harvesting?.enabled], ); const threeDotsMenuItems = [HeaderUtils.getPinMenuItem(moneyRequestReport)]; @@ -100,6 +102,7 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money shouldShowPinButton={false} report={moneyRequestReport} policy={policy} + personalDetails={personalDetails} shouldShowBackButton={isSmallScreenWidth} onBackButtonPress={() => Navigation.goBack(undefined, false, true)} // Shows border if no buttons or next steps are showing below the header diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx index 338796cd856e..b5c84354e466 100644 --- a/src/components/MoneyRequestHeader.tsx +++ b/src/components/MoneyRequestHeader.tsx @@ -21,6 +21,7 @@ import HeaderWithBackButton from './HeaderWithBackButton'; import HoldBanner from './HoldBanner'; import * as Expensicons from './Icon/Expensicons'; import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar'; +import {usePersonalDetails} from './OnyxProvider'; import ProcessMoneyRequestHoldMenu from './ProcessMoneyRequestHoldMenu'; type MoneyRequestHeaderOnyxProps = { @@ -46,13 +47,14 @@ type MoneyRequestHeaderProps = MoneyRequestHeaderOnyxProps & { report: Report; /** The policy which the report is tied to */ - policy: Policy; + policy: OnyxEntry; /** The report action the transaction is tied to from the parent report */ - parentReportAction: ReportAction & OriginalMessageIOU; + parentReportAction: OnyxEntry; }; function MoneyRequestHeader({session, parentReport, report, parentReportAction, transaction, shownHoldUseExplanation = false, policy}: MoneyRequestHeaderProps) { + const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; const styles = useThemeStyles(); const {translate} = useLocalize(); const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false); @@ -69,7 +71,11 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, const isApprover = ReportUtils.isMoneyRequestReport(moneyRequestReport) && (session?.accountID ?? null) === moneyRequestReport?.managerID; const deleteTransaction = useCallback(() => { - IOU.deleteMoneyRequest(parentReportAction?.originalMessage?.IOUTransactionID ?? '', parentReportAction, true); + if (parentReportAction) { + const iouTransactionID = parentReportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? parentReportAction.originalMessage?.IOUTransactionID ?? '' : ''; + IOU.deleteMoneyRequest(iouTransactionID, parentReportAction, true); + } + setIsDeleteModalVisible(false); }, [parentReportAction, setIsDeleteModalVisible]); @@ -83,11 +89,13 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, const canDeleteRequest = isActionOwner && ReportUtils.canAddOrDeleteTransactions(moneyRequestReport) && !isDeletedParentAction; const changeMoneyRequestStatus = () => { + const iouTransactionID = parentReportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? parentReportAction.originalMessage?.IOUTransactionID ?? '' : ''; + if (isOnHold) { - IOU.unholdRequest(parentReportAction?.originalMessage?.IOUTransactionID ?? '', report?.reportID); + IOU.unholdRequest(iouTransactionID, report?.reportID); } else { const activeRoute = encodeURIComponent(Navigation.getActiveRouteWithoutParams()); - Navigation.navigate(ROUTES.MONEY_REQUEST_HOLD_REASON.getRoute(policy?.type, parentReportAction?.originalMessage?.IOUTransactionID ?? '', report?.reportID, activeRoute)); + Navigation.navigate(ROUTES.MONEY_REQUEST_HOLD_REASON.getRoute(policy?.type ?? '', iouTransactionID, report?.reportID, activeRoute)); } }; @@ -165,6 +173,7 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, ownerAccountID: parentReport?.ownerAccountID, }} policy={policy} + personalDetails={personalDetails} shouldShowBackButton={isSmallScreenWidth} onBackButtonPress={() => Navigation.goBack(undefined, false, true)} /> diff --git a/src/components/Onfido/BaseOnfidoWeb.js b/src/components/Onfido/BaseOnfidoWeb.js index ee206b15fc24..57f10f49f396 100644 --- a/src/components/Onfido/BaseOnfidoWeb.js +++ b/src/components/Onfido/BaseOnfidoWeb.js @@ -1,5 +1,5 @@ import lodashGet from 'lodash/get'; -import * as OnfidoSDK from 'onfido-sdk-ui'; +import {Onfido as OnfidoSDK} from 'onfido-sdk-ui'; import React, {forwardRef, useEffect} from 'react'; import _ from 'underscore'; import useLocalize from '@hooks/useLocalize'; @@ -15,7 +15,6 @@ function initializeOnfido({sdkToken, onSuccess, onError, onUserExit, preferredLo OnfidoSDK.init({ token: sdkToken, containerId: CONST.ONFIDO.CONTAINER_ID, - useMemoryHistory: true, customUI: { fontFamilyTitle: `${FontUtils.fontFamily.platform.EXP_NEUE}, -apple-system, serif`, fontFamilySubtitle: `${FontUtils.fontFamily.platform.EXP_NEUE}, -apple-system, serif`, @@ -86,18 +85,15 @@ function initializeOnfido({sdkToken, onSuccess, onError, onUserExit, preferredLo onSuccess(data); }, onError: (error) => { - const errorMessage = lodashGet(error, 'message', CONST.ERROR.UNKNOWN_ERROR); const errorType = lodashGet(error, 'type'); + const errorMessage = lodashGet(error, 'message', CONST.ERROR.UNKNOWN_ERROR); Log.hmmm('Onfido error', {errorType, errorMessage}); + if (errorType === CONST.WALLET.ERROR.ONFIDO_USER_CONSENT_DENIED) { + onUserExit(); + return; + } onError(errorMessage); }, - onUserExit: (userExitCode) => { - Log.hmmm('Onfido user exits the flow', {userExitCode}); - onUserExit(userExitCode); - }, - onModalRequestClose: () => { - Log.hmmm('Onfido user closed the modal'); - }, language: { // We need to use ES_ES as locale key because the key `ES` is not a valid config key for Onfido locale: preferredLocale === CONST.LOCALES.ES ? CONST.LOCALES.ES_ES_ONFIDO : preferredLocale, diff --git a/src/components/PDFThumbnail/index.native.tsx b/src/components/PDFThumbnail/index.native.tsx index 4d3a33ae4e67..0232dba99f05 100644 --- a/src/components/PDFThumbnail/index.native.tsx +++ b/src/components/PDFThumbnail/index.native.tsx @@ -6,7 +6,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import addEncryptedAuthTokenToURL from '@libs/addEncryptedAuthTokenToURL'; import type PDFThumbnailProps from './types'; -function PDFThumbnail({previewSourceURL, style, isAuthTokenRequired = false, enabled = true, onPassword = () => {}}: PDFThumbnailProps) { +function PDFThumbnail({previewSourceURL, style, isAuthTokenRequired = false, enabled = true, onPassword}: PDFThumbnailProps) { const styles = useThemeStyles(); const sizeStyles = [styles.w100, styles.h100]; @@ -25,6 +25,9 @@ function PDFThumbnail({previewSourceURL, style, isAuthTokenRequired = false, ena if (!('message' in error && typeof error.message === 'string' && error.message.match(/password/i))) { return; } + if (!onPassword) { + return; + } onPassword(); }} /> diff --git a/src/components/PDFThumbnail/index.tsx b/src/components/PDFThumbnail/index.tsx index e69e4dd5075b..a5b911deb6ff 100644 --- a/src/components/PDFThumbnail/index.tsx +++ b/src/components/PDFThumbnail/index.tsx @@ -12,7 +12,7 @@ if (!pdfjs.GlobalWorkerOptions.workerSrc) { pdfjs.GlobalWorkerOptions.workerSrc = URL.createObjectURL(new Blob([pdfWorkerSource], {type: 'text/javascript'})); } -function PDFThumbnail({previewSourceURL, style, isAuthTokenRequired = false, enabled = true, onPassword = () => {}}: PDFThumbnailProps) { +function PDFThumbnail({previewSourceURL, style, isAuthTokenRequired = false, enabled = true, onPassword}: PDFThumbnailProps) { const styles = useThemeStyles(); const thumbnail = useMemo( @@ -25,9 +25,7 @@ function PDFThumbnail({previewSourceURL, style, isAuthTokenRequired = false, ena cMapPacked: true, }} externalLinkTarget="_blank" - onPassword={() => { - onPassword(); - }} + onPassword={onPassword} > diff --git a/src/components/ReportActionItem/MoneyRequestAction.tsx b/src/components/ReportActionItem/MoneyRequestAction.tsx index 9e169b23391a..05891311ba6d 100644 --- a/src/components/ReportActionItem/MoneyRequestAction.tsx +++ b/src/components/ReportActionItem/MoneyRequestAction.tsx @@ -8,9 +8,7 @@ import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import * as IOUUtils from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; -import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; -import * as ReportUtils from '@libs/ReportUtils'; import type {ContextMenuAnchor} from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import * as Report from '@userActions/Report'; import CONST from '@src/CONST'; @@ -91,15 +89,7 @@ function MoneyRequestAction({ return; } - // If the childReportID is not present, we need to create a new thread - const childReportID = action?.childReportID; - if (!childReportID) { - const thread = ReportUtils.buildTransactionThread(action, requestReportID); - const userLogins = PersonalDetailsUtils.getLoginsByAccountIDs(thread.participantAccountIDs ?? []); - Report.openReport(thread.reportID, userLogins, thread, action.reportActionID); - Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(thread.reportID)); - return; - } + const childReportID = action?.childReportID ?? '0'; Report.openReport(childReportID); Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(childReportID)); }; diff --git a/src/components/ScreenWrapper.tsx b/src/components/ScreenWrapper.tsx index 4605d27b32dc..306846ad7d99 100644 --- a/src/components/ScreenWrapper.tsx +++ b/src/components/ScreenWrapper.tsx @@ -14,7 +14,7 @@ import useTackInputFocus from '@hooks/useTackInputFocus'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as Browser from '@libs/Browser'; -import type {RootStackParamList} from '@libs/Navigation/types'; +import type {CentralPaneNavigatorParamList, RootStackParamList} from '@libs/Navigation/types'; import toggleTestToolsModal from '@userActions/TestTool'; import CONST from '@src/CONST'; import CustomDevMenu from './CustomDevMenu'; @@ -92,7 +92,7 @@ type ScreenWrapperProps = { * * This is required because transitionEnd event doesn't trigger in the testing environment. */ - navigation?: StackNavigationProp; + navigation?: StackNavigationProp | StackNavigationProp; /** Whether to show offline indicator on wide screens */ shouldShowOfflineIndicatorInWideScreen?: boolean; diff --git a/src/components/SelectionList/BaseListItem.tsx b/src/components/SelectionList/BaseListItem.tsx index c032fe2d081b..6eedc322f393 100644 --- a/src/components/SelectionList/BaseListItem.tsx +++ b/src/components/SelectionList/BaseListItem.tsx @@ -66,19 +66,19 @@ function BaseListItem({ {...bind} onPress={() => onSelectRow(item)} disabled={isDisabled} - accessibilityLabel={item.text} + accessibilityLabel={item.text ?? ''} role={CONST.ROLE.BUTTON} hoverDimmingValue={1} hoverStyle={!item.isSelected && styles.hoveredComponentBG} dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}} onMouseDown={shouldPreventDefaultFocusOnSelectRow ? (e) => e.preventDefault() : undefined} - nativeID={keyForList} + nativeID={keyForList ?? ''} style={pressableStyle} > {canSelectMultiple && ( ( onDismissError={() => onDismissError?.(item)} shouldPreventDefaultFocusOnSelectRow={shouldPreventDefaultFocusOnSelectRow} rightHandSideComponent={rightHandSideComponent} - keyForList={item.keyForList} + keyForList={item.keyForList ?? ''} isMultilineSupported={isRowMultilineSupported} /> ); @@ -499,7 +499,7 @@ function BaseSelectionList( getItemLayout={getItemLayout} onScroll={onScroll} onScrollBeginDrag={onScrollBeginDrag} - keyExtractor={(item) => item.keyForList} + keyExtractor={(item, index) => item.keyForList ?? `${index}`} extraData={focusedIndex} indicatorStyle="white" keyboardShouldPersistTaps="always" diff --git a/src/components/SelectionList/RadioListItem.tsx b/src/components/SelectionList/RadioListItem.tsx index be2302d21b89..f61e0d1c6a3d 100644 --- a/src/components/SelectionList/RadioListItem.tsx +++ b/src/components/SelectionList/RadioListItem.tsx @@ -42,7 +42,7 @@ function RadioListItem({ - {(hovered) => ( + {(hovered?: boolean) => ( <> {!!item.icons && (item.shouldShowSubscript ? ( @@ -81,7 +81,7 @@ function UserListItem({ = { isFocused?: boolean; /** Whether this item is disabled */ - isDisabled?: boolean; + isDisabled?: boolean | null; /** Whether this item should show Tooltip */ showTooltip: boolean; @@ -47,19 +49,19 @@ type CommonListItemProps = { type ListItem = { /** Text to display */ - text: string; + text?: string; /** Alternate text to display */ alternateText?: string | null; /** Key used internally by React */ - keyForList: string; + keyForList?: string | null; /** Whether this option is selected */ isSelected?: boolean; /** Whether this option is disabled for selection */ - isDisabled?: boolean; + isDisabled?: boolean | null; /** List title is bold by default. Use this props to customize it */ isBold?: boolean; @@ -90,6 +92,9 @@ type ListItem = { /** Represents the index of the option within the section it came from */ index?: number; + /** ID of the report */ + reportID?: string; + /** Whether this option should show subscript */ shouldShowSubscript?: boolean | null; @@ -117,7 +122,7 @@ type ListItemProps = CommonListItemProps & { type BaseListItemProps = CommonListItemProps & { item: TItem; shouldPreventDefaultFocusOnSelectRow?: boolean; - keyForList?: string; + keyForList?: string | null; errors?: Errors | ReceiptErrors | null; pendingAction?: PendingAction | null; FooterComponent?: ReactElement; @@ -158,7 +163,7 @@ type Section = { type BaseSelectionListProps = Partial & { /** Sections for the section list */ - sections: Array>>; + sections: Array>> | typeof CONST.EMPTY_ARRAY; /** Default renderer for every item in the list */ ListItem: typeof RadioListItem | typeof UserListItem | typeof TableListItem; @@ -185,7 +190,7 @@ type BaseSelectionListProps = Partial & { textInputPlaceholder?: string; /** Hint for the text input */ - textInputHint?: string; + textInputHint?: MaybePhraseKey; /** Value for the text input */ textInputValue?: string; @@ -274,6 +279,9 @@ type BaseSelectionListProps = Partial & { /** Styles for the list header wrapper */ listHeaderWrapperStyle?: StyleProp; + /** Whether to auto focus the Search Input */ + autoFocus?: boolean; + /** Whether to wrap long text up to 2 lines */ isRowMultilineSupported?: boolean; diff --git a/src/components/withViewportOffsetTop.tsx b/src/components/withViewportOffsetTop.tsx index d3e9b63ad3ee..2b659ac608b5 100644 --- a/src/components/withViewportOffsetTop.tsx +++ b/src/components/withViewportOffsetTop.tsx @@ -40,3 +40,5 @@ export default function withViewportOffsetTop {}; - -export default useFlipper; diff --git a/src/hooks/useFlipper/types.ts b/src/hooks/useFlipper/types.ts deleted file mode 100644 index e69272fcb92c..000000000000 --- a/src/hooks/useFlipper/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type {NavigationContainerRefWithCurrent} from '@react-navigation/core'; -import type {RootStackParamList} from '@libs/Navigation/types'; - -type UseFlipper = (ref: NavigationContainerRefWithCurrent) => void; - -export default UseFlipper; diff --git a/src/languages/en.ts b/src/languages/en.ts index 9c5b388cfff8..96673be70698 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1755,6 +1755,7 @@ export default { workspaceType: 'Workspace type', workspaceAvatar: 'Workspace avatar', mustBeOnlineToViewMembers: 'You must be online in order to view members of this workspace.', + moreFeatures: 'More features', requested: 'Requested', distanceRates: 'Distance rates', selected: ({selectedNumber}) => `${selectedNumber} selected`, @@ -1779,6 +1780,48 @@ export default { existingCategoryError: 'A category with this name already exists.', invalidCategoryName: 'Invalid category name.', }, + moreFeatures: { + spendSection: { + title: 'Spend', + subtitle: 'Enable optional functionality that helps you scale your team.', + }, + organizeSection: { + title: 'Organize', + subtitle: 'Group and analyze spend, record every tax paid.', + }, + integrateSection: { + title: 'Integrate', + subtitle: 'Connect Expensify to popular financial products.', + }, + distanceRates: { + title: 'Distance rates', + subtitle: 'Add, update and enforce rates.', + }, + workflows: { + title: 'Workflows', + subtitle: 'Configure how spend is approved and paid.', + }, + categories: { + title: 'Categories', + subtitle: 'Track and organize spend.', + }, + tags: { + title: 'Tags', + subtitle: 'Add additional ways to classify spend.', + }, + taxes: { + title: 'Taxes', + subtitle: 'Document and reclaim eligible taxes.', + }, + reportFields: { + title: 'Report fields', + subtitle: 'Set up custom fields for spend.', + }, + connections: { + title: 'Connections', + subtitle: 'Sync your chart of accounts and more.', + }, + }, tags: { requiresTag: 'Members must tag all spend', enableTag: 'Enable tag', diff --git a/src/languages/es.ts b/src/languages/es.ts index 9e2418d89233..d7dc9a1b404e 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1779,6 +1779,7 @@ export default { workspaceType: 'Tipo de espacio de trabajo', workspaceAvatar: 'Espacio de trabajo avatar', mustBeOnlineToViewMembers: 'Debes estar en línea para poder ver los miembros de este espacio de trabajo.', + moreFeatures: 'Más características', requested: 'Solicitado', distanceRates: 'Tasas de distancia', selected: ({selectedNumber}) => `${selectedNumber} seleccionados`, @@ -1803,6 +1804,48 @@ export default { existingCategoryError: 'Ya existe una categoría con este nombre.', invalidCategoryName: 'Lo nombre de la categoría es invalido.', }, + moreFeatures: { + spendSection: { + title: 'Gasto', + subtitle: 'Habilita otras funcionalidades que ayudan a aumentar tu equipo.', + }, + organizeSection: { + title: 'Organizar', + subtitle: 'Agrupa y analiza el gasto, registra cada impuesto pagado.', + }, + integrateSection: { + title: 'Integrar', + subtitle: 'Conecta Expensify a otros productos financieros populares.', + }, + distanceRates: { + title: 'Tasas de distancia', + subtitle: 'Añade, actualiza y haz cumplir las tasas.', + }, + workflows: { + title: 'Flujos de trabajo', + subtitle: 'Configura cómo se aprueba y paga los gastos.', + }, + categories: { + title: 'Categorías', + subtitle: 'Monitoriza y organiza los gastos.', + }, + tags: { + title: 'Etiquetas', + subtitle: 'Añade formas adicionales de clasificar los gastos.', + }, + taxes: { + title: 'Impuestos', + subtitle: 'Documenta y reclama los impuestos aplicables.', + }, + reportFields: { + title: 'Campos de informes', + subtitle: 'Configura campos personalizados para los gastos.', + }, + connections: { + title: 'Conexión', + subtitle: 'Sincroniza tu plan de cuentas y otras opciones.', + }, + }, tags: { requiresTag: 'Los miembros deben etiquetar todos los gastos', enableTag: 'Habilitar etiqueta', diff --git a/src/libs/API/parameters/AddCommentOrAttachementParams.ts b/src/libs/API/parameters/AddCommentOrAttachementParams.ts index a705c92f7f27..335df4f91fe2 100644 --- a/src/libs/API/parameters/AddCommentOrAttachementParams.ts +++ b/src/libs/API/parameters/AddCommentOrAttachementParams.ts @@ -7,7 +7,6 @@ type AddCommentOrAttachementParams = { reportComment?: string; file?: FileObject; timezone?: string; - shouldAllowActionableMentionWhispers?: boolean; clientCreatedTime?: string; isOldDotConciergeChat?: boolean; }; diff --git a/src/libs/API/parameters/EnablePolicyCategoriesParams.ts b/src/libs/API/parameters/EnablePolicyCategoriesParams.ts new file mode 100644 index 000000000000..61aa600b8ea0 --- /dev/null +++ b/src/libs/API/parameters/EnablePolicyCategoriesParams.ts @@ -0,0 +1,6 @@ +type EnablePolicyCategoriesParams = { + policyID: string; + enabled: boolean; +}; + +export default EnablePolicyCategoriesParams; diff --git a/src/libs/API/parameters/EnablePolicyConnectionsParams.ts b/src/libs/API/parameters/EnablePolicyConnectionsParams.ts new file mode 100644 index 000000000000..cd2ac828b359 --- /dev/null +++ b/src/libs/API/parameters/EnablePolicyConnectionsParams.ts @@ -0,0 +1,6 @@ +type EnablePolicyConnectionsParams = { + policyID: string; + enabled: boolean; +}; + +export default EnablePolicyConnectionsParams; diff --git a/src/libs/API/parameters/EnablePolicyDistanceRatesParams.ts b/src/libs/API/parameters/EnablePolicyDistanceRatesParams.ts new file mode 100644 index 000000000000..d66f898e6e10 --- /dev/null +++ b/src/libs/API/parameters/EnablePolicyDistanceRatesParams.ts @@ -0,0 +1,6 @@ +type EnablePolicyDistanceRatesParams = { + policyID: string; + enabled: boolean; +}; + +export default EnablePolicyDistanceRatesParams; diff --git a/src/libs/API/parameters/EnablePolicyReportFieldsParams.ts b/src/libs/API/parameters/EnablePolicyReportFieldsParams.ts new file mode 100644 index 000000000000..7a5670e200c8 --- /dev/null +++ b/src/libs/API/parameters/EnablePolicyReportFieldsParams.ts @@ -0,0 +1,6 @@ +type EnablePolicyReportFieldsParams = { + policyID: string; + enabled: boolean; +}; + +export default EnablePolicyReportFieldsParams; diff --git a/src/libs/API/parameters/EnablePolicyTagsParams.ts b/src/libs/API/parameters/EnablePolicyTagsParams.ts new file mode 100644 index 000000000000..8a8e21dd3371 --- /dev/null +++ b/src/libs/API/parameters/EnablePolicyTagsParams.ts @@ -0,0 +1,6 @@ +type EnablePolicyTagsParams = { + policyID: string; + enabled: boolean; +}; + +export default EnablePolicyTagsParams; diff --git a/src/libs/API/parameters/EnablePolicyTaxesParams.ts b/src/libs/API/parameters/EnablePolicyTaxesParams.ts new file mode 100644 index 000000000000..4a235d5d6a1f --- /dev/null +++ b/src/libs/API/parameters/EnablePolicyTaxesParams.ts @@ -0,0 +1,6 @@ +type EnablePolicyTaxesParams = { + policyID: string; + enabled: boolean; +}; + +export default EnablePolicyTaxesParams; diff --git a/src/libs/API/parameters/EnablePolicyWorkflowsParams.ts b/src/libs/API/parameters/EnablePolicyWorkflowsParams.ts new file mode 100644 index 000000000000..1958ec8df581 --- /dev/null +++ b/src/libs/API/parameters/EnablePolicyWorkflowsParams.ts @@ -0,0 +1,6 @@ +type EnablePolicyWorkflowsParams = { + policyID: string; + enabled: boolean; +}; + +export default EnablePolicyWorkflowsParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index b56398f6c4ad..df11c3bb9a7d 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -156,6 +156,13 @@ export type {default as SetWorkspaceAutoReportingFrequencyParams} from './SetWor export type {default as SetWorkspaceAutoReportingMonthlyOffsetParams} from './SetWorkspaceAutoReportingMonthlyOffsetParams'; export type {default as SetWorkspaceApprovalModeParams} from './SetWorkspaceApprovalModeParams'; export type {default as SwitchToOldDotParams} from './SwitchToOldDotParams'; +export type {default as EnablePolicyCategoriesParams} from './EnablePolicyCategoriesParams'; +export type {default as EnablePolicyConnectionsParams} from './EnablePolicyConnectionsParams'; +export type {default as EnablePolicyDistanceRatesParams} from './EnablePolicyDistanceRatesParams'; +export type {default as EnablePolicyTagsParams} from './EnablePolicyTagsParams'; +export type {default as EnablePolicyTaxesParams} from './EnablePolicyTaxesParams'; +export type {default as EnablePolicyWorkflowsParams} from './EnablePolicyWorkflowsParams'; +export type {default as EnablePolicyReportFieldsParams} from './EnablePolicyReportFieldsParams'; export type {default as AcceptJoinRequestParams} from './AcceptJoinRequest'; export type {default as DeclineJoinRequestParams} from './DeclineJoinRequest'; export type {default as JoinPolicyInviteLinkParams} from './JoinPolicyInviteLink'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index d2aa1c84a9a1..e75e743d5788 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -157,6 +157,13 @@ const WRITE_COMMANDS = { CANCEL_PAYMENT: 'CancelPayment', ACCEPT_ACH_CONTRACT_FOR_BANK_ACCOUNT: 'AcceptACHContractForBankAccount', SWITCH_TO_OLD_DOT: 'SwitchToOldDot', + ENABLE_POLICY_CATEGORIES: 'EnablePolicyCategories', + ENABLE_POLICY_CONNECTIONS: 'EnablePolicyConnections', + ENABLE_POLICY_DISTANCE_RATES: 'EnablePolicyDistanceRates', + ENABLE_POLICY_TAGS: 'EnablePolicyTags', + ENABLE_POLICY_TAXES: 'EnablePolicyTaxes', + ENABLE_POLICY_WORKFLOWS: 'EnablePolicyWorkflows', + ENABLE_POLICY_REPORT_FIELDS: 'EnablePolicyReportFields', JOIN_POLICY_VIA_INVITE_LINK: 'JoinWorkspaceViaInviteLink', ACCEPT_JOIN_REQUEST: 'AcceptJoinRequest', DECLINE_JOIN_REQUEST: 'DeclineJoinRequest', @@ -315,6 +322,13 @@ type WriteCommandParameters = { [WRITE_COMMANDS.SET_WORKSPACE_AUTO_REPORTING_MONTHLY_OFFSET]: Parameters.SetWorkspaceAutoReportingMonthlyOffsetParams; [WRITE_COMMANDS.SET_WORKSPACE_APPROVAL_MODE]: Parameters.SetWorkspaceApprovalModeParams; [WRITE_COMMANDS.SWITCH_TO_OLD_DOT]: Parameters.SwitchToOldDotParams; + [WRITE_COMMANDS.ENABLE_POLICY_CATEGORIES]: Parameters.EnablePolicyCategoriesParams; + [WRITE_COMMANDS.ENABLE_POLICY_CONNECTIONS]: Parameters.EnablePolicyConnectionsParams; + [WRITE_COMMANDS.ENABLE_POLICY_DISTANCE_RATES]: Parameters.EnablePolicyDistanceRatesParams; + [WRITE_COMMANDS.ENABLE_POLICY_TAGS]: Parameters.EnablePolicyTagsParams; + [WRITE_COMMANDS.ENABLE_POLICY_TAXES]: Parameters.EnablePolicyTaxesParams; + [WRITE_COMMANDS.ENABLE_POLICY_WORKFLOWS]: Parameters.EnablePolicyWorkflowsParams; + [WRITE_COMMANDS.ENABLE_POLICY_REPORT_FIELDS]: Parameters.EnablePolicyReportFieldsParams; [WRITE_COMMANDS.JOIN_POLICY_VIA_INVITE_LINK]: Parameters.JoinPolicyInviteLinkParams; [WRITE_COMMANDS.ACCEPT_JOIN_REQUEST]: Parameters.AcceptJoinRequestParams; [WRITE_COMMANDS.DECLINE_JOIN_REQUEST]: Parameters.DeclineJoinRequestParams; diff --git a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx index 28e3bc8f5a88..5a3af07a3d5a 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx @@ -24,6 +24,7 @@ const workspaceSettingsScreens = { [SCREENS.WORKSPACE.TRAVEL]: () => require('../../../../../pages/workspace/travel/WorkspaceTravelPage').default as React.ComponentType, [SCREENS.WORKSPACE.MEMBERS]: () => require('../../../../../pages/workspace/WorkspaceMembersPage').default as React.ComponentType, [SCREENS.WORKSPACE.CATEGORIES]: () => require('../../../../../pages/workspace/categories/WorkspaceCategoriesPage').default as React.ComponentType, + [SCREENS.WORKSPACE.MORE_FEATURES]: () => require('../../../../../pages/workspace/WorkspaceMoreFeaturesPage').default as React.ComponentType, [SCREENS.WORKSPACE.TAGS]: () => require('../../../../../pages/workspace/tags/WorkspaceTagsPage').default as React.ComponentType, [SCREENS.WORKSPACE.DISTANCE_RATES]: () => require('../../../../../pages/workspace/distanceRates/PolicyDistanceRatesPage').default as React.ComponentType, } satisfies Screens; diff --git a/src/libs/Navigation/AppNavigator/ReportScreenWrapper.tsx b/src/libs/Navigation/AppNavigator/ReportScreenWrapper.tsx index fd764cdb5861..5d710afa69e9 100644 --- a/src/libs/Navigation/AppNavigator/ReportScreenWrapper.tsx +++ b/src/libs/Navigation/AppNavigator/ReportScreenWrapper.tsx @@ -13,7 +13,6 @@ function ReportScreenWrapper({route, navigation}: ReportScreenWrapperProps) { return ( <> diff --git a/src/libs/Navigation/NavigationRoot.tsx b/src/libs/Navigation/NavigationRoot.tsx index 2ca4c5178a5e..39efd8203c75 100644 --- a/src/libs/Navigation/NavigationRoot.tsx +++ b/src/libs/Navigation/NavigationRoot.tsx @@ -3,7 +3,6 @@ import {DefaultTheme, findFocusedRoute, NavigationContainer} from '@react-naviga import React, {useEffect, useMemo, useRef} from 'react'; import useActiveWorkspace from '@hooks/useActiveWorkspace'; import useCurrentReportID from '@hooks/useCurrentReportID'; -import useFlipper from '@hooks/useFlipper'; import useTheme from '@hooks/useTheme'; import useWindowDimensions from '@hooks/useWindowDimensions'; import Log from '@libs/Log'; @@ -60,7 +59,6 @@ function parseAndLogRoute(state: NavigationState) { } function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady}: NavigationRootProps) { - useFlipper(navigationRef); const firstRenderRef = useRef(true); const theme = useTheme(); diff --git a/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts b/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts index 6641b2c88f1a..be9c0b55e761 100755 --- a/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts @@ -14,6 +14,7 @@ const TAB_TO_CENTRAL_PANE_MAPPING: Record = { SCREENS.WORKSPACE.TRAVEL, SCREENS.WORKSPACE.MEMBERS, SCREENS.WORKSPACE.CATEGORIES, + SCREENS.WORKSPACE.MORE_FEATURES, SCREENS.WORKSPACE.DISTANCE_RATES, ], }; diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index fc3ad1668cd4..643860e4d81e 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -69,6 +69,9 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.CATEGORIES]: { path: ROUTES.WORKSPACE_CATEGORIES.route, }, + [SCREENS.WORKSPACE.MORE_FEATURES]: { + path: ROUTES.WORKSPACE_MORE_FEATURES.route, + }, [SCREENS.WORKSPACE.TAGS]: { path: ROUTES.WORKSPACE_TAGS.route, }, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 8fa7290323a2..b6b19c4560e0 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -91,6 +91,9 @@ type CentralPaneNavigatorParamList = { [SCREENS.WORKSPACE.CATEGORIES]: { policyID: string; }; + [SCREENS.WORKSPACE.MORE_FEATURES]: { + policyID: string; + }; [SCREENS.WORKSPACE.TAGS]: { policyID: string; categoryName: string; @@ -602,7 +605,7 @@ type AuthScreensParamList = SharedScreensParamList & { }; }; -type RootStackParamList = PublicScreensParamList & AuthScreensParamList; +type RootStackParamList = PublicScreensParamList & AuthScreensParamList & SearchNavigatorParamList; type BottomTabName = keyof BottomTabNavigatorParamList; diff --git a/src/libs/Notification/LocalNotification/BrowserNotifications.ts b/src/libs/Notification/LocalNotification/BrowserNotifications.ts index 8908b91c4f42..2a0dfc1cd91a 100644 --- a/src/libs/Notification/LocalNotification/BrowserNotifications.ts +++ b/src/libs/Notification/LocalNotification/BrowserNotifications.ts @@ -44,7 +44,15 @@ function canUseBrowserNotifications(): Promise { * @param icon Path to icon * @param data extra data to attach to the notification */ -function push(title: string, body = '', icon: string | ImageSourcePropType = '', data: LocalNotificationData = {}, onClick: LocalNotificationClickHandler = () => {}, silent = false) { +function push( + title: string, + body = '', + icon: string | ImageSourcePropType = '', + data: LocalNotificationData = {}, + onClick: LocalNotificationClickHandler = () => {}, + silent = false, + tag = '', +) { canUseBrowserNotifications().then((canUseNotifications) => { if (!canUseNotifications) { return; @@ -57,6 +65,7 @@ function push(title: string, body = '', icon: string | ImageSourcePropType = '', icon: String(icon), data, silent, + tag, }); notificationCache[notificationID].onclick = () => { onClick(); @@ -124,9 +133,17 @@ export default { * Create a notification to indicate that an update is available. */ pushUpdateAvailableNotification() { - push('Update available', 'A new version of this app is available!', '', {}, () => { - AppUpdate.triggerUpdateAvailable(); - }); + push( + 'Update available', + 'A new version of this app is available!', + '', + {}, + () => { + AppUpdate.triggerUpdateAvailable(); + }, + false, + 'UpdateAvailable', + ); }, /** diff --git a/src/libs/OnyxSelectors/reportWithoutHasDraftSelector.ts b/src/libs/OnyxSelectors/reportWithoutHasDraftSelector.ts index 82410b120df2..8305fa217f79 100644 --- a/src/libs/OnyxSelectors/reportWithoutHasDraftSelector.ts +++ b/src/libs/OnyxSelectors/reportWithoutHasDraftSelector.ts @@ -1,10 +1,14 @@ import type {OnyxEntry} from 'react-native-onyx'; import type {Report} from '@src/types/onyx'; -export default function reportWithoutHasDraftSelector(report: OnyxEntry) { +type ReportWithoutHasDraft = Omit; + +export default function reportWithoutHasDraftSelector(report: OnyxEntry): OnyxEntry { if (!report) { - return report; + return null; } const {hasDraft, ...reportWithoutHasDraft} = report; return reportWithoutHasDraft; } + +export type {ReportWithoutHasDraft}; diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 3dd23752d5db..1aa73e752eae 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -902,7 +902,7 @@ function sortTags(tags: Record | Tag[]) { * @param options[].name - a name of an option * @param [isOneLine] - a flag to determine if text should be one line */ -function getCategoryOptionTree(options: Record | Category[], isOneLine = false): OptionTree[] { +function getCategoryOptionTree(options: Record | Category[], isOneLine = false, selectedOptionsName: string[] = []): OptionTree[] { const optionCollection = new Map(); Object.values(options).forEach((option) => { if (isOneLine) { @@ -937,7 +937,7 @@ function getCategoryOptionTree(options: Record | Category[], i searchText, tooltipText: optionName, isDisabled: isChild ? !option.enabled : true, - isSelected: !!option.isSelected, + isSelected: isChild ? !!option.isSelected : selectedOptionsName.includes(searchText), }); }); }); @@ -1045,7 +1045,7 @@ function getCategoryListSections( title: Localize.translateLocal('common.all'), shouldShow: true, indexOffset, - data: getCategoryOptionTree(filteredCategories), + data: getCategoryOptionTree(filteredCategories, false, selectedOptionNames), }); return categorySections; @@ -1723,7 +1723,7 @@ function getOptions( /** * Build the options for the Search view */ -function getSearchOptions(reports: Record, personalDetails: OnyxEntry, searchValue = '', betas: Beta[] = []): GetOptions { +function getSearchOptions(reports: OnyxCollection, personalDetails: OnyxEntry, searchValue = '', betas: Beta[] = []): GetOptions { Timing.start(CONST.TIMING.LOAD_SEARCH_OPTIONS); Performance.markStart(CONST.TIMING.LOAD_SEARCH_OPTIONS); const options = getOptions(reports, personalDetails, { diff --git a/src/libs/Performance.tsx b/src/libs/Performance.tsx index 5ac064e75727..8d812014dbf8 100644 --- a/src/libs/Performance.tsx +++ b/src/libs/Performance.tsx @@ -122,9 +122,6 @@ if (Metrics.canCapturePerformanceMetrics()) { * Sets up an observer to capture events recorded in the native layer before the app fully initializes. */ Performance.setupPerformanceObserver = () => { - const performanceReported = require('react-native-performance-flipper-reporter'); - performanceReported.setupDefaultFlipperReporter(); - // Monitor some native marks that we want to put on the timeline new perfModule.PerformanceObserver((list: PerformanceObserverEntryList, observer: PerformanceObserver) => { list.getEntries().forEach((entry: PerformanceEntry) => { diff --git a/src/libs/Pusher/pusher.ts b/src/libs/Pusher/pusher.ts index 48c5e5c1409f..3cb15c0f3fc3 100644 --- a/src/libs/Pusher/pusher.ts +++ b/src/libs/Pusher/pusher.ts @@ -1,6 +1,5 @@ import isObject from 'lodash/isObject'; import type {Channel, ChannelAuthorizerGenerator, Options} from 'pusher-js/with-encryption'; -import {InteractionManager} from 'react-native'; import Onyx from 'react-native-onyx'; import type {LiteralUnion, ValueOf} from 'type-fest'; import Log from '@libs/Log'; @@ -227,50 +226,48 @@ function subscribe( onResubscribe = () => {}, ): Promise { return new Promise((resolve, reject) => { - InteractionManager.runAfterInteractions(() => { - // We cannot call subscribe() before init(). Prevent any attempt to do this on dev. - if (!socket) { - throw new Error(`[Pusher] instance not found. Pusher.subscribe() + // We cannot call subscribe() before init(). Prevent any attempt to do this on dev. + if (!socket) { + throw new Error(`[Pusher] instance not found. Pusher.subscribe() most likely has been called before Pusher.init()`); - } - - Log.info('[Pusher] Attempting to subscribe to channel', false, {channelName, eventName}); - let channel = getChannel(channelName); - - if (!channel || !channel.subscribed) { - channel = socket.subscribe(channelName); - let isBound = false; - channel.bind('pusher:subscription_succeeded', () => { - // Check so that we do not bind another event with each reconnect attempt - if (!isBound) { - bindEventToChannel(channel, eventName, eventCallback); - resolve(); - isBound = true; - return; - } - - // When subscribing for the first time we register a success callback that can be - // called multiple times when the subscription succeeds again in the future - // e.g. as a result of Pusher disconnecting and reconnecting. This callback does - // not fire on the first subscription_succeeded event. - onResubscribe(); - }); + } - channel.bind('pusher:subscription_error', (data: PusherSubscribtionErrorData = {}) => { - const {type, error, status} = data; - Log.hmmm('[Pusher] Issue authenticating with Pusher during subscribe attempt.', { - channelName, - status, - type, - error, - }); - reject(error); + Log.info('[Pusher] Attempting to subscribe to channel', false, {channelName, eventName}); + let channel = getChannel(channelName); + + if (!channel || !channel.subscribed) { + channel = socket.subscribe(channelName); + let isBound = false; + channel.bind('pusher:subscription_succeeded', () => { + // Check so that we do not bind another event with each reconnect attempt + if (!isBound) { + bindEventToChannel(channel, eventName, eventCallback); + resolve(); + isBound = true; + return; + } + + // When subscribing for the first time we register a success callback that can be + // called multiple times when the subscription succeeds again in the future + // e.g. as a result of Pusher disconnecting and reconnecting. This callback does + // not fire on the first subscription_succeeded event. + onResubscribe(); + }); + + channel.bind('pusher:subscription_error', (data: PusherSubscribtionErrorData = {}) => { + const {type, error, status} = data; + Log.hmmm('[Pusher] Issue authenticating with Pusher during subscribe attempt.', { + channelName, + status, + type, + error, }); - } else { - bindEventToChannel(channel, eventName, eventCallback); - resolve(); - } - }); + reject(error); + }); + } else { + bindEventToChannel(channel, eventName, eventCallback); + resolve(); + } }); } diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index b12469941fd9..c158a0c2972c 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -135,7 +135,7 @@ function isModifiedExpenseAction(reportAction: OnyxEntry): boolean return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.MODIFIEDEXPENSE; } -function isWhisperAction(reportAction: OnyxEntry): boolean { +function isWhisperAction(reportAction: OnyxEntry | EmptyObject): boolean { return (reportAction?.whisperedToAccountIDs ?? []).length > 0; } @@ -205,7 +205,7 @@ function isSentMoneyReportAction(reportAction: OnyxEntry): boolean { +function isTransactionThread(parentReportAction: OnyxEntry | EmptyObject): boolean { return ( parentReportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU && (parentReportAction.originalMessage.type === CONST.IOU.REPORT_ACTION_TYPE.CREATE || @@ -482,8 +482,8 @@ function getLastVisibleAction(reportID: string, actionsToMerge: OnyxCollection = {}): LastVisibleMessage { - const lastVisibleAction = getLastVisibleAction(reportID, actionsToMerge); +function getLastVisibleMessage(reportID: string, actionsToMerge: OnyxCollection = {}, reportAction: OnyxEntry | undefined = undefined): LastVisibleMessage { + const lastVisibleAction = reportAction ?? getLastVisibleAction(reportID, actionsToMerge); const message = lastVisibleAction?.message?.[0]; if (message && isReportMessageAttachment(message)) { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index e3708126322f..6c5b19704af4 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -175,6 +175,7 @@ type OptimisticIOUReportAction = Pick< | 'pendingAction' | 'receipt' | 'whisperedToAccountIDs' + | 'childReportID' >; type ReportRouteParams = { @@ -1230,7 +1231,7 @@ function isMoneyRequest(reportOrID: OnyxEntry | string): boolean { /** * Checks if a report is an IOU or expense report. */ -function isMoneyRequestReport(reportOrID: OnyxEntry | string): boolean { +function isMoneyRequestReport(reportOrID: OnyxEntry | EmptyObject | string): boolean { const report = typeof reportOrID === 'object' ? reportOrID : allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null; return isIOUReport(report) || isExpenseReport(report); } @@ -2806,8 +2807,8 @@ function getReportDescriptionText(report: Report): string { return parser.htmlToText(report.description); } -function getPolicyDescriptionText(policy: Policy): string { - if (!policy.description) { +function getPolicyDescriptionText(policy: OnyxEntry): string { + if (!policy?.description) { return ''; } @@ -3160,7 +3161,6 @@ function getIOUReportActionMessage(iouReportID: string, type: string, total: num * @param [receipt] * @param [isOwnPolicyExpenseChat] - Whether this is an expense report create from the current user's policy expense chat */ - function buildOptimisticIOUReportAction( type: ValueOf, amount: number, @@ -3925,16 +3925,15 @@ function buildOptimisticTaskReport( * A helper method to create transaction thread * * @param reportAction - the parent IOU report action from which to create the thread - * - * @param moneyRequestReportID - the reportID which the report action belong to + * @param moneyRequestReport - the report which the report action belongs to */ -function buildTransactionThread(reportAction: OnyxEntry, moneyRequestReportID: string): OptimisticChatReport { +function buildTransactionThread(reportAction: OnyxEntry, moneyRequestReport: Report): OptimisticChatReport { const participantAccountIDs = [...new Set([currentUserAccountID, Number(reportAction?.actorAccountID)])].filter(Boolean) as number[]; return buildOptimisticChatReport( participantAccountIDs, getTransactionReportName(reportAction), undefined, - getReport(moneyRequestReportID)?.policyID ?? CONST.POLICY.OWNER_EMAIL_FAKE, + moneyRequestReport.policyID, CONST.POLICY.OWNER_ACCOUNT_ID_FAKE, false, '', @@ -3942,10 +3941,63 @@ function buildTransactionThread(reportAction: OnyxEntry, + amount: number, + currency: string, + comment: string, + payeeEmail: string, + participants: Participant[], + transactionID: string, + paymentType?: PaymentMethodType, + isSettlingUp = false, + isSendMoneyFlow = false, + receipt: Receipt = {}, + isOwnPolicyExpenseChat = false, +): [OptimisticCreatedReportAction, OptimisticIOUReportAction, OptimisticChatReport, OptimisticCreatedReportAction] { + const iouActionCreationTime = DateUtils.getDBTime(); + + // The `CREATED` action must be optimistically generated before the IOU action so that it won't appear after the IOU action in the chat. + const createdActionForIOUReport = buildOptimisticCreatedReportAction(payeeEmail, DateUtils.subtractMillisecondsFromDateTime(iouActionCreationTime, 1)); + const iouAction = buildOptimisticIOUReportAction( + type, + amount, + currency, + comment, + participants, + transactionID, + paymentType, + iouReport.reportID, + isSettlingUp, + isSendMoneyFlow, + receipt, + isOwnPolicyExpenseChat, + iouActionCreationTime, + ); + + // Create optimistic transactionThread and the `CREATED` action for it + const transactionThread = buildTransactionThread(iouAction, iouReport); + const createdActionForTransactionThread = buildOptimisticCreatedReportAction(payeeEmail); + + // The IOU action and the transactionThread are co-dependent as parent-child, so we need to link them together + iouAction.childReportID = transactionThread.reportID; + + return [createdActionForIOUReport, iouAction, transactionThread, createdActionForTransactionThread]; +} + function isUnread(report: OnyxEntry): boolean { if (!report) { return false; @@ -4603,7 +4655,7 @@ function getAddWorkspaceRoomOrChatReportErrors(report: OnyxEntry): Error /** * Return true if the Money Request report is marked for deletion. */ -function isMoneyRequestReportPendingDeletion(report: OnyxEntry): boolean { +function isMoneyRequestReportPendingDeletion(report: OnyxEntry | EmptyObject): boolean { if (!isMoneyRequestReport(report)) { return false; } @@ -5279,6 +5331,7 @@ export { buildOptimisticSubmittedReportAction, buildOptimisticExpenseReport, buildOptimisticIOUReportAction, + buildOptimisticMoneyRequestEntities, buildOptimisticReportPreview, buildOptimisticModifiedExpenseReportAction, buildOptimisticCancelPaymentReportAction, diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 71b3fd23a03c..40aa4c7247c6 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -357,7 +357,10 @@ function getOptionData({ } else if (lastAction?.actionName !== CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && lastActorDisplayName && lastMessageTextFromReport) { result.alternateText = `${lastActorDisplayName}: ${lastMessageText}`; } else { - result.alternateText = lastMessageTextFromReport.length > 0 ? lastMessageText : Localize.translate(preferredLocale, 'report.noActivityYet'); + result.alternateText = lastMessageTextFromReport.length > 0 ? lastMessageText : ReportActionsUtils.getLastVisibleMessage(report.reportID, {}, lastAction)?.lastMessageText; + if (!result.alternateText) { + result.alternateText = Localize.translate(preferredLocale, 'report.noActivityYet'); + } } } else { if (!lastMessageText) { diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 4b88bb7a77a8..af5c40836c74 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -914,26 +914,22 @@ function getMoneyRequestInformation( // 4. The transaction thread, which requires the iouAction, and CREATED action for the transaction thread // 5. REPORTPREVIEW action for the chatReport // Note: The CREATED action for the IOU report must be optimistically generated before the IOU action so there's no chance that it appears after the IOU action in the chat - const currentTime = DateUtils.getDBTime(); const optimisticCreatedActionForChat = ReportUtils.buildOptimisticCreatedReportAction(payeeEmail); - const optimisticCreatedActionForIOU = ReportUtils.buildOptimisticCreatedReportAction(payeeEmail, DateUtils.subtractMillisecondsFromDateTime(currentTime, 1)); - const iouAction = ReportUtils.buildOptimisticIOUReportAction( + const [optimisticCreatedActionForIOUReport, iouAction, optimisticTransactionThread, optimisticCreatedActionForTransactionThread] = ReportUtils.buildOptimisticMoneyRequestEntities( + iouReport, CONST.IOU.REPORT_ACTION_TYPE.CREATE, amount, currency, comment, + payeeEmail, [participant], optimisticTransaction.transactionID, undefined, - iouReport.reportID, false, false, receiptObject, false, - currentTime, ); - const optimisticTransactionThread = ReportUtils.buildTransactionThread(iouAction, iouReport.reportID); - const optimisticCreatedActionForTransactionThread = ReportUtils.buildOptimisticCreatedReportAction(payeeEmail); let reportPreviewAction = shouldCreateNewMoneyRequestReport ? null : ReportActionsUtils.getReportPreviewAction(chatReport.reportID, iouReport.reportID); if (reportPreviewAction) { @@ -970,7 +966,7 @@ function getMoneyRequestInformation( iouReport, optimisticTransaction, optimisticCreatedActionForChat, - optimisticCreatedActionForIOU, + optimisticCreatedActionForIOUReport, iouAction, optimisticPersonalDetailListAction, reportPreviewAction, @@ -994,7 +990,7 @@ function getMoneyRequestInformation( transaction: optimisticTransaction, iouAction, createdChatReportActionID: isNewChatReport ? optimisticCreatedActionForChat.reportActionID : '0', - createdIOUReportActionID: shouldCreateNewMoneyRequestReport ? optimisticCreatedActionForIOU.reportActionID : '0', + createdIOUReportActionID: shouldCreateNewMoneyRequestReport ? optimisticCreatedActionForIOUReport.reportActionID : '0', reportPreviewAction, transactionThreadReportID: optimisticTransactionThread.reportID, createdReportActionIDForThread: optimisticCreatedActionForTransactionThread.reportActionID, @@ -1889,25 +1885,18 @@ function createSplitsAndOnyxData( // 1. CREATED action for the chatReport // 2. CREATED action for the iouReport // 3. IOU action for the iouReport - // 4. REPORTPREVIEW action for the chatReport - // Note: The CREATED action for the IOU report must be optimistically generated before the IOU action so there's no chance that it appears after the IOU action in the chat - const currentTime = DateUtils.getDBTime(); + // 4. Transaction Thread and the CREATED action for it + // 5. REPORTPREVIEW action for the chatReport const oneOnOneCreatedActionForChat = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmailForIOUSplit); - const oneOnOneCreatedActionForIOU = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmailForIOUSplit, DateUtils.subtractMillisecondsFromDateTime(currentTime, 1)); - const oneOnOneIOUAction = ReportUtils.buildOptimisticIOUReportAction( + const [oneOnOneCreatedActionForIOU, oneOnOneIOUAction, optimisticTransactionThread, optimisticCreatedActionForTransactionThread] = ReportUtils.buildOptimisticMoneyRequestEntities( + oneOnOneIOUReport, CONST.IOU.REPORT_ACTION_TYPE.CREATE, splitAmount, currency, comment, + currentUserEmailForIOUSplit, [participant], oneOnOneTransaction.transactionID, - undefined, - oneOnOneIOUReport.reportID, - undefined, - undefined, - undefined, - undefined, - currentTime, ); // Add optimistic personal details for new participants @@ -1938,10 +1927,6 @@ function createSplitsAndOnyxData( // Add tag to optimistic policy recently used tags when a participant is a workspace const optimisticPolicyRecentlyUsedTags = isPolicyExpenseChat ? Policy.buildOptimisticPolicyRecentlyUsedTags(participant.policyID, tag) : {}; - // Create optimistic transactionThread - const optimisticTransactionThread = ReportUtils.buildTransactionThread(oneOnOneIOUAction, oneOnOneIOUReport.reportID); - const optimisticCreatedActionForTransactionThread = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmailForIOUSplit); - // STEP 5: Build Onyx Data const [oneOnOneOptimisticData, oneOnOneSuccessData, oneOnOneFailureData] = buildOnyxDataForMoneyRequest( oneOnOneChatReport, @@ -2058,7 +2043,7 @@ function splitBill( } /** - * @param amount - always in smallest currency unit + * @param amount - always in the smallest currency unit */ function splitBillAndOpenReport( participants: Participant[], @@ -2523,16 +2508,16 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA ); const oneOnOneCreatedActionForChat = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmailForIOUSplit); - const oneOnOneCreatedActionForIOU = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmailForIOUSplit); - const oneOnOneIOUAction = ReportUtils.buildOptimisticIOUReportAction( + const [oneOnOneCreatedActionForIOU, oneOnOneIOUAction, optimisticTransactionThread, optimisticCreatedActionForTransactionThread] = ReportUtils.buildOptimisticMoneyRequestEntities( + oneOnOneIOUReport, CONST.IOU.REPORT_ACTION_TYPE.CREATE, splitAmount, currency ?? '', updatedTransaction.comment.comment ?? '', + currentUserEmailForIOUSplit, [participant], oneOnOneTransaction.transactionID, undefined, - oneOnOneIOUReport?.reportID, ); let oneOnOneReportPreviewAction = ReportActionsUtils.getReportPreviewAction(oneOnOneChatReport?.reportID ?? '', oneOnOneIOUReport?.reportID ?? ''); @@ -2542,9 +2527,6 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA oneOnOneReportPreviewAction = ReportUtils.buildOptimisticReportPreview(oneOnOneChatReport, oneOnOneIOUReport, '', oneOnOneTransaction); } - const optimisticTransactionThread = ReportUtils.buildTransactionThread(oneOnOneIOUAction, oneOnOneIOUReport.reportID); - const optimisticCreatedActionForTransactionThread = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmailForIOUSplit); - const [oneOnOneOptimisticData, oneOnOneSuccessData, oneOnOneFailureData] = buildOnyxDataForMoneyRequest( oneOnOneChatReport, oneOnOneIOUReport, @@ -3252,26 +3234,23 @@ function getSendMoneyParams( value: optimisticTransaction, }; - // Note: The created action must be optimistically generated before the IOU action so there's no chance that the created action appears after the IOU action in the chat - const optimisticCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(recipientEmail); - const optimisticIOUReportAction = ReportUtils.buildOptimisticIOUReportAction( - CONST.IOU.REPORT_ACTION_TYPE.PAY, - amount, - currency, - comment, - [recipient], - optimisticTransaction.transactionID, - paymentMethodType, - optimisticIOUReport.reportID, - false, - true, - ); + const [optimisticCreatedActionForIOUReport, optimisticIOUReportAction, optimisticTransactionThread, optimisticCreatedActionForTransactionThread] = + ReportUtils.buildOptimisticMoneyRequestEntities( + optimisticIOUReport, + CONST.IOU.REPORT_ACTION_TYPE.PAY, + amount, + currency, + comment, + recipientEmail, + [recipient], + optimisticTransaction.transactionID, + paymentMethodType, + false, + true, + ); const reportPreviewAction = ReportUtils.buildOptimisticReportPreview(chatReport, optimisticIOUReport); - const optimisticTransactionThread = ReportUtils.buildTransactionThread(optimisticIOUReportAction, optimisticIOUReport.reportID); - const optimisticCreatedActionForTransactionThread = ReportUtils.buildOptimisticCreatedReportAction(recipientEmail); - // Change the method to set for new reports because it doesn't exist yet, is faster, // and we need the data to be available when we navigate to the chat page const optimisticChatReportData: OnyxUpdate = isNewChat @@ -3445,7 +3424,7 @@ function getSendMoneyParams( if (optimisticChatReportActionsData.value) { // Add an optimistic created action to the optimistic chat reportActions data - optimisticChatReportActionsData.value[optimisticCreatedAction.reportActionID] = optimisticCreatedAction; + optimisticChatReportActionsData.value[optimisticCreatedActionForIOUReport.reportActionID] = optimisticCreatedActionForIOUReport; } } else { failureData.push({ @@ -3481,7 +3460,7 @@ function getSendMoneyParams( paymentMethodType, transactionID: optimisticTransaction.transactionID, newIOUReportDetails, - createdReportActionID: isNewChat ? optimisticCreatedAction.reportActionID : '0', + createdReportActionID: isNewChat ? optimisticCreatedActionForIOUReport.reportActionID : '0', reportPreviewReportActionID: reportPreviewAction.reportActionID, transactionThreadReportID: optimisticTransactionThread.reportID, createdReportActionIDForThread: optimisticCreatedActionForTransactionThread.reportActionID, diff --git a/src/libs/actions/PaymentMethods.ts b/src/libs/actions/PaymentMethods.ts index 13e0a42e839f..dc8cefc0c30e 100644 --- a/src/libs/actions/PaymentMethods.ts +++ b/src/libs/actions/PaymentMethods.ts @@ -295,7 +295,7 @@ function saveWalletTransferMethodType(filterPaymentMethodType?: FilterMethodPaym function dismissSuccessfulTransferBalancePage() { Onyx.merge(ONYXKEYS.WALLET_TRANSFER, {shouldShowSuccess: false}); - Navigation.goBack(ROUTES.SETTINGS_WALLET); + Navigation.goBack(); } /** diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index e43a93e26f76..8bfa2a4a11fd 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -15,6 +15,13 @@ import type { DeleteMembersFromWorkspaceParams, DeleteWorkspaceAvatarParams, DeleteWorkspaceParams, + EnablePolicyCategoriesParams, + EnablePolicyConnectionsParams, + EnablePolicyDistanceRatesParams, + EnablePolicyReportFieldsParams, + EnablePolicyTagsParams, + EnablePolicyTaxesParams, + EnablePolicyWorkflowsParams, OpenDraftWorkspaceRequestParams, OpenPolicyCategoriesPageParams, OpenPolicyDistanceRatesPageParams, @@ -35,7 +42,9 @@ import type { import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; import DateUtils from '@libs/DateUtils'; import * as ErrorUtils from '@libs/ErrorUtils'; +import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import Log from '@libs/Log'; +import Navigation from '@libs/Navigation/Navigation'; import * as NumberUtils from '@libs/NumberUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as PhoneNumber from '@libs/PhoneNumber'; @@ -44,6 +53,8 @@ import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {Route} from '@src/ROUTES'; +import ROUTES from '@src/ROUTES'; import type { InvitedEmailsToAccountIDs, PersonalDetailsList, @@ -2642,6 +2653,346 @@ function clearCategoryErrors(policyID: string, categoryName: string) { }); } +function navigateWhenEnableFeature(policyID: string, featureRoute: Route) { + const isNarrowLayout = getIsNarrowLayout(); + + if (isNarrowLayout) { + Navigation.navigate(ROUTES.WORKSPACE_INITIAL.getRoute(policyID), CONST.NAVIGATION.TYPE.FORCED_UP); + + return; + } + + Navigation.navigate(featureRoute); +} + +function enablePolicyCategories(policyID: string, enabled: boolean) { + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areCategoriesEnabled: enabled, + pendingFields: { + areCategoriesEnabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + areCategoriesEnabled: null, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areCategoriesEnabled: !enabled, + pendingFields: { + areCategoriesEnabled: null, + }, + }, + }, + ], + }; + + const parameters: EnablePolicyCategoriesParams = {policyID, enabled}; + + API.write(WRITE_COMMANDS.ENABLE_POLICY_CATEGORIES, parameters, onyxData); + + if (enabled) { + navigateWhenEnableFeature(policyID, ROUTES.WORKSPACE_CATEGORIES.getRoute(policyID)); + } +} + +function enablePolicyConnections(policyID: string, enabled: boolean) { + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areConnectionsEnabled: enabled, + pendingFields: { + areConnectionsEnabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + areConnectionsEnabled: null, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areConnectionsEnabled: !enabled, + pendingFields: { + areConnectionsEnabled: null, + }, + }, + }, + ], + }; + + const parameters: EnablePolicyConnectionsParams = {policyID, enabled}; + + API.write(WRITE_COMMANDS.ENABLE_POLICY_CONNECTIONS, parameters, onyxData); +} + +function enablePolicyDistanceRates(policyID: string, enabled: boolean) { + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areDistanceRatesEnabled: enabled, + pendingFields: { + areDistanceRatesEnabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + areDistanceRatesEnabled: null, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areDistanceRatesEnabled: !enabled, + pendingFields: { + areDistanceRatesEnabled: null, + }, + }, + }, + ], + }; + + const parameters: EnablePolicyDistanceRatesParams = {policyID, enabled}; + + API.write(WRITE_COMMANDS.ENABLE_POLICY_DISTANCE_RATES, parameters, onyxData); + + if (enabled) { + navigateWhenEnableFeature(policyID, ROUTES.WORKSPACE_DISTANCE_RATES.getRoute(policyID)); + } +} + +function enablePolicyReportFields(policyID: string, enabled: boolean) { + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areReportFieldsEnabled: enabled, + pendingFields: { + areReportFieldsEnabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + areReportFieldsEnabled: null, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areReportFieldsEnabled: !enabled, + pendingFields: { + areReportFieldsEnabled: null, + }, + }, + }, + ], + }; + + const parameters: EnablePolicyReportFieldsParams = {policyID, enabled}; + + API.write(WRITE_COMMANDS.ENABLE_POLICY_REPORT_FIELDS, parameters, onyxData); +} + +function enablePolicyTags(policyID: string, enabled: boolean) { + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areTagsEnabled: enabled, + pendingFields: { + areTagsEnabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + areTagsEnabled: null, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areTagsEnabled: !enabled, + pendingFields: { + areTagsEnabled: null, + }, + }, + }, + ], + }; + + const parameters: EnablePolicyTagsParams = {policyID, enabled}; + + API.write(WRITE_COMMANDS.ENABLE_POLICY_TAGS, parameters, onyxData); + + if (enabled) { + navigateWhenEnableFeature(policyID, ROUTES.WORKSPACE_TAGS.getRoute(policyID)); + } +} + +function enablePolicyTaxes(policyID: string, enabled: boolean) { + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + tax: { + trackingEnabled: enabled, + }, + pendingFields: { + tax: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + tax: null, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + tax: { + trackingEnabled: !enabled, + }, + pendingFields: { + tax: null, + }, + }, + }, + ], + }; + + const parameters: EnablePolicyTaxesParams = {policyID, enabled}; + + API.write(WRITE_COMMANDS.ENABLE_POLICY_TAXES, parameters, onyxData); +} + +function enablePolicyWorkflows(policyID: string, enabled: boolean) { + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areWorkflowsEnabled: enabled, + pendingFields: { + areWorkflowsEnabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingFields: { + areWorkflowsEnabled: null, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + areWorkflowsEnabled: !enabled, + pendingFields: { + areWorkflowsEnabled: null, + }, + }, + }, + ], + }; + + const parameters: EnablePolicyWorkflowsParams = {policyID, enabled}; + + API.write(WRITE_COMMANDS.ENABLE_POLICY_WORKFLOWS, parameters, onyxData); + + if (enabled) { + navigateWhenEnableFeature(policyID, ROUTES.WORKSPACE_WORKFLOWS.getRoute(policyID)); + } +} + /** * Accept user join request to a workspace */ @@ -2823,5 +3174,12 @@ export { declineJoinRequest, createPolicyCategory, clearCategoryErrors, + enablePolicyCategories, + enablePolicyConnections, + enablePolicyDistanceRates, + enablePolicyReportFields, + enablePolicyTags, + enablePolicyTaxes, + enablePolicyWorkflows, openPolicyDistanceRatesPage, }; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index f3cbabcd453c..b677b5369b68 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -447,7 +447,6 @@ function addActions(reportID: string, text = '', file?: FileObject) { commentReportActionID: file && reportCommentAction ? reportCommentAction.reportActionID : null, reportComment: reportCommentText, file, - shouldAllowActionableMentionWhispers: true, clientCreatedTime: file ? attachmentAction?.created : reportCommentAction?.created, }; @@ -2904,17 +2903,6 @@ function clearNewRoomFormError() { }); } -function getReportDraftStatus(reportID: string) { - if (!allReports) { - return false; - } - - if (!allReports[reportID]) { - return false; - } - return allReports[reportID]?.hasDraft; -} - function resolveActionableMentionWhisper(reportId: string, reportAction: OnyxEntry, resolution: ValueOf) { const message = reportAction?.message?.[0]; if (!message) { @@ -2965,7 +2953,6 @@ function resolveActionableMentionWhisper(reportId: string, reportAction: OnyxEnt } export { - getReportDraftStatus, searchInServer, addComment, addAttachment, diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 07bc7f3ed418..619281ac7ecf 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -253,7 +253,7 @@ function signOutAndRedirectToSignIn(shouldReplaceCurrentScreen?: boolean, should * @returns same callback if the action is allowed, otherwise a function that signs out and redirects to sign in */ // eslint-disable-next-line @typescript-eslint/no-explicit-any -function checkIfActionIsAllowed any>(callback: TCallback, isAnonymousAction = false): TCallback | (() => void) { +function checkIfActionIsAllowed any) | void>(callback: TCallback, isAnonymousAction = false): TCallback | (() => void) { if (isAnonymousUser() && !isAnonymousAction) { return () => signOutAndRedirectToSignIn(); } diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 27c7f3e36fd4..3e00d8084825 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -100,7 +100,7 @@ function createTaskAndNavigate( assigneeEmail: string, assigneeAccountID = 0, assigneeChatReport: OnyxEntry = null, - policyID = CONST.POLICY.OWNER_EMAIL_FAKE, + policyID: string = CONST.POLICY.OWNER_EMAIL_FAKE, ) { const optimisticTaskReport = ReportUtils.buildOptimisticTaskReport(currentUserAccountID, assigneeAccountID, parentReportID, title, description, policyID); @@ -262,6 +262,7 @@ function createTaskAndNavigate( API.write(WRITE_COMMANDS.CREATE_TASK, parameters, {optimisticData, successData, failureData}); Navigation.dismissModal(parentReportID); + Report.notifyNewAction(parentReportID, currentUserAccountID); } /** diff --git a/src/pages/ReimbursementAccount/BankInfo/substeps/Confirmation.tsx b/src/pages/ReimbursementAccount/BankInfo/substeps/Confirmation.tsx index af4b251952de..44f83f55a23f 100644 --- a/src/pages/ReimbursementAccount/BankInfo/substeps/Confirmation.tsx +++ b/src/pages/ReimbursementAccount/BankInfo/substeps/Confirmation.tsx @@ -5,6 +5,7 @@ import {withOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import DotIndicatorMessage from '@components/DotIndicatorMessage'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import SafeAreaConsumer from '@components/SafeAreaConsumer'; import ScrollView from '@components/ScrollView'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; @@ -48,55 +49,59 @@ function Confirmation({reimbursementAccount, reimbursementAccountDraft, onNext, }; return ( - - {translate('bankAccount.letsDoubleCheck')} - {translate('bankAccount.thisBankAccount')} - {setupType === CONST.BANK_ACCOUNT.SUBSTEP.MANUAL && ( - - + + {({safeAreaPaddingBottomStyle}) => ( + + {translate('bankAccount.letsDoubleCheck')} + {translate('bankAccount.thisBankAccount')} + {setupType === CONST.BANK_ACCOUNT.SUBSTEP.MANUAL && ( + + - - + + + )} + {setupType === CONST.BANK_ACCOUNT.SUBSTEP.PLAID && ( + + )} + + {error && error.length > 0 && ( + + )} +