From 595a1b0a6809dceabe4f6942c142db8de731c6e3 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 5 Dec 2023 12:08:53 +0100 Subject: [PATCH 001/660] add required changes to run hybrid app --- package-lock.json | 14 ++-- package.json | 2 +- ...react-native-image-manipulator+1.0.5.patch | 4 +- patches/@onfido+react-native-sdk+8.3.0.patch | 46 ++++++++++ .../@react-native+gradle-plugin+0.72.11.patch | 83 +++++++++++++++++++ ...-async-storage+async-storage+1.17.11.patch | 13 +++ ...native-camera-roll+camera-roll+5.4.0.patch | 15 ++++ ...ommunity+cli-platform-android+11.3.6.patch | 57 +++++++++++++ ...ve-community+cli-platform-ios+11.3.6.patch | 51 ++++++++++++ ...act-native-firebase+analytics+12.9.3.patch | 18 ++++ .../@react-native-firebase+app+12.9.3.patch | 25 ++++++ ...t-native-firebase+crashlytics+12.9.3.patch | 17 ++++ .../@react-native-firebase+perf+12.9.3.patch | 17 ++++ patches/react-native-reanimated+3.5.4.patch | 17 ++++ src/App.js | 77 ++++++++++------- src/InitialUrlContext.ts | 5 ++ src/libs/Navigation/AppNavigator/index.js | 17 +++- src/pages/LogOutPreviousUserPage.js | 14 ++-- 18 files changed, 445 insertions(+), 47 deletions(-) create mode 100644 patches/@react-native+gradle-plugin+0.72.11.patch create mode 100644 patches/@react-native-async-storage+async-storage+1.17.11.patch create mode 100644 patches/@react-native-camera-roll+camera-roll+5.4.0.patch create mode 100644 patches/@react-native-community+cli-platform-android+11.3.6.patch create mode 100644 patches/@react-native-community+cli-platform-ios+11.3.6.patch create mode 100644 patches/@react-native-firebase+analytics+12.9.3.patch create mode 100644 patches/@react-native-firebase+app+12.9.3.patch create mode 100644 patches/@react-native-firebase+crashlytics+12.9.3.patch create mode 100644 patches/@react-native-firebase+perf+12.9.3.patch create mode 100644 patches/react-native-reanimated+3.5.4.patch create mode 100644 src/InitialUrlContext.ts diff --git a/package-lock.json b/package-lock.json index 1d6333ad719e..cccdd27c6bcd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -108,7 +108,7 @@ "react-native-tab-view": "^3.5.2", "react-native-url-polyfill": "^2.0.0", "react-native-view-shot": "^3.6.0", - "react-native-vision-camera": "^2.16.2", + "react-native-vision-camera": "2.16.5", "react-native-web": "^0.19.9", "react-native-web-linear-gradient": "^1.1.2", "react-native-webview": "^11.17.2", @@ -44717,9 +44717,9 @@ } }, "node_modules/react-native-vision-camera": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/react-native-vision-camera/-/react-native-vision-camera-2.16.2.tgz", - "integrity": "sha512-QIpG33l3QB0AkTfX/ccRknwNRu1APNUkokVKF1lpRO2+tBnkXnGL0UapgXg5u9KIONZtrpupeDeO+J5B2TeQVw==", + "version": "2.16.5", + "resolved": "https://registry.npmjs.org/react-native-vision-camera/-/react-native-vision-camera-2.16.5.tgz", + "integrity": "sha512-MzXhNd597OyMQSEGhqWI4DufWkdr7PR7U9B30E3gXnln7cnvjMVIp4j3eIW9BIrgvEyUcEeL7nZM5NLhTmO/fA==", "peerDependencies": { "react": "*", "react-native": "*" @@ -84992,9 +84992,9 @@ "requires": {} }, "react-native-vision-camera": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/react-native-vision-camera/-/react-native-vision-camera-2.16.2.tgz", - "integrity": "sha512-QIpG33l3QB0AkTfX/ccRknwNRu1APNUkokVKF1lpRO2+tBnkXnGL0UapgXg5u9KIONZtrpupeDeO+J5B2TeQVw==", + "version": "2.16.5", + "resolved": "https://registry.npmjs.org/react-native-vision-camera/-/react-native-vision-camera-2.16.5.tgz", + "integrity": "sha512-MzXhNd597OyMQSEGhqWI4DufWkdr7PR7U9B30E3gXnln7cnvjMVIp4j3eIW9BIrgvEyUcEeL7nZM5NLhTmO/fA==", "requires": {} }, "react-native-web": { diff --git a/package.json b/package.json index 1453e85fef53..79a74d05a281 100644 --- a/package.json +++ b/package.json @@ -155,7 +155,7 @@ "react-native-tab-view": "^3.5.2", "react-native-url-polyfill": "^2.0.0", "react-native-view-shot": "^3.6.0", - "react-native-vision-camera": "^2.16.2", + "react-native-vision-camera": "2.16.5", "react-native-web": "^0.19.9", "react-native-web-linear-gradient": "^1.1.2", "react-native-webview": "^11.17.2", diff --git a/patches/@oguzhnatly+react-native-image-manipulator+1.0.5.patch b/patches/@oguzhnatly+react-native-image-manipulator+1.0.5.patch index c613a47a3072..eb5ef2da6247 100644 --- a/patches/@oguzhnatly+react-native-image-manipulator+1.0.5.patch +++ b/patches/@oguzhnatly+react-native-image-manipulator+1.0.5.patch @@ -7,13 +7,13 @@ index 3a1a548..fe030bb 100644 android { - compileSdkVersion 28 -+ compileSdkVersion 30 ++ compileSdkVersion 33 buildToolsVersion "28.0.3" defaultConfig { minSdkVersion 16 - targetSdkVersion 28 -+ targetSdkVersion 30 ++ targetSdkVersion 33 versionCode 1 versionName "1.0" } diff --git a/patches/@onfido+react-native-sdk+8.3.0.patch b/patches/@onfido+react-native-sdk+8.3.0.patch index 12245cb58355..940c40eb9bb0 100644 --- a/patches/@onfido+react-native-sdk+8.3.0.patch +++ b/patches/@onfido+react-native-sdk+8.3.0.patch @@ -15,3 +15,49 @@ index b4c7106..d5083d3 100644 } classDirectories.setFrom(fileTree( dir: 'build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/onfido/reactnative/sdk', +diff --git a/node_modules/@onfido/react-native-sdk/android/publish.gradle b/node_modules/@onfido/react-native-sdk/android/publish.gradle +index 216d52d..1f1c17f 100644 +--- a/node_modules/@onfido/react-native-sdk/android/publish.gradle ++++ b/node_modules/@onfido/react-native-sdk/android/publish.gradle +@@ -4,41 +4,5 @@ afterEvaluate { + archiveClassifier = "sources" + } + +- publishing { +- def packageJson = new groovy.json.JsonSlurper().parseText(file('../package.json').text) + +- publications { +- mavenJava(MavenPublication) { +- // The new gradle publish plugin doesn't allow the @onfido/ prefix +- artifactId = packageJson.name.replace("@onfido/", "onfido-") +- groupId = 'com.onfido.reactnative.sdk' +- version = packageJson.version +- +- from components.release +- +- artifact sourceJar +- +- pom { +- name = packageJson.title +- description = packageJson.description +- url = packageJson.repository.baseUrl +- +- licenses { +- license { +- name = packageJson.license +- url = packageJson.repository.baseUrl + '/blob/master/' + packageJson.licenseFilename +- distribution = 'repo' +- } +- } +- +- developers { +- developer { +- id = packageJson.author.email +- name = packageJson.author.name +- } +- } +- } +- } +- } +- } + } diff --git a/patches/@react-native+gradle-plugin+0.72.11.patch b/patches/@react-native+gradle-plugin+0.72.11.patch new file mode 100644 index 000000000000..8609c992b4d6 --- /dev/null +++ b/patches/@react-native+gradle-plugin+0.72.11.patch @@ -0,0 +1,83 @@ +diff --git a/node_modules/@react-native/gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt b/node_modules/@react-native/gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt +index 062dcc7..cb400ec 100644 +--- a/node_modules/@react-native/gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt ++++ b/node_modules/@react-native/gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt +@@ -15,6 +15,7 @@ import com.facebook.react.tasks.GenerateCodegenArtifactsTask + import com.facebook.react.tasks.GenerateCodegenSchemaTask + import com.facebook.react.utils.AgpConfiguratorUtils.configureBuildConfigFields + import com.facebook.react.utils.AgpConfiguratorUtils.configureDevPorts ++import com.facebook.react.utils.AgpConfiguratorUtils.configureNamespaceForLibraries + import com.facebook.react.utils.BackwardCompatUtils.configureBackwardCompatibilityReactMap + import com.facebook.react.utils.DependencyUtils.configureDependencies + import com.facebook.react.utils.DependencyUtils.configureRepositories +@@ -64,6 +65,7 @@ class ReactPlugin : Plugin { + + configureReactNativeNdk(project, extension) + configureBuildConfigFields(project) ++ configureNamespaceForLibraries(project) + configureDevPorts(project) + configureBackwardCompatibilityReactMap(project) + +diff --git a/node_modules/@react-native/gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt b/node_modules/@react-native/gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt +index 3bf0085..59397b4 100644 +--- a/node_modules/@react-native/gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt ++++ b/node_modules/@react-native/gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt +@@ -8,11 +8,17 @@ + package com.facebook.react.utils + + import com.android.build.api.variant.AndroidComponentsExtension ++import com.facebook.react.ReactExtension + import com.facebook.react.utils.ProjectUtils.isHermesEnabled + import com.facebook.react.utils.ProjectUtils.isNewArchEnabled + import org.gradle.api.Action + import org.gradle.api.Project + import org.gradle.api.plugins.AppliedPlugin ++import java.io.File ++import com.android.build.gradle.LibraryExtension ++import javax.xml.parsers.DocumentBuilder ++import javax.xml.parsers.DocumentBuilderFactory ++import org.w3c.dom.Element + + @Suppress("UnstableApiUsage") + internal object AgpConfiguratorUtils { +@@ -49,6 +55,40 @@ internal object AgpConfiguratorUtils { + project.pluginManager.withPlugin("com.android.application", action) + project.pluginManager.withPlugin("com.android.library", action) + } ++ ++ fun configureNamespaceForLibraries(appProject: Project) { ++ appProject.rootProject.allprojects { subproject -> ++ subproject.pluginManager.withPlugin("com.android.library") { ++ subproject.extensions.getByType(AndroidComponentsExtension::class.java).finalizeDsl { ext -> ++ if(ext.namespace == null){ ++ val android = subproject.extensions.getByType(LibraryExtension::class.java) ++ val manifestFile = android.sourceSets.getByName("main").manifest.srcFile ++ ++ manifestFile.takeIf { it.exists() }?.let { file -> ++ getPackageNameFromManifest(file)?.let { packageName -> ++ ext.namespace = packageName ++ } ++ } ++ } ++ } ++ } ++ } ++ } ++ fun getPackageNameFromManifest(manifest: File): String? { ++ val factory: DocumentBuilderFactory = DocumentBuilderFactory.newInstance() ++ val builder: DocumentBuilder = factory.newDocumentBuilder() ++ ++ try { ++ val xmlDocument = builder.parse(manifest) ++ ++ val manifestElement = xmlDocument.getElementsByTagName("manifest").item(0) as? Element ++ val packageName = manifestElement?.getAttribute("package") ++ ++ return if (packageName.isNullOrEmpty()) null else packageName ++ } catch (e: Exception) { ++ return null ++ } ++ } + } + + const val DEFAULT_DEV_SERVER_PORT = "8081" diff --git a/patches/@react-native-async-storage+async-storage+1.17.11.patch b/patches/@react-native-async-storage+async-storage+1.17.11.patch new file mode 100644 index 000000000000..1c8a139bc10f --- /dev/null +++ b/patches/@react-native-async-storage+async-storage+1.17.11.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/@react-native-async-storage/async-storage/android/build.gradle b/node_modules/@react-native-async-storage/async-storage/android/build.gradle +index ff1baa4..32d8020 100644 +--- a/node_modules/@react-native-async-storage/async-storage/android/build.gradle ++++ b/node_modules/@react-native-async-storage/async-storage/android/build.gradle +@@ -112,7 +112,7 @@ repositories { + dependencies { + + if (useNextStorage) { +- def room_version = getVersionOrDefault('AsyncStorage_next_roomVersion', '2.4.2') ++ def room_version = getVersionOrDefault('AsyncStorage_next_roomVersion', '2.4.3') + def coroutines_version = "1.6.0" + def coroutinesTest_version = "1.6.0" + // if we don't provide explicit dependency on reflection, kotlin plugin \ No newline at end of file diff --git a/patches/@react-native-camera-roll+camera-roll+5.4.0.patch b/patches/@react-native-camera-roll+camera-roll+5.4.0.patch new file mode 100644 index 000000000000..f0429bc10125 --- /dev/null +++ b/patches/@react-native-camera-roll+camera-roll+5.4.0.patch @@ -0,0 +1,15 @@ +diff --git a/node_modules/@react-native-camera-roll/camera-roll/android/build.gradle b/node_modules/@react-native-camera-roll/camera-roll/android/build.gradle +index 3f76132..63dc946 100644 +--- a/node_modules/@react-native-camera-roll/camera-roll/android/build.gradle ++++ b/node_modules/@react-native-camera-roll/camera-roll/android/build.gradle +@@ -81,7 +81,9 @@ def findNodeModulePath(baseDir, packageName) { + } + + def resolveReactNativeDirectory() { +- def reactNative = file("${findNodeModulePath(rootProject.projectDir, "react-native")}") ++ def projectDir = this.hasProperty('reactNativeProject') ? this.reactNativeProject : rootProject.projectDir ++ def modulePath = file(projectDir); ++ def reactNative = file("${findNodeModulePath(modulePath, 'react-native')}") + if (reactNative.exists()) { + return reactNative + } diff --git a/patches/@react-native-community+cli-platform-android+11.3.6.patch b/patches/@react-native-community+cli-platform-android+11.3.6.patch new file mode 100644 index 000000000000..ddebb53db36d --- /dev/null +++ b/patches/@react-native-community+cli-platform-android+11.3.6.patch @@ -0,0 +1,57 @@ +diff --git a/node_modules/@react-native-community/cli-platform-android/native_modules.gradle b/node_modules/@react-native-community/cli-platform-android/native_modules.gradle +index 8a218d5..8deb9c0 100644 +--- a/node_modules/@react-native-community/cli-platform-android/native_modules.gradle ++++ b/node_modules/@react-native-community/cli-platform-android/native_modules.gradle +@@ -140,6 +140,7 @@ class ReactNativeModules { + private Logger logger + private String packageName + private File root ++ private File rnRoot + private ArrayList> reactNativeModules + private ArrayList unstable_reactLegacyComponentNames + private HashMap reactNativeModulesBuildVariants +@@ -147,8 +148,13 @@ class ReactNativeModules { + private static String LOG_PREFIX = ":ReactNative:" + + ReactNativeModules(Logger logger, File root) { ++ this(logger, root, root) ++ } ++ ++ ReactNativeModules(Logger logger, File root, File rnRoot) { + this.logger = logger + this.root = root ++ this.rnRoot = rnRoot + + def (nativeModules, reactNativeModulesBuildVariants, androidProject) = this.getReactNativeConfig() + this.reactNativeModules = nativeModules +@@ -413,11 +419,12 @@ class ReactNativeModules { + * @todo: `fastlane` has been reported to not work too. + */ + def cliResolveScript = "try {console.log(require('@react-native-community/cli').bin);} catch (e) {console.log(require('react-native/cli').bin);}" ++ + String[] nodeCommand = ["node", "-e", cliResolveScript] +- def cliPath = this.getCommandOutput(nodeCommand, this.root) ++ def cliPath = this.getCommandOutput(nodeCommand, this.rnRoot) + + String[] reactNativeConfigCommand = ["node", cliPath, "config"] +- def reactNativeConfigOutput = this.getCommandOutput(reactNativeConfigCommand, this.root) ++ def reactNativeConfigOutput = this.getCommandOutput(reactNativeConfigCommand, this.rnRoot) + + def json + try { +@@ -482,8 +489,14 @@ class ReactNativeModules { + * where build files of an active project are located. + */ + def projectRoot = rootProject.projectDir ++def autoModules ++ ++if(this.hasProperty('reactNativeProject')){ ++ autoModules = new ReactNativeModules(logger, projectRoot, new File(projectRoot, reactNativeProject)) ++} else { ++ autoModules = new ReactNativeModules(logger, projectRoot) ++} + +-def autoModules = new ReactNativeModules(logger, projectRoot) + + /** ----------------------- + * Exported Extensions diff --git a/patches/@react-native-community+cli-platform-ios+11.3.6.patch b/patches/@react-native-community+cli-platform-ios+11.3.6.patch new file mode 100644 index 000000000000..9c97b8666ef2 --- /dev/null +++ b/patches/@react-native-community+cli-platform-ios+11.3.6.patch @@ -0,0 +1,51 @@ +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 1b6eece..914039e 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 @@ + require 'pathname' + require 'cocoapods' + +-def use_native_modules!(config = nil) ++def updateConfig(config = nil) + if (config.is_a? String) + Pod::UI.warn("Passing custom root to use_native_modules! is deprecated.", + [ +@@ -24,7 +24,6 @@ def use_native_modules!(config = nil) + # Resolving the path the RN CLI. The `@react-native-community/cli` module may not be there for certain package managers, so we fall back to resolving it through `react-native` package, that's always present in RN projects + cli_resolve_script = "try {console.log(require('@react-native-community/cli').bin);} catch (e) {console.log(require('react-native/cli').bin);}" + cli_bin = Pod::Executable.execute_command("node", ["-e", cli_resolve_script], true).strip +- + if (!config) + json = [] + +@@ -36,10 +35,29 @@ def use_native_modules!(config = nil) + + config = JSON.parse(json.join("\n")) + end ++end ++ ++def use_native_modules!(config = nil) ++ if (ENV['REACT_NATIVE_DIR']) ++ Dir.chdir(ENV['REACT_NATIVE_DIR']) do ++ config = updateConfig(config) ++ end ++ else ++ config = updateConfig(config) ++ end + + project_root = Pathname.new(config["project"]["ios"]["sourceDir"]) ++ if(ENV["PROJECT_ROOT_DIR"]) ++ project_root = File.join(Dir.pwd, ENV["PROJECT_ROOT_DIR"]) ++ ++ end + + packages = config["dependencies"] ++ ++ if (ENV["NO_FLIPPER"]) ++ packages["react-native-flipper"]["platforms"]["ios"] = nil ++ end ++ + found_pods = [] + + packages.each do |package_name, package| diff --git a/patches/@react-native-firebase+analytics+12.9.3.patch b/patches/@react-native-firebase+analytics+12.9.3.patch new file mode 100644 index 000000000000..74d3e2a8005a --- /dev/null +++ b/patches/@react-native-firebase+analytics+12.9.3.patch @@ -0,0 +1,18 @@ +diff --git a/node_modules/@react-native-firebase/analytics/android/build.gradle b/node_modules/@react-native-firebase/analytics/android/build.gradle +index d223ebf..821b730 100644 +--- a/node_modules/@react-native-firebase/analytics/android/build.gradle ++++ b/node_modules/@react-native-firebase/analytics/android/build.gradle +@@ -45,6 +45,8 @@ if (coreVersionDetected != coreVersionRequired) { + } + } + ++apply plugin: 'com.android.library' ++ + project.ext { + set('react-native', [ + versions: [ +@@ -144,4 +146,3 @@ dependencies { + ReactNative.shared.applyPackageVersion() + ReactNative.shared.applyDefaultExcludes() + ReactNative.module.applyAndroidVersions() +-ReactNative.module.applyReactNativeDependency("api") diff --git a/patches/@react-native-firebase+app+12.9.3.patch b/patches/@react-native-firebase+app+12.9.3.patch new file mode 100644 index 000000000000..312fdacf4432 --- /dev/null +++ b/patches/@react-native-firebase+app+12.9.3.patch @@ -0,0 +1,25 @@ +diff --git a/node_modules/@react-native-firebase/app/android/build.gradle b/node_modules/@react-native-firebase/app/android/build.gradle +index 05f629a..7c36693 100644 +--- a/node_modules/@react-native-firebase/app/android/build.gradle ++++ b/node_modules/@react-native-firebase/app/android/build.gradle +@@ -18,6 +18,7 @@ buildscript { + + plugins { + id "io.invertase.gradle.build" version "1.5" ++ id 'com.android.library' + } + + def packageJson = PackageJson.getForProject(project) +@@ -91,6 +92,7 @@ repositories { + } + + dependencies { ++ api 'com.facebook.react:react-native:+' + implementation platform("com.google.firebase:firebase-bom:${ReactNative.ext.getVersion("firebase", "bom")}") + implementation "com.google.firebase:firebase-common" + implementation "com.google.android.gms:play-services-auth:${ReactNative.ext.getVersion("play", "play-services-auth")}" +@@ -99,4 +101,3 @@ dependencies { + ReactNative.shared.applyPackageVersion() + ReactNative.shared.applyDefaultExcludes() + ReactNative.module.applyAndroidVersions() +-ReactNative.module.applyReactNativeDependency("api") diff --git a/patches/@react-native-firebase+crashlytics+12.9.3.patch b/patches/@react-native-firebase+crashlytics+12.9.3.patch new file mode 100644 index 000000000000..560f462731dc --- /dev/null +++ b/patches/@react-native-firebase+crashlytics+12.9.3.patch @@ -0,0 +1,17 @@ +diff --git a/node_modules/@react-native-firebase/crashlytics/android/build.gradle b/node_modules/@react-native-firebase/crashlytics/android/build.gradle +index 6b6de57..9b89ae7 100644 +--- a/node_modules/@react-native-firebase/crashlytics/android/build.gradle ++++ b/node_modules/@react-native-firebase/crashlytics/android/build.gradle +@@ -18,6 +18,7 @@ buildscript { + + plugins { + id "io.invertase.gradle.build" version "1.5" ++ id 'com.android.library' + } + + def appProject +@@ -92,4 +93,3 @@ dependencies { + ReactNative.shared.applyPackageVersion() + ReactNative.shared.applyDefaultExcludes() + ReactNative.module.applyAndroidVersions() +-ReactNative.module.applyReactNativeDependency("api") diff --git a/patches/@react-native-firebase+perf+12.9.3.patch b/patches/@react-native-firebase+perf+12.9.3.patch new file mode 100644 index 000000000000..7d8a9f4f23b5 --- /dev/null +++ b/patches/@react-native-firebase+perf+12.9.3.patch @@ -0,0 +1,17 @@ +diff --git a/node_modules/@react-native-firebase/perf/android/build.gradle b/node_modules/@react-native-firebase/perf/android/build.gradle +index b4a9c7b..5835e3d 100644 +--- a/node_modules/@react-native-firebase/perf/android/build.gradle ++++ b/node_modules/@react-native-firebase/perf/android/build.gradle +@@ -19,6 +19,7 @@ buildscript { + + plugins { + id "io.invertase.gradle.build" version "1.5" ++ id 'com.android.library' + } + + def appProject +@@ -129,4 +130,3 @@ dependencies { + ReactNative.shared.applyPackageVersion() + ReactNative.shared.applyDefaultExcludes() + ReactNative.module.applyAndroidVersions() +-ReactNative.module.applyReactNativeDependency("api") diff --git a/patches/react-native-reanimated+3.5.4.patch b/patches/react-native-reanimated+3.5.4.patch new file mode 100644 index 000000000000..3b40360d5860 --- /dev/null +++ b/patches/react-native-reanimated+3.5.4.patch @@ -0,0 +1,17 @@ +diff --git a/node_modules/react-native-reanimated/scripts/reanimated_utils.rb b/node_modules/react-native-reanimated/scripts/reanimated_utils.rb +index af0935f..ccd2a9e 100644 +--- a/node_modules/react-native-reanimated/scripts/reanimated_utils.rb ++++ b/node_modules/react-native-reanimated/scripts/reanimated_utils.rb +@@ -17,7 +17,11 @@ def find_config() + :react_native_common_dir => nil, + } + +- react_native_node_modules_dir = File.join(File.dirname(`cd "#{Pod::Config.instance.installation_root.to_s}" && node --print "require.resolve('react-native/package.json')"`), '..') ++ root_project = Pod::Config.instance.installation_root.to_s ++ if(ENV['PROJECT_ROOT_DIR']) ++ root_project = ENV['PROJECT_ROOT_DIR'] ++ end ++ react_native_node_modules_dir = File.join(File.dirname(`cd "#{root_project}" && node --print "require.resolve('react-native/package.json')"`), '..') + react_native_json = try_to_parse_react_native_package_json(react_native_node_modules_dir) + + if react_native_json == nil diff --git a/src/App.js b/src/App.js index 2caa6b9ffc29..6479e05e13da 100644 --- a/src/App.js +++ b/src/App.js @@ -1,4 +1,5 @@ import {PortalProvider} from '@gorhom/portal'; +import PropTypes from 'prop-types'; import React from 'react'; import {LogBox} from 'react-native'; import {GestureHandlerRootView} from 'react-native-gesture-handler'; @@ -22,6 +23,7 @@ import {KeyboardStateProvider} from './components/withKeyboardState'; import {WindowDimensionsProvider} from './components/withWindowDimensions'; import Expensify from './Expensify'; import useDefaultDragAndDrop from './hooks/useDefaultDragAndDrop'; +import InitialUrlContext from './InitialUrlContext'; import OnyxUpdateManager from './libs/actions/OnyxUpdateManager'; import * as Session from './libs/actions/Session'; import * as Environment from './libs/Environment/Environment'; @@ -30,6 +32,15 @@ import ThemeIllustrationsProvider from './styles/illustrations/ThemeIllustration import ThemeProvider from './styles/themes/ThemeProvider'; import ThemeStylesProvider from './styles/ThemeStylesProvider'; +const propTypes = { + /** If we have an authToken this is true */ + url: PropTypes.string, +}; + +const defaultProps = { + url: null, +}; + // For easier debugging and development, when we are in web we expose Onyx to the window, so you can more easily set data into Onyx if (window && Environment.isDevelopment()) { window.Onyx = Onyx; @@ -45,43 +56,47 @@ LogBox.ignoreLogs([ const fill = {flex: 1}; -function App() { +function App(props) { useDefaultDragAndDrop(); OnyxUpdateManager(); return ( - - - - - - - - - - + + + + + + + + + + + + ); } +App.propTypes = propTypes; +App.defaultProps = defaultProps; App.displayName = 'App'; export default App; diff --git a/src/InitialUrlContext.ts b/src/InitialUrlContext.ts new file mode 100644 index 000000000000..eabf099dafe0 --- /dev/null +++ b/src/InitialUrlContext.ts @@ -0,0 +1,5 @@ +import {createContext} from 'react'; + +const InitialUrlContext = createContext<{initialUrl: string | null}>({initialUrl: null}); + +export default InitialUrlContext; diff --git a/src/libs/Navigation/AppNavigator/index.js b/src/libs/Navigation/AppNavigator/index.js index 0d03badf37bc..0a39a0bafeb9 100644 --- a/src/libs/Navigation/AppNavigator/index.js +++ b/src/libs/Navigation/AppNavigator/index.js @@ -1,5 +1,8 @@ import PropTypes from 'prop-types'; -import React from 'react'; +import React, {useContext, useEffect} from 'react'; +import {NativeModules} from 'react-native'; +import Navigation from '@libs/Navigation/Navigation'; +import InitialUrlContext from '@src/InitialUrlContext'; const propTypes = { /** If we have an authToken this is true */ @@ -7,6 +10,18 @@ const propTypes = { }; function AppNavigator(props) { + const initUrl = useContext(InitialUrlContext); + + useEffect(() => { + if (!NativeModules.ReactNativeModule) { + return; + } + + Navigation.isNavigationReady().then(() => { + Navigation.navigate(initUrl); + }); + }, [initUrl]); + if (props.authenticated) { const AuthScreens = require('./AuthScreens').default; diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index df38c28e561a..81fe480ffcce 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -1,12 +1,14 @@ import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; -import React, {useEffect} from 'react'; -import {Linking} from 'react-native'; +import React, {useContext, useEffect} from 'react'; +import {Linking, NativeModules} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import * as SessionUtils from '@libs/SessionUtils'; import Navigation from '@navigation/Navigation'; import * as Session from '@userActions/Session'; +import CONST from '@src/CONST'; +import InitialUrlContext from '@src/InitialUrlContext'; import ONYXKEYS from '@src/ONYXKEYS'; const propTypes = { @@ -33,10 +35,12 @@ const defaultProps = { }; function LogOutPreviousUserPage(props) { + const initUrl = useContext(InitialUrlContext); useEffect(() => { - Linking.getInitialURL().then((transitionURL) => { + Linking.getInitialURL().then((url) => { const sessionEmail = props.session.email; - const isLoggingInAsNewUser = SessionUtils.isLoggingInAsNewUser(transitionURL, sessionEmail); + const transitionUrl = NativeModules.ReactNativeModule ? CONST.DEEPLINK_BASE_URL + initUrl : url; + const isLoggingInAsNewUser = SessionUtils.isLoggingInAsNewUser(transitionUrl, sessionEmail); if (isLoggingInAsNewUser) { Session.signOutAndRedirectToSignIn(); @@ -60,7 +64,7 @@ function LogOutPreviousUserPage(props) { }); } }); - }, [props]); + }, [initUrl, props]); return ; } From cd14eca4326575f36d5b6fb39649fc7f5eae3e61 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 5 Dec 2023 12:28:15 +0100 Subject: [PATCH 002/660] update comment for appjs prop --- src/App.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.js b/src/App.js index c12f2654b5c9..b88d9b564864 100644 --- a/src/App.js +++ b/src/App.js @@ -33,7 +33,7 @@ import ThemeProvider from './styles/themes/ThemeProvider'; import ThemeStylesProvider from './styles/ThemeStylesProvider'; const propTypes = { - /** If we have an authToken this is true */ + /** Initial url that may be passed as deeplink from Hybrid App */ url: PropTypes.string, }; From a818a6bf212b910869627d85222c537476cada25 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 5 Dec 2023 12:34:13 +0100 Subject: [PATCH 003/660] move InitialUrlContext --- src/App.js | 3 ++- src/{InitialUrlContext.ts => libs/InitialUrlContext/index.ts} | 0 src/libs/Navigation/AppNavigator/index.js | 2 +- src/pages/LogOutPreviousUserPage.js | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) rename src/{InitialUrlContext.ts => libs/InitialUrlContext/index.ts} (100%) diff --git a/src/App.js b/src/App.js index b88d9b564864..19e9d45a530c 100644 --- a/src/App.js +++ b/src/App.js @@ -23,7 +23,7 @@ import {KeyboardStateProvider} from './components/withKeyboardState'; import {WindowDimensionsProvider} from './components/withWindowDimensions'; import Expensify from './Expensify'; import useDefaultDragAndDrop from './hooks/useDefaultDragAndDrop'; -import InitialUrlContext from './InitialUrlContext'; + import OnyxUpdateManager from './libs/actions/OnyxUpdateManager'; import * as Session from './libs/actions/Session'; import * as Environment from './libs/Environment/Environment'; @@ -31,6 +31,7 @@ import {ReportAttachmentsProvider} from './pages/home/report/ReportAttachmentsCo import ThemeIllustrationsProvider from './styles/illustrations/ThemeIllustrationsProvider'; import ThemeProvider from './styles/themes/ThemeProvider'; import ThemeStylesProvider from './styles/ThemeStylesProvider'; +import InitialUrlContext from './libs/InitialUrlContext'; const propTypes = { /** Initial url that may be passed as deeplink from Hybrid App */ diff --git a/src/InitialUrlContext.ts b/src/libs/InitialUrlContext/index.ts similarity index 100% rename from src/InitialUrlContext.ts rename to src/libs/InitialUrlContext/index.ts diff --git a/src/libs/Navigation/AppNavigator/index.js b/src/libs/Navigation/AppNavigator/index.js index 0a39a0bafeb9..b56e2dcf57e6 100644 --- a/src/libs/Navigation/AppNavigator/index.js +++ b/src/libs/Navigation/AppNavigator/index.js @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import React, {useContext, useEffect} from 'react'; import {NativeModules} from 'react-native'; import Navigation from '@libs/Navigation/Navigation'; -import InitialUrlContext from '@src/InitialUrlContext'; +import InitialUrlContext from '@libs/InitialUrlContext'; const propTypes = { /** If we have an authToken this is true */ diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index 81fe480ffcce..e1c18f8211d4 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -8,7 +8,7 @@ import * as SessionUtils from '@libs/SessionUtils'; import Navigation from '@navigation/Navigation'; import * as Session from '@userActions/Session'; import CONST from '@src/CONST'; -import InitialUrlContext from '@src/InitialUrlContext'; +import InitialUrlContext from '@libs/InitialUrlContext'; import ONYXKEYS from '@src/ONYXKEYS'; const propTypes = { From 0e228ca18b9ec48b9cf9d82c5a1847349e52fa96 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 5 Dec 2023 13:57:12 +0100 Subject: [PATCH 004/660] add short explanation to InitialurlContext --- src/libs/InitialUrlContext/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/InitialUrlContext/index.ts b/src/libs/InitialUrlContext/index.ts index eabf099dafe0..e0593c7cf152 100644 --- a/src/libs/InitialUrlContext/index.ts +++ b/src/libs/InitialUrlContext/index.ts @@ -1,5 +1,6 @@ import {createContext} from 'react'; +// Context to keep the initial url that will be opened when NewDot is embedded into Hybrid App const InitialUrlContext = createContext<{initialUrl: string | null}>({initialUrl: null}); export default InitialUrlContext; From 3ab29cc77597a8263ebe61cb624797f545fc94a4 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 5 Dec 2023 13:58:58 +0100 Subject: [PATCH 005/660] add last lint to async storage patch --- patches/@react-native-async-storage+async-storage+1.17.11.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patches/@react-native-async-storage+async-storage+1.17.11.patch b/patches/@react-native-async-storage+async-storage+1.17.11.patch index 1c8a139bc10f..4f695d4607b1 100644 --- a/patches/@react-native-async-storage+async-storage+1.17.11.patch +++ b/patches/@react-native-async-storage+async-storage+1.17.11.patch @@ -10,4 +10,4 @@ index ff1baa4..32d8020 100644 + def room_version = getVersionOrDefault('AsyncStorage_next_roomVersion', '2.4.3') def coroutines_version = "1.6.0" def coroutinesTest_version = "1.6.0" - // if we don't provide explicit dependency on reflection, kotlin plugin \ No newline at end of file + // if we don't provide explicit dependency on reflection, kotlin plugin From 7ce52547cc41f913fbb0a1ac8c7cb54ed21172e4 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 5 Dec 2023 13:59:34 +0100 Subject: [PATCH 006/660] remove line from App.js --- src/App.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/App.js b/src/App.js index 19e9d45a530c..db6a41cc11ec 100644 --- a/src/App.js +++ b/src/App.js @@ -23,7 +23,6 @@ import {KeyboardStateProvider} from './components/withKeyboardState'; import {WindowDimensionsProvider} from './components/withWindowDimensions'; import Expensify from './Expensify'; import useDefaultDragAndDrop from './hooks/useDefaultDragAndDrop'; - import OnyxUpdateManager from './libs/actions/OnyxUpdateManager'; import * as Session from './libs/actions/Session'; import * as Environment from './libs/Environment/Environment'; From 4da5437979a32d8a86de13001a49b1cbcc82f607 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 5 Dec 2023 14:00:10 +0100 Subject: [PATCH 007/660] run prettier --- src/App.js | 2 +- src/libs/Navigation/AppNavigator/index.js | 2 +- src/pages/LogOutPreviousUserPage.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/App.js b/src/App.js index db6a41cc11ec..6f2f61a11061 100644 --- a/src/App.js +++ b/src/App.js @@ -26,11 +26,11 @@ import useDefaultDragAndDrop from './hooks/useDefaultDragAndDrop'; import OnyxUpdateManager from './libs/actions/OnyxUpdateManager'; import * as Session from './libs/actions/Session'; import * as Environment from './libs/Environment/Environment'; +import InitialUrlContext from './libs/InitialUrlContext'; import {ReportAttachmentsProvider} from './pages/home/report/ReportAttachmentsContext'; import ThemeIllustrationsProvider from './styles/illustrations/ThemeIllustrationsProvider'; import ThemeProvider from './styles/themes/ThemeProvider'; import ThemeStylesProvider from './styles/ThemeStylesProvider'; -import InitialUrlContext from './libs/InitialUrlContext'; const propTypes = { /** Initial url that may be passed as deeplink from Hybrid App */ diff --git a/src/libs/Navigation/AppNavigator/index.js b/src/libs/Navigation/AppNavigator/index.js index b56e2dcf57e6..b89f2aa5071e 100644 --- a/src/libs/Navigation/AppNavigator/index.js +++ b/src/libs/Navigation/AppNavigator/index.js @@ -1,8 +1,8 @@ import PropTypes from 'prop-types'; import React, {useContext, useEffect} from 'react'; import {NativeModules} from 'react-native'; -import Navigation from '@libs/Navigation/Navigation'; import InitialUrlContext from '@libs/InitialUrlContext'; +import Navigation from '@libs/Navigation/Navigation'; const propTypes = { /** If we have an authToken this is true */ diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index e1c18f8211d4..77d282611da7 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -4,11 +4,11 @@ import React, {useContext, useEffect} from 'react'; import {Linking, NativeModules} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; +import InitialUrlContext from '@libs/InitialUrlContext'; import * as SessionUtils from '@libs/SessionUtils'; import Navigation from '@navigation/Navigation'; import * as Session from '@userActions/Session'; import CONST from '@src/CONST'; -import InitialUrlContext from '@libs/InitialUrlContext'; import ONYXKEYS from '@src/ONYXKEYS'; const propTypes = { From d33702c1fe8e0b9afeed6da672a9149c56f27c6c Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 5 Dec 2023 14:09:38 +0100 Subject: [PATCH 008/660] cleanup --- src/libs/InitialUrlContext/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/InitialUrlContext/index.ts b/src/libs/InitialUrlContext/index.ts index e0593c7cf152..432a2f6c583b 100644 --- a/src/libs/InitialUrlContext/index.ts +++ b/src/libs/InitialUrlContext/index.ts @@ -1,6 +1,6 @@ import {createContext} from 'react'; -// Context to keep the initial url that will be opened when NewDot is embedded into Hybrid App -const InitialUrlContext = createContext<{initialUrl: string | null}>({initialUrl: null}); +/** Initial url that will be opened when NewDot is embedded into Hybrid App. */ +const InitialUrlContext = createContext(null); export default InitialUrlContext; From ae2bec1410b98c334ea0164b0448b791e9e5f242 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 5 Dec 2023 15:01:17 +0100 Subject: [PATCH 009/660] improve if statement --- src/libs/Navigation/AppNavigator/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/index.js b/src/libs/Navigation/AppNavigator/index.js index b89f2aa5071e..4cfb24a3f34b 100644 --- a/src/libs/Navigation/AppNavigator/index.js +++ b/src/libs/Navigation/AppNavigator/index.js @@ -13,7 +13,7 @@ function AppNavigator(props) { const initUrl = useContext(InitialUrlContext); useEffect(() => { - if (!NativeModules.ReactNativeModule) { + if (!NativeModules.ReactNativeModule || initUrl === null) { return; } From 7796468797db46f34461490bdbbd90aea0bf71d5 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 5 Dec 2023 15:25:47 +0100 Subject: [PATCH 010/660] rename ReactNativeModule to HybridAppModule --- src/libs/InitialUrlContext/index.ts | 2 +- src/libs/Navigation/AppNavigator/index.js | 2 +- src/pages/LogOutPreviousUserPage.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/InitialUrlContext/index.ts b/src/libs/InitialUrlContext/index.ts index 432a2f6c583b..684af2bb2b60 100644 --- a/src/libs/InitialUrlContext/index.ts +++ b/src/libs/InitialUrlContext/index.ts @@ -1,6 +1,6 @@ import {createContext} from 'react'; /** Initial url that will be opened when NewDot is embedded into Hybrid App. */ -const InitialUrlContext = createContext(null); +const InitialUrlContext = createContext(undefined); export default InitialUrlContext; diff --git a/src/libs/Navigation/AppNavigator/index.js b/src/libs/Navigation/AppNavigator/index.js index 4cfb24a3f34b..5f4958f61f70 100644 --- a/src/libs/Navigation/AppNavigator/index.js +++ b/src/libs/Navigation/AppNavigator/index.js @@ -13,7 +13,7 @@ function AppNavigator(props) { const initUrl = useContext(InitialUrlContext); useEffect(() => { - if (!NativeModules.ReactNativeModule || initUrl === null) { + if (!NativeModules.HybridAppModule || initUrl === undefined) { return; } diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index 77d282611da7..36dce86b3be7 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -39,7 +39,7 @@ function LogOutPreviousUserPage(props) { useEffect(() => { Linking.getInitialURL().then((url) => { const sessionEmail = props.session.email; - const transitionUrl = NativeModules.ReactNativeModule ? CONST.DEEPLINK_BASE_URL + initUrl : url; + const transitionUrl = NativeModules.HybridAppModule ? CONST.DEEPLINK_BASE_URL + initUrl : url; const isLoggingInAsNewUser = SessionUtils.isLoggingInAsNewUser(transitionUrl, sessionEmail); if (isLoggingInAsNewUser) { From 405c29bb1b9518ef028fea5f9ef4d07fdad46489 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 5 Dec 2023 17:20:20 +0100 Subject: [PATCH 011/660] update patch for onfido --- patches/@onfido+react-native-sdk+8.3.0.patch | 64 +++++--------------- 1 file changed, 16 insertions(+), 48 deletions(-) diff --git a/patches/@onfido+react-native-sdk+8.3.0.patch b/patches/@onfido+react-native-sdk+8.3.0.patch index 940c40eb9bb0..5d3fd51bf826 100644 --- a/patches/@onfido+react-native-sdk+8.3.0.patch +++ b/patches/@onfido+react-native-sdk+8.3.0.patch @@ -1,8 +1,22 @@ diff --git a/node_modules/@onfido/react-native-sdk/android/build.gradle b/node_modules/@onfido/react-native-sdk/android/build.gradle -index b4c7106..d5083d3 100644 +index b4c7106..c6efb0f 100644 --- a/node_modules/@onfido/react-native-sdk/android/build.gradle +++ b/node_modules/@onfido/react-native-sdk/android/build.gradle -@@ -135,9 +135,9 @@ afterEvaluate { project -> +@@ -84,6 +84,13 @@ android { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } ++ ++ publishing { ++ singleVariant('release') { ++ withSourcesJar() ++ withJavadocJar() ++ } ++ } + } + + repositories { +@@ -135,9 +142,9 @@ afterEvaluate { project -> group = "Reporting" description = "Generate Jacoco coverage reports after running tests." reports { @@ -15,49 +29,3 @@ index b4c7106..d5083d3 100644 } classDirectories.setFrom(fileTree( dir: 'build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/onfido/reactnative/sdk', -diff --git a/node_modules/@onfido/react-native-sdk/android/publish.gradle b/node_modules/@onfido/react-native-sdk/android/publish.gradle -index 216d52d..1f1c17f 100644 ---- a/node_modules/@onfido/react-native-sdk/android/publish.gradle -+++ b/node_modules/@onfido/react-native-sdk/android/publish.gradle -@@ -4,41 +4,5 @@ afterEvaluate { - archiveClassifier = "sources" - } - -- publishing { -- def packageJson = new groovy.json.JsonSlurper().parseText(file('../package.json').text) - -- publications { -- mavenJava(MavenPublication) { -- // The new gradle publish plugin doesn't allow the @onfido/ prefix -- artifactId = packageJson.name.replace("@onfido/", "onfido-") -- groupId = 'com.onfido.reactnative.sdk' -- version = packageJson.version -- -- from components.release -- -- artifact sourceJar -- -- pom { -- name = packageJson.title -- description = packageJson.description -- url = packageJson.repository.baseUrl -- -- licenses { -- license { -- name = packageJson.license -- url = packageJson.repository.baseUrl + '/blob/master/' + packageJson.licenseFilename -- distribution = 'repo' -- } -- } -- -- developers { -- developer { -- id = packageJson.author.email -- name = packageJson.author.name -- } -- } -- } -- } -- } -- } - } From 486533054bb6aab94d4fe9036dc9c7de59a89961 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Wed, 6 Dec 2023 10:08:45 +0100 Subject: [PATCH 012/660] add way to go back to Expensify Classic --- src/languages/en.ts | 1 + src/languages/es.ts | 1 + src/pages/settings/InitialSettingsPage.js | 22 ++++++++++++++++++++-- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index b03e5d228a55..63cadbe4245f 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -750,6 +750,7 @@ export default { phrase3: 'and', phrase4: 'Privacy', }, + returnToClassic: 'Return to Expensify Classic', help: 'Help', }, closeAccountPage: { diff --git a/src/languages/es.ts b/src/languages/es.ts index c6bc77681b21..63c10d4556e7 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -744,6 +744,7 @@ export default { phrase3: 'y', phrase4: 'Privacidad', }, + returnToClassic: 'Volver a Expensify Clásico', help: 'Ayuda', }, closeAccountPage: { diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index 8ca1f96b3796..0bf34a99b91f 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -1,7 +1,7 @@ import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; -import {View} from 'react-native'; +import {NativeModules, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import Avatar from '@components/Avatar'; @@ -187,7 +187,7 @@ function InitialSettingsPage(props) { const paymentCardList = props.fundList || {}; - return [ + const defaultMenuItems = [ { translationKey: 'common.shareCode', icon: Expensicons.QrCode, @@ -264,6 +264,24 @@ function InitialSettingsPage(props) { }, }, ]; + + if (NativeModules.HybridAppModule) { + // eslint-disable-next-line rulesdir/prefer-underscore-method + const hybridAppMenuItems = [ + { + translationKey: 'initialSettingsPage.returnToClassic', + icon: Expensicons.RotateLeft, + shouldShowRightIcon: true, + iconRight: Expensicons.NewWindow, + action: () => NativeModules.HybridAppModule.closeReactNativeApp(), + }, + ...defaultMenuItems, + ].filter((item) => item.translationKey !== 'initialSettingsPage.signOut'); + + return hybridAppMenuItems; + } + + return defaultMenuItems; }, [ props.allPolicyMembers, props.bankAccountList, From c75ea5653d2fcd1103ded0955f5c2b9f5996feeb Mon Sep 17 00:00:00 2001 From: staszekscp Date: Wed, 6 Dec 2023 11:27:57 +0100 Subject: [PATCH 013/660] tweak BootSplash to work with Hybrid App --- ios/NewExpensify/RCTBootSplash.h | 1 + ios/NewExpensify/RCTBootSplash.m | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/ios/NewExpensify/RCTBootSplash.h b/ios/NewExpensify/RCTBootSplash.h index df38a5eb35bf..c25c676feb4a 100644 --- a/ios/NewExpensify/RCTBootSplash.h +++ b/ios/NewExpensify/RCTBootSplash.h @@ -10,6 +10,7 @@ @interface RCTBootSplash : NSObject ++ (void)invalidateBootSplash; + (void)initWithStoryboard:(NSString * _Nonnull)storyboardName rootView:(RCTRootView * _Nullable)rootView; diff --git a/ios/NewExpensify/RCTBootSplash.m b/ios/NewExpensify/RCTBootSplash.m index bceac70efdcf..6c2baaed4ee0 100644 --- a/ios/NewExpensify/RCTBootSplash.m +++ b/ios/NewExpensify/RCTBootSplash.m @@ -26,6 +26,12 @@ - (dispatch_queue_t)methodQueue { return dispatch_get_main_queue(); } ++ (void)invalidateBootSplash { + _resolverQueue = nil; + _rootView = nil; + _nativeHidden = false; +} + + (void)initWithStoryboard:(NSString * _Nonnull)storyboardName rootView:(RCTRootView * _Nullable)rootView { if (rootView == nil || _rootView != nil || RCTRunningInAppExtension()) @@ -102,6 +108,9 @@ + (void)onContentDidAppear { block:^(NSTimer * _Nonnull timer) { [timer invalidate]; + if (_rootView == nil) + return; + if (_resolverQueue == nil) _resolverQueue = [[NSMutableArray alloc] init]; From 4dc32d4d6ff6120baa66af9ef0358df12a44ecd1 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Wed, 6 Dec 2023 14:06:33 +0100 Subject: [PATCH 014/660] switch to lodash filter --- src/pages/settings/InitialSettingsPage.js | 24 ++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index 0bf34a99b91f..4039768cacf4 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -266,17 +266,19 @@ function InitialSettingsPage(props) { ]; if (NativeModules.HybridAppModule) { - // eslint-disable-next-line rulesdir/prefer-underscore-method - const hybridAppMenuItems = [ - { - translationKey: 'initialSettingsPage.returnToClassic', - icon: Expensicons.RotateLeft, - shouldShowRightIcon: true, - iconRight: Expensicons.NewWindow, - action: () => NativeModules.HybridAppModule.closeReactNativeApp(), - }, - ...defaultMenuItems, - ].filter((item) => item.translationKey !== 'initialSettingsPage.signOut'); + const hybridAppMenuItems = _.filter( + [ + { + translationKey: 'initialSettingsPage.returnToClassic', + icon: Expensicons.RotateLeft, + shouldShowRightIcon: true, + iconRight: Expensicons.NewWindow, + action: () => NativeModules.HybridAppModule.closeReactNativeApp(), + }, + ...defaultMenuItems, + ], + (item) => item.translationKey !== 'initialSettingsPage.signOut', + ); return hybridAppMenuItems; } From b53412c8f2faf1ac7035967438d05822c7363705 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Wed, 6 Dec 2023 14:12:18 +0100 Subject: [PATCH 015/660] update InitialUrlContext --- src/libs/InitialUrlContext/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/InitialUrlContext/index.ts b/src/libs/InitialUrlContext/index.ts index 684af2bb2b60..989f3369585f 100644 --- a/src/libs/InitialUrlContext/index.ts +++ b/src/libs/InitialUrlContext/index.ts @@ -1,6 +1,7 @@ import {createContext} from 'react'; +import {Route} from '@src/ROUTES'; /** Initial url that will be opened when NewDot is embedded into Hybrid App. */ -const InitialUrlContext = createContext(undefined); +const InitialUrlContext = createContext(undefined); export default InitialUrlContext; From fd1e6447fa7f5f8cc738617bcc42ee0d9f194c5e Mon Sep 17 00:00:00 2001 From: staszekscp Date: Thu, 7 Dec 2023 15:30:53 +0100 Subject: [PATCH 016/660] upgrade async storage and remove patch --- package-lock.json | 13 ++++++++----- package.json | 2 +- ...native-async-storage+async-storage+1.17.11.patch | 13 ------------- 3 files changed, 9 insertions(+), 19 deletions(-) delete mode 100644 patches/@react-native-async-storage+async-storage+1.17.11.patch diff --git a/package-lock.json b/package-lock.json index 2e78d6b02264..3d879b57220a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "@kie/mock-github": "^1.0.0", "@oguzhnatly/react-native-image-manipulator": "github:Expensify/react-native-image-manipulator#5cdae3d4455b03a04c57f50be3863e2fe6c92c52", "@onfido/react-native-sdk": "8.3.0", - "@react-native-async-storage/async-storage": "^1.17.10", + "@react-native-async-storage/async-storage": "1.21.0", "@react-native-camera-roll/camera-roll": "5.4.0", "@react-native-clipboard/clipboard": "^1.12.1", "@react-native-community/geolocation": "^3.0.6", @@ -6648,13 +6648,14 @@ } }, "node_modules/@react-native-async-storage/async-storage": { - "version": "1.17.11", - "license": "MIT", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.21.0.tgz", + "integrity": "sha512-JL0w36KuFHFCvnbOXRekqVAUplmOyT/OuCQkogo6X98MtpSaJOKEAeZnYO8JB0U/RIEixZaGI5px73YbRm/oag==", "dependencies": { "merge-options": "^3.0.4" }, "peerDependencies": { - "react-native": "^0.0.0-0 || 0.60 - 0.71 || 1000.0.0" + "react-native": "^0.0.0-0 || >=0.60 <1.0" } }, "node_modules/@react-native-camera-roll/camera-roll": { @@ -57465,7 +57466,9 @@ } }, "@react-native-async-storage/async-storage": { - "version": "1.17.11", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.21.0.tgz", + "integrity": "sha512-JL0w36KuFHFCvnbOXRekqVAUplmOyT/OuCQkogo6X98MtpSaJOKEAeZnYO8JB0U/RIEixZaGI5px73YbRm/oag==", "requires": { "merge-options": "^3.0.4" } diff --git a/package.json b/package.json index 968bea8c8abf..c876fd441465 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "@kie/mock-github": "^1.0.0", "@oguzhnatly/react-native-image-manipulator": "github:Expensify/react-native-image-manipulator#5cdae3d4455b03a04c57f50be3863e2fe6c92c52", "@onfido/react-native-sdk": "8.3.0", - "@react-native-async-storage/async-storage": "^1.17.10", + "@react-native-async-storage/async-storage": "1.21.0", "@react-native-camera-roll/camera-roll": "5.4.0", "@react-native-clipboard/clipboard": "^1.12.1", "@react-native-community/geolocation": "^3.0.6", diff --git a/patches/@react-native-async-storage+async-storage+1.17.11.patch b/patches/@react-native-async-storage+async-storage+1.17.11.patch deleted file mode 100644 index 4f695d4607b1..000000000000 --- a/patches/@react-native-async-storage+async-storage+1.17.11.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/node_modules/@react-native-async-storage/async-storage/android/build.gradle b/node_modules/@react-native-async-storage/async-storage/android/build.gradle -index ff1baa4..32d8020 100644 ---- a/node_modules/@react-native-async-storage/async-storage/android/build.gradle -+++ b/node_modules/@react-native-async-storage/async-storage/android/build.gradle -@@ -112,7 +112,7 @@ repositories { - dependencies { - - if (useNextStorage) { -- def room_version = getVersionOrDefault('AsyncStorage_next_roomVersion', '2.4.2') -+ def room_version = getVersionOrDefault('AsyncStorage_next_roomVersion', '2.4.3') - def coroutines_version = "1.6.0" - def coroutinesTest_version = "1.6.0" - // if we don't provide explicit dependency on reflection, kotlin plugin From 48d422ae495d134a953de5a050d8daf59f21dc45 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 18 Dec 2023 16:21:41 +0700 Subject: [PATCH 017/660] fix: add singleExecution for menu items --- src/components/MenuItemGroup.js | 35 +++++++++++++++++++ .../PersonalDetailsInitialPage.js | 3 ++ 2 files changed, 38 insertions(+) create mode 100644 src/components/MenuItemGroup.js diff --git a/src/components/MenuItemGroup.js b/src/components/MenuItemGroup.js new file mode 100644 index 000000000000..60d27247a45f --- /dev/null +++ b/src/components/MenuItemGroup.js @@ -0,0 +1,35 @@ +import useSingleExecution from '@hooks/useSingleExecution'; +import _ from 'lodash'; +import PropTypes from 'prop-types'; +import React, {Children, cloneElement} from 'react'; + +const propTypes = { + /* Actual content wrapped by this component */ + children: PropTypes.node.isRequired, + + /** Whether or not to use the single execution hook */ + shouldUseSingleExecution: PropTypes.bool, +}; +const defaultProps = { + shouldUseSingleExecution: true +}; + +function MenuItemGroup(props){ + const {isExecuting, singleExecution} = useSingleExecution(); + const arrayChildren = Children.toArray(props.children); + + return <> + {Children.map(arrayChildren, (child, index) => { + return cloneElement(child,{ + ...child.props, + onPress: props.shouldUseSingleExecution ? singleExecution(child.props.onPress) : child.props.onPress + }) + })} + +} + +MenuItemGroup.displayName = 'MenuItemGroup'; +MenuItemGroup.propTypes = propTypes; +MenuItemGroup.defaultProps = defaultProps; + +export default MenuItemGroup; \ No newline at end of file diff --git a/src/pages/settings/Profile/PersonalDetails/PersonalDetailsInitialPage.js b/src/pages/settings/Profile/PersonalDetails/PersonalDetailsInitialPage.js index cf6887b7e04c..a35ad78836b8 100644 --- a/src/pages/settings/Profile/PersonalDetails/PersonalDetailsInitialPage.js +++ b/src/pages/settings/Profile/PersonalDetails/PersonalDetailsInitialPage.js @@ -17,6 +17,7 @@ import Navigation from '@libs/Navigation/Navigation'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import MenuItemGroup from '@components/MenuItemGroup'; const propTypes = { /* Onyx Props */ @@ -77,6 +78,7 @@ function PersonalDetailsInitialPage(props) { {props.translate('privatePersonalDetails.privateDataMessage')} + Navigation.navigate(ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS)} /> + )} From b81373faf01130e8f568e2bc3a63141933b28d78 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 18 Dec 2023 16:32:51 +0700 Subject: [PATCH 018/660] disable when is executing --- src/components/MenuItemGroup.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/MenuItemGroup.js b/src/components/MenuItemGroup.js index 60d27247a45f..4fe5adf989b6 100644 --- a/src/components/MenuItemGroup.js +++ b/src/components/MenuItemGroup.js @@ -1,5 +1,4 @@ import useSingleExecution from '@hooks/useSingleExecution'; -import _ from 'lodash'; import PropTypes from 'prop-types'; import React, {Children, cloneElement} from 'react'; @@ -22,7 +21,8 @@ function MenuItemGroup(props){ {Children.map(arrayChildren, (child, index) => { return cloneElement(child,{ ...child.props, - onPress: props.shouldUseSingleExecution ? singleExecution(child.props.onPress) : child.props.onPress + onPress: props.shouldUseSingleExecution ? singleExecution(child.props.onPress) : child.props.onPress, + disabled: child.props.disabled || isExecuting }) })} From 996ee26a0463376fc95539227a607b2cd5d18850 Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Thu, 28 Dec 2023 12:16:50 +0100 Subject: [PATCH 019/660] Migrate Onyx.connect methods to TS --- src/ONYXKEYS.ts | 3 +++ src/libs/actions/{IOU.js => IOU.ts} | 42 +++++++++++++++-------------- src/types/onyx/Transaction.ts | 4 ++- src/types/onyx/index.ts | 3 ++- 4 files changed, 30 insertions(+), 22 deletions(-) rename src/libs/actions/{IOU.js => IOU.ts} (99%) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 53cd37e71f67..f19320a8bbc6 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -457,6 +457,9 @@ type OnyxValues = { [ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM]: boolean; [ONYXKEYS.COLLECTION.SECURITY_GROUP]: OnyxTypes.SecurityGroup; [ONYXKEYS.COLLECTION.TRANSACTION]: OnyxTypes.Transaction; + [ONYXKEYS.COLLECTION.TRANSACTION_DRAFT]: OnyxTypes.TransactionDraft; + [ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT]: OnyxTypes.TransactionDraft; + [ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS]: OnyxTypes.TransactionViolation; [ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS]: OnyxTypes.RecentlyUsedTags; [ONYXKEYS.COLLECTION.SELECTED_TAB]: string; [ONYXKEYS.COLLECTION.PRIVATE_NOTES_DRAFT]: string; diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.ts similarity index 99% rename from src/libs/actions/IOU.js rename to src/libs/actions/IOU.ts index d43fefca20bc..dec566ee977a 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.ts @@ -2,7 +2,7 @@ import {format} from 'date-fns'; import Str from 'expensify-common/lib/str'; import lodashGet from 'lodash/get'; import lodashHas from 'lodash/has'; -import Onyx from 'react-native-onyx'; +import Onyx, {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import OnyxUtils from 'react-native-onyx/lib/utils'; import _ from 'underscore'; import ReceiptGeneric from '@assets/images/receipt-generic.png'; @@ -24,31 +24,33 @@ import * as UserUtils from '@libs/UserUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import * as OnyxTypes from '@src/types/onyx'; +import {EmptyObject} from '@src/types/utils/EmptyObject'; import * as Policy from './Policy'; import * as Report from './Report'; -let betas; +let betas: OnyxTypes.Beta[] = []; Onyx.connect({ key: ONYXKEYS.BETAS, - callback: (val) => (betas = val || []), + callback: (val) => (betas = val ?? []), }); -let allPersonalDetails; +let allPersonalDetails: OnyxTypes.PersonalDetailsList = {}; Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (val) => { - allPersonalDetails = val || {}; + allPersonalDetails = val ?? {}; }, }); -let allReports; +let allReports: OnyxCollection = null; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, waitForCollectionCallback: true, callback: (val) => (allReports = val), }); -let allTransactions; +let allTransactions: Record = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION, waitForCollectionCallback: true, @@ -62,16 +64,16 @@ Onyx.connect({ }, }); -let allTransactionDrafts = {}; +let allTransactionDrafts: Record = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, waitForCollectionCallback: true, callback: (val) => { - allTransactionDrafts = val || {}; + allTransactionDrafts = val ?? {}; }, }); -let allTransactionViolations; +let allTransactionViolations: Record = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, waitForCollectionCallback: true, @@ -85,43 +87,43 @@ Onyx.connect({ }, }); -let allDraftSplitTransactions; +let allDraftSplitTransactions: Record = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT, waitForCollectionCallback: true, callback: (val) => { - allDraftSplitTransactions = val || {}; + allDraftSplitTransactions = val ?? {}; }, }); -let allNextSteps = {}; +let allNextSteps: Record = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.NEXT_STEP, waitForCollectionCallback: true, callback: (val) => { - allNextSteps = val || {}; + allNextSteps = val ?? {}; }, }); -let userAccountID = ''; +let userAccountID = -1; let currentUserEmail = ''; Onyx.connect({ key: ONYXKEYS.SESSION, callback: (val) => { - currentUserEmail = lodashGet(val, 'email', ''); - userAccountID = lodashGet(val, 'accountID', ''); + currentUserEmail = val?.email ?? ''; + userAccountID = val?.accountID ?? -1; }, }); -let currentUserPersonalDetails = {}; +let currentUserPersonalDetails: OnyxTypes.PersonalDetails | EmptyObject = {}; Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (val) => { - currentUserPersonalDetails = lodashGet(val, userAccountID, {}); + currentUserPersonalDetails = val?.[userAccountID] ?? {}; }, }); -let currentDate = ''; +let currentDate: OnyxEntry = ''; Onyx.connect({ key: ONYXKEYS.CURRENT_DATE, callback: (val) => { diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 53bfc36a4e47..8b79966451ed 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -96,5 +96,7 @@ type Transaction = { originalCurrency?: string; }; +type TransactionDraft = Partial; + export default Transaction; -export type {WaypointCollection, Comment, Receipt, Waypoint}; +export type {WaypointCollection, Comment, Receipt, Waypoint, TransactionDraft}; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 3d4eef500f1d..3a59959364c5 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -46,7 +46,7 @@ import ScreenShareRequest from './ScreenShareRequest'; import SecurityGroup from './SecurityGroup'; import Session from './Session'; import Task from './Task'; -import Transaction from './Transaction'; +import Transaction, {TransactionDraft} from './Transaction'; import {TransactionViolation, ViolationName} from './TransactionViolation'; import User from './User'; import UserLocation from './UserLocation'; @@ -115,6 +115,7 @@ export type { Session, Task, Transaction, + TransactionDraft, TransactionViolation, User, UserLocation, From b7af44adc59140a9d227fedd6c11990b494fd93f Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Thu, 28 Dec 2023 12:26:48 +0100 Subject: [PATCH 020/660] Migrate simple functions --- src/libs/actions/IOU.ts | 140 +++++++--------------------------- src/types/onyx/IOU.ts | 4 + src/types/onyx/Transaction.ts | 2 + 3 files changed, 33 insertions(+), 113 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index dec566ee977a..f62799066d62 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/naming-convention */ import {format} from 'date-fns'; import Str from 'expensify-common/lib/str'; import lodashGet from 'lodash/get'; @@ -25,6 +26,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import * as OnyxTypes from '@src/types/onyx'; +import {Participant} from '@src/types/onyx/IOU'; import {EmptyObject} from '@src/types/utils/EmptyObject'; import * as Policy from './Policy'; import * as Report from './Report'; @@ -165,106 +167,55 @@ function startMoneyRequest_temporaryForRefactor(reportID, isFromGlobalCreate, io }); } -/** - * @param {String} transactionID - */ -function clearMoneyRequest(transactionID) { +function clearMoneyRequest(transactionID: string) { Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, null); } -/** - * @param {String} transactionID - * @param {Number} amount - * @param {String} currency - */ -function setMoneyRequestAmount_temporaryForRefactor(transactionID, amount, currency) { +function setMoneyRequestAmount_temporaryForRefactor(transactionID: string, amount: number, currency: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {amount, currency}); } -/** - * @param {String} transactionID - * @param {String} created - */ -function setMoneyRequestCreated_temporaryForRefactor(transactionID, created) { +function setMoneyRequestCreated_temporaryForRefactor(transactionID: string, created: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {created}); } -/** - * @param {String} transactionID - * @param {String} currency - */ -function setMoneyRequestCurrency_temporaryForRefactor(transactionID, currency) { +function setMoneyRequestCurrency_temporaryForRefactor(transactionID: string, currency: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {currency}); } -/** - * @param {String} transactionID - * @param {String} comment - */ -function setMoneyRequestDescription_temporaryForRefactor(transactionID, comment) { +function setMoneyRequestDescription_temporaryForRefactor(transactionID: string, comment: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {comment: {comment: comment.trim()}}); } -/** - * @param {String} transactionID - * @param {String} merchant - */ -function setMoneyRequestMerchant_temporaryForRefactor(transactionID, merchant) { +function setMoneyRequestMerchant_temporaryForRefactor(transactionID: string, merchant: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {merchant: merchant.trim()}); } -/** - * @param {String} transactionID - * @param {String} category - */ -function setMoneyRequestCategory_temporaryForRefactor(transactionID, category) { +function setMoneyRequestCategory_temporaryForRefactor(transactionID: string, category: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {category}); } -/* - * @param {String} transactionID - */ -function resetMoneyRequestCategory_temporaryForRefactor(transactionID) { +function resetMoneyRequestCategory_temporaryForRefactor(transactionID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {category: null}); } -/* - * @param {String} transactionID - * @param {String} tag - */ -function setMoneyRequestTag_temporaryForRefactor(transactionID, tag) { +function setMoneyRequestTag_temporaryForRefactor(transactionID: string, tag: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {tag}); } -/* - * @param {String} transactionID - */ -function resetMoneyRequestTag_temporaryForRefactor(transactionID) { +function resetMoneyRequestTag_temporaryForRefactor(transactionID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {tag: null}); } -/** - * @param {String} transactionID - * @param {Boolean} billable - */ -function setMoneyRequestBillable_temporaryForRefactor(transactionID, billable) { +function setMoneyRequestBillable_temporaryForRefactor(transactionID: string, billable: boolean) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {billable}); } -/** - * @param {String} transactionID - * @param {Object[]} participants - */ -function setMoneyRequestParticipants_temporaryForRefactor(transactionID, participants) { +function setMoneyRequestParticipants_temporaryForRefactor(transactionID: string, participants: Participant[]) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {participants}); } -/** - * @param {String} transactionID - * @param {String} source - * @param {String} filename - */ -function setMoneyRequestReceipt_temporaryForRefactor(transactionID, source, filename) { +function setMoneyRequestReceipt_temporaryForRefactor(transactionID: string, source: string, filename: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {receipt: {source}, filename}); } @@ -3214,60 +3165,37 @@ function setMoneyRequestParticipantsFromReport(transactionID, report) { /** * Initialize money request info and navigate to the MoneyRequest page - * @param {String} iouType - * @param {String} reportID */ -function startMoneyRequest(iouType, reportID = '') { +function startMoneyRequest(iouType: string, reportID = '') { resetMoneyRequestInfo(`${iouType}${reportID}`); Navigation.navigate(ROUTES.MONEY_REQUEST.getRoute(iouType, reportID)); } -/** - * @param {String} id - */ -function setMoneyRequestId(id) { +function setMoneyRequestId(id: string) { Onyx.merge(ONYXKEYS.IOU, {id}); } -/** - * @param {Number} amount - */ -function setMoneyRequestAmount(amount) { +function setMoneyRequestAmount(amount: number) { Onyx.merge(ONYXKEYS.IOU, {amount}); } -/** - * @param {String} created - */ -function setMoneyRequestCreated(created) { +function setMoneyRequestCreated(created: string) { Onyx.merge(ONYXKEYS.IOU, {created}); } -/** - * @param {String} currency - */ -function setMoneyRequestCurrency(currency) { +function setMoneyRequestCurrency(currency: string) { Onyx.merge(ONYXKEYS.IOU, {currency}); } -/** - * @param {String} comment - */ -function setMoneyRequestDescription(comment) { +function setMoneyRequestDescription(comment: string) { Onyx.merge(ONYXKEYS.IOU, {comment: comment.trim()}); } -/** - * @param {String} merchant - */ -function setMoneyRequestMerchant(merchant) { +function setMoneyRequestMerchant(merchant: string) { Onyx.merge(ONYXKEYS.IOU, {merchant: merchant.trim()}); } -/** - * @param {String} category - */ -function setMoneyRequestCategory(category) { +function setMoneyRequestCategory(category: string) { Onyx.merge(ONYXKEYS.IOU, {category}); } @@ -3275,10 +3203,7 @@ function resetMoneyRequestCategory() { Onyx.merge(ONYXKEYS.IOU, {category: ''}); } -/* - * @param {String} tag - */ -function setMoneyRequestTag(tag) { +function setMoneyRequestTag(tag: string) { Onyx.merge(ONYXKEYS.IOU, {tag}); } @@ -3286,26 +3211,15 @@ function resetMoneyRequestTag() { Onyx.merge(ONYXKEYS.IOU, {tag: ''}); } -/** - * @param {Boolean} billable - */ -function setMoneyRequestBillable(billable) { +function setMoneyRequestBillable(billable: boolean) { Onyx.merge(ONYXKEYS.IOU, {billable}); } -/** - * @param {Object[]} participants - * @param {Boolean} isSplitRequest - */ -function setMoneyRequestParticipants(participants, isSplitRequest) { +function setMoneyRequestParticipants(participants: Participant[], isSplitRequest: boolean) { Onyx.merge(ONYXKEYS.IOU, {participants, isSplitRequest}); } -/** - * @param {String} receiptPath - * @param {String} receiptFilename - */ -function setMoneyRequestReceipt(receiptPath, receiptFilename) { +function setMoneyRequestReceipt(receiptPath: string, receiptFilename: string) { Onyx.merge(ONYXKEYS.IOU, {receiptPath, receiptFilename, merchant: ''}); } diff --git a/src/types/onyx/IOU.ts b/src/types/onyx/IOU.ts index a89b0d4530ef..e6a0d861e989 100644 --- a/src/types/onyx/IOU.ts +++ b/src/types/onyx/IOU.ts @@ -13,6 +13,7 @@ type IOU = { /** Selected Currency Code of the current IOU */ currency?: string; comment?: string; + category?: string; merchant?: string; created?: string; receiptPath?: string; @@ -20,6 +21,9 @@ type IOU = { transactionID?: string; participants?: Participant[]; tag?: string; + billable?: boolean; + isSplitRequest?: boolean; }; export default IOU; +export type {Participant}; diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 8b79966451ed..bed682822add 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -1,5 +1,6 @@ import {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; +import {Participant} from './IOU'; import * as OnyxCommon from './OnyxCommon'; import RecentWaypoint from './RecentWaypoint'; @@ -70,6 +71,7 @@ type Transaction = { modifiedWaypoints?: WaypointCollection; // Used during the creation flow before the transaction is saved to the server and helps dictate where the user is navigated to when pressing the back button on the confirmation step participantsAutoAssigned?: boolean; + participants?: Participant[]; pendingAction: OnyxCommon.PendingAction; receipt?: Receipt; reportID: string; From 519852e6437f81d8153d03e481d7ccab9f359305 Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Thu, 28 Dec 2023 12:38:31 +0100 Subject: [PATCH 021/660] Migrate startMoneyRequest_temporaryForRefactor and resetMoneyRequestInfo functions --- src/libs/actions/IOU.ts | 19 ++++++++++++------- src/types/onyx/Transaction.ts | 4 +++- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index f62799066d62..5fcfc13fe6c9 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -5,6 +5,7 @@ import lodashGet from 'lodash/get'; import lodashHas from 'lodash/has'; import Onyx, {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import OnyxUtils from 'react-native-onyx/lib/utils'; +import {ValueOf} from 'type-fest'; import _ from 'underscore'; import ReceiptGeneric from '@assets/images/receipt-generic.png'; import * as API from '@libs/API'; @@ -27,10 +28,13 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import * as OnyxTypes from '@src/types/onyx'; import {Participant} from '@src/types/onyx/IOU'; +import {Comment} from '@src/types/onyx/Transaction'; import {EmptyObject} from '@src/types/utils/EmptyObject'; import * as Policy from './Policy'; import * as Report from './Report'; +type IOURequestType = ValueOf; + let betas: OnyxTypes.Beta[] = []; Onyx.connect({ key: ONYXKEYS.BETAS, @@ -135,15 +139,16 @@ Onyx.connect({ /** * Initialize money request info - * @param {String} reportID to attach the transaction to - * @param {Boolean} isFromGlobalCreate - * @param {String} [iouRequestType] one of manual/scan/distance + * @param reportID to attach the transaction to + * @param isFromGlobalCreate + * @param [iouRequestType] one of manual/scan/distance */ -function startMoneyRequest_temporaryForRefactor(reportID, isFromGlobalCreate, iouRequestType = CONST.IOU.REQUEST_TYPE.MANUAL) { +function startMoneyRequest_temporaryForRefactor(reportID: string, isFromGlobalCreate: boolean, iouRequestType: IOURequestType = CONST.IOU.REQUEST_TYPE.MANUAL) { // Generate a brand new transactionID const newTransactionID = CONST.IOU.OPTIMISTIC_TRANSACTION_ID; + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- currentDate can be an empty string const created = currentDate || format(new Date(), 'yyyy-MM-dd'); - const comment = {}; + const comment: Comment = {}; // Add initial empty waypoints when starting a distance request if (iouRequestType === CONST.IOU.REQUEST_TYPE.DISTANCE) { @@ -221,14 +226,14 @@ function setMoneyRequestReceipt_temporaryForRefactor(transactionID: string, sour /** * Reset money request info from the store with its initial value - * @param {String} id */ function resetMoneyRequestInfo(id = '') { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- currentDate can be an empty string const created = currentDate || format(new Date(), CONST.DATE.FNS_FORMAT_STRING); Onyx.merge(ONYXKEYS.IOU, { id, amount: 0, - currency: lodashGet(currentUserPersonalDetails, 'localCurrencyCode', CONST.CURRENCY.USD), + currency: currentUserPersonalDetails?.localCurrencyCode ?? CONST.CURRENCY.USD, comment: '', participants: [], merchant: CONST.TRANSACTION.DEFAULT_MERCHANT, diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index bed682822add..34068a6d12d9 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -98,7 +98,9 @@ type Transaction = { originalCurrency?: string; }; -type TransactionDraft = Partial; +type TransactionDraft = Partial & { + isFromGlobalCreate?: boolean; +}; export default Transaction; export type {WaypointCollection, Comment, Receipt, Waypoint, TransactionDraft}; From e5f8cc337ccbf4b1a3a6e90cad83d23a8a999fd9 Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Thu, 28 Dec 2023 13:39:45 +0100 Subject: [PATCH 022/660] Migrate buildOnyxDataForMoneyRequest function --- src/libs/ErrorUtils.ts | 2 +- src/libs/ReportUtils.ts | 2 +- src/libs/actions/IOU.ts | 243 ++++++++++++++++++++++++---------------- 3 files changed, 147 insertions(+), 100 deletions(-) diff --git a/src/libs/ErrorUtils.ts b/src/libs/ErrorUtils.ts index 46bdd510f5c4..8d3cc4b83fab 100644 --- a/src/libs/ErrorUtils.ts +++ b/src/libs/ErrorUtils.ts @@ -38,7 +38,7 @@ function getAuthenticateErrorMessage(response: Response): keyof TranslationFlatO * Method used to get an error object with microsecond as the key. * @param error - error key or message to be saved */ -function getMicroSecondOnyxError(error: string): Record { +function getMicroSecondOnyxError(error: string | null): Record { return {[DateUtils.getMicroseconds()]: error}; } diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 81bbf1df6273..b72b36a5caca 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4368,4 +4368,4 @@ export { shouldAutoFocusOnKeyPress, }; -export type {ExpenseOriginalMessage, OptionData, OptimisticChatReport}; +export type {ExpenseOriginalMessage, OptionData, OptimisticChatReport, OptimisticCreatedReportAction, OptimisticIOUReportAction}; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 5fcfc13fe6c9..e9e6bc394b16 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3,7 +3,7 @@ import {format} from 'date-fns'; import Str from 'expensify-common/lib/str'; import lodashGet from 'lodash/get'; import lodashHas from 'lodash/has'; -import Onyx, {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import Onyx, {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import OnyxUtils from 'react-native-onyx/lib/utils'; import {ValueOf} from 'type-fest'; import _ from 'underscore'; @@ -21,6 +21,7 @@ import * as OptionsListUtils from '@libs/OptionsListUtils'; import Permissions from '@libs/Permissions'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; +import {OptimisticCreatedReportAction, OptimisticIOUReportAction} from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import * as UserUtils from '@libs/UserUtils'; import CONST from '@src/CONST'; @@ -28,11 +29,18 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import * as OnyxTypes from '@src/types/onyx'; import {Participant} from '@src/types/onyx/IOU'; +import ReportAction from '@src/types/onyx/ReportAction'; import {Comment} from '@src/types/onyx/Transaction'; import {EmptyObject} from '@src/types/utils/EmptyObject'; import * as Policy from './Policy'; import * as Report from './Report'; +// TODO: Remove this once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. +type OptimisticPolicyRecentlyUsedCategories = string[]; + +// TODO: Remove this once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. +type OptimisticPolicyRecentlyUsedTags = Record; + type IOURequestType = ValueOf; let betas: OnyxTypes.Beta[] = []; @@ -249,63 +257,100 @@ function resetMoneyRequestInfo(id = '') { } function buildOnyxDataForMoneyRequest( - chatReport, - iouReport, - transaction, - chatCreatedAction, - iouCreatedAction, - iouAction, - optimisticPersonalDetailListAction, - reportPreviewAction, - optimisticPolicyRecentlyUsedCategories, - optimisticPolicyRecentlyUsedTags, - isNewChatReport, - isNewIOUReport, -) { - const optimisticData = [ - { - // Use 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 - onyxMethod: isNewChatReport ? Onyx.METHOD.SET : Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, - value: { - ...chatReport, - lastReadTime: DateUtils.getDBTime(), - lastMessageTranslationKey: '', - iouReportID: iouReport.reportID, - ...(isNewChatReport ? {pendingFields: {createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}} : {}), - }, - }, - { - onyxMethod: isNewIOUReport ? Onyx.METHOD.SET : Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, - value: { - ...iouReport, - lastMessageText: iouAction.message[0].text, - lastMessageHtml: iouAction.message[0].html, - ...(isNewIOUReport ? {pendingFields: {createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}} : {}), - }, - }, + chatReport: OnyxTypes.Report, + iouReport: OnyxTypes.Report, + transaction: OnyxTypes.Transaction, + chatCreatedAction: OptimisticCreatedReportAction, + iouCreatedAction: OptimisticCreatedReportAction, + iouAction: OptimisticIOUReportAction, + optimisticPersonalDetailListAction: OnyxTypes.PersonalDetailsList | undefined, + reportPreviewAction: ReportAction, + optimisticPolicyRecentlyUsedCategories: OptimisticPolicyRecentlyUsedCategories, + optimisticPolicyRecentlyUsedTags: OptimisticPolicyRecentlyUsedTags, + isNewChatReport: boolean, + isNewIOUReport: boolean, +): OnyxUpdate[][] { + const optimisticData: OnyxUpdate[] = [ + isNewChatReport + ? { + // Use 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 + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport?.reportID}`, + value: { + ...chatReport, + lastReadTime: DateUtils.getDBTime(), + lastMessageTranslationKey: '', + iouReportID: iouReport?.reportID, + pendingFields: {createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}, + }, + } + : { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport?.reportID}`, + value: { + ...chatReport, + lastReadTime: DateUtils.getDBTime(), + lastMessageTranslationKey: '', + iouReportID: iouReport?.reportID, + }, + }, + isNewIOUReport + ? { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, + value: { + ...iouReport, + lastMessageText: iouAction.message?.[0].text, + lastMessageHtml: iouAction.message?.[0].html, + pendingFields: {createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}, + }, + } + : { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, + value: { + ...iouReport, + lastMessageText: iouAction.message?.[0].text, + lastMessageHtml: iouAction.message?.[0].html, + }, + }, { onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, value: transaction, }, - { - onyxMethod: isNewChatReport ? Onyx.METHOD.SET : Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport.reportID}`, - value: { - ...(isNewChatReport ? {[chatCreatedAction.reportActionID]: chatCreatedAction} : {}), - [reportPreviewAction.reportActionID]: reportPreviewAction, - }, - }, - { - onyxMethod: isNewIOUReport ? Onyx.METHOD.SET : Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.reportID}`, - value: { - ...(isNewIOUReport ? {[iouCreatedAction.reportActionID]: iouCreatedAction} : {}), - [iouAction.reportActionID]: iouAction, - }, - }, + isNewChatReport + ? { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport.reportID}`, + value: { + [chatCreatedAction.reportActionID]: chatCreatedAction, + [reportPreviewAction.reportActionID]: reportPreviewAction, + }, + } + : { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport.reportID}`, + value: { + [reportPreviewAction.reportActionID]: reportPreviewAction, + }, + }, + isNewIOUReport + ? { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.reportID}`, + value: { + [iouCreatedAction.reportActionID]: iouCreatedAction as OnyxTypes.ReportAction, + [iouAction.reportActionID]: iouAction as OnyxTypes.ReportAction, + }, + } + : { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.reportID}`, + value: { + [iouAction.reportActionID]: iouAction as OnyxTypes.ReportAction, + }, + }, // Remove the temporary transaction used during the creation flow { @@ -315,7 +360,7 @@ function buildOnyxDataForMoneyRequest( }, ]; - if (!_.isEmpty(optimisticPolicyRecentlyUsedCategories)) { + if (optimisticPolicyRecentlyUsedCategories.length) { optimisticData.push({ onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${iouReport.policyID}`, @@ -323,7 +368,7 @@ function buildOnyxDataForMoneyRequest( }); } - if (!_.isEmpty(optimisticPolicyRecentlyUsedTags)) { + if (Object.keys(optimisticPolicyRecentlyUsedTags).length) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${iouReport.policyID}`, @@ -331,7 +376,7 @@ function buildOnyxDataForMoneyRequest( }); } - if (!_.isEmpty(optimisticPersonalDetailListAction)) { + if (optimisticPersonalDetailListAction && Object.keys(optimisticPersonalDetailListAction).length) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.PERSONAL_DETAILS_LIST, @@ -339,31 +384,31 @@ function buildOnyxDataForMoneyRequest( }); } - const successData = [ - ...(isNewChatReport - ? [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, - value: { - pendingFields: null, - errorFields: null, - }, - }, - ] - : []), - ...(isNewIOUReport - ? [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, - value: { - pendingFields: null, - errorFields: null, - }, - }, - ] - : []), + const successData: OnyxUpdate[] = []; + + if (isNewChatReport) { + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, + value: { + pendingFields: null, + errorFields: null, + }, + }); + } + + if (isNewIOUReport) { + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, + value: { + pendingFields: null, + errorFields: null, + }, + }); + } + + successData.push( { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, @@ -408,9 +453,9 @@ function buildOnyxDataForMoneyRequest( }, }, }, - ]; + ); - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, @@ -427,20 +472,22 @@ function buildOnyxDataForMoneyRequest( : {}), }, }, - ...(isNewIOUReport - ? [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, - value: { - pendingFields: null, - errorFields: { - createChat: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), - }, - }, - }, - ] - : []), + ]; + + if (isNewIOUReport) { + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, + value: { + pendingFields: null, + errorFields: { + createChat: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), + }, + }, + }); + } + + failureData.push( { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, @@ -499,7 +546,7 @@ function buildOnyxDataForMoneyRequest( }), }, }, - ]; + ); return [optimisticData, successData, failureData]; } From 98a1e6bb49ed2cfe18bd777c3806a37a3a54db36 Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Thu, 28 Dec 2023 15:40:05 +0100 Subject: [PATCH 023/660] Migrate getMoneyRequestInformation function --- src/libs/IOUUtils.ts | 3 +- src/libs/ReportUtils.ts | 47 +++------------ src/libs/actions/IOU.ts | 107 +++++++++++++++------------------ src/libs/actions/Policy.js | 8 +-- src/types/onyx/IOU.ts | 11 ++++ src/types/onyx/ReportAction.ts | 2 + src/types/onyx/Transaction.ts | 1 + 7 files changed, 74 insertions(+), 105 deletions(-) diff --git a/src/libs/IOUUtils.ts b/src/libs/IOUUtils.ts index edfb9e3691d0..786a91ce3227 100644 --- a/src/libs/IOUUtils.ts +++ b/src/libs/IOUUtils.ts @@ -1,4 +1,3 @@ -import {OnyxEntry} from 'react-native-onyx'; import {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; @@ -74,7 +73,7 @@ function calculateAmount(numberOfParticipants: number, total: number, currency: * * @param isDeleting - whether the user is deleting the request */ -function updateIOUOwnerAndTotal(iouReport: OnyxEntry, actorAccountID: number, amount: number, currency: string, isDeleting = false): OnyxEntry { +function updateIOUOwnerAndTotal(iouReport: Report, actorAccountID: number, amount: number, currency: string, isDeleting = false): Report { if (currency !== iouReport?.currency) { return iouReport; } diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index b72b36a5caca..68fb91c7f3e9 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -14,6 +14,7 @@ import {ParentNavigationSummaryParams, TranslationPaths} from '@src/languages/ty import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import {Beta, Login, PersonalDetails, PersonalDetailsList, Policy, Report, ReportAction, Session, Transaction} from '@src/types/onyx'; +import {Participant} from '@src/types/onyx/IOU'; import {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; import {IOUMessage, OriginalMessageActionName, OriginalMessageCreated} from '@src/types/onyx/OriginalMessage'; import {NotificationPreference} from '@src/types/onyx/Report'; @@ -60,20 +61,6 @@ type ExpenseOriginalMessage = { oldBillable?: string; }; -type Participant = { - accountID: number; - alternateText: string; - firstName: string; - icons: Icon[]; - keyForList: string; - lastName: string; - login: string; - phoneNumber: string; - searchText: string; - selected: boolean; - text: string; -}; - type SpendBreakdown = { nonReimbursableSpend: number; reimbursableSpend: number; @@ -135,27 +122,6 @@ type OptimisticIOUReportAction = Pick< | 'whisperedToAccountIDs' >; -type OptimisticReportPreview = Pick< - ReportAction, - | 'actionName' - | 'reportActionID' - | 'pendingAction' - | 'originalMessage' - | 'message' - | 'created' - | 'actorAccountID' - | 'childMoneyRequestCount' - | 'childLastMoneyRequestComment' - | 'childRecentReceiptTransactionIDs' - | 'childReportID' - | 'whisperedToAccountIDs' -> & {reportID?: string; accountID?: number}; - -type UpdateReportPreview = Pick< - ReportAction, - 'created' | 'message' | 'childLastMoneyRequestComment' | 'childMoneyRequestCount' | 'childRecentReceiptTransactionIDs' | 'whisperedToAccountIDs' ->; - type ReportRouteParams = { reportID: string; isSubReportPageRoute: boolean; @@ -2607,7 +2573,7 @@ function buildOptimisticIOUReportAction( comment: string, participants: Participant[], transactionID: string, - paymentType: DeepValueOf, + paymentType: DeepValueOf | undefined, iouReportID = '', isSettlingUp = false, isSendMoneyFlow = false, @@ -2801,7 +2767,7 @@ function buildOptimisticReportPreview( comment = '', transaction: OnyxEntry = null, childReportID?: string, -): OptimisticReportPreview { +): ReportAction { const hasReceipt = TransactionUtils.hasReceipt(transaction); const isReceiptBeingScanned = hasReceipt && TransactionUtils.isReceiptBeingScanned(transaction); const message = getReportPreviewMessage(iouReport); @@ -2812,7 +2778,7 @@ function buildOptimisticReportPreview( actionName: CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, originalMessage: { - linkedReportID: iouReport?.reportID, + linkedReportID: iouReport?.reportID ?? '', }, message: [ { @@ -2887,7 +2853,7 @@ function updateReportPreview( isPayRequest = false, comment = '', transaction: OnyxEntry = null, -): UpdateReportPreview { +): ReportAction { const hasReceipt = TransactionUtils.hasReceipt(transaction); const recentReceiptTransactions = reportPreviewAction?.childRecentReceiptTransactionIDs ?? {}; const transactionsToKeep = TransactionUtils.getRecentTransactions(recentReceiptTransactions); @@ -2904,7 +2870,8 @@ function updateReportPreview( const message = getReportPreviewMessage(iouReport, reportPreviewAction); return { - ...reportPreviewAction, + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style + ...(reportPreviewAction as ReportAction), created: DateUtils.getDBTime(), message: [ { diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index e9e6bc394b16..831859f5d9c4 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -30,7 +30,8 @@ import ROUTES from '@src/ROUTES'; import * as OnyxTypes from '@src/types/onyx'; import {Participant} from '@src/types/onyx/IOU'; import ReportAction from '@src/types/onyx/ReportAction'; -import {Comment} from '@src/types/onyx/Transaction'; +import {OnyxData} from '@src/types/onyx/Request'; +import {Comment, Receipt} from '@src/types/onyx/Transaction'; import {EmptyObject} from '@src/types/utils/EmptyObject'; import * as Policy from './Policy'; import * as Report from './Report'; @@ -43,6 +44,19 @@ type OptimisticPolicyRecentlyUsedTags = Record; type IOURequestType = ValueOf; +type MoneyRequestInformation = { + payerAccountID: number; + payerEmail: string; + iouReport: OnyxTypes.Report; + chatReport: OnyxTypes.Report; + transaction: OnyxTypes.Transaction; + iouAction: OptimisticIOUReportAction; + createdChatReportActionID: string; + createdIOUReportActionID: string; + reportPreviewAction: OnyxTypes.ReportAction; + onyxData: OnyxData; +}; + let betas: OnyxTypes.Beta[] = []; Onyx.connect({ key: ONYXKEYS.BETAS, @@ -554,63 +568,35 @@ function buildOnyxDataForMoneyRequest( /** * Gathers all the data needed to make a money request. It attempts to find existing reports, iouReports, and receipts. If it doesn't find them, then * it creates optimistic versions of them and uses those instead - * - * @param {Object} report - * @param {Object} participant - * @param {String} comment - * @param {Number} amount - * @param {String} currency - * @param {String} created - * @param {String} merchant - * @param {Number} [payeeAccountID] - * @param {String} [payeeEmail] - * @param {Object} [receipt] - * @param {String} [existingTransactionID] - * @param {String} [category] - * @param {String} [tag] - * @param {Boolean} [billable] - * @returns {Object} data - * @returns {String} data.payerEmail - * @returns {Object} data.iouReport - * @returns {Object} data.chatReport - * @returns {Object} data.transaction - * @returns {Object} data.iouAction - * @returns {Object} data.createdChatReportActionID - * @returns {Object} data.createdIOUReportActionID - * @returns {Object} data.reportPreviewAction - * @returns {Object} data.onyxData - * @returns {Object} data.onyxData.optimisticData - * @returns {Object} data.onyxData.successData - * @returns {Object} data.onyxData.failureData */ function getMoneyRequestInformation( - report, - participant, - comment, - amount, - currency, - created, - merchant, + report: OnyxTypes.Report, + participant: Participant, + comment: string, + amount: number, + currency: string, + created: string, + merchant: string, payeeAccountID = userAccountID, payeeEmail = currentUserEmail, - receipt = undefined, - existingTransactionID = undefined, - category = undefined, - tag = undefined, - billable = undefined, -) { - const payerEmail = OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login); + receipt?: Receipt, + existingTransactionID?: string, + category?: string, + tag?: string, + billable?: boolean, +): MoneyRequestInformation { + const payerEmail = OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login ?? ''); const payerAccountID = Number(participant.accountID); const isPolicyExpenseChat = participant.isPolicyExpenseChat; // STEP 1: Get existing chat report OR build a new optimistic one let isNewChatReport = false; - let chatReport = lodashGet(report, 'reportID', null) ? report : null; + let chatReport = report?.reportID ? report : null; // If this is a policyExpenseChat, the chatReport must exist and we can get it from Onyx. // report is null if the flow is initiated from the global create menu. However, participant always stores the reportID if it exists, which is the case for policyExpenseChats if (!chatReport && isPolicyExpenseChat) { - chatReport = allReports[`${ONYXKEYS.COLLECTION.REPORT}${participant.reportID}`]; + chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${participant.reportID}`] ?? null; } if (!chatReport) { @@ -625,11 +611,11 @@ function getMoneyRequestInformation( // STEP 2: Get existing IOU report and update its total OR build a new optimistic one const isNewIOUReport = !chatReport.iouReportID || ReportUtils.hasIOUWaitingOnCurrentUserBankAccount(chatReport); - let iouReport = isNewIOUReport ? null : allReports[`${ONYXKEYS.COLLECTION.REPORT}${chatReport.iouReportID}`]; + let iouReport = isNewIOUReport ? null : allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${chatReport.iouReportID}`]; // If the linked expense report on paid policy is not draft, we need to create a new draft expense report if (isPolicyExpenseChat && iouReport) { - const policyType = ReportUtils.getPolicy(iouReport.policyID).type || ''; + const policyType = ReportUtils.getPolicy(iouReport.policyID ?? '').type || ''; const isFromPaidPolicy = policyType === CONST.POLICY.TYPE.TEAM || policyType === CONST.POLICY.TYPE.CORPORATE; if (isFromPaidPolicy && !ReportUtils.isDraftExpenseReport(iouReport)) { iouReport = null; @@ -640,23 +626,25 @@ function getMoneyRequestInformation( if (isPolicyExpenseChat) { iouReport = {...iouReport}; - // Because of the Expense reports are stored as negative values, we substract the total from the amount - iouReport.total -= amount; + if (iouReport.total) { + // Because of the Expense reports are stored as negative values, we substract the total from the amount + iouReport.total -= amount; + } } else { iouReport = IOUUtils.updateIOUOwnerAndTotal(iouReport, payeeAccountID, amount, currency); } } else { iouReport = isPolicyExpenseChat - ? ReportUtils.buildOptimisticExpenseReport(chatReport.reportID, chatReport.policyID, payeeAccountID, amount, currency) + ? ReportUtils.buildOptimisticExpenseReport(chatReport.reportID, chatReport.policyID ?? '', payeeAccountID, amount, currency) : ReportUtils.buildOptimisticIOUReport(payeeAccountID, payerAccountID, amount, chatReport.reportID, currency); } // STEP 3: Build optimistic receipt and transaction - const receiptObject = {}; + const receiptObject: Receipt = {}; let filename; - if (receipt && receipt.source) { + if (receipt?.source) { receiptObject.source = receipt.source; - receiptObject.state = receipt.state || CONST.IOU.RECEIPT_STATE.SCANREADY; + receiptObject.state = receipt.state ?? CONST.IOU.RECEIPT_STATE.SCANREADY; filename = receipt.name; } let optimisticTransaction = TransactionUtils.buildOptimisticTransaction( @@ -676,9 +664,9 @@ function getMoneyRequestInformation( billable, ); - const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories(iouReport.policyID, category); + const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories(iouReport.policyID, category) as OptimisticPolicyRecentlyUsedCategories; - const optimisticPolicyRecentlyUsedTags = Policy.buildOptimisticPolicyRecentlyUsedTags(iouReport.policyID, tag); + const optimisticPolicyRecentlyUsedTags = Policy.buildOptimisticPolicyRecentlyUsedTags(iouReport.policyID, tag) as OptimisticPolicyRecentlyUsedTags; // If there is an existing transaction (which is the case for distance requests), then the data from the existing transaction // needs to be manually merged into the optimistic transaction. This is because buildOnyxDataForMoneyRequest() uses `Onyx.set()` for the transaction @@ -687,7 +675,7 @@ function getMoneyRequestInformation( // to remind me to do this. const existingTransaction = allTransactionDrafts[`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${CONST.IOU.OPTIMISTIC_TRANSACTION_ID}`]; if (existingTransaction && existingTransaction.iouRequestType === CONST.IOU.REQUEST_TYPE.DISTANCE) { - optimisticTransaction = OnyxUtils.fastMerge(existingTransaction, optimisticTransaction); + optimisticTransaction = OnyxUtils.fastMerge(existingTransaction, optimisticTransaction) as OnyxTypes.Transaction; } // STEP 4: Build optimistic reportActions. We need: @@ -706,7 +694,7 @@ function getMoneyRequestInformation( comment, [participant], optimisticTransaction.transactionID, - '', + undefined, iouReport.reportID, false, false, @@ -733,6 +721,7 @@ function getMoneyRequestInformation( [payerAccountID]: { accountID: payerAccountID, avatar: UserUtils.getDefaultAvatarURL(payerAccountID), + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing displayName: LocalePhoneNumber.formatPhoneNumber(participant.displayName || payerEmail), login: participant.login, isOptimisticPersonalDetail: true, @@ -763,8 +752,8 @@ function getMoneyRequestInformation( chatReport, transaction: optimisticTransaction, iouAction, - createdChatReportActionID: isNewChatReport ? optimisticCreatedActionForChat.reportActionID : 0, - createdIOUReportActionID: isNewIOUReport ? optimisticCreatedActionForIOU.reportActionID : 0, + createdChatReportActionID: isNewChatReport ? optimisticCreatedActionForChat.reportActionID : '', + createdIOUReportActionID: isNewIOUReport ? optimisticCreatedActionForIOU.reportActionID : '', reportPreviewAction, onyxData: { optimisticData, diff --git a/src/libs/actions/Policy.js b/src/libs/actions/Policy.js index f33e6637e2de..0ff1b75315c7 100644 --- a/src/libs/actions/Policy.js +++ b/src/libs/actions/Policy.js @@ -1475,8 +1475,8 @@ function dismissAddedWithPrimaryLoginMessages(policyID) { } /** - * @param {String} policyID - * @param {String} category + * @param {String | undefined} policyID + * @param {String | undefined} category * @returns {Object} */ function buildOptimisticPolicyRecentlyUsedCategories(policyID, category) { @@ -1490,8 +1490,8 @@ function buildOptimisticPolicyRecentlyUsedCategories(policyID, category) { } /** - * @param {String} policyID - * @param {String} tag + * @param {String | undefined} policyID + * @param {String | undefined} tag * @returns {Object} */ function buildOptimisticPolicyRecentlyUsedTags(policyID, tag) { diff --git a/src/types/onyx/IOU.ts b/src/types/onyx/IOU.ts index e6a0d861e989..4558e5a99c08 100644 --- a/src/types/onyx/IOU.ts +++ b/src/types/onyx/IOU.ts @@ -1,3 +1,5 @@ +import {Icon} from './OnyxCommon'; + type Participant = { accountID: number; login?: string; @@ -5,6 +7,15 @@ type Participant = { isOwnPolicyExpenseChat?: boolean; selected?: boolean; reportID?: string; + displayName?: string; + alternateText: string; + firstName: string; + icons: Icon[]; + keyForList: string; + lastName: string; + phoneNumber: string; + searchText: string; + text: string; }; type IOU = { diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index a881b63fbb95..a9adc66a7806 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -139,6 +139,8 @@ type ReportActionBase = { /** Type of child report */ childType?: string; + accountID?: number; + childOldestFourEmails?: string; childOldestFourAccountIDs?: string; childCommenterCount?: number; diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 34068a6d12d9..a40acda79c91 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -39,6 +39,7 @@ type Geometry = { type Receipt = { receiptID?: number; path?: string; + name?: string; source?: string; state?: ValueOf; }; From 19035e5ab44357977b65cbdab8338f119c77a7ac Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Thu, 28 Dec 2023 15:47:31 +0100 Subject: [PATCH 024/660] Fix failing Money request --- src/libs/actions/IOU.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 831859f5d9c4..04e07ef4b2ef 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -51,8 +51,8 @@ type MoneyRequestInformation = { chatReport: OnyxTypes.Report; transaction: OnyxTypes.Transaction; iouAction: OptimisticIOUReportAction; - createdChatReportActionID: string; - createdIOUReportActionID: string; + createdChatReportActionID: string | number; + createdIOUReportActionID: string | number; reportPreviewAction: OnyxTypes.ReportAction; onyxData: OnyxData; }; @@ -752,8 +752,8 @@ function getMoneyRequestInformation( chatReport, transaction: optimisticTransaction, iouAction, - createdChatReportActionID: isNewChatReport ? optimisticCreatedActionForChat.reportActionID : '', - createdIOUReportActionID: isNewIOUReport ? optimisticCreatedActionForIOU.reportActionID : '', + createdChatReportActionID: isNewChatReport ? optimisticCreatedActionForChat.reportActionID : 0, + createdIOUReportActionID: isNewIOUReport ? optimisticCreatedActionForIOU.reportActionID : 0, reportPreviewAction, onyxData: { optimisticData, From ad41a6053698949e0f5c08bfb2805ffbc0cacc54 Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Thu, 28 Dec 2023 17:43:37 +0100 Subject: [PATCH 025/660] Migrate createDistanceRequest and requestMoney functions --- src/libs/ReportUtils.ts | 2 +- src/libs/actions/IOU.ts | 199 +++++++++++++++++++--------------- src/types/onyx/Transaction.ts | 3 +- 3 files changed, 114 insertions(+), 90 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 68fb91c7f3e9..57c5d7b8fc42 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -974,7 +974,7 @@ function isOneOnOneChat(report: OnyxEntry): boolean { /** * Get the report given a reportID */ -function getReport(reportID: string | undefined): OnyxEntry | EmptyObject { +function getReport(reportID: string | undefined): Report | EmptyObject { /** * Using typical string concatenation here due to performance issues * with template literals. diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 04e07ef4b2ef..017f51c4e510 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -31,8 +31,8 @@ import * as OnyxTypes from '@src/types/onyx'; import {Participant} from '@src/types/onyx/IOU'; import ReportAction from '@src/types/onyx/ReportAction'; import {OnyxData} from '@src/types/onyx/Request'; -import {Comment, Receipt} from '@src/types/onyx/Transaction'; -import {EmptyObject} from '@src/types/utils/EmptyObject'; +import {Comment, Receipt, WaypointCollection} from '@src/types/onyx/Transaction'; +import {EmptyObject, isEmptyObject} from '@src/types/utils/EmptyObject'; import * as Policy from './Policy'; import * as Report from './Report'; @@ -570,7 +570,7 @@ function buildOnyxDataForMoneyRequest( * it creates optimistic versions of them and uses those instead */ function getMoneyRequestInformation( - report: OnyxTypes.Report, + report: OnyxTypes.Report | EmptyObject, participant: Participant, comment: string, amount: number, @@ -591,7 +591,7 @@ function getMoneyRequestInformation( // STEP 1: Get existing chat report OR build a new optimistic one let isNewChatReport = false; - let chatReport = report?.reportID ? report : null; + let chatReport = !isEmptyObject(report) && report?.reportID ? report : null; // If this is a policyExpenseChat, the chatReport must exist and we can get it from Onyx. // report is null if the flow is initiated from the global create menu. However, participant always stores the reportID if it exists, which is the case for policyExpenseChats @@ -765,25 +765,25 @@ function getMoneyRequestInformation( /** * Requests money based on a distance (eg. mileage from a map) - * - * @param {Object} report - * @param {Object} participant - * @param {String} comment - * @param {String} created - * @param {String} [category] - * @param {String} [tag] - * @param {Number} amount - * @param {String} currency - * @param {String} merchant - * @param {Boolean} [billable] - * @param {Obejct} validWaypoints */ -function createDistanceRequest(report, participant, comment, created, category, tag, amount, currency, merchant, billable, validWaypoints) { +function createDistanceRequest( + report: OnyxTypes.Report, + participant: Participant, + comment: string, + created: string, + category: string | undefined, + tag: string | undefined, + amount: number, + currency: string, + merchant: string, + billable: boolean | undefined, + validWaypoints: WaypointCollection, +) { // If the report is an iou or expense report, we should get the linked chat report to be passed to the getMoneyRequestInformation function const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); const currentChatReport = isMoneyRequestReport ? ReportUtils.getReport(report.chatReportID) : report; - const optimisticReceipt = { + const optimisticReceipt: Receipt = { source: ReceiptGeneric, state: CONST.IOU.RECEIPT_STATE.OPEN, }; @@ -803,25 +803,40 @@ function createDistanceRequest(report, participant, comment, created, category, tag, billable, ); - API.write( - 'CreateDistanceRequest', - { - comment, - iouReportID: iouReport.reportID, - chatReportID: chatReport.reportID, - transactionID: transaction.transactionID, - reportActionID: iouAction.reportActionID, - createdChatReportActionID, - createdIOUReportActionID, - reportPreviewReportActionID: reportPreviewAction.reportActionID, - waypoints: JSON.stringify(validWaypoints), - created, - category, - tag, - billable, - }, - onyxData, - ); + + type CreateDistanceRequestParams = { + comment: string; + iouReportID: string; + chatReportID: string; + transactionID: string; + reportActionID: string; + createdChatReportActionID: string | number; + createdIOUReportActionID: string | number; + reportPreviewReportActionID: string; + waypoints: string; + created: string; + category?: string; + tag?: string; + billable?: boolean; + }; + + const parameters: CreateDistanceRequestParams = { + comment, + iouReportID: iouReport.reportID, + chatReportID: chatReport.reportID, + transactionID: transaction.transactionID, + reportActionID: iouAction.reportActionID, + createdChatReportActionID, + createdIOUReportActionID, + reportPreviewReportActionID: reportPreviewAction.reportActionID, + waypoints: JSON.stringify(validWaypoints), + created, + category, + tag, + billable, + }; + + API.write('CreateDistanceRequest', parameters, onyxData); Navigation.dismissModal(isMoneyRequestReport ? report.reportID : chatReport.reportID); Report.notifyNewAction(chatReport.reportID, userAccountID); } @@ -1039,34 +1054,22 @@ function updateDistanceRequest(transactionID, transactionThreadReportID, transac /** * Request money from another user * - * @param {Object} report - * @param {Number} amount - always in the smallest unit of the currency - * @param {String} currency - * @param {String} created - * @param {String} merchant - * @param {String} payeeEmail - * @param {Number} payeeAccountID - * @param {Object} participant - * @param {String} comment - * @param {Object} [receipt] - * @param {String} [category] - * @param {String} [tag] - * @param {Boolean} [billable] + * @param amount - always in the smallest unit of the currency */ function requestMoney( - report, - amount, - currency, - created, - merchant, - payeeEmail, - payeeAccountID, - participant, - comment, - receipt = undefined, - category = undefined, - tag = undefined, - billable = undefined, + report: OnyxTypes.Report, + amount: number, + currency: string, + created: string, + merchant: string, + payeeEmail: string, + payeeAccountID: number, + participant: Participant, + comment: string, + receipt: Receipt, + category?: string, + tag?: string, + billable?: boolean, ) { // If the report is iou or expense report, we should get the linked chat report to be passed to the getMoneyRequestInformation function const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); @@ -1075,31 +1078,51 @@ function requestMoney( getMoneyRequestInformation(currentChatReport, participant, comment, amount, currency, created, merchant, payeeAccountID, payeeEmail, receipt, undefined, category, tag, billable); const activeReportID = isMoneyRequestReport ? report.reportID : chatReport.reportID; - API.write( - 'RequestMoney', - { - debtorEmail: payerEmail, - debtorAccountID: payerAccountID, - amount, - currency, - comment, - created, - merchant, - iouReportID: iouReport.reportID, - chatReportID: chatReport.reportID, - transactionID: transaction.transactionID, - reportActionID: iouAction.reportActionID, - createdChatReportActionID, - createdIOUReportActionID, - reportPreviewReportActionID: reportPreviewAction.reportActionID, - receipt, - receiptState: lodashGet(receipt, 'state'), - category, - tag, - billable, - }, - onyxData, - ); + type RequestMoneyParams = { + debtorEmail: string; + debtorAccountID: number; + amount: number; + currency: string; + comment: string; + created: string; + merchant: string; + iouReportID: string; + chatReportID: string; + transactionID: string; + reportActionID: string; + createdChatReportActionID: string | number; + createdIOUReportActionID: string | number; + reportPreviewReportActionID: string; + receipt: Receipt; + receiptState?: ValueOf; + category?: string; + tag?: string; + billable?: boolean; + }; + + const parameters: RequestMoneyParams = { + debtorEmail: payerEmail, + debtorAccountID: payerAccountID, + amount, + currency, + comment, + created, + merchant, + iouReportID: iouReport.reportID, + chatReportID: chatReport.reportID, + transactionID: transaction.transactionID, + reportActionID: iouAction.reportActionID, + createdChatReportActionID, + createdIOUReportActionID, + reportPreviewReportActionID: reportPreviewAction.reportActionID, + receipt, + receiptState: receipt?.state, + category, + tag, + billable, + }; + + API.write('RequestMoney', parameters, onyxData); resetMoneyRequestInfo(); Navigation.dismissModal(activeReportID); Report.notifyNewAction(activeReportID, payeeAccountID); diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index a40acda79c91..126a12bcb3f8 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -1,3 +1,4 @@ +import {ImageSourcePropType} from 'react-native'; import {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import {Participant} from './IOU'; @@ -40,7 +41,7 @@ type Receipt = { receiptID?: number; path?: string; name?: string; - source?: string; + source?: ImageSourcePropType; state?: ValueOf; }; From 578cc507525b69a9661df8c395179be9caf44e37 Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Fri, 29 Dec 2023 13:10:32 +0100 Subject: [PATCH 026/660] Migrate startSplitBill function --- src/libs/ReportUtils.ts | 2 + src/libs/TransactionUtils.ts | 6 +- src/libs/actions/IOU.ts | 120 ++++++++++++++++++++-------------- src/types/onyx/IOU.ts | 1 + src/types/onyx/Transaction.ts | 20 +++++- 5 files changed, 95 insertions(+), 54 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 57c5d7b8fc42..93c4f5870720 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -159,6 +159,7 @@ type OptimisticChatReport = Pick< Report, | 'type' | 'chatType' + | 'chatReportID' | 'isOwnPolicyExpenseChat' | 'isPinned' | 'lastActorAccountID' @@ -170,6 +171,7 @@ type OptimisticChatReport = Pick< | 'notificationPreference' | 'oldPolicyName' | 'ownerAccountID' + | 'pendingFields' | 'parentReportActionID' | 'parentReportID' | 'participantAccountIDs' diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 6905a542fa5b..e3d36484056a 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -4,16 +4,12 @@ import {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {RecentWaypoint, ReportAction, Transaction} from '@src/types/onyx'; -import {Comment, Receipt, Waypoint, WaypointCollection} from '@src/types/onyx/Transaction'; +import {Comment, Receipt, TransactionChanges, Waypoint, WaypointCollection} from '@src/types/onyx/Transaction'; import {EmptyObject} from '@src/types/utils/EmptyObject'; import {isCorporateCard, isExpensifyCard} from './CardUtils'; import DateUtils from './DateUtils'; import * as NumberUtils from './NumberUtils'; -type AdditionalTransactionChanges = {comment?: string; waypoints?: WaypointCollection}; - -type TransactionChanges = Partial & AdditionalTransactionChanges; - let allTransactions: OnyxCollection = {}; Onyx.connect({ diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 017f51c4e510..8d016fbda0f9 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -31,7 +31,7 @@ import * as OnyxTypes from '@src/types/onyx'; import {Participant} from '@src/types/onyx/IOU'; import ReportAction from '@src/types/onyx/ReportAction'; import {OnyxData} from '@src/types/onyx/Request'; -import {Comment, Receipt, WaypointCollection} from '@src/types/onyx/Transaction'; +import {Comment, Receipt, Split, WaypointCollection} from '@src/types/onyx/Transaction'; import {EmptyObject, isEmptyObject} from '@src/types/utils/EmptyObject'; import * as Policy from './Policy'; import * as Report from './Report'; @@ -1573,27 +1573,29 @@ function splitBillAndOpenReport(participants, currentUserLogin, currentUserAccou /** Used exclusively for starting a split bill request that contains a receipt, the split request will be completed once the receipt is scanned * or user enters details manually. * - * @param {Array} participants - * @param {String} currentUserLogin - * @param {Number} currentUserAccountID - * @param {String} comment - * @param {String} category - * @param {String} tag - * @param {Object} receipt - * @param {String} existingSplitChatReportID - Either a group DM or a workspace chat + * @param existingSplitChatReportID - Either a group DM or a workspace chat */ -function startSplitBill(participants, currentUserLogin, currentUserAccountID, comment, category, tag, receipt, existingSplitChatReportID = '') { +function startSplitBill( + participants: Participant[], + currentUserLogin: string, + currentUserAccountID: number, + comment: string, + category: string, + tag: string, + receipt: Receipt, + existingSplitChatReportID = '', +) { const currentUserEmailForIOUSplit = OptionsListUtils.addSMSDomainIfPhoneNumber(currentUserLogin); - const participantAccountIDs = _.map(participants, (participant) => Number(participant.accountID)); + const participantAccountIDs = participants.map((participant) => Number(participant.accountID)); const existingSplitChatReport = existingSplitChatReportID || participants[0].reportID - ? allReports[`${ONYXKEYS.COLLECTION.REPORT}${existingSplitChatReportID || participants[0].reportID}`] + ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${existingSplitChatReportID || participants[0].reportID}`] : ReportUtils.getChatByParticipants(participantAccountIDs); - const splitChatReport = existingSplitChatReport || ReportUtils.buildOptimisticChatReport(participantAccountIDs); - const isOwnPolicyExpenseChat = splitChatReport.isOwnPolicyExpenseChat || false; + const splitChatReport = existingSplitChatReport ?? ReportUtils.buildOptimisticChatReport(participantAccountIDs); + const isOwnPolicyExpenseChat = !!splitChatReport.isOwnPolicyExpenseChat || false; const {name: filename, source, state = CONST.IOU.RECEIPT_STATE.SCANREADY} = receipt; - const receiptObject = {state, source}; + const receiptObject: Receipt = {state, source}; // ReportID is -2 (aka "deleted") on the group transaction const splitTransaction = TransactionUtils.buildOptimisticTransaction( @@ -1618,7 +1620,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co comment, participants, splitTransaction.transactionID, - '', + undefined, '', false, false, @@ -1627,8 +1629,8 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co ); splitChatReport.lastReadTime = DateUtils.getDBTime(); - splitChatReport.lastMessageText = splitIOUReportAction.message[0].text; - splitChatReport.lastMessageHtml = splitIOUReportAction.message[0].html; + splitChatReport.lastMessageText = splitIOUReportAction.message?.[0].text; + splitChatReport.lastMessageHtml = splitIOUReportAction.message?.[0].html; // If we have an existing splitChatReport (group chat or workspace) use it's pending fields, otherwise indicate that we are adding a chat if (!existingSplitChatReport) { @@ -1637,7 +1639,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co }; } - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { // Use 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 @@ -1645,14 +1647,22 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co key: `${ONYXKEYS.COLLECTION.REPORT}${splitChatReport.reportID}`, value: splitChatReport, }, - { - onyxMethod: existingSplitChatReport ? Onyx.METHOD.MERGE : Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, - value: { - ...(existingSplitChatReport ? {} : {[splitChatCreatedReportAction.reportActionID]: splitChatCreatedReportAction}), - [splitIOUReportAction.reportActionID]: splitIOUReportAction, - }, - }, + existingSplitChatReport + ? { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, + value: { + [splitIOUReportAction.reportActionID]: splitIOUReportAction as OnyxTypes.ReportAction, + }, + } + : { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, + value: { + [splitChatCreatedReportAction.reportActionID]: splitChatCreatedReportAction, + [splitIOUReportAction.reportActionID]: splitIOUReportAction as OnyxTypes.ReportAction, + }, + }, { onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${splitTransaction.transactionID}`, @@ -1660,7 +1670,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co }, ]; - const successData = [ + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, @@ -1684,7 +1694,7 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co }); } - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${splitTransaction.transactionID}`, @@ -1730,9 +1740,10 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co ); } - const splits = [{email: currentUserEmailForIOUSplit, accountID: currentUserAccountID}]; + const splits: Split[] = [{email: currentUserEmailForIOUSplit, accountID: currentUserAccountID}]; - _.each(participants, (participant) => { + participants.forEach((participant) => { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const email = participant.isOwnPolicyExpenseChat ? '' : OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login || participant.text).toLowerCase(); const accountID = participant.isOwnPolicyExpenseChat ? 0 : Number(participant.accountID); if (email === currentUserEmailForIOUSplit) { @@ -1757,7 +1768,9 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co [accountID]: { accountID, avatar: UserUtils.getDefaultAvatarURL(accountID), + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing displayName: LocalePhoneNumber.formatPhoneNumber(participant.displayName || email), + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing login: participant.login || participant.text, isOptimisticPersonalDetail: true, }, @@ -1782,26 +1795,37 @@ function startSplitBill(participants, currentUserLogin, currentUserAccountID, co }, }); - API.write( - 'StartSplitBill', - { - chatReportID: splitChatReport.reportID, - reportActionID: splitIOUReportAction.reportActionID, - transactionID: splitTransaction.transactionID, - splits: JSON.stringify(splits), - receipt, - comment, - category, - tag, - isFromGroupDM: !existingSplitChatReport, - ...(existingSplitChatReport ? {} : {createdReportActionID: splitChatCreatedReportAction.reportActionID}), - }, - {optimisticData, successData, failureData}, - ); + type StartSplitBillParams = { + chatReportID: string; + reportActionID: string; + transactionID: string; + splits: string; + receipt: Receipt; + comment: string; + category: string; + tag: string; + isFromGroupDM: boolean; + createdReportActionID?: string; + }; + + const parameters: StartSplitBillParams = { + chatReportID: splitChatReport.reportID, + reportActionID: splitIOUReportAction.reportActionID, + transactionID: splitTransaction.transactionID, + splits: JSON.stringify(splits), + receipt, + comment, + category, + tag, + isFromGroupDM: !existingSplitChatReport, + ...(existingSplitChatReport ? {} : {createdReportActionID: splitChatCreatedReportAction.reportActionID}), + }; + + API.write('StartSplitBill', parameters, {optimisticData, successData, failureData}); resetMoneyRequestInfo(); Navigation.dismissModal(splitChatReport.reportID); - Report.notifyNewAction(splitChatReport.chatReportID, currentUserAccountID); + Report.notifyNewAction(splitChatReport.chatReportID ?? '', currentUserAccountID); } /** Used for editing a split bill while it's still scanning or when SmartScan fails, it completes a split bill started by startSplitBill above. diff --git a/src/types/onyx/IOU.ts b/src/types/onyx/IOU.ts index 4558e5a99c08..98d04fb2ea9e 100644 --- a/src/types/onyx/IOU.ts +++ b/src/types/onyx/IOU.ts @@ -7,6 +7,7 @@ type Participant = { isOwnPolicyExpenseChat?: boolean; selected?: boolean; reportID?: string; + policyID?: string; displayName?: string; alternateText: string; firstName: string; diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 126a12bcb3f8..d7acd8851341 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -20,6 +20,19 @@ type Waypoint = { }; type WaypointCollection = Record; + +type UserSplit = { + email: string; + accountID: number; +}; + +type WorkspaceSplit = { + policyID?: string; + chatReportID: string; +}; + +type Split = UserSplit | WorkspaceSplit; + type Comment = { comment?: string; waypoints?: WaypointCollection; @@ -28,6 +41,7 @@ type Comment = { customUnit?: Record; source?: string; originalTransactionID?: string; + splits?: Split[]; }; type GeometryType = 'LineString'; @@ -104,5 +118,9 @@ type TransactionDraft = Partial & { isFromGlobalCreate?: boolean; }; +type AdditionalTransactionChanges = {comment?: string; waypoints?: WaypointCollection}; + +type TransactionChanges = Partial & AdditionalTransactionChanges; + export default Transaction; -export type {WaypointCollection, Comment, Receipt, Waypoint, TransactionDraft}; +export type {WaypointCollection, Comment, Receipt, Waypoint, TransactionDraft, TransactionChanges, Split}; From 8f0418d24e9e603a8d9c11d7fe0dfd993297afd4 Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Fri, 29 Dec 2023 16:32:29 +0100 Subject: [PATCH 027/660] Replace Split type with Participant --- src/libs/actions/IOU.ts | 11 ++++++----- src/types/onyx/IOU.ts | 20 +++++++++++--------- src/types/onyx/Transaction.ts | 16 ++-------------- 3 files changed, 19 insertions(+), 28 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 07a172192021..193793744548 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3,6 +3,7 @@ import {format} from 'date-fns'; import Str from 'expensify-common/lib/str'; import lodashGet from 'lodash/get'; import lodashHas from 'lodash/has'; +import {ImageSourcePropType} from 'react-native'; import Onyx, {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import OnyxUtils from 'react-native-onyx/lib/utils'; import {ValueOf} from 'type-fest'; @@ -31,7 +32,7 @@ import * as OnyxTypes from '@src/types/onyx'; import {Participant} from '@src/types/onyx/IOU'; import ReportAction from '@src/types/onyx/ReportAction'; import {OnyxData} from '@src/types/onyx/Request'; -import {Comment, Receipt, Split, WaypointCollection} from '@src/types/onyx/Transaction'; +import {Comment, Receipt, WaypointCollection} from '@src/types/onyx/Transaction'; import {EmptyObject, isEmptyObject} from '@src/types/utils/EmptyObject'; import * as Policy from './Policy'; import * as Report from './Report'; @@ -243,7 +244,7 @@ function setMoneyRequestParticipants_temporaryForRefactor(transactionID: string, Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {participants}); } -function setMoneyRequestReceipt_temporaryForRefactor(transactionID: string, source: string, filename: string) { +function setMoneyRequestReceipt_temporaryForRefactor(transactionID: string, source: ImageSourcePropType, filename: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {receipt: {source}, filename}); } @@ -1741,11 +1742,11 @@ function startSplitBill( ); } - const splits: Split[] = [{email: currentUserEmailForIOUSplit, accountID: currentUserAccountID}]; + const splits: Participant[] = [{email: currentUserEmailForIOUSplit, accountID: currentUserAccountID}]; participants.forEach((participant) => { // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const email = participant.isOwnPolicyExpenseChat ? '' : OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login || participant.text).toLowerCase(); + const email = participant.isOwnPolicyExpenseChat ? '' : OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login || participant.text || '').toLowerCase(); const accountID = participant.isOwnPolicyExpenseChat ? 0 : Number(participant.accountID); if (email === currentUserEmailForIOUSplit) { return; @@ -1760,7 +1761,7 @@ function startSplitBill( return; } - const participantPersonalDetails = allPersonalDetails[participant.accountID]; + const participantPersonalDetails = allPersonalDetails[participant?.accountID ?? -1]; if (!participantPersonalDetails) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, diff --git a/src/types/onyx/IOU.ts b/src/types/onyx/IOU.ts index 98d04fb2ea9e..5647bd31d3ef 100644 --- a/src/types/onyx/IOU.ts +++ b/src/types/onyx/IOU.ts @@ -1,22 +1,24 @@ import {Icon} from './OnyxCommon'; type Participant = { - accountID: number; + accountID?: number; login?: string; + email?: string; isPolicyExpenseChat?: boolean; + chatReportID?: string; isOwnPolicyExpenseChat?: boolean; selected?: boolean; reportID?: string; policyID?: string; displayName?: string; - alternateText: string; - firstName: string; - icons: Icon[]; - keyForList: string; - lastName: string; - phoneNumber: string; - searchText: string; - text: string; + alternateText?: string; + firstName?: string; + icons?: Icon[]; + keyForList?: string; + lastName?: string; + phoneNumber?: string; + searchText?: string; + text?: string; }; type IOU = { diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index d7acd8851341..ad8b2e20d1d6 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -21,18 +21,6 @@ type Waypoint = { type WaypointCollection = Record; -type UserSplit = { - email: string; - accountID: number; -}; - -type WorkspaceSplit = { - policyID?: string; - chatReportID: string; -}; - -type Split = UserSplit | WorkspaceSplit; - type Comment = { comment?: string; waypoints?: WaypointCollection; @@ -41,7 +29,7 @@ type Comment = { customUnit?: Record; source?: string; originalTransactionID?: string; - splits?: Split[]; + splits?: Participant[]; }; type GeometryType = 'LineString'; @@ -123,4 +111,4 @@ type AdditionalTransactionChanges = {comment?: string; waypoints?: WaypointColle type TransactionChanges = Partial & AdditionalTransactionChanges; export default Transaction; -export type {WaypointCollection, Comment, Receipt, Waypoint, TransactionDraft, TransactionChanges, Split}; +export type {WaypointCollection, Comment, Receipt, Waypoint, TransactionDraft, TransactionChanges}; From 417e173cb1022398210c0b508f0e89594f5a74dc Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Fri, 29 Dec 2023 16:58:57 +0100 Subject: [PATCH 028/660] Migrate completeSplitBill function --- src/libs/ReportUtils.ts | 4 +- src/libs/actions/IOU.ts | 124 ++++++++++++++++++++++------------------ src/types/onyx/IOU.ts | 6 ++ 3 files changed, 76 insertions(+), 58 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 1e71d58563b3..791a64312589 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2644,8 +2644,8 @@ function buildOptimisticIOUReportAction( originalMessage.participantAccountIDs = currentUserAccountID ? [currentUserAccountID] : []; } else { originalMessage.participantAccountIDs = currentUserAccountID - ? [currentUserAccountID, ...participants.map((participant) => participant.accountID)] - : participants.map((participant) => participant.accountID); + ? [currentUserAccountID, ...participants.map((participant) => participant.accountID ?? -1)] + : participants.map((participant) => participant.accountID ?? -1); } } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 193793744548..c745a7b0b865 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1832,19 +1832,19 @@ function startSplitBill( /** Used for editing a split bill while it's still scanning or when SmartScan fails, it completes a split bill started by startSplitBill above. * - * @param {number} chatReportID - The group chat or workspace reportID - * @param {Object} reportAction - The split action that lives in the chatReport above - * @param {Object} updatedTransaction - The updated **draft** split transaction - * @param {Number} sessionAccountID - accountID of the current user - * @param {String} sessionEmail - email of the current user + * @param chatReportID - The group chat or workspace reportID + * @param reportAction - The split action that lives in the chatReport above + * @param updatedTransaction - The updated **draft** split transaction + * @param sessionAccountID - accountID of the current user + * @param sessionEmail - email of the current user */ -function completeSplitBill(chatReportID, reportAction, updatedTransaction, sessionAccountID, sessionEmail) { +function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportAction, updatedTransaction: OnyxTypes.Transaction, sessionAccountID: number, sessionEmail: string) { const currentUserEmailForIOUSplit = OptionsListUtils.addSMSDomainIfPhoneNumber(sessionEmail); const {transactionID} = updatedTransaction; const unmodifiedTransaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; // Save optimistic updated transaction and action - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, @@ -1867,7 +1867,7 @@ function completeSplitBill(chatReportID, reportAction, updatedTransaction, sessi }, ]; - const successData = [ + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, @@ -1876,11 +1876,11 @@ function completeSplitBill(chatReportID, reportAction, updatedTransaction, sessi { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`, - value: null, + value: {pendingAction: null}, }, ]; - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, @@ -1901,25 +1901,25 @@ function completeSplitBill(chatReportID, reportAction, updatedTransaction, sessi }, ]; - const splitParticipants = updatedTransaction.comment.splits; + const splitParticipants = updatedTransaction.comment.splits ?? []; const {modifiedAmount: amount, modifiedCurrency: currency} = updatedTransaction; // Exclude the current user when calculating the split amount, `calculateAmount` takes it into account - const splitAmount = IOUUtils.calculateAmount(splitParticipants.length - 1, amount, currency, false); + const splitAmount = IOUUtils.calculateAmount(splitParticipants.length - 1, amount ?? 0, currency ?? '', false); - const splits = [{email: currentUserEmailForIOUSplit}]; - _.each(splitParticipants, (participant) => { + const splits: Participant[] = [{email: currentUserEmailForIOUSplit}]; + splitParticipants.forEach((participant) => { // Skip creating the transaction for the current user if (participant.email === currentUserEmailForIOUSplit) { return; } - const isPolicyExpenseChat = !_.isEmpty(participant.policyID); + const isPolicyExpenseChat = !!participant.policyID; if (!isPolicyExpenseChat) { // In case this is still the optimistic accountID saved in the splits array, return early as we cannot know // if there is an existing chat between the split creator and this participant // Instead, we will rely on Auth generating the report IDs and the user won't see any optimistic chats or reports created - const participantPersonalDetails = allPersonalDetails[participant.accountID] || {}; + const participantPersonalDetails: OnyxTypes.PersonalDetails | EmptyObject = allPersonalDetails[participant?.accountID ?? -1] || {}; if (!participantPersonalDetails || participantPersonalDetails.isOptimisticPersonalDetail) { splits.push({ email: participant.email, @@ -1928,36 +1928,38 @@ function completeSplitBill(chatReportID, reportAction, updatedTransaction, sessi } } - let oneOnOneChatReport; + let oneOnOneChatReport: OnyxTypes.Report | null; let isNewOneOnOneChatReport = false; if (isPolicyExpenseChat) { // The workspace chat reportID is saved in the splits array when starting a split bill with a workspace - oneOnOneChatReport = allReports[`${ONYXKEYS.COLLECTION.REPORT}${participant.chatReportID}`]; + oneOnOneChatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${participant.chatReportID}`] ?? null; } else { - const existingChatReport = ReportUtils.getChatByParticipants([participant.accountID]); + const existingChatReport = ReportUtils.getChatByParticipants(participant.accountID ? [participant.accountID] : []); isNewOneOnOneChatReport = !existingChatReport; - oneOnOneChatReport = existingChatReport || ReportUtils.buildOptimisticChatReport([participant.accountID]); + oneOnOneChatReport = existingChatReport || ReportUtils.buildOptimisticChatReport(participant.accountID ? [participant.accountID] : []); } - let oneOnOneIOUReport = oneOnOneChatReport.iouReportID ? lodashGet(allReports, `${ONYXKEYS.COLLECTION.REPORT}${oneOnOneChatReport.iouReportID}`, undefined) : undefined; + let oneOnOneIOUReport = oneOnOneChatReport?.iouReportID ? lodashGet(allReports, `${ONYXKEYS.COLLECTION.REPORT}${oneOnOneChatReport.iouReportID}`, undefined) : undefined; const shouldCreateNewOneOnOneIOUReport = _.isUndefined(oneOnOneIOUReport) || (isPolicyExpenseChat && ReportUtils.isControlPolicyExpenseReport(oneOnOneIOUReport) && ReportUtils.isReportApproved(oneOnOneIOUReport)); if (shouldCreateNewOneOnOneIOUReport) { oneOnOneIOUReport = isPolicyExpenseChat - ? ReportUtils.buildOptimisticExpenseReport(oneOnOneChatReport.reportID, participant.policyID, sessionAccountID, splitAmount, currency) - : ReportUtils.buildOptimisticIOUReport(sessionAccountID, participant.accountID, splitAmount, oneOnOneChatReport.reportID, currency); + ? ReportUtils.buildOptimisticExpenseReport(oneOnOneChatReport?.reportID ?? '', participant.policyID ?? '', sessionAccountID, splitAmount, currency ?? '') + : ReportUtils.buildOptimisticIOUReport(sessionAccountID, participant.accountID ?? -1, splitAmount, oneOnOneChatReport?.reportID ?? '', currency ?? ''); } else if (isPolicyExpenseChat) { - // Because of the Expense reports are stored as negative values, we subtract the total from the amount - oneOnOneIOUReport.total -= splitAmount; + if (oneOnOneIOUReport?.total) { + // Because of the Expense reports are stored as negative values, we subtract the total from the amount + oneOnOneIOUReport.total -= splitAmount; + } } else { - oneOnOneIOUReport = IOUUtils.updateIOUOwnerAndTotal(oneOnOneIOUReport, sessionAccountID, splitAmount, currency); + oneOnOneIOUReport = IOUUtils.updateIOUOwnerAndTotal(oneOnOneIOUReport as OnyxTypes.Report, sessionAccountID, splitAmount, currency ?? ''); } const oneOnOneTransaction = TransactionUtils.buildOptimisticTransaction( isPolicyExpenseChat ? -splitAmount : splitAmount, - currency, - oneOnOneIOUReport.reportID, + currency ?? '', + oneOnOneIOUReport?.reportID ?? '', updatedTransaction.comment.comment, updatedTransaction.modifiedCreated, CONST.IOU.TYPE.SPLIT, @@ -1972,31 +1974,31 @@ function completeSplitBill(chatReportID, reportAction, updatedTransaction, sessi const oneOnOneIOUAction = ReportUtils.buildOptimisticIOUReportAction( CONST.IOU.REPORT_ACTION_TYPE.CREATE, splitAmount, - currency, - updatedTransaction.comment.comment, + currency ?? '', + updatedTransaction.comment.comment ?? '', [participant], oneOnOneTransaction.transactionID, - '', - oneOnOneIOUReport.reportID, + undefined, + oneOnOneIOUReport?.reportID, ); - let oneOnOneReportPreviewAction = ReportActionsUtils.getReportPreviewAction(oneOnOneChatReport.reportID, oneOnOneIOUReport.reportID); + let oneOnOneReportPreviewAction = ReportActionsUtils.getReportPreviewAction(oneOnOneChatReport?.reportID ?? '', oneOnOneIOUReport?.reportID ?? ''); if (oneOnOneReportPreviewAction) { - oneOnOneReportPreviewAction = ReportUtils.updateReportPreview(oneOnOneIOUReport, oneOnOneReportPreviewAction); + oneOnOneReportPreviewAction = ReportUtils.updateReportPreview(oneOnOneIOUReport ?? null, oneOnOneReportPreviewAction); } else { - oneOnOneReportPreviewAction = ReportUtils.buildOptimisticReportPreview(oneOnOneChatReport, oneOnOneIOUReport, '', oneOnOneTransaction); + oneOnOneReportPreviewAction = ReportUtils.buildOptimisticReportPreview(oneOnOneChatReport, oneOnOneIOUReport ?? null, '', oneOnOneTransaction); } const [oneOnOneOptimisticData, oneOnOneSuccessData, oneOnOneFailureData] = buildOnyxDataForMoneyRequest( - oneOnOneChatReport, - oneOnOneIOUReport, + oneOnOneChatReport as OnyxTypes.Report, + oneOnOneIOUReport as OnyxTypes.Report, oneOnOneTransaction, oneOnOneCreatedActionForChat, oneOnOneCreatedActionForIOU, oneOnOneIOUAction, {}, oneOnOneReportPreviewAction, - {}, + [], {}, isNewOneOnOneChatReport, shouldCreateNewOneOnOneIOUReport, @@ -2006,8 +2008,8 @@ function completeSplitBill(chatReportID, reportAction, updatedTransaction, sessi email: participant.email, accountID: participant.accountID, policyID: participant.policyID, - iouReportID: oneOnOneIOUReport.reportID, - chatReportID: oneOnOneChatReport.reportID, + iouReportID: oneOnOneIOUReport?.reportID, + chatReportID: oneOnOneChatReport?.reportID, transactionID: oneOnOneTransaction.transactionID, reportActionID: oneOnOneIOUAction.reportActionID, createdChatReportActionID: oneOnOneCreatedActionForChat.reportActionID, @@ -2028,23 +2030,33 @@ function completeSplitBill(chatReportID, reportAction, updatedTransaction, sessi comment: transactionComment, category: transactionCategory, tag: transactionTag, - } = ReportUtils.getTransactionDetails(updatedTransaction); + } = ReportUtils.getTransactionDetails(updatedTransaction) ?? {}; - API.write( - 'CompleteSplitBill', - { - transactionID, - amount: transactionAmount, - currency: transactionCurrency, - created: transactionCreated, - merchant: transactionMerchant, - comment: transactionComment, - category: transactionCategory, - tag: transactionTag, - splits: JSON.stringify(splits), - }, - {optimisticData, successData, failureData}, - ); + type CompleteSplitBillParams = { + transactionID: string; + amount?: number; + currency?: string; + created?: string; + merchant?: string; + comment?: string; + category?: string; + tag?: string; + splits: string; + }; + + const parameters: CompleteSplitBillParams = { + transactionID, + amount: transactionAmount, + currency: transactionCurrency, + created: transactionCreated, + merchant: transactionMerchant, + comment: transactionComment, + category: transactionCategory, + tag: transactionTag, + splits: JSON.stringify(splits), + }; + + API.write('CompleteSplitBill', parameters, {optimisticData, successData, failureData}); Navigation.dismissModal(chatReportID); Report.notifyNewAction(chatReportID, sessionAccountID); } diff --git a/src/types/onyx/IOU.ts b/src/types/onyx/IOU.ts index 5647bd31d3ef..f3cc20975fb7 100644 --- a/src/types/onyx/IOU.ts +++ b/src/types/onyx/IOU.ts @@ -6,6 +6,9 @@ type Participant = { email?: string; isPolicyExpenseChat?: boolean; chatReportID?: string; + iouReportID?: string; + transactionID?: string; + reportActionID?: string; isOwnPolicyExpenseChat?: boolean; selected?: boolean; reportID?: string; @@ -19,6 +22,9 @@ type Participant = { phoneNumber?: string; searchText?: string; text?: string; + createdChatReportActionID?: string; + createdIOUReportActionID?: string; + reportPreviewReportActionID?: string; }; type IOU = { From ecdd7ddd037ecbcab52fed56556c0366eb01f413 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 2 Jan 2024 13:31:57 +0100 Subject: [PATCH 029/660] Migrate getIOUReportID, navigateToNextPage and setMoneyRequestParticipantsFromReport functions --- src/libs/ReportUtils.ts | 4 +-- src/libs/actions/IOU.ts | 62 ++++++++++++++++++----------------------- 2 files changed, 29 insertions(+), 37 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 99c7814c94cd..6e951ad472be 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -395,7 +395,7 @@ Onyx.connect({ }, }); -function getChatType(report: OnyxEntry): ValueOf | undefined { +function getChatType(report: OnyxEntry | EmptyObject): ValueOf | undefined { return report?.chatType; } @@ -619,7 +619,7 @@ function isUserCreatedPolicyRoom(report: OnyxEntry): boolean { /** * Whether the provided report is a Policy Expense chat. */ -function isPolicyExpenseChat(report: OnyxEntry): boolean { +function isPolicyExpenseChat(report: OnyxEntry | EmptyObject): boolean { return getChatType(report) === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT || (report?.isPolicyExpenseChat ?? false); } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 91cf73ce8bfa..6e9e01dc5b96 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/naming-convention */ +import {StackScreenProps} from '@react-navigation/stack'; import {format} from 'date-fns'; import Str from 'expensify-common/lib/str'; import lodashGet from 'lodash/get'; @@ -25,9 +26,11 @@ import * as ReportUtils from '@libs/ReportUtils'; import {OptimisticCreatedReportAction, OptimisticIOUReportAction} from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import * as UserUtils from '@libs/UserUtils'; +import type {MoneyRequestNavigatorParamList} from '@navigation/types'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import SCREENS from '@src/SCREENS'; import * as OnyxTypes from '@src/types/onyx'; import {Participant} from '@src/types/onyx/IOU'; import ReportAction from '@src/types/onyx/ReportAction'; @@ -43,6 +46,11 @@ type OptimisticPolicyRecentlyUsedCategories = string[]; // TODO: Remove this once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. type OptimisticPolicyRecentlyUsedTags = Record; +type MoneyRequestRoute = StackScreenProps< + MoneyRequestNavigatorParamList, + typeof SCREENS.MONEY_REQUEST.CATEGORY | typeof SCREENS.MONEY_REQUEST.TAG | typeof SCREENS.MONEY_REQUEST.CONFIRMATION +>['route']; + type IOURequestType = ValueOf; type MoneyRequestInformation = { @@ -3260,19 +3268,17 @@ function replaceReceipt(transactionID, receipt, filePath) { /** * Finds the participants for an IOU based on the attached report - * @param {String} transactionID of the transaction to set the participants of - * @param {Object} report attached to the transaction + * @param transactionID of the transaction to set the participants of + * @param report attached to the transaction */ -function setMoneyRequestParticipantsFromReport(transactionID, report) { +function setMoneyRequestParticipantsFromReport(transactionID: string, report: OnyxTypes.Report) { // If the report is iou or expense report, we should get the chat report to set participant for request money const chatReport = ReportUtils.isMoneyRequestReport(report) ? ReportUtils.getReport(report.chatReportID) : report; const currentUserAccountID = currentUserPersonalDetails.accountID; - const participants = ReportUtils.isPolicyExpenseChat(chatReport) + const participants: Participant[] = ReportUtils.isPolicyExpenseChat(chatReport) ? [{reportID: chatReport.reportID, isPolicyExpenseChat: true, selected: true}] - : _.chain(chatReport.participantAccountIDs) - .filter((accountID) => currentUserAccountID !== accountID) - .map((accountID) => ({accountID, selected: true})) - .value(); + : (chatReport.participantAccountIDs ?? []).filter((accountID) => currentUserAccountID !== accountID).map((accountID) => ({accountID, selected: true})); + Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {participants, participantsAutoAssigned: true}); } @@ -3328,7 +3334,7 @@ function setMoneyRequestBillable(billable: boolean) { Onyx.merge(ONYXKEYS.IOU, {billable}); } -function setMoneyRequestParticipants(participants: Participant[], isSplitRequest: boolean) { +function setMoneyRequestParticipants(participants: Participant[], isSplitRequest?: boolean) { Onyx.merge(ONYXKEYS.IOU, {participants, isSplitRequest}); } @@ -3347,16 +3353,10 @@ function setUpDistanceTransaction() { /** * Navigates to the next IOU page based on where the IOU request was started - * - * @param {Object} iou - * @param {String} iouType - * @param {Object} report - * @param {String} report.reportID - * @param {String} path */ -function navigateToNextPage(iou, iouType, report, path = '') { - const moneyRequestID = `${iouType}${report.reportID || ''}`; - const shouldReset = iou.id !== moneyRequestID && !_.isEmpty(report.reportID); +function navigateToNextPage(iou: OnyxEntry, iouType: string, report?: OnyxTypes.Report, path = '') { + const moneyRequestID = `${iouType}${report?.reportID ?? ''}`; + const shouldReset = iou?.id !== moneyRequestID && !!report?.reportID; // If the money request ID in Onyx does not match the ID from params, we want to start a new request // with the ID from params. We need to clear the participants in case the new request is initiated from FAB. @@ -3365,24 +3365,21 @@ function navigateToNextPage(iou, iouType, report, path = '') { } // If we're adding a receipt, that means the user came from the confirmation page and we need to navigate back to it. - if (path.slice(1) === ROUTES.MONEY_REQUEST_RECEIPT.getRoute(iouType, report.reportID)) { - Navigation.navigate(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, report.reportID)); + if (path.slice(1) === ROUTES.MONEY_REQUEST_RECEIPT.getRoute(iouType, report?.reportID)) { + Navigation.navigate(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, report?.reportID)); return; } // If a request is initiated on a report, skip the participants selection step and navigate to the confirmation page. - if (report.reportID) { + if (report?.reportID) { // If the report is iou or expense report, we should get the chat report to set participant for request money const chatReport = ReportUtils.isMoneyRequestReport(report) ? ReportUtils.getReport(report.chatReportID) : report; // Reinitialize the participants when the money request ID in Onyx does not match the ID from params - if (_.isEmpty(iou.participants) || shouldReset) { + if (!iou?.participants?.length || shouldReset) { const currentUserAccountID = currentUserPersonalDetails.accountID; - const participants = ReportUtils.isPolicyExpenseChat(chatReport) + const participants: Participant[] = ReportUtils.isPolicyExpenseChat(chatReport) ? [{reportID: chatReport.reportID, isPolicyExpenseChat: true, selected: true}] - : _.chain(chatReport.participantAccountIDs) - .filter((accountID) => currentUserAccountID !== accountID) - .map((accountID) => ({accountID, selected: true})) - .value(); + : (chatReport.participantAccountIDs ?? []).filter((accountID) => currentUserAccountID !== accountID).map((accountID) => ({accountID, selected: true})); setMoneyRequestParticipants(participants); resetMoneyRequestCategory(); resetMoneyRequestTag(); @@ -3397,15 +3394,10 @@ function navigateToNextPage(iou, iouType, report, path = '') { * When the money request or split bill creation flow is initialized via FAB, the reportID is not passed as a navigation * parameter. * Gets a report id from the first participant of the IOU object stored in Onyx. - * @param {Object} iou - * @param {Array} iou.participants - * @param {Object} route - * @param {Object} route.params - * @param {String} [route.params.reportID] - * @returns {String} */ -function getIOUReportID(iou, route) { - return lodashGet(route, 'params.reportID') || lodashGet(iou, 'participants.0.reportID', ''); +function getIOUReportID(iou?: OnyxTypes.IOU, route?: MoneyRequestRoute): string { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + return route?.params.reportID || iou?.participants?.[0]?.reportID || ''; } export { From 016c19eab622a9fdfc81a63f7b286f713f31bce5 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 2 Jan 2024 15:38:33 +0100 Subject: [PATCH 030/660] Migrate approveMoneyRequest, submitReport, detachReceipt, replaceReceipt functions --- src/libs/actions/IOU.ts | 167 +++++++++++++++++++++------------------ src/types/onyx/Report.ts | 3 + 2 files changed, 94 insertions(+), 76 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 6e9e01dc5b96..bdfbef57c995 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3005,35 +3005,35 @@ function sendMoneyWithWallet(report, amount, currency, comment, managerID, recip Report.notifyNewAction(params.chatReportID, managerID); } -function approveMoneyRequest(expenseReport) { - const currentNextStep = lodashGet(allNextSteps, `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`, null); +function approveMoneyRequest(expenseReport: OnyxTypes.Report) { + const currentNextStep = allNextSteps[`${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`] ?? null; - const optimisticApprovedReportAction = ReportUtils.buildOptimisticApprovedReportAction(expenseReport.total, expenseReport.currency, expenseReport.reportID); + const optimisticApprovedReportAction = ReportUtils.buildOptimisticApprovedReportAction(expenseReport.total ?? 0, expenseReport.currency ?? '', expenseReport.reportID); - const optimisticReportActionsData = { + const optimisticReportActionsData: OnyxUpdate = { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, value: { [optimisticApprovedReportAction.reportActionID]: { - ...optimisticApprovedReportAction, + ...(optimisticApprovedReportAction as OnyxTypes.ReportAction), pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, }, }, }; - const optimisticIOUReportData = { + const optimisticIOUReportData: OnyxUpdate = { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`, value: { ...expenseReport, - lastMessageText: optimisticApprovedReportAction.message[0].text, - lastMessageHtml: optimisticApprovedReportAction.message[0].html, + lastMessageText: optimisticApprovedReportAction.message?.[0].text, + lastMessageHtml: optimisticApprovedReportAction.message?.[0].html, stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS.APPROVED, }, }; - const optimisticData = [optimisticIOUReportData, optimisticReportActionsData]; + const optimisticData: OnyxUpdate[] = [optimisticIOUReportData, optimisticReportActionsData]; - const successData = [ + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, @@ -3045,19 +3045,19 @@ function approveMoneyRequest(expenseReport) { }, ]; - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, value: { - [expenseReport.reportActionID]: { + [expenseReport.reportActionID ?? '']: { errors: ErrorUtils.getMicroSecondOnyxError('iou.error.other'), }, }, }, ]; - if (!_.isNull(currentNextStep)) { + if (currentNextStep) { optimisticData.push({ onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`, @@ -3070,25 +3070,32 @@ function approveMoneyRequest(expenseReport) { }); } - API.write('ApproveMoneyRequest', {reportID: expenseReport.reportID, approvedReportActionID: optimisticApprovedReportAction.reportActionID}, {optimisticData, successData, failureData}); + type ApproveMoneyRequestParams = { + reportID: string; + approvedReportActionID: string; + }; + + const parameters: ApproveMoneyRequestParams = { + reportID: expenseReport.reportID, + approvedReportActionID: optimisticApprovedReportAction.reportActionID, + }; + + API.write('ApproveMoneyRequest', parameters, {optimisticData, successData, failureData}); } -/** - * @param {Object} expenseReport - */ -function submitReport(expenseReport) { - const currentNextStep = lodashGet(allNextSteps, `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`, null); +function submitReport(expenseReport: OnyxTypes.Report) { + const currentNextStep = allNextSteps[`${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`] ?? null; - const optimisticSubmittedReportAction = ReportUtils.buildOptimisticSubmittedReportAction(expenseReport.total, expenseReport.currency, expenseReport.reportID); + const optimisticSubmittedReportAction = ReportUtils.buildOptimisticSubmittedReportAction(expenseReport?.total ?? 0, expenseReport.currency ?? '', expenseReport.reportID); const parentReport = ReportUtils.getReport(expenseReport.parentReportID); - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, value: { [optimisticSubmittedReportAction.reportActionID]: { - ...optimisticSubmittedReportAction, + ...(optimisticSubmittedReportAction as OnyxTypes.ReportAction), pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, }, }, @@ -3105,22 +3112,21 @@ function submitReport(expenseReport) { statusNum: CONST.REPORT.STATUS.SUBMITTED, }, }, - ...(parentReport.reportID - ? [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${parentReport.reportID}`, - value: { - ...parentReport, - hasOutstandingChildRequest: false, - iouReportID: null, - }, - }, - ] - : []), ]; - const successData = [ + if (parentReport.reportID) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${parentReport.reportID}`, + value: { + ...parentReport, + hasOutstandingChildRequest: false, + iouReportID: null, + }, + }); + } + + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, @@ -3132,12 +3138,12 @@ function submitReport(expenseReport) { }, ]; - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, value: { - [expenseReport.reportActionID]: { + [expenseReport.reportActionID ?? '']: { errors: ErrorUtils.getMicroSecondOnyxError('iou.error.other'), }, }, @@ -3150,21 +3156,20 @@ function submitReport(expenseReport) { stateNum: CONST.REPORT.STATE_NUM.OPEN, }, }, - ...(parentReport.reportID - ? [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${parentReport.reportID}`, - value: { - hasOutstandingChildRequest: parentReport.hasOutstandingChildRequest, - iouReportID: expenseReport.reportID, - }, - }, - ] - : []), ]; - if (!_.isNull(currentNextStep)) { + if (parentReport.reportID) { + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${parentReport.reportID}`, + value: { + hasOutstandingChildRequest: parentReport.hasOutstandingChildRequest, + iouReportID: expenseReport.reportID, + }, + }); + } + + if (currentNextStep) { optimisticData.push({ onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`, @@ -3177,15 +3182,19 @@ function submitReport(expenseReport) { }); } - API.write( - 'SubmitReport', - { - reportID: expenseReport.reportID, - managerAccountID: expenseReport.managerID, - reportActionID: optimisticSubmittedReportAction.reportActionID, - }, - {optimisticData, successData, failureData}, - ); + type SubmitReportParams = { + reportID: string; + managerAccountID?: number; + reportActionID: string; + }; + + const parameters: SubmitReportParams = { + reportID: expenseReport.reportID, + managerAccountID: expenseReport.managerID, + reportActionID: optimisticSubmittedReportAction.reportActionID, + }; + + API.write('SubmitReport', parameters, {optimisticData, successData, failureData}); } /** @@ -3206,11 +3215,12 @@ function payMoneyRequest(paymentType, chatReport, iouReport) { Navigation.dismissModal(chatReport.reportID); } -function detachReceipt(transactionID) { - const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`] || {}; +function detachReceipt(transactionID: string) { + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style + const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`] as OnyxTypes.Transaction; const newTransaction = {...transaction, filename: '', receipt: {}}; - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, @@ -3218,7 +3228,7 @@ function detachReceipt(transactionID) { }, ]; - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, @@ -3229,16 +3239,11 @@ function detachReceipt(transactionID) { API.write('DetachReceipt', {transactionID}, {optimisticData, failureData}); } -/** - * @param {String} transactionID - * @param {Object} receipt - * @param {String} filePath - */ -function replaceReceipt(transactionID, receipt, filePath) { - const transaction = lodashGet(allTransactions, 'transactionID', {}); - const oldReceipt = lodashGet(transaction, 'receipt', {}); +function replaceReceipt(transactionID: string, receipt: Receipt, filePath: ImageSourcePropType) { + const transaction = allTransactions.transactionID; + const oldReceipt = transaction?.receipt ?? {}; - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, @@ -3252,18 +3257,28 @@ function replaceReceipt(transactionID, receipt, filePath) { }, ]; - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, value: { receipt: oldReceipt, - filename: transaction.filename, + filename: transaction?.filename, }, }, ]; - API.write('ReplaceReceipt', {transactionID, receipt}, {optimisticData, failureData}); + type ReplaceReceiptParams = { + transactionID: string; + receipt: Receipt; + }; + + const parameters: ReplaceReceiptParams = { + transactionID, + receipt, + }; + + API.write('ReplaceReceipt', parameters, {optimisticData, failureData}); } /** diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index b274025908f5..b13421e48047 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -80,6 +80,9 @@ type Report = { /** ID of the report */ reportID: string; + /** ID of the report action */ + reportActionID?: string; + /** ID of the chat report */ chatReportID?: string; From 012cd769b43e4d37e4535c12c516f0b25fce20c8 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 2 Jan 2024 16:16:26 +0100 Subject: [PATCH 031/660] Migrate createSplitsAndOnyxData function --- src/libs/ReportUtils.ts | 5 +- src/libs/actions/IOU.ts | 137 ++++++++++++++++++++++++---------------- src/types/onyx/IOU.ts | 4 ++ 3 files changed, 90 insertions(+), 56 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 6e951ad472be..2ea861b6372d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -161,6 +161,7 @@ type OptimisticChatReport = Pick< | 'type' | 'chatType' | 'chatReportID' + | 'iouReportID' | 'isOwnPolicyExpenseChat' | 'isPinned' | 'lastActorAccountID' @@ -395,7 +396,7 @@ Onyx.connect({ }, }); -function getChatType(report: OnyxEntry | EmptyObject): ValueOf | undefined { +function getChatType(report: OnyxEntry | Participant | EmptyObject): ValueOf | undefined { return report?.chatType; } @@ -619,7 +620,7 @@ function isUserCreatedPolicyRoom(report: OnyxEntry): boolean { /** * Whether the provided report is a Policy Expense chat. */ -function isPolicyExpenseChat(report: OnyxEntry | EmptyObject): boolean { +function isPolicyExpenseChat(report: OnyxEntry | Participant | EmptyObject): boolean { return getChatType(report) === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT || (report?.isPolicyExpenseChat ?? false); } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index bdfbef57c995..cbbefc6e6729 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -23,7 +23,7 @@ import * as OptionsListUtils from '@libs/OptionsListUtils'; import Permissions from '@libs/Permissions'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; -import {OptimisticCreatedReportAction, OptimisticIOUReportAction} from '@libs/ReportUtils'; +import {OptimisticChatReport, OptimisticCreatedReportAction, OptimisticIOUReportAction} from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import * as UserUtils from '@libs/UserUtils'; import type {MoneyRequestNavigatorParamList} from '@navigation/types'; @@ -66,6 +66,20 @@ type MoneyRequestInformation = { onyxData: OnyxData; }; +type SplitData = { + chatReportID: string; + transactionID: string; + reportActionID: string; + policyID?: string; + createdReportActionID?: string; +}; + +type SplitsAndOnyxData = { + splitData: SplitData; + splits: Participant[]; + onyxData: OnyxData; +}; + let betas: OnyxTypes.Beta[] = []; Onyx.connect({ key: ONYXKEYS.BETAS, @@ -1149,25 +1163,26 @@ function requestMoney( * {email: 'user2', amount: 100, iouReportID: '100', chatReportID: '110', transactionID: '120', reportActionID: '130'}, * {email: 'user3', amount: 100, iouReportID: '200', chatReportID: '210', transactionID: '220', reportActionID: '230'} * ] - * @param {Array} participants - * @param {String} currentUserLogin - * @param {Number} currentUserAccountID - * @param {Number} amount - always in the smallest unit of the currency - * @param {String} comment - * @param {String} currency - * @param {String} merchant - * @param {String} category - * @param {String} tag - * @param {String} existingSplitChatReportID - the report ID where the split bill happens, could be a group chat or a workspace chat - * - * @return {Object} + * @param amount - always in the smallest unit of the currency + * @param existingSplitChatReportID - the report ID where the split bill happens, could be a group chat or a workspace chat */ -function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, merchant, category, tag, existingSplitChatReportID = '') { +function createSplitsAndOnyxData( + participants: Participant[], + currentUserLogin: string, + currentUserAccountID: number, + amount: number, + comment: string, + currency: string, + merchant: string, + category: string, + tag: string, + existingSplitChatReportID = '', +): SplitsAndOnyxData { const currentUserEmailForIOUSplit = OptionsListUtils.addSMSDomainIfPhoneNumber(currentUserLogin); - const participantAccountIDs = _.map(participants, (participant) => Number(participant.accountID)); + const participantAccountIDs = participants.map((participant) => Number(participant.accountID)); const existingSplitChatReport = existingSplitChatReportID || participants[0].reportID - ? allReports[`${ONYXKEYS.COLLECTION.REPORT}${existingSplitChatReportID || participants[0].reportID}`] + ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${existingSplitChatReportID || participants[0].reportID}`] : ReportUtils.getChatByParticipants(participantAccountIDs); const splitChatReport = existingSplitChatReport || ReportUtils.buildOptimisticChatReport(participantAccountIDs); const isOwnPolicyExpenseChat = splitChatReport.isOwnPolicyExpenseChat; @@ -1197,7 +1212,7 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco comment, participants, splitTransaction.transactionID, - '', + undefined, '', false, false, @@ -1206,8 +1221,8 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco ); splitChatReport.lastReadTime = DateUtils.getDBTime(); - splitChatReport.lastMessageText = splitIOUReportAction.message[0].text; - splitChatReport.lastMessageHtml = splitIOUReportAction.message[0].html; + splitChatReport.lastMessageText = splitIOUReportAction.message?.[0].text; + splitChatReport.lastMessageHtml = splitIOUReportAction.message?.[0].html; // If we have an existing splitChatReport (group chat or workspace) use it's pending fields, otherwise indicate that we are adding a chat if (!existingSplitChatReport) { @@ -1216,7 +1231,7 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco }; } - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { // Use 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 @@ -1224,14 +1239,22 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco key: `${ONYXKEYS.COLLECTION.REPORT}${splitChatReport.reportID}`, value: splitChatReport, }, - { - onyxMethod: existingSplitChatReport ? Onyx.METHOD.MERGE : Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, - value: { - ...(existingSplitChatReport ? {} : {[splitCreatedReportAction.reportActionID]: splitCreatedReportAction}), - [splitIOUReportAction.reportActionID]: splitIOUReportAction, - }, - }, + existingSplitChatReport + ? { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, + value: { + [splitIOUReportAction.reportActionID]: splitIOUReportAction as OnyxTypes.ReportAction, + }, + } + : { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, + value: { + [splitCreatedReportAction.reportActionID]: splitCreatedReportAction as OnyxTypes.ReportAction, + [splitIOUReportAction.reportActionID]: splitIOUReportAction as OnyxTypes.ReportAction, + }, + }, { onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${splitTransaction.transactionID}`, @@ -1239,7 +1262,7 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco }, ]; - const successData = [ + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${splitChatReport.reportID}`, @@ -1256,7 +1279,7 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${CONST.IOU.OPTIMISTIC_TRANSACTION_ID}`, - value: null, + value: {pendingAction: null}, }, ]; @@ -1268,7 +1291,7 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco }); } - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${splitTransaction.transactionID}`, @@ -1279,7 +1302,7 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${CONST.IOU.OPTIMISTIC_TRANSACTION_ID}`, - value: null, + value: {pendingAction: null}, }, ]; @@ -1318,10 +1341,10 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco // Loop through participants creating individual chats, iouReports and reportActionIDs as needed const splitAmount = IOUUtils.calculateAmount(participants.length, amount, currency, false); - const splits = [{email: currentUserEmailForIOUSplit, accountID: currentUserAccountID, amount: IOUUtils.calculateAmount(participants.length, amount, currency, true)}]; + const splits: Participant[] = [{email: currentUserEmailForIOUSplit, accountID: currentUserAccountID, amount: IOUUtils.calculateAmount(participants.length, amount, currency, true)}]; const hasMultipleParticipants = participants.length > 1; - _.each(participants, (participant) => { + participants.forEach((participant) => { // In a case when a participant is a workspace, even when a current user is not an owner of the workspace const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(participant); @@ -1335,7 +1358,7 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco // STEP 1: Get existing chat report OR build a new optimistic one // If we only have one participant and the request was initiated from the global create menu, i.e. !existingGroupChatReportID, the oneOnOneChatReport is the groupChatReport - let oneOnOneChatReport; + let oneOnOneChatReport: OptimisticChatReport; let isNewOneOnOneChatReport = false; let shouldCreateOptimisticPersonalDetails = false; const personalDetailExists = lodashHas(allPersonalDetails, accountID); @@ -1363,20 +1386,22 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco if (shouldCreateNewOneOnOneIOUReport) { oneOnOneIOUReport = isOwnPolicyExpenseChat - ? ReportUtils.buildOptimisticExpenseReport(oneOnOneChatReport.reportID, oneOnOneChatReport.policyID, currentUserAccountID, splitAmount, currency) + ? ReportUtils.buildOptimisticExpenseReport(oneOnOneChatReport.reportID, oneOnOneChatReport.policyID ?? '', currentUserAccountID, splitAmount, currency) : ReportUtils.buildOptimisticIOUReport(currentUserAccountID, accountID, splitAmount, oneOnOneChatReport.reportID, currency); } else if (isOwnPolicyExpenseChat) { - // Because of the Expense reports are stored as negative values, we subtract the total from the amount - oneOnOneIOUReport.total -= splitAmount; + if (oneOnOneIOUReport?.total) { + // Because of the Expense reports are stored as negative values, we subtract the total from the amount + oneOnOneIOUReport.total -= splitAmount; + } } else { - oneOnOneIOUReport = IOUUtils.updateIOUOwnerAndTotal(oneOnOneIOUReport, currentUserAccountID, splitAmount, currency); + oneOnOneIOUReport = IOUUtils.updateIOUOwnerAndTotal(oneOnOneIOUReport as OnyxTypes.Report, currentUserAccountID, splitAmount, currency); } // STEP 3: Build optimistic transaction const oneOnOneTransaction = TransactionUtils.buildOptimisticTransaction( - ReportUtils.isExpenseReport(oneOnOneIOUReport) ? -splitAmount : splitAmount, + ReportUtils.isExpenseReport(oneOnOneIOUReport as OnyxTypes.Report) ? -splitAmount : splitAmount, currency, - oneOnOneIOUReport.reportID, + oneOnOneIOUReport?.reportID ?? '', comment, '', CONST.IOU.TYPE.SPLIT, @@ -1405,8 +1430,8 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco comment, [participant], oneOnOneTransaction.transactionID, - '', - oneOnOneIOUReport.reportID, + undefined, + oneOnOneIOUReport?.reportID ?? '', undefined, undefined, undefined, @@ -1415,7 +1440,7 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco ); // Add optimistic personal details for new participants - const oneOnOnePersonalDetailListAction = shouldCreateOptimisticPersonalDetails + const oneOnOnePersonalDetailListAction: OnyxTypes.PersonalDetailsList | undefined = shouldCreateOptimisticPersonalDetails ? { [accountID]: { accountID, @@ -1427,23 +1452,27 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco } : undefined; - let oneOnOneReportPreviewAction = ReportActionsUtils.getReportPreviewAction(oneOnOneChatReport.reportID, oneOnOneIOUReport.reportID); + let oneOnOneReportPreviewAction = ReportActionsUtils.getReportPreviewAction(oneOnOneChatReport.reportID, oneOnOneIOUReport?.reportID ?? ''); if (oneOnOneReportPreviewAction) { - oneOnOneReportPreviewAction = ReportUtils.updateReportPreview(oneOnOneIOUReport, oneOnOneReportPreviewAction); + oneOnOneReportPreviewAction = ReportUtils.updateReportPreview(oneOnOneIOUReport as OnyxTypes.Report, oneOnOneReportPreviewAction); } else { - oneOnOneReportPreviewAction = ReportUtils.buildOptimisticReportPreview(oneOnOneChatReport, oneOnOneIOUReport); + oneOnOneReportPreviewAction = ReportUtils.buildOptimisticReportPreview(oneOnOneChatReport, oneOnOneIOUReport as OnyxTypes.Report); } // Add category to optimistic policy recently used categories when a participant is a workspace - const optimisticPolicyRecentlyUsedCategories = isPolicyExpenseChat ? Policy.buildOptimisticPolicyRecentlyUsedCategories(participant.policyID, category) : []; + const optimisticPolicyRecentlyUsedCategories: OptimisticPolicyRecentlyUsedCategories = isPolicyExpenseChat + ? (Policy.buildOptimisticPolicyRecentlyUsedCategories(participant.policyID, category) as OptimisticPolicyRecentlyUsedCategories) + : []; // Add tag to optimistic policy recently used tags when a participant is a workspace - const optimisticPolicyRecentlyUsedTags = isPolicyExpenseChat ? Policy.buildOptimisticPolicyRecentlyUsedTags(participant.policyID, tag) : {}; + const optimisticPolicyRecentlyUsedTags: OptimisticPolicyRecentlyUsedTags = isPolicyExpenseChat + ? (Policy.buildOptimisticPolicyRecentlyUsedTags(participant.policyID, tag) as OptimisticPolicyRecentlyUsedTags) + : {}; // STEP 5: Build Onyx Data const [oneOnOneOptimisticData, oneOnOneSuccessData, oneOnOneFailureData] = buildOnyxDataForMoneyRequest( oneOnOneChatReport, - oneOnOneIOUReport, + oneOnOneIOUReport as OnyxTypes.Report, oneOnOneTransaction, oneOnOneCreatedActionForChat, oneOnOneCreatedActionForIOU, @@ -1453,14 +1482,14 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco optimisticPolicyRecentlyUsedCategories, optimisticPolicyRecentlyUsedTags, isNewOneOnOneChatReport, - shouldCreateNewOneOnOneIOUReport, + !!shouldCreateNewOneOnOneIOUReport, ); const individualSplit = { email, accountID, amount: splitAmount, - iouReportID: oneOnOneIOUReport.reportID, + iouReportID: oneOnOneIOUReport?.reportID, chatReportID: oneOnOneChatReport.reportID, transactionID: oneOnOneTransaction.transactionID, reportActionID: oneOnOneIOUAction.reportActionID, @@ -1475,14 +1504,14 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco failureData.push(...oneOnOneFailureData); }); - const splitData = { + const splitData: SplitData = { chatReportID: splitChatReport.reportID, transactionID: splitTransaction.transactionID, reportActionID: splitIOUReportAction.reportActionID, policyID: splitChatReport.policyID, }; - if (_.isEmpty(existingSplitChatReport)) { + if (!existingSplitChatReport) { splitData.createdReportActionID = splitCreatedReportAction.reportActionID; } diff --git a/src/types/onyx/IOU.ts b/src/types/onyx/IOU.ts index f3cc20975fb7..4f9989d347a6 100644 --- a/src/types/onyx/IOU.ts +++ b/src/types/onyx/IOU.ts @@ -1,3 +1,5 @@ +import {ValueOf} from 'type-fest'; +import CONST from '@src/CONST'; import {Icon} from './OnyxCommon'; type Participant = { @@ -10,6 +12,8 @@ type Participant = { transactionID?: string; reportActionID?: string; isOwnPolicyExpenseChat?: boolean; + chatType?: ValueOf; + amount?: number; selected?: boolean; reportID?: string; policyID?: string; From 10cb535cedbe180f6ef73c90bd6f8a9a501d6573 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Wed, 3 Jan 2024 12:02:34 +0300 Subject: [PATCH 032/660] Migrate SettlementButton.js component to TypeScript --- src/components/KYCWall/kycWallPropTypes.js | 5 + ...ttlementButton.js => SettlementButton.tsx} | 189 +++++++----------- 2 files changed, 79 insertions(+), 115 deletions(-) rename src/components/{SettlementButton.js => SettlementButton.tsx} (65%) diff --git a/src/components/KYCWall/kycWallPropTypes.js b/src/components/KYCWall/kycWallPropTypes.js index 2f14f1e12e11..aeda8544dd0d 100644 --- a/src/components/KYCWall/kycWallPropTypes.js +++ b/src/components/KYCWall/kycWallPropTypes.js @@ -65,6 +65,11 @@ const propTypes = { /** Whether the personal bank account option should be shown */ shouldShowPersonalBankAccountOption: PropTypes.bool, + + /** Callback to execute when KYC is successful */ + onSuccessfulKYC: PropTypes.func.isRequired, + + children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]).isRequired, }; const defaultProps = { diff --git a/src/components/SettlementButton.js b/src/components/SettlementButton.tsx similarity index 65% rename from src/components/SettlementButton.js rename to src/components/SettlementButton.tsx index 0c8e193af4cc..2786cd852434 100644 --- a/src/components/SettlementButton.js +++ b/src/components/SettlementButton.tsx @@ -1,143 +1,106 @@ -import PropTypes from 'prop-types'; -import React, {useEffect, useMemo} from 'react'; -import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; +import _ from 'lodash'; +import React, {MutableRefObject, useEffect, useMemo} from 'react'; +import {StyleProp, ViewStyle} from 'react-native'; +import {OnyxEntry, withOnyx} from 'react-native-onyx'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; -import compose from '@libs/compose'; import * as ReportUtils from '@libs/ReportUtils'; -import iouReportPropTypes from '@pages/iouReportPropTypes'; import * as BankAccounts from '@userActions/BankAccounts'; import * as IOU from '@userActions/IOU'; import * as PaymentMethods from '@userActions/PaymentMethods'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import {ButtonSizeValue} from '@src/styles/utils/types'; +import type {Report} from '@src/types/onyx'; +import {EmptyObject} from '@src/types/utils/EmptyObject'; import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; import * as Expensicons from './Icon/Expensicons'; import KYCWall from './KYCWall'; -import withNavigation from './withNavigation'; -const propTypes = { - /** Callback to execute when this button is pressed. Receives a single payment type argument. */ - onPress: PropTypes.func.isRequired, +type AnchorAlignment = { + horizontal: 'left' | 'right' | 'center'; + vertical: 'top' | 'center' | 'bottom'; +}; - /** Call the onPress function on main button when Enter key is pressed */ - pressOnEnter: PropTypes.bool, +type TriggerKYCFlow = (event: Event, iouPaymentType: string) => void; - /** Settlement currency type */ - currency: PropTypes.string, +type SettlementButtonOnyxProps = { + /** The last payment method used per policy */ + nvpLastPaymentMethod?: Record; +}; +type SettlementButtonProps = SettlementButtonOnyxProps & { + /** Callback to execute when this button is pressed. Receives a single payment type argument. */ + onPress: (paymentType: string) => void; + /** The route to redirect if user does not have a payment method setup */ + enablePaymentsRoute: string; + /** Call the onPress function on main button when Enter key is pressed */ + pressOnEnter?: boolean; + /** Settlement currency type */ + currency?: string; /** When the button is opened via an IOU, ID for the chatReport that the IOU is linked to */ - chatReportID: PropTypes.string, - + chatReportID?: string; /** The IOU/Expense report we are paying */ - iouReport: iouReportPropTypes, - - /** The route to redirect if user does not have a payment method setup */ - enablePaymentsRoute: PropTypes.string.isRequired, - - /** Should we show the approve button? */ - shouldHidePaymentOptions: PropTypes.bool, - + iouReport?: OnyxEntry | EmptyObject; /** Should we show the payment options? */ - shouldShowApproveButton: PropTypes.bool, - - /** The last payment method used per policy */ - nvp_lastPaymentMethod: PropTypes.objectOf(PropTypes.string), - + shouldHidePaymentOptions?: boolean; + /** Should we show the payment options? */ + shouldShowApproveButton?: boolean; /** The policyID of the report we are paying */ - policyID: PropTypes.string, - + policyID?: string; /** Additional styles to add to the component */ - style: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]), - + style?: StyleProp; /** Total money amount in form */ - formattedAmount: PropTypes.string, - + formattedAmount?: string; /** The size of button size */ - buttonSize: PropTypes.oneOf(_.values(CONST.DROPDOWN_BUTTON_SIZE)), - + buttonSize?: ButtonSizeValue; // Replace with the actual sizes /** Route for the Add Bank Account screen for a given navigation stack */ - addBankAccountRoute: PropTypes.string, - + addBankAccountRoute?: string; /** Route for the Add Debit Card screen for a given navigation stack */ - addDebitCardRoute: PropTypes.string, - + addDebitCardRoute?: string; /** Whether the button should be disabled */ - isDisabled: PropTypes.bool, - + isDisabled?: boolean; /** Whether we should show a loading state for the main button */ - isLoading: PropTypes.bool, - + isLoading?: boolean; /** The anchor alignment of the popover menu for payment method dropdown */ - paymentMethodDropdownAnchorAlignment: PropTypes.shape({ - horizontal: PropTypes.oneOf(_.values(CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL)), - vertical: PropTypes.oneOf(_.values(CONST.MODAL.ANCHOR_ORIGIN_VERTICAL)), - }), - + paymentMethodDropdownAnchorAlignment?: AnchorAlignment; /** The anchor alignment of the popover menu for KYC wall popover */ - kycWallAnchorAlignment: PropTypes.shape({ - horizontal: PropTypes.oneOf(_.values(CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL)), - vertical: PropTypes.oneOf(_.values(CONST.MODAL.ANCHOR_ORIGIN_VERTICAL)), - }), - + kycWallAnchorAlignment?: AnchorAlignment; /** Whether the personal bank account option should be shown */ - shouldShowPersonalBankAccountOption: PropTypes.bool, + shouldShowPersonalBankAccountOption?: boolean; }; -const defaultProps = { - isLoading: false, - isDisabled: false, - pressOnEnter: false, - addBankAccountRoute: '', - addDebitCardRoute: '', - currency: CONST.CURRENCY.USD, - chatReportID: '', - - // The "iouReport" and "nvp_lastPaymentMethod" objects needs to be stable to prevent the "useMemo" - // hook from being recreated unnecessarily, hence the use of CONST.EMPTY_ARRAY and CONST.EMPTY_OBJECT - iouReport: CONST.EMPTY_OBJECT, - nvp_lastPaymentMethod: CONST.EMPTY_OBJECT, - shouldHidePaymentOptions: false, - shouldShowApproveButton: false, - style: [], - policyID: '', - formattedAmount: '', - buttonSize: CONST.DROPDOWN_BUTTON_SIZE.MEDIUM, - kycWallAnchorAlignment: { +function SettlementButton({ + addDebitCardRoute = '', + addBankAccountRoute = '', + kycWallAnchorAlignment = { horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, // button is at left, so horizontal anchor is at LEFT vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP, // we assume that popover menu opens below the button, anchor is at TOP }, - paymentMethodDropdownAnchorAlignment: { + paymentMethodDropdownAnchorAlignment = { horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT, // caret for dropdown is at right, so horizontal anchor is at RIGHT vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP, // we assume that popover menu opens below the button, anchor is at TOP }, - shouldShowPersonalBankAccountOption: false, -}; - -function SettlementButton({ - addDebitCardRoute, - addBankAccountRoute, - kycWallAnchorAlignment, - paymentMethodDropdownAnchorAlignment, - buttonSize, - chatReportID, - currency, + buttonSize = CONST.DROPDOWN_BUTTON_SIZE.MEDIUM, + chatReportID = '', + currency = CONST.CURRENCY.USD, enablePaymentsRoute, - iouReport, - isDisabled, - isLoading, - formattedAmount, - nvp_lastPaymentMethod, + // The "iouReport" and "nvpLastPaymentMethod" objects needs to be stable to prevent the "useMemo" + // hook from being recreated unnecessarily, hence the use of CONST.EMPTY_ARRAY and CONST.EMPTY_OBJECT + iouReport = CONST.EMPTY_OBJECT, + nvpLastPaymentMethod = CONST.EMPTY_OBJECT, + isDisabled = false, + isLoading = false, + formattedAmount = '', onPress, - pressOnEnter, - policyID, - shouldHidePaymentOptions, - shouldShowApproveButton, - style, - shouldShowPersonalBankAccountOption, -}) { + pressOnEnter = false, + policyID = '', + shouldHidePaymentOptions = false, + shouldShowApproveButton = false, + style = [], + shouldShowPersonalBankAccountOption = false, +}: SettlementButtonProps) { const {translate} = useLocalize(); const {isOffline} = useNetwork(); @@ -179,7 +142,7 @@ function SettlementButton({ // To achieve the one tap pay experience we need to choose the correct payment type as default, // if user already paid for some request or expense, let's use the last payment method or use default. - const paymentMethod = nvp_lastPaymentMethod[policyID] || ''; + const paymentMethod = nvpLastPaymentMethod[policyID] || ''; if (canUseWallet) { buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.EXPENSIFY]); } @@ -197,9 +160,9 @@ function SettlementButton({ return _.sortBy(buttonOptions, (method) => (method.value === paymentMethod ? 0 : 1)); } return buttonOptions; - }, [currency, formattedAmount, iouReport, nvp_lastPaymentMethod, policyID, translate, shouldHidePaymentOptions, shouldShowApproveButton]); + }, [currency, formattedAmount, iouReport, nvpLastPaymentMethod, policyID, translate, shouldHidePaymentOptions, shouldShowApproveButton]); - const selectPaymentType = (event, iouPaymentType, triggerKYCFlow) => { + const selectPaymentType = (event: Event, iouPaymentType: string, triggerKYCFlow: TriggerKYCFlow) => { if (iouPaymentType === CONST.IOU.PAYMENT_TYPE.EXPENSIFY || iouPaymentType === CONST.IOU.PAYMENT_TYPE.VBBA) { triggerKYCFlow(event, iouPaymentType); BankAccounts.setPersonalBankAccountContinueKYCOnSuccess(ROUTES.ENABLE_PAYMENTS); @@ -227,7 +190,7 @@ function SettlementButton({ anchorAlignment={kycWallAnchorAlignment} shouldShowPersonalBankAccountOption={shouldShowPersonalBankAccountOption} > - {(triggerKYCFlow, buttonRef) => ( + {(triggerKYCFlow: TriggerKYCFlow, buttonRef: MutableRefObject) => ( selectPaymentType(event, iouPaymentType, triggerKYCFlow)} pressOnEnter={pressOnEnter} options={paymentButtonOptions} - style={style} + style={[style]} buttonSize={buttonSize} anchorAlignment={paymentMethodDropdownAnchorAlignment} /> @@ -244,15 +207,11 @@ function SettlementButton({ ); } -SettlementButton.propTypes = propTypes; -SettlementButton.defaultProps = defaultProps; SettlementButton.displayName = 'SettlementButton'; -export default compose( - withNavigation, - withOnyx({ - nvp_lastPaymentMethod: { - key: ONYXKEYS.NVP_LAST_PAYMENT_METHOD, - }, - }), -)(SettlementButton); +export default withOnyx({ + nvpLastPaymentMethod: { + key: ONYXKEYS.NVP_LAST_PAYMENT_METHOD, + selector: (paymentMethod) => paymentMethod ?? {}, + }, +})(SettlementButton); From 53e4b00429ebc9663d55599f3487d90280faea69 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Wed, 3 Jan 2024 13:06:18 +0300 Subject: [PATCH 033/660] Update types --- src/components/SettlementButton.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton.tsx index 2786cd852434..3eb4ac844fb2 100644 --- a/src/components/SettlementButton.tsx +++ b/src/components/SettlementButton.tsx @@ -195,10 +195,10 @@ function SettlementButton({ buttonRef={buttonRef} isDisabled={isDisabled} isLoading={isLoading} - onPress={(event, iouPaymentType) => selectPaymentType(event, iouPaymentType, triggerKYCFlow)} + onPress={(event: Event, iouPaymentType: string) => selectPaymentType(event, iouPaymentType, triggerKYCFlow)} pressOnEnter={pressOnEnter} options={paymentButtonOptions} - style={[style]} + style={style as Record} buttonSize={buttonSize} anchorAlignment={paymentMethodDropdownAnchorAlignment} /> From 4711e1cd99dfae34ccb59c3de37a10b4f8f6a44c Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 4 Jan 2024 12:41:09 +0100 Subject: [PATCH 034/660] Migrate getReceiptError function; fix type imports after merging main --- src/libs/ErrorUtils.ts | 16 +++- src/libs/PolicyUtils.ts | 2 +- src/libs/ReportUtils.ts | 2 +- src/libs/actions/IOU.ts | 150 +++++++++++++++++----------------- src/types/onyx/OnyxCommon.ts | 9 +- src/types/onyx/Transaction.ts | 1 + src/types/onyx/index.ts | 3 +- 7 files changed, 97 insertions(+), 86 deletions(-) diff --git a/src/libs/ErrorUtils.ts b/src/libs/ErrorUtils.ts index 3160427ee81f..cd2b04f27782 100644 --- a/src/libs/ErrorUtils.ts +++ b/src/libs/ErrorUtils.ts @@ -1,10 +1,17 @@ +import type {ImageSourcePropType} from 'react-native'; import CONST from '@src/CONST'; import type {TranslationFlatObject, TranslationPaths} from '@src/languages/types'; -import type {ErrorFields, Errors} from '@src/types/onyx/OnyxCommon'; +import type {ErrorFields, SimpleErrors} from '@src/types/onyx/OnyxCommon'; import type Response from '@src/types/onyx/Response'; import DateUtils from './DateUtils'; import * as Localize from './Localize'; +type ErrorObject = Record; + +type MicroSecondOnyxError = Record; + +type MicroSecondOnyxErrorObject = Record; + function getAuthenticateErrorMessage(response: Response): keyof TranslationFlatObject { switch (response.jsonCode) { case CONST.JSON_CODE.UNABLE_TO_RETRY: @@ -38,7 +45,7 @@ function getAuthenticateErrorMessage(response: Response): keyof TranslationFlatO * Method used to get an error object with microsecond as the key. * @param error - error key or message to be saved */ -function getMicroSecondOnyxError(error: string | null): Record { +function getMicroSecondOnyxError(error: string | null): MicroSecondOnyxError { return {[DateUtils.getMicroseconds()]: error}; } @@ -46,12 +53,12 @@ function getMicroSecondOnyxError(error: string | null): Record): Record> { +function getMicroSecondOnyxErrorObject(error: ErrorObject): MicroSecondOnyxErrorObject { return {[DateUtils.getMicroseconds()]: error}; } type OnyxDataWithErrors = { - errors?: Errors; + errors?: SimpleErrors; }; function getLatestErrorMessage(onyxData: TOnyxData): string { @@ -120,3 +127,4 @@ function addErrorMessage(errors: ErrorsList, inpu } export {getAuthenticateErrorMessage, getMicroSecondOnyxError, getMicroSecondOnyxErrorObject, getLatestErrorMessage, getLatestErrorField, getEarliestErrorField, addErrorMessage}; +export type {MicroSecondOnyxErrorObject, MicroSecondOnyxError}; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 0cab97299324..f50923aa0ecb 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -198,7 +198,7 @@ function isPendingDeletePolicy(policy: OnyxEntry): boolean { return policy?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; } -function isPaidGroupPolicy(policy: OnyxEntry): boolean { +function isPaidGroupPolicy(policy: OnyxEntry | EmptyObject): boolean { return policy?.type === CONST.POLICY.TYPE.TEAM || policy?.type === CONST.POLICY.TYPE.CORPORATE; } diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 0ba456608680..56d074a0ade8 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -16,7 +16,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Beta, Login, PersonalDetails, PersonalDetailsList, Policy, Report, ReportAction, ReportMetadata, Session, Transaction} from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; -import {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; +import type {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; import type {IOUMessage, OriginalMessageActionName, OriginalMessageCreated} from '@src/types/onyx/OriginalMessage'; import type {Status} from '@src/types/onyx/PersonalDetails'; import type {NotificationPreference} from '@src/types/onyx/Report'; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index c92c7dec4b76..dc92c1b9043b 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1,19 +1,21 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import {StackScreenProps} from '@react-navigation/stack'; +import type {StackScreenProps} from '@react-navigation/stack'; import {format} from 'date-fns'; import Str from 'expensify-common/lib/str'; import lodashGet from 'lodash/get'; import lodashHas from 'lodash/has'; import {ImageSourcePropType} from 'react-native'; -import Onyx, {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; +import Onyx from 'react-native-onyx'; +import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import OnyxUtils from 'react-native-onyx/lib/utils'; -import {ValueOf} from 'type-fest'; +import type {ValueOf} from 'type-fest'; import _ from 'underscore'; import ReceiptGeneric from '@assets/images/receipt-generic.png'; import * as API from '@libs/API'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import DateUtils from '@libs/DateUtils'; import * as ErrorUtils from '@libs/ErrorUtils'; +import type {MicroSecondOnyxError, MicroSecondOnyxErrorObject} from '@libs/ErrorUtils'; import * as IOUUtils from '@libs/IOUUtils'; import * as LocalePhoneNumber from '@libs/LocalePhoneNumber'; import * as Localize from '@libs/Localize'; @@ -24,20 +26,21 @@ import Permissions from '@libs/Permissions'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; -import {OptimisticChatReport, OptimisticCreatedReportAction, OptimisticIOUReportAction} from '@libs/ReportUtils'; +import type {OptimisticChatReport, OptimisticCreatedReportAction, OptimisticIOUReportAction} from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import * as UserUtils from '@libs/UserUtils'; import type {MoneyRequestNavigatorParamList} from '@navigation/types'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import SCREENS from '@src/SCREENS'; -import * as OnyxTypes from '@src/types/onyx'; -import {Participant} from '@src/types/onyx/IOU'; -import ReportAction from '@src/types/onyx/ReportAction'; -import {OnyxData} from '@src/types/onyx/Request'; -import {Comment, Receipt, WaypointCollection} from '@src/types/onyx/Transaction'; -import {EmptyObject, isEmptyObject} from '@src/types/utils/EmptyObject'; +import type SCREENS from '@src/SCREENS'; +import type * as OnyxTypes from '@src/types/onyx'; +import type {Participant} from '@src/types/onyx/IOU'; +import type ReportAction from '@src/types/onyx/ReportAction'; +import type {OnyxData} from '@src/types/onyx/Request'; +import type {Comment, Receipt, WaypointCollection} from '@src/types/onyx/Transaction'; +import type {EmptyObject} from '@src/types/utils/EmptyObject'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import * as Policy from './Policy'; import * as Report from './Report'; @@ -186,7 +189,6 @@ Onyx.connect({ /** * Initialize money request info * @param reportID to attach the transaction to - * @param isFromGlobalCreate * @param [iouRequestType] one of manual/scan/distance */ function startMoneyRequest_temporaryForRefactor(reportID: string, isFromGlobalCreate: boolean, iouRequestType: IOURequestType = CONST.IOU.REQUEST_TYPE.MANUAL) { @@ -297,14 +299,9 @@ function resetMoneyRequestInfo(id = '') { /** * Helper function to get the receipt error for money requests, or the generic error if there's no receipt - * - * @param {Object} receipt - * @param {String} filename - * @param {Boolean} [isScanRequest] - * @returns {Object} */ -function getReceiptError(receipt, filename, isScanRequest = true) { - return _.isEmpty(receipt) || !isScanRequest +function getReceiptError(receipt?: Receipt, filename?: string, isScanRequest = true): MicroSecondOnyxError | MicroSecondOnyxErrorObject { + return isEmptyObject(receipt) || !isScanRequest ? ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage') : ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source: receipt.source, filename}); } @@ -325,7 +322,7 @@ function buildOnyxDataForMoneyRequest( hasOutstandingChildRequest = false, ): OnyxUpdate[][] { const isScanRequest = TransactionUtils.isScanRequest(transaction); - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { // Use 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 onyxMethod: isNewChatReport ? Onyx.METHOD.SET : Onyx.METHOD.MERGE, @@ -344,8 +341,8 @@ function buildOnyxDataForMoneyRequest( key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, value: { ...iouReport, - lastMessageText: iouAction.message[0].text, - lastMessageHtml: iouAction.message[0].html, + lastMessageText: iouAction.message?.[0].text, + lastMessageHtml: iouAction.message?.[0].html, pendingFields: { ...(isNewIOUReport ? {createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD} : {preview: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}), }, @@ -421,19 +418,20 @@ function buildOnyxDataForMoneyRequest( }); } - const successData = [ - ...(isNewChatReport - ? [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, - value: { - pendingFields: null, - errorFields: null, - }, - }, - ] - : []), + const successData: OnyxUpdate[] = []; + + if (isNewChatReport) { + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, + value: { + pendingFields: null, + errorFields: null, + }, + }); + } + + successData.push( { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, @@ -457,11 +455,11 @@ function buildOnyxDataForMoneyRequest( value: { ...(isNewChatReport ? { - [chatCreatedAction.reportActionID]: { - pendingAction: null, - errors: null, - }, - } + [chatCreatedAction.reportActionID]: { + pendingAction: null, + errors: null, + }, + } : {}), [reportPreviewAction.reportActionID]: { pendingAction: null, @@ -474,11 +472,11 @@ function buildOnyxDataForMoneyRequest( value: { ...(isNewIOUReport ? { - [iouCreatedAction.reportActionID]: { - pendingAction: null, - errors: null, - }, - } + [iouCreatedAction.reportActionID]: { + pendingAction: null, + errors: null, + }, + } : {}), [iouAction.reportActionID]: { pendingAction: null, @@ -486,9 +484,9 @@ function buildOnyxDataForMoneyRequest( }, }, }, - ]; + ); - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, @@ -498,10 +496,10 @@ function buildOnyxDataForMoneyRequest( pendingFields: null, ...(isNewChatReport ? { - errorFields: { - createChat: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), - }, - } + errorFields: { + createChat: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), + }, + } : {}), }, }, @@ -538,19 +536,19 @@ function buildOnyxDataForMoneyRequest( value: { ...(isNewChatReport ? { - [chatCreatedAction.reportActionID]: { - errors: getReceiptError(transaction.receipt, transaction.filename || transaction.receipt.filename, isScanRequest), - }, - [reportPreviewAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError(null), - }, - } + [chatCreatedAction.reportActionID]: { + errors: getReceiptError(transaction?.receipt, transaction.filename ?? transaction.receipt?.filename, isScanRequest), + }, + [reportPreviewAction.reportActionID]: { + errors: ErrorUtils.getMicroSecondOnyxError(null), + }, + } : { - [reportPreviewAction.reportActionID]: { - created: reportPreviewAction.created, - errors: getReceiptError(transaction.receipt, transaction.filename || transaction.receipt.filename, isScanRequest), - }, - }), + [reportPreviewAction.reportActionID]: { + created: reportPreviewAction.created, + errors: getReceiptError(transaction?.receipt, transaction.filename ?? transaction.receipt?.filename, isScanRequest), + }, + }), }, }, { @@ -559,18 +557,18 @@ function buildOnyxDataForMoneyRequest( value: { ...(isNewIOUReport ? { - [iouCreatedAction.reportActionID]: { - errors: getReceiptError(transaction.receipt, transaction.filename || transaction.receipt.filename, isScanRequest), - }, - [iouAction.reportActionID]: { - errors: ErrorUtils.getMicroSecondOnyxError(null), - }, - } + [iouCreatedAction.reportActionID]: { + errors: getReceiptError(transaction.receipt, transaction.filename ?? transaction.receipt?.filename, isScanRequest), + }, + [iouAction.reportActionID]: { + errors: ErrorUtils.getMicroSecondOnyxError(null), + }, + } : { - [iouAction.reportActionID]: { - errors: getReceiptError(transaction.receipt, transaction.filename || transaction.receipt.filename, isScanRequest), - }, - }), + [iouAction.reportActionID]: { + errors: getReceiptError(transaction.receipt, transaction.filename ?? transaction.receipt?.filename, isScanRequest), + }, + }), }, }, ]; @@ -630,7 +628,7 @@ function getMoneyRequestInformation( let needsToBeManuallySubmitted = false; let isFromPaidPolicy = false; if (isPolicyExpenseChat) { - const policy = ReportUtils.getPolicy(chatReport.policyID); + const policy = ReportUtils.getPolicy(chatReport.policyID ?? ''); isFromPaidPolicy = PolicyUtils.isPaidGroupPolicy(policy); // If the scheduled submit is turned off on the policy, user needs to manually submit the report which is indicated by GBR in LHN @@ -645,7 +643,7 @@ function getMoneyRequestInformation( if (iouReport) { if (isPolicyExpenseChat) { iouReport = {...iouReport}; - if (lodashGet(iouReport, 'currency') === currency) { + if (iouReport?.currency === currency && iouReport.total) { // Because of the Expense reports are stored as negative values, we substract the total from the amount iouReport.total -= amount; } diff --git a/src/types/onyx/OnyxCommon.ts b/src/types/onyx/OnyxCommon.ts index b26dc167ed44..60a3fc5b89df 100644 --- a/src/types/onyx/OnyxCommon.ts +++ b/src/types/onyx/OnyxCommon.ts @@ -1,4 +1,5 @@ import type {ValueOf} from 'type-fest'; +import type {MicroSecondOnyxError, MicroSecondOnyxErrorObject} from '@libs/ErrorUtils'; import type {AvatarSource} from '@libs/UserUtils'; import type CONST from '@src/CONST'; @@ -6,9 +7,11 @@ type PendingAction = ValueOf; type PendingFields = Record; -type ErrorFields = Record; +type SimpleErrors = Record; -type Errors = Record; +type ErrorFields = Record; + +type Errors = SimpleErrors | MicroSecondOnyxError | MicroSecondOnyxErrorObject; type AvatarType = typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; @@ -29,4 +32,4 @@ type Icon = { fallbackIcon?: AvatarSource; }; -export type {Icon, PendingAction, PendingFields, ErrorFields, Errors, AvatarType}; +export type {Icon, PendingAction, PendingFields, ErrorFields, Errors, AvatarType, SimpleErrors}; diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 28fd451d002e..7f83c1de12c3 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -44,6 +44,7 @@ type Receipt = { path?: string; name?: string; source?: ImageSourcePropType; + filename?: string; state?: ValueOf; }; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index bf59c7520364..46de2b23bf32 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -56,7 +56,8 @@ import type ScreenShareRequest from './ScreenShareRequest'; import type SecurityGroup from './SecurityGroup'; import type Session from './Session'; import type Task from './Task'; -import type Transaction, {TransactionDraft} from './Transaction'; +import type Transaction from './Transaction'; +import type {TransactionDraft} from './Transaction'; import type {TransactionViolation, ViolationName} from './TransactionViolation'; import type User from './User'; import type UserLocation from './UserLocation'; From 372785bf1e756dda69023922023c917a3a17e8bb Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 4 Jan 2024 14:14:17 +0100 Subject: [PATCH 035/660] Migrate splitBill and splitBillAndOpenReport functions --- src/libs/actions/IOU.ts | 179 +++++++++++++++++++--------------- src/types/onyx/Transaction.ts | 21 +++- 2 files changed, 120 insertions(+), 80 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index dc92c1b9043b..94f2ec59ad9c 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -38,7 +38,7 @@ import type * as OnyxTypes from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; import type ReportAction from '@src/types/onyx/ReportAction'; import type {OnyxData} from '@src/types/onyx/Request'; -import type {Comment, Receipt, WaypointCollection} from '@src/types/onyx/Transaction'; +import type {Comment, Receipt, TaxRate, WaypointCollection} from '@src/types/onyx/Transaction'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import * as Policy from './Policy'; @@ -581,7 +581,7 @@ function buildOnyxDataForMoneyRequest( * it creates optimistic versions of them and uses those instead */ function getMoneyRequestInformation( - report: OnyxTypes.Report | EmptyObject, + report: OnyxEntry | EmptyObject, participant: Participant, comment: string, amount: number, @@ -1527,18 +1527,21 @@ function createSplitsAndOnyxData( } /** - * @param {Array} participants - * @param {String} currentUserLogin - * @param {Number} currentUserAccountID - * @param {Number} amount - always in smallest currency unit - * @param {String} comment - * @param {String} currency - * @param {String} merchant - * @param {String} category - * @param {String} tag - * @param {String} existingSplitChatReportID - Either a group DM or a workspace chat + * @param amount - always in smallest currency unit + * @param existingSplitChatReportID - Either a group DM or a workspace chat */ -function splitBill(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, merchant, category, tag, existingSplitChatReportID = '') { +function splitBill( + participants: Participant[], + currentUserLogin: string, + currentUserAccountID: number, + amount: number, + comment: string, + currency: string, + merchant: string, + category: string, + tag: string, + existingSplitChatReportID = '', +) { const {splitData, splits, onyxData} = createSplitsAndOnyxData( participants, currentUserLogin, @@ -1551,24 +1554,38 @@ function splitBill(participants, currentUserLogin, currentUserAccountID, amount, tag, existingSplitChatReportID, ); - API.write( - 'SplitBill', - { - reportID: splitData.chatReportID, - amount, - splits: JSON.stringify(splits), - currency, - comment, - category, - merchant, - tag, - transactionID: splitData.transactionID, - reportActionID: splitData.reportActionID, - createdReportActionID: splitData.createdReportActionID, - policyID: splitData.policyID, - }, - onyxData, - ); + + type SplitBillParams = { + reportID: string; + amount: number; + splits: string; + comment: string; + currency: string; + merchant: string; + category: string; + tag: string; + transactionID: string; + reportActionID: string; + createdReportActionID?: string; + policyID?: string; + }; + + const parameters: SplitBillParams = { + reportID: splitData.chatReportID, + amount, + splits: JSON.stringify(splits), + currency, + comment, + category, + merchant, + tag, + transactionID: splitData.transactionID, + reportActionID: splitData.reportActionID, + createdReportActionID: splitData.createdReportActionID, + policyID: splitData.policyID, + }; + + API.write('SplitBill', parameters, onyxData); resetMoneyRequestInfo(); Navigation.dismissModal(); @@ -1576,37 +1593,52 @@ function splitBill(participants, currentUserLogin, currentUserAccountID, amount, } /** - * @param {Array} participants - * @param {String} currentUserLogin - * @param {Number} currentUserAccountID - * @param {Number} amount - always in smallest currency unit - * @param {String} comment - * @param {String} currency - * @param {String} merchant - * @param {String} category - * @param {String} tag + * @param amount - always in smallest currency unit */ -function splitBillAndOpenReport(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, merchant, category, tag) { +function splitBillAndOpenReport( + participants: Participant[], + currentUserLogin: string, + currentUserAccountID: number, + amount: number, + comment: string, + currency: string, + merchant: string, + category: string, + tag: string, +) { const {splitData, splits, onyxData} = createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, merchant, category, tag); - API.write( - 'SplitBillAndOpenReport', - { - reportID: splitData.chatReportID, - amount, - splits: JSON.stringify(splits), - currency, - merchant, - comment, - category, - tag, - transactionID: splitData.transactionID, - reportActionID: splitData.reportActionID, - createdReportActionID: splitData.createdReportActionID, - policyID: splitData.policyID, - }, - onyxData, - ); + type SplitBillAndOpenReport = { + reportID: string; + amount: number; + splits: string; + currency: string; + merchant: string; + comment: string; + category: string; + tag: string; + transactionID: string; + reportActionID: string; + createdReportActionID?: string; + policyID?: string; + }; + + const parameters: SplitBillAndOpenReport = { + reportID: splitData.chatReportID, + amount, + splits: JSON.stringify(splits), + currency, + merchant, + comment, + category, + tag, + transactionID: splitData.transactionID, + reportActionID: splitData.reportActionID, + createdReportActionID: splitData.createdReportActionID, + policyID: splitData.policyID, + }; + + API.write('SplitBillAndOpenReport', parameters, onyxData); resetMoneyRequestInfo(); Navigation.dismissModal(splitData.chatReportID); @@ -3148,7 +3180,7 @@ function submitReport(expenseReport: OnyxTypes.Report) { }, ]; - if (parentReport.reportID) { + if (parentReport?.reportID) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${parentReport.reportID}`, @@ -3193,7 +3225,7 @@ function submitReport(expenseReport: OnyxTypes.Report) { }, ]; - if (parentReport.reportID) { + if (parentReport?.reportID) { failureData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${parentReport.reportID}`, @@ -3326,8 +3358,8 @@ function setMoneyRequestParticipantsFromReport(transactionID: string, report: On const chatReport = ReportUtils.isMoneyRequestReport(report) ? ReportUtils.getReport(report.chatReportID) : report; const currentUserAccountID = currentUserPersonalDetails.accountID; const participants: Participant[] = ReportUtils.isPolicyExpenseChat(chatReport) - ? [{reportID: chatReport.reportID, isPolicyExpenseChat: true, selected: true}] - : (chatReport.participantAccountIDs ?? []).filter((accountID) => currentUserAccountID !== accountID).map((accountID) => ({accountID, selected: true})); + ? [{reportID: chatReport?.reportID, isPolicyExpenseChat: true, selected: true}] + : (chatReport?.participantAccountIDs ?? []).filter((accountID) => currentUserAccountID !== accountID).map((accountID) => ({accountID, selected: true})); Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {participants, participantsAutoAssigned: true}); } @@ -3380,26 +3412,15 @@ function resetMoneyRequestTag() { Onyx.merge(ONYXKEYS.IOU, {tag: ''}); } -/** - * @param {String} transactionID - * @param {Object} taxRate - */ -function setMoneyRequestTaxRate(transactionID, taxRate) { +function setMoneyRequestTaxRate(transactionID: string, taxRate: TaxRate) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {taxRate}); } -/** - * @param {String} transactionID - * @param {Number} taxAmount - */ -function setMoneyRequestTaxAmount(transactionID, taxAmount) { +function setMoneyRequestTaxAmount(transactionID: string, taxAmount: number) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {taxAmount}); } -/** - * @param {Boolean} billable - */ -function setMoneyRequestBillable(billable) { +function setMoneyRequestBillable(billable: boolean) { Onyx.merge(ONYXKEYS.IOU, {billable}); } @@ -3447,8 +3468,8 @@ function navigateToNextPage(iou: OnyxEntry, iouType: string, repo if (!iou?.participants?.length || shouldReset) { const currentUserAccountID = currentUserPersonalDetails.accountID; const participants: Participant[] = ReportUtils.isPolicyExpenseChat(chatReport) - ? [{reportID: chatReport.reportID, isPolicyExpenseChat: true, selected: true}] - : (chatReport.participantAccountIDs ?? []).filter((accountID) => currentUserAccountID !== accountID).map((accountID) => ({accountID, selected: true})); + ? [{reportID: chatReport?.reportID, isPolicyExpenseChat: true, selected: true}] + : (chatReport?.participantAccountIDs ?? []).filter((accountID) => currentUserAccountID !== accountID).map((accountID) => ({accountID, selected: true})); setMoneyRequestParticipants(participants); resetMoneyRequestCategory(); resetMoneyRequestTag(); diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 7f83c1de12c3..986ed8b46d0a 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -55,6 +55,21 @@ type Route = { type Routes = Record; +type TaxRateData = { + name: string; + value: string; + code?: string; +}; + +type TaxRate = { + text: string; + keyForList: string; + searchText: string; + tooltipText: string; + isDisabled?: boolean; + data?: TaxRateData; +}; + type Transaction = { amount: number; billable: boolean; @@ -105,6 +120,10 @@ type Transaction = { type TransactionDraft = Partial & { isFromGlobalCreate?: boolean; + taxRate?: TaxRate; + + /** Calculated tax amount based on selected tax rate */ + taxAmount?: number; }; type AdditionalTransactionChanges = {comment?: string; waypoints?: WaypointCollection}; @@ -112,4 +131,4 @@ type AdditionalTransactionChanges = {comment?: string; waypoints?: WaypointColle type TransactionChanges = Partial & AdditionalTransactionChanges; export default Transaction; -export type {WaypointCollection, Comment, Receipt, Waypoint, TransactionDraft, TransactionChanges}; +export type {WaypointCollection, Comment, Receipt, Waypoint, TransactionDraft, TransactionChanges, TaxRate}; From 4e7fab0fdc150b97e5dc987a552c912000201785 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 4 Jan 2024 15:42:07 +0100 Subject: [PATCH 036/660] Migrate getUpdateMoneyRequestParams function --- src/libs/ReportUtils.ts | 12 ++-- src/libs/actions/IOU.ts | 132 +++++++++++++++++++++------------- src/types/onyx/Transaction.ts | 8 ++- 3 files changed, 94 insertions(+), 58 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 56d074a0ade8..60cba5cacfcc 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -21,7 +21,7 @@ import type {IOUMessage, OriginalMessageActionName, OriginalMessageCreated} from import type {Status} from '@src/types/onyx/PersonalDetails'; import type {NotificationPreference} from '@src/types/onyx/Report'; import type {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction'; -import type {Receipt, WaypointCollection} from '@src/types/onyx/Transaction'; +import type {Receipt, TransactionChanges, WaypointCollection} from '@src/types/onyx/Transaction'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject, isNotEmptyObject} from '@src/types/utils/EmptyObject'; @@ -250,7 +250,7 @@ type TransactionDetails = amount: number; currency: string; merchant: string; - waypoints?: WaypointCollection; + waypoints?: WaypointCollection | string; comment: string; category: string; billable: boolean; @@ -2090,7 +2090,7 @@ function getReportPreviewMessage( * * At the moment, we only allow changing one transaction field at a time. */ -function getModifiedExpenseOriginalMessage(oldTransaction: OnyxEntry, transactionChanges: ExpenseOriginalMessage, isFromExpenseReport: boolean): ExpenseOriginalMessage { +function getModifiedExpenseOriginalMessage(oldTransaction: OnyxEntry, transactionChanges: TransactionChanges, isFromExpenseReport: boolean): ExpenseOriginalMessage { const originalMessage: ExpenseOriginalMessage = {}; // Remark: Comment field is the only one which has new/old prefixes for the keys (newComment/ oldComment), // all others have old/- pattern such as oldCreated/created @@ -2856,9 +2856,9 @@ function buildOptimisticReportPreview( * Builds an optimistic modified expense action with a randomly generated reportActionID. */ function buildOptimisticModifiedExpenseReportAction( - transactionThread: OnyxEntry, + transactionThread: OnyxEntry, oldTransaction: OnyxEntry, - transactionChanges: ExpenseOriginalMessage, + transactionChanges: TransactionChanges, isFromExpenseReport: boolean, ): OptimisticModifiedExpenseReportAction { const originalMessage = getModifiedExpenseOriginalMessage(oldTransaction, transactionChanges, isFromExpenseReport); @@ -4413,4 +4413,4 @@ export { shouldDisableThread, }; -export type {ExpenseOriginalMessage, OptionData, OptimisticChatReport, OptimisticCreatedReportAction, OptimisticIOUReportAction}; +export type {ExpenseOriginalMessage, OptionData, OptimisticChatReport, OptimisticCreatedReportAction, OptimisticIOUReportAction, TransactionDetails}; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 94f2ec59ad9c..ad04d0a7bbd9 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -26,7 +26,7 @@ import Permissions from '@libs/Permissions'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; -import type {OptimisticChatReport, OptimisticCreatedReportAction, OptimisticIOUReportAction} from '@libs/ReportUtils'; +import type {OptimisticChatReport, OptimisticCreatedReportAction, OptimisticIOUReportAction, TransactionDetails} from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import * as UserUtils from '@libs/UserUtils'; import type {MoneyRequestNavigatorParamList} from '@navigation/types'; @@ -38,7 +38,7 @@ import type * as OnyxTypes from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; import type ReportAction from '@src/types/onyx/ReportAction'; import type {OnyxData} from '@src/types/onyx/Request'; -import type {Comment, Receipt, TaxRate, WaypointCollection} from '@src/types/onyx/Transaction'; +import type {Comment, Receipt, TaxRate, TransactionChanges, WaypointCollection} from '@src/types/onyx/Transaction'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import * as Policy from './Policy'; @@ -84,6 +84,17 @@ type SplitsAndOnyxData = { onyxData: OnyxData; }; +type UpdateMoneyRequestParams = Partial & { + reportID?: string; + transactionID: string; + reportActionID?: string; +}; + +type UpdateMoneyRequestData = { + params: UpdateMoneyRequestParams; + onyxData: OnyxData; +}; + let betas: OnyxTypes.Beta[] = []; Onyx.connect({ key: ONYXKEYS.BETAS, @@ -864,44 +875,55 @@ function createDistanceRequest( } /** - * @param {String} transactionID - * @param {String} transactionThreadReportID - * @param {Object} transactionChanges - * @param {String} [transactionChanges.created] Present when updated the date field - * @param {Boolean} onlyIncludeChangedFields - * When 'true', then the returned params will only include the transaction details for the fields that were changed. - * When `false`, then the returned params will include all the transaction details, regardless of which fields were changed. - * This setting is necessary while the UpdateDistanceRequest API is refactored to be fully 1:1:1 in https://github.com/Expensify/App/issues/28358 - * @returns {object} + * @param transactionChanges + * @param [transactionChanges.created] Present when updated the date field + * @param onlyIncludeChangedFields + * When 'true', then the returned params will only include the transaction details for the fields that were changed. + * When `false`, then the returned params will include all the transaction details, regardless of which fields were changed. + * This setting is necessary while the UpdateDistanceRequest API is refactored to be fully 1:1:1 in https://github.com/Expensify/App/issues/28358 */ -function getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, onlyIncludeChangedFields) { - const optimisticData = []; - const successData = []; - const failureData = []; +function getUpdateMoneyRequestParams( + transactionID: string, + transactionThreadReportID: string, + transactionChanges: TransactionChanges, + onlyIncludeChangedFields: boolean, +): UpdateMoneyRequestData { + const optimisticData: OnyxUpdate[] = []; + const successData: OnyxUpdate[] = []; + const failureData: OnyxUpdate[] = []; // Step 1: Set any "pending fields" (ones updated while the user was offline) to have error messages in the failureData - const pendingFields = _.mapObject(transactionChanges, () => CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE); - const clearedPendingFields = _.mapObject(transactionChanges, () => null); - const errorFields = _.mapObject(pendingFields, () => ({ - [DateUtils.getMicroseconds()]: Localize.translateLocal('iou.error.genericEditFailureMessage'), - })); + const pendingFields = Object.fromEntries(Object.keys(transactionChanges).map((key) => [key, CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE])); + const clearedPendingFields = Object.fromEntries(Object.keys(transactionChanges).map((key) => [key, null])); + const errorFields = Object.fromEntries( + Object.keys(pendingFields).map((key) => [ + key, + { + [DateUtils.getMicroseconds()]: Localize.translateLocal('iou.error.genericEditFailureMessage'), + }, + ]), + ); // Step 2: Get all the collections being updated - const transactionThread = allReports[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`]; - const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; - const iouReport = allReports[`${ONYXKEYS.COLLECTION.REPORT}${transactionThread.parentReportID}`]; + const transactionThread = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; + const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; + const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThread?.parentReportID}`] ?? null; const isFromExpenseReport = ReportUtils.isExpenseReport(iouReport); - const updatedTransaction = TransactionUtils.getUpdatedTransaction(transaction, transactionChanges, isFromExpenseReport); + const updatedTransaction = transaction ? TransactionUtils.getUpdatedTransaction(transaction, transactionChanges, isFromExpenseReport) : null; const transactionDetails = ReportUtils.getTransactionDetails(updatedTransaction); - // This needs to be a JSON string since we're sending this to the MapBox API - transactionDetails.waypoints = JSON.stringify(transactionDetails.waypoints); + if (transactionDetails?.waypoints) { + // This needs to be a JSON string since we're sending this to the MapBox API + transactionDetails.waypoints = JSON.stringify(transactionDetails.waypoints); + } - const dataToIncludeInParams = onlyIncludeChangedFields ? _.pick(transactionDetails, _.keys(transactionChanges)) : transactionDetails; + const dataToIncludeInParams: Partial = onlyIncludeChangedFields + ? (Object.fromEntries(Object.entries(transactionDetails ?? {}).filter(([key]) => Object.keys(transactionChanges).includes(key))) as Partial) + : transactionDetails; - const params = { + const params: UpdateMoneyRequestParams = { ...dataToIncludeInParams, - reportID: iouReport.reportID, + reportID: iouReport?.reportID, transactionID, }; @@ -909,29 +931,29 @@ function getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, t // We don't create a modified report action if we're updating the waypoints, // since there isn't actually any optimistic data we can create for them and the report action is created on the server // with the response from the MapBox API - if (!_.has(transactionChanges, 'waypoints')) { + if (!('waypoints' in transactionChanges)) { const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(transactionThread, transaction, transactionChanges, isFromExpenseReport); params.reportActionID = updatedReportAction.reportActionID; optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThread.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThread?.reportID}`, value: { - [updatedReportAction.reportActionID]: updatedReportAction, + [updatedReportAction.reportActionID]: updatedReportAction as OnyxTypes.ReportAction, }, }); successData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThread.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThread?.reportID}`, value: { [updatedReportAction.reportActionID]: {pendingAction: null}, }, }); failureData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThread.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThread?.reportID}`, value: { - [updatedReportAction.reportActionID]: updatedReportAction, + [updatedReportAction.reportActionID]: updatedReportAction as OnyxTypes.ReportAction, }, }); @@ -939,23 +961,25 @@ function getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, t // Should only update if the transaction matches the currency of the report, else we wait for the update // from the server with the currency conversion let updatedMoneyRequestReport = {...iouReport}; - if (updatedTransaction.currency === iouReport.currency && updatedTransaction.modifiedAmount) { + if (updatedTransaction?.currency === iouReport?.currency && updatedTransaction?.modifiedAmount) { const diff = TransactionUtils.getAmount(transaction, true) - TransactionUtils.getAmount(updatedTransaction, true); - if (ReportUtils.isExpenseReport(iouReport)) { + if (ReportUtils.isExpenseReport(iouReport) && typeof updatedMoneyRequestReport.total === 'number') { updatedMoneyRequestReport.total += diff; } else { - updatedMoneyRequestReport = IOUUtils.updateIOUOwnerAndTotal(iouReport, updatedReportAction.actorAccountID, diff, TransactionUtils.getCurrency(transaction), false); + updatedMoneyRequestReport = iouReport + ? IOUUtils.updateIOUOwnerAndTotal(iouReport, updatedReportAction.actorAccountID ?? -1, diff, TransactionUtils.getCurrency(transaction), false) + : {}; } updatedMoneyRequestReport.cachedTotal = CurrencyUtils.convertToDisplayString(updatedMoneyRequestReport.total, updatedTransaction.currency); optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, value: updatedMoneyRequestReport, }); successData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, value: {pendingAction: null}, }); } @@ -968,30 +992,36 @@ function getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, t value: { ...updatedTransaction, pendingFields, - isLoading: _.has(transactionChanges, 'waypoints'), + isLoading: 'waypoints' in transactionChanges, errorFields: null, }, }); // Update recently used categories if the category is changed - if (_.has(transactionChanges, 'category')) { - const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories(iouReport.policyID, transactionChanges.category); - if (!_.isEmpty(optimisticPolicyRecentlyUsedCategories)) { + if ('category' in transactionChanges) { + const optimisticPolicyRecentlyUsedCategories: OptimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories( + iouReport?.policyID, + transactionChanges.category, + ) as OptimisticPolicyRecentlyUsedCategories; + if (optimisticPolicyRecentlyUsedCategories.length) { optimisticData.push({ onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${iouReport.policyID}`, + key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${iouReport?.policyID}`, value: optimisticPolicyRecentlyUsedCategories, }); } } // Update recently used categories if the tag is changed - if (_.has(transactionChanges, 'tag')) { - const optimisticPolicyRecentlyUsedTags = Policy.buildOptimisticPolicyRecentlyUsedTags(iouReport.policyID, transactionChanges.tag); + if ('tag' in transactionChanges) { + const optimisticPolicyRecentlyUsedTags: OptimisticPolicyRecentlyUsedTags = Policy.buildOptimisticPolicyRecentlyUsedTags( + iouReport?.policyID, + transactionChanges.tag, + ) as OptimisticPolicyRecentlyUsedTags; if (!_.isEmpty(optimisticPolicyRecentlyUsedTags)) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${iouReport.policyID}`, + key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${iouReport?.policyID}`, value: optimisticPolicyRecentlyUsedTags, }); } @@ -1008,7 +1038,7 @@ function getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, t }, }); - if (_.has(transactionChanges, 'waypoints')) { + if ('waypoints' in transactionChanges) { // Delete the draft transaction when editing waypoints when the server responds successfully and there are no errors successData.push({ onyxMethod: Onyx.METHOD.SET, @@ -1031,8 +1061,8 @@ function getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, t // Reset the iouReport to it's original state failureData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, - value: iouReport, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, + value: iouReport as OnyxTypes.Report, }); return { diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 986ed8b46d0a..3875e70aff48 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -116,6 +116,7 @@ type Transaction = { /** If the transaction was made in a foreign currency, we send the original amount and currency */ originalAmount?: number; originalCurrency?: string; + isLoading?: boolean; }; type TransactionDraft = Partial & { @@ -126,7 +127,12 @@ type TransactionDraft = Partial & { taxAmount?: number; }; -type AdditionalTransactionChanges = {comment?: string; waypoints?: WaypointCollection}; +type AdditionalTransactionChanges = { + comment?: string; + waypoints?: WaypointCollection; + oldAmount?: number; + oldCurrency?: string; +}; type TransactionChanges = Partial & AdditionalTransactionChanges; From cdcb7d5b7b83572a0cbbeecb87e1b3259d9b074b Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 4 Jan 2024 15:51:47 +0100 Subject: [PATCH 037/660] Migrate updateMoneyRequestDate, updateDistanceRequest, editMoneyRequest, updateMoneyRequestAmountAndCurrency functions --- src/libs/ReportUtils.ts | 34 ++++++++++++++++------------------ src/libs/actions/IOU.ts | 34 ++++++---------------------------- 2 files changed, 22 insertions(+), 46 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 60cba5cacfcc..a4dadc25b469 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -244,23 +244,21 @@ type OptimisticTaskReport = Pick< | 'lastVisibleActionCreated' >; -type TransactionDetails = - | { - created: string; - amount: number; - currency: string; - merchant: string; - waypoints?: WaypointCollection | string; - comment: string; - category: string; - billable: boolean; - tag: string; - mccGroup?: ValueOf; - cardID: number; - originalAmount: number; - originalCurrency: string; - } - | undefined; +type TransactionDetails = { + created: string; + amount: number; + currency: string; + merchant: string; + waypoints?: WaypointCollection | string; + comment: string; + category: string; + billable: boolean; + tag: string; + mccGroup?: ValueOf; + cardID: number; + originalAmount: number; + originalCurrency: string; +}; type OptimisticIOUReport = Pick< Report, @@ -1787,7 +1785,7 @@ function getMoneyRequestReportName(report: OnyxEntry, policy: OnyxEntry< * into a flat object. Used for displaying transactions and sending them in API commands */ -function getTransactionDetails(transaction: OnyxEntry, createdDateFormat: string = CONST.DATE.FNS_FORMAT_STRING): TransactionDetails { +function getTransactionDetails(transaction: OnyxEntry, createdDateFormat: string = CONST.DATE.FNS_FORMAT_STRING): TransactionDetails | undefined { if (!transaction) { return; } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index ad04d0a7bbd9..114174d11e34 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -917,7 +917,7 @@ function getUpdateMoneyRequestParams( transactionDetails.waypoints = JSON.stringify(transactionDetails.waypoints); } - const dataToIncludeInParams: Partial = onlyIncludeChangedFields + const dataToIncludeInParams: Partial | undefined = onlyIncludeChangedFields ? (Object.fromEntries(Object.entries(transactionDetails ?? {}).filter(([key]) => Object.keys(transactionChanges).includes(key))) as Partial) : transactionDetails; @@ -1073,13 +1073,9 @@ function getUpdateMoneyRequestParams( /** * Updates the created date of a money request - * - * @param {String} transactionID - * @param {String} transactionThreadReportID - * @param {String} val */ -function updateMoneyRequestDate(transactionID, transactionThreadReportID, val) { - const transactionChanges = { +function updateMoneyRequestDate(transactionID: string, transactionThreadReportID: string, val: string) { + const transactionChanges: TransactionChanges = { created: val, }; const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, true); @@ -1089,16 +1085,8 @@ function updateMoneyRequestDate(transactionID, transactionThreadReportID, val) { /** * Edits an existing distance request * - * @param {String} transactionID - * @param {String} transactionThreadReportID - * @param {Object} transactionChanges - * @param {String} [transactionChanges.created] - * @param {Number} [transactionChanges.amount] - * @param {Object} [transactionChanges.comment] - * @param {Object} [transactionChanges.waypoints] - * */ -function updateDistanceRequest(transactionID, transactionThreadReportID, transactionChanges) { +function updateDistanceRequest(transactionID: string, transactionThreadReportID: string, transactionChanges: TransactionChanges) { const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, false); API.write('UpdateDistanceRequest', params, onyxData); } @@ -2415,12 +2403,7 @@ function editRegularMoneyRequest(transactionID, transactionThreadReportID, trans ); } -/** - * @param {object} transaction - * @param {String} transactionThreadReportID - * @param {Object} transactionChanges - */ -function editMoneyRequest(transaction, transactionThreadReportID, transactionChanges) { +function editMoneyRequest(transaction: OnyxTypes.Transaction, transactionThreadReportID: string, transactionChanges: TransactionChanges) { if (TransactionUtils.isDistanceRequest(transaction)) { updateDistanceRequest(transaction.transactionID, transactionThreadReportID, transactionChanges); } else { @@ -2430,13 +2413,8 @@ function editMoneyRequest(transaction, transactionThreadReportID, transactionCha /** * Updates the amount and currency fields of a money request - * - * @param {String} transactionID - * @param {String} transactionThreadReportID - * @param {String} currency - * @param {Number} amount */ -function updateMoneyRequestAmountAndCurrency(transactionID, transactionThreadReportID, currency, amount) { +function updateMoneyRequestAmountAndCurrency(transactionID: string, transactionThreadReportID: string, currency: string, amount: number) { const transactionChanges = { amount, currency, From c71ae90e0502c4fcc707bf6410c8522b5626f7b6 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 4 Jan 2024 16:31:58 +0100 Subject: [PATCH 038/660] Migrate setDraftSplitTransaction, editRegularMoneyRequest functions --- src/libs/actions/IOU.ts | 197 +++++++++++++++++++++------------------- 1 file changed, 103 insertions(+), 94 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 114174d11e34..41074a802b16 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -4,7 +4,7 @@ import {format} from 'date-fns'; import Str from 'expensify-common/lib/str'; import lodashGet from 'lodash/get'; import lodashHas from 'lodash/has'; -import {ImageSourcePropType} from 'react-native'; +import type {ImageSourcePropType} from 'react-native'; import Onyx from 'react-native-onyx'; import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import OnyxUtils from 'react-native-onyx/lib/utils'; @@ -39,7 +39,7 @@ import type {Participant} from '@src/types/onyx/IOU'; import type ReportAction from '@src/types/onyx/ReportAction'; import type {OnyxData} from '@src/types/onyx/Request'; import type {Comment, Receipt, TaxRate, TransactionChanges, WaypointCollection} from '@src/types/onyx/Transaction'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; +import {EmptyObject, isNotEmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import * as Policy from './Policy'; import * as Report from './Report'; @@ -1206,7 +1206,7 @@ function createSplitsAndOnyxData( existingSplitChatReportID || participants[0].reportID ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${existingSplitChatReportID || participants[0].reportID}`] : ReportUtils.getChatByParticipants(participantAccountIDs); - const splitChatReport = existingSplitChatReport || ReportUtils.buildOptimisticChatReport(participantAccountIDs); + const splitChatReport = existingSplitChatReport ?? ReportUtils.buildOptimisticChatReport(participantAccountIDs); const isOwnPolicyExpenseChat = splitChatReport.isOwnPolicyExpenseChat; const splitTransaction = TransactionUtils.buildOptimisticTransaction( @@ -2152,38 +2152,29 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA Report.notifyNewAction(chatReportID, sessionAccountID); } -/** - * @param {String} transactionID - * @param {Object} transactionChanges - */ -function setDraftSplitTransaction(transactionID, transactionChanges = {}) { +function setDraftSplitTransaction(transactionID: string, transactionChanges: TransactionChanges = {}) { let draftSplitTransaction = allDraftSplitTransactions[`${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`]; if (!draftSplitTransaction) { draftSplitTransaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; } - const updatedTransaction = TransactionUtils.getUpdatedTransaction(draftSplitTransaction, transactionChanges, false, false); + const updatedTransaction = TransactionUtils.getUpdatedTransaction(draftSplitTransaction as OnyxTypes.Transaction, transactionChanges, false, false); Onyx.merge(`${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`, updatedTransaction); } -/** - * @param {String} transactionID - * @param {Number} transactionThreadReportID - * @param {Object} transactionChanges - */ -function editRegularMoneyRequest(transactionID, transactionThreadReportID, transactionChanges) { +function editRegularMoneyRequest(transactionID: string, transactionThreadReportID: string, transactionChanges: TransactionChanges) { // STEP 1: Get all collections we're updating - const transactionThread = allReports[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`]; + const transactionThread = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; - const iouReport = allReports[`${ONYXKEYS.COLLECTION.REPORT}${transactionThread.parentReportID}`]; - const chatReport = allReports[`${ONYXKEYS.COLLECTION.REPORT}${iouReport.chatReportID}`]; + const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThread?.parentReportID}`] ?? null; + const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReport?.chatReportID}`] ?? null; const isFromExpenseReport = ReportUtils.isExpenseReport(iouReport); // STEP 2: Build new modified expense report action. const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(transactionThread, transaction, transactionChanges, isFromExpenseReport); - const updatedTransaction = TransactionUtils.getUpdatedTransaction(transaction, transactionChanges, isFromExpenseReport); + const updatedTransaction = TransactionUtils.getUpdatedTransaction(transaction as OnyxTypes.Transaction, transactionChanges, isFromExpenseReport); // STEP 3: Compute the IOU total and update the report preview message so LHN amount owed is correct // Should only update if the transaction matches the currency of the report, else we wait for the update @@ -2191,20 +2182,22 @@ function editRegularMoneyRequest(transactionID, transactionThreadReportID, trans let updatedMoneyRequestReport = {...iouReport}; const updatedChatReport = {...chatReport}; const diff = TransactionUtils.getAmount(transaction, true) - TransactionUtils.getAmount(updatedTransaction, true); - if (updatedTransaction.currency === iouReport.currency && updatedTransaction.modifiedAmount && diff !== 0) { - if (ReportUtils.isExpenseReport(iouReport)) { + if (updatedTransaction?.currency === iouReport?.currency && updatedTransaction?.modifiedAmount && diff !== 0) { + if (ReportUtils.isExpenseReport(iouReport) && updatedMoneyRequestReport.total) { updatedMoneyRequestReport.total += diff; } else { - updatedMoneyRequestReport = IOUUtils.updateIOUOwnerAndTotal(iouReport, updatedReportAction.actorAccountID, diff, TransactionUtils.getCurrency(transaction), false); + updatedMoneyRequestReport = iouReport + ? IOUUtils.updateIOUOwnerAndTotal(iouReport, updatedReportAction.actorAccountID ?? -1, diff, TransactionUtils.getCurrency(transaction), false) + : {}; } updatedMoneyRequestReport.cachedTotal = CurrencyUtils.convertToDisplayString(updatedMoneyRequestReport.total, updatedTransaction.currency); // Update the last message of the IOU report const lastMessage = ReportUtils.getIOUReportActionMessage( - iouReport.reportID, + iouReport?.reportID ?? '', CONST.IOU.REPORT_ACTION_TYPE.CREATE, - updatedMoneyRequestReport.total, + updatedMoneyRequestReport.total ?? 0, '', updatedTransaction.currency, '', @@ -2214,9 +2207,9 @@ function editRegularMoneyRequest(transactionID, transactionThreadReportID, trans updatedMoneyRequestReport.lastMessageHtml = lastMessage[0].html; // Update the last message of the chat report - const hasNonReimbursableTransactions = ReportUtils.hasNonReimbursableTransactions(iouReport); + const hasNonReimbursableTransactions = ReportUtils.hasNonReimbursableTransactions(iouReport?.reportID); // There was an error before - wrong value was sent const messageText = Localize.translateLocal(hasNonReimbursableTransactions ? 'iou.payerSpentAmount' : 'iou.payerOwesAmount', { - payer: ReportUtils.getPersonalDetailsForAccountID(updatedMoneyRequestReport.managerID).login || '', + payer: ReportUtils.getPersonalDetailsForAccountID(updatedMoneyRequestReport.managerID ?? -1).login || '', amount: CurrencyUtils.convertToDisplayString(updatedMoneyRequestReport.total, updatedMoneyRequestReport.currency), }); updatedChatReport.lastMessageText = messageText; @@ -2227,12 +2220,12 @@ function editRegularMoneyRequest(transactionID, transactionThreadReportID, trans // STEP 4: Compose the optimistic data const currentTime = DateUtils.getDBTime(); - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThread.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThread?.reportID}`, value: { - [updatedReportAction.reportActionID]: updatedReportAction, + [updatedReportAction.reportActionID]: updatedReportAction as OnyxTypes.ReportAction, }, }, { @@ -2242,12 +2235,12 @@ function editRegularMoneyRequest(transactionID, transactionThreadReportID, trans }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, value: updatedMoneyRequestReport, }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.chatReportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.chatReportID}`, value: updatedChatReport, }, { @@ -2258,58 +2251,62 @@ function editRegularMoneyRequest(transactionID, transactionThreadReportID, trans lastVisibleActionCreated: currentTime, }, }, - ...(!isScanning - ? [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.reportID}`, - value: { - [transactionThread.parentReportActionID]: { - whisperedToAccountIDs: [], - }, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.parentReportID}`, - value: { - [iouReport.parentReportActionID]: { - whisperedToAccountIDs: [], - }, - }, - }, - ] - : []), ]; + if (!isScanning) { + optimisticData.push( + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport?.reportID}`, + value: { + [transactionThread?.parentReportActionID ?? '']: { + whisperedToAccountIDs: [], + }, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport?.parentReportID}`, + value: { + [iouReport?.parentReportActionID ?? '']: { + whisperedToAccountIDs: [], + }, + }, + }, + ); + } + // Update recently used categories if the category is changed - if (_.has(transactionChanges, 'category')) { - const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories(iouReport.policyID, transactionChanges.category); - if (!_.isEmpty(optimisticPolicyRecentlyUsedCategories)) { + if ('category' in transactionChanges) { + const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories( + iouReport?.policyID, + transactionChanges.category, + ) as OptimisticPolicyRecentlyUsedCategories; + if (optimisticPolicyRecentlyUsedCategories.length) { optimisticData.push({ onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${iouReport.policyID}`, + key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${iouReport?.policyID}`, value: optimisticPolicyRecentlyUsedCategories, }); } } // Update recently used categories if the tag is changed - if (_.has(transactionChanges, 'tag')) { - const optimisticPolicyRecentlyUsedTags = Policy.buildOptimisticPolicyRecentlyUsedTags(iouReport.policyID, transactionChanges.tag); - if (!_.isEmpty(optimisticPolicyRecentlyUsedTags)) { + if ('tag' in transactionChanges) { + const optimisticPolicyRecentlyUsedTags = Policy.buildOptimisticPolicyRecentlyUsedTags(iouReport?.policyID, transactionChanges.tag) as OptimisticPolicyRecentlyUsedTags; + if (isNotEmptyObject(optimisticPolicyRecentlyUsedTags)) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${iouReport.policyID}`, + key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${iouReport?.policyID}`, value: optimisticPolicyRecentlyUsedTags, }); } } - const successData = [ + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThread.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThread?.reportID}`, value: { [updatedReportAction.reportActionID]: {pendingAction: null}, }, @@ -2332,15 +2329,15 @@ function editRegularMoneyRequest(transactionID, transactionThreadReportID, trans }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, value: {pendingAction: null}, }, ]; - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThread.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThread?.reportID}`, value: { [updatedReportAction.reportActionID]: { errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericEditFailureMessage'), @@ -2352,55 +2349,67 @@ function editRegularMoneyRequest(transactionID, transactionThreadReportID, trans key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, value: { ...transaction, - modifiedCreated: transaction.modifiedCreated ? transaction.modifiedCreated : null, - modifiedAmount: transaction.modifiedAmount ? transaction.modifiedAmount : null, - modifiedCurrency: transaction.modifiedCurrency ? transaction.modifiedCurrency : null, - modifiedMerchant: transaction.modifiedMerchant ? transaction.modifiedMerchant : null, - modifiedWaypoints: transaction.modifiedWaypoints ? transaction.modifiedWaypoints : null, + modifiedCreated: transaction?.modifiedCreated ? transaction.modifiedCreated : null, + modifiedAmount: transaction?.modifiedAmount ? transaction.modifiedAmount : null, + modifiedCurrency: transaction?.modifiedCurrency ? transaction.modifiedCurrency : null, + modifiedMerchant: transaction?.modifiedMerchant ? transaction.modifiedMerchant : null, + modifiedWaypoints: transaction?.modifiedWaypoints ? transaction.modifiedWaypoints : null, pendingFields: null, }, }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, value: { ...iouReport, - cachedTotal: iouReport.cachedTotal ? iouReport.cachedTotal : null, + cachedTotal: iouReport?.cachedTotal ? iouReport?.cachedTotal : null, }, }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.chatReportID}`, - value: chatReport, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.chatReportID}`, + value: chatReport as OnyxTypes.Report, }, { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`, value: { - lastReadTime: transactionThread.lastReadTime, - lastVisibleActionCreated: transactionThread.lastVisibleActionCreated, + lastReadTime: transactionThread?.lastReadTime, + lastVisibleActionCreated: transactionThread?.lastVisibleActionCreated, }, }, ]; // STEP 6: Call the API endpoint - const {created, amount, currency, comment, merchant, category, billable, tag} = ReportUtils.getTransactionDetails(updatedTransaction); - API.write( - 'EditMoneyRequest', - { - transactionID, - reportActionID: updatedReportAction.reportActionID, - created, - amount, - currency, - comment, - merchant, - category, - billable, - tag, - }, - {optimisticData, successData, failureData}, - ); + const {created, amount, currency, comment, merchant, category, billable, tag} = ReportUtils.getTransactionDetails(updatedTransaction) ?? {}; + + type EditMoneyRequestParams = { + transactionID: string; + reportActionID: string; + created?: string; + amount?: number; + currency?: string; + comment?: string; + merchant?: string; + category?: string; + billable?: boolean; + tag?: string; + }; + + const parameters: EditMoneyRequestParams = { + transactionID, + reportActionID: updatedReportAction.reportActionID, + created, + amount, + currency, + comment, + merchant, + category, + billable, + tag, + }; + + API.write('EditMoneyRequest', parameters, {optimisticData, successData, failureData}); } function editMoneyRequest(transaction: OnyxTypes.Transaction, transactionThreadReportID: string, transactionChanges: TransactionChanges) { From 9aad84a4a6cc0ce9e1672880345f4aa31a4ee9d9 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 4 Jan 2024 17:21:41 +0100 Subject: [PATCH 039/660] Migrate getSendMoneyParams function --- src/libs/actions/IOU.ts | 136 +++++++++++++++++++++++----------------- 1 file changed, 79 insertions(+), 57 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 41074a802b16..ac41becf0e1c 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -39,8 +39,9 @@ import type {Participant} from '@src/types/onyx/IOU'; import type ReportAction from '@src/types/onyx/ReportAction'; import type {OnyxData} from '@src/types/onyx/Request'; import type {Comment, Receipt, TaxRate, TransactionChanges, WaypointCollection} from '@src/types/onyx/Transaction'; -import {EmptyObject, isNotEmptyObject} from '@src/types/utils/EmptyObject'; -import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import DeepValueOf from '@src/types/utils/DeepValueOf'; +import type {EmptyObject} from '@src/types/utils/EmptyObject'; +import {isEmptyObject, isNotEmptyObject} from '@src/types/utils/EmptyObject'; import * as Policy from './Policy'; import * as Report from './Report'; @@ -95,6 +96,26 @@ type UpdateMoneyRequestData = { onyxData: OnyxData; }; +type PaymentMethodType = DeepValueOf; + +type SendMoneyParams = { + iouReportID: string; + chatReportID: string; + reportActionID: string; + paymentMethodType: PaymentMethodType; + transactionID: string; + newIOUReportDetails: string; + createdReportActionID: string; + reportPreviewReportActionID: string; +}; + +type SendMoneyParamsData = { + params: SendMoneyParams; + optimisticData: OnyxUpdate[]; + successData: OnyxUpdate[]; + failureData: OnyxUpdate[]; +}; + let betas: OnyxTypes.Beta[] = []; Onyx.connect({ key: ONYXKEYS.BETAS, @@ -2693,17 +2714,20 @@ function deleteMoneyRequest(transactionID, reportAction, isSingleTransactionView } /** - * @param {Object} report - * @param {Number} amount - * @param {String} currency - * @param {String} comment - * @param {String} paymentMethodType - * @param {String} managerID - Account ID of the person sending the money - * @param {Object} recipient - The user receiving the money + * @param managerID - Account ID of the person sending the money + * @param recipient - The user receiving the money * @returns {Object} */ -function getSendMoneyParams(report, amount, currency, comment, paymentMethodType, managerID, recipient) { - const recipientEmail = OptionsListUtils.addSMSDomainIfPhoneNumber(recipient.login); +function getSendMoneyParams( + report: OnyxTypes.Report, + amount: number, + currency: string, + comment: string, + paymentMethodType: PaymentMethodType, + managerID: number, + recipient: Participant, +): SendMoneyParamsData { + const recipientEmail = OptionsListUtils.addSMSDomainIfPhoneNumber(recipient.login ?? ''); const recipientAccountID = Number(recipient.accountID); const newIOUReportDetails = JSON.stringify({ amount, @@ -2726,7 +2750,7 @@ function getSendMoneyParams(report, amount, currency, comment, paymentMethodType const optimisticIOUReport = ReportUtils.buildOptimisticIOUReport(recipientAccountID, managerID, amount, chatReport.reportID, currency, true); const optimisticTransaction = TransactionUtils.buildOptimisticTransaction(amount, currency, optimisticIOUReport.reportID, comment); - const optimisticTransactionData = { + const optimisticTransactionData: OnyxUpdate = { onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${optimisticTransaction.transactionID}`, value: optimisticTransaction, @@ -2749,36 +2773,49 @@ function getSendMoneyParams(report, amount, currency, comment, paymentMethodType const reportPreviewAction = ReportUtils.buildOptimisticReportPreview(chatReport, optimisticIOUReport); - // First, add data that will be used in all cases - const optimisticChatReportData = { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, - value: { - ...chatReport, - lastReadTime: DateUtils.getDBTime(), - lastVisibleActionCreated: reportPreviewAction.created, - }, - }; - const optimisticIOUReportData = { + // 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 + let optimisticChatReportData: OnyxUpdate = isNewChat + ? { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, + value: { + ...chatReport, + // Set and clear pending fields on the chat report + pendingFields: {createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}, + lastReadTime: DateUtils.getDBTime(), + lastVisibleActionCreated: reportPreviewAction.created, + }, + } + : { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, + value: { + ...chatReport, + lastReadTime: DateUtils.getDBTime(), + lastVisibleActionCreated: reportPreviewAction.created, + }, + }; + const optimisticIOUReportData: OnyxUpdate = { onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT}${optimisticIOUReport.reportID}`, value: { ...optimisticIOUReport, - lastMessageText: optimisticIOUReportAction.message[0].text, - lastMessageHtml: optimisticIOUReportAction.message[0].html, + lastMessageText: optimisticIOUReportAction.message?.[0].text, + lastMessageHtml: optimisticIOUReportAction.message?.[0].html, }, }; - const optimisticIOUReportActionsData = { + const optimisticIOUReportActionsData: OnyxUpdate = { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${optimisticIOUReport.reportID}`, value: { [optimisticIOUReportAction.reportActionID]: { - ...optimisticIOUReportAction, + ...(optimisticIOUReportAction as OnyxTypes.ReportAction), pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, }, }, }; - const optimisticChatReportActionsData = { + const optimisticChatReportActionsData: OnyxUpdate = { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport.reportID}`, value: { @@ -2786,7 +2823,7 @@ function getSendMoneyParams(report, amount, currency, comment, paymentMethodType }, }; - const successData = [ + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${optimisticIOUReport.reportID}`, @@ -2812,7 +2849,7 @@ function getSendMoneyParams(report, amount, currency, comment, paymentMethodType }, ]; - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${optimisticTransaction.transactionID}`, @@ -2822,26 +2859,19 @@ function getSendMoneyParams(report, amount, currency, comment, paymentMethodType }, ]; - let optimisticPersonalDetailListData = {}; + let optimisticPersonalDetailListData: OnyxUpdate | EmptyObject = {}; // Now, let's add the data we need just when we are creating a new chat report if (isNewChat) { - // 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 - optimisticChatReportData.onyxMethod = Onyx.METHOD.SET; - optimisticIOUReportData.onyxMethod = Onyx.METHOD.SET; - - // Set and clear pending fields on the chat report - optimisticChatReportData.value.pendingFields = {createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}; successData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: optimisticChatReportData.key, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, value: {pendingFields: null}, }); failureData.push( { onyxMethod: Onyx.METHOD.MERGE, - key: optimisticChatReportData.key, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, value: { errorFields: { createChat: ErrorUtils.getMicroSecondOnyxError('report.genericCreateReportFailureMessage'), @@ -2887,8 +2917,8 @@ function getSendMoneyParams(report, amount, currency, comment, paymentMethodType }); } - const optimisticData = [optimisticChatReportData, optimisticIOUReportData, optimisticChatReportActionsData, optimisticIOUReportActionsData, optimisticTransactionData]; - if (!_.isEmpty(optimisticPersonalDetailListData)) { + const optimisticData: OnyxUpdate[] = [optimisticChatReportData, optimisticIOUReportData, optimisticChatReportActionsData, optimisticIOUReportActionsData, optimisticTransactionData]; + if (isNotEmptyObject(optimisticPersonalDetailListData)) { optimisticData.push(optimisticPersonalDetailListData); } @@ -2900,7 +2930,7 @@ function getSendMoneyParams(report, amount, currency, comment, paymentMethodType paymentMethodType, transactionID: optimisticTransaction.transactionID, newIOUReportDetails, - createdReportActionID: isNewChat ? optimisticCreatedAction.reportActionID : 0, + createdReportActionID: isNewChat ? optimisticCreatedAction.reportActionID : '', reportPreviewReportActionID: reportPreviewAction.reportActionID, }, optimisticData, @@ -3052,14 +3082,10 @@ function getPayMoneyRequestParams(chatReport, iouReport, recipient, paymentMetho } /** - * @param {Object} report - * @param {Number} amount - * @param {String} currency - * @param {String} comment - * @param {String} managerID - Account ID of the person sending the money - * @param {Object} recipient - The user receiving the money + * @param managerID - Account ID of the person sending the money + * @param recipient - The user receiving the money */ -function sendMoneyElsewhere(report, amount, currency, comment, managerID, recipient) { +function sendMoneyElsewhere(report: OnyxTypes.Report, amount: number, currency: string, comment: string, managerID: number, recipient: Participant) { const {params, optimisticData, successData, failureData} = getSendMoneyParams(report, amount, currency, comment, CONST.IOU.PAYMENT_TYPE.ELSEWHERE, managerID, recipient); API.write('SendMoneyElsewhere', params, {optimisticData, successData, failureData}); @@ -3070,14 +3096,10 @@ function sendMoneyElsewhere(report, amount, currency, comment, managerID, recipi } /** - * @param {Object} report - * @param {Number} amount - * @param {String} currency - * @param {String} comment - * @param {String} managerID - Account ID of the person sending the money - * @param {Object} recipient - The user receiving the money + * @param managerID - Account ID of the person sending the money + * @param recipient - The user receiving the money */ -function sendMoneyWithWallet(report, amount, currency, comment, managerID, recipient) { +function sendMoneyWithWallet(report: OnyxTypes.Report, amount: number, currency: string, comment: string, managerID: number, recipient: Participant) { const {params, optimisticData, successData, failureData} = getSendMoneyParams(report, amount, currency, comment, CONST.IOU.PAYMENT_TYPE.EXPENSIFY, managerID, recipient); API.write('SendMoneyWithWallet', params, {optimisticData, successData, failureData}); From 4d9d193f09ef42b84d728615b1853993b7474b56 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 5 Jan 2024 16:13:22 +0700 Subject: [PATCH 040/660] set default category for distance request --- src/pages/iou/request/step/IOURequestStepConfirmation.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.js b/src/pages/iou/request/step/IOURequestStepConfirmation.js index d41442edd670..3ff22ba218ab 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.js +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.js @@ -115,6 +115,15 @@ function IOURequestStepConfirmation({ IOU.setMoneyRequestBillable_temporaryForRefactor(transactionID, defaultBillable); }, [transactionID, defaultBillable]); + const defaultCategory = _.chain(policy) + .get('customUnits') + .find((customUnit) => customUnit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE) + .get('defaultCategory') + .value(); + useEffect(() => { + IOU.setMoneyRequestCategory_temporaryForRefactor(transactionID, defaultCategory); + }, [transactionID, defaultCategory]); + const navigateBack = useCallback(() => { // If there is not a report attached to the IOU with a reportID, then the participants were manually selected and the user needs taken // back to the participants step From 62f1fb19cf7f9c62ed0245876ba6f5551961ed5d Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 5 Jan 2024 16:26:21 +0700 Subject: [PATCH 041/660] use lodashGet instead --- .../iou/request/step/IOURequestStepConfirmation.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.js b/src/pages/iou/request/step/IOURequestStepConfirmation.js index 3ff22ba218ab..27d284eb4965 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.js +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.js @@ -115,11 +115,11 @@ function IOURequestStepConfirmation({ IOU.setMoneyRequestBillable_temporaryForRefactor(transactionID, defaultBillable); }, [transactionID, defaultBillable]); - const defaultCategory = _.chain(policy) - .get('customUnits') - .find((customUnit) => customUnit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE) - .get('defaultCategory') - .value(); + const defaultCategory = lodashGet( + _.find(lodashGet(policy, 'customUnits', {}), (customUnit) => customUnit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE), + 'defaultCategory', + '', + ); useEffect(() => { IOU.setMoneyRequestCategory_temporaryForRefactor(transactionID, defaultCategory); }, [transactionID, defaultCategory]); From dcdf95b12e86c34f0efb4f366c0b01ee02fabf0b Mon Sep 17 00:00:00 2001 From: Yauheni Date: Fri, 5 Jan 2024 13:30:24 +0300 Subject: [PATCH 042/660] Update types --- src/components/Popover/types.ts | 13 ++-------- src/components/PopoverMenu.tsx | 2 +- src/components/SettlementButton.tsx | 37 ++++++++++++++++------------- src/types/onyx/AnchorAlignment.ts | 12 ++++++++++ 4 files changed, 35 insertions(+), 29 deletions(-) create mode 100644 src/types/onyx/AnchorAlignment.ts diff --git a/src/components/Popover/types.ts b/src/components/Popover/types.ts index 3d1f95822e6a..4875676a3caa 100644 --- a/src/components/Popover/types.ts +++ b/src/components/Popover/types.ts @@ -1,16 +1,7 @@ -import type {ValueOf} from 'type-fest'; import type {PopoverAnchorPosition} from '@components/Modal/types'; import type BaseModalProps from '@components/Modal/types'; import type {WindowDimensionsProps} from '@components/withWindowDimensions/types'; -import type CONST from '@src/CONST'; - -type AnchorAlignment = { - /** The horizontal anchor alignment of the popover */ - horizontal: ValueOf; - - /** The vertical anchor alignment of the popover */ - vertical: ValueOf; -}; +import type AnchorAlignment from '@src/types/onyx/AnchorAlignment'; type PopoverDimensions = { width: number; @@ -48,4 +39,4 @@ type PopoverProps = BaseModalProps & { type PopoverWithWindowDimensionsProps = PopoverProps & WindowDimensionsProps; -export type {PopoverProps, PopoverWithWindowDimensionsProps, AnchorAlignment}; +export type {PopoverProps, PopoverWithWindowDimensionsProps}; diff --git a/src/components/PopoverMenu.tsx b/src/components/PopoverMenu.tsx index 502bdbf83b53..9b59b87056e0 100644 --- a/src/components/PopoverMenu.tsx +++ b/src/components/PopoverMenu.tsx @@ -10,8 +10,8 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import CONST from '@src/CONST'; import type {AnchorPosition} from '@src/styles'; +import type AnchorAlignment from '@src/types/onyx/AnchorAlignment'; import MenuItem from './MenuItem'; -import type {AnchorAlignment} from './Popover/types'; import PopoverWithMeasuredContent from './PopoverWithMeasuredContent'; import Text from './Text'; diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton.tsx index 3eb4ac844fb2..f09a31701536 100644 --- a/src/components/SettlementButton.tsx +++ b/src/components/SettlementButton.tsx @@ -1,7 +1,9 @@ import _ from 'lodash'; -import React, {MutableRefObject, useEffect, useMemo} from 'react'; -import {StyleProp, ViewStyle} from 'react-native'; -import {OnyxEntry, withOnyx} from 'react-native-onyx'; +import type {MutableRefObject} from 'react'; +import React, {useEffect, useMemo} from 'react'; +import type {StyleProp, ViewStyle} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import * as ReportUtils from '@libs/ReportUtils'; @@ -11,30 +13,29 @@ import * as PaymentMethods from '@userActions/PaymentMethods'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import {ButtonSizeValue} from '@src/styles/utils/types'; +import type {ButtonSizeValue} from '@src/styles/utils/types'; import type {Report} from '@src/types/onyx'; -import {EmptyObject} from '@src/types/utils/EmptyObject'; +import type AnchorAlignment from '@src/types/onyx/AnchorAlignment'; +import type DeepValueOf from '@src/types/utils/DeepValueOf'; +import type {EmptyObject} from '@src/types/utils/EmptyObject'; import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; import * as Expensicons from './Icon/Expensicons'; import KYCWall from './KYCWall'; -type AnchorAlignment = { - horizontal: 'left' | 'right' | 'center'; - vertical: 'top' | 'center' | 'bottom'; -}; - type TriggerKYCFlow = (event: Event, iouPaymentType: string) => void; +type PaymentType = DeepValueOf; + type SettlementButtonOnyxProps = { /** The last payment method used per policy */ - nvpLastPaymentMethod?: Record; + nvpLastPaymentMethod?: OnyxEntry>; }; type SettlementButtonProps = SettlementButtonOnyxProps & { /** Callback to execute when this button is pressed. Receives a single payment type argument. */ - onPress: (paymentType: string) => void; + onPress: (paymentType: PaymentType) => void; /** The route to redirect if user does not have a payment method setup */ - enablePaymentsRoute: string; + enablePaymentsRoute: typeof ROUTES.ENABLE_PAYMENTS | typeof ROUTES.IOU_SEND_ENABLE_PAYMENTS | typeof ROUTES.SETTINGS_ENABLE_PAYMENTS; /** Call the onPress function on main button when Enter key is pressed */ pressOnEnter?: boolean; /** Settlement currency type */ @@ -98,7 +99,7 @@ function SettlementButton({ policyID = '', shouldHidePaymentOptions = false, shouldShowApproveButton = false, - style = [], + style, shouldShowPersonalBankAccountOption = false, }: SettlementButtonProps) { const {translate} = useLocalize(); @@ -142,7 +143,7 @@ function SettlementButton({ // To achieve the one tap pay experience we need to choose the correct payment type as default, // if user already paid for some request or expense, let's use the last payment method or use default. - const paymentMethod = nvpLastPaymentMethod[policyID] || ''; + const paymentMethod = nvpLastPaymentMethod?.[policyID] ?? ''; if (canUseWallet) { buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.EXPENSIFY]); } @@ -162,7 +163,7 @@ function SettlementButton({ return buttonOptions; }, [currency, formattedAmount, iouReport, nvpLastPaymentMethod, policyID, translate, shouldHidePaymentOptions, shouldShowApproveButton]); - const selectPaymentType = (event: Event, iouPaymentType: string, triggerKYCFlow: TriggerKYCFlow) => { + const selectPaymentType = (event: Event, iouPaymentType: PaymentType, triggerKYCFlow: TriggerKYCFlow) => { if (iouPaymentType === CONST.IOU.PAYMENT_TYPE.EXPENSIFY || iouPaymentType === CONST.IOU.PAYMENT_TYPE.VBBA) { triggerKYCFlow(event, iouPaymentType); BankAccounts.setPersonalBankAccountContinueKYCOnSuccess(ROUTES.ENABLE_PAYMENTS); @@ -195,7 +196,9 @@ function SettlementButton({ buttonRef={buttonRef} isDisabled={isDisabled} isLoading={isLoading} - onPress={(event: Event, iouPaymentType: string) => selectPaymentType(event, iouPaymentType, triggerKYCFlow)} + onPress={(event: Event, iouPaymentType: PaymentType) => + selectPaymentType(event, iouPaymentType, triggerKYCFlow) + } pressOnEnter={pressOnEnter} options={paymentButtonOptions} style={style as Record} diff --git a/src/types/onyx/AnchorAlignment.ts b/src/types/onyx/AnchorAlignment.ts new file mode 100644 index 000000000000..899e3d9e277b --- /dev/null +++ b/src/types/onyx/AnchorAlignment.ts @@ -0,0 +1,12 @@ +import type {ValueOf} from 'type-fest'; +import type CONST from '@src/CONST'; + +type AnchorAlignment = { + /** The horizontal anchor alignment of the popover */ + horizontal: ValueOf; + + /** The vertical anchor alignment of the popover */ + vertical: ValueOf; +}; + +export default AnchorAlignment; From f3004da4052942de46f8134f1faa9a9434794593 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Fri, 5 Jan 2024 15:01:21 +0300 Subject: [PATCH 043/660] Update types x2 --- src/components/SettlementButton.tsx | 39 +++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton.tsx index f09a31701536..a11481c99cb1 100644 --- a/src/components/SettlementButton.tsx +++ b/src/components/SettlementButton.tsx @@ -1,7 +1,6 @@ -import _ from 'lodash'; import type {MutableRefObject} from 'react'; import React, {useEffect, useMemo} from 'react'; -import type {StyleProp, ViewStyle} from 'react-native'; +import type {GestureResponderEvent, StyleProp, View, ViewStyle} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import useLocalize from '@hooks/useLocalize'; @@ -11,6 +10,7 @@ import * as BankAccounts from '@userActions/BankAccounts'; import * as IOU from '@userActions/IOU'; import * as PaymentMethods from '@userActions/PaymentMethods'; import CONST from '@src/CONST'; +import type {OnyxValues} from '@src/ONYXKEYS'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {ButtonSizeValue} from '@src/styles/utils/types'; @@ -22,52 +22,72 @@ import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; import * as Expensicons from './Icon/Expensicons'; import KYCWall from './KYCWall'; +type Event = GestureResponderEvent | KeyboardEvent; + type TriggerKYCFlow = (event: Event, iouPaymentType: string) => void; type PaymentType = DeepValueOf; type SettlementButtonOnyxProps = { /** The last payment method used per policy */ - nvpLastPaymentMethod?: OnyxEntry>; + nvpLastPaymentMethod?: OnyxEntry; }; type SettlementButtonProps = SettlementButtonOnyxProps & { /** Callback to execute when this button is pressed. Receives a single payment type argument. */ onPress: (paymentType: PaymentType) => void; + /** The route to redirect if user does not have a payment method setup */ enablePaymentsRoute: typeof ROUTES.ENABLE_PAYMENTS | typeof ROUTES.IOU_SEND_ENABLE_PAYMENTS | typeof ROUTES.SETTINGS_ENABLE_PAYMENTS; + /** Call the onPress function on main button when Enter key is pressed */ pressOnEnter?: boolean; + /** Settlement currency type */ currency?: string; + /** When the button is opened via an IOU, ID for the chatReport that the IOU is linked to */ chatReportID?: string; + /** The IOU/Expense report we are paying */ iouReport?: OnyxEntry | EmptyObject; + /** Should we show the payment options? */ shouldHidePaymentOptions?: boolean; + /** Should we show the payment options? */ shouldShowApproveButton?: boolean; + /** The policyID of the report we are paying */ policyID?: string; + /** Additional styles to add to the component */ style?: StyleProp; + /** Total money amount in form */ formattedAmount?: string; + /** The size of button size */ - buttonSize?: ButtonSizeValue; // Replace with the actual sizes + buttonSize?: ButtonSizeValue; + /** Route for the Add Bank Account screen for a given navigation stack */ addBankAccountRoute?: string; + /** Route for the Add Debit Card screen for a given navigation stack */ addDebitCardRoute?: string; + /** Whether the button should be disabled */ isDisabled?: boolean; + /** Whether we should show a loading state for the main button */ isLoading?: boolean; + /** The anchor alignment of the popover menu for payment method dropdown */ paymentMethodDropdownAnchorAlignment?: AnchorAlignment; + /** The anchor alignment of the popover menu for KYC wall popover */ kycWallAnchorAlignment?: AnchorAlignment; + /** Whether the personal bank account option should be shown */ shouldShowPersonalBankAccountOption?: boolean; }; @@ -158,7 +178,7 @@ function SettlementButton({ // Put the preferred payment method to the front of the array so its shown as default if (paymentMethod) { - return _.sortBy(buttonOptions, (method) => (method.value === paymentMethod ? 0 : 1)); + return buttonOptions.sort((method) => (method.value === paymentMethod ? 0 : 1)); } return buttonOptions; }, [currency, formattedAmount, iouReport, nvpLastPaymentMethod, policyID, translate, shouldHidePaymentOptions, shouldShowApproveButton]); @@ -191,17 +211,16 @@ function SettlementButton({ anchorAlignment={kycWallAnchorAlignment} shouldShowPersonalBankAccountOption={shouldShowPersonalBankAccountOption} > - {(triggerKYCFlow: TriggerKYCFlow, buttonRef: MutableRefObject) => ( + {(triggerKYCFlow: TriggerKYCFlow, buttonRef: MutableRefObject) => ( - selectPaymentType(event, iouPaymentType, triggerKYCFlow) - } + onPress={(event: Event, iouPaymentType: PaymentType) => selectPaymentType(event, iouPaymentType, triggerKYCFlow)} pressOnEnter={pressOnEnter} options={paymentButtonOptions} - style={style as Record} + // @ts-expect-error TODO: Remove this once OptionsListUtils (https://github.com/Expensify/App/issues/25065) is migrated to TypeScript. + style={style} buttonSize={buttonSize} anchorAlignment={paymentMethodDropdownAnchorAlignment} /> From e92901687bd21152499eca4cde4167d0f68e58da Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Sat, 6 Jan 2024 19:01:19 +0530 Subject: [PATCH 044/660] fixed currency selection on confirm step --- src/libs/actions/IOU.js | 9 +++++++++ .../iou/request/step/IOURequestStepAmount.js | 19 ++++++++++++++++--- .../request/step/IOURequestStepCurrency.js | 4 +--- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index fb4e9f02f1b6..37e72b2133e3 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -198,6 +198,14 @@ function setMoneyRequestCurrency_temporaryForRefactor(transactionID, currency) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {currency}); } +/** + * @param {String} transactionID + * @param {String} originalCurrency + */ +function setMoneyRequestOriginalCurrency_temporaryForRefactor(transactionID, originalCurrency) { + Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {originalCurrency}); +} + /** * @param {String} transactionID * @param {String} comment @@ -3566,6 +3574,7 @@ export { setMoneyRequestCategory_temporaryForRefactor, setMoneyRequestCreated_temporaryForRefactor, setMoneyRequestCurrency_temporaryForRefactor, + setMoneyRequestOriginalCurrency_temporaryForRefactor, setMoneyRequestDescription_temporaryForRefactor, setMoneyRequestMerchant_temporaryForRefactor, setMoneyRequestParticipants_temporaryForRefactor, diff --git a/src/pages/iou/request/step/IOURequestStepAmount.js b/src/pages/iou/request/step/IOURequestStepAmount.js index 84e0ac8533c5..55826244434f 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.js +++ b/src/pages/iou/request/step/IOURequestStepAmount.js @@ -1,6 +1,6 @@ import {useFocusEffect} from '@react-navigation/native'; import PropTypes from 'prop-types'; -import React, {useCallback, useRef} from 'react'; +import React, {useCallback, useEffect, useRef} from 'react'; import {withOnyx} from 'react-native-onyx'; import taxPropTypes from '@components/taxPropTypes'; import transactionPropTypes from '@components/transactionPropTypes'; @@ -62,15 +62,16 @@ function IOURequestStepAmount({ params: {iouType, reportID, transactionID, backTo, currency: selectedCurrency}, }, transaction, - transaction: {currency: originalCurrency}, + transaction: {currency: currentCurrency}, policyTaxRates, policy, }) { const {translate} = useLocalize(); const textInput = useRef(null); const focusTimeoutRef = useRef(null); + const isSaveButtonPressed = useRef(false); const iouRequestType = getRequestType(transaction); - const currency = selectedCurrency || originalCurrency; + const currency = selectedCurrency || currentCurrency; const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(report)); const isTaxTrackingEnabled = isPolicyExpenseChat && policy.isTaxTrackingEnabled; @@ -87,6 +88,17 @@ function IOURequestStepAmount({ }, []), ); + useEffect(() => { + IOU.setMoneyRequestOriginalCurrency_temporaryForRefactor(transactionID, currentCurrency); + return () => { + if (isSaveButtonPressed.current) { + return; + } + IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, transaction.originalCurrency); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const navigateBack = () => { Navigation.goBack(backTo || ROUTES.HOME); }; @@ -99,6 +111,7 @@ function IOURequestStepAmount({ * @param {Number} amount */ const navigateToNextPage = ({amount}) => { + isSaveButtonPressed.current = true; const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(amount)); if ((iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL || backTo) && isTaxTrackingEnabled) { diff --git a/src/pages/iou/request/step/IOURequestStepCurrency.js b/src/pages/iou/request/step/IOURequestStepCurrency.js index b4281de4d16e..ea6173408371 100644 --- a/src/pages/iou/request/step/IOURequestStepCurrency.js +++ b/src/pages/iou/request/step/IOURequestStepCurrency.js @@ -82,9 +82,7 @@ function IOURequestStepCurrency({ */ const confirmCurrencySelection = (option) => { Keyboard.dismiss(); - if (pageIndex !== 'confirm') { - IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, option.currencyCode); - } + IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, option.currencyCode); navigateBack(option.currencyCode); }; From ef3810cf358833bd609a8f75db282118d69d6bdc Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Sat, 6 Jan 2024 21:14:02 +0530 Subject: [PATCH 045/660] fixed minor bugs regarding currency change --- src/libs/actions/IOU.js | 14 ++++++++++++-- src/pages/iou/request/step/IOURequestStepAmount.js | 6 ++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 37e72b2133e3..098685206c2c 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -177,8 +177,13 @@ function clearMoneyRequest(transactionID) { * @param {String} transactionID * @param {Number} amount * @param {String} currency + * @param {Boolean} [removeOriginalCurrency] */ -function setMoneyRequestAmount_temporaryForRefactor(transactionID, amount, currency) { +function setMoneyRequestAmount_temporaryForRefactor(transactionID, amount, currency, removeOriginalCurrency = false) { + if (removeOriginalCurrency) { + Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {amount, currency, originalCurrency: null}); + return; + } Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {amount, currency}); } @@ -193,8 +198,13 @@ function setMoneyRequestCreated_temporaryForRefactor(transactionID, created) { /** * @param {String} transactionID * @param {String} currency + * @param {Boolean} [removeOriginalCurrency] */ -function setMoneyRequestCurrency_temporaryForRefactor(transactionID, currency) { +function setMoneyRequestCurrency_temporaryForRefactor(transactionID, currency, removeOriginalCurrency = false) { + if (removeOriginalCurrency) { + Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {currency, originalCurrency: null}); + return; + } Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {currency}); } diff --git a/src/pages/iou/request/step/IOURequestStepAmount.js b/src/pages/iou/request/step/IOURequestStepAmount.js index 55826244434f..99dd9ba25ae8 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.js +++ b/src/pages/iou/request/step/IOURequestStepAmount.js @@ -70,6 +70,7 @@ function IOURequestStepAmount({ const textInput = useRef(null); const focusTimeoutRef = useRef(null); const isSaveButtonPressed = useRef(false); + const originalCurrency = useRef(null); const iouRequestType = getRequestType(transaction); const currency = selectedCurrency || currentCurrency; @@ -89,12 +90,13 @@ function IOURequestStepAmount({ ); useEffect(() => { + originalCurrency.current = currentCurrency; IOU.setMoneyRequestOriginalCurrency_temporaryForRefactor(transactionID, currentCurrency); return () => { if (isSaveButtonPressed.current) { return; } - IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, transaction.originalCurrency); + IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, originalCurrency.current, true); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -120,7 +122,7 @@ function IOURequestStepAmount({ IOU.setMoneyRequestTaxAmount(transaction.transactionID, taxAmountInSmallestCurrencyUnits); } - IOU.setMoneyRequestAmount_temporaryForRefactor(transactionID, amountInSmallestCurrencyUnits, currency || CONST.CURRENCY.USD); + IOU.setMoneyRequestAmount_temporaryForRefactor(transactionID, amountInSmallestCurrencyUnits, currency || CONST.CURRENCY.USD, true); if (backTo) { Navigation.goBack(backTo); From ca2763dd1ffa2bbbeeebb0123b6b5f98f48de9a8 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Sat, 6 Jan 2024 21:17:06 +0530 Subject: [PATCH 046/660] fixed deep linking bugs --- src/pages/iou/request/step/IOURequestStepAmount.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepAmount.js b/src/pages/iou/request/step/IOURequestStepAmount.js index 99dd9ba25ae8..8e16af248dc4 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.js +++ b/src/pages/iou/request/step/IOURequestStepAmount.js @@ -90,8 +90,12 @@ function IOURequestStepAmount({ ); useEffect(() => { - originalCurrency.current = currentCurrency; - IOU.setMoneyRequestOriginalCurrency_temporaryForRefactor(transactionID, currentCurrency); + if (transaction.originalCurrency) { + originalCurrency.current = transaction.originalCurrency; + } else { + originalCurrency.current = currentCurrency; + IOU.setMoneyRequestOriginalCurrency_temporaryForRefactor(transactionID, currentCurrency); + } return () => { if (isSaveButtonPressed.current) { return; From 939684581574a4e63218d80805d231ee3a18b3fa Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Sat, 6 Jan 2024 21:20:10 +0530 Subject: [PATCH 047/660] mirroring changes in tax step --- .../step/IOURequestStepTaxAmountPage.js | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js index 8ee3abb56d00..70b2ccd7e270 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js @@ -1,5 +1,5 @@ import {useFocusEffect} from '@react-navigation/native'; -import React, {useCallback, useRef} from 'react'; +import React, {useCallback, useRef, useEffect} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; @@ -56,7 +56,7 @@ function IOURequestStepTaxAmountPage({ params: {iouType, reportID, transactionID, backTo, currency: selectedCurrency}, }, transaction, - transaction: {currency: originalCurrency}, + transaction: {currency: currentCurrency}, report, policyTaxRates, }) { @@ -65,10 +65,29 @@ function IOURequestStepTaxAmountPage({ const textInput = useRef(null); const isEditing = Navigation.getActiveRoute().includes('taxAmount'); - const currency = selectedCurrency || originalCurrency; + const currency = selectedCurrency || currentCurrency; const focusTimeoutRef = useRef(null); + const isSaveButtonPressed = useRef(false); + const originalCurrency = useRef(null); + + useEffect(() => { + if (transaction.originalCurrency) { + originalCurrency.current = transaction.originalCurrency; + } else { + originalCurrency.current = currentCurrency; + IOU.setMoneyRequestOriginalCurrency_temporaryForRefactor(transactionID, currentCurrency); + } + return () => { + if (isSaveButtonPressed.current) { + return; + } + IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, originalCurrency.current, true); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useFocusEffect( useCallback(() => { focusTimeoutRef.current = setTimeout(() => textInput.current && textInput.current.focus(), CONST.ANIMATED_TRANSITION); @@ -93,10 +112,11 @@ function IOURequestStepTaxAmountPage({ }; const updateTaxAmount = (currentAmount) => { + isSaveButtonPressed.current = true; const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(currentAmount.amount)); IOU.setMoneyRequestTaxAmount(transactionID, amountInSmallestCurrencyUnits); - IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, currency || CONST.CURRENCY.USD); + IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, currency || CONST.CURRENCY.USD, true); if (backTo) { Navigation.goBack(backTo); From db92b88156a975a959177cd93eade70856a20339 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Sat, 6 Jan 2024 21:22:59 +0530 Subject: [PATCH 048/660] fix lint --- src/pages/iou/request/step/IOURequestStepTaxAmountPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js index 70b2ccd7e270..9cfd8f7ddf26 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js @@ -1,5 +1,5 @@ import {useFocusEffect} from '@react-navigation/native'; -import React, {useCallback, useRef, useEffect} from 'react'; +import React, {useCallback, useEffect, useRef} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; From f27c0371bd62b2f08811aecadb07e803b1888f50 Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Sat, 6 Jan 2024 21:32:26 +0530 Subject: [PATCH 049/660] removed unecessary param --- src/pages/iou/request/step/IOURequestStepAmount.js | 9 ++++----- src/pages/iou/request/step/IOURequestStepCurrency.js | 10 +++------- .../iou/request/step/IOURequestStepTaxAmountPage.js | 10 ++++------ 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepAmount.js b/src/pages/iou/request/step/IOURequestStepAmount.js index 8e16af248dc4..f91e7cea533b 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.js +++ b/src/pages/iou/request/step/IOURequestStepAmount.js @@ -59,10 +59,10 @@ const getTaxAmount = (transaction, defaultTaxValue, amount) => { function IOURequestStepAmount({ report, route: { - params: {iouType, reportID, transactionID, backTo, currency: selectedCurrency}, + params: {iouType, reportID, transactionID, backTo}, }, transaction, - transaction: {currency: currentCurrency}, + transaction: {currency}, policyTaxRates, policy, }) { @@ -72,7 +72,6 @@ function IOURequestStepAmount({ const isSaveButtonPressed = useRef(false); const originalCurrency = useRef(null); const iouRequestType = getRequestType(transaction); - const currency = selectedCurrency || currentCurrency; const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(report)); const isTaxTrackingEnabled = isPolicyExpenseChat && policy.isTaxTrackingEnabled; @@ -93,8 +92,8 @@ function IOURequestStepAmount({ if (transaction.originalCurrency) { originalCurrency.current = transaction.originalCurrency; } else { - originalCurrency.current = currentCurrency; - IOU.setMoneyRequestOriginalCurrency_temporaryForRefactor(transactionID, currentCurrency); + originalCurrency.current = currency; + IOU.setMoneyRequestOriginalCurrency_temporaryForRefactor(transactionID, currency); } return () => { if (isSaveButtonPressed.current) { diff --git a/src/pages/iou/request/step/IOURequestStepCurrency.js b/src/pages/iou/request/step/IOURequestStepCurrency.js index ea6173408371..06af0ecf3ca4 100644 --- a/src/pages/iou/request/step/IOURequestStepCurrency.js +++ b/src/pages/iou/request/step/IOURequestStepCurrency.js @@ -59,18 +59,14 @@ function IOURequestStepCurrency({ const [searchValue, setSearchValue] = useState(''); const optionsSelectorRef = useRef(); - const navigateBack = (selectedCurrency = undefined) => { + const navigateBack = () => { // If the currency selection was done from the confirmation step (eg. + > request money > manual > confirm > amount > currency) // then the user needs taken back to the confirmation page instead of the initial amount page. This is because the route params // are only able to handle one backTo param at a time and the user needs to go back to the amount page before going back // to the confirmation page if (pageIndex === 'confirm') { const routeToAmountPageWithConfirmationAsBackTo = getUrlWithBackToParam(backTo, `/${ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(iouType, transactionID, reportID)}`); - if (selectedCurrency) { - Navigation.navigate(`${routeToAmountPageWithConfirmationAsBackTo}¤cy=${selectedCurrency}`); - } else { - Navigation.goBack(routeToAmountPageWithConfirmationAsBackTo); - } + Navigation.goBack(routeToAmountPageWithConfirmationAsBackTo); return; } Navigation.goBack(backTo || ROUTES.HOME); @@ -83,7 +79,7 @@ function IOURequestStepCurrency({ const confirmCurrencySelection = (option) => { Keyboard.dismiss(); IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, option.currencyCode); - navigateBack(option.currencyCode); + navigateBack(); }; const {sections, headerMessage, initiallyFocusedOptionKey} = useMemo(() => { diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js index 9cfd8f7ddf26..5c968b8415a4 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js @@ -53,10 +53,10 @@ const getTaxAmount = (transaction, defaultTaxValue) => { function IOURequestStepTaxAmountPage({ route: { - params: {iouType, reportID, transactionID, backTo, currency: selectedCurrency}, + params: {iouType, reportID, transactionID, backTo}, }, transaction, - transaction: {currency: currentCurrency}, + transaction: {currency}, report, policyTaxRates, }) { @@ -65,8 +65,6 @@ function IOURequestStepTaxAmountPage({ const textInput = useRef(null); const isEditing = Navigation.getActiveRoute().includes('taxAmount'); - const currency = selectedCurrency || currentCurrency; - const focusTimeoutRef = useRef(null); const isSaveButtonPressed = useRef(false); @@ -76,8 +74,8 @@ function IOURequestStepTaxAmountPage({ if (transaction.originalCurrency) { originalCurrency.current = transaction.originalCurrency; } else { - originalCurrency.current = currentCurrency; - IOU.setMoneyRequestOriginalCurrency_temporaryForRefactor(transactionID, currentCurrency); + originalCurrency.current = currency; + IOU.setMoneyRequestOriginalCurrency_temporaryForRefactor(transactionID, currency); } return () => { if (isSaveButtonPressed.current) { From c3de7d1faf23f915b781b29eef7e978fe08dd983 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Mon, 8 Jan 2024 08:45:39 +0100 Subject: [PATCH 050/660] bump minor of vision camera --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index b10078ee7e6b..4a3b55d65205 100644 --- a/package-lock.json +++ b/package-lock.json @@ -111,7 +111,7 @@ "react-native-tab-view": "^3.5.2", "react-native-url-polyfill": "^2.0.0", "react-native-view-shot": "^3.6.0", - "react-native-vision-camera": "2.16.5", + "react-native-vision-camera": "2.16.6", "react-native-web": "^0.19.9", "react-native-web-linear-gradient": "^1.1.2", "react-native-webview": "^11.17.2", @@ -47872,9 +47872,9 @@ } }, "node_modules/react-native-vision-camera": { - "version": "2.16.5", - "resolved": "https://registry.npmjs.org/react-native-vision-camera/-/react-native-vision-camera-2.16.5.tgz", - "integrity": "sha512-MzXhNd597OyMQSEGhqWI4DufWkdr7PR7U9B30E3gXnln7cnvjMVIp4j3eIW9BIrgvEyUcEeL7nZM5NLhTmO/fA==", + "version": "2.16.6", + "resolved": "https://registry.npmjs.org/react-native-vision-camera/-/react-native-vision-camera-2.16.6.tgz", + "integrity": "sha512-rhlRC+yLB1fIB5eVjtnbjgTqNzihFqvoc4B0n+PlytYvHeK3BpD3Shawmy8Me/nBVL5k4Ay+QOd4niFOir+6YQ==", "peerDependencies": { "react": "*", "react-native": "*" @@ -90688,9 +90688,9 @@ "requires": {} }, "react-native-vision-camera": { - "version": "2.16.5", - "resolved": "https://registry.npmjs.org/react-native-vision-camera/-/react-native-vision-camera-2.16.5.tgz", - "integrity": "sha512-MzXhNd597OyMQSEGhqWI4DufWkdr7PR7U9B30E3gXnln7cnvjMVIp4j3eIW9BIrgvEyUcEeL7nZM5NLhTmO/fA==", + "version": "2.16.6", + "resolved": "https://registry.npmjs.org/react-native-vision-camera/-/react-native-vision-camera-2.16.6.tgz", + "integrity": "sha512-rhlRC+yLB1fIB5eVjtnbjgTqNzihFqvoc4B0n+PlytYvHeK3BpD3Shawmy8Me/nBVL5k4Ay+QOd4niFOir+6YQ==", "requires": {} }, "react-native-web": { diff --git a/package.json b/package.json index 2c25c863f49f..446a815d4f79 100644 --- a/package.json +++ b/package.json @@ -159,7 +159,7 @@ "react-native-tab-view": "^3.5.2", "react-native-url-polyfill": "^2.0.0", "react-native-view-shot": "^3.6.0", - "react-native-vision-camera": "2.16.5", + "react-native-vision-camera": "2.16.6", "react-native-web": "^0.19.9", "react-native-web-linear-gradient": "^1.1.2", "react-native-webview": "^11.17.2", From c92cde7964361533e28ef4b04f8ce1880faba90a Mon Sep 17 00:00:00 2001 From: Yauheni Date: Mon, 8 Jan 2024 10:59:51 +0300 Subject: [PATCH 051/660] Fix ts issue --- src/components/ProcessMoneyRequestHoldMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ProcessMoneyRequestHoldMenu.tsx b/src/components/ProcessMoneyRequestHoldMenu.tsx index 1b711633ed3b..a6b53da467b5 100644 --- a/src/components/ProcessMoneyRequestHoldMenu.tsx +++ b/src/components/ProcessMoneyRequestHoldMenu.tsx @@ -2,11 +2,11 @@ import React from 'react'; import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import type AnchorAlignment from '@src/types/onyx/AnchorAlignment'; import Button from './Button'; import HoldMenuSectionList from './HoldMenuSectionList'; import type {PopoverAnchorPosition} from './Modal/types'; import Popover from './Popover'; -import type {AnchorAlignment} from './Popover/types'; import Text from './Text'; import TextPill from './TextPill'; From 63576708f951cd44fa6750572fac4b7e2d12eed7 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Mon, 8 Jan 2024 09:34:52 +0100 Subject: [PATCH 052/660] Migrate deleteMoneyRequest function --- src/libs/actions/IOU.ts | 366 ++++++++++++++++++++++------------------ 1 file changed, 205 insertions(+), 161 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index ac41becf0e1c..43b6a06890a5 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -36,12 +36,13 @@ import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; +import type {IOUMessage} from '@src/types/onyx/OriginalMessage'; import type ReportAction from '@src/types/onyx/ReportAction'; import type {OnyxData} from '@src/types/onyx/Request'; import type {Comment, Receipt, TaxRate, TransactionChanges, WaypointCollection} from '@src/types/onyx/Transaction'; -import DeepValueOf from '@src/types/utils/DeepValueOf'; +import type DeepValueOf from '@src/types/utils/DeepValueOf'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; -import {isEmptyObject, isNotEmptyObject} from '@src/types/utils/EmptyObject'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import * as Policy from './Policy'; import * as Report from './Report'; @@ -1083,6 +1084,7 @@ function getUpdateMoneyRequestParams( failureData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style value: iouReport as OnyxTypes.Report, }); @@ -1393,8 +1395,8 @@ function createSplitsAndOnyxData( // In case the participant is a workspace, email & accountID should remain undefined and won't be used in the rest of this code // participant.login is undefined when the request is initiated from a group DM with an unknown user, so we need to add a default - const email = isOwnPolicyExpenseChat || isPolicyExpenseChat ? '' : OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login || '').toLowerCase(); - const accountID = isOwnPolicyExpenseChat || isPolicyExpenseChat ? 0 : Number(participant.accountID); + const email = !!isOwnPolicyExpenseChat || isPolicyExpenseChat ? '' : OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login ?? '').toLowerCase(); + const accountID = !!isOwnPolicyExpenseChat || isPolicyExpenseChat ? 0 : Number(participant.accountID); if (email === currentUserEmailForIOUSplit) { return; } @@ -1418,7 +1420,7 @@ function createSplitsAndOnyxData( const existingChatReport = ReportUtils.getChatByParticipants([accountID]); isNewOneOnOneChatReport = !existingChatReport; shouldCreateOptimisticPersonalDetails = isNewOneOnOneChatReport && !personalDetailExists; - oneOnOneChatReport = existingChatReport || ReportUtils.buildOptimisticChatReport([accountID]); + oneOnOneChatReport = existingChatReport ?? ReportUtils.buildOptimisticChatReport([accountID]); } // STEP 2: Get existing IOU/Expense report and update its total OR build a new optimistic one @@ -1437,11 +1439,13 @@ function createSplitsAndOnyxData( oneOnOneIOUReport.total -= splitAmount; } } else { + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style oneOnOneIOUReport = IOUUtils.updateIOUOwnerAndTotal(oneOnOneIOUReport as OnyxTypes.Report, currentUserAccountID, splitAmount, currency); } // STEP 3: Build optimistic transaction const oneOnOneTransaction = TransactionUtils.buildOptimisticTransaction( + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style ReportUtils.isExpenseReport(oneOnOneIOUReport as OnyxTypes.Report) ? -splitAmount : splitAmount, currency, oneOnOneIOUReport?.reportID ?? '', @@ -1497,8 +1501,10 @@ function createSplitsAndOnyxData( let oneOnOneReportPreviewAction = ReportActionsUtils.getReportPreviewAction(oneOnOneChatReport.reportID, oneOnOneIOUReport?.reportID ?? ''); if (oneOnOneReportPreviewAction) { + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style oneOnOneReportPreviewAction = ReportUtils.updateReportPreview(oneOnOneIOUReport as OnyxTypes.Report, oneOnOneReportPreviewAction); } else { + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style oneOnOneReportPreviewAction = ReportUtils.buildOptimisticReportPreview(oneOnOneChatReport, oneOnOneIOUReport as OnyxTypes.Report); } @@ -1515,6 +1521,7 @@ function createSplitsAndOnyxData( // STEP 5: Build Onyx Data const [oneOnOneOptimisticData, oneOnOneSuccessData, oneOnOneFailureData] = buildOnyxDataForMoneyRequest( oneOnOneChatReport, + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style oneOnOneIOUReport as OnyxTypes.Report, oneOnOneTransaction, oneOnOneCreatedActionForChat, @@ -2031,7 +2038,7 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA // In case this is still the optimistic accountID saved in the splits array, return early as we cannot know // if there is an existing chat between the split creator and this participant // Instead, we will rely on Auth generating the report IDs and the user won't see any optimistic chats or reports created - const participantPersonalDetails: OnyxTypes.PersonalDetails | EmptyObject = allPersonalDetails[participant?.accountID ?? -1] || {}; + const participantPersonalDetails: OnyxTypes.PersonalDetails | EmptyObject = allPersonalDetails[participant?.accountID ?? -1] ?? {}; if (!participantPersonalDetails || participantPersonalDetails.isOptimisticPersonalDetail) { splits.push({ email: participant.email, @@ -2048,7 +2055,7 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA } else { const existingChatReport = ReportUtils.getChatByParticipants(participant.accountID ? [participant.accountID] : []); isNewOneOnOneChatReport = !existingChatReport; - oneOnOneChatReport = existingChatReport || ReportUtils.buildOptimisticChatReport(participant.accountID ? [participant.accountID] : []); + oneOnOneChatReport = existingChatReport ?? ReportUtils.buildOptimisticChatReport(participant.accountID ? [participant.accountID] : []); } let oneOnOneIOUReport = oneOnOneChatReport?.iouReportID ? lodashGet(allReports, `${ONYXKEYS.COLLECTION.REPORT}${oneOnOneChatReport.iouReportID}`, undefined) : undefined; @@ -2065,6 +2072,7 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA oneOnOneIOUReport.total -= splitAmount; } } else { + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style oneOnOneIOUReport = IOUUtils.updateIOUOwnerAndTotal(oneOnOneIOUReport as OnyxTypes.Report, sessionAccountID, splitAmount, currency ?? ''); } @@ -2102,7 +2110,9 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA } const [oneOnOneOptimisticData, oneOnOneSuccessData, oneOnOneFailureData] = buildOnyxDataForMoneyRequest( + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style oneOnOneChatReport as OnyxTypes.Report, + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style oneOnOneIOUReport as OnyxTypes.Report, oneOnOneTransaction, oneOnOneCreatedActionForChat, @@ -2195,6 +2205,7 @@ function editRegularMoneyRequest(transactionID: string, transactionThreadReportI // STEP 2: Build new modified expense report action. const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(transactionThread, transaction, transactionChanges, isFromExpenseReport); + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style const updatedTransaction = TransactionUtils.getUpdatedTransaction(transaction as OnyxTypes.Transaction, transactionChanges, isFromExpenseReport); // STEP 3: Compute the IOU total and update the report preview message so LHN amount owed is correct @@ -2230,7 +2241,7 @@ function editRegularMoneyRequest(transactionID: string, transactionThreadReportI // Update the last message of the chat report const hasNonReimbursableTransactions = ReportUtils.hasNonReimbursableTransactions(iouReport?.reportID); // There was an error before - wrong value was sent const messageText = Localize.translateLocal(hasNonReimbursableTransactions ? 'iou.payerSpentAmount' : 'iou.payerOwesAmount', { - payer: ReportUtils.getPersonalDetailsForAccountID(updatedMoneyRequestReport.managerID ?? -1).login || '', + payer: ReportUtils.getPersonalDetailsForAccountID(updatedMoneyRequestReport.managerID ?? -1).login ?? '', amount: CurrencyUtils.convertToDisplayString(updatedMoneyRequestReport.total, updatedMoneyRequestReport.currency), }); updatedChatReport.lastMessageText = messageText; @@ -2315,7 +2326,7 @@ function editRegularMoneyRequest(transactionID: string, transactionThreadReportI // Update recently used categories if the tag is changed if ('tag' in transactionChanges) { const optimisticPolicyRecentlyUsedTags = Policy.buildOptimisticPolicyRecentlyUsedTags(iouReport?.policyID, transactionChanges.tag) as OptimisticPolicyRecentlyUsedTags; - if (isNotEmptyObject(optimisticPolicyRecentlyUsedTags)) { + if (!isEmptyObject(optimisticPolicyRecentlyUsedTags)) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${iouReport?.policyID}`, @@ -2389,6 +2400,7 @@ function editRegularMoneyRequest(transactionID: string, transactionThreadReportI { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.chatReportID}`, + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style value: chatReport as OnyxTypes.Report, }, { @@ -2453,29 +2465,24 @@ function updateMoneyRequestAmountAndCurrency(transactionID: string, transactionT API.write('UpdateMoneyRequestAmountAndCurrency', params, onyxData); } -/** - * @param {String} transactionID - * @param {Object} reportAction - the money request reportAction we are deleting - * @param {Boolean} isSingleTransactionView - */ -function deleteMoneyRequest(transactionID, reportAction, isSingleTransactionView = false) { +function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false) { // STEP 1: Get all collections we're updating - const iouReport = allReports[`${ONYXKEYS.COLLECTION.REPORT}${reportAction.originalMessage.IOUReportID}`]; - const chatReport = allReports[`${ONYXKEYS.COLLECTION.REPORT}${iouReport.chatReportID}`]; - const reportPreviewAction = ReportActionsUtils.getReportPreviewAction(iouReport.chatReportID, iouReport.reportID); + const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${(reportAction.originalMessage as IOUMessage).IOUReportID}`]; + const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReport?.chatReportID}`]; + const reportPreviewAction = ReportActionsUtils.getReportPreviewAction(iouReport?.chatReportID ?? '', iouReport?.reportID ?? ''); const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; const transactionViolations = allTransactionViolations[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`]; const transactionThreadID = reportAction.childReportID; let transactionThread = null; if (transactionThreadID) { - transactionThread = allReports[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`]; + transactionThread = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`]; } // STEP 2: Decide if we need to: // 1. Delete the transactionThread - delete if there are no visible comments in the thread // 2. Update the moneyRequestPreview to show [Deleted request] - update if the transactionThread exists AND it isn't being deleted const shouldDeleteTransactionThread = transactionThreadID ? ReportActionsUtils.getLastVisibleMessage(transactionThreadID).lastMessageText.length === 0 : false; - const shouldShowDeletedRequestMessage = transactionThreadID && !shouldDeleteTransactionThread; + const shouldShowDeletedRequestMessage = !!transactionThreadID && !shouldDeleteTransactionThread; // STEP 3: Update the IOU reportAction and decide if the iouReport should be deleted. We delete the iouReport if there are no visible comments left in the report. const updatedReportAction = { @@ -2494,28 +2501,31 @@ function deleteMoneyRequest(transactionID, reportAction, isSingleTransactionView originalMessage: { IOUTransactionID: null, }, - errors: null, + errors: undefined, }, - }; + } as OnyxTypes.ReportActions; - const lastVisibleAction = ReportActionsUtils.getLastVisibleAction(iouReport.reportID, updatedReportAction); - const iouReportLastMessageText = ReportActionsUtils.getLastVisibleMessage(iouReport.reportID, updatedReportAction).lastMessageText; + const lastVisibleAction = ReportActionsUtils.getLastVisibleAction(iouReport?.reportID ?? '', updatedReportAction); + const iouReportLastMessageText = ReportActionsUtils.getLastVisibleMessage(iouReport?.reportID ?? '', updatedReportAction).lastMessageText; const shouldDeleteIOUReport = iouReportLastMessageText.length === 0 && !ReportActionsUtils.isDeletedParentAction(lastVisibleAction) && (!transactionThreadID || shouldDeleteTransactionThread); // STEP 4: Update the iouReport and reportPreview with new totals and messages if it wasn't deleted let updatedIOUReport = null; - let updatedReportPreviewAction = null; + let updatedReportPreviewAction: OnyxTypes.ReportAction | null = null; if (!shouldDeleteIOUReport) { - if (ReportUtils.isExpenseReport(iouReport)) { + if (iouReport && ReportUtils.isExpenseReport(iouReport)) { updatedIOUReport = {...iouReport}; - // Because of the Expense reports are stored as negative values, we add the total from the amount - updatedIOUReport.total += TransactionUtils.getAmount(transaction, true); + if (updatedIOUReport.total) { + // Because of the Expense reports are stored as negative values, we add the total from the amount + updatedIOUReport.total += TransactionUtils.getAmount(transaction, true); + } } else { updatedIOUReport = IOUUtils.updateIOUOwnerAndTotal( - iouReport, - reportAction.actorAccountID, + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style + iouReport as OnyxTypes.Report, + reportAction.actorAccountID ?? -1, TransactionUtils.getAmount(transaction, false), TransactionUtils.getCurrency(transaction), true, @@ -2525,130 +2535,156 @@ function deleteMoneyRequest(transactionID, reportAction, isSingleTransactionView updatedIOUReport.lastMessageText = iouReportLastMessageText; updatedIOUReport.lastVisibleActionCreated = lodashGet(lastVisibleAction, 'created'); - updatedReportPreviewAction = {...reportPreviewAction}; - const hasNonReimbursableTransactions = ReportUtils.hasNonReimbursableTransactions(iouReport); + updatedReportPreviewAction = reportPreviewAction ? {...reportPreviewAction} : null; + const hasNonReimbursableTransactions = ReportUtils.hasNonReimbursableTransactions(iouReport?.reportID); const messageText = Localize.translateLocal(hasNonReimbursableTransactions ? 'iou.payerSpentAmount' : 'iou.payerOwesAmount', { - payer: ReportUtils.getPersonalDetailsForAccountID(updatedIOUReport.managerID).login || '', + payer: ReportUtils.getPersonalDetailsForAccountID(updatedIOUReport?.managerID ?? -1).login ?? '', amount: CurrencyUtils.convertToDisplayString(updatedIOUReport.total, updatedIOUReport.currency), }); - updatedReportPreviewAction.message[0].text = messageText; - updatedReportPreviewAction.message[0].html = messageText; - if (reportPreviewAction.childMoneyRequestCount > 0) { + + if (updatedReportPreviewAction?.message?.[0]) { + updatedReportPreviewAction.message[0].text = messageText; + updatedReportPreviewAction.message[0].html = messageText; + } + if (updatedReportPreviewAction && reportPreviewAction?.childMoneyRequestCount && reportPreviewAction?.childMoneyRequestCount > 0) { updatedReportPreviewAction.childMoneyRequestCount = reportPreviewAction.childMoneyRequestCount - 1; } } // STEP 5: Build Onyx data - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, value: null, }, - ...(Permissions.canUseViolations(betas) - ? [ - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`, - value: null, - }, - ] - : []), - ...(shouldDeleteTransactionThread - ? [ - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadID}`, - value: null, - }, - ] - : []), - { - onyxMethod: shouldDeleteIOUReport ? Onyx.METHOD.SET : Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.reportID}`, - value: shouldDeleteIOUReport ? null : updatedReportAction, - }, - { - onyxMethod: shouldDeleteIOUReport ? Onyx.METHOD.SET : Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, - value: updatedIOUReport, - }, + ]; + + if (Permissions.canUseViolations(betas)) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`, + value: null, + }); + } + + if (shouldDeleteTransactionThread) { + optimisticData.push( + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadID}`, + value: null, + }, + ); + } + + optimisticData.push( + shouldDeleteIOUReport + ? { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport?.reportID}`, + value: null, + } + : { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport?.reportID}`, + value: updatedReportAction, + }, + shouldDeleteIOUReport + ? { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style + value: updatedIOUReport as OnyxTypes.Report, + } + : { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style + value: updatedIOUReport as OnyxTypes.Report, + }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport?.reportID}`, value: { - [reportPreviewAction.reportActionID]: updatedReportPreviewAction, + [reportPreviewAction?.reportActionID ?? '']: updatedReportPreviewAction, }, }, - ...(!shouldDeleteIOUReport && updatedReportPreviewAction.childMoneyRequestCount === 0 - ? [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, - value: { - hasOutstandingChildRequest: false, - }, - }, - ] - : []), - ...(shouldDeleteIOUReport - ? [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, - value: { - hasOutstandingChildRequest: false, - iouReportID: null, - lastMessageText: ReportActionsUtils.getLastVisibleMessage(iouReport.chatReportID, {[reportPreviewAction.reportActionID]: null}).lastMessageText, - lastVisibleActionCreated: lodashGet(ReportActionsUtils.getLastVisibleAction(iouReport.chatReportID, {[reportPreviewAction.reportActionID]: null}), 'created'), - }, - }, - ] - : []), - ]; + ); - const successData = [ + if (!shouldDeleteIOUReport && updatedReportPreviewAction?.childMoneyRequestCount === 0) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport?.reportID}`, + value: { + hasOutstandingChildRequest: false, + }, + }); + } + + if (shouldDeleteIOUReport) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport?.reportID}`, + value: { + hasOutstandingChildRequest: false, + iouReportID: null, + lastMessageText: + iouReport && reportPreviewAction + ? ReportActionsUtils.getLastVisibleMessage(iouReport.chatReportID ?? '', {[reportPreviewAction.reportActionID]: null}).lastMessageText + : '', + lastVisibleActionCreated: + iouReport && reportPreviewAction + ? lodashGet(ReportActionsUtils.getLastVisibleAction(iouReport.chatReportID ?? '', {[reportPreviewAction.reportActionID]: null}), 'created') + : 'created', + }, + }); + } + + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport?.reportID}`, value: { [reportAction.reportActionID]: {pendingAction: null}, }, }, ]; - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, value: transaction, }, - ...(Permissions.canUseViolations(betas) - ? [ - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`, - value: transactionViolations, - }, - ] - : []), - ...(shouldDeleteTransactionThread - ? [ - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`, - value: transactionThread, - }, - ] - : []), + ]; + + if (Permissions.canUseViolations(betas)) { + failureData.push({ + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`, + value: transactionViolations, + }); + } + + if (shouldDeleteTransactionThread) { + failureData.push({ + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`, + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style + value: transactionThread as OnyxTypes.Report, + }); + } + + failureData.push( { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport?.reportID}`, value: { [reportAction.reportActionID]: { ...reportAction, @@ -2656,58 +2692,67 @@ function deleteMoneyRequest(transactionID, reportAction, isSingleTransactionView }, }, }, - { - onyxMethod: shouldDeleteIOUReport ? Onyx.METHOD.SET : Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, - value: iouReport, - }, + shouldDeleteIOUReport + ? { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style + value: iouReport as OnyxTypes.Report, + } + : { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style + value: iouReport as OnyxTypes.Report, + }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport?.reportID}`, value: { - [reportPreviewAction.reportActionID]: reportPreviewAction, + [reportPreviewAction?.reportActionID ?? '']: reportPreviewAction, }, }, - ...(shouldDeleteIOUReport - ? [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, - value: chatReport, - }, - ] - : []), - ...(!shouldDeleteIOUReport && updatedReportPreviewAction.childMoneyRequestCount === 0 - ? [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, - value: { - hasOutstandingChildRequest: true, - }, - }, - ] - : []), - ]; + ); + + if (shouldDeleteIOUReport) { + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport?.reportID}`, + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style + value: chatReport as OnyxTypes.Report, + }); + } + if (!shouldDeleteIOUReport && updatedReportPreviewAction?.childMoneyRequestCount === 0) { + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport?.reportID}`, + value: { + hasOutstandingChildRequest: true, + }, + }); + } + + type DeleteMoneyRequestParams = { + transactionID: string; + reportActionID: string; + }; + + const parameters: DeleteMoneyRequestParams = { + transactionID, + reportActionID: reportAction.reportActionID, + }; // STEP 6: Make the API request - API.write( - 'DeleteMoneyRequest', - { - transactionID, - reportActionID: reportAction.reportActionID, - }, - {optimisticData, successData, failureData}, - ); + API.write('DeleteMoneyRequest', parameters, {optimisticData, successData, failureData}); // STEP 7: Navigate the user depending on which page they are on and which resources were deleted - if (isSingleTransactionView && shouldDeleteTransactionThread && !shouldDeleteIOUReport) { + if (iouReport && isSingleTransactionView && shouldDeleteTransactionThread && !shouldDeleteIOUReport) { // Pop the deleted report screen before navigating. This prevents navigating to the Concierge chat due to the missing report. Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(iouReport.reportID)); return; } - if (shouldDeleteIOUReport) { + if (iouReport?.chatReportID && shouldDeleteIOUReport) { // Pop the deleted report screen before navigating. This prevents navigating to the Concierge chat due to the missing report. Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(iouReport.chatReportID)); } @@ -2716,7 +2761,6 @@ function deleteMoneyRequest(transactionID, reportAction, isSingleTransactionView /** * @param managerID - Account ID of the person sending the money * @param recipient - The user receiving the money - * @returns {Object} */ function getSendMoneyParams( report: OnyxTypes.Report, @@ -2775,7 +2819,7 @@ function getSendMoneyParams( // 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 - let optimisticChatReportData: OnyxUpdate = isNewChat + const optimisticChatReportData: OnyxUpdate = isNewChat ? { onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, @@ -2918,7 +2962,7 @@ function getSendMoneyParams( } const optimisticData: OnyxUpdate[] = [optimisticChatReportData, optimisticIOUReportData, optimisticChatReportActionsData, optimisticIOUReportActionsData, optimisticTransactionData]; - if (isNotEmptyObject(optimisticPersonalDetailListData)) { + if (!isEmptyObject(optimisticPersonalDetailListData)) { optimisticData.push(optimisticPersonalDetailListData); } From 992fdeaa0a9c8597f47257cd8de032e7be356145 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Mon, 8 Jan 2024 09:50:40 +0100 Subject: [PATCH 053/660] Migrate getPayMoneyRequestParams and payMoneyRequest functions --- src/libs/actions/IOU.ts | 66 +++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 43b6a06890a5..ef84843f6116 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -97,6 +97,20 @@ type UpdateMoneyRequestData = { onyxData: OnyxData; }; +type PayMoneyRequestParams = { + iouReportID: string; + chatReportID: string; + reportActionID: string; + paymentMethodType: PaymentMethodType; +}; + +type PayMoneyRequestData = { + params: PayMoneyRequestParams; + optimisticData: OnyxUpdate[]; + successData: OnyxUpdate[]; + failureData: OnyxUpdate[]; +}; + type PaymentMethodType = DeepValueOf; type SendMoneyParams = { @@ -2634,14 +2648,8 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor value: { hasOutstandingChildRequest: false, iouReportID: null, - lastMessageText: - iouReport && reportPreviewAction - ? ReportActionsUtils.getLastVisibleMessage(iouReport.chatReportID ?? '', {[reportPreviewAction.reportActionID]: null}).lastMessageText - : '', - lastVisibleActionCreated: - iouReport && reportPreviewAction - ? lodashGet(ReportActionsUtils.getLastVisibleAction(iouReport.chatReportID ?? '', {[reportPreviewAction.reportActionID]: null}), 'created') - : 'created', + lastMessageText: ReportActionsUtils.getLastVisibleMessage(iouReport?.chatReportID ?? '', {[reportPreviewAction?.reportActionID ?? '']: null}).lastMessageText, + lastVisibleActionCreated: lodashGet(ReportActionsUtils.getLastVisibleAction(iouReport?.chatReportID ?? '', {[reportPreviewAction?.reportActionID ?? '']: null}), 'created'), }, }); } @@ -2941,6 +2949,7 @@ function getSendMoneyParams( [recipientAccountID]: { accountID: recipientAccountID, avatar: UserUtils.getDefaultAvatarURL(recipient.accountID), + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing displayName: recipient.displayName || recipient.login, login: recipient.login, }, @@ -2983,18 +2992,11 @@ function getSendMoneyParams( }; } -/** - * @param {Object} chatReport - * @param {Object} iouReport - * @param {Object} recipient - * @param {String} paymentMethodType - * @returns {Object} - */ -function getPayMoneyRequestParams(chatReport, iouReport, recipient, paymentMethodType) { +function getPayMoneyRequestParams(chatReport: OnyxTypes.Report, iouReport: OnyxTypes.Report, recipient: Participant, paymentMethodType: PaymentMethodType): PayMoneyRequestData { const optimisticIOUReportAction = ReportUtils.buildOptimisticIOUReportAction( CONST.IOU.REPORT_ACTION_TYPE.PAY, - -iouReport.total, - iouReport.currency, + -(iouReport.total ?? 0), + iouReport.currency ?? '', '', [recipient], '', @@ -3013,7 +3015,7 @@ function getPayMoneyRequestParams(chatReport, iouReport, recipient, paymentMetho const currentNextStep = lodashGet(allNextSteps, `${ONYXKEYS.COLLECTION.NEXT_STEP}${iouReport.reportID}`, null); - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, @@ -3023,8 +3025,8 @@ function getPayMoneyRequestParams(chatReport, iouReport, recipient, paymentMetho lastVisibleActionCreated: optimisticIOUReportAction.created, hasOutstandingChildRequest: false, iouReportID: null, - lastMessageText: optimisticIOUReportAction.message[0].text, - lastMessageHtml: optimisticIOUReportAction.message[0].html, + lastMessageText: optimisticIOUReportAction.message?.[0].text, + lastMessageHtml: optimisticIOUReportAction.message?.[0].html, }, }, { @@ -3032,7 +3034,7 @@ function getPayMoneyRequestParams(chatReport, iouReport, recipient, paymentMetho key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.reportID}`, value: { [optimisticIOUReportAction.reportActionID]: { - ...optimisticIOUReportAction, + ...(optimisticIOUReportAction as OnyxTypes.ReportAction), pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, }, }, @@ -3042,8 +3044,8 @@ function getPayMoneyRequestParams(chatReport, iouReport, recipient, paymentMetho key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, value: { ...iouReport, - lastMessageText: optimisticIOUReportAction.message[0].text, - lastMessageHtml: optimisticIOUReportAction.message[0].html, + lastMessageText: optimisticIOUReportAction.message?.[0].text, + lastMessageHtml: optimisticIOUReportAction.message?.[0].html, hasOutstandingChildRequest: false, statusNum: CONST.REPORT.STATUS.REIMBURSED, }, @@ -3051,11 +3053,11 @@ function getPayMoneyRequestParams(chatReport, iouReport, recipient, paymentMetho { onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.NVP_LAST_PAYMENT_METHOD, - value: {[iouReport.policyID]: paymentMethodType}, + value: {[iouReport.policyID ?? '']: paymentMethodType}, }, ]; - const successData = [ + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.reportID}`, @@ -3067,7 +3069,7 @@ function getPayMoneyRequestParams(chatReport, iouReport, recipient, paymentMetho }, ]; - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.reportID}`, @@ -3079,7 +3081,7 @@ function getPayMoneyRequestParams(chatReport, iouReport, recipient, paymentMetho }, ]; - if (!_.isNull(currentNextStep)) { + if (currentNextStep !== null) { optimisticData.push({ onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${iouReport.reportID}`, @@ -3347,13 +3349,7 @@ function submitReport(expenseReport: OnyxTypes.Report) { API.write('SubmitReport', parameters, {optimisticData, successData, failureData}); } -/** - * @param {String} paymentType - * @param {Object} chatReport - * @param {Object} iouReport - * @param {String} reimbursementBankAccountState - */ -function payMoneyRequest(paymentType, chatReport, iouReport) { +function payMoneyRequest(paymentType: PaymentMethodType, chatReport: OnyxTypes.Report, iouReport: OnyxTypes.Report) { const recipient = {accountID: iouReport.ownerAccountID}; const {params, optimisticData, successData, failureData} = getPayMoneyRequestParams(chatReport, iouReport, recipient, paymentType); From 6f658cddf5c177280578a08211e6f50dbe6b10b5 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Mon, 8 Jan 2024 10:09:10 +0100 Subject: [PATCH 054/660] Get rid of lodashGet and underscore usage --- src/libs/actions/IOU.ts | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index ef84843f6116..d7e4fb2cde07 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -2,14 +2,12 @@ import type {StackScreenProps} from '@react-navigation/stack'; import {format} from 'date-fns'; import Str from 'expensify-common/lib/str'; -import lodashGet from 'lodash/get'; import lodashHas from 'lodash/has'; import type {ImageSourcePropType} from 'react-native'; import Onyx from 'react-native-onyx'; import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import OnyxUtils from 'react-native-onyx/lib/utils'; import type {ValueOf} from 'type-fest'; -import _ from 'underscore'; import ReceiptGeneric from '@assets/images/receipt-generic.png'; import * as API from '@libs/API'; import * as CurrencyUtils from '@libs/CurrencyUtils'; @@ -259,7 +257,7 @@ function startMoneyRequest_temporaryForRefactor(reportID: string, isFromGlobalCr amount: 0, comment, created, - currency: lodashGet(currentUserPersonalDetails, 'localCurrencyCode', CONST.CURRENCY.USD), + currency: currentUserPersonalDetails?.localCurrencyCode ?? CONST.CURRENCY.USD, iouRequestType, reportID, transactionID: newTransactionID, @@ -1054,7 +1052,7 @@ function getUpdateMoneyRequestParams( iouReport?.policyID, transactionChanges.tag, ) as OptimisticPolicyRecentlyUsedTags; - if (!_.isEmpty(optimisticPolicyRecentlyUsedTags)) { + if (!isEmptyObject(optimisticPolicyRecentlyUsedTags)) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${iouReport?.policyID}`, @@ -1439,9 +1437,9 @@ function createSplitsAndOnyxData( // STEP 2: Get existing IOU/Expense report and update its total OR build a new optimistic one // For Control policy expense chats, if the report is already approved, create a new expense report - let oneOnOneIOUReport = oneOnOneChatReport.iouReportID ? lodashGet(allReports, `${ONYXKEYS.COLLECTION.REPORT}${oneOnOneChatReport.iouReportID}`, undefined) : undefined; + let oneOnOneIOUReport = oneOnOneChatReport.iouReportID ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oneOnOneChatReport.iouReportID}`] ?? undefined : undefined; const shouldCreateNewOneOnOneIOUReport = - _.isUndefined(oneOnOneIOUReport) || (isOwnPolicyExpenseChat && ReportUtils.isControlPolicyExpenseReport(oneOnOneIOUReport) && ReportUtils.isReportApproved(oneOnOneIOUReport)); + oneOnOneIOUReport === undefined || (isOwnPolicyExpenseChat && ReportUtils.isControlPolicyExpenseReport(oneOnOneIOUReport) && ReportUtils.isReportApproved(oneOnOneIOUReport)); if (shouldCreateNewOneOnOneIOUReport) { oneOnOneIOUReport = isOwnPolicyExpenseChat @@ -1506,6 +1504,7 @@ function createSplitsAndOnyxData( [accountID]: { accountID, avatar: UserUtils.getDefaultAvatarURL(accountID), + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing displayName: LocalePhoneNumber.formatPhoneNumber(participant.displayName || email), login: participant.login, isOptimisticPersonalDetail: true, @@ -2072,9 +2071,9 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA oneOnOneChatReport = existingChatReport ?? ReportUtils.buildOptimisticChatReport(participant.accountID ? [participant.accountID] : []); } - let oneOnOneIOUReport = oneOnOneChatReport?.iouReportID ? lodashGet(allReports, `${ONYXKEYS.COLLECTION.REPORT}${oneOnOneChatReport.iouReportID}`, undefined) : undefined; + let oneOnOneIOUReport = oneOnOneChatReport?.iouReportID ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oneOnOneChatReport.iouReportID}`] ?? undefined : undefined; const shouldCreateNewOneOnOneIOUReport = - _.isUndefined(oneOnOneIOUReport) || (isPolicyExpenseChat && ReportUtils.isControlPolicyExpenseReport(oneOnOneIOUReport) && ReportUtils.isReportApproved(oneOnOneIOUReport)); + oneOnOneIOUReport === undefined || (isPolicyExpenseChat && ReportUtils.isControlPolicyExpenseReport(oneOnOneIOUReport) && ReportUtils.isReportApproved(oneOnOneIOUReport)); if (shouldCreateNewOneOnOneIOUReport) { oneOnOneIOUReport = isPolicyExpenseChat @@ -2547,7 +2546,7 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor } updatedIOUReport.lastMessageText = iouReportLastMessageText; - updatedIOUReport.lastVisibleActionCreated = lodashGet(lastVisibleAction, 'created'); + updatedIOUReport.lastVisibleActionCreated = lastVisibleAction?.created; updatedReportPreviewAction = reportPreviewAction ? {...reportPreviewAction} : null; const hasNonReimbursableTransactions = ReportUtils.hasNonReimbursableTransactions(iouReport?.reportID); @@ -2648,8 +2647,8 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor value: { hasOutstandingChildRequest: false, iouReportID: null, - lastMessageText: ReportActionsUtils.getLastVisibleMessage(iouReport?.chatReportID ?? '', {[reportPreviewAction?.reportActionID ?? '']: null}).lastMessageText, - lastVisibleActionCreated: lodashGet(ReportActionsUtils.getLastVisibleAction(iouReport?.chatReportID ?? '', {[reportPreviewAction?.reportActionID ?? '']: null}), 'created'), + lastMessageText: ReportActionsUtils.getLastVisibleMessage(iouReport?.chatReportID ?? '', {[reportPreviewAction?.reportActionID ?? '']: null})?.lastMessageText, + lastVisibleActionCreated: ReportActionsUtils.getLastVisibleAction(iouReport?.chatReportID ?? '', {[reportPreviewAction?.reportActionID ?? '']: null})?.created, }, }); } @@ -3013,7 +3012,7 @@ function getPayMoneyRequestParams(chatReport: OnyxTypes.Report, iouReport: OnyxT optimisticReportPreviewAction = ReportUtils.updateReportPreview(iouReport, reportPreviewAction, true); } - const currentNextStep = lodashGet(allNextSteps, `${ONYXKEYS.COLLECTION.NEXT_STEP}${iouReport.reportID}`, null); + const currentNextStep = allNextSteps[`${ONYXKEYS.COLLECTION.NEXT_STEP}${iouReport.reportID}`] ?? null; const optimisticData: OnyxUpdate[] = [ { @@ -3256,8 +3255,8 @@ function submitReport(expenseReport: OnyxTypes.Report) { key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`, value: { ...expenseReport, - lastMessageText: lodashGet(optimisticSubmittedReportAction, 'message.0.text', ''), - lastMessageHtml: lodashGet(optimisticSubmittedReportAction, 'message.0.html', ''), + lastMessageText: optimisticSubmittedReportAction.message?.[0].text ?? '', + lastMessageHtml: optimisticSubmittedReportAction.message?.[0].html ?? '', state: CONST.REPORT.STATE.SUBMITTED, stateNum: CONST.REPORT.STATE_NUM.PROCESSING, statusNum: CONST.REPORT.STATUS.SUBMITTED, From 4c7928b905cae9977b2dcb354cd01fe50d7673d8 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Mon, 8 Jan 2024 10:41:49 +0100 Subject: [PATCH 055/660] Fix TS errors --- src/components/OfflineWithFeedback.tsx | 2 +- src/libs/ErrorUtils.ts | 4 ++-- src/libs/Localize/index.ts | 2 +- src/libs/ReceiptUtils.ts | 2 +- src/libs/ReportActionsUtils.ts | 8 +++++--- src/libs/ReportUtils.ts | 4 ++-- src/libs/SidebarUtils.ts | 2 +- src/libs/ValidationUtils.ts | 2 +- 8 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/components/OfflineWithFeedback.tsx b/src/components/OfflineWithFeedback.tsx index fa0ae5162346..0446bd31025f 100644 --- a/src/components/OfflineWithFeedback.tsx +++ b/src/components/OfflineWithFeedback.tsx @@ -25,7 +25,7 @@ type OfflineWithFeedbackProps = ChildrenProps & { shouldHideOnDelete?: boolean; /** The errors to display */ - errors?: OnyxCommon.Errors | null; + errors?: OnyxCommon.SimpleErrors | null; /** Whether we should show the error messages */ shouldShowErrorMessages?: boolean; diff --git a/src/libs/ErrorUtils.ts b/src/libs/ErrorUtils.ts index cd2b04f27782..98b137502755 100644 --- a/src/libs/ErrorUtils.ts +++ b/src/libs/ErrorUtils.ts @@ -8,9 +8,9 @@ import * as Localize from './Localize'; type ErrorObject = Record; -type MicroSecondOnyxError = Record; +type MicroSecondOnyxError = Record; -type MicroSecondOnyxErrorObject = Record; +type MicroSecondOnyxErrorObject = Record; function getAuthenticateErrorMessage(response: Response): keyof TranslationFlatObject { switch (response.jsonCode) { diff --git a/src/libs/Localize/index.ts b/src/libs/Localize/index.ts index bc40f93dd13b..0cdbbc2e6e1f 100644 --- a/src/libs/Localize/index.ts +++ b/src/libs/Localize/index.ts @@ -97,7 +97,7 @@ function translateLocal(phrase: TKey, ...variable return translate(BaseLocaleListener.getPreferredLocale(), phrase, ...variables); } -type MaybePhraseKey = string | [string, Record & {isTranslated?: true}] | []; +type MaybePhraseKey = string | [string, Record & {isTranslated?: true}] | [] | null; /** * Return translated string for given error. diff --git a/src/libs/ReceiptUtils.ts b/src/libs/ReceiptUtils.ts index 734c40271208..091a3a89bc01 100644 --- a/src/libs/ReceiptUtils.ts +++ b/src/libs/ReceiptUtils.ts @@ -30,7 +30,7 @@ type FileNameAndExtension = { */ function getThumbnailAndImageURIs(transaction: Transaction, receiptPath: string | null = null, receiptFileName: string | null = null): ThumbnailAndImageURI { // URI to image, i.e. blob:new.expensify.com/9ef3a018-4067-47c6-b29f-5f1bd35f213d or expensify.com/receipts/w_e616108497ef940b7210ec6beb5a462d01a878f4.jpg - const path = transaction?.receipt?.source ?? receiptPath ?? ''; + const path = (transaction?.receipt?.source as string) ?? receiptPath ?? ''; // filename of uploaded image or last part of remote URI const filename = transaction?.filename ?? receiptFileName ?? ''; const isReceiptImage = Str.isImage(filename); diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index c8b7e673aed4..b6d60bbf726c 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -39,6 +39,8 @@ type MemberChangeMessageRoomReferenceElement = { type MemberChangeMessageElement = MessageTextElement | MemberChangeMessageUserMentionElement | MemberChangeMessageRoomReferenceElement; +type ActionsToMerge = Record; + const allReports: OnyxCollection = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, @@ -432,9 +434,9 @@ function replaceBaseURL(reportAction: ReportAction): ReportAction { /** */ -function getLastVisibleAction(reportID: string, actionsToMerge: ReportActions = {}): OnyxEntry { +function getLastVisibleAction(reportID: string, actionsToMerge: ActionsToMerge = {}): OnyxEntry { const reportActions = Object.values(OnyxUtils.fastMerge(allReportActions?.[reportID] ?? {}, actionsToMerge)); - const visibleReportActions = Object.values(reportActions ?? {}).filter((action) => shouldReportActionBeVisibleAsLastAction(action)); + const visibleReportActions = Object.values(reportActions ?? {}).filter((action): action is ReportAction => shouldReportActionBeVisibleAsLastAction(action)); const sortedReportActions = getSortedReportActions(visibleReportActions, true); if (sortedReportActions.length === 0) { return null; @@ -442,7 +444,7 @@ function getLastVisibleAction(reportID: string, actionsToMerge: ReportActions = return sortedReportActions[0]; } -function getLastVisibleMessage(reportID: string, actionsToMerge: ReportActions = {}): LastVisibleMessage { +function getLastVisibleMessage(reportID: string, actionsToMerge: ActionsToMerge = {}): LastVisibleMessage { const lastVisibleAction = getLastVisibleAction(reportID, actionsToMerge); const message = lastVisibleAction?.message?.[0]; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index a4dadc25b469..ceae4f6603dc 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -16,7 +16,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Beta, Login, PersonalDetails, PersonalDetailsList, Policy, Report, ReportAction, ReportMetadata, Session, Transaction} from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; -import type {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; +import type {Errors, Icon, PendingAction, SimpleErrors} from '@src/types/onyx/OnyxCommon'; import type {IOUMessage, OriginalMessageActionName, OriginalMessageCreated} from '@src/types/onyx/OriginalMessage'; import type {Status} from '@src/types/onyx/PersonalDetails'; import type {NotificationPreference} from '@src/types/onyx/Report'; @@ -289,7 +289,7 @@ type CustomIcon = { type OptionData = { text: string; alternateText?: string | null; - allReportErrors?: Errors | null; + allReportErrors?: SimpleErrors | null; brickRoadIndicator?: typeof CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR | '' | null; tooltipText?: string | null; alternateTextMaxLines?: number; diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index db16cf5cb552..3c49d8dd7aba 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -290,7 +290,7 @@ function getOptionData( result.isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); result.shouldShowSubscript = ReportUtils.shouldReportShowSubscript(report); result.pendingAction = report.pendingFields ? report.pendingFields.addWorkspaceRoom || report.pendingFields.createChat : undefined; - result.allReportErrors = OptionsListUtils.getAllReportErrors(report, reportActions) as OnyxCommon.Errors; + result.allReportErrors = OptionsListUtils.getAllReportErrors(report, reportActions) as OnyxCommon.SimpleErrors; result.brickRoadIndicator = Object.keys(result.allReportErrors ?? {}).length !== 0 ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; result.ownerAccountID = report.ownerAccountID; result.managerID = report.managerID; diff --git a/src/libs/ValidationUtils.ts b/src/libs/ValidationUtils.ts index 4b973d95d136..f7e725abb3a0 100644 --- a/src/libs/ValidationUtils.ts +++ b/src/libs/ValidationUtils.ts @@ -73,7 +73,7 @@ function isValidPastDate(date: string | Date): boolean { /** * Used to validate a value that is "required". */ -function isRequiredFulfilled(value: string | Date | unknown[] | Record): boolean { +function isRequiredFulfilled(value: string | Date | unknown[] | Record | null): boolean { if (typeof value === 'string') { return !StringUtils.isEmptyString(value); } From e60ebaad7428186dfc850248a55cc77f0a41f87e Mon Sep 17 00:00:00 2001 From: VickyStash Date: Mon, 8 Jan 2024 11:49:50 +0100 Subject: [PATCH 056/660] TS fixes after merging main --- src/ONYXKEYS.ts | 2 +- src/libs/ViolationsUtils.ts | 7 +-- src/libs/actions/IOU.ts | 87 ++++++++++--------------------------- src/types/onyx/Policy.ts | 6 +++ 4 files changed, 31 insertions(+), 71 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 0eadce1524bd..904e44feff5f 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -460,7 +460,7 @@ type OnyxValues = { [ONYXKEYS.COLLECTION.TRANSACTION]: OnyxTypes.Transaction; [ONYXKEYS.COLLECTION.TRANSACTION_DRAFT]: OnyxTypes.TransactionDraft; [ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT]: OnyxTypes.TransactionDraft; - [ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS]: OnyxTypes.TransactionViolation; + [ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS]: OnyxTypes.TransactionViolation[]; [ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS]: OnyxTypes.RecentlyUsedTags; [ONYXKEYS.COLLECTION.SELECTED_TAB]: string; [ONYXKEYS.COLLECTION.PRIVATE_NOTES_DRAFT]: string; diff --git a/src/libs/ViolationsUtils.ts b/src/libs/ViolationsUtils.ts index 2637686e726b..9b2b2c5f6dda 100644 --- a/src/libs/ViolationsUtils.ts +++ b/src/libs/ViolationsUtils.ts @@ -1,5 +1,6 @@ import reject from 'lodash/reject'; import Onyx from 'react-native-onyx'; +import type {OnyxUpdate} from 'react-native-onyx'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PolicyCategories, PolicyTags, Transaction, TransactionViolation} from '@src/types/onyx'; @@ -17,11 +18,7 @@ const ViolationsUtils = { policyTags: PolicyTags, policyRequiresCategories: boolean, policyCategories: PolicyCategories, - ): { - onyxMethod: string; - key: string; - value: TransactionViolation[]; - } { + ): OnyxUpdate { let newTransactionViolations = [...transactionViolations]; if (policyRequiresCategories) { diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 101adfceed76..46c1f8cae17e 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -27,8 +27,8 @@ import * as ReportUtils from '@libs/ReportUtils'; import type {OptimisticChatReport, OptimisticCreatedReportAction, OptimisticIOUReportAction, TransactionDetails} from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import * as UserUtils from '@libs/UserUtils'; -import type {MoneyRequestNavigatorParamList} from '@navigation/types'; import ViolationsUtils from '@libs/ViolationsUtils'; +import type {MoneyRequestNavigatorParamList} from '@navigation/types'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -174,7 +174,7 @@ Onyx.connect({ }, }); -let allTransactionViolations: Record = {}; +let allTransactionViolations: Record = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, waitForCollectionCallback: true, @@ -352,27 +352,7 @@ function getReceiptError(receipt?: Receipt, filename?: string, isScanRequest = t : ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source: receipt.source, filename}); } -/** - * Builds the Onyx data for a money request. - * - * @param {Object} chatReport - * @param {Object} iouReport - * @param {Object} transaction - * @param {Object} chatCreatedAction - * @param {Object} iouCreatedAction - * @param {Object} iouAction - * @param {Object} optimisticPersonalDetailListAction - * @param {Object} reportPreviewAction - * @param {Array} optimisticPolicyRecentlyUsedCategories - * @param {Array} optimisticPolicyRecentlyUsedTags - * @param {boolean} isNewChatReport - * @param {boolean} isNewIOUReport - * @param {Object} policy - May be undefined, an empty object, or an object matching the Policy type (src/types/onyx/Policy.ts) - * @param {Array} policyTags - * @param {Array} policyCategories - * @param {Boolean} hasOutstandingChildRequest - * @returns {Array} - An array containing the optimistic data, success data, and failure data. - */ +/** Builds the Onyx data for a money request */ function buildOnyxDataForMoneyRequest( chatReport: OnyxTypes.Report, iouReport: OnyxTypes.Report, @@ -386,9 +366,9 @@ function buildOnyxDataForMoneyRequest( optimisticPolicyRecentlyUsedTags: OptimisticPolicyRecentlyUsedTags, isNewChatReport: boolean, isNewIOUReport: boolean, - policy, - policyTags, - policyCategories, + policy?: OnyxTypes.Policy | EmptyObject | undefined, + policyTags: OnyxTypes.PolicyTags = {}, + policyCategories: OnyxTypes.PolicyCategories = {}, hasOutstandingChildRequest = false, ): OnyxUpdate[][] { const isScanRequest = TransactionUtils.isScanRequest(transaction); @@ -644,18 +624,18 @@ function buildOnyxDataForMoneyRequest( ]; // Policy won't be set for P2P cases for which we don't need to compute violations - if (!policy || !policy.id) { + if (!policy?.id) { return [optimisticData, successData, failureData]; } - const violationsOnyxData = ViolationsUtils.getViolationsOnyxData(transaction, [], policy.requiresTag, policyTags, policy.requiresCategory, policyCategories); + const violationsOnyxData = ViolationsUtils.getViolationsOnyxData(transaction, [], !!policy.requiresTag, policyTags, !!policy.requiresCategory, policyCategories); if (violationsOnyxData) { optimisticData.push(violationsOnyxData); failureData.push({ onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transaction.transactionID}`, - value: [], + value: null, }); } @@ -681,9 +661,9 @@ function getMoneyRequestInformation( category?: string, tag?: string, billable?: boolean, - policy = undefined, - policyTags = undefined, - policyCategories = undefined, + policy?: OnyxTypes.Policy | EmptyObject, + policyTags?: OnyxTypes.PolicyTags, + policyCategories?: OnyxTypes.PolicyCategories, ): MoneyRequestInformation { const payerEmail = OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login ?? ''); const payerAccountID = Number(participant.accountID); @@ -717,10 +697,10 @@ function getMoneyRequestInformation( let needsToBeManuallySubmitted = false; let isFromPaidPolicy = false; if (isPolicyExpenseChat) { - isFromPaidPolicy = PolicyUtils.isPaidGroupPolicy(policy); + isFromPaidPolicy = PolicyUtils.isPaidGroupPolicy(policy ?? null); // If the scheduled submit is turned off on the policy, user needs to manually submit the report which is indicated by GBR in LHN - needsToBeManuallySubmitted = isFromPaidPolicy && !(policy.isHarvestingEnabled || false); + needsToBeManuallySubmitted = isFromPaidPolicy && !(!!policy?.isHarvestingEnabled || false); // If the linked expense report on paid policy is not draft, we need to create a new draft expense report if (iouReport && isFromPaidPolicy && !ReportUtils.isDraftExpenseReport(iouReport)) { @@ -876,24 +856,7 @@ function getMoneyRequestInformation( }; } -/** - * Requests money based on a distance (eg. mileage from a map) - * - * @param {Object} report - * @param {Object} participant - * @param {String} comment - * @param {String} created - * @param {String} [category] - * @param {String} [tag] - * @param {Number} amount - * @param {String} currency - * @param {String} merchant - * @param {Boolean} [billable] - * @param {Object} validWaypoints - * @param {Object} policy - May be undefined, an empty object, or an object matching the Policy type (src/types/onyx/Policy.ts) - * @param {Array} policyTags - * @param {Array} policyCategories - */ +/** Requests money based on a distance (eg. mileage from a map) */ function createDistanceRequest( report: OnyxTypes.Report, participant: Participant, @@ -906,9 +869,9 @@ function createDistanceRequest( merchant: string, billable: boolean | undefined, validWaypoints: WaypointCollection, - policy, - policyTags, - policyCategories + policy: OnyxTypes.Policy | EmptyObject | undefined, + policyTags: OnyxTypes.PolicyTags, + policyCategories: OnyxTypes.PolicyCategories, ) { // If the report is an iou or expense report, we should get the linked chat report to be passed to the getMoneyRequestInformation function const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); @@ -1055,7 +1018,7 @@ function getUpdateMoneyRequestParams( key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThread?.reportID}`, value: { [updatedReportAction.reportActionID]: { - ...updatedReportAction, + ...(updatedReportAction as OnyxTypes.ReportAction), errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericEditFailureMessage'), }, }, @@ -1187,15 +1150,9 @@ function updateMoneyRequestDate(transactionID: string, transactionThreadReportID API.write('UpdateMoneyRequestDate', params, onyxData); } -/** - * Updates the created date of a money request - * - * @param {String} transactionID - * @param {Number} transactionThreadReportID - * @param {String} tag - */ -function updateMoneyRequestTag(transactionID, transactionThreadReportID, tag) { - const transactionChanges = { +/** Updates the created date of a money request */ +function updateMoneyRequestTag(transactionID: string, transactionThreadReportID: string, tag: string) { + const transactionChanges: TransactionChanges = { tag, }; const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, true); diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index ff3a5e1dd23c..b71705a92192 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -82,6 +82,12 @@ type Policy = { /** The employee list of the policy */ employeeList?: []; + + /** Whether or not the policy requires tags */ + requiresTag?: boolean; + + /** Whether or not the policy requires categories */ + requiresCategory?: boolean; }; export default Policy; From 36420a7379571986475cab9510af6b6a1e593d9b Mon Sep 17 00:00:00 2001 From: VickyStash Date: Mon, 8 Jan 2024 11:57:41 +0100 Subject: [PATCH 057/660] Lint fixes --- src/components/TextInput/BaseTextInput/index.native.tsx | 2 ++ src/components/TextInput/BaseTextInput/index.tsx | 2 ++ src/libs/IOUUtils.ts | 1 - src/libs/ReportUtils.ts | 2 +- src/types/onyx/IOU.ts | 6 +++--- src/types/onyx/Transaction.ts | 4 ++-- 6 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/components/TextInput/BaseTextInput/index.native.tsx b/src/components/TextInput/BaseTextInput/index.native.tsx index f4cc1ee9e0ba..7d3c2d087914 100644 --- a/src/components/TextInput/BaseTextInput/index.native.tsx +++ b/src/components/TextInput/BaseTextInput/index.native.tsx @@ -247,6 +247,8 @@ function BaseTextInput( const hasLabel = Boolean(label?.length); const isReadOnly = inputProps.readOnly ?? inputProps.disabled; + // Disabling this line for safeness as nullish coalescing works only if the value is undefined or null + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const inputHelpText = errorText || hint; const placeholderValue = !!prefixCharacter || isFocused || !hasLabel || (hasLabel && forceActiveLabel) ? placeholder : undefined; const maxHeight = StyleSheet.flatten(containerStyles)?.maxHeight; diff --git a/src/components/TextInput/BaseTextInput/index.tsx b/src/components/TextInput/BaseTextInput/index.tsx index 9c3899979aaa..ad19948316ee 100644 --- a/src/components/TextInput/BaseTextInput/index.tsx +++ b/src/components/TextInput/BaseTextInput/index.tsx @@ -243,6 +243,8 @@ function BaseTextInput( const hasLabel = Boolean(label?.length); const isReadOnly = inputProps.readOnly ?? inputProps.disabled; + // Disabling this line for safeness as nullish coalescing works only if the value is undefined or null + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const inputHelpText = errorText || hint; const newPlaceholder = !!prefixCharacter || isFocused || !hasLabel || (hasLabel && forceActiveLabel) ? placeholder : undefined; const maxHeight = StyleSheet.flatten(containerStyles).maxHeight; diff --git a/src/libs/IOUUtils.ts b/src/libs/IOUUtils.ts index 8a6036ca95d8..19a9e558bf46 100644 --- a/src/libs/IOUUtils.ts +++ b/src/libs/IOUUtils.ts @@ -1,4 +1,3 @@ -import type {OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index e04969f6e375..8e113634ba82 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -16,7 +16,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Beta, Login, PersonalDetails, PersonalDetailsList, Policy, Report, ReportAction, ReportMetadata, Session, Transaction} from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; -import type {Errors, Icon, PendingAction, SimpleErrors} from '@src/types/onyx/OnyxCommon'; +import type {Icon, PendingAction, SimpleErrors} from '@src/types/onyx/OnyxCommon'; import type {IOUMessage, OriginalMessageActionName, OriginalMessageCreated} from '@src/types/onyx/OriginalMessage'; import type {Status} from '@src/types/onyx/PersonalDetails'; import type {NotificationPreference} from '@src/types/onyx/Report'; diff --git a/src/types/onyx/IOU.ts b/src/types/onyx/IOU.ts index 4f9989d347a6..59709de4638e 100644 --- a/src/types/onyx/IOU.ts +++ b/src/types/onyx/IOU.ts @@ -1,6 +1,6 @@ -import {ValueOf} from 'type-fest'; -import CONST from '@src/CONST'; -import {Icon} from './OnyxCommon'; +import type {ValueOf} from 'type-fest'; +import type CONST from '@src/CONST'; +import type {Icon} from './OnyxCommon'; type Participant = { accountID?: number; diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 3875e70aff48..b093234368b5 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -1,7 +1,7 @@ -import {ImageSourcePropType} from 'react-native'; +import type {ImageSourcePropType} from 'react-native'; import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; -import {Participant} from './IOU'; +import type {Participant} from './IOU'; import type * as OnyxCommon from './OnyxCommon'; import type RecentWaypoint from './RecentWaypoint'; From a7e9e5aec49d03ba0fb95fad080deadcdd026425 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Mon, 8 Jan 2024 15:12:13 +0100 Subject: [PATCH 058/660] add patches and fixes for expo --- package-lock.json | 14 ++-- package.json | 2 +- patches/expo+49.0.21.patch | 32 ++++++++ patches/expo-modules-autolinking+1.5.1.patch | 82 ++++++++++++++++++++ patches/expo-modules-core+1.5.12.patch | 16 ++++ 5 files changed, 138 insertions(+), 8 deletions(-) create mode 100644 patches/expo+49.0.21.patch create mode 100644 patches/expo-modules-autolinking+1.5.1.patch create mode 100644 patches/expo-modules-core+1.5.12.patch diff --git a/package-lock.json b/package-lock.json index 4a3b55d65205..b10078ee7e6b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -111,7 +111,7 @@ "react-native-tab-view": "^3.5.2", "react-native-url-polyfill": "^2.0.0", "react-native-view-shot": "^3.6.0", - "react-native-vision-camera": "2.16.6", + "react-native-vision-camera": "2.16.5", "react-native-web": "^0.19.9", "react-native-web-linear-gradient": "^1.1.2", "react-native-webview": "^11.17.2", @@ -47872,9 +47872,9 @@ } }, "node_modules/react-native-vision-camera": { - "version": "2.16.6", - "resolved": "https://registry.npmjs.org/react-native-vision-camera/-/react-native-vision-camera-2.16.6.tgz", - "integrity": "sha512-rhlRC+yLB1fIB5eVjtnbjgTqNzihFqvoc4B0n+PlytYvHeK3BpD3Shawmy8Me/nBVL5k4Ay+QOd4niFOir+6YQ==", + "version": "2.16.5", + "resolved": "https://registry.npmjs.org/react-native-vision-camera/-/react-native-vision-camera-2.16.5.tgz", + "integrity": "sha512-MzXhNd597OyMQSEGhqWI4DufWkdr7PR7U9B30E3gXnln7cnvjMVIp4j3eIW9BIrgvEyUcEeL7nZM5NLhTmO/fA==", "peerDependencies": { "react": "*", "react-native": "*" @@ -90688,9 +90688,9 @@ "requires": {} }, "react-native-vision-camera": { - "version": "2.16.6", - "resolved": "https://registry.npmjs.org/react-native-vision-camera/-/react-native-vision-camera-2.16.6.tgz", - "integrity": "sha512-rhlRC+yLB1fIB5eVjtnbjgTqNzihFqvoc4B0n+PlytYvHeK3BpD3Shawmy8Me/nBVL5k4Ay+QOd4niFOir+6YQ==", + "version": "2.16.5", + "resolved": "https://registry.npmjs.org/react-native-vision-camera/-/react-native-vision-camera-2.16.5.tgz", + "integrity": "sha512-MzXhNd597OyMQSEGhqWI4DufWkdr7PR7U9B30E3gXnln7cnvjMVIp4j3eIW9BIrgvEyUcEeL7nZM5NLhTmO/fA==", "requires": {} }, "react-native-web": { diff --git a/package.json b/package.json index 446a815d4f79..2c25c863f49f 100644 --- a/package.json +++ b/package.json @@ -159,7 +159,7 @@ "react-native-tab-view": "^3.5.2", "react-native-url-polyfill": "^2.0.0", "react-native-view-shot": "^3.6.0", - "react-native-vision-camera": "2.16.6", + "react-native-vision-camera": "2.16.5", "react-native-web": "^0.19.9", "react-native-web-linear-gradient": "^1.1.2", "react-native-webview": "^11.17.2", diff --git a/patches/expo+49.0.21.patch b/patches/expo+49.0.21.patch new file mode 100644 index 000000000000..0e246b301c28 --- /dev/null +++ b/patches/expo+49.0.21.patch @@ -0,0 +1,32 @@ +diff --git a/node_modules/expo/scripts/autolinking.gradle b/node_modules/expo/scripts/autolinking.gradle +index 0505395..08098e0 100644 +--- a/node_modules/expo/scripts/autolinking.gradle ++++ b/node_modules/expo/scripts/autolinking.gradle +@@ -1,2 +1,3 @@ + def autolinkingPath = ["node", "--print", "require.resolve('expo-modules-autolinking/package.json')"].execute(null, rootDir).text.trim() +-apply from: new File(autolinkingPath, "../scripts/android/autolinking_implementation.gradle"); ++ ++apply from: hasProperty("reactNativeProject") ? file('../../expo-modules-autolinking/scripts/android/autolinking_implementation.gradle') : new File(autolinkingPath, "../scripts/android/autolinking_implementation.gradle"); +diff --git a/node_modules/expo/scripts/autolinking.rb b/node_modules/expo/scripts/autolinking.rb +index 0617de5..584364f 100644 +--- a/node_modules/expo/scripts/autolinking.rb ++++ b/node_modules/expo/scripts/autolinking.rb +@@ -2,13 +2,14 @@ require 'json' + require 'pathname' + require 'colored2' # dependency of CocoaPods + +-require File.join(File.dirname(`node --print "require.resolve('expo-modules-autolinking/package.json')"`), "scripts/ios/autolinking_manager") +-require File.join(File.dirname(`node --print "require.resolve('expo-modules-autolinking/package.json')"`), "scripts/ios/xcode_env_generator") +-require File.join(File.dirname(`node --print "require.resolve('expo-modules-autolinking/package.json')"`), "scripts/ios/react_import_patcher") ++AUTOLINKING_PATH = ENV['REACT_NATIVE_DIR'] ? `node --print "require.resolve('#{ENV['REACT_NATIVE_DIR']}/node_modules/expo-modules-autolinking/package.json')"` : `node --print "require.resolve('expo-modules-autolinking/package.json')"` ++ ++require File.join(File.dirname(AUTOLINKING_PATH), "scripts/ios/autolinking_manager") ++require File.join(File.dirname(AUTOLINKING_PATH), "scripts/ios/xcode_env_generator") ++require File.join(File.dirname(AUTOLINKING_PATH), "scripts/ios/react_import_patcher") + + def use_expo_modules!(options = {}) + # When run from the Podfile, `self` points to Pod::Podfile object +- + if @current_target_definition.autolinking_manager.present? + Pod::UI.message 'Expo modules are already being used in this target definition'.red + return diff --git a/patches/expo-modules-autolinking+1.5.1.patch b/patches/expo-modules-autolinking+1.5.1.patch new file mode 100644 index 000000000000..312c1aa8967e --- /dev/null +++ b/patches/expo-modules-autolinking+1.5.1.patch @@ -0,0 +1,82 @@ +diff --git a/node_modules/expo-modules-autolinking/scripts/android/autolinking_implementation.gradle b/node_modules/expo-modules-autolinking/scripts/android/autolinking_implementation.gradle +index 5f4cd8e..bdbea02 100644 +--- a/node_modules/expo-modules-autolinking/scripts/android/autolinking_implementation.gradle ++++ b/node_modules/expo-modules-autolinking/scripts/android/autolinking_implementation.gradle +@@ -135,6 +135,7 @@ class ExpoAutolinkingManager { + } + + static String exec(String[] commandArgs, File dir) { ++ print(dir) + Process proc = commandArgs.execute(null, dir) + StringBuffer outputStream = new StringBuffer() + proc.waitForProcessOutput(outputStream, System.err) +@@ -142,11 +143,12 @@ class ExpoAutolinkingManager { + } + + static private String[] convertOptionsToCommandArgs(String command, Map options) { ++ def expoModulesPath = options.searchPaths ? "../react-native/node_modules/expo-modules-autolinking" : "expo-modules-autolinking" + String[] args = [ + 'node', + '--no-warnings', + '--eval', +- 'require(\'expo-modules-autolinking\')(process.argv.slice(1))', ++ "require(\'${expoModulesPath}\')(process.argv.slice(1))", + '--', + command, + '--platform', +diff --git a/node_modules/expo-modules-autolinking/scripts/ios/autolinking_manager.rb b/node_modules/expo-modules-autolinking/scripts/ios/autolinking_manager.rb +index d570ad2..b9c5350 100644 +--- a/node_modules/expo-modules-autolinking/scripts/ios/autolinking_manager.rb ++++ b/node_modules/expo-modules-autolinking/scripts/ios/autolinking_manager.rb +@@ -26,7 +26,6 @@ module Expo + if has_packages? + return + end +- + global_flags = @options.fetch(:flags, {}) + tests_only = @options.fetch(:testsOnly, false) + include_tests = @options.fetch(:includeTests, false) +@@ -174,11 +173,12 @@ module Expo + end + + private def node_command_args(command_name) ++ autolinking_path = ENV['REACT_NATIVE_DIR'] ? "#{ENV['REACT_NATIVE_DIR']}/node_modules/expo-modules-autolinking" : "expo-modules-autolinking" + eval_command_args = [ + 'node', + '--no-warnings', + '--eval', +- 'require(\'expo-modules-autolinking\')(process.argv.slice(1))', ++ "require(\'#{autolinking_path}\')(process.argv.slice(1))", + command_name, + '--platform', + 'ios' +diff --git a/node_modules/expo-modules-autolinking/scripts/ios/project_integrator.rb b/node_modules/expo-modules-autolinking/scripts/ios/project_integrator.rb +index bddfa0d..3ae2c6c 100644 +--- a/node_modules/expo-modules-autolinking/scripts/ios/project_integrator.rb ++++ b/node_modules/expo-modules-autolinking/scripts/ios/project_integrator.rb +@@ -213,6 +213,7 @@ module Expo + # Generates the support script that is executed by the build script phase. + def self.generate_support_script(autolinking_manager, modules_provider_path) + args = autolinking_manager.base_command_args.map { |arg| "\"#{arg}\"" }.join(' ') ++ autolinking_path = ENV['REACT_NATIVE_DIR'] ? "#{ENV['REACT_NATIVE_DIR']}/node_modules/expo-modules-autolinking" : "expo-modules-autolinking" + + <<~SUPPORT_SCRIPT + #!/usr/bin/env bash +@@ -245,7 +246,6 @@ module Expo + You can set this up quickly by running: + + echo "export NODE_BINARY=\\$(command -v node)" >> .xcode.env +- + in the ios folder of your project. + NODE_NOT_FOUND + +@@ -257,8 +257,7 @@ module Expo + "$NODE_BINARY" "$@" + fi + } +- +- with_node --no-warnings --eval "require(\'expo-modules-autolinking\')(process.argv.slice(1))" generate-package-list #{args} --target "#{modules_provider_path}" ++ with_node --no-warnings --eval "require(\'#{autolinking_path}\')(process.argv.slice(1))" generate-package-list #{args} --target "#{modules_provider_path}" + SUPPORT_SCRIPT + end + diff --git a/patches/expo-modules-core+1.5.12.patch b/patches/expo-modules-core+1.5.12.patch new file mode 100644 index 000000000000..93383f04440c --- /dev/null +++ b/patches/expo-modules-core+1.5.12.patch @@ -0,0 +1,16 @@ +diff --git a/node_modules/expo-modules-core/android/build.gradle b/node_modules/expo-modules-core/android/build.gradle +index 8f9e0dc..539a201 100644 +--- a/node_modules/expo-modules-core/android/build.gradle ++++ b/node_modules/expo-modules-core/android/build.gradle +@@ -54,9 +54,10 @@ def downloadsDir = customDownloadsDir ? new File(customDownloadsDir) : new File( + def thirdPartyNdkDir = new File("$buildDir/third-party-ndk") + + def REACT_NATIVE_BUILD_FROM_SOURCE = findProject(":packages:react-native:ReactAndroid") != null ++def FALLBACK_REACT_NATIVE_DIR = ext.has("customRNProjectPath") ? ext.customRNProjectPath : new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).parent) + def REACT_NATIVE_DIR = REACT_NATIVE_BUILD_FROM_SOURCE + ? findProject(":packages:react-native:ReactAndroid").getProjectDir().parent +- : new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).parent ++ : FALLBACK_REACT_NATIVE_DIR + def REACT_NATIVE_SO_DIR = REACT_NATIVE_BUILD_FROM_SOURCE + ? Paths.get(findProject(":packages:react-native:ReactAndroid").getProjectDir().toString(), "build", "intermediates", "library_*", "*", "jni") + : "${buildDir}/react/jni" From d17163a1b7e019dc292aff09f3aacae255cd0045 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Mon, 8 Jan 2024 15:18:49 +0100 Subject: [PATCH 059/660] one more fix --- patches/expo-modules-autolinking+1.5.1.patch | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/patches/expo-modules-autolinking+1.5.1.patch b/patches/expo-modules-autolinking+1.5.1.patch index 312c1aa8967e..20fed8608ebd 100644 --- a/patches/expo-modules-autolinking+1.5.1.patch +++ b/patches/expo-modules-autolinking+1.5.1.patch @@ -80,3 +80,21 @@ index bddfa0d..3ae2c6c 100644 SUPPORT_SCRIPT end +diff --git a/node_modules/expo-modules-autolinking/scripts/ios/react_import_patcher.rb b/node_modules/expo-modules-autolinking/scripts/ios/react_import_patcher.rb +index 4acf193..b54ea94 100644 +--- a/node_modules/expo-modules-autolinking/scripts/ios/react_import_patcher.rb ++++ b/node_modules/expo-modules-autolinking/scripts/ios/react_import_patcher.rb +@@ -10,11 +10,12 @@ module Expo + end + + public def run! ++ autolinking_path = ENV['REACT_NATIVE_DIR'] ? "#{ENV['REACT_NATIVE_DIR']}/node_modules/expo-modules-autolinking" : "expo-modules-autolinking" + args = [ + 'node', + '--no-warnings', + '--eval', +- 'require(\'expo-modules-autolinking\')(process.argv.slice(1))', ++ "require(\'#{autolinking_path}\')(process.argv.slice(1))", + 'patch-react-imports', + '--pods-root', + File.expand_path(@root), From d082cd428da7a711ca63fa64d6dfcbac4621562d Mon Sep 17 00:00:00 2001 From: VickyStash Date: Mon, 8 Jan 2024 15:35:17 +0100 Subject: [PATCH 060/660] Minor code improvements --- src/ONYXKEYS.ts | 2 +- src/libs/Localize/index.ts | 2 +- src/libs/actions/IOU.ts | 67 ++++++++++++-------------- src/types/onyx/TransactionViolation.ts | 4 +- src/types/onyx/index.ts | 3 +- 5 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 904e44feff5f..781f375385f6 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -460,7 +460,7 @@ type OnyxValues = { [ONYXKEYS.COLLECTION.TRANSACTION]: OnyxTypes.Transaction; [ONYXKEYS.COLLECTION.TRANSACTION_DRAFT]: OnyxTypes.TransactionDraft; [ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT]: OnyxTypes.TransactionDraft; - [ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS]: OnyxTypes.TransactionViolation[]; + [ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS]: OnyxTypes.TransactionViolations; [ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS]: OnyxTypes.RecentlyUsedTags; [ONYXKEYS.COLLECTION.SELECTED_TAB]: string; [ONYXKEYS.COLLECTION.PRIVATE_NOTES_DRAFT]: string; diff --git a/src/libs/Localize/index.ts b/src/libs/Localize/index.ts index 0cdbbc2e6e1f..bc40f93dd13b 100644 --- a/src/libs/Localize/index.ts +++ b/src/libs/Localize/index.ts @@ -97,7 +97,7 @@ function translateLocal(phrase: TKey, ...variable return translate(BaseLocaleListener.getPreferredLocale(), phrase, ...variables); } -type MaybePhraseKey = string | [string, Record & {isTranslated?: true}] | [] | null; +type MaybePhraseKey = string | [string, Record & {isTranslated?: true}] | []; /** * Return translated string for given error. diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 46c1f8cae17e..0ed7a47e2aa5 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/naming-convention */ import type {StackScreenProps} from '@react-navigation/stack'; import {format} from 'date-fns'; import Str from 'expensify-common/lib/str'; @@ -58,6 +57,8 @@ type MoneyRequestRoute = StackScreenProps< type IOURequestType = ValueOf; +type PaymentMethodType = DeepValueOf; + type MoneyRequestInformation = { payerAccountID: number; payerEmail: string; @@ -110,8 +111,6 @@ type PayMoneyRequestData = { failureData: OnyxUpdate[]; }; -type PaymentMethodType = DeepValueOf; - type SendMoneyParams = { iouReportID: string; chatReportID: string; @@ -174,7 +173,7 @@ Onyx.connect({ }, }); -let allTransactionViolations: Record = {}; +let allTransactionViolations: Record = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, waitForCollectionCallback: true, @@ -235,7 +234,7 @@ Onyx.connect({ /** * Initialize money request info * @param reportID to attach the transaction to - * @param [iouRequestType] one of manual/scan/distance + * @param iouRequestType one of manual/scan/distance */ function startMoneyRequest_temporaryForRefactor(reportID: string, isFromGlobalCreate: boolean, iouRequestType: IOURequestType = CONST.IOU.REQUEST_TYPE.MANUAL) { // Generate a brand new transactionID @@ -258,7 +257,7 @@ function startMoneyRequest_temporaryForRefactor(reportID: string, isFromGlobalCr amount: 0, comment, created, - currency: currentUserPersonalDetails?.localCurrencyCode ?? CONST.CURRENCY.USD, + currency: currentUserPersonalDetails.localCurrencyCode ?? CONST.CURRENCY.USD, iouRequestType, reportID, transactionID: newTransactionID, @@ -328,7 +327,7 @@ function resetMoneyRequestInfo(id = '') { Onyx.merge(ONYXKEYS.IOU, { id, amount: 0, - currency: currentUserPersonalDetails?.localCurrencyCode ?? CONST.CURRENCY.USD, + currency: currentUserPersonalDetails.localCurrencyCode ?? CONST.CURRENCY.USD, comment: '', participants: [], merchant: CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, @@ -360,13 +359,13 @@ function buildOnyxDataForMoneyRequest( chatCreatedAction: OptimisticCreatedReportAction, iouCreatedAction: OptimisticCreatedReportAction, iouAction: OptimisticIOUReportAction, - optimisticPersonalDetailListAction: OnyxTypes.PersonalDetailsList | undefined, + optimisticPersonalDetailListAction: OnyxTypes.PersonalDetailsList, reportPreviewAction: ReportAction, optimisticPolicyRecentlyUsedCategories: OptimisticPolicyRecentlyUsedCategories, optimisticPolicyRecentlyUsedTags: OptimisticPolicyRecentlyUsedTags, isNewChatReport: boolean, isNewIOUReport: boolean, - policy?: OnyxTypes.Policy | EmptyObject | undefined, + policy?: OnyxTypes.Policy | EmptyObject, policyTags: OnyxTypes.PolicyTags = {}, policyCategories: OnyxTypes.PolicyCategories = {}, hasOutstandingChildRequest = false, @@ -711,7 +710,7 @@ function getMoneyRequestInformation( if (iouReport) { if (isPolicyExpenseChat) { iouReport = {...iouReport}; - if (iouReport?.currency === currency && iouReport.total) { + if (iouReport?.currency === currency && typeof iouReport.total === 'number') { // Because of the Expense reports are stored as negative values, we substract the total from the amount iouReport.total -= amount; } @@ -749,8 +748,10 @@ function getMoneyRequestInformation( billable, ); + // TODO: Remove this type once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories(iouReport.policyID, category) as OptimisticPolicyRecentlyUsedCategories; + // TODO: Remove this type once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. const optimisticPolicyRecentlyUsedTags = Policy.buildOptimisticPolicyRecentlyUsedTags(iouReport.policyID, tag) as OptimisticPolicyRecentlyUsedTags; // If there is an existing transaction (which is the case for distance requests), then the data from the existing transaction @@ -812,7 +813,7 @@ function getMoneyRequestInformation( isOptimisticPersonalDetail: true, }, } - : undefined; + : {}; // The policy expense chat should have the GBR only when its a paid policy and the scheduled submit is turned off // so the employee has to submit to their manager manually. @@ -959,14 +960,7 @@ function getUpdateMoneyRequestParams( // Step 1: Set any "pending fields" (ones updated while the user was offline) to have error messages in the failureData const pendingFields = Object.fromEntries(Object.keys(transactionChanges).map((key) => [key, CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE])); const clearedPendingFields = Object.fromEntries(Object.keys(transactionChanges).map((key) => [key, null])); - const errorFields = Object.fromEntries( - Object.keys(pendingFields).map((key) => [ - key, - { - [DateUtils.getMicroseconds()]: Localize.translateLocal('iou.error.genericEditFailureMessage'), - }, - ]), - ); + const errorFields = Object.fromEntries(Object.keys(pendingFields).map((key) => [key, {[DateUtils.getMicroseconds()]: Localize.translateLocal('iou.error.genericEditFailureMessage')}])); // Step 2: Get all the collections being updated const transactionThread = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; @@ -1066,7 +1060,8 @@ function getUpdateMoneyRequestParams( // Update recently used categories if the category is changed if ('category' in transactionChanges) { - const optimisticPolicyRecentlyUsedCategories: OptimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories( + // TODO: Remove assertion once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. + const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories( iouReport?.policyID, transactionChanges.category, ) as OptimisticPolicyRecentlyUsedCategories; @@ -1081,10 +1076,8 @@ function getUpdateMoneyRequestParams( // Update recently used categories if the tag is changed if ('tag' in transactionChanges) { - const optimisticPolicyRecentlyUsedTags: OptimisticPolicyRecentlyUsedTags = Policy.buildOptimisticPolicyRecentlyUsedTags( - iouReport?.policyID, - transactionChanges.tag, - ) as OptimisticPolicyRecentlyUsedTags; + // TODO: Remove assertion once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. + const optimisticPolicyRecentlyUsedTags = Policy.buildOptimisticPolicyRecentlyUsedTags(iouReport?.policyID, transactionChanges.tag) as OptimisticPolicyRecentlyUsedTags; if (!isEmptyObject(optimisticPolicyRecentlyUsedTags)) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, @@ -1509,7 +1502,7 @@ function createSplitsAndOnyxData( ? ReportUtils.buildOptimisticExpenseReport(oneOnOneChatReport.reportID, oneOnOneChatReport.policyID ?? '', currentUserAccountID, splitAmount, currency) : ReportUtils.buildOptimisticIOUReport(currentUserAccountID, accountID, splitAmount, oneOnOneChatReport.reportID, currency); } else if (isOwnPolicyExpenseChat) { - if (oneOnOneIOUReport?.total) { + if (typeof oneOnOneIOUReport?.total === 'number') { // Because of the Expense reports are stored as negative values, we subtract the total from the amount oneOnOneIOUReport.total -= splitAmount; } @@ -1562,7 +1555,7 @@ function createSplitsAndOnyxData( ); // Add optimistic personal details for new participants - const oneOnOnePersonalDetailListAction: OnyxTypes.PersonalDetailsList | undefined = shouldCreateOptimisticPersonalDetails + const oneOnOnePersonalDetailListAction: OnyxTypes.PersonalDetailsList = shouldCreateOptimisticPersonalDetails ? { [accountID]: { accountID, @@ -1573,7 +1566,7 @@ function createSplitsAndOnyxData( isOptimisticPersonalDetail: true, }, } - : undefined; + : {}; let oneOnOneReportPreviewAction = ReportActionsUtils.getReportPreviewAction(oneOnOneChatReport.reportID, oneOnOneIOUReport?.reportID ?? ''); if (oneOnOneReportPreviewAction) { @@ -1585,13 +1578,15 @@ function createSplitsAndOnyxData( } // Add category to optimistic policy recently used categories when a participant is a workspace - const optimisticPolicyRecentlyUsedCategories: OptimisticPolicyRecentlyUsedCategories = isPolicyExpenseChat - ? (Policy.buildOptimisticPolicyRecentlyUsedCategories(participant.policyID, category) as OptimisticPolicyRecentlyUsedCategories) + const optimisticPolicyRecentlyUsedCategories = isPolicyExpenseChat + ? // TODO: Remove assertion once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. + (Policy.buildOptimisticPolicyRecentlyUsedCategories(participant.policyID, category) as OptimisticPolicyRecentlyUsedCategories) : []; // Add tag to optimistic policy recently used tags when a participant is a workspace - const optimisticPolicyRecentlyUsedTags: OptimisticPolicyRecentlyUsedTags = isPolicyExpenseChat - ? (Policy.buildOptimisticPolicyRecentlyUsedTags(participant.policyID, tag) as OptimisticPolicyRecentlyUsedTags) + const optimisticPolicyRecentlyUsedTags = isPolicyExpenseChat + ? // TODO: Remove assertion once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. + (Policy.buildOptimisticPolicyRecentlyUsedTags(participant.policyID, tag) as OptimisticPolicyRecentlyUsedTags) : {}; // STEP 5: Build Onyx Data @@ -2143,7 +2138,7 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA ? ReportUtils.buildOptimisticExpenseReport(oneOnOneChatReport?.reportID ?? '', participant.policyID ?? '', sessionAccountID, splitAmount, currency ?? '') : ReportUtils.buildOptimisticIOUReport(sessionAccountID, participant.accountID ?? -1, splitAmount, oneOnOneChatReport?.reportID ?? '', currency ?? ''); } else if (isPolicyExpenseChat) { - if (oneOnOneIOUReport?.total) { + if (typeof oneOnOneIOUReport?.total === 'number') { // Because of the Expense reports are stored as negative values, we subtract the total from the amount oneOnOneIOUReport.total -= splitAmount; } @@ -2291,7 +2286,7 @@ function editRegularMoneyRequest(transactionID: string, transactionThreadReportI const updatedChatReport = {...chatReport}; const diff = TransactionUtils.getAmount(transaction, true) - TransactionUtils.getAmount(updatedTransaction, true); if (updatedTransaction?.currency === iouReport?.currency && updatedTransaction?.modifiedAmount && diff !== 0) { - if (ReportUtils.isExpenseReport(iouReport) && updatedMoneyRequestReport.total) { + if (ReportUtils.isExpenseReport(iouReport) && typeof updatedMoneyRequestReport.total === 'number') { updatedMoneyRequestReport.total += diff; } else { updatedMoneyRequestReport = iouReport @@ -2315,7 +2310,7 @@ function editRegularMoneyRequest(transactionID: string, transactionThreadReportI updatedMoneyRequestReport.lastMessageHtml = lastMessage[0].html; // Update the last message of the chat report - const hasNonReimbursableTransactions = ReportUtils.hasNonReimbursableTransactions(iouReport?.reportID); // There was an error before - wrong value was sent + const hasNonReimbursableTransactions = ReportUtils.hasNonReimbursableTransactions(iouReport?.reportID); const messageText = Localize.translateLocal(hasNonReimbursableTransactions ? 'iou.payerSpentAmount' : 'iou.payerOwesAmount', { payer: ReportUtils.getPersonalDetailsForAccountID(updatedMoneyRequestReport.managerID ?? -1).login ?? '', amount: CurrencyUtils.convertToDisplayString(updatedMoneyRequestReport.total, updatedMoneyRequestReport.currency), @@ -2386,6 +2381,7 @@ function editRegularMoneyRequest(transactionID: string, transactionThreadReportI // Update recently used categories if the category is changed if ('category' in transactionChanges) { + // TODO: Remove assertion once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories( iouReport?.policyID, transactionChanges.category, @@ -2401,6 +2397,7 @@ function editRegularMoneyRequest(transactionID: string, transactionThreadReportI // Update recently used categories if the tag is changed if ('tag' in transactionChanges) { + // TODO: Remove assertion once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. const optimisticPolicyRecentlyUsedTags = Policy.buildOptimisticPolicyRecentlyUsedTags(iouReport?.policyID, transactionChanges.tag) as OptimisticPolicyRecentlyUsedTags; if (!isEmptyObject(optimisticPolicyRecentlyUsedTags)) { optimisticData.push({ @@ -2593,7 +2590,7 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor if (iouReport && ReportUtils.isExpenseReport(iouReport)) { updatedIOUReport = {...iouReport}; - if (updatedIOUReport.total) { + if (typeof updatedIOUReport.total === 'number') { // Because of the Expense reports are stored as negative values, we add the total from the amount updatedIOUReport.total += TransactionUtils.getAmount(transaction, true); } diff --git a/src/types/onyx/TransactionViolation.ts b/src/types/onyx/TransactionViolation.ts index dd7a9ef65746..6b5882cfc73d 100644 --- a/src/types/onyx/TransactionViolation.ts +++ b/src/types/onyx/TransactionViolation.ts @@ -31,4 +31,6 @@ type TransactionViolation = { }; }; -export type {TransactionViolation, ViolationName}; +type TransactionViolations = TransactionViolation[]; + +export type {TransactionViolation, TransactionViolations, ViolationName}; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 262312c563f7..b17dd5b7bafc 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -56,7 +56,7 @@ import type Session from './Session'; import type Task from './Task'; import type Transaction from './Transaction'; import type {TransactionDraft} from './Transaction'; -import type {TransactionViolation, ViolationName} from './TransactionViolation'; +import type {TransactionViolation, TransactionViolations, ViolationName} from './TransactionViolation'; import type User from './User'; import type UserLocation from './UserLocation'; import type UserWallet from './UserWallet'; @@ -127,6 +127,7 @@ export type { Transaction, TransactionDraft, TransactionViolation, + TransactionViolations, User, UserLocation, UserWallet, From a8a7d64203bc2bef070f5b9a6d4be5d8b40d6c1b Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 9 Jan 2024 09:16:27 +0100 Subject: [PATCH 061/660] TS updates after merging main --- src/libs/actions/IOU.ts | 42 +++++++++++------------------------------ 1 file changed, 11 insertions(+), 31 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 796fe99635d7..0b4f592ccf80 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/naming-convention */ import type {StackScreenProps} from '@react-navigation/stack'; import {format} from 'date-fns'; import Str from 'expensify-common/lib/str'; @@ -1143,55 +1144,34 @@ function updateMoneyRequestDate(transactionID: string, transactionThreadReportID API.write('UpdateMoneyRequestDate', params, onyxData); } -/** - * Updates the billable field of a money request - * - * @param {String} transactionID - * @param {Number} transactionThreadReportID - * @param {String} val - */ -function updateMoneyRequestBillable(transactionID, transactionThreadReportID, val) { - const transactionChanges = { +/** Updates the billable field of a money request */ +function updateMoneyRequestBillable(transactionID: string, transactionThreadReportID: string, val: boolean) { + const transactionChanges: TransactionChanges = { billable: val, }; const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, true); API.write('UpdateMoneyRequestBillable', params, onyxData); } -/** - * Updates the merchant field of a money request - * - * @param {String} transactionID - * @param {Number} transactionThreadReportID - * @param {String} val - */ -function updateMoneyRequestMerchant(transactionID, transactionThreadReportID, val) { - const transactionChanges = { +/** Updates the merchant field of a money request */ +function updateMoneyRequestMerchant(transactionID: string, transactionThreadReportID: string, val: string) { + const transactionChanges: TransactionChanges = { merchant: val, }; const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, true); API.write('UpdateMoneyRequestMerchant', params, onyxData); } -/** - * Updates the created date of a money request - * - * @param {String} transactionID - * @param {Number} transactionThreadReportID - * @param {String} tag - */ -function updateMoneyRequestTag(transactionID, transactionThreadReportID, tag) { - const transactionChanges = { +/** Updates the created date of a money request */ +function updateMoneyRequestTag(transactionID: string, transactionThreadReportID: string, tag: string) { + const transactionChanges: TransactionChanges = { tag, }; const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, true); API.write('UpdateMoneyRequestTag', params, onyxData); } -/** - * Edits an existing distance request - * - */ +/** Edits an existing distance request */ function updateDistanceRequest(transactionID: string, transactionThreadReportID: string, transactionChanges: TransactionChanges) { const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, false); API.write('UpdateDistanceRequest', params, onyxData); From fb6a0ad03120b61a422cba0748a972b42335bbed Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 9 Jan 2024 10:34:17 +0100 Subject: [PATCH 062/660] Update updateIOUOwnerAndTotal function typing to get rid of assertions --- src/libs/IOUUtils.ts | 5 +++-- src/libs/actions/IOU.ts | 25 +++++++++++++------------ 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/libs/IOUUtils.ts b/src/libs/IOUUtils.ts index 19a9e558bf46..095c715dfeae 100644 --- a/src/libs/IOUUtils.ts +++ b/src/libs/IOUUtils.ts @@ -1,3 +1,4 @@ +import type {OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; @@ -73,13 +74,13 @@ function calculateAmount(numberOfParticipants: number, total: number, currency: * * @param isDeleting - whether the user is deleting the request */ -function updateIOUOwnerAndTotal(iouReport: Report, actorAccountID: number, amount: number, currency: string, isDeleting = false): Report { +function updateIOUOwnerAndTotal>(iouReport: TReport, actorAccountID: number, amount: number, currency: string, isDeleting = false): TReport { if (currency !== iouReport?.currency) { return iouReport; } // Make a copy so we don't mutate the original object - const iouReportUpdate: Report = {...iouReport}; + const iouReportUpdate = {...iouReport}; if (iouReportUpdate.total) { if (actorAccountID === iouReport.ownerAccountID) { diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 0b4f592ccf80..e75c8f000f79 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -60,6 +60,8 @@ type IOURequestType = ValueOf; type PaymentMethodType = DeepValueOf; +type OneOnOneIOUReport = OnyxTypes.Report | undefined | null; + type MoneyRequestInformation = { payerAccountID: number; payerEmail: string; @@ -1509,7 +1511,7 @@ function createSplitsAndOnyxData( // STEP 2: Get existing IOU/Expense report and update its total OR build a new optimistic one // For Control policy expense chats, if the report is already approved, create a new expense report - let oneOnOneIOUReport = oneOnOneChatReport.iouReportID ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oneOnOneChatReport.iouReportID}`] ?? undefined : undefined; + let oneOnOneIOUReport: OneOnOneIOUReport = oneOnOneChatReport.iouReportID ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oneOnOneChatReport.iouReportID}`] ?? undefined : undefined; const shouldCreateNewOneOnOneIOUReport = oneOnOneIOUReport === undefined || (isOwnPolicyExpenseChat && ReportUtils.isControlPolicyExpenseReport(oneOnOneIOUReport) && ReportUtils.isReportApproved(oneOnOneIOUReport)); @@ -1523,8 +1525,7 @@ function createSplitsAndOnyxData( oneOnOneIOUReport.total -= splitAmount; } } else { - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - oneOnOneIOUReport = IOUUtils.updateIOUOwnerAndTotal(oneOnOneIOUReport as OnyxTypes.Report, currentUserAccountID, splitAmount, currency); + oneOnOneIOUReport = IOUUtils.updateIOUOwnerAndTotal(oneOnOneIOUReport ?? null, currentUserAccountID, splitAmount, currency); } // STEP 3: Build optimistic transaction @@ -2145,7 +2146,7 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA oneOnOneChatReport = existingChatReport ?? ReportUtils.buildOptimisticChatReport(participant.accountID ? [participant.accountID] : []); } - let oneOnOneIOUReport = oneOnOneChatReport?.iouReportID ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oneOnOneChatReport.iouReportID}`] ?? undefined : undefined; + let oneOnOneIOUReport: OneOnOneIOUReport = oneOnOneChatReport?.iouReportID ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oneOnOneChatReport.iouReportID}`] ?? undefined : undefined; const shouldCreateNewOneOnOneIOUReport = oneOnOneIOUReport === undefined || (isPolicyExpenseChat && ReportUtils.isControlPolicyExpenseReport(oneOnOneIOUReport) && ReportUtils.isReportApproved(oneOnOneIOUReport)); @@ -2159,8 +2160,7 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA oneOnOneIOUReport.total -= splitAmount; } } else { - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - oneOnOneIOUReport = IOUUtils.updateIOUOwnerAndTotal(oneOnOneIOUReport as OnyxTypes.Report, sessionAccountID, splitAmount, currency ?? ''); + oneOnOneIOUReport = IOUUtils.updateIOUOwnerAndTotal(oneOnOneIOUReport ?? null, sessionAccountID, splitAmount, currency ?? ''); } const oneOnOneTransaction = TransactionUtils.buildOptimisticTransaction( @@ -2556,7 +2556,7 @@ function updateMoneyRequestAmountAndCurrency(transactionID: string, transactionT function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false) { // STEP 1: Get all collections we're updating - const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${(reportAction.originalMessage as IOUMessage).IOUReportID}`]; + const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${(reportAction.originalMessage as IOUMessage).IOUReportID}`] ?? null; const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReport?.chatReportID}`]; const reportPreviewAction = ReportActionsUtils.getReportPreviewAction(iouReport?.chatReportID ?? '', iouReport?.reportID ?? ''); const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; @@ -2612,8 +2612,7 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor } } else { updatedIOUReport = IOUUtils.updateIOUOwnerAndTotal( - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - iouReport as OnyxTypes.Report, + iouReport, reportAction.actorAccountID ?? -1, TransactionUtils.getAmount(transaction, false), TransactionUtils.getCurrency(transaction), @@ -2621,14 +2620,16 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor ); } - updatedIOUReport.lastMessageText = iouReportLastMessageText; - updatedIOUReport.lastVisibleActionCreated = lastVisibleAction?.created; + if (updatedIOUReport) { + updatedIOUReport.lastMessageText = iouReportLastMessageText; + updatedIOUReport.lastVisibleActionCreated = lastVisibleAction?.created; + } updatedReportPreviewAction = reportPreviewAction ? {...reportPreviewAction} : null; const hasNonReimbursableTransactions = ReportUtils.hasNonReimbursableTransactions(iouReport?.reportID); const messageText = Localize.translateLocal(hasNonReimbursableTransactions ? 'iou.payerSpentAmount' : 'iou.payerOwesAmount', { payer: ReportUtils.getPersonalDetailsForAccountID(updatedIOUReport?.managerID ?? -1).login ?? '', - amount: CurrencyUtils.convertToDisplayString(updatedIOUReport.total, updatedIOUReport.currency), + amount: CurrencyUtils.convertToDisplayString(updatedIOUReport?.total, updatedIOUReport?.currency), }); if (updatedReportPreviewAction?.message?.[0]) { From 659f2363458fafcc744cf3b3bcbd471bcc03080b Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 9 Jan 2024 11:49:47 +0100 Subject: [PATCH 063/660] Get rid of nullable assertions in onyx update data --- src/libs/actions/IOU.ts | 74 +++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 43 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index e75c8f000f79..2b275037e7e0 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -35,7 +35,6 @@ import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; -import type {IOUMessage} from '@src/types/onyx/OriginalMessage'; import type ReportAction from '@src/types/onyx/ReportAction'; import type {OnyxData} from '@src/types/onyx/Request'; import type {Comment, Receipt, TaxRate, TransactionChanges, WaypointCollection} from '@src/types/onyx/Transaction'; @@ -979,7 +978,7 @@ function getUpdateMoneyRequestParams( } const dataToIncludeInParams: Partial | undefined = onlyIncludeChangedFields - ? (Object.fromEntries(Object.entries(transactionDetails ?? {}).filter(([key]) => Object.keys(transactionChanges).includes(key))) as Partial) + ? Object.fromEntries(Object.entries(transactionDetails ?? {}).filter(([key]) => Object.keys(transactionChanges).includes(key))) : transactionDetails; const params: UpdateMoneyRequestParams = { @@ -1121,13 +1120,14 @@ function getUpdateMoneyRequestParams( }, }); - // Reset the iouReport to it's original state - failureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - value: iouReport as OnyxTypes.Report, - }); + if (iouReport) { + // Reset the iouReport to it's original state + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, + value: iouReport, + }); + } return { params, @@ -1515,7 +1515,7 @@ function createSplitsAndOnyxData( const shouldCreateNewOneOnOneIOUReport = oneOnOneIOUReport === undefined || (isOwnPolicyExpenseChat && ReportUtils.isControlPolicyExpenseReport(oneOnOneIOUReport) && ReportUtils.isReportApproved(oneOnOneIOUReport)); - if (shouldCreateNewOneOnOneIOUReport) { + if (oneOnOneIOUReport === undefined || shouldCreateNewOneOnOneIOUReport) { oneOnOneIOUReport = isOwnPolicyExpenseChat ? ReportUtils.buildOptimisticExpenseReport(oneOnOneChatReport.reportID, oneOnOneChatReport.policyID ?? '', currentUserAccountID, splitAmount, currency) : ReportUtils.buildOptimisticIOUReport(currentUserAccountID, accountID, splitAmount, oneOnOneChatReport.reportID, currency); @@ -1530,8 +1530,7 @@ function createSplitsAndOnyxData( // STEP 3: Build optimistic transaction const oneOnOneTransaction = TransactionUtils.buildOptimisticTransaction( - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - ReportUtils.isExpenseReport(oneOnOneIOUReport as OnyxTypes.Report) ? -splitAmount : splitAmount, + ReportUtils.isExpenseReport(oneOnOneIOUReport ?? null) ? -splitAmount : splitAmount, currency, oneOnOneIOUReport?.reportID ?? '', comment, @@ -1587,11 +1586,9 @@ function createSplitsAndOnyxData( let oneOnOneReportPreviewAction = ReportActionsUtils.getReportPreviewAction(oneOnOneChatReport.reportID, oneOnOneIOUReport?.reportID ?? ''); if (oneOnOneReportPreviewAction) { - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - oneOnOneReportPreviewAction = ReportUtils.updateReportPreview(oneOnOneIOUReport as OnyxTypes.Report, oneOnOneReportPreviewAction); + oneOnOneReportPreviewAction = ReportUtils.updateReportPreview(oneOnOneIOUReport, oneOnOneReportPreviewAction); } else { - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - oneOnOneReportPreviewAction = ReportUtils.buildOptimisticReportPreview(oneOnOneChatReport, oneOnOneIOUReport as OnyxTypes.Report); + oneOnOneReportPreviewAction = ReportUtils.buildOptimisticReportPreview(oneOnOneChatReport, oneOnOneIOUReport); } // Add category to optimistic policy recently used categories when a participant is a workspace @@ -1609,8 +1606,7 @@ function createSplitsAndOnyxData( // STEP 5: Build Onyx Data const [oneOnOneOptimisticData, oneOnOneSuccessData, oneOnOneFailureData] = buildOnyxDataForMoneyRequest( oneOnOneChatReport, - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - oneOnOneIOUReport as OnyxTypes.Report, + oneOnOneIOUReport, oneOnOneTransaction, oneOnOneCreatedActionForChat, oneOnOneCreatedActionForIOU, @@ -2150,7 +2146,7 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA const shouldCreateNewOneOnOneIOUReport = oneOnOneIOUReport === undefined || (isPolicyExpenseChat && ReportUtils.isControlPolicyExpenseReport(oneOnOneIOUReport) && ReportUtils.isReportApproved(oneOnOneIOUReport)); - if (shouldCreateNewOneOnOneIOUReport) { + if (oneOnOneIOUReport === undefined || shouldCreateNewOneOnOneIOUReport) { oneOnOneIOUReport = isPolicyExpenseChat ? ReportUtils.buildOptimisticExpenseReport(oneOnOneChatReport?.reportID ?? '', participant.policyID ?? '', sessionAccountID, splitAmount, currency ?? '') : ReportUtils.buildOptimisticIOUReport(sessionAccountID, participant.accountID ?? -1, splitAmount, oneOnOneChatReport?.reportID ?? '', currency ?? ''); @@ -2199,8 +2195,7 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA const [oneOnOneOptimisticData, oneOnOneSuccessData, oneOnOneFailureData] = buildOnyxDataForMoneyRequest( // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style oneOnOneChatReport as OnyxTypes.Report, - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - oneOnOneIOUReport as OnyxTypes.Report, + oneOnOneIOUReport, oneOnOneTransaction, oneOnOneCreatedActionForChat, oneOnOneCreatedActionForIOU, @@ -2489,8 +2484,7 @@ function editRegularMoneyRequest(transactionID: string, transactionThreadReportI { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.chatReportID}`, - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - value: chatReport as OnyxTypes.Report, + value: chatReport ?? {}, }, { onyxMethod: Onyx.METHOD.MERGE, @@ -2556,7 +2550,8 @@ function updateMoneyRequestAmountAndCurrency(transactionID: string, transactionT function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.ReportAction, isSingleTransactionView = false) { // STEP 1: Get all collections we're updating - const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${(reportAction.originalMessage as IOUMessage).IOUReportID}`] ?? null; + const iouReportID = reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? reportAction.originalMessage.IOUReportID : ''; + const iouReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`] ?? null; const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${iouReport?.chatReportID}`]; const reportPreviewAction = ReportActionsUtils.getReportPreviewAction(iouReport?.chatReportID ?? '', iouReport?.reportID ?? ''); const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; @@ -2564,7 +2559,7 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor const transactionThreadID = reportAction.childReportID; let transactionThread = null; if (transactionThreadID) { - transactionThread = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`]; + transactionThread = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`] ?? null; } // STEP 2: Decide if we need to: @@ -2689,14 +2684,12 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor ? { onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - value: updatedIOUReport as OnyxTypes.Report, + value: updatedIOUReport, } : { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - value: updatedIOUReport as OnyxTypes.Report, + value: updatedIOUReport ?? {}, }, { onyxMethod: Onyx.METHOD.MERGE, @@ -2760,8 +2753,7 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor failureData.push({ onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`, - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - value: transactionThread as OnyxTypes.Report, + value: transactionThread, }); } @@ -2780,14 +2772,12 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor ? { onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - value: iouReport as OnyxTypes.Report, + value: iouReport, } : { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - value: iouReport as OnyxTypes.Report, + value: iouReport ?? {}, }, { onyxMethod: Onyx.METHOD.MERGE, @@ -2801,12 +2791,11 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor }, ); - if (shouldDeleteIOUReport) { + if (chatReport && shouldDeleteIOUReport) { failureData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport?.reportID}`, - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - value: chatReport as OnyxTypes.Report, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, + value: chatReport, }); } if (!shouldDeleteIOUReport && updatedReportPreviewAction?.childMoneyRequestCount === 0) { @@ -3441,9 +3430,8 @@ function payMoneyRequest(paymentType: PaymentMethodType, chatReport: OnyxTypes.R } function detachReceipt(transactionID: string) { - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`] as OnyxTypes.Transaction; - const newTransaction = {...transaction, filename: '', receipt: {}}; + const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; + const newTransaction = transaction ? {...transaction, filename: '', receipt: {}} : null; const optimisticData: OnyxUpdate[] = [ { @@ -3457,7 +3445,7 @@ function detachReceipt(transactionID: string) { { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, - value: transaction, + value: transaction ?? {}, }, ]; From f4aff81e8292c86d1a2ad4325fb9bfdde9082079 Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Tue, 9 Jan 2024 11:50:14 +0100 Subject: [PATCH 064/660] Use transaction.isLoading to identify pending Distance requests --- src/components/DistanceEReceipt.js | 4 +--- .../ReportActionItem/MoneyRequestPreview.js | 17 ++++++++--------- .../ReportActionItem/MoneyRequestView.js | 9 ++------- .../ReportActionItem/ReportPreview.js | 10 ++-------- src/libs/ReportUtils.ts | 17 +++++++++++++++-- src/libs/TransactionUtils.ts | 18 +++++++++++++++++- src/types/onyx/Transaction.ts | 2 ++ 7 files changed, 47 insertions(+), 30 deletions(-) diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index 0241eea44063..f566fb77b912 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -7,7 +7,6 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as CurrencyUtils from '@libs/CurrencyUtils'; import * as ReceiptUtils from '@libs/ReceiptUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; @@ -35,8 +34,7 @@ function DistanceEReceipt({transaction}) { const {translate} = useLocalize(); const {isOffline} = useNetwork(); const {thumbnail} = TransactionUtils.hasReceipt(transaction) ? ReceiptUtils.getThumbnailAndImageURIs(transaction) : {}; - const {amount: transactionAmount, currency: transactionCurrency, merchant: transactionMerchant, created: transactionDate} = ReportUtils.getTransactionDetails(transaction); - const formattedTransactionAmount = transactionAmount ? CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency) : translate('common.tbd'); + const {formattedAmount: formattedTransactionAmount, merchant: transactionMerchant, created: transactionDate} = ReportUtils.getTransactionDetails(transaction); const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); const waypoints = lodashGet(transaction, 'comment.waypoints', {}); const sortedWaypoints = useMemo( diff --git a/src/components/ReportActionItem/MoneyRequestPreview.js b/src/components/ReportActionItem/MoneyRequestPreview.js index c052a885245f..f66b5b90efbd 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview.js +++ b/src/components/ReportActionItem/MoneyRequestPreview.js @@ -152,7 +152,13 @@ function MoneyRequestPreview(props) { // Pay button should only be visible to the manager of the report. const isCurrentUserManager = managerID === sessionAccountID; - const {amount: requestAmount, currency: requestCurrency, comment: requestComment, merchant} = ReportUtils.getTransactionDetails(props.transaction); + const { + amount: requestAmount, + formattedAmount: formattedRequestAmount, + currency: requestCurrency, + comment: requestComment, + merchant, + } = ReportUtils.getTransactionDetails(props.transaction); const description = truncate(requestComment, {length: CONST.REQUEST_PREVIEW.MAX_LENGTH}); const requestMerchant = truncate(merchant, {length: CONST.REQUEST_PREVIEW.MAX_LENGTH}); const hasReceipt = TransactionUtils.hasReceipt(props.transaction); @@ -166,13 +172,10 @@ function MoneyRequestPreview(props) { // Show the merchant for IOUs and expenses only if they are custom or not related to scanning smartscan const shouldShowMerchant = !_.isEmpty(requestMerchant) && requestMerchant !== CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT && requestMerchant !== CONST.TRANSACTION.DEFAULT_MERCHANT; const shouldShowDescription = !_.isEmpty(description) && !shouldShowMerchant && !isScanning; - const hasPendingWaypoints = lodashGet(props.transaction, 'pendingFields.waypoints', null); let merchantOrDescription = requestMerchant; if (!shouldShowMerchant) { merchantOrDescription = description || ''; - } else if (hasPendingWaypoints) { - merchantOrDescription = requestMerchant.replace(CONST.REGEX.FIRST_SPACE, translate('common.tbd')); } const receiptImages = hasReceipt ? [ReceiptUtils.getThumbnailAndImageURIs(props.transaction)] : []; @@ -221,10 +224,6 @@ function MoneyRequestPreview(props) { }; const getDisplayAmountText = () => { - if (isDistanceRequest) { - return requestAmount && !hasPendingWaypoints ? CurrencyUtils.convertToDisplayString(requestAmount, props.transaction.currency) : translate('common.tbd'); - } - if (isScanning) { return translate('iou.receiptScanning'); } @@ -233,7 +232,7 @@ function MoneyRequestPreview(props) { return Localize.translateLocal('iou.receiptMissingDetails'); } - return CurrencyUtils.convertToDisplayString(requestAmount, requestCurrency); + return formattedRequestAmount; }; const getDisplayDeleteAmountText = () => { diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index 37ff163f23c8..42de7b461f9d 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -128,7 +128,7 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate const { created: transactionDate, amount: transactionAmount, - currency: transactionCurrency, + formattedAmount: formattedTransactionAmount, comment: transactionDescription, merchant: transactionMerchant, billable: transactionBillable, @@ -140,11 +140,6 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate } = ReportUtils.getTransactionDetails(transaction); const isEmptyMerchant = transactionMerchant === '' || transactionMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; const isDistanceRequest = TransactionUtils.isDistanceRequest(transaction); - let formattedTransactionAmount = transactionAmount ? CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency) : ''; - const hasPendingWaypoints = lodashGet(transaction, 'pendingFields.waypoints', null); - if (isDistanceRequest && (!formattedTransactionAmount || hasPendingWaypoints)) { - formattedTransactionAmount = translate('common.tbd'); - } const formattedOriginalAmount = transactionOriginalAmount && transactionOriginalCurrency && CurrencyUtils.convertToDisplayString(transactionOriginalAmount, transactionOriginalCurrency); const isCardTransaction = TransactionUtils.isCardTransaction(transaction); const cardProgramName = isCardTransaction ? CardUtils.getCardDescription(transactionCardID) : ''; @@ -274,7 +269,7 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate ReceiptUtils.getThumbnailAndImageURIs(transaction)); let formattedMerchant = numberOfRequests === 1 && hasReceipts ? TransactionUtils.getMerchant(transactionsWithReceipts[0]) : null; - const hasPendingWaypoints = formattedMerchant && hasOnlyDistanceRequests && _.every(transactionsWithReceipts, (transaction) => lodashGet(transaction, 'pendingFields.waypoints', null)); - if (hasPendingWaypoints) { - formattedMerchant = formattedMerchant.replace(CONST.REGEX.FIRST_SPACE, props.translate('common.tbd')); - } + const hasOnlyLoadingDistanceRequests = hasOnlyDistanceRequests && _.every(transactionsWithReceipts, (transaction) => TransactionUtils.isLoadingDistanceRequest(transaction)); const previewSubtitle = formattedMerchant || props.translate('iou.requestCount', { @@ -178,7 +175,7 @@ function ReportPreview(props) { ); const getDisplayAmount = () => { - if (hasPendingWaypoints) { + if (hasOnlyLoadingDistanceRequests) { return props.translate('common.tbd'); } if (totalDisplaySpend) { @@ -187,9 +184,6 @@ function ReportPreview(props) { if (isScanning) { return props.translate('iou.receiptScanning'); } - if (hasOnlyDistanceRequests) { - return props.translate('common.tbd'); - } // If iouReport is not available, get amount from the action message (Ex: "Domain20821's Workspace owes $33.00" or "paid ₫60" or "paid -₫60 elsewhere") let displayAmount = ''; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 0d7658adf180..dad7314168ae 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -292,6 +292,7 @@ type TransactionDetails = cardID: number; originalAmount: number; originalCurrency: string; + formattedAmount: string; } | undefined; @@ -1835,11 +1836,23 @@ function getTransactionDetails(transaction: OnyxEntry, createdDateF if (!transaction) { return; } + const report = getReport(transaction?.reportID); + const amount = TransactionUtils.getAmount(transaction, isNotEmptyObject(report) && isExpenseReport(report)); + const currency = TransactionUtils.getCurrency(transaction); + + let formattedAmount; + if (TransactionUtils.isLoadingDistanceRequest(transaction)) { + formattedAmount = Localize.translateLocal('common.tbd'); + } else { + formattedAmount = amount ? CurrencyUtils.convertToDisplayString(amount, currency) : ''; + } + return { created: TransactionUtils.getCreated(transaction, createdDateFormat), - amount: TransactionUtils.getAmount(transaction, isNotEmptyObject(report) && isExpenseReport(report)), - currency: TransactionUtils.getCurrency(transaction), + amount, + currency, + formattedAmount, comment: TransactionUtils.getDescription(transaction), merchant: TransactionUtils.getMerchant(transaction), waypoints: TransactionUtils.getWaypoints(transaction), diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index c34a6753c1d5..7badbc524085 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -2,6 +2,7 @@ import lodashHas from 'lodash/has'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; +import * as Localize from '@libs/Localize'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {RecentWaypoint, Report, ReportAction, Transaction} from '@src/types/onyx'; @@ -49,6 +50,15 @@ function isDistanceRequest(transaction: Transaction): boolean { return type === CONST.TRANSACTION.TYPE.CUSTOM_UNIT && customUnitName === CONST.CUSTOM_UNITS.NAME_DISTANCE; } +function isLoadingDistanceRequest(transaction: OnyxEntry): boolean { + if (!transaction) { + return false; + } + + const amount = getAmount(transaction, false); + return isDistanceRequest(transaction) && (!!transaction?.isLoading || amount === 0); +} + function isScanRequest(transaction: Transaction): boolean { // This is used during the request creation flow before the transaction has been saved to the server if (lodashHas(transaction, 'iouRequestType')) { @@ -311,7 +321,12 @@ function getOriginalAmount(transaction: Transaction): number { * Return the merchant field from the transaction, return the modifiedMerchant if present. */ function getMerchant(transaction: OnyxEntry): string { - return transaction?.modifiedMerchant ? transaction.modifiedMerchant : transaction?.merchant ?? ''; + if (!transaction) { + return ''; + } + + const merchant = transaction.modifiedMerchant ? transaction.modifiedMerchant : transaction.merchant ?? ''; + return isLoadingDistanceRequest(transaction) ? merchant.replace(CONST.REGEX.FIRST_SPACE, Localize.translateLocal('common.tbd')) : merchant; } function getDistance(transaction: Transaction): number { @@ -566,6 +581,7 @@ export { isReceiptBeingScanned, getValidWaypoints, isDistanceRequest, + isLoadingDistanceRequest, isExpensifyCardTransaction, isCardTransaction, isPending, diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 8b7e26280305..4b8a0a1cf9b3 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -94,6 +94,8 @@ type Transaction = { /** If the transaction was made in a foreign currency, we send the original amount and currency */ originalAmount?: number; originalCurrency?: string; + + isLoading?: boolean; }; export default Transaction; From 5944ec61bb590aa627cfc19c8ccc925fb5756b79 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 9 Jan 2024 12:24:27 +0100 Subject: [PATCH 065/660] Remove assertions, update buildOnyxDataForMoneyRequest typing --- src/ONYXKEYS.ts | 2 +- src/libs/actions/IOU.ts | 45 ++++++++++++++++++++++------------------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 781f375385f6..7b3d56ad7485 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -459,7 +459,7 @@ type OnyxValues = { [ONYXKEYS.COLLECTION.SECURITY_GROUP]: OnyxTypes.SecurityGroup; [ONYXKEYS.COLLECTION.TRANSACTION]: OnyxTypes.Transaction; [ONYXKEYS.COLLECTION.TRANSACTION_DRAFT]: OnyxTypes.TransactionDraft; - [ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT]: OnyxTypes.TransactionDraft; + [ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT]: OnyxTypes.Transaction; [ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS]: OnyxTypes.TransactionViolations; [ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS]: OnyxTypes.RecentlyUsedTags; [ONYXKEYS.COLLECTION.SELECTED_TAB]: string; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 2b275037e7e0..759311568604 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -189,7 +189,7 @@ Onyx.connect({ }, }); -let allDraftSplitTransactions: Record = {}; +let allDraftSplitTransactions: Record = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT, waitForCollectionCallback: true, @@ -355,7 +355,7 @@ function getReceiptError(receipt?: Receipt, filename?: string, isScanRequest = t /** Builds the Onyx data for a money request */ function buildOnyxDataForMoneyRequest( - chatReport: OnyxTypes.Report, + chatReport: OnyxEntry, iouReport: OnyxTypes.Report, transaction: OnyxTypes.Transaction, chatCreatedAction: OptimisticCreatedReportAction, @@ -373,8 +373,10 @@ function buildOnyxDataForMoneyRequest( hasOutstandingChildRequest = false, ): OnyxUpdate[][] { const isScanRequest = TransactionUtils.isScanRequest(transaction); - const optimisticData: OnyxUpdate[] = [ - { + const optimisticData: OnyxUpdate[] = []; + + if (chatReport) { + optimisticData.push({ // Use 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 onyxMethod: isNewChatReport ? Onyx.METHOD.SET : Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, @@ -386,7 +388,10 @@ function buildOnyxDataForMoneyRequest( hasOutstandingChildRequest, ...(isNewChatReport ? {pendingFields: {createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}} : {}), }, - }, + }); + } + + optimisticData.push( { onyxMethod: isNewIOUReport ? Onyx.METHOD.SET : Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, @@ -407,7 +412,7 @@ function buildOnyxDataForMoneyRequest( isNewChatReport ? { onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport?.reportID}`, value: { [chatCreatedAction.reportActionID]: chatCreatedAction, [reportPreviewAction.reportActionID]: reportPreviewAction, @@ -415,7 +420,7 @@ function buildOnyxDataForMoneyRequest( } : { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport?.reportID}`, value: { [reportPreviewAction.reportActionID]: reportPreviewAction, }, @@ -443,7 +448,7 @@ function buildOnyxDataForMoneyRequest( key: `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${CONST.IOU.OPTIMISTIC_TRANSACTION_ID}`, value: null, }, - ]; + ); if (optimisticPolicyRecentlyUsedCategories.length) { optimisticData.push({ @@ -474,7 +479,7 @@ function buildOnyxDataForMoneyRequest( if (isNewChatReport) { successData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport?.reportID}`, value: { pendingFields: null, errorFields: null, @@ -502,7 +507,7 @@ function buildOnyxDataForMoneyRequest( { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport?.reportID}`, value: { ...(isNewChatReport ? { @@ -540,10 +545,10 @@ function buildOnyxDataForMoneyRequest( const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport?.reportID}`, value: { - iouReportID: chatReport.iouReportID, - lastReadTime: chatReport.lastReadTime, + iouReportID: chatReport?.iouReportID, + lastReadTime: chatReport?.lastReadTime, pendingFields: null, ...(isNewChatReport ? { @@ -583,7 +588,7 @@ function buildOnyxDataForMoneyRequest( { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport?.reportID}`, value: { ...(isNewChatReport ? { @@ -2193,8 +2198,7 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA } const [oneOnOneOptimisticData, oneOnOneSuccessData, oneOnOneFailureData] = buildOnyxDataForMoneyRequest( - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - oneOnOneChatReport as OnyxTypes.Report, + oneOnOneChatReport, oneOnOneIOUReport, oneOnOneTransaction, oneOnOneCreatedActionForChat, @@ -2272,9 +2276,9 @@ function setDraftSplitTransaction(transactionID: string, transactionChanges: Tra draftSplitTransaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; } - const updatedTransaction = TransactionUtils.getUpdatedTransaction(draftSplitTransaction as OnyxTypes.Transaction, transactionChanges, false, false); + const updatedTransaction = draftSplitTransaction ? TransactionUtils.getUpdatedTransaction(draftSplitTransaction, transactionChanges, false, false) : null; - Onyx.merge(`${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`, updatedTransaction); + Onyx.merge(`${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`, updatedTransaction ?? {}); } function editRegularMoneyRequest(transactionID: string, transactionThreadReportID: string, transactionChanges: TransactionChanges) { @@ -2287,8 +2291,7 @@ function editRegularMoneyRequest(transactionID: string, transactionThreadReportI // STEP 2: Build new modified expense report action. const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(transactionThread, transaction, transactionChanges, isFromExpenseReport); - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - const updatedTransaction = TransactionUtils.getUpdatedTransaction(transaction as OnyxTypes.Transaction, transactionChanges, isFromExpenseReport); + const updatedTransaction = transaction ? TransactionUtils.getUpdatedTransaction(transaction, transactionChanges, isFromExpenseReport) : null; // STEP 3: Compute the IOU total and update the report preview message so LHN amount owed is correct // Should only update if the transaction matches the currency of the report, else we wait for the update @@ -2345,7 +2348,7 @@ function editRegularMoneyRequest(transactionID: string, transactionThreadReportI { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, - value: updatedTransaction, + value: updatedTransaction ?? {}, }, { onyxMethod: Onyx.METHOD.MERGE, From c51b798f25019ddfb8e8a0bf95a59a2c303202a3 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 9 Jan 2024 14:52:33 +0100 Subject: [PATCH 066/660] Add Split type --- src/libs/actions/IOU.ts | 12 ++++++------ src/types/onyx/IOU.ts | 31 ++++++++++++++----------------- src/types/onyx/Transaction.ts | 4 ++-- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 759311568604..feb27d7987cd 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -34,7 +34,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; -import type {Participant} from '@src/types/onyx/IOU'; +import type {Participant, Split} from '@src/types/onyx/IOU'; import type ReportAction from '@src/types/onyx/ReportAction'; import type {OnyxData} from '@src/types/onyx/Request'; import type {Comment, Receipt, TaxRate, TransactionChanges, WaypointCollection} from '@src/types/onyx/Transaction'; @@ -84,7 +84,7 @@ type SplitData = { type SplitsAndOnyxData = { splitData: SplitData; - splits: Participant[]; + splits: Split[]; onyxData: OnyxData; }; @@ -1477,7 +1477,7 @@ function createSplitsAndOnyxData( // Loop through participants creating individual chats, iouReports and reportActionIDs as needed const splitAmount = IOUUtils.calculateAmount(participants.length, amount, currency, false); - const splits: Participant[] = [{email: currentUserEmailForIOUSplit, accountID: currentUserAccountID, amount: IOUUtils.calculateAmount(participants.length, amount, currency, true)}]; + const splits: Split[] = [{email: currentUserEmailForIOUSplit, accountID: currentUserAccountID, amount: IOUUtils.calculateAmount(participants.length, amount, currency, true)}]; const hasMultipleParticipants = participants.length > 1; participants.forEach((participant) => { @@ -1950,7 +1950,7 @@ function startSplitBill( ); } - const splits: Participant[] = [{email: currentUserEmailForIOUSplit, accountID: currentUserAccountID}]; + const splits: Split[] = [{email: currentUserEmailForIOUSplit, accountID: currentUserAccountID}]; participants.forEach((participant) => { // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing @@ -2109,13 +2109,13 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA }, ]; - const splitParticipants = updatedTransaction.comment.splits ?? []; + const splitParticipants: Split[] = updatedTransaction.comment.splits ?? []; const {modifiedAmount: amount, modifiedCurrency: currency} = updatedTransaction; // Exclude the current user when calculating the split amount, `calculateAmount` takes it into account const splitAmount = IOUUtils.calculateAmount(splitParticipants.length - 1, amount ?? 0, currency ?? '', false); - const splits: Participant[] = [{email: currentUserEmailForIOUSplit}]; + const splits: Split[] = [{email: currentUserEmailForIOUSplit}]; splitParticipants.forEach((participant) => { // Skip creating the transaction for the current user if (participant.email === currentUserEmailForIOUSplit) { diff --git a/src/types/onyx/IOU.ts b/src/types/onyx/IOU.ts index 59709de4638e..9aa4a024a3c4 100644 --- a/src/types/onyx/IOU.ts +++ b/src/types/onyx/IOU.ts @@ -1,31 +1,28 @@ import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; -import type {Icon} from './OnyxCommon'; type Participant = { accountID?: number; login?: string; - email?: string; + displayName?: string; isPolicyExpenseChat?: boolean; - chatReportID?: string; - iouReportID?: string; - transactionID?: string; - reportActionID?: string; isOwnPolicyExpenseChat?: boolean; chatType?: ValueOf; - amount?: number; - selected?: boolean; reportID?: string; policyID?: string; - displayName?: string; - alternateText?: string; - firstName?: string; - icons?: Icon[]; - keyForList?: string; - lastName?: string; - phoneNumber?: string; - searchText?: string; + selected?: boolean; text?: string; +}; + +type Split = { + email?: string; + amount?: number; + accountID?: number; + chatReportID?: string; + iouReportID?: string; + reportActionID?: string; + transactionID?: string; + policyID?: string; createdChatReportActionID?: string; createdIOUReportActionID?: string; reportPreviewReportActionID?: string; @@ -50,4 +47,4 @@ type IOU = { }; export default IOU; -export type {Participant}; +export type {Participant, Split}; diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index b093234368b5..3498fa375c67 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -1,7 +1,7 @@ import type {ImageSourcePropType} from 'react-native'; import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; -import type {Participant} from './IOU'; +import type {Participant, Split} from './IOU'; import type * as OnyxCommon from './OnyxCommon'; import type RecentWaypoint from './RecentWaypoint'; @@ -29,7 +29,7 @@ type Comment = { customUnit?: Record; source?: string; originalTransactionID?: string; - splits?: Participant[]; + splits?: Split[]; }; type GeometryType = 'LineString'; From b30a5e7eb90279177b4c51febdc1cfa5a7c4db36 Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Tue, 9 Jan 2024 15:15:38 +0100 Subject: [PATCH 067/660] Use transaction.isLoading to identify pending Distance requests --- .../ReportActionItem/ReportPreview.js | 2 +- src/libs/TransactionUtils.ts | 25 +++++++++++-------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js index e0346e3c6bcb..d50811c15fbe 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.js @@ -157,7 +157,7 @@ function ReportPreview(props) { const hasErrors = hasReceipts && hasMissingSmartscanFields; const lastThreeTransactionsWithReceipts = transactionsWithReceipts.slice(-3); const lastThreeReceipts = _.map(lastThreeTransactionsWithReceipts, (transaction) => ReceiptUtils.getThumbnailAndImageURIs(transaction)); - let formattedMerchant = numberOfRequests === 1 && hasReceipts ? TransactionUtils.getMerchant(transactionsWithReceipts[0]) : null; + const formattedMerchant = numberOfRequests === 1 && hasReceipts ? TransactionUtils.getMerchant(transactionsWithReceipts[0]) : null; const hasOnlyLoadingDistanceRequests = hasOnlyDistanceRequests && _.every(transactionsWithReceipts, (transaction) => TransactionUtils.isLoadingDistanceRequest(transaction)); const previewSubtitle = formattedMerchant || diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 7badbc524085..9ab09a1978e1 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -2,7 +2,6 @@ import lodashHas from 'lodash/has'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; -import * as Localize from '@libs/Localize'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {RecentWaypoint, Report, ReportAction, Transaction} from '@src/types/onyx'; @@ -12,6 +11,7 @@ import type {Comment, Receipt, Waypoint, WaypointCollection} from '@src/types/on import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isCorporateCard, isExpensifyCard} from './CardUtils'; import DateUtils from './DateUtils'; +import * as Localize from './Localize'; import * as NumberUtils from './NumberUtils'; type AdditionalTransactionChanges = {comment?: string; waypoints?: WaypointCollection}; @@ -50,15 +50,6 @@ function isDistanceRequest(transaction: Transaction): boolean { return type === CONST.TRANSACTION.TYPE.CUSTOM_UNIT && customUnitName === CONST.CUSTOM_UNITS.NAME_DISTANCE; } -function isLoadingDistanceRequest(transaction: OnyxEntry): boolean { - if (!transaction) { - return false; - } - - const amount = getAmount(transaction, false); - return isDistanceRequest(transaction) && (!!transaction?.isLoading || amount === 0); -} - function isScanRequest(transaction: Transaction): boolean { // This is used during the request creation flow before the transaction has been saved to the server if (lodashHas(transaction, 'iouRequestType')) { @@ -317,6 +308,20 @@ function getOriginalAmount(transaction: Transaction): number { return Math.abs(amount); } +/** + * Verify if the transaction is of Distance request and is not fully ready: + * - it has a zero amount, which means the request was created offline and expects the distance calculation from the server + * - it is in `isLoading` state, which means the waypoints were updated offline and the distance requires re-calculation + */ +function isLoadingDistanceRequest(transaction: OnyxEntry): boolean { + if (!transaction) { + return false; + } + + const amount = getAmount(transaction, false); + return isDistanceRequest(transaction) && (!!transaction?.isLoading || amount === 0); +} + /** * Return the merchant field from the transaction, return the modifiedMerchant if present. */ From a1eeacd9f9ccf6c45ae871fd102b76d59fb08f65 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 9 Jan 2024 15:17:02 +0100 Subject: [PATCH 068/660] Add type params comments --- src/types/onyx/ReportAction.ts | 1 + src/types/onyx/Transaction.ts | 86 ++++++++++++++++++++++++++++++---- 2 files changed, 77 insertions(+), 10 deletions(-) diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index 19ab889b1801..47945ffba4bb 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -140,6 +140,7 @@ type ReportActionBase = { /** Type of child report */ childType?: string; + /** The user's ID */ accountID?: number; childOldestFourEmails?: string; diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 3498fa375c67..7c40118207a0 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -71,61 +71,127 @@ type TaxRate = { }; type Transaction = { + /** The original transaction amount */ amount: number; + + /** Whether the request is billable */ billable: boolean; + + /** The category name */ category: string; + + /** The comment object on the transaction */ comment: Comment; + + /** Date that the request was created */ created: string; + + /** The original currency of the transaction */ currency: string; + + /** Any additional error message to show */ errors?: OnyxCommon.Errors; + + /** Server side errors keyed by microtime */ errorFields?: OnyxCommon.ErrorFields<'route'>; - // The name of the file used for a receipt (formerly receiptFilename) + + /** The name of the file used for a receipt (formerly receiptFilename) */ filename?: string; - // Used during the creation flow before the transaction is saved to the server + + /** Used during the creation flow before the transaction is saved to the server */ iouRequestType?: ValueOf; + + /** The original merchant name */ merchant: string; + + /** The edited transaction amount */ modifiedAmount?: number; + + /** The edited transaction date */ modifiedCreated?: string; + + /** The edited currency of the transaction */ modifiedCurrency?: string; + + /** The edited merchant name */ modifiedMerchant?: string; + + /** The edited waypoints for the distance request */ modifiedWaypoints?: WaypointCollection; - // Used during the creation flow before the transaction is saved to the server and helps dictate where the user is navigated to when pressing the back button on the confirmation step + + /** + * Used during the creation flow before the transaction is saved to the server and helps dictate where + * the user is navigated to when pressing the back button on the confirmation step + */ participantsAutoAssigned?: boolean; + + /** Selected participants */ participants?: Participant[]; + + /** The type of action that's pending */ pendingAction: OnyxCommon.PendingAction; + + /** The receipt object associated with the transaction */ receipt?: Receipt; + + /** The iouReportID associated with the transaction */ reportID: string; + + /** Existing routes */ routes?: Routes; + + /** The transaction id */ transactionID: string; + + /** The transaction tag */ tag: string; + + /** Whether the transaction was created globally */ + isFromGlobalCreate?: boolean; + + /** The transaction tax rate */ + taxRate?: TaxRate; + + /** Tax amount */ + taxAmount?: number; + + /** Pending fields for the transaction */ pendingFields?: Partial<{[K in keyof Transaction | keyof Comment]: ValueOf}>; /** Card Transactions */ + /** The parent transaction id */ parentTransactionID?: string; + + /** Whether the expense is reimbursable or not */ reimbursable?: boolean; + /** The CC for this transaction */ cardID?: number; + /** If the transaction is pending or posted */ status?: ValueOf; + /** If an EReceipt should be generated for this transaction */ hasEReceipt?: boolean; + /** The MCC Group for this transaction */ mccGroup?: ValueOf; + + /** Modified MCC Group */ modifiedMCCGroup?: ValueOf; + /** If the transaction was made in a foreign currency, we send the original amount and currency */ originalAmount?: number; + + /** The original currency of the transaction */ originalCurrency?: string; + + /** Indicates transaction loading */ isLoading?: boolean; }; -type TransactionDraft = Partial & { - isFromGlobalCreate?: boolean; - taxRate?: TaxRate; - - /** Calculated tax amount based on selected tax rate */ - taxAmount?: number; -}; +type TransactionDraft = Partial; type AdditionalTransactionChanges = { comment?: string; From 992e61d8d25492c531fe58ac3b187470661479c1 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 9 Jan 2024 15:50:54 +0100 Subject: [PATCH 069/660] ReportUtils file updates --- src/libs/ReportUtils.ts | 27 +++++++-------------------- src/libs/actions/IOU.ts | 6 +++--- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 7f1cdb3d2893..2a1cbfc21550 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -400,7 +400,7 @@ Onyx.connect({ }, }); -function getChatType(report: OnyxEntry | Participant | EmptyObject): ValueOf | undefined { +function getChatType(report: OnyxEntry | Participant): ValueOf | undefined { return report?.chatType; } @@ -673,7 +673,7 @@ function isUserCreatedPolicyRoom(report: OnyxEntry): boolean { /** * Whether the provided report is a Policy Expense chat. */ -function isPolicyExpenseChat(report: OnyxEntry | Participant | EmptyObject): boolean { +function isPolicyExpenseChat(report: OnyxEntry | Participant): boolean { return getChatType(report) === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT || (report?.isPolicyExpenseChat ?? false); } @@ -2670,7 +2670,7 @@ function buildOptimisticIOUReportAction( comment: string, participants: Participant[], transactionID: string, - paymentType: DeepValueOf | undefined, + paymentType?: DeepValueOf, iouReportID = '', isSettlingUp = false, isSendMoneyFlow = false, @@ -2858,13 +2858,7 @@ function buildOptimisticSubmittedReportAction(amount: number, currency: string, * @param [comment] - User comment for the IOU. * @param [transaction] - optimistic first transaction of preview */ -function buildOptimisticReportPreview( - chatReport: OnyxEntry, - iouReport: OnyxEntry, - comment = '', - transaction: OnyxEntry = null, - childReportID?: string, -): ReportAction { +function buildOptimisticReportPreview(chatReport: OnyxEntry, iouReport: Report, comment = '', transaction: OnyxEntry = null, childReportID?: string): ReportAction { const hasReceipt = TransactionUtils.hasReceipt(transaction); const isReceiptBeingScanned = hasReceipt && TransactionUtils.isReceiptBeingScanned(transaction); const message = getReportPreviewMessage(iouReport); @@ -2875,7 +2869,7 @@ function buildOptimisticReportPreview( actionName: CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, originalMessage: { - linkedReportID: iouReport?.reportID ?? '', + linkedReportID: iouReport?.reportID, }, message: [ { @@ -2944,13 +2938,7 @@ function buildOptimisticModifiedExpenseReportAction( * @param [transaction] - optimistic newest transaction of a report preview * */ -function updateReportPreview( - iouReport: OnyxEntry, - reportPreviewAction: OnyxEntry, - isPayRequest = false, - comment = '', - transaction: OnyxEntry = null, -): ReportAction { +function updateReportPreview(iouReport: OnyxEntry, reportPreviewAction: ReportAction, isPayRequest = false, comment = '', transaction: OnyxEntry = null): ReportAction { const hasReceipt = TransactionUtils.hasReceipt(transaction); const recentReceiptTransactions = reportPreviewAction?.childRecentReceiptTransactionIDs ?? {}; const transactionsToKeep = TransactionUtils.getRecentTransactions(recentReceiptTransactions); @@ -2967,8 +2955,7 @@ function updateReportPreview( const message = getReportPreviewMessage(iouReport, reportPreviewAction); return { - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - ...(reportPreviewAction as ReportAction), + ...reportPreviewAction, created: DateUtils.getDBTime(), message: [ { diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index feb27d7987cd..69a919185296 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -2161,7 +2161,7 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA oneOnOneIOUReport.total -= splitAmount; } } else { - oneOnOneIOUReport = IOUUtils.updateIOUOwnerAndTotal(oneOnOneIOUReport ?? null, sessionAccountID, splitAmount, currency ?? ''); + oneOnOneIOUReport = IOUUtils.updateIOUOwnerAndTotal(oneOnOneIOUReport, sessionAccountID, splitAmount, currency ?? ''); } const oneOnOneTransaction = TransactionUtils.buildOptimisticTransaction( @@ -2192,9 +2192,9 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA let oneOnOneReportPreviewAction = ReportActionsUtils.getReportPreviewAction(oneOnOneChatReport?.reportID ?? '', oneOnOneIOUReport?.reportID ?? ''); if (oneOnOneReportPreviewAction) { - oneOnOneReportPreviewAction = ReportUtils.updateReportPreview(oneOnOneIOUReport ?? null, oneOnOneReportPreviewAction); + oneOnOneReportPreviewAction = ReportUtils.updateReportPreview(oneOnOneIOUReport, oneOnOneReportPreviewAction); } else { - oneOnOneReportPreviewAction = ReportUtils.buildOptimisticReportPreview(oneOnOneChatReport, oneOnOneIOUReport ?? null, '', oneOnOneTransaction); + oneOnOneReportPreviewAction = ReportUtils.buildOptimisticReportPreview(oneOnOneChatReport, oneOnOneIOUReport, '', oneOnOneTransaction); } const [oneOnOneOptimisticData, oneOnOneSuccessData, oneOnOneFailureData] = buildOnyxDataForMoneyRequest( From cf01991b76d89dcf4c700e47df981cc0f6e3ed84 Mon Sep 17 00:00:00 2001 From: someone-here Date: Tue, 9 Jan 2024 20:27:16 +0530 Subject: [PATCH 070/660] Migrate welcome message to room description for report --- src/CONST.ts | 1 - src/ONYXKEYS.ts | 8 +-- src/ROUTES.ts | 6 +-- src/SCREENS.ts | 4 +- src/components/ReportWelcomeText.tsx | 27 ++++++---- src/languages/en.ts | 8 +-- src/languages/es.ts | 8 +-- .../AppNavigator/ModalStackNavigators.tsx | 8 +-- .../Navigators/RightModalNavigator.tsx | 4 +- src/libs/Navigation/linkingConfig.ts | 4 +- src/libs/Navigation/types.ts | 8 +-- src/libs/Permissions.ts | 2 +- src/libs/ReportUtils.ts | 22 +++++--- src/libs/actions/Report.ts | 26 +++++----- ...essagePage.js => ReportDescriptionPage.js} | 52 +++++++++---------- src/pages/ReportDetailsPage.js | 8 +++ src/pages/home/HeaderView.js | 49 +++++++++++++---- .../settings/Report/ReportSettingsPage.js | 11 ---- src/pages/workspace/WorkspaceNewRoomPage.js | 10 ++-- src/types/onyx/Report.ts | 1 - 20 files changed, 155 insertions(+), 112 deletions(-) rename src/pages/{ReportWelcomeMessagePage.js => ReportDescriptionPage.js} (71%) diff --git a/src/CONST.ts b/src/CONST.ts index c6849db630f2..8fcb888d65da 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1534,7 +1534,6 @@ const CONST = { INVITE: 'invite', SETTINGS: 'settings', LEAVE_ROOM: 'leaveRoom', - WELCOME_MESSAGE: 'welcomeMessage', PRIVATE_NOTES: 'privateNotes', }, EDIT_REQUEST_FIELD: { diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 89ddbdc06883..0ef82873e929 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -302,8 +302,8 @@ const ONYXKEYS = { DISPLAY_NAME_FORM_DRAFT: 'displayNameFormDraft', ROOM_NAME_FORM: 'roomNameForm', ROOM_NAME_FORM_DRAFT: 'roomNameFormDraft', - WELCOME_MESSAGE_FORM: 'welcomeMessageForm', - WELCOME_MESSAGE_FORM_DRAFT: 'welcomeMessageFormDraft', + REPORT_DESCRIPTION_FORM: 'reportDescriptionForm', + REPORT_DESCRIPTION_FORM_DRAFT: 'reportDescriptionFormDraft', LEGAL_NAME_FORM: 'legalNameForm', LEGAL_NAME_FORM_DRAFT: 'legalNameFormDraft', WORKSPACE_INVITE_MESSAGE_FORM: 'workspaceInviteMessageForm', @@ -478,8 +478,8 @@ type OnyxValues = { [ONYXKEYS.FORMS.DISPLAY_NAME_FORM_DRAFT]: OnyxTypes.Form; [ONYXKEYS.FORMS.ROOM_NAME_FORM]: OnyxTypes.Form; [ONYXKEYS.FORMS.ROOM_NAME_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.WELCOME_MESSAGE_FORM]: OnyxTypes.Form; - [ONYXKEYS.FORMS.WELCOME_MESSAGE_FORM_DRAFT]: OnyxTypes.Form; + [ONYXKEYS.FORMS.REPORT_DESCRIPTION_FORM]: OnyxTypes.Form; + [ONYXKEYS.FORMS.REPORT_DESCRIPTION_FORM_DRAFT]: OnyxTypes.Form; [ONYXKEYS.FORMS.LEGAL_NAME_FORM]: OnyxTypes.Form; [ONYXKEYS.FORMS.LEGAL_NAME_FORM_DRAFT]: OnyxTypes.Form; [ONYXKEYS.FORMS.WORKSPACE_INVITE_MESSAGE_FORM]: OnyxTypes.Form; diff --git a/src/ROUTES.ts b/src/ROUTES.ts index e8a860582bb1..1dfe3a2dcf84 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -195,9 +195,9 @@ const ROUTES = { route: 'r/:reportID/settings/who-can-post', getRoute: (reportID: string) => `r/${reportID}/settings/who-can-post` as const, }, - REPORT_WELCOME_MESSAGE: { - route: 'r/:reportID/welcomeMessage', - getRoute: (reportID: string) => `r/${reportID}/welcomeMessage` as const, + REPORT_DESCRIPTION: { + route: 'r/:reportID/roomDescription', + getRoute: (reportID: string) => `r/${reportID}/roomDescription` as const, }, SPLIT_BILL_DETAILS: { route: 'r/:reportID/split/:reportActionID', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 703cb309d641..b053782d43be 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -91,7 +91,7 @@ const SCREENS = { PROFILE: 'Profile', REPORT_DETAILS: 'Report_Details', REPORT_SETTINGS: 'Report_Settings', - REPORT_WELCOME_MESSAGE: 'Report_WelcomeMessage', + REPORT_DESCRIPTION: 'Report_Description', PARTICIPANTS: 'Participants', MONEY_REQUEST: 'MoneyRequest', NEW_TASK: 'NewTask', @@ -233,7 +233,7 @@ const SCREENS = { DETAILS_ROOT: 'Details_Root', PROFILE_ROOT: 'Profile_Root', PROCESS_MONEY_REQUEST_HOLD_ROOT: 'ProcessMoneyRequestHold_Root', - REPORT_WELCOME_MESSAGE_ROOT: 'Report_WelcomeMessage_Root', + REPORT_DESCRIPTION_ROOT: 'Report_Description_Root', REPORT_PARTICIPANTS_ROOT: 'ReportParticipants_Root', ROOM_MEMBERS_ROOT: 'RoomMembers_Root', ROOM_INVITE_ROOT: 'RoomInvite_Root', diff --git a/src/components/ReportWelcomeText.tsx b/src/components/ReportWelcomeText.tsx index b337c3581213..2c544720a22d 100644 --- a/src/components/ReportWelcomeText.tsx +++ b/src/components/ReportWelcomeText.tsx @@ -12,6 +12,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {PersonalDetailsList, Policy, Report} from '@src/types/onyx'; +import RenderHTML from './RenderHTML'; import Text from './Text'; import UserDetailsTooltip from './UserDetailsTooltip'; @@ -73,17 +74,23 @@ function ReportWelcomeText({report, policy, personalDetails}: ReportWelcomeTextP )} {isChatRoom && ( <> - {roomWelcomeMessage.phrase1} - {roomWelcomeMessage.showReportName && ( - - {ReportUtils.getReportName(report)} - + {report?.description ? ( + + ) : ( + <> + {roomWelcomeMessage.phrase1} + {roomWelcomeMessage.showReportName && ( + + {ReportUtils.getReportName(report)} + + )} + {roomWelcomeMessage.phrase2 !== undefined && {roomWelcomeMessage.phrase2}} + )} - {roomWelcomeMessage.phrase2 !== undefined && {roomWelcomeMessage.phrase2}} )} {isDefault && ( diff --git a/src/languages/en.ts b/src/languages/en.ts index c57b1ce310b5..e90060b3f740 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1033,10 +1033,10 @@ export default { }, }, }, - welcomeMessagePage: { - welcomeMessage: 'Welcome message', - welcomeMessageOptional: 'Welcome message (optional)', - explainerText: 'Set a custom welcome message that will be sent to users when they join this room.', + reportDescriptionPage: { + roomDescription: 'Room description', + roomDescriptionOptional: 'Room description (optional)', + explainerText: 'Set a custom decription for the room.', }, languagePage: { language: 'Language', diff --git a/src/languages/es.ts b/src/languages/es.ts index b83385b602c8..29688d4a19ae 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1032,10 +1032,10 @@ export default { }, }, }, - welcomeMessagePage: { - welcomeMessage: 'Mensaje de bienvenida', - welcomeMessageOptional: 'Mensaje de bienvenida (opcional)', - explainerText: 'Configura un mensaje de bienvenida privado y personalizado que se enviará cuando los usuarios se unan a esta sala de chat.', + reportDescriptionPage: { + roomDescription: 'Descripción de la habitación', + roomDescriptionOptional: 'Descripción de la habitación (opcional)', + explainerText: 'Establece una descripción personalizada para la habitación.', }, languagePage: { language: 'Idioma', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index 4be1c988561b..e033de853275 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -17,9 +17,9 @@ import type { ProfileNavigatorParamList, ReferralDetailsNavigatorParamList, ReimbursementAccountNavigatorParamList, + ReportDescriptionNavigatorParamList, ReportDetailsNavigatorParamList, ReportSettingsNavigatorParamList, - ReportWelcomeMessageNavigatorParamList, RoomInviteNavigatorParamList, RoomMembersNavigatorParamList, SearchNavigatorParamList, @@ -141,8 +141,8 @@ const TaskModalStackNavigator = createModalStackNavigator require('../../../pages/tasks/TaskAssigneeSelectorModal').default as React.ComponentType, }); -const ReportWelcomeMessageModalStackNavigator = createModalStackNavigator({ - [SCREENS.REPORT_WELCOME_MESSAGE_ROOT]: () => require('../../../pages/ReportWelcomeMessagePage').default as React.ComponentType, +const ReportDescriptionModalStackNavigator = createModalStackNavigator({ + [SCREENS.REPORT_DESCRIPTION_ROOT]: () => require('../../../pages/ReportDescriptionPage').default as React.ComponentType, }); const ReportParticipantsModalStackNavigator = createModalStackNavigator({ @@ -295,7 +295,7 @@ export { ReportDetailsModalStackNavigator, TaskModalStackNavigator, ReportSettingsModalStackNavigator, - ReportWelcomeMessageModalStackNavigator, + ReportDescriptionModalStackNavigator, ReportParticipantsModalStackNavigator, SearchModalStackNavigator, NewChatModalStackNavigator, diff --git a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx index 7721a64adea9..93d2f8fba989 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx @@ -62,8 +62,8 @@ function RightModalNavigator({navigation}: RightModalNavigatorProps) { component={ModalStackNavigators.ReportSettingsModalStackNavigator} /> = { }, }, }, - [SCREENS.RIGHT_MODAL.REPORT_WELCOME_MESSAGE]: { + [SCREENS.RIGHT_MODAL.REPORT_DESCRIPTION]: { screens: { - [SCREENS.REPORT_WELCOME_MESSAGE_ROOT]: ROUTES.REPORT_WELCOME_MESSAGE.route, + [SCREENS.REPORT_DESCRIPTION_ROOT]: ROUTES.REPORT_DESCRIPTION.route, }, }, [SCREENS.RIGHT_MODAL.NEW_CHAT]: { diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 90f5361f11f4..fd6942f8c9c6 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -161,8 +161,8 @@ type ReportSettingsNavigatorParamList = { [SCREENS.REPORT_SETTINGS.WRITE_CAPABILITY]: undefined; }; -type ReportWelcomeMessageNavigatorParamList = { - [SCREENS.REPORT_WELCOME_MESSAGE_ROOT]: {reportID: string}; +type ReportDescriptionNavigatorParamList = { + [SCREENS.REPORT_DESCRIPTION_ROOT]: {reportID: string}; }; type ParticipantsNavigatorParamList = { @@ -361,7 +361,7 @@ type RightModalNavigatorParamList = { [SCREENS.RIGHT_MODAL.PROFILE]: NavigatorScreenParams; [SCREENS.RIGHT_MODAL.REPORT_DETAILS]: NavigatorScreenParams; [SCREENS.RIGHT_MODAL.REPORT_SETTINGS]: NavigatorScreenParams; - [SCREENS.RIGHT_MODAL.REPORT_WELCOME_MESSAGE]: NavigatorScreenParams; + [SCREENS.RIGHT_MODAL.REPORT_DESCRIPTION]: NavigatorScreenParams; [SCREENS.RIGHT_MODAL.PARTICIPANTS]: NavigatorScreenParams; [SCREENS.RIGHT_MODAL.ROOM_MEMBERS]: NavigatorScreenParams; [SCREENS.RIGHT_MODAL.ROOM_INVITE]: NavigatorScreenParams; @@ -447,7 +447,7 @@ export type { ReportDetailsNavigatorParamList, ReportSettingsNavigatorParamList, TaskDetailsNavigatorParamList, - ReportWelcomeMessageNavigatorParamList, + ReportDescriptionNavigatorParamList, ParticipantsNavigatorParamList, RoomMembersNavigatorParamList, RoomInviteNavigatorParamList, diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index ce5e0e674c59..582115b0a914 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -3,7 +3,7 @@ import CONST from '@src/CONST'; import type Beta from '@src/types/onyx/Beta'; function canUseAllBetas(betas: OnyxEntry): boolean { - return !!betas?.includes(CONST.BETAS.ALL); + return !!betas?.includes(CONST.BETAS.ALL) || true; } function canUseChronos(betas: OnyxEntry): boolean { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 0d7658adf180..26a69cc51818 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -218,7 +218,7 @@ type OptimisticChatReport = Pick< | 'stateNum' | 'statusNum' | 'visibility' - | 'welcomeMessage' + | 'description' | 'writeCapability' >; @@ -2388,6 +2388,15 @@ function getParsedComment(text: string): string { return text.length <= CONST.MAX_MARKUP_LENGTH ? parser.replace(text) : lodashEscape(text); } +function getReportDescriptionText(report: Report): string { + if (!report.description) { + return ''; + } + + const parser = new ExpensiMark(); + return parser.htmlToText(report.description); +} + function buildOptimisticAddCommentReportAction(text?: string, file?: File): OptimisticReportAction { const parser = new ExpensiMark(); const commentText = getParsedComment(text ?? ''); @@ -3075,7 +3084,7 @@ function buildOptimisticChatReport( notificationPreference: NotificationPreference = CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, parentReportActionID = '', parentReportID = '', - welcomeMessage = '', + description = '', ): OptimisticChatReport { const currentTime = DateUtils.getDBTime(); const isNewlyCreatedWorkspaceChat = chatType === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT && isOwnPolicyExpenseChat; @@ -3104,7 +3113,7 @@ function buildOptimisticChatReport( stateNum: 0, statusNum: 0, visibility, - welcomeMessage, + description, writeCapability, }; } @@ -3585,7 +3594,7 @@ function canFlagReportAction(reportAction: OnyxEntry, reportID: st reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.CHRONOSOOOLIST; if (ReportActionsUtils.isWhisperAction(reportAction)) { // Allow flagging welcome message whispers as they can be set by any room creator - if (report?.welcomeMessage && !isCurrentUserAction && isOriginalMessageHaveHtml && reportAction?.originalMessage?.html === report.welcomeMessage) { + if (report?.description && !isCurrentUserAction && isOriginalMessageHaveHtml && reportAction?.originalMessage?.html === report.description) { return true; } @@ -4270,7 +4279,7 @@ function getRoom(type: ValueOf, policyID: string) /** * We only want policy owners and admins to be able to modify the welcome message, but not in thread chat. */ -function shouldDisableWelcomeMessage(report: OnyxEntry, policy: OnyxEntry): boolean { +function shouldDisableReportDescription(report: OnyxEntry, policy: OnyxEntry): boolean { return isMoneyRequestReport(report) || isArchivedRoom(report) || !isChatRoom(report) || isChatThread(report) || !PolicyUtils.isPolicyAdmin(policy); } /** @@ -4525,13 +4534,14 @@ export { getReimbursementDeQueuedActionMessage, getPersonalDetailsForAccountID, getRoom, - shouldDisableWelcomeMessage, + shouldDisableReportDescription, navigateToPrivateNotes, canEditWriteCapability, hasSmartscanError, shouldAutoFocusOnKeyPress, shouldDisplayThreadReplies, shouldDisableThread, + getReportDescriptionText, }; export type {ExpenseOriginalMessage, OptionData, OptimisticChatReport}; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 55e91834a803..9cffcb45f9cf 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1481,39 +1481,39 @@ function toggleSubscribeToChildReport(childReportID = '0', parentReportAction: P } } -function updateWelcomeMessage(reportID: string, previousValue: string, newValue: string) { +function updateDescription(reportID: string, previousValue: string, newValue: string) { // No change needed, navigate back if (previousValue === newValue) { - Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(reportID)); + Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(reportID)); return; } - const parsedWelcomeMessage = ReportUtils.getParsedComment(newValue); + const parsedDescription = ReportUtils.getParsedComment(newValue); const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: {welcomeMessage: parsedWelcomeMessage}, + value: {description: parsedDescription}, }, ]; const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: {welcomeMessage: previousValue}, + value: {description: previousValue}, }, ]; - type UpdateWelcomeMessageParameters = { + type UpdateReportDescriptionParameters = { reportID: string; - welcomeMessage: string; + description: string; }; - const parameters: UpdateWelcomeMessageParameters = {reportID, welcomeMessage: parsedWelcomeMessage}; + const parameters: UpdateReportDescriptionParameters = {reportID, description: parsedDescription}; - API.write('UpdateWelcomeMessage', parameters, {optimisticData, failureData}); - Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(reportID)); + API.write('UpdateRoomDescription', parameters, {optimisticData, failureData}); + Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(reportID)); } function updateWriteCapabilityAndNavigate(report: Report, newValue: WriteCapability) { @@ -1649,7 +1649,7 @@ function addPolicyReport(policyReport: ReportUtils.OptimisticChatReport) { reportName?: string; visibility?: ValueOf; writeCapability?: WriteCapability; - welcomeMessage?: string; + description?: string; }; const parameters: AddWorkspaceRoomParameters = { @@ -1659,7 +1659,7 @@ function addPolicyReport(policyReport: ReportUtils.OptimisticChatReport) { reportID: policyReport.reportID, createdReportActionID: createdReportAction.reportActionID, writeCapability: policyReport.writeCapability, - welcomeMessage: policyReport.welcomeMessage, + description: policyReport.description, }; API.write('AddWorkspaceRoom', parameters, {optimisticData, successData, failureData}); @@ -2590,7 +2590,7 @@ export { addComment, addAttachment, reconnect, - updateWelcomeMessage, + updateDescription, updateWriteCapabilityAndNavigate, updateNotificationPreference, subscribeToReportTypingEvents, diff --git a/src/pages/ReportWelcomeMessagePage.js b/src/pages/ReportDescriptionPage.js similarity index 71% rename from src/pages/ReportWelcomeMessagePage.js rename to src/pages/ReportDescriptionPage.js index ae8a4635a98e..cba19d25c1d3 100644 --- a/src/pages/ReportWelcomeMessagePage.js +++ b/src/pages/ReportDescriptionPage.js @@ -35,7 +35,7 @@ const propTypes = { /** Route params */ route: PropTypes.shape({ params: PropTypes.shape({ - /** Report ID passed via route r/:reportID/welcomeMessage */ + /** Report ID passed via route r/:reportID/roomDescription */ reportID: PropTypes.string, }), }).isRequired, @@ -45,26 +45,26 @@ const defaultProps = { ...policyDefaultProps, }; -function ReportWelcomeMessagePage(props) { +function ReportDescriptionPage(props) { const styles = useThemeStyles(); const parser = new ExpensiMark(); - const [welcomeMessage, setWelcomeMessage] = useState(() => parser.htmlToMarkdown(props.report.welcomeMessage)); - const welcomeMessageInputRef = useRef(null); + const [description, setdescription] = useState(() => parser.htmlToMarkdown(props.report.description)); + const reportDescriptionInputRef = useRef(null); const focusTimeoutRef = useRef(null); - const handleWelcomeMessageChange = useCallback((value) => { - setWelcomeMessage(value); + const handleReportDescriptionChange = useCallback((value) => { + setdescription(value); }, []); const submitForm = useCallback(() => { - Report.updateWelcomeMessage(props.report.reportID, props.report.welcomeMessage, welcomeMessage.trim()); - }, [props.report.reportID, props.report.welcomeMessage, welcomeMessage]); + Report.updateDescription(props.report.reportID, props.report.description, description.trim()); + }, [props.report.reportID, props.report.description, description]); useFocusEffect( useCallback(() => { focusTimeoutRef.current = setTimeout(() => { - if (welcomeMessageInputRef.current) { - welcomeMessageInputRef.current.focus(); + if (reportDescriptionInputRef.current) { + reportDescriptionInputRef.current.focus(); } return () => { if (!focusTimeoutRef.current) { @@ -80,27 +80,27 @@ function ReportWelcomeMessagePage(props) { - + Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(props.report.reportID))} /> - {props.translate('welcomeMessagePage.explainerText')} + {props.translate('reportDescriptionPage.explainerText')} @@ -123,9 +123,9 @@ function ReportWelcomeMessagePage(props) { ); } -ReportWelcomeMessagePage.displayName = 'ReportWelcomeMessagePage'; -ReportWelcomeMessagePage.propTypes = propTypes; -ReportWelcomeMessagePage.defaultProps = defaultProps; +ReportDescriptionPage.displayName = 'ReportDescriptionPage'; +ReportDescriptionPage.propTypes = propTypes; +ReportDescriptionPage.defaultProps = defaultProps; export default compose( withLocalize, @@ -135,4 +135,4 @@ export default compose( key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, }, }), -)(ReportWelcomeMessagePage); +)(ReportDescriptionPage); diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index ff9ed62c6a65..57b940374d8a 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -8,6 +8,7 @@ import DisplayNames from '@components/DisplayNames'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; +import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import MultipleAvatars from '@components/MultipleAvatars'; import {withNetwork} from '@components/OnyxProvider'; import ParentNavigationSubtitle from '@components/ParentNavigationSubtitle'; @@ -235,6 +236,13 @@ function ReportDetailsPage(props) { )} + Navigation.navigate(ROUTES.REPORT_DESCRIPTION.getRoute(props.report.reportID))} + /> {_.map(menuItems, (item) => { const brickRoadIndicator = ReportUtils.hasReportNameError(props.report) && item.key === CONST.REPORT_DETAILS_MENU_ITEM.SETTINGS ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index edf6b65b2f4a..d2a4e8442b6f 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -116,6 +116,10 @@ function HeaderView(props) { const isPolicyMember = useMemo(() => !_.isEmpty(props.policy), [props.policy]); const canLeaveRoom = ReportUtils.canLeaveRoom(props.report, isPolicyMember); const isArchivedRoom = ReportUtils.isArchivedRoom(props.report); + const reportDescription = ReportUtils.getReportDescriptionText(props.report); + const policyName = ReportUtils.getPolicyName(props.report); + // const policyDescription = ReportUtils.getPolicyDescription(props.report.policyID); + const description = reportDescription; // We hide the button when we are chatting with an automated Expensify account since it's not possible to contact // these users via alternative means. It is possible to request a call with Concierge so we leave the option for them. @@ -271,14 +275,33 @@ function HeaderView(props) { /> )} - + + + {!_.isEmpty(policyName) && + !_.isEmpty(reportDescription) && ( // code for the policyName display + <> + + {translate('threads.in')} + + + {policyName} + + + )} + {!_.isEmpty(parentNavigationSubtitleData) && ( )} - {!_.isEmpty(subtitle) && ( + {!_.isEmpty(subtitle) && _.isEmpty(description) && ( )} + {!_.isEmpty(description) && ( + + {description} + + )} {brickRoadIndicator === CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR && ( diff --git a/src/pages/settings/Report/ReportSettingsPage.js b/src/pages/settings/Report/ReportSettingsPage.js index c7cfd9c7850d..fe3953dd0248 100644 --- a/src/pages/settings/Report/ReportSettingsPage.js +++ b/src/pages/settings/Report/ReportSettingsPage.js @@ -63,9 +63,6 @@ function ReportSettingsPage(props) { const shouldDisableRename = useMemo(() => ReportUtils.shouldDisableRename(report, linkedWorkspace), [report, linkedWorkspace]); const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); - // We only want policy owners and admins to be able to modify the welcome message, but not in thread chat - const shouldDisableWelcomeMessage = ReportUtils.shouldDisableWelcomeMessage(report, linkedWorkspace); - const shouldDisableSettings = _.isEmpty(report) || ReportUtils.isArchivedRoom(report); const shouldShowRoomName = !ReportUtils.isPolicyExpenseChat(report) && !ReportUtils.isChatThread(report); const notificationPreference = @@ -192,14 +189,6 @@ function ReportSettingsPage(props) { )} - {!shouldDisableWelcomeMessage && ( - Navigation.navigate(ROUTES.REPORT_WELCOME_MESSAGE.getRoute(report.reportID))} - shouldShowRightIcon - /> - )} diff --git a/src/pages/workspace/WorkspaceNewRoomPage.js b/src/pages/workspace/WorkspaceNewRoomPage.js index 21c93b87806a..aa5b24af9958 100644 --- a/src/pages/workspace/WorkspaceNewRoomPage.js +++ b/src/pages/workspace/WorkspaceNewRoomPage.js @@ -114,7 +114,7 @@ function WorkspaceNewRoomPage(props) { */ const submit = (values) => { const participants = [props.session.accountID]; - const parsedWelcomeMessage = ReportUtils.getParsedComment(values.welcomeMessage); + const parsedDescription = ReportUtils.getParsedComment(values.reportDescription); const policyReport = ReportUtils.buildOptimisticChatReport( participants, values.roomName, @@ -128,7 +128,7 @@ function WorkspaceNewRoomPage(props) { CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, '', '', - parsedWelcomeMessage, + parsedDescription, ); setNewRoomReportID(policyReport.reportID); Report.addPolicyReport(policyReport); @@ -280,9 +280,9 @@ function WorkspaceNewRoomPage(props) { Date: Tue, 9 Jan 2024 20:49:40 +0530 Subject: [PATCH 071/660] =?UTF-8?q?Fix=20style=20=F0=9F=92=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/libs/E2E/apiMocks/openReport.ts | 1 - src/pages/home/HeaderView.js | 8 +++----- src/pages/settings/Report/ReportSettingsPage.js | 2 -- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/libs/E2E/apiMocks/openReport.ts b/src/libs/E2E/apiMocks/openReport.ts index 49d44605592d..4165db650a5c 100644 --- a/src/libs/E2E/apiMocks/openReport.ts +++ b/src/libs/E2E/apiMocks/openReport.ts @@ -24,7 +24,6 @@ export default (): Response => ({ lastMessageText: 'terry+hightraffic@margelo.io owes \u20ac12.00', lastActorAccountID: 14567013, notificationPreference: 'always', - welcomeMessage: '', stateNum: 0, statusNum: 0, oldPolicyName: '', diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index d2a4e8442b6f..0832d789ae35 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -118,8 +118,6 @@ function HeaderView(props) { const isArchivedRoom = ReportUtils.isArchivedRoom(props.report); const reportDescription = ReportUtils.getReportDescriptionText(props.report); const policyName = ReportUtils.getPolicyName(props.report); - // const policyDescription = ReportUtils.getPolicyDescription(props.report.policyID); - const description = reportDescription; // We hide the button when we are chatting with an automated Expensify account since it's not possible to contact // these users via alternative means. It is possible to request a call with Concierge so we leave the option for them. @@ -309,7 +307,7 @@ function HeaderView(props) { pressableStyles={[styles.alignSelfStart, styles.mw100]} /> )} - {!_.isEmpty(subtitle) && _.isEmpty(description) && ( + {!_.isEmpty(subtitle) && _.isEmpty(reportDescription) && ( )} - {!_.isEmpty(description) && ( + {!_.isEmpty(reportDescription) && ( - {description} + {reportDescription} )} diff --git a/src/pages/settings/Report/ReportSettingsPage.js b/src/pages/settings/Report/ReportSettingsPage.js index fe3953dd0248..4c0e3b477a02 100644 --- a/src/pages/settings/Report/ReportSettingsPage.js +++ b/src/pages/settings/Report/ReportSettingsPage.js @@ -7,8 +7,6 @@ import _ from 'underscore'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import DisplayNames from '@components/DisplayNames'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import * as Expensicons from '@components/Icon/Expensicons'; -import MenuItem from '@components/MenuItem'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import ScreenWrapper from '@components/ScreenWrapper'; From a488df6475ba7d4deb506138e2d3a3883b0bd297 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 9 Jan 2024 16:36:58 +0100 Subject: [PATCH 072/660] Update receipt source type --- src/libs/ErrorUtils.ts | 3 +-- src/libs/ReceiptUtils.ts | 2 +- src/libs/actions/IOU.ts | 7 +++---- src/types/onyx/Transaction.ts | 3 +-- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/libs/ErrorUtils.ts b/src/libs/ErrorUtils.ts index 98b137502755..d38a05dc5a5c 100644 --- a/src/libs/ErrorUtils.ts +++ b/src/libs/ErrorUtils.ts @@ -1,4 +1,3 @@ -import type {ImageSourcePropType} from 'react-native'; import CONST from '@src/CONST'; import type {TranslationFlatObject, TranslationPaths} from '@src/languages/types'; import type {ErrorFields, SimpleErrors} from '@src/types/onyx/OnyxCommon'; @@ -6,7 +5,7 @@ import type Response from '@src/types/onyx/Response'; import DateUtils from './DateUtils'; import * as Localize from './Localize'; -type ErrorObject = Record; +type ErrorObject = Record; type MicroSecondOnyxError = Record; diff --git a/src/libs/ReceiptUtils.ts b/src/libs/ReceiptUtils.ts index 70e7a72f6b5c..bcba68a3a0bd 100644 --- a/src/libs/ReceiptUtils.ts +++ b/src/libs/ReceiptUtils.ts @@ -30,7 +30,7 @@ type FileNameAndExtension = { */ function getThumbnailAndImageURIs(transaction: Transaction, receiptPath: string | null = null, receiptFileName: string | null = null): ThumbnailAndImageURI { // URI to image, i.e. blob:new.expensify.com/9ef3a018-4067-47c6-b29f-5f1bd35f213d or expensify.com/receipts/w_e616108497ef940b7210ec6beb5a462d01a878f4.jpg - const path = (transaction?.receipt?.source as string) ?? receiptPath ?? ''; + const path = transaction?.receipt?.source ?? receiptPath ?? ''; // filename of uploaded image or last part of remote URI const filename = transaction?.filename ?? receiptFileName ?? ''; const isReceiptImage = Str.isImage(filename); diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 69a919185296..a0e522ea54ed 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3,7 +3,6 @@ import type {StackScreenProps} from '@react-navigation/stack'; import {format} from 'date-fns'; import Str from 'expensify-common/lib/str'; import lodashHas from 'lodash/has'; -import type {ImageSourcePropType} from 'react-native'; import Onyx from 'react-native-onyx'; import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import OnyxUtils from 'react-native-onyx/lib/utils'; @@ -316,7 +315,7 @@ function setMoneyRequestParticipants_temporaryForRefactor(transactionID: string, Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {participants}); } -function setMoneyRequestReceipt_temporaryForRefactor(transactionID: string, source: ImageSourcePropType, filename: string) { +function setMoneyRequestReceipt_temporaryForRefactor(transactionID: string, source: string, filename: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {receipt: {source}, filename}); } @@ -886,7 +885,7 @@ function createDistanceRequest( const currentChatReport = isMoneyRequestReport ? ReportUtils.getReport(report.chatReportID) : report; const optimisticReceipt: Receipt = { - source: ReceiptGeneric, + source: ReceiptGeneric as string, state: CONST.IOU.RECEIPT_STATE.OPEN, }; const {iouReport, chatReport, transaction, iouAction, createdChatReportActionID, createdIOUReportActionID, reportPreviewAction, onyxData} = getMoneyRequestInformation( @@ -3455,7 +3454,7 @@ function detachReceipt(transactionID: string) { API.write('DetachReceipt', {transactionID}, {optimisticData, failureData}); } -function replaceReceipt(transactionID: string, receipt: Receipt, filePath: ImageSourcePropType) { +function replaceReceipt(transactionID: string, receipt: Receipt, filePath: string) { const transaction = allTransactions.transactionID; const oldReceipt = transaction?.receipt ?? {}; diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 7c40118207a0..6fdd8d309408 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -1,4 +1,3 @@ -import type {ImageSourcePropType} from 'react-native'; import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; import type {Participant, Split} from './IOU'; @@ -43,7 +42,7 @@ type Receipt = { receiptID?: number; path?: string; name?: string; - source?: ImageSourcePropType; + source?: string; filename?: string; state?: ValueOf; }; From 5be28c79dba6f03e364c8f506e452edf54bd36de Mon Sep 17 00:00:00 2001 From: someone-here Date: Tue, 9 Jan 2024 21:48:40 +0530 Subject: [PATCH 073/660] Fix Permissions --- src/libs/Permissions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index 582115b0a914..ce5e0e674c59 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -3,7 +3,7 @@ import CONST from '@src/CONST'; import type Beta from '@src/types/onyx/Beta'; function canUseAllBetas(betas: OnyxEntry): boolean { - return !!betas?.includes(CONST.BETAS.ALL) || true; + return !!betas?.includes(CONST.BETAS.ALL); } function canUseChronos(betas: OnyxEntry): boolean { From bd8005542ce0212cae6cf2eb9079ae49ee74acbc Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 9 Jan 2024 17:24:18 +0100 Subject: [PATCH 074/660] Update errors types --- src/components/OfflineWithFeedback.tsx | 9 ++------- src/libs/ErrorUtils.ts | 21 +++++++-------------- src/libs/ReportUtils.ts | 8 ++++---- src/libs/SidebarUtils.ts | 2 +- src/libs/actions/IOU.ts | 6 +++--- src/types/onyx/OnyxCommon.ts | 9 ++++----- src/types/onyx/ReportAction.ts | 2 +- 7 files changed, 22 insertions(+), 35 deletions(-) diff --git a/src/components/OfflineWithFeedback.tsx b/src/components/OfflineWithFeedback.tsx index 0446bd31025f..5a825e9ce24f 100644 --- a/src/components/OfflineWithFeedback.tsx +++ b/src/components/OfflineWithFeedback.tsx @@ -25,7 +25,7 @@ type OfflineWithFeedbackProps = ChildrenProps & { shouldHideOnDelete?: boolean; /** The errors to display */ - errors?: OnyxCommon.SimpleErrors | null; + errors?: OnyxCommon.Errors | null; /** Whether we should show the error messages */ shouldShowErrorMessages?: boolean; @@ -57,11 +57,6 @@ type OfflineWithFeedbackProps = ChildrenProps & { type StrikethroughProps = Partial & {style: Array}; -function omitBy(obj: Record | undefined | null, predicate: (value: T) => boolean) { - // eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-unused-vars - return Object.fromEntries(Object.entries(obj ?? {}).filter(([_, value]) => !predicate(value))); -} - function OfflineWithFeedback({ pendingAction, canDismissError = true, @@ -83,7 +78,7 @@ function OfflineWithFeedback({ const hasErrors = isNotEmptyObject(errors ?? {}); // Some errors have a null message. This is used to apply opacity only and to avoid showing redundant messages. - const errorMessages = omitBy(errors, (e) => e === null); + const errorMessages = Object.fromEntries(Object.entries(errors ?? {}).filter((errorEntry): errorEntry is [string, string] => errorEntry[1] !== null)); const hasErrorMessages = isNotEmptyObject(errorMessages); const isOfflinePendingAction = !!isOffline && !!pendingAction; const isUpdateOrDeleteError = hasErrors && (pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE); diff --git a/src/libs/ErrorUtils.ts b/src/libs/ErrorUtils.ts index d38a05dc5a5c..6c1540ccaa55 100644 --- a/src/libs/ErrorUtils.ts +++ b/src/libs/ErrorUtils.ts @@ -1,16 +1,10 @@ import CONST from '@src/CONST'; import type {TranslationFlatObject, TranslationPaths} from '@src/languages/types'; -import type {ErrorFields, SimpleErrors} from '@src/types/onyx/OnyxCommon'; +import type {ErrorFields, Errors, ErrorsObject} from '@src/types/onyx/OnyxCommon'; import type Response from '@src/types/onyx/Response'; import DateUtils from './DateUtils'; import * as Localize from './Localize'; -type ErrorObject = Record; - -type MicroSecondOnyxError = Record; - -type MicroSecondOnyxErrorObject = Record; - function getAuthenticateErrorMessage(response: Response): keyof TranslationFlatObject { switch (response.jsonCode) { case CONST.JSON_CODE.UNABLE_TO_RETRY: @@ -44,7 +38,7 @@ function getAuthenticateErrorMessage(response: Response): keyof TranslationFlatO * Method used to get an error object with microsecond as the key. * @param error - error key or message to be saved */ -function getMicroSecondOnyxError(error: string | null): MicroSecondOnyxError { +function getMicroSecondOnyxError(error: string | null): Errors { return {[DateUtils.getMicroseconds()]: error}; } @@ -52,15 +46,15 @@ function getMicroSecondOnyxError(error: string | null): MicroSecondOnyxError { * Method used to get an error object with microsecond as the key and an object as the value. * @param error - error key or message to be saved */ -function getMicroSecondOnyxErrorObject(error: ErrorObject): MicroSecondOnyxErrorObject { +function getMicroSecondOnyxErrorObject(error: Errors): ErrorsObject { return {[DateUtils.getMicroseconds()]: error}; } type OnyxDataWithErrors = { - errors?: SimpleErrors; + errors?: Errors; }; -function getLatestErrorMessage(onyxData: TOnyxData): string { +function getLatestErrorMessage(onyxData: TOnyxData): string | null { const errors = onyxData.errors ?? {}; if (Object.keys(errors).length === 0) { @@ -76,7 +70,7 @@ type OnyxDataWithErrorFields = { errorFields?: ErrorFields; }; -function getLatestErrorField(onyxData: TOnyxData, fieldName: string): Record { +function getLatestErrorField(onyxData: TOnyxData, fieldName: string): Errors { const errorsForField = onyxData.errorFields?.[fieldName] ?? {}; if (Object.keys(errorsForField).length === 0) { @@ -88,7 +82,7 @@ function getLatestErrorField(onyxData return {[key]: errorsForField[key]}; } -function getEarliestErrorField(onyxData: TOnyxData, fieldName: string): Record { +function getEarliestErrorField(onyxData: TOnyxData, fieldName: string): Errors { const errorsForField = onyxData.errorFields?.[fieldName] ?? {}; if (Object.keys(errorsForField).length === 0) { @@ -126,4 +120,3 @@ function addErrorMessage(errors: ErrorsList, inpu } export {getAuthenticateErrorMessage, getMicroSecondOnyxError, getMicroSecondOnyxErrorObject, getLatestErrorMessage, getLatestErrorField, getEarliestErrorField, addErrorMessage}; -export type {MicroSecondOnyxErrorObject, MicroSecondOnyxError}; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 2a1cbfc21550..1fb20a558294 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -16,7 +16,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Beta, Login, PersonalDetails, PersonalDetailsList, Policy, Report, ReportAction, ReportMetadata, Session, Transaction} from '@src/types/onyx'; import type {Participant} from '@src/types/onyx/IOU'; -import type {Icon, PendingAction, SimpleErrors} from '@src/types/onyx/OnyxCommon'; +import type {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; import type {IOUMessage, OriginalMessageActionName, OriginalMessageCreated} from '@src/types/onyx/OriginalMessage'; import type {Status} from '@src/types/onyx/PersonalDetails'; import type {NotificationPreference} from '@src/types/onyx/Report'; @@ -133,7 +133,7 @@ type ReportRouteParams = { type ReportOfflinePendingActionAndErrors = { addWorkspaceRoomOrChatPendingAction: PendingAction | undefined; - addWorkspaceRoomOrChatErrors: Record | null | undefined; + addWorkspaceRoomOrChatErrors: Errors | null | undefined; }; type OptimisticApprovedReportAction = Pick< @@ -292,7 +292,7 @@ type CustomIcon = { type OptionData = { text: string; alternateText?: string | null; - allReportErrors?: SimpleErrors | null; + allReportErrors?: Errors | null; brickRoadIndicator?: typeof CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR | '' | null; tooltipText?: string | null; alternateTextMaxLines?: number; @@ -3885,7 +3885,7 @@ function isValidReportIDFromPath(reportIDFromPath: string): boolean { /** * Return the errors we have when creating a chat or a workspace room */ -function getAddWorkspaceRoomOrChatReportErrors(report: OnyxEntry): Record | null | undefined { +function getAddWorkspaceRoomOrChatReportErrors(report: OnyxEntry): Errors | null | undefined { // We are either adding a workspace room, or we're creating a chat, it isn't possible for both of these to have errors for the same report at the same time, so // simply looking up the first truthy value will get the relevant property if it's set. return report?.errorFields?.addWorkspaceRoom ?? report?.errorFields?.createChat; diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index a5d3b40e99fc..6e46ec320066 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -291,7 +291,7 @@ function getOptionData( result.isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); result.shouldShowSubscript = ReportUtils.shouldReportShowSubscript(report); result.pendingAction = report.pendingFields ? report.pendingFields.addWorkspaceRoom || report.pendingFields.createChat : undefined; - result.allReportErrors = OptionsListUtils.getAllReportErrors(report, reportActions) as OnyxCommon.SimpleErrors; + result.allReportErrors = OptionsListUtils.getAllReportErrors(report, reportActions) as OnyxCommon.Errors; result.brickRoadIndicator = Object.keys(result.allReportErrors ?? {}).length !== 0 ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; result.ownerAccountID = report.ownerAccountID; result.managerID = report.managerID; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index a0e522ea54ed..16a43be58ccd 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -12,7 +12,6 @@ import * as API from '@libs/API'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import DateUtils from '@libs/DateUtils'; import * as ErrorUtils from '@libs/ErrorUtils'; -import type {MicroSecondOnyxError, MicroSecondOnyxErrorObject} from '@libs/ErrorUtils'; import * as IOUUtils from '@libs/IOUUtils'; import * as LocalePhoneNumber from '@libs/LocalePhoneNumber'; import * as Localize from '@libs/Localize'; @@ -34,6 +33,7 @@ import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; import type {Participant, Split} from '@src/types/onyx/IOU'; +import {Errors, ErrorsObject} from '@src/types/onyx/OnyxCommon'; import type ReportAction from '@src/types/onyx/ReportAction'; import type {OnyxData} from '@src/types/onyx/Request'; import type {Comment, Receipt, TaxRate, TransactionChanges, WaypointCollection} from '@src/types/onyx/Transaction'; @@ -346,10 +346,10 @@ function resetMoneyRequestInfo(id = '') { /** * Helper function to get the receipt error for money requests, or the generic error if there's no receipt */ -function getReceiptError(receipt?: Receipt, filename?: string, isScanRequest = true): MicroSecondOnyxError | MicroSecondOnyxErrorObject { +function getReceiptError(receipt?: Receipt, filename?: string, isScanRequest = true): Errors | ErrorsObject { return isEmptyObject(receipt) || !isScanRequest ? ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage') - : ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source: receipt.source, filename}); + : ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source: receipt.source ?? '', filename: filename ?? ''}); } /** Builds the Onyx data for a money request */ diff --git a/src/types/onyx/OnyxCommon.ts b/src/types/onyx/OnyxCommon.ts index 60a3fc5b89df..cff72ed94e10 100644 --- a/src/types/onyx/OnyxCommon.ts +++ b/src/types/onyx/OnyxCommon.ts @@ -1,5 +1,4 @@ import type {ValueOf} from 'type-fest'; -import type {MicroSecondOnyxError, MicroSecondOnyxErrorObject} from '@libs/ErrorUtils'; import type {AvatarSource} from '@libs/UserUtils'; import type CONST from '@src/CONST'; @@ -7,11 +6,11 @@ type PendingAction = ValueOf; type PendingFields = Record; -type SimpleErrors = Record; +type ErrorFields = Record; -type ErrorFields = Record; +type Errors = Record; -type Errors = SimpleErrors | MicroSecondOnyxError | MicroSecondOnyxErrorObject; +type ErrorsObject = Record; type AvatarType = typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; @@ -32,4 +31,4 @@ type Icon = { fallbackIcon?: AvatarSource; }; -export type {Icon, PendingAction, PendingFields, ErrorFields, Errors, AvatarType, SimpleErrors}; +export type {Icon, PendingAction, PendingFields, ErrorFields, Errors, AvatarType, ErrorsObject}; diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index 47945ffba4bb..f14f93d84372 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -178,7 +178,7 @@ type ReportActionBase = { delegateAccountID?: string; /** Server side errors keyed by microtime */ - errors?: OnyxCommon.Errors; + errors?: OnyxCommon.Errors | OnyxCommon.ErrorsObject; /** Whether the report action is attachment */ isAttachment?: boolean; From 55d7cf0373fe398c70aceed62ba408f759dbd66e Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 9 Jan 2024 18:11:48 +0100 Subject: [PATCH 075/660] Minor code improvements --- src/libs/actions/IOU.ts | 46 ++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 16a43be58ccd..9429809d6568 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -2,7 +2,6 @@ import type {StackScreenProps} from '@react-navigation/stack'; import {format} from 'date-fns'; import Str from 'expensify-common/lib/str'; -import lodashHas from 'lodash/has'; import Onyx from 'react-native-onyx'; import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import OnyxUtils from 'react-native-onyx/lib/utils'; @@ -33,7 +32,7 @@ import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; import type {Participant, Split} from '@src/types/onyx/IOU'; -import {Errors, ErrorsObject} from '@src/types/onyx/OnyxCommon'; +import type {Errors, ErrorsObject} from '@src/types/onyx/OnyxCommon'; import type ReportAction from '@src/types/onyx/ReportAction'; import type {OnyxData} from '@src/types/onyx/Request'; import type {Comment, Receipt, TaxRate, TransactionChanges, WaypointCollection} from '@src/types/onyx/Transaction'; @@ -151,7 +150,7 @@ Onyx.connect({ callback: (val) => (allReports = val), }); -let allTransactions: Record = {}; +let allTransactions: Record> = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION, waitForCollectionCallback: true, @@ -165,7 +164,7 @@ Onyx.connect({ }, }); -let allTransactionDrafts: Record = {}; +let allTransactionDrafts: Record> = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, waitForCollectionCallback: true, @@ -174,7 +173,7 @@ Onyx.connect({ }, }); -let allTransactionViolations: Record = {}; +let allTransactionViolations: Record> = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, waitForCollectionCallback: true, @@ -188,7 +187,7 @@ Onyx.connect({ }, }); -let allDraftSplitTransactions: Record = {}; +let allDraftSplitTransactions: Record> = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT, waitForCollectionCallback: true, @@ -197,7 +196,7 @@ Onyx.connect({ }, }); -let allNextSteps: Record = {}; +let allNextSteps: Record> = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.NEXT_STEP, waitForCollectionCallback: true, @@ -240,7 +239,8 @@ Onyx.connect({ function startMoneyRequest_temporaryForRefactor(reportID: string, isFromGlobalCreate: boolean, iouRequestType: IOURequestType = CONST.IOU.REQUEST_TYPE.MANUAL) { // Generate a brand new transactionID const newTransactionID = CONST.IOU.OPTIMISTIC_TRANSACTION_ID; - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- currentDate can be an empty string + // Disabling this line since currentDate can be an empty string + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const created = currentDate || format(new Date(), 'yyyy-MM-dd'); const comment: Comment = {}; @@ -323,7 +323,8 @@ function setMoneyRequestReceipt_temporaryForRefactor(transactionID: string, sour * Reset money request info from the store with its initial value */ function resetMoneyRequestInfo(id = '') { - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- currentDate can be an empty string + // Disabling this line since currentDate can be an empty string + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const created = currentDate || format(new Date(), CONST.DATE.FNS_FORMAT_STRING); Onyx.merge(ONYXKEYS.IOU, { id, @@ -640,7 +641,7 @@ function buildOnyxDataForMoneyRequest( failureData.push({ onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transaction.transactionID}`, - value: null, + value: [], }); } @@ -813,6 +814,7 @@ function getMoneyRequestInformation( [payerAccountID]: { accountID: payerAccountID, avatar: UserUtils.getDefaultAvatarURL(payerAccountID), + // Disabling this line since participant.displayName can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing displayName: LocalePhoneNumber.formatPhoneNumber(participant.displayName || payerEmail), login: participant.login, @@ -1414,7 +1416,7 @@ function createSplitsAndOnyxData( { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${CONST.IOU.OPTIMISTIC_TRANSACTION_ID}`, - value: {pendingAction: null}, + value: {}, }, ]; @@ -1437,7 +1439,7 @@ function createSplitsAndOnyxData( { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${CONST.IOU.OPTIMISTIC_TRANSACTION_ID}`, - value: {pendingAction: null}, + value: {}, }, ]; @@ -1496,7 +1498,7 @@ function createSplitsAndOnyxData( let oneOnOneChatReport: OptimisticChatReport; let isNewOneOnOneChatReport = false; let shouldCreateOptimisticPersonalDetails = false; - const personalDetailExists = lodashHas(allPersonalDetails, accountID); + const personalDetailExists = accountID in allPersonalDetails; // If this is a split between two people only and the function // wasn't provided with an existing group chat report id @@ -1529,14 +1531,14 @@ function createSplitsAndOnyxData( oneOnOneIOUReport.total -= splitAmount; } } else { - oneOnOneIOUReport = IOUUtils.updateIOUOwnerAndTotal(oneOnOneIOUReport ?? null, currentUserAccountID, splitAmount, currency); + oneOnOneIOUReport = IOUUtils.updateIOUOwnerAndTotal(oneOnOneIOUReport, currentUserAccountID, splitAmount, currency); } // STEP 3: Build optimistic transaction const oneOnOneTransaction = TransactionUtils.buildOptimisticTransaction( - ReportUtils.isExpenseReport(oneOnOneIOUReport ?? null) ? -splitAmount : splitAmount, + ReportUtils.isExpenseReport(oneOnOneIOUReport) ? -splitAmount : splitAmount, currency, - oneOnOneIOUReport?.reportID ?? '', + oneOnOneIOUReport.reportID, comment, '', CONST.IOU.TYPE.SPLIT, @@ -1566,7 +1568,7 @@ function createSplitsAndOnyxData( [participant], oneOnOneTransaction.transactionID, undefined, - oneOnOneIOUReport?.reportID ?? '', + oneOnOneIOUReport.reportID, undefined, undefined, undefined, @@ -1580,6 +1582,7 @@ function createSplitsAndOnyxData( [accountID]: { accountID, avatar: UserUtils.getDefaultAvatarURL(accountID), + // Disabling this line since participant.displayName can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing displayName: LocalePhoneNumber.formatPhoneNumber(participant.displayName || email), login: participant.login, @@ -1588,7 +1591,7 @@ function createSplitsAndOnyxData( } : {}; - let oneOnOneReportPreviewAction = ReportActionsUtils.getReportPreviewAction(oneOnOneChatReport.reportID, oneOnOneIOUReport?.reportID ?? ''); + let oneOnOneReportPreviewAction = ReportActionsUtils.getReportPreviewAction(oneOnOneChatReport.reportID, oneOnOneIOUReport.reportID); if (oneOnOneReportPreviewAction) { oneOnOneReportPreviewAction = ReportUtils.updateReportPreview(oneOnOneIOUReport, oneOnOneReportPreviewAction); } else { @@ -1627,7 +1630,7 @@ function createSplitsAndOnyxData( email, accountID, amount: splitAmount, - iouReportID: oneOnOneIOUReport?.reportID, + iouReportID: oneOnOneIOUReport.reportID, chatReportID: oneOnOneChatReport.reportID, transactionID: oneOnOneTransaction.transactionID, reportActionID: oneOnOneIOUAction.reportActionID, @@ -1952,6 +1955,7 @@ function startSplitBill( const splits: Split[] = [{email: currentUserEmailForIOUSplit, accountID: currentUserAccountID}]; participants.forEach((participant) => { + // Disabling this line since participant.login can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const email = participant.isOwnPolicyExpenseChat ? '' : OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login || participant.text || '').toLowerCase(); const accountID = participant.isOwnPolicyExpenseChat ? 0 : Number(participant.accountID); @@ -1977,8 +1981,10 @@ function startSplitBill( [accountID]: { accountID, avatar: UserUtils.getDefaultAvatarURL(accountID), + // Disabling this line since participant.displayName can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing displayName: LocalePhoneNumber.formatPhoneNumber(participant.displayName || email), + // Disabling this line since participant.login can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing login: participant.login || participant.text, isOptimisticPersonalDetail: true, @@ -3019,6 +3025,7 @@ function getSendMoneyParams( [recipientAccountID]: { accountID: recipientAccountID, avatar: UserUtils.getDefaultAvatarURL(recipient.accountID), + // Disabling this line since participant.displayName can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing displayName: recipient.displayName || recipient.login, login: recipient.login, @@ -3634,6 +3641,7 @@ function navigateToNextPage(iou: OnyxEntry, iouType: string, repo * Gets a report id from the first participant of the IOU object stored in Onyx. */ function getIOUReportID(iou?: OnyxTypes.IOU, route?: MoneyRequestRoute): string { + // Disabling this line for safeness as nullish coalescing works only if the value is undefined or null // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing return route?.params.reportID || iou?.participants?.[0]?.reportID || ''; } From 3fec0575686d919ac3e1b341e39dfb281fae9b43 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Tue, 9 Jan 2024 18:39:13 +0100 Subject: [PATCH 076/660] Fix comments --- src/components/SettlementButton.tsx | 15 +++++++-------- src/types/onyx/LastPaymentMethod.ts | 3 +++ src/types/onyx/index.ts | 2 ++ 3 files changed, 12 insertions(+), 8 deletions(-) create mode 100644 src/types/onyx/LastPaymentMethod.ts diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton.tsx index a11481c99cb1..1ad4cb496008 100644 --- a/src/components/SettlementButton.tsx +++ b/src/components/SettlementButton.tsx @@ -10,11 +10,10 @@ import * as BankAccounts from '@userActions/BankAccounts'; import * as IOU from '@userActions/IOU'; import * as PaymentMethods from '@userActions/PaymentMethods'; import CONST from '@src/CONST'; -import type {OnyxValues} from '@src/ONYXKEYS'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {ButtonSizeValue} from '@src/styles/utils/types'; -import type {Report} from '@src/types/onyx'; +import type {LastPaymentMethod, Report} from '@src/types/onyx'; import type AnchorAlignment from '@src/types/onyx/AnchorAlignment'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; @@ -22,15 +21,15 @@ import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; import * as Expensicons from './Icon/Expensicons'; import KYCWall from './KYCWall'; -type Event = GestureResponderEvent | KeyboardEvent; +type KYCFlowEvent = GestureResponderEvent | KeyboardEvent; -type TriggerKYCFlow = (event: Event, iouPaymentType: string) => void; +type TriggerKYCFlow = (event: KYCFlowEvent, iouPaymentType: string) => void; type PaymentType = DeepValueOf; type SettlementButtonOnyxProps = { /** The last payment method used per policy */ - nvpLastPaymentMethod?: OnyxEntry; + nvpLastPaymentMethod?: OnyxEntry; }; type SettlementButtonProps = SettlementButtonOnyxProps & { @@ -183,7 +182,7 @@ function SettlementButton({ return buttonOptions; }, [currency, formattedAmount, iouReport, nvpLastPaymentMethod, policyID, translate, shouldHidePaymentOptions, shouldShowApproveButton]); - const selectPaymentType = (event: Event, iouPaymentType: PaymentType, triggerKYCFlow: TriggerKYCFlow) => { + const selectPaymentType = (event: KYCFlowEvent, iouPaymentType: PaymentType, triggerKYCFlow: TriggerKYCFlow) => { if (iouPaymentType === CONST.IOU.PAYMENT_TYPE.EXPENSIFY || iouPaymentType === CONST.IOU.PAYMENT_TYPE.VBBA) { triggerKYCFlow(event, iouPaymentType); BankAccounts.setPersonalBankAccountContinueKYCOnSuccess(ROUTES.ENABLE_PAYMENTS); @@ -211,12 +210,12 @@ function SettlementButton({ anchorAlignment={kycWallAnchorAlignment} shouldShowPersonalBankAccountOption={shouldShowPersonalBankAccountOption} > - {(triggerKYCFlow: TriggerKYCFlow, buttonRef: MutableRefObject) => ( + {(triggerKYCFlow: TriggerKYCFlow, buttonRef: MutableRefObject) => ( selectPaymentType(event, iouPaymentType, triggerKYCFlow)} + onPress={(event: KYCFlowEvent, iouPaymentType: PaymentType) => selectPaymentType(event, iouPaymentType, triggerKYCFlow)} pressOnEnter={pressOnEnter} options={paymentButtonOptions} // @ts-expect-error TODO: Remove this once OptionsListUtils (https://github.com/Expensify/App/issues/25065) is migrated to TypeScript. diff --git a/src/types/onyx/LastPaymentMethod.ts b/src/types/onyx/LastPaymentMethod.ts new file mode 100644 index 000000000000..677a23fa9586 --- /dev/null +++ b/src/types/onyx/LastPaymentMethod.ts @@ -0,0 +1,3 @@ +type LastPaymentMethod = Record; + +export default LastPaymentMethod; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 7bd9c321be5e..824275ace170 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -15,6 +15,7 @@ import type FrequentlyUsedEmoji from './FrequentlyUsedEmoji'; import type {FundList} from './Fund'; import type Fund from './Fund'; import type IOU from './IOU'; +import type LastPaymentMethod from './LastPaymentMethod'; import type Locale from './Locale'; import type {LoginList} from './Login'; import type Login from './Login'; @@ -137,4 +138,5 @@ export type { ReportUserIsTyping, PolicyReportField, RecentlyUsedReportFields, + LastPaymentMethod, }; From 48b5419b3c0266f852c30c306e75ab5a316c6408 Mon Sep 17 00:00:00 2001 From: Cong Pham Date: Tue, 26 Dec 2023 09:38:00 +0700 Subject: [PATCH 077/660] 33546 visual viewport deplay --- src/components/ScreenWrapper.tsx | 2 +- src/hooks/useWindowDimensions/index.ts | 83 +++++++++++++++++++++++--- 2 files changed, 75 insertions(+), 10 deletions(-) diff --git a/src/components/ScreenWrapper.tsx b/src/components/ScreenWrapper.tsx index 0653e2ff8577..97bff2a6b3bb 100644 --- a/src/components/ScreenWrapper.tsx +++ b/src/components/ScreenWrapper.tsx @@ -118,7 +118,7 @@ function ScreenWrapper( */ const navigationFallback = useNavigation>(); const navigation = navigationProp ?? navigationFallback; - const {windowHeight, isSmallScreenWidth} = useWindowDimensions(); + const { windowHeight, isSmallScreenWidth } = useWindowDimensions(shouldEnableMaxHeight); const {initialHeight} = useInitialDimensions(); const styles = useThemeStyles(); const keyboardState = useKeyboardState(); diff --git a/src/hooks/useWindowDimensions/index.ts b/src/hooks/useWindowDimensions/index.ts index b0a29e9f901b..d0cc34701630 100644 --- a/src/hooks/useWindowDimensions/index.ts +++ b/src/hooks/useWindowDimensions/index.ts @@ -1,13 +1,21 @@ +import {useEffect, useMemo, useRef, useState} from 'react'; // eslint-disable-next-line no-restricted-imports import {Dimensions, useWindowDimensions} from 'react-native'; +import * as Browser from '@libs/Browser'; import variables from '@styles/variables'; import type WindowDimensions from './types'; +const initalViewportHeight = window.visualViewport?.height ?? window.innerHeight; +const tagNamesOpenKeyboard = ['INPUT', 'TEXTAREA']; + /** * A convenience wrapper around React Native's useWindowDimensions hook that also provides booleans for our breakpoints. */ -export default function (): WindowDimensions { +export default function (isCachedViewportHeight = false): WindowDimensions { + const shouldAwareVitualViewportHeight = isCachedViewportHeight && Browser.isMobileSafari(); + const cachedViewportHeightWithKeyboardRef = useRef(initalViewportHeight); const {width: windowWidth, height: windowHeight} = useWindowDimensions(); + // When the soft keyboard opens on mWeb, the window height changes. Use static screen height instead to get real screenHeight. const screenHeight = Dimensions.get('screen').height; const isExtraSmallScreenHeight = screenHeight <= variables.extraSmallMobileResponsiveHeightBreakpoint; @@ -15,12 +23,69 @@ export default function (): WindowDimensions { const isMediumScreenWidth = windowWidth > variables.mobileResponsiveWidthBreakpoint && windowWidth <= variables.tabletResponsiveWidthBreakpoint; const isLargeScreenWidth = windowWidth > variables.tabletResponsiveWidthBreakpoint; - return { - windowWidth, - windowHeight, - isExtraSmallScreenHeight, - isSmallScreenWidth, - isMediumScreenWidth, - isLargeScreenWidth, - }; + const [cachedViewportHeight, setCachedViewportHeight] = useState(windowHeight); + + const handleFocusIn = useRef((event: FocusEvent) => { + const targetElement = event.target as HTMLElement; + if (tagNamesOpenKeyboard.includes(targetElement.tagName)) { + setCachedViewportHeight(cachedViewportHeightWithKeyboardRef.current); + } + }); + + // eslint-disable-next-line rulesdir/prefer-early-return + useEffect(() => { + if (shouldAwareVitualViewportHeight) { + window.addEventListener('focusin', handleFocusIn.current); + return () => { + // eslint-disable-next-line react-hooks/exhaustive-deps + window.removeEventListener('focusin', handleFocusIn.current); + }; + } + }, [shouldAwareVitualViewportHeight]); + + const handleFocusOut = useRef((event: FocusEvent) => { + const targetElement = event.target as HTMLElement; + if (tagNamesOpenKeyboard.includes(targetElement.tagName)) { + setCachedViewportHeight(initalViewportHeight); + } + }); + + // eslint-disable-next-line rulesdir/prefer-early-return + useEffect(() => { + if (shouldAwareVitualViewportHeight) { + window.addEventListener('focusout', handleFocusOut.current); + return () => { + // eslint-disable-next-line react-hooks/exhaustive-deps + window.removeEventListener('focusout', handleFocusOut.current); + }; + } + }, [shouldAwareVitualViewportHeight]); + + // eslint-disable-next-line rulesdir/prefer-early-return + useEffect(() => { + if (shouldAwareVitualViewportHeight && windowHeight < cachedViewportHeightWithKeyboardRef.current) { + setCachedViewportHeight(windowHeight); + } + }, [windowHeight, shouldAwareVitualViewportHeight]); + + // eslint-disable-next-line rulesdir/prefer-early-return + useEffect(() => { + if (shouldAwareVitualViewportHeight && window.matchMedia('(orientation: portrait)').matches) { + if (windowHeight < initalViewportHeight) { + cachedViewportHeightWithKeyboardRef.current = windowHeight; + } + } + }, [shouldAwareVitualViewportHeight, windowHeight]); + + return useMemo( + () => ({ + windowWidth, + windowHeight: shouldAwareVitualViewportHeight ? cachedViewportHeight : windowHeight, + isExtraSmallScreenHeight, + isSmallScreenWidth, + isMediumScreenWidth, + isLargeScreenWidth, + }), + [windowWidth, shouldAwareVitualViewportHeight, cachedViewportHeight, windowHeight, isExtraSmallScreenHeight, isSmallScreenWidth, isMediumScreenWidth, isLargeScreenWidth], + ); } From c059ca3466198a648f3ead8a6ef83f1590b6d4fa Mon Sep 17 00:00:00 2001 From: VickyStash Date: Wed, 10 Jan 2024 10:44:50 +0100 Subject: [PATCH 078/660] Remove TransactionDraft type, update deleteMoneyRequest typing after merging main --- src/ONYXKEYS.ts | 2 +- src/libs/ViolationsUtils.ts | 4 +- src/libs/actions/IOU.ts | 276 ++++++++++++++++++---------------- src/types/onyx/Transaction.ts | 12 +- src/types/onyx/index.ts | 2 - 5 files changed, 152 insertions(+), 144 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 7b3d56ad7485..3049ed8978b8 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -458,7 +458,7 @@ type OnyxValues = { [ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM]: boolean; [ONYXKEYS.COLLECTION.SECURITY_GROUP]: OnyxTypes.SecurityGroup; [ONYXKEYS.COLLECTION.TRANSACTION]: OnyxTypes.Transaction; - [ONYXKEYS.COLLECTION.TRANSACTION_DRAFT]: OnyxTypes.TransactionDraft; + [ONYXKEYS.COLLECTION.TRANSACTION_DRAFT]: OnyxTypes.Transaction; [ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT]: OnyxTypes.Transaction; [ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS]: OnyxTypes.TransactionViolations; [ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS]: OnyxTypes.RecentlyUsedTags; diff --git a/src/libs/ViolationsUtils.ts b/src/libs/ViolationsUtils.ts index 9b2b2c5f6dda..d014030c7b3e 100644 --- a/src/libs/ViolationsUtils.ts +++ b/src/libs/ViolationsUtils.ts @@ -24,7 +24,7 @@ const ViolationsUtils = { if (policyRequiresCategories) { const hasCategoryOutOfPolicyViolation = transactionViolations.some((violation) => violation.name === 'categoryOutOfPolicy'); const hasMissingCategoryViolation = transactionViolations.some((violation) => violation.name === 'missingCategory'); - const isCategoryInPolicy = Boolean(policyCategories[transaction.category]?.enabled); + const isCategoryInPolicy = Boolean(policyCategories[transaction.category ?? '']?.enabled); // Add 'categoryOutOfPolicy' violation if category is not in policy if (!hasCategoryOutOfPolicyViolation && transaction.category && !isCategoryInPolicy) { @@ -50,7 +50,7 @@ const ViolationsUtils = { if (policyRequiresTags) { const hasTagOutOfPolicyViolation = transactionViolations.some((violation) => violation.name === 'tagOutOfPolicy'); const hasMissingTagViolation = transactionViolations.some((violation) => violation.name === 'missingTag'); - const isTagInPolicy = Boolean(policyTags[transaction.tag]?.enabled); + const isTagInPolicy = Boolean(policyTags[transaction.tag ?? '']?.enabled); // Add 'tagOutOfPolicy' violation if tag is not in policy if (!hasTagOutOfPolicyViolation && transaction.tag && !isTagInPolicy) { diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 41c3b4d28d8e..0602732e4965 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -164,7 +164,7 @@ Onyx.connect({ }, }); -let allTransactionDrafts: Record> = {}; +let allTransactionDrafts: Record> = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, waitForCollectionCallback: true, @@ -768,7 +768,7 @@ function getMoneyRequestInformation( // to remind me to do this. const existingTransaction = allTransactionDrafts[`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${CONST.IOU.OPTIMISTIC_TRANSACTION_ID}`]; if (existingTransaction && existingTransaction.iouRequestType === CONST.IOU.REQUEST_TYPE.DISTANCE) { - optimisticTransaction = OnyxUtils.fastMerge(existingTransaction, optimisticTransaction) as OnyxTypes.Transaction; + optimisticTransaction = OnyxUtils.fastMerge(existingTransaction, optimisticTransaction); } // STEP 4: Build optimistic reportActions. We need: @@ -2603,117 +2603,125 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor iouReportLastMessageText.length === 0 && !ReportActionsUtils.isDeletedParentAction(lastVisibleAction) && (!transactionThreadID || shouldDeleteTransactionThread); // STEP 4: Update the iouReport and reportPreview with new totals and messages if it wasn't deleted - let updatedIOUReport = {...iouReport}; - const updatedReportPreviewAction = {...reportPreviewAction}; + let updatedIOUReport: OnyxTypes.Report | null; + const updatedReportPreviewAction: OnyxTypes.ReportAction | EmptyObject = {...reportPreviewAction}; updatedReportPreviewAction.pendingAction = shouldDeleteIOUReport ? CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE : CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE; - if (ReportUtils.isExpenseReport(iouReport)) { + if (iouReport && ReportUtils.isExpenseReport(iouReport)) { updatedIOUReport = {...iouReport}; - // Because of the Expense reports are stored as negative values, we add the total from the amount - updatedIOUReport.total += TransactionUtils.getAmount(transaction, true); + if (typeof updatedIOUReport.total === 'number') { + // Because of the Expense reports are stored as negative values, we add the total from the amount + updatedIOUReport.total += TransactionUtils.getAmount(transaction, true); + } } else { updatedIOUReport = IOUUtils.updateIOUOwnerAndTotal( iouReport, - reportAction.actorAccountID, + reportAction.actorAccountID ?? -1, TransactionUtils.getAmount(transaction, false), TransactionUtils.getCurrency(transaction), true, ); } - updatedIOUReport.lastMessageText = iouReportLastMessageText; - updatedIOUReport.lastVisibleActionCreated = lodashGet(lastVisibleAction, 'created'); + if (updatedIOUReport) { + updatedIOUReport.lastMessageText = iouReportLastMessageText; + updatedIOUReport.lastVisibleActionCreated = lastVisibleAction?.created; + } - const hasNonReimbursableTransactions = ReportUtils.hasNonReimbursableTransactions(iouReport); + const hasNonReimbursableTransactions = ReportUtils.hasNonReimbursableTransactions(iouReport?.reportID); const messageText = Localize.translateLocal(hasNonReimbursableTransactions ? 'iou.payerSpentAmount' : 'iou.payerOwesAmount', { - payer: ReportUtils.getPersonalDetailsForAccountID(updatedIOUReport.managerID).login || '', - amount: CurrencyUtils.convertToDisplayString(updatedIOUReport.total, updatedIOUReport.currency), + payer: ReportUtils.getPersonalDetailsForAccountID(updatedIOUReport?.managerID ?? -1).login ?? '', + amount: CurrencyUtils.convertToDisplayString(updatedIOUReport?.total, updatedIOUReport?.currency), }); - updatedReportPreviewAction.message[0].text = messageText; - updatedReportPreviewAction.message[0].html = messageText; - if (reportPreviewAction.childMoneyRequestCount > 0) { + if (updatedReportPreviewAction?.message?.[0]) { + updatedReportPreviewAction.message[0].text = messageText; + updatedReportPreviewAction.message[0].html = messageText; + } + + if (updatedReportPreviewAction && reportPreviewAction?.childMoneyRequestCount && reportPreviewAction?.childMoneyRequestCount > 0) { updatedReportPreviewAction.childMoneyRequestCount = reportPreviewAction.childMoneyRequestCount - 1; } // STEP 5: Build Onyx data - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, value: null, }, - ...(Permissions.canUseViolations(betas) - ? [ - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`, - value: null, - }, - ] - : []), - ...(shouldDeleteTransactionThread - ? [ - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadID}`, - value: null, - }, - ] - : []), + ]; + + if (Permissions.canUseViolations(betas)) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`, + value: null, + }); + } + + if (shouldDeleteTransactionThread) { + optimisticData.push( + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadID}`, + value: null, + }, + ); + } + + optimisticData.push( { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport?.reportID}`, value: updatedReportAction, }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, - value: updatedIOUReport, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, + value: updatedIOUReport ?? {}, }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport?.reportID}`, value: { - [reportPreviewAction.reportActionID]: updatedReportPreviewAction, + [reportPreviewAction?.reportActionID ?? '']: updatedReportPreviewAction, }, }, - ...(!shouldDeleteIOUReport && updatedReportPreviewAction.childMoneyRequestCount === 0 - ? [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, - value: { - hasOutstandingChildRequest: false, - }, - }, - ] - : []), - ...(shouldDeleteIOUReport - ? [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, - value: { - hasOutstandingChildRequest: false, - iouReportID: null, - lastMessageText: ReportActionsUtils.getLastVisibleMessage(iouReport.chatReportID, {[reportPreviewAction.reportActionID]: null}).lastMessageText, - lastVisibleActionCreated: lodashGet(ReportActionsUtils.getLastVisibleAction(iouReport.chatReportID, {[reportPreviewAction.reportActionID]: null}), 'created'), - }, - }, - ] - : []), - ]; + ); - const successData = [ + if (!shouldDeleteIOUReport && updatedReportPreviewAction.childMoneyRequestCount === 0) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport?.reportID}`, + value: { + hasOutstandingChildRequest: false, + }, + }); + } + + if (shouldDeleteIOUReport) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport?.reportID}`, + value: { + hasOutstandingChildRequest: false, + iouReportID: null, + lastMessageText: ReportActionsUtils.getLastVisibleMessage(iouReport?.chatReportID ?? '', {[reportPreviewAction?.reportActionID ?? '']: null})?.lastMessageText, + lastVisibleActionCreated: ReportActionsUtils.getLastVisibleAction(iouReport?.chatReportID ?? '', {[reportPreviewAction?.reportActionID ?? '']: null})?.created, + }, + }); + } + + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport?.reportID}`, value: { [reportAction.reportActionID]: shouldDeleteIOUReport ? null @@ -2724,9 +2732,9 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport?.reportID}`, value: { - [reportPreviewAction.reportActionID]: shouldDeleteIOUReport + [reportPreviewAction?.reportActionID ?? '']: shouldDeleteIOUReport ? null : { pendingAction: null, @@ -2734,44 +2742,44 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor }, }, }, - ...(shouldDeleteIOUReport - ? [ - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, - value: null, - }, - ] - : []), ]; - const failureData = [ + if (shouldDeleteIOUReport) { + successData.push({ + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, + value: null, + }); + } + + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, value: transaction, }, - ...(Permissions.canUseViolations(betas) - ? [ - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`, - value: transactionViolations, - }, - ] - : []), - ...(shouldDeleteTransactionThread - ? [ - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`, - value: transactionThread, - }, - ] - : []), + ]; + + if (Permissions.canUseViolations(betas)) { + failureData.push({ + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`, + value: transactionViolations, + }); + } + + if (shouldDeleteTransactionThread) { + failureData.push({ + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${transactionThreadID}`, + value: transactionThread, + }); + } + + failureData.push( { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport?.reportID}`, value: { [reportAction.reportActionID]: { ...reportAction, @@ -2779,42 +2787,46 @@ function deleteMoneyRequest(transactionID: string, reportAction: OnyxTypes.Repor }, }, }, - { - onyxMethod: shouldDeleteIOUReport ? Onyx.METHOD.SET : Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, - value: iouReport, - }, + shouldDeleteIOUReport + ? { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, + value: iouReport, + } + : { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport?.reportID}`, + value: iouReport ?? {}, + }, { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${chatReport?.reportID}`, value: { - [reportPreviewAction.reportActionID]: { + [reportPreviewAction?.reportActionID ?? '']: { ...reportPreviewAction, errors: ErrorUtils.getMicroSecondOnyxError('iou.error.genericDeleteFailureMessage'), }, }, }, - ...(shouldDeleteIOUReport - ? [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, - value: chatReport, - }, - ] - : []), - ...(!shouldDeleteIOUReport && updatedReportPreviewAction.childMoneyRequestCount === 0 - ? [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, - value: { - hasOutstandingChildRequest: true, - }, - }, - ] - : []), - ]; + ); + + if (chatReport && shouldDeleteIOUReport) { + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, + value: chatReport, + }); + } + + if (!shouldDeleteIOUReport && updatedReportPreviewAction.childMoneyRequestCount === 0) { + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport?.reportID}`, + value: { + hasOutstandingChildRequest: true, + }, + }); + } type DeleteMoneyRequestParams = { transactionID: string; diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 6fdd8d309408..7117158216e4 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -74,10 +74,10 @@ type Transaction = { amount: number; /** Whether the request is billable */ - billable: boolean; + billable?: boolean; /** The category name */ - category: string; + category?: string; /** The comment object on the transaction */ comment: Comment; @@ -128,7 +128,7 @@ type Transaction = { participants?: Participant[]; /** The type of action that's pending */ - pendingAction: OnyxCommon.PendingAction; + pendingAction?: OnyxCommon.PendingAction; /** The receipt object associated with the transaction */ receipt?: Receipt; @@ -143,7 +143,7 @@ type Transaction = { transactionID: string; /** The transaction tag */ - tag: string; + tag?: string; /** Whether the transaction was created globally */ isFromGlobalCreate?: boolean; @@ -190,8 +190,6 @@ type Transaction = { isLoading?: boolean; }; -type TransactionDraft = Partial; - type AdditionalTransactionChanges = { comment?: string; waypoints?: WaypointCollection; @@ -202,4 +200,4 @@ type AdditionalTransactionChanges = { type TransactionChanges = Partial & AdditionalTransactionChanges; export default Transaction; -export type {WaypointCollection, Comment, Receipt, Waypoint, TransactionDraft, TransactionChanges, TaxRate}; +export type {WaypointCollection, Comment, Receipt, Waypoint, TransactionChanges, TaxRate}; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index b17dd5b7bafc..9fd52bd0628e 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -55,7 +55,6 @@ import type SecurityGroup from './SecurityGroup'; import type Session from './Session'; import type Task from './Task'; import type Transaction from './Transaction'; -import type {TransactionDraft} from './Transaction'; import type {TransactionViolation, TransactionViolations, ViolationName} from './TransactionViolation'; import type User from './User'; import type UserLocation from './UserLocation'; @@ -125,7 +124,6 @@ export type { Session, Task, Transaction, - TransactionDraft, TransactionViolation, TransactionViolations, User, From 1c1fa2638917145e489f91ea80571b294a0e78df Mon Sep 17 00:00:00 2001 From: staszekscp Date: Wed, 10 Jan 2024 10:58:13 +0100 Subject: [PATCH 079/660] fix patch --- patches/expo-modules-core+1.5.12.patch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/patches/expo-modules-core+1.5.12.patch b/patches/expo-modules-core+1.5.12.patch index 93383f04440c..9025a7c42d9f 100644 --- a/patches/expo-modules-core+1.5.12.patch +++ b/patches/expo-modules-core+1.5.12.patch @@ -1,12 +1,12 @@ diff --git a/node_modules/expo-modules-core/android/build.gradle b/node_modules/expo-modules-core/android/build.gradle -index 8f9e0dc..539a201 100644 +index 8f9e0dc..af5b5d6 100644 --- a/node_modules/expo-modules-core/android/build.gradle +++ b/node_modules/expo-modules-core/android/build.gradle @@ -54,9 +54,10 @@ def downloadsDir = customDownloadsDir ? new File(customDownloadsDir) : new File( def thirdPartyNdkDir = new File("$buildDir/third-party-ndk") def REACT_NATIVE_BUILD_FROM_SOURCE = findProject(":packages:react-native:ReactAndroid") != null -+def FALLBACK_REACT_NATIVE_DIR = ext.has("customRNProjectPath") ? ext.customRNProjectPath : new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).parent) ++def FALLBACK_REACT_NATIVE_DIR = hasProperty("reactNativeProject") ? file('../../react-native') : new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).parent def REACT_NATIVE_DIR = REACT_NATIVE_BUILD_FROM_SOURCE ? findProject(":packages:react-native:ReactAndroid").getProjectDir().parent - : new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).parent From 5754adc74b3158b0c0d63a74999974f6cf709dc7 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Wed, 10 Jan 2024 11:34:14 +0100 Subject: [PATCH 080/660] Update type imports --- src/components/Popover/types.ts | 2 +- src/components/PopoverMenu.tsx | 2 +- src/components/ProcessMoneyRequestHoldMenu.tsx | 2 +- src/components/SettlementButton.tsx | 3 +-- src/types/onyx/index.ts | 2 ++ 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/Popover/types.ts b/src/components/Popover/types.ts index 4875676a3caa..298c95eeca76 100644 --- a/src/components/Popover/types.ts +++ b/src/components/Popover/types.ts @@ -1,7 +1,7 @@ import type {PopoverAnchorPosition} from '@components/Modal/types'; import type BaseModalProps from '@components/Modal/types'; import type {WindowDimensionsProps} from '@components/withWindowDimensions/types'; -import type AnchorAlignment from '@src/types/onyx/AnchorAlignment'; +import type {AnchorAlignment} from '@src/types/onyx'; type PopoverDimensions = { width: number; diff --git a/src/components/PopoverMenu.tsx b/src/components/PopoverMenu.tsx index 9b59b87056e0..0a1fdf38a172 100644 --- a/src/components/PopoverMenu.tsx +++ b/src/components/PopoverMenu.tsx @@ -10,7 +10,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import CONST from '@src/CONST'; import type {AnchorPosition} from '@src/styles'; -import type AnchorAlignment from '@src/types/onyx/AnchorAlignment'; +import type {AnchorAlignment} from '@src/types/onyx'; import MenuItem from './MenuItem'; import PopoverWithMeasuredContent from './PopoverWithMeasuredContent'; import Text from './Text'; diff --git a/src/components/ProcessMoneyRequestHoldMenu.tsx b/src/components/ProcessMoneyRequestHoldMenu.tsx index a6b53da467b5..1fd37b82afc8 100644 --- a/src/components/ProcessMoneyRequestHoldMenu.tsx +++ b/src/components/ProcessMoneyRequestHoldMenu.tsx @@ -2,7 +2,7 @@ import React from 'react'; import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import type AnchorAlignment from '@src/types/onyx/AnchorAlignment'; +import type {AnchorAlignment} from '@src/types/onyx'; import Button from './Button'; import HoldMenuSectionList from './HoldMenuSectionList'; import type {PopoverAnchorPosition} from './Modal/types'; diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton.tsx index 1ad4cb496008..2fe45e6f7b90 100644 --- a/src/components/SettlementButton.tsx +++ b/src/components/SettlementButton.tsx @@ -13,8 +13,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {ButtonSizeValue} from '@src/styles/utils/types'; -import type {LastPaymentMethod, Report} from '@src/types/onyx'; -import type AnchorAlignment from '@src/types/onyx/AnchorAlignment'; +import type {AnchorAlignment, LastPaymentMethod, Report} from '@src/types/onyx'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 824275ace170..ecc3603e8f8d 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -1,5 +1,6 @@ import type Account from './Account'; import type AccountData from './AccountData'; +import type AnchorAlignment from './AnchorAlignment'; import type {BankAccountList} from './BankAccount'; import type BankAccount from './BankAccount'; import type Beta from './Beta'; @@ -139,4 +140,5 @@ export type { PolicyReportField, RecentlyUsedReportFields, LastPaymentMethod, + AnchorAlignment, }; From ef7487ab74bc146b82e40dcbe69dd6875cb617dc Mon Sep 17 00:00:00 2001 From: Yauheni Date: Wed, 10 Jan 2024 14:24:13 +0100 Subject: [PATCH 081/660] Fix comments --- src/ONYXKEYS.ts | 4 +++- src/components/SettlementButton.tsx | 7 +++++-- src/types/onyx/LastPaymentMethod.ts | 3 --- src/types/onyx/index.ts | 2 -- 4 files changed, 8 insertions(+), 8 deletions(-) delete mode 100644 src/types/onyx/LastPaymentMethod.ts diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 89ddbdc06883..d3c7756b0dee 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -530,5 +530,7 @@ type OnyxValues = { type OnyxKeyValue = OnyxEntry; +type LastPaymentMethod = Record; + export default ONYXKEYS; -export type {OnyxKey, OnyxCollectionKey, OnyxValues, OnyxKeyValue, OnyxFormKey, OnyxKeysMap}; +export type {OnyxKey, OnyxCollectionKey, OnyxValues, OnyxKeyValue, OnyxFormKey, OnyxKeysMap,LastPaymentMethod}; diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton.tsx index 2fe45e6f7b90..1fae611018c1 100644 --- a/src/components/SettlementButton.tsx +++ b/src/components/SettlementButton.tsx @@ -10,10 +10,11 @@ import * as BankAccounts from '@userActions/BankAccounts'; import * as IOU from '@userActions/IOU'; import * as PaymentMethods from '@userActions/PaymentMethods'; import CONST from '@src/CONST'; +import type {LastPaymentMethod} from '@src/ONYXKEYS'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {ButtonSizeValue} from '@src/styles/utils/types'; -import type {AnchorAlignment, LastPaymentMethod, Report} from '@src/types/onyx'; +import type {AnchorAlignment, Report} from '@src/types/onyx'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; @@ -26,6 +27,8 @@ type TriggerKYCFlow = (event: KYCFlowEvent, iouPaymentType: string) => void; type PaymentType = DeepValueOf; +type EnablePaymentsRoute = typeof ROUTES.ENABLE_PAYMENTS | typeof ROUTES.IOU_SEND_ENABLE_PAYMENTS | typeof ROUTES.SETTINGS_ENABLE_PAYMENTS; + type SettlementButtonOnyxProps = { /** The last payment method used per policy */ nvpLastPaymentMethod?: OnyxEntry; @@ -36,7 +39,7 @@ type SettlementButtonProps = SettlementButtonOnyxProps & { onPress: (paymentType: PaymentType) => void; /** The route to redirect if user does not have a payment method setup */ - enablePaymentsRoute: typeof ROUTES.ENABLE_PAYMENTS | typeof ROUTES.IOU_SEND_ENABLE_PAYMENTS | typeof ROUTES.SETTINGS_ENABLE_PAYMENTS; + enablePaymentsRoute: EnablePaymentsRoute; /** Call the onPress function on main button when Enter key is pressed */ pressOnEnter?: boolean; diff --git a/src/types/onyx/LastPaymentMethod.ts b/src/types/onyx/LastPaymentMethod.ts deleted file mode 100644 index 677a23fa9586..000000000000 --- a/src/types/onyx/LastPaymentMethod.ts +++ /dev/null @@ -1,3 +0,0 @@ -type LastPaymentMethod = Record; - -export default LastPaymentMethod; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index ecc3603e8f8d..4e199e88ce88 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -16,7 +16,6 @@ import type FrequentlyUsedEmoji from './FrequentlyUsedEmoji'; import type {FundList} from './Fund'; import type Fund from './Fund'; import type IOU from './IOU'; -import type LastPaymentMethod from './LastPaymentMethod'; import type Locale from './Locale'; import type {LoginList} from './Login'; import type Login from './Login'; @@ -139,6 +138,5 @@ export type { ReportUserIsTyping, PolicyReportField, RecentlyUsedReportFields, - LastPaymentMethod, AnchorAlignment, }; From ebfb6b91b3990076bf89c48b1c88513e53c6f585 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Wed, 10 Jan 2024 16:32:50 +0100 Subject: [PATCH 082/660] Update createdChatReportActionID and createdIOUReportActionID type, update TODO comment --- src/libs/actions/IOU.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 0602732e4965..80ca05ffb08a 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -66,8 +66,8 @@ type MoneyRequestInformation = { chatReport: OnyxTypes.Report; transaction: OnyxTypes.Transaction; iouAction: OptimisticIOUReportAction; - createdChatReportActionID: string | number; - createdIOUReportActionID: string | number; + createdChatReportActionID: string; + createdIOUReportActionID: string; reportPreviewAction: OnyxTypes.ReportAction; onyxData: OnyxData; }; @@ -755,10 +755,10 @@ function getMoneyRequestInformation( billable, ); - // TODO: Remove this type once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. + // TODO: Remove assertion once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories(iouReport.policyID, category) as OptimisticPolicyRecentlyUsedCategories; - // TODO: Remove this type once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. + // TODO: Remove assertion once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. const optimisticPolicyRecentlyUsedTags = Policy.buildOptimisticPolicyRecentlyUsedTags(iouReport.policyID, tag) as OptimisticPolicyRecentlyUsedTags; // If there is an existing transaction (which is the case for distance requests), then the data from the existing transaction @@ -854,8 +854,8 @@ function getMoneyRequestInformation( chatReport, transaction: optimisticTransaction, iouAction, - createdChatReportActionID: isNewChatReport ? optimisticCreatedActionForChat.reportActionID : 0, - createdIOUReportActionID: isNewIOUReport ? optimisticCreatedActionForIOU.reportActionID : 0, + createdChatReportActionID: isNewChatReport ? optimisticCreatedActionForChat.reportActionID : '0', + createdIOUReportActionID: isNewIOUReport ? optimisticCreatedActionForIOU.reportActionID : '0', reportPreviewAction, onyxData: { optimisticData, @@ -916,8 +916,8 @@ function createDistanceRequest( chatReportID: string; transactionID: string; reportActionID: string; - createdChatReportActionID: string | number; - createdIOUReportActionID: string | number; + createdChatReportActionID: string; + createdIOUReportActionID: string; reportPreviewReportActionID: string; waypoints: string; created: string; @@ -1247,8 +1247,8 @@ function requestMoney( chatReportID: string; transactionID: string; reportActionID: string; - createdChatReportActionID: string | number; - createdIOUReportActionID: string | number; + createdChatReportActionID: string; + createdIOUReportActionID: string; reportPreviewReportActionID: string; receipt: Receipt; receiptState?: ValueOf; From 5ae984d2c65f9a9ff2f510adab244f43fb3f72f0 Mon Sep 17 00:00:00 2001 From: Cong Pham Date: Wed, 10 Jan 2024 22:30:34 +0700 Subject: [PATCH 083/660] 33546 cleanup prefer-early-return --- src/components/ScreenWrapper.tsx | 2 +- src/hooks/useWindowDimensions/index.ts | 73 ++++++++++++-------------- 2 files changed, 35 insertions(+), 40 deletions(-) diff --git a/src/components/ScreenWrapper.tsx b/src/components/ScreenWrapper.tsx index 97bff2a6b3bb..5b1ad95b6554 100644 --- a/src/components/ScreenWrapper.tsx +++ b/src/components/ScreenWrapper.tsx @@ -118,7 +118,7 @@ function ScreenWrapper( */ const navigationFallback = useNavigation>(); const navigation = navigationProp ?? navigationFallback; - const { windowHeight, isSmallScreenWidth } = useWindowDimensions(shouldEnableMaxHeight); + const {windowHeight, isSmallScreenWidth} = useWindowDimensions(shouldEnableMaxHeight); const {initialHeight} = useInitialDimensions(); const styles = useThemeStyles(); const keyboardState = useKeyboardState(); diff --git a/src/hooks/useWindowDimensions/index.ts b/src/hooks/useWindowDimensions/index.ts index d0cc34701630..0b805202ede8 100644 --- a/src/hooks/useWindowDimensions/index.ts +++ b/src/hooks/useWindowDimensions/index.ts @@ -11,8 +11,8 @@ const tagNamesOpenKeyboard = ['INPUT', 'TEXTAREA']; /** * A convenience wrapper around React Native's useWindowDimensions hook that also provides booleans for our breakpoints. */ -export default function (isCachedViewportHeight = false): WindowDimensions { - const shouldAwareVitualViewportHeight = isCachedViewportHeight && Browser.isMobileSafari(); +export default function (useCachedViewportHeight = false): WindowDimensions { + const isCachedViewportHeight = useCachedViewportHeight && Browser.isMobileSafari(); const cachedViewportHeightWithKeyboardRef = useRef(initalViewportHeight); const {width: windowWidth, height: windowHeight} = useWindowDimensions(); @@ -32,16 +32,16 @@ export default function (isCachedViewportHeight = false): WindowDimensions { } }); - // eslint-disable-next-line rulesdir/prefer-early-return useEffect(() => { - if (shouldAwareVitualViewportHeight) { - window.addEventListener('focusin', handleFocusIn.current); - return () => { - // eslint-disable-next-line react-hooks/exhaustive-deps - window.removeEventListener('focusin', handleFocusIn.current); - }; + if (!isCachedViewportHeight) { + return; } - }, [shouldAwareVitualViewportHeight]); + window.addEventListener('focusin', handleFocusIn.current); + return () => { + // eslint-disable-next-line react-hooks/exhaustive-deps + window.removeEventListener('focusin', handleFocusIn.current); + }; + }, [isCachedViewportHeight]); const handleFocusOut = useRef((event: FocusEvent) => { const targetElement = event.target as HTMLElement; @@ -50,42 +50,37 @@ export default function (isCachedViewportHeight = false): WindowDimensions { } }); - // eslint-disable-next-line rulesdir/prefer-early-return useEffect(() => { - if (shouldAwareVitualViewportHeight) { - window.addEventListener('focusout', handleFocusOut.current); - return () => { - // eslint-disable-next-line react-hooks/exhaustive-deps - window.removeEventListener('focusout', handleFocusOut.current); - }; + if (!isCachedViewportHeight) { + return; } - }, [shouldAwareVitualViewportHeight]); + window.addEventListener('focusout', handleFocusOut.current); + return () => { + // eslint-disable-next-line react-hooks/exhaustive-deps + window.removeEventListener('focusout', handleFocusOut.current); + }; + }, [isCachedViewportHeight]); - // eslint-disable-next-line rulesdir/prefer-early-return useEffect(() => { - if (shouldAwareVitualViewportHeight && windowHeight < cachedViewportHeightWithKeyboardRef.current) { - setCachedViewportHeight(windowHeight); + if (!isCachedViewportHeight && windowHeight >= cachedViewportHeightWithKeyboardRef.current) { + return; } - }, [windowHeight, shouldAwareVitualViewportHeight]); + setCachedViewportHeight(windowHeight); + }, [windowHeight, isCachedViewportHeight]); - // eslint-disable-next-line rulesdir/prefer-early-return useEffect(() => { - if (shouldAwareVitualViewportHeight && window.matchMedia('(orientation: portrait)').matches) { - if (windowHeight < initalViewportHeight) { - cachedViewportHeightWithKeyboardRef.current = windowHeight; - } + if (!isCachedViewportHeight || !window.matchMedia('(orientation: portrait)').matches || windowHeight >= initalViewportHeight) { + return; } - }, [shouldAwareVitualViewportHeight, windowHeight]); + cachedViewportHeightWithKeyboardRef.current = windowHeight; + }, [isCachedViewportHeight, windowHeight]); - return useMemo( - () => ({ - windowWidth, - windowHeight: shouldAwareVitualViewportHeight ? cachedViewportHeight : windowHeight, - isExtraSmallScreenHeight, - isSmallScreenWidth, - isMediumScreenWidth, - isLargeScreenWidth, - }), - [windowWidth, shouldAwareVitualViewportHeight, cachedViewportHeight, windowHeight, isExtraSmallScreenHeight, isSmallScreenWidth, isMediumScreenWidth, isLargeScreenWidth], - ); + return { + windowWidth, + windowHeight: isCachedViewportHeight ? cachedViewportHeight : windowHeight, + isExtraSmallScreenHeight, + isSmallScreenWidth, + isMediumScreenWidth, + isLargeScreenWidth, + }; } From 185f26b4f678a1533dc774bf4e7a9ac551b901da Mon Sep 17 00:00:00 2001 From: VickyStash Date: Wed, 10 Jan 2024 17:11:14 +0100 Subject: [PATCH 084/660] Minor code improvements --- src/libs/actions/IOU.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 80ca05ffb08a..6a60a27f9b1b 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -368,8 +368,8 @@ function buildOnyxDataForMoneyRequest( isNewChatReport: boolean, isNewIOUReport: boolean, policy?: OnyxTypes.Policy | EmptyObject, - policyTags: OnyxTypes.PolicyTags = {}, - policyCategories: OnyxTypes.PolicyCategories = {}, + policyTags?: OnyxTypes.PolicyTags, + policyCategories?: OnyxTypes.PolicyCategories, hasOutstandingChildRequest = false, ): OnyxUpdate[][] { const isScanRequest = TransactionUtils.isScanRequest(transaction); @@ -634,7 +634,7 @@ function buildOnyxDataForMoneyRequest( return [optimisticData, successData, failureData]; } - const violationsOnyxData = ViolationsUtils.getViolationsOnyxData(transaction, [], !!policy.requiresTag, policyTags, !!policy.requiresCategory, policyCategories); + const violationsOnyxData = ViolationsUtils.getViolationsOnyxData(transaction, [], !!policy.requiresTag, policyTags ?? {}, !!policy.requiresCategory, policyCategories ?? {}); if (violationsOnyxData) { optimisticData.push(violationsOnyxData); @@ -706,7 +706,7 @@ function getMoneyRequestInformation( isFromPaidPolicy = PolicyUtils.isPaidGroupPolicy(policy ?? null); // If the scheduled submit is turned off on the policy, user needs to manually submit the report which is indicated by GBR in LHN - needsToBeManuallySubmitted = isFromPaidPolicy && !(!!policy?.isHarvestingEnabled || false); + needsToBeManuallySubmitted = isFromPaidPolicy && !policy?.isHarvestingEnabled; // If the linked expense report on paid policy is not draft, we need to create a new draft expense report if (iouReport && isFromPaidPolicy && !ReportUtils.isDraftExpenseReport(iouReport)) { @@ -1804,7 +1804,7 @@ function startSplitBill( ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${existingSplitChatReportID || participants[0].reportID}`] : ReportUtils.getChatByParticipants(participantAccountIDs); const splitChatReport = existingSplitChatReport ?? ReportUtils.buildOptimisticChatReport(participantAccountIDs); - const isOwnPolicyExpenseChat = !!splitChatReport.isOwnPolicyExpenseChat || false; + const isOwnPolicyExpenseChat = !!splitChatReport.isOwnPolicyExpenseChat; const {name: filename, source, state = CONST.IOU.RECEIPT_STATE.SCANREADY} = receipt; const receiptObject: Receipt = {state, source}; From 9a0471b3f43715d78ef2f17fb038e77b1f560487 Mon Sep 17 00:00:00 2001 From: rayane-djouah <77965000+rayane-djouah@users.noreply.github.com> Date: Wed, 10 Jan 2024 17:20:20 +0100 Subject: [PATCH 085/660] Create Automation for when main fail --- .github/workflows/failureNotifier.yml | 108 ++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 .github/workflows/failureNotifier.yml diff --git a/.github/workflows/failureNotifier.yml b/.github/workflows/failureNotifier.yml new file mode 100644 index 000000000000..794c472994f3 --- /dev/null +++ b/.github/workflows/failureNotifier.yml @@ -0,0 +1,108 @@ +name: Notify on Workflow Failure + +on: + workflow_run: + workflows: ["Process new code merged to main"] + types: + - completed + +jobs: + notify-failure: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'failure' }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Fetch Workflow Run Details + id: fetch-workflow-details + uses: actions/github-script@v7 + with: + script: | + const runId = "${{ github.event.workflow_run.id }}"; + const runData = await github.rest.actions.getWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: runId + }); + return runData.data; + + - name: Fetch Workflow Run Jobs + id: fetch-workflow-jobs + uses: actions/github-script@v7 + with: + script: | + const runId = "${{ github.event.workflow_run.id }}"; + const jobsData = await github.rest.actions.listJobsForWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: runId, + }); + return jobsData.data; + + - name: Get merged pull request number + id: getMergedPullRequestNumber + uses: actions-ecosystem/action-get-merged-pull-request@59afe90821bb0b555082ce8ff1e36b03f91553d9 + with: + github_token: ${{ github.token }} + + + - name: Process Each Failed Job + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.OS_BOTIFY_TOKEN }} + script: | + const prNumber = ${{ steps.getMergedPullRequestNumber.outputs.number }}; + const runData = ${{fromJSON(steps.fetch-workflow-details.outputs.result)}}; + const jobs = ${{fromJSON(steps.fetch-workflow-jobs.outputs.result)}}; + const failureLabel = 'workflow-failure'; + + const prData = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber, + }); + + const pr = prData.data; + + const prLink = pr.html_url; + const prAuthor = pr.user.login; + const prMerger = pr.merged_by.login; + + for (let i=0; i issue.title.includes(jobName)); + if (!existingIssue) { + const issueTitle = `🔍 Investigation Needed: ${jobName} Failure due to PR Merge 🔍`; + const issueBody = `🚨 **Failure Summary** 🚨:\n\n + - **📋 Job Name**: [${jobName}](${jobLink})\n + - **🔧 Failure in Workflow**: Main branch\n + - **🔗 Triggered by PR**: [PR Link](${prLink})\n + - **👤 PR Author**: @${prAuthor}\n + - **🤝 Merged by**: @${prMerger}\n\n + ⚠️ **Action Required** ⚠️:\n\n + 🛠️ A recent merge appears to have caused a failure in the job named [${jobName}](${jobLink}). + This issue has been automatically created and labeled with \`${failureLabel}\` for investigation. \n\n + 👀 **Please look into the following**:\n + 1. **Why the PR caused the job to fail?**\n + 2. **Address any underlying issues.**\n\n + 🐛 We appreciate your help in squashing this bug!`; + github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: issueTitle, + body: issueBody, + labels: [failureLabel], + assignees: [prMerger, prAuthor] + }); + } + } + } From 1064c3248a590dd44c11e6d3de27f73b8769d950 Mon Sep 17 00:00:00 2001 From: Cong Pham Date: Wed, 10 Jan 2024 23:23:20 +0700 Subject: [PATCH 086/660] fix eslint --- src/hooks/useWindowDimensions/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useWindowDimensions/index.ts b/src/hooks/useWindowDimensions/index.ts index 0b805202ede8..4ba2c4ad9b41 100644 --- a/src/hooks/useWindowDimensions/index.ts +++ b/src/hooks/useWindowDimensions/index.ts @@ -1,4 +1,4 @@ -import {useEffect, useMemo, useRef, useState} from 'react'; +import {useEffect, useRef, useState} from 'react'; // eslint-disable-next-line no-restricted-imports import {Dimensions, useWindowDimensions} from 'react-native'; import * as Browser from '@libs/Browser'; From 0ce489bf90216bd510748ce44255f11106eb5d60 Mon Sep 17 00:00:00 2001 From: rayane-djouah <77965000+rayane-djouah@users.noreply.github.com> Date: Wed, 10 Jan 2024 18:55:29 +0100 Subject: [PATCH 087/660] Fix workflow --- .github/workflows/failureNotifier.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/failureNotifier.yml b/.github/workflows/failureNotifier.yml index 794c472994f3..f1b94b97f8a3 100644 --- a/.github/workflows/failureNotifier.yml +++ b/.github/workflows/failureNotifier.yml @@ -6,6 +6,9 @@ on: types: - completed +permissions: + issues: write + jobs: notify-failure: runs-on: ubuntu-latest @@ -50,11 +53,10 @@ jobs: - name: Process Each Failed Job uses: actions/github-script@v7 with: - github-token: ${{ secrets.OS_BOTIFY_TOKEN }} script: | const prNumber = ${{ steps.getMergedPullRequestNumber.outputs.number }}; const runData = ${{fromJSON(steps.fetch-workflow-details.outputs.result)}}; - const jobs = ${{fromJSON(steps.fetch-workflow-jobs.outputs.result)}}; + const jobs = ${{steps.fetch-workflow-jobs.outputs.result}}; const failureLabel = 'workflow-failure'; const prData = await github.rest.pulls.get({ From 28143ef5b4c778d577abf2d966fa596a2ac6ea8b Mon Sep 17 00:00:00 2001 From: rayane-djouah <77965000+rayane-djouah@users.noreply.github.com> Date: Wed, 10 Jan 2024 19:00:33 +0100 Subject: [PATCH 088/660] fix issue body --- .github/workflows/failureNotifier.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/failureNotifier.yml b/.github/workflows/failureNotifier.yml index f1b94b97f8a3..96647ee261c3 100644 --- a/.github/workflows/failureNotifier.yml +++ b/.github/workflows/failureNotifier.yml @@ -84,19 +84,19 @@ jobs: const existingIssue = issues.data.find(issue => issue.title.includes(jobName)); if (!existingIssue) { const issueTitle = `🔍 Investigation Needed: ${jobName} Failure due to PR Merge 🔍`; - const issueBody = `🚨 **Failure Summary** 🚨:\n\n - - **📋 Job Name**: [${jobName}](${jobLink})\n - - **🔧 Failure in Workflow**: Main branch\n - - **🔗 Triggered by PR**: [PR Link](${prLink})\n - - **👤 PR Author**: @${prAuthor}\n - - **🤝 Merged by**: @${prMerger}\n\n - ⚠️ **Action Required** ⚠️:\n\n - 🛠️ A recent merge appears to have caused a failure in the job named [${jobName}](${jobLink}). - This issue has been automatically created and labeled with \`${failureLabel}\` for investigation. \n\n - 👀 **Please look into the following**:\n - 1. **Why the PR caused the job to fail?**\n - 2. **Address any underlying issues.**\n\n - 🐛 We appreciate your help in squashing this bug!`; + const issueBody = `🚨 **Failure Summary** 🚨:\n\n` + + `- **📋 Job Name**: [${jobName}](${jobLink})\n` + + `- **🔧 Failure in Workflow**: Main branch\n` + + `- **🔗 Triggered by PR**: [PR Link](${prLink})\n` + + `- **👤 PR Author**: @${prAuthor}\n` + + `- **🤝 Merged by**: @${prMerger}\n\n` + + `⚠️ **Action Required** ⚠️:\n\n` + + `🛠️ A recent merge appears to have caused a failure in the job named [${jobName}](${jobLink}).\n` + + `This issue has been automatically created and labeled with \`${failureLabel}\` for investigation. \n\n` + + `👀 **Please look into the following**:\n` + + `1. **Why the PR caused the job to fail?**\n` + + `2. **Address any underlying issues.**\n\n` + + `🐛 We appreciate your help in squashing this bug!`; github.rest.issues.create({ owner: context.repo.owner, repo: context.repo.repo, From 0e41b6613fc611fc6d407d3a5876f4ac705e3e33 Mon Sep 17 00:00:00 2001 From: rayane-djouah <77965000+rayane-djouah@users.noreply.github.com> Date: Wed, 10 Jan 2024 19:24:48 +0100 Subject: [PATCH 089/660] add daily label --- .github/workflows/failureNotifier.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/failureNotifier.yml b/.github/workflows/failureNotifier.yml index 96647ee261c3..fd70b02adb29 100644 --- a/.github/workflows/failureNotifier.yml +++ b/.github/workflows/failureNotifier.yml @@ -102,7 +102,7 @@ jobs: repo: context.repo.repo, title: issueTitle, body: issueBody, - labels: [failureLabel], + labels: [failureLabel, 'daily'], assignees: [prMerger, prAuthor] }); } From c2d9c887fcc609d90324b556d1716640f6547018 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Wed, 10 Jan 2024 20:05:55 +0100 Subject: [PATCH 090/660] Fix comments x2 --- src/ONYXKEYS.ts | 6 ++---- src/components/SettlementButton.tsx | 3 +-- src/types/onyx/LastPaymentMethod.ts | 3 +++ src/types/onyx/index.ts | 2 ++ 4 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 src/types/onyx/LastPaymentMethod.ts diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index d3c7756b0dee..63eecbea3984 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -391,7 +391,7 @@ type OnyxValues = { [ONYXKEYS.NVP_PRIVATE_PUSH_NOTIFICATION_ID]: string; [ONYXKEYS.NVP_TRY_FOCUS_MODE]: boolean; [ONYXKEYS.FOCUS_MODE_NOTIFICATION]: boolean; - [ONYXKEYS.NVP_LAST_PAYMENT_METHOD]: Record; + [ONYXKEYS.NVP_LAST_PAYMENT_METHOD]: OnyxTypes.LastPaymentMethod; [ONYXKEYS.NVP_RECENT_WAYPOINTS]: OnyxTypes.RecentWaypoint[]; [ONYXKEYS.PUSH_NOTIFICATIONS_ENABLED]: boolean; [ONYXKEYS.PLAID_DATA]: OnyxTypes.PlaidData; @@ -530,7 +530,5 @@ type OnyxValues = { type OnyxKeyValue = OnyxEntry; -type LastPaymentMethod = Record; - export default ONYXKEYS; -export type {OnyxKey, OnyxCollectionKey, OnyxValues, OnyxKeyValue, OnyxFormKey, OnyxKeysMap,LastPaymentMethod}; +export type {OnyxKey, OnyxCollectionKey, OnyxValues, OnyxKeyValue, OnyxFormKey, OnyxKeysMap}; diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton.tsx index 1fae611018c1..4a4abea14c70 100644 --- a/src/components/SettlementButton.tsx +++ b/src/components/SettlementButton.tsx @@ -10,11 +10,10 @@ import * as BankAccounts from '@userActions/BankAccounts'; import * as IOU from '@userActions/IOU'; import * as PaymentMethods from '@userActions/PaymentMethods'; import CONST from '@src/CONST'; -import type {LastPaymentMethod} from '@src/ONYXKEYS'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {ButtonSizeValue} from '@src/styles/utils/types'; -import type {AnchorAlignment, Report} from '@src/types/onyx'; +import type {AnchorAlignment, LastPaymentMethod, Report} from '@src/types/onyx'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; diff --git a/src/types/onyx/LastPaymentMethod.ts b/src/types/onyx/LastPaymentMethod.ts new file mode 100644 index 000000000000..677a23fa9586 --- /dev/null +++ b/src/types/onyx/LastPaymentMethod.ts @@ -0,0 +1,3 @@ +type LastPaymentMethod = Record; + +export default LastPaymentMethod; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 4e199e88ce88..ecc3603e8f8d 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -16,6 +16,7 @@ import type FrequentlyUsedEmoji from './FrequentlyUsedEmoji'; import type {FundList} from './Fund'; import type Fund from './Fund'; import type IOU from './IOU'; +import type LastPaymentMethod from './LastPaymentMethod'; import type Locale from './Locale'; import type {LoginList} from './Login'; import type Login from './Login'; @@ -138,5 +139,6 @@ export type { ReportUserIsTyping, PolicyReportField, RecentlyUsedReportFields, + LastPaymentMethod, AnchorAlignment, }; From 812dd2f2c6df21ecaf123eb06f45e64048b037b4 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Thu, 11 Jan 2024 09:55:47 +0100 Subject: [PATCH 091/660] update commend --- android/app/src/main/res/values/colors.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml index b4d8c2181b0b..8d5bfbf4eed7 100644 --- a/android/app/src/main/res/values/colors.xml +++ b/android/app/src/main/res/values/colors.xml @@ -1,4 +1,5 @@ + #03D47C #FFFFFF #03D47C From 402e85f1786e4b64766d0c060b26531d5387024f Mon Sep 17 00:00:00 2001 From: staszekscp Date: Thu, 11 Jan 2024 10:00:30 +0100 Subject: [PATCH 092/660] add quick fix for nav --- src/pages/LogOutPreviousUserPage.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index 3ed803cb0b32..a1f5ed035763 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -64,6 +64,8 @@ function LogOutPreviousUserPage(props) { // which is already called when AuthScreens mounts. if (exitTo && exitTo !== ROUTES.WORKSPACE_NEW && !props.account.isLoading && !isLoggingInAsNewUser) { Navigation.isNavigationReady().then(() => { + // temporary until finding clean solution + Navigation.goBack(); Navigation.navigate(exitTo); }); } From 1af5ac6c17f84dc803537a279134b1c94256ba65 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 11 Jan 2024 10:36:27 +0100 Subject: [PATCH 093/660] Update replaceReceipt typing after merging main --- src/libs/actions/IOU.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index bf02aa4e6528..b7eba7e54085 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3476,8 +3476,8 @@ function detachReceipt(transactionID: string) { API.write('DetachReceipt', {transactionID}, {optimisticData, failureData}); } -function replaceReceipt(transactionID: string, file: any, source: string) { - const transaction = allTransactions.transactionID ?? {}; +function replaceReceipt(transactionID: string, file: File, source: string) { + const transaction = allTransactions.transactionID; const oldReceipt = transaction?.receipt ?? {}; const optimisticData: OnyxUpdate[] = [ @@ -3507,7 +3507,7 @@ function replaceReceipt(transactionID: string, file: any, source: string) { type ReplaceReceiptParams = { transactionID: string; - receipt: any; + receipt: File; }; const parameters: ReplaceReceiptParams = { From d2d5d460235f08e2c614835d6649512b3f8dfc61 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 12 Jan 2024 12:08:08 +0100 Subject: [PATCH 094/660] Onyx values type updates, minor code improvements --- src/libs/actions/IOU.ts | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 74dbffe030d8..3595f65e329c 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/naming-convention */ import type {StackScreenProps} from '@react-navigation/stack'; import {format} from 'date-fns'; import Str from 'expensify-common/lib/str'; @@ -150,7 +149,7 @@ Onyx.connect({ callback: (val) => (allReports = val), }); -let allTransactions: Record> = {}; +let allTransactions: NonNullable> = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION, waitForCollectionCallback: true, @@ -164,7 +163,7 @@ Onyx.connect({ }, }); -let allTransactionDrafts: Record> = {}; +let allTransactionDrafts: NonNullable> = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, waitForCollectionCallback: true, @@ -173,7 +172,7 @@ Onyx.connect({ }, }); -let allTransactionViolations: Record> = {}; +let allTransactionViolations: NonNullable> = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, waitForCollectionCallback: true, @@ -187,7 +186,7 @@ Onyx.connect({ }, }); -let allDraftSplitTransactions: Record> = {}; +let allDraftSplitTransactions: NonNullable> = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT, waitForCollectionCallback: true, @@ -196,7 +195,7 @@ Onyx.connect({ }, }); -let allNextSteps: Record> = {}; +let allNextSteps: NonNullable> = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.NEXT_STEP, waitForCollectionCallback: true, @@ -236,6 +235,7 @@ Onyx.connect({ * @param reportID to attach the transaction to * @param iouRequestType one of manual/scan/distance */ +// eslint-disable-next-line @typescript-eslint/naming-convention function startMoneyRequest_temporaryForRefactor(reportID: string, isFromGlobalCreate: boolean, iouRequestType: IOURequestType = CONST.IOU.REQUEST_TYPE.MANUAL) { // Generate a brand new transactionID const newTransactionID = CONST.IOU.OPTIMISTIC_TRANSACTION_ID; @@ -271,46 +271,57 @@ function clearMoneyRequest(transactionID: string) { Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, null); } +// eslint-disable-next-line @typescript-eslint/naming-convention function setMoneyRequestAmount_temporaryForRefactor(transactionID: string, amount: number, currency: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {amount, currency}); } +// eslint-disable-next-line @typescript-eslint/naming-convention function setMoneyRequestCreated_temporaryForRefactor(transactionID: string, created: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {created}); } +// eslint-disable-next-line @typescript-eslint/naming-convention function setMoneyRequestCurrency_temporaryForRefactor(transactionID: string, currency: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {currency}); } +// eslint-disable-next-line @typescript-eslint/naming-convention function setMoneyRequestDescription_temporaryForRefactor(transactionID: string, comment: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {comment: {comment: comment.trim()}}); } +// eslint-disable-next-line @typescript-eslint/naming-convention function setMoneyRequestMerchant_temporaryForRefactor(transactionID: string, merchant: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {merchant: merchant.trim()}); } +// eslint-disable-next-line @typescript-eslint/naming-convention function setMoneyRequestCategory_temporaryForRefactor(transactionID: string, category: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {category}); } +// eslint-disable-next-line @typescript-eslint/naming-convention function resetMoneyRequestCategory_temporaryForRefactor(transactionID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {category: null}); } +// eslint-disable-next-line @typescript-eslint/naming-convention function setMoneyRequestTag_temporaryForRefactor(transactionID: string, tag: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {tag}); } +// eslint-disable-next-line @typescript-eslint/naming-convention function resetMoneyRequestTag_temporaryForRefactor(transactionID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {tag: null}); } +// eslint-disable-next-line @typescript-eslint/naming-convention function setMoneyRequestBillable_temporaryForRefactor(transactionID: string, billable: boolean) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {billable}); } +// eslint-disable-next-line @typescript-eslint/naming-convention function setMoneyRequestParticipants_temporaryForRefactor(transactionID: string, participants: Participant[]) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {participants}); } @@ -374,7 +385,7 @@ function buildOnyxDataForMoneyRequest( policyTags?: OnyxTypes.PolicyTags, policyCategories?: OnyxTypes.PolicyCategories, hasOutstandingChildRequest = false, -): OnyxUpdate[][] { +): [OnyxUpdate[], OnyxUpdate[], OnyxUpdate[]] { const isScanRequest = TransactionUtils.isScanRequest(transaction); const optimisticData: OnyxUpdate[] = []; @@ -596,7 +607,9 @@ function buildOnyxDataForMoneyRequest( ...(isNewChatReport ? { [chatCreatedAction.reportActionID]: { - errors: getReceiptError(transaction?.receipt, transaction.filename ?? transaction.receipt?.filename, isScanRequest), + // Disabling this line since transaction.filename can be an empty string + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + errors: getReceiptError(transaction?.receipt, transaction.filename || transaction.receipt?.filename, isScanRequest), }, [reportPreviewAction.reportActionID]: { errors: ErrorUtils.getMicroSecondOnyxError(null), @@ -605,7 +618,9 @@ function buildOnyxDataForMoneyRequest( : { [reportPreviewAction.reportActionID]: { created: reportPreviewAction.created, - errors: getReceiptError(transaction?.receipt, transaction.filename ?? transaction.receipt?.filename, isScanRequest), + // Disabling this line since transaction.filename can be an empty string + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + errors: getReceiptError(transaction?.receipt, transaction.filename || transaction.receipt?.filename, isScanRequest), }, }), }, @@ -617,7 +632,9 @@ function buildOnyxDataForMoneyRequest( ...(isNewIOUReport ? { [iouCreatedAction.reportActionID]: { - errors: getReceiptError(transaction.receipt, transaction.filename ?? transaction.receipt?.filename, isScanRequest), + // Disabling this line since transaction.filename can be an empty string + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + errors: getReceiptError(transaction.receipt, transaction.filename || transaction.receipt?.filename, isScanRequest), }, [iouAction.reportActionID]: { errors: ErrorUtils.getMicroSecondOnyxError(null), @@ -625,7 +642,9 @@ function buildOnyxDataForMoneyRequest( } : { [iouAction.reportActionID]: { - errors: getReceiptError(transaction.receipt, transaction.filename ?? transaction.receipt?.filename, isScanRequest), + // Disabling this line since transaction.filename can be an empty string + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + errors: getReceiptError(transaction.receipt, transaction.filename || transaction.receipt?.filename, isScanRequest), }, }), }, From 038f64638c2736a03eeda62ce46a16651086bf38 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 12 Jan 2024 14:06:04 +0100 Subject: [PATCH 095/660] Get rid of undefined checks --- src/libs/actions/IOU.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 3595f65e329c..cac07dc1c501 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1539,11 +1539,11 @@ function createSplitsAndOnyxData( // STEP 2: Get existing IOU/Expense report and update its total OR build a new optimistic one // For Control policy expense chats, if the report is already approved, create a new expense report - let oneOnOneIOUReport: OneOnOneIOUReport = oneOnOneChatReport.iouReportID ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oneOnOneChatReport.iouReportID}`] ?? undefined : undefined; + let oneOnOneIOUReport: OneOnOneIOUReport = oneOnOneChatReport.iouReportID ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oneOnOneChatReport.iouReportID}`] : null; const shouldCreateNewOneOnOneIOUReport = - oneOnOneIOUReport === undefined || (isOwnPolicyExpenseChat && ReportUtils.isControlPolicyExpenseReport(oneOnOneIOUReport) && ReportUtils.isReportApproved(oneOnOneIOUReport)); + !oneOnOneIOUReport || (isOwnPolicyExpenseChat && ReportUtils.isControlPolicyExpenseReport(oneOnOneIOUReport) && ReportUtils.isReportApproved(oneOnOneIOUReport)); - if (oneOnOneIOUReport === undefined || shouldCreateNewOneOnOneIOUReport) { + if (!oneOnOneIOUReport || shouldCreateNewOneOnOneIOUReport) { oneOnOneIOUReport = isOwnPolicyExpenseChat ? ReportUtils.buildOptimisticExpenseReport(oneOnOneChatReport.reportID, oneOnOneChatReport.policyID ?? '', currentUserAccountID, splitAmount, currency) : ReportUtils.buildOptimisticIOUReport(currentUserAccountID, accountID, splitAmount, oneOnOneChatReport.reportID, currency); @@ -2174,11 +2174,11 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA oneOnOneChatReport = existingChatReport ?? ReportUtils.buildOptimisticChatReport(participant.accountID ? [participant.accountID] : []); } - let oneOnOneIOUReport: OneOnOneIOUReport = oneOnOneChatReport?.iouReportID ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oneOnOneChatReport.iouReportID}`] ?? undefined : undefined; + let oneOnOneIOUReport: OneOnOneIOUReport = oneOnOneChatReport?.iouReportID ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oneOnOneChatReport.iouReportID}`] : null; const shouldCreateNewOneOnOneIOUReport = - oneOnOneIOUReport === undefined || (isPolicyExpenseChat && ReportUtils.isControlPolicyExpenseReport(oneOnOneIOUReport) && ReportUtils.isReportApproved(oneOnOneIOUReport)); + !oneOnOneIOUReport || (isPolicyExpenseChat && ReportUtils.isControlPolicyExpenseReport(oneOnOneIOUReport) && ReportUtils.isReportApproved(oneOnOneIOUReport)); - if (oneOnOneIOUReport === undefined || shouldCreateNewOneOnOneIOUReport) { + if (!oneOnOneIOUReport || shouldCreateNewOneOnOneIOUReport) { oneOnOneIOUReport = isPolicyExpenseChat ? ReportUtils.buildOptimisticExpenseReport(oneOnOneChatReport?.reportID ?? '', participant.policyID ?? '', sessionAccountID, splitAmount, currency ?? '') : ReportUtils.buildOptimisticIOUReport(sessionAccountID, participant.accountID ?? -1, splitAmount, oneOnOneChatReport?.reportID ?? '', currency ?? ''); From 10baa724ac7821a6f70a49c09b70b50ff206f0ae Mon Sep 17 00:00:00 2001 From: rayane-djouah <77965000+rayane-djouah@users.noreply.github.com> Date: Fri, 12 Jan 2024 16:19:08 +0100 Subject: [PATCH 096/660] address review comments --- .github/workflows/failureNotifier.yml | 32 ++++++++------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/.github/workflows/failureNotifier.yml b/.github/workflows/failureNotifier.yml index fd70b02adb29..82b0b0fbe6ed 100644 --- a/.github/workflows/failureNotifier.yml +++ b/.github/workflows/failureNotifier.yml @@ -17,19 +17,6 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Fetch Workflow Run Details - id: fetch-workflow-details - uses: actions/github-script@v7 - with: - script: | - const runId = "${{ github.event.workflow_run.id }}"; - const runData = await github.rest.actions.getWorkflowRun({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: runId - }); - return runData.data; - - name: Fetch Workflow Run Jobs id: fetch-workflow-jobs uses: actions/github-script@v7 @@ -55,8 +42,7 @@ jobs: with: script: | const prNumber = ${{ steps.getMergedPullRequestNumber.outputs.number }}; - const runData = ${{fromJSON(steps.fetch-workflow-details.outputs.result)}}; - const jobs = ${{steps.fetch-workflow-jobs.outputs.result}}; + const jobs = ${{ steps.fetch-workflow-jobs.outputs.result }}; const failureLabel = 'workflow-failure'; const prData = await github.rest.pulls.get({ @@ -71,7 +57,7 @@ jobs: const prAuthor = pr.user.login; const prMerger = pr.merged_by.login; - for (let i=0; i issue.title.includes(jobName)); if (!existingIssue) { - const issueTitle = `🔍 Investigation Needed: ${jobName} Failure due to PR Merge 🔍`; + const issueTitle = `🔍 Investigation Needed: ${ jobName } Failure due to PR Merge 🔍`; const issueBody = `🚨 **Failure Summary** 🚨:\n\n` + - `- **📋 Job Name**: [${jobName}](${jobLink})\n` + + `- **📋 Job Name**: [${ jobName }](${ jobLink })\n` + `- **🔧 Failure in Workflow**: Main branch\n` + - `- **🔗 Triggered by PR**: [PR Link](${prLink})\n` + - `- **👤 PR Author**: @${prAuthor}\n` + - `- **🤝 Merged by**: @${prMerger}\n\n` + + `- **🔗 Triggered by PR**: [PR Link](${ prLink })\n` + + `- **👤 PR Author**: @${ prAuthor }\n` + + `- **🤝 Merged by**: @${ prMerger }\n\n` + `⚠️ **Action Required** ⚠️:\n\n` + - `🛠️ A recent merge appears to have caused a failure in the job named [${jobName}](${jobLink}).\n` + - `This issue has been automatically created and labeled with \`${failureLabel}\` for investigation. \n\n` + + `🛠️ A recent merge appears to have caused a failure in the job named [${ jobName }](${ jobLink }).\n` + + `This issue has been automatically created and labeled with \`${ failureLabel }\` for investigation. \n\n` + `👀 **Please look into the following**:\n` + `1. **Why the PR caused the job to fail?**\n` + `2. **Address any underlying issues.**\n\n` + From b91bc05bd24e8514f1a75493705f0561d20fe72b Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Fri, 12 Jan 2024 22:41:58 +0700 Subject: [PATCH 097/660] Add highlight to active emoji in EmojiDropDown --- src/CONST.ts | 2 ++ src/components/EmojiPicker/EmojiPicker.js | 5 ++++- .../EmojiPicker/EmojiPickerButtonDropdown.js | 19 +++++++++++++------ .../EmojiPicker/EmojiPickerMenu/index.js | 7 +++++-- src/libs/EmojiUtils.ts | 10 ++++++++++ src/libs/actions/EmojiPickerAction.ts | 4 +++- 6 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index b1a6b6895de7..2734a607615e 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1387,6 +1387,8 @@ const CONST = { EMOJI: /[\p{Extended_Pictographic}\u200d\u{1f1e6}-\u{1f1ff}\u{1f3fb}-\u{1f3ff}\u{e0020}-\u{e007f}\u20E3\uFE0F]|[#*0-9]\uFE0F?\u20E3/gu, // eslint-disable-next-line max-len, no-misleading-character-class EMOJIS: /[\p{Extended_Pictographic}](\u200D[\p{Extended_Pictographic}]|[\u{1F3FB}-\u{1F3FF}]|[\u{E0020}-\u{E007F}]|\uFE0F|\u20E3)*|[\u{1F1E6}-\u{1F1FF}]{2}|[#*0-9]\uFE0F?\u20E3/gu, + // eslint-disable-next-line max-len, no-misleading-character-class + EMOJI_SKIN_TONES: /[\u{1f3fb}-\u{1f3ff}]/gu, TAX_ID: /^\d{9}$/, NON_NUMERIC: /\D/g, diff --git a/src/components/EmojiPicker/EmojiPicker.js b/src/components/EmojiPicker/EmojiPicker.js index 532eb61a99a9..563a2696fab4 100644 --- a/src/components/EmojiPicker/EmojiPicker.js +++ b/src/components/EmojiPicker/EmojiPicker.js @@ -33,6 +33,7 @@ const EmojiPicker = forwardRef((props, ref) => { const emojiPopoverAnchorRef = useRef(null); const onModalHide = useRef(() => {}); const onEmojiSelected = useRef(() => {}); + const activeEmoji = useRef(); const emojiSearchInput = useRef(); const {isSmallScreenWidth, windowHeight} = useWindowDimensions(); @@ -56,9 +57,10 @@ const EmojiPicker = forwardRef((props, ref) => { * @param {Function} [onWillShow=() => {}] - Run a callback when Popover will show * @param {String} id - Unique id for EmojiPicker */ - const showEmojiPicker = (onModalHideValue, onEmojiSelectedValue, emojiPopoverAnchorValue, anchorOrigin, onWillShow = () => {}, id) => { + const showEmojiPicker = (onModalHideValue, onEmojiSelectedValue, emojiPopoverAnchorValue, anchorOrigin, onWillShow = () => {}, id, activeEmojiValue) => { onModalHide.current = onModalHideValue; onEmojiSelected.current = onEmojiSelectedValue; + activeEmoji.current = activeEmojiValue; emojiPopoverAnchorRef.current = emojiPopoverAnchorValue; const emojiPopoverAnchor = getEmojiPopoverAnchor(); if (emojiPopoverAnchor.current && emojiPopoverAnchor.current.blur) { @@ -184,6 +186,7 @@ const EmojiPicker = forwardRef((props, ref) => { > (emojiSearchInput.current = el)} /> diff --git a/src/components/EmojiPicker/EmojiPickerButtonDropdown.js b/src/components/EmojiPicker/EmojiPickerButtonDropdown.js index bfcb66aeefbb..4d4eae49bf86 100644 --- a/src/components/EmojiPicker/EmojiPickerButtonDropdown.js +++ b/src/components/EmojiPicker/EmojiPickerButtonDropdown.js @@ -29,18 +29,25 @@ function EmojiPickerButtonDropdown(props) { const StyleUtils = useStyleUtils(); const emojiPopoverAnchor = useRef(null); useEffect(() => EmojiPickerAction.resetEmojiPopoverAnchor, []); - const onPress = () => { if (EmojiPickerAction.isEmojiPickerVisible()) { EmojiPickerAction.hideEmojiPicker(); return; } - EmojiPickerAction.showEmojiPicker(props.onModalHide, (emoji) => props.onInputChange(emoji), emojiPopoverAnchor, { - horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, - vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP, - shiftVertical: 4, - }); + EmojiPickerAction.showEmojiPicker( + props.onModalHide, + (emoji) => props.onInputChange(emoji), + emojiPopoverAnchor, + { + horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, + vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP, + shiftVertical: 4, + }, + () => {}, + undefined, + props.value, + ); }; return ( diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.js b/src/components/EmojiPicker/EmojiPickerMenu/index.js index a723eed446a4..0be9eafdd4a7 100755 --- a/src/components/EmojiPicker/EmojiPickerMenu/index.js +++ b/src/components/EmojiPicker/EmojiPickerMenu/index.js @@ -14,6 +14,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as Browser from '@libs/Browser'; import canFocusInputOnScreenFocus from '@libs/canFocusInputOnScreenFocus'; +import * as EmojiUtils from '@libs/EmojiUtils'; import isEnterWhileComposition from '@libs/KeyboardShortcut/isEnterWhileComposition'; import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; @@ -33,7 +34,7 @@ const defaultProps = { const throttleTime = Browser.isMobile() ? 200 : 50; -function EmojiPickerMenu({forwardedRef, onEmojiSelected}) { +function EmojiPickerMenu({forwardedRef, onEmojiSelected, activeEmoji}) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {isSmallScreenWidth, windowWidth} = useWindowDimensions(); @@ -344,7 +345,8 @@ function EmojiPickerMenu({forwardedRef, onEmojiSelected}) { const emojiCode = types && types[preferredSkinTone] ? types[preferredSkinTone] : code; const isEmojiFocused = index === highlightedIndex && isUsingKeyboardMovement; - const shouldEmojiBeHighlighted = index === highlightedIndex && highlightFirstEmoji; + const shouldEmojiBeHighlighted = + (index === highlightedIndex && highlightFirstEmoji) || (activeEmoji && EmojiUtils.getRemovedSkinToneEmoji(emojiCode) === EmojiUtils.getRemovedSkinToneEmoji(activeEmoji)); return ( (prevState === index ? -1 : prevState)) } isFocused={isEmojiFocused} + //isHighlighted={true} isHighlighted={shouldEmojiBeHighlighted} /> ); diff --git a/src/libs/EmojiUtils.ts b/src/libs/EmojiUtils.ts index e34fa0b90fc6..b42049cc627d 100644 --- a/src/libs/EmojiUtils.ts +++ b/src/libs/EmojiUtils.ts @@ -548,6 +548,15 @@ const getEmojiReactionDetails = (emojiName: string, reaction: ReportActionReacti }; }; +/** + * Get base emoji without skintone + * @param emoji emoji string + * @returns emoji without skin tone + */ +const getRemovedSkinToneEmoji = (emoji: string) => { + return emoji.replace(CONST.REGEX.EMOJI_SKIN_TONES, ''); +}; + export { findEmojiByName, findEmojiByCode, @@ -570,4 +579,5 @@ export { getAddedEmojis, isFirstLetterEmoji, hasAccountIDEmojiReacted, + getRemovedSkinToneEmoji, }; diff --git a/src/libs/actions/EmojiPickerAction.ts b/src/libs/actions/EmojiPickerAction.ts index 56a5f34c0b8e..0fc21713c608 100644 --- a/src/libs/actions/EmojiPickerAction.ts +++ b/src/libs/actions/EmojiPickerAction.ts @@ -26,6 +26,7 @@ type EmojiPickerRef = { anchorOrigin?: AnchorOrigin, onWillShow?: OnWillShowPicker, id?: string, + activeEmoji?: string, ) => void; isActive: (id: string) => boolean; clearActive: () => void; @@ -55,12 +56,13 @@ function showEmojiPicker( anchorOrigin?: AnchorOrigin, onWillShow: OnWillShowPicker = () => {}, id?: string, + activeEmoji?: string, ) { if (!emojiPickerRef.current) { return; } - emojiPickerRef.current.showEmojiPicker(onModalHide, onEmojiSelected, emojiPopoverAnchor, anchorOrigin, onWillShow, id); + emojiPickerRef.current.showEmojiPicker(onModalHide, onEmojiSelected, emojiPopoverAnchor, anchorOrigin, onWillShow, id, activeEmoji); } /** From 9698dbf988b7abfa751e514fe7d4de0211fe3735 Mon Sep 17 00:00:00 2001 From: Wildan Muhlis Date: Sat, 13 Jan 2024 10:57:16 +0700 Subject: [PATCH 098/660] resolve lint --- src/components/EmojiPicker/EmojiPicker.js | 1 + src/components/EmojiPicker/EmojiPickerMenu/index.js | 3 +-- src/libs/EmojiUtils.ts | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/components/EmojiPicker/EmojiPicker.js b/src/components/EmojiPicker/EmojiPicker.js index 563a2696fab4..68b74a7aeb15 100644 --- a/src/components/EmojiPicker/EmojiPicker.js +++ b/src/components/EmojiPicker/EmojiPicker.js @@ -56,6 +56,7 @@ const EmojiPicker = forwardRef((props, ref) => { * @param {Object} [anchorOrigin=DEFAULT_ANCHOR_ORIGIN] - Anchor origin for Popover * @param {Function} [onWillShow=() => {}] - Run a callback when Popover will show * @param {String} id - Unique id for EmojiPicker + * @param {String} activeEmojiValue - Selected emoji to be highlighted */ const showEmojiPicker = (onModalHideValue, onEmojiSelectedValue, emojiPopoverAnchorValue, anchorOrigin, onWillShow = () => {}, id, activeEmojiValue) => { onModalHide.current = onModalHideValue; diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.js b/src/components/EmojiPicker/EmojiPickerMenu/index.js index 0be9eafdd4a7..c8ca9c6ac6d1 100755 --- a/src/components/EmojiPicker/EmojiPickerMenu/index.js +++ b/src/components/EmojiPicker/EmojiPickerMenu/index.js @@ -366,12 +366,11 @@ function EmojiPickerMenu({forwardedRef, onEmojiSelected, activeEmoji}) { setHighlightedIndex((prevState) => (prevState === index ? -1 : prevState)) } isFocused={isEmojiFocused} - //isHighlighted={true} isHighlighted={shouldEmojiBeHighlighted} /> ); }, - [preferredSkinTone, highlightedIndex, isUsingKeyboardMovement, highlightFirstEmoji, singleExecution, translate, onEmojiSelected, isSmallScreenWidth, windowWidth, styles], + [preferredSkinTone, highlightedIndex, isUsingKeyboardMovement, highlightFirstEmoji, singleExecution, translate, onEmojiSelected, isSmallScreenWidth, windowWidth, styles, activeEmoji], ); return ( diff --git a/src/libs/EmojiUtils.ts b/src/libs/EmojiUtils.ts index b42049cc627d..9a1998b35462 100644 --- a/src/libs/EmojiUtils.ts +++ b/src/libs/EmojiUtils.ts @@ -553,9 +553,7 @@ const getEmojiReactionDetails = (emojiName: string, reaction: ReportActionReacti * @param emoji emoji string * @returns emoji without skin tone */ -const getRemovedSkinToneEmoji = (emoji: string) => { - return emoji.replace(CONST.REGEX.EMOJI_SKIN_TONES, ''); -}; +const getRemovedSkinToneEmoji = (emoji: string) => emoji.replace(CONST.REGEX.EMOJI_SKIN_TONES, ''); export { findEmojiByName, From 6e7fc2761fac092106366d45e54e92a9fc10fb63 Mon Sep 17 00:00:00 2001 From: someone-here Date: Sun, 14 Jan 2024 22:16:36 +0530 Subject: [PATCH 099/660] Requested changes --- src/libs/ReportUtils.ts | 8 ++++---- src/pages/ReportDescriptionPage.js | 4 ++-- src/pages/ReportDetailsPage.js | 6 ++++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 26a69cc51818..4f6d2032c8cf 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4277,10 +4277,10 @@ function getRoom(type: ValueOf, policyID: string) } /** - * We only want policy owners and admins to be able to modify the welcome message, but not in thread chat. + * We only want policy owners and admins to be able to modify the report description, but not in thread chat. */ -function shouldDisableReportDescription(report: OnyxEntry, policy: OnyxEntry): boolean { - return isMoneyRequestReport(report) || isArchivedRoom(report) || !isChatRoom(report) || isChatThread(report) || !PolicyUtils.isPolicyAdmin(policy); +function canEditReportDescription(report: OnyxEntry, policy: OnyxEntry): boolean { + return !isMoneyRequestReport(report) && !isArchivedRoom(report) && isChatRoom(report) && !isChatThread(report) && PolicyUtils.isPolicyAdmin(policy); } /** * Checks if report action has error when smart scanning @@ -4534,7 +4534,7 @@ export { getReimbursementDeQueuedActionMessage, getPersonalDetailsForAccountID, getRoom, - shouldDisableReportDescription, + canEditReportDescription, navigateToPrivateNotes, canEditWriteCapability, hasSmartscanError, diff --git a/src/pages/ReportDescriptionPage.js b/src/pages/ReportDescriptionPage.js index cba19d25c1d3..9093d165b876 100644 --- a/src/pages/ReportDescriptionPage.js +++ b/src/pages/ReportDescriptionPage.js @@ -82,14 +82,14 @@ function ReportDescriptionPage(props) { includeSafeAreaPaddingBottom={false} testID={ReportDescriptionPage.displayName} > - + Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(props.report.reportID))} /> ReportUtils.isUserCreatedPolicyRoom(props.report), [props.report]); const isArchivedRoom = useMemo(() => ReportUtils.isArchivedRoom(props.report), [props.report]); const isMoneyRequestReport = useMemo(() => ReportUtils.isMoneyRequestReport(props.report), [props.report]); + const canEditReportDescription = useMemo(() => ReportUtils.canEditReportDescription(props.report, policy), [props.report, policy]); // eslint-disable-next-line react-hooks/exhaustive-deps -- policy is a dependency because `getChatRoomSubtitle` calls `getPolicyName` which in turn retrieves the value from the `policy` value stored in Onyx const chatRoomSubtitle = useMemo(() => ReportUtils.getChatRoomSubtitle(props.report), [props.report, policy]); @@ -237,10 +238,11 @@ function ReportDetailsPage(props) { Navigation.navigate(ROUTES.REPORT_DESCRIPTION.getRoute(props.report.reportID))} /> {_.map(menuItems, (item) => { From e28f6cddaf021ce341d75b4a631b1a5a5ffaa3a4 Mon Sep 17 00:00:00 2001 From: someone-here Date: Sun, 14 Jan 2024 22:36:23 +0530 Subject: [PATCH 100/660] Hide Description conditionally --- src/pages/ReportDetailsPage.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index 6be27454a928..f1636d85182f 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -71,6 +71,7 @@ function ReportDetailsPage(props) { const isArchivedRoom = useMemo(() => ReportUtils.isArchivedRoom(props.report), [props.report]); const isMoneyRequestReport = useMemo(() => ReportUtils.isMoneyRequestReport(props.report), [props.report]); const canEditReportDescription = useMemo(() => ReportUtils.canEditReportDescription(props.report, policy), [props.report, policy]); + const shouldShowReportDescription = canEditReportDescription || !_.isEmpty(props.report.description); // eslint-disable-next-line react-hooks/exhaustive-deps -- policy is a dependency because `getChatRoomSubtitle` calls `getPolicyName` which in turn retrieves the value from the `policy` value stored in Onyx const chatRoomSubtitle = useMemo(() => ReportUtils.getChatRoomSubtitle(props.report), [props.report, policy]); @@ -237,14 +238,16 @@ function ReportDetailsPage(props) { )} - Navigation.navigate(ROUTES.REPORT_DESCRIPTION.getRoute(props.report.reportID))} - /> + {shouldShowReportDescription && ( + Navigation.navigate(ROUTES.REPORT_DESCRIPTION.getRoute(props.report.reportID))} + /> + )} {_.map(menuItems, (item) => { const brickRoadIndicator = ReportUtils.hasReportNameError(props.report) && item.key === CONST.REPORT_DETAILS_MENU_ITEM.SETTINGS ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; From 679ce09bc7dfe755e3bc1cca2b3e1cb393d991a6 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Mon, 15 Jan 2024 16:30:12 +0100 Subject: [PATCH 101/660] Migrate new cancelPayment function --- src/libs/ReportUtils.ts | 2 +- src/libs/actions/IOU.ts | 99 ++++++++++++++++++++-------------------- src/types/onyx/Report.ts | 3 ++ 3 files changed, 53 insertions(+), 51 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index bba9a3e7cb2f..098a583e2750 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -452,7 +452,7 @@ function getRootParentReport(report: OnyxEntry | undefined | EmptyObject return getRootParentReport(isNotEmptyObject(parentReport) ? parentReport : null); } -function getPolicy(policyID: string): Policy | EmptyObject { +function getPolicy(policyID: string | undefined): Policy | EmptyObject { if (!allPolicies || !policyID) { return {}; } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index f883b4a16841..ae6c13163fad 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3469,21 +3469,17 @@ function submitReport(expenseReport: OnyxTypes.Report) { API.write('SubmitReport', parameters, {optimisticData, successData, failureData}); } -/** - * @param {Object} expenseReport - * @param {Object} chatReport - */ -function cancelPayment(expenseReport, chatReport) { +function cancelPayment(expenseReport: OnyxTypes.Report, chatReport: OnyxEntry) { const optimisticReportAction = ReportUtils.buildOptimisticCancelPaymentReportAction(expenseReport.reportID); - const policy = ReportUtils.getPolicy(chatReport.policyID); + const policy = ReportUtils.getPolicy(chatReport?.policyID); const isFree = policy && policy.type === CONST.POLICY.TYPE.FREE; - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, value: { [optimisticReportAction.reportActionID]: { - ...optimisticReportAction, + ...(optimisticReportAction as OnyxTypes.ReportAction), pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, }, }, @@ -3493,29 +3489,28 @@ function cancelPayment(expenseReport, chatReport) { key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`, value: { ...expenseReport, - lastMessageText: lodashGet(optimisticReportAction, 'message.0.text', ''), - lastMessageHtml: lodashGet(optimisticReportAction, 'message.0.html', ''), + lastMessageText: optimisticReportAction.message?.[0]?.text ?? '', + lastMessageHtml: optimisticReportAction.message?.[0]?.html ?? '', stateNum: isFree ? CONST.REPORT.STATE_NUM.SUBMITTED : CONST.REPORT.STATE_NUM.OPEN, statusNum: isFree ? CONST.REPORT.STATUS_NUM.SUBMITTED : CONST.REPORT.STATUS_NUM.OPEN, }, }, - ...(chatReport.reportID - ? [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, - value: { - ...chatReport, - hasOutstandingIOU: true, - hasOutstandingChildRequest: true, - iouReportID: expenseReport.reportID, - }, - }, - ] - : []), ]; - const successData = [ + if (chatReport?.reportID) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, + value: { + ...chatReport, + hasOutstandingIOU: true, + hasOutstandingChildRequest: true, + iouReportID: expenseReport.reportID, + }, + }); + } + + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, @@ -3527,12 +3522,12 @@ function cancelPayment(expenseReport, chatReport) { }, ]; - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, value: { - [expenseReport.reportActionID]: { + [expenseReport.reportActionID ?? '']: { errors: ErrorUtils.getMicroSecondOnyxError('iou.error.other'), }, }, @@ -3544,31 +3539,35 @@ function cancelPayment(expenseReport, chatReport) { statusNum: CONST.REPORT.STATUS_NUM.REIMBURSED, }, }, - ...(chatReport.reportID - ? [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, - value: { - hasOutstandingIOU: false, - hasOutstandingChildRequest: false, - iouReportID: 0, - }, - }, - ] - : []), ]; - API.write( - 'CancelPayment', - { - iouReportID: expenseReport.reportID, - chatReportID: chatReport.reportID, - managerAccountID: expenseReport.managerID, - reportActionID: optimisticReportAction.reportActionID, - }, - {optimisticData, successData, failureData}, - ); + if (chatReport?.reportID) { + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, + value: { + hasOutstandingIOU: false, + hasOutstandingChildRequest: false, + iouReportID: '0', + }, + }); + } + + type CancelPaymentParams = { + iouReportID: string; + chatReportID?: string; + managerAccountID?: number; + reportActionID: string; + }; + + const parameters: CancelPaymentParams = { + iouReportID: expenseReport.reportID, + chatReportID: chatReport?.reportID, + managerAccountID: expenseReport.managerID, + reportActionID: optimisticReportAction.reportActionID, + }; + + API.write('CancelPayment', parameters, {optimisticData, successData, failureData}); } function payMoneyRequest(paymentType: PaymentMethodType, chatReport: OnyxTypes.Report, iouReport: OnyxTypes.Report) { diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index 57a36be1d99a..cd0f339382ce 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -104,6 +104,9 @@ type Report = { /** The report visibility */ visibility?: ValueOf; + /** Whether the report has outstanding IOU */ + hasOutstandingIOU?: boolean; + /** Report cached total */ cachedTotal?: string; From b85408fd62052a8aa6e260acae57e1aecb6204d7 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Mon, 15 Jan 2024 16:50:47 +0100 Subject: [PATCH 102/660] Code improvement --- src/components/OfflineWithFeedback.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/OfflineWithFeedback.tsx b/src/components/OfflineWithFeedback.tsx index 361113488801..54657661cc65 100644 --- a/src/components/OfflineWithFeedback.tsx +++ b/src/components/OfflineWithFeedback.tsx @@ -78,8 +78,12 @@ function OfflineWithFeedback({ const {isOffline} = useNetwork(); const hasErrors = isNotEmptyObject(errors ?? {}); + // Some errors have a null message. This is used to apply opacity only and to avoid showing redundant messages. - const errorMessages = Object.fromEntries(Object.entries(errors ?? {}).filter((errorEntry): errorEntry is [string, string] => errorEntry[1] !== null)); + const errorEntries = Object.entries(errors ?? {}); + const filteredErrorEntries = errorEntries.filter((errorEntry): errorEntry is [string, string] => errorEntry[1] !== null); + const errorMessages = Object.fromEntries(filteredErrorEntries); + const hasErrorMessages = isNotEmptyObject(errorMessages); const isOfflinePendingAction = !!isOffline && !!pendingAction; const isUpdateOrDeleteError = hasErrors && (pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE); From e2173e13a89785f7250bf7b975b363b11304efbf Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 16 Jan 2024 10:28:58 +0100 Subject: [PATCH 103/660] apply changes to work with rn 73 --- ...mmunity+cli-platform-android+12.3.0.patch} | 34 +++--- ...ve-community+cli-platform-ios+11.3.6.patch | 51 --------- patches/expo+49.0.21.patch | 32 ------ patches/expo+50.0.0-preview.7.patch | 10 ++ patches/expo-modules-autolinking+1.5.1.patch | 100 ------------------ patches/expo-modules-autolinking+1.9.0.patch | 21 ++++ ...2.patch => expo-modules-core+1.11.4.patch} | 12 +-- ...era+2.16.5+001+fix-boost-dependency.patch} | 13 ++- src/pages/LogOutPreviousUserPage.js | 18 +++- 9 files changed, 78 insertions(+), 213 deletions(-) rename patches/{@react-native-community+cli-platform-android+11.3.6.patch => @react-native-community+cli-platform-android+12.3.0.patch} (68%) delete mode 100644 patches/@react-native-community+cli-platform-ios+11.3.6.patch delete mode 100644 patches/expo+49.0.21.patch create mode 100644 patches/expo+50.0.0-preview.7.patch delete mode 100644 patches/expo-modules-autolinking+1.5.1.patch create mode 100644 patches/expo-modules-autolinking+1.9.0.patch rename patches/{expo-modules-core+1.5.12.patch => expo-modules-core+1.11.4.patch} (66%) rename patches/{react-native-vision-camera+2.16.2+001+fix-boost-dependency.patch => react-native-vision-camera+2.16.5+001+fix-boost-dependency.patch} (56%) diff --git a/patches/@react-native-community+cli-platform-android+11.3.6.patch b/patches/@react-native-community+cli-platform-android+12.3.0.patch similarity index 68% rename from patches/@react-native-community+cli-platform-android+11.3.6.patch rename to patches/@react-native-community+cli-platform-android+12.3.0.patch index ddebb53db36d..b6805a4fd93e 100644 --- a/patches/@react-native-community+cli-platform-android+11.3.6.patch +++ b/patches/@react-native-community+cli-platform-android+12.3.0.patch @@ -1,5 +1,5 @@ diff --git a/node_modules/@react-native-community/cli-platform-android/native_modules.gradle b/node_modules/@react-native-community/cli-platform-android/native_modules.gradle -index 8a218d5..8deb9c0 100644 +index bbfa7f7..6e024fd 100644 --- a/node_modules/@react-native-community/cli-platform-android/native_modules.gradle +++ b/node_modules/@react-native-community/cli-platform-android/native_modules.gradle @@ -140,6 +140,7 @@ class ReactNativeModules { @@ -10,25 +10,24 @@ index 8a218d5..8deb9c0 100644 private ArrayList> reactNativeModules private ArrayList unstable_reactLegacyComponentNames private HashMap reactNativeModulesBuildVariants -@@ -147,8 +148,13 @@ class ReactNativeModules { +@@ -147,9 +148,10 @@ class ReactNativeModules { + private static String LOG_PREFIX = ":ReactNative:" - ReactNativeModules(Logger logger, File root) { -+ this(logger, root, root) -+ } -+ +- ReactNativeModules(Logger logger, File root) { + ReactNativeModules(Logger logger, File root, File rnRoot) { this.logger = logger this.root = root + this.rnRoot = rnRoot - def (nativeModules, reactNativeModulesBuildVariants, androidProject) = this.getReactNativeConfig() + def (nativeModules, reactNativeModulesBuildVariants, androidProject, reactNativeVersion) = this.getReactNativeConfig() this.reactNativeModules = nativeModules -@@ -413,11 +419,12 @@ class ReactNativeModules { +@@ -414,12 +416,12 @@ class ReactNativeModules { + * * @todo: `fastlane` has been reported to not work too. */ - def cliResolveScript = "try {console.log(require('@react-native-community/cli').bin);} catch (e) {console.log(require('react-native/cli').bin);}" -+ +- def cliResolveScript = "try {console.log(require('@react-native-community/cli').bin);} catch (e) {console.log(require('react-native/cli').bin);}" ++ def cliResolveScript = "try {console.log(require('../react-native/node_modules/@react-native-community/cli').bin);} catch (e) {console.log(require('../react-native/node_modules/react-native/cli').bin);}" String[] nodeCommand = ["node", "-e", cliResolveScript] - def cliPath = this.getCommandOutput(nodeCommand, this.root) + def cliPath = this.getCommandOutput(nodeCommand, this.rnRoot) @@ -39,19 +38,18 @@ index 8a218d5..8deb9c0 100644 def json try { -@@ -482,8 +489,14 @@ class ReactNativeModules { - * where build files of an active project are located. +@@ -486,7 +488,13 @@ class ReactNativeModules { */ def projectRoot = rootProject.projectDir + +-def autoModules = new ReactNativeModules(logger, projectRoot) +def autoModules + +if(this.hasProperty('reactNativeProject')){ -+ autoModules = new ReactNativeModules(logger, projectRoot, new File(projectRoot, reactNativeProject)) ++ autoModules = new ReactNativeModules(logger, projectRoot, new File(projectRoot, reactNativeProject)) +} else { -+ autoModules = new ReactNativeModules(logger, projectRoot) ++ autoModules = new ReactNativeModules(logger, projectRoot) +} --def autoModules = new ReactNativeModules(logger, projectRoot) - - /** ----------------------- - * Exported Extensions + def reactNativeVersionRequireNewArchEnabled(autoModules) { + def rnVersion = autoModules.reactNativeVersion diff --git a/patches/@react-native-community+cli-platform-ios+11.3.6.patch b/patches/@react-native-community+cli-platform-ios+11.3.6.patch deleted file mode 100644 index 9c97b8666ef2..000000000000 --- a/patches/@react-native-community+cli-platform-ios+11.3.6.patch +++ /dev/null @@ -1,51 +0,0 @@ -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 1b6eece..914039e 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 @@ - require 'pathname' - require 'cocoapods' - --def use_native_modules!(config = nil) -+def updateConfig(config = nil) - if (config.is_a? String) - Pod::UI.warn("Passing custom root to use_native_modules! is deprecated.", - [ -@@ -24,7 +24,6 @@ def use_native_modules!(config = nil) - # Resolving the path the RN CLI. The `@react-native-community/cli` module may not be there for certain package managers, so we fall back to resolving it through `react-native` package, that's always present in RN projects - cli_resolve_script = "try {console.log(require('@react-native-community/cli').bin);} catch (e) {console.log(require('react-native/cli').bin);}" - cli_bin = Pod::Executable.execute_command("node", ["-e", cli_resolve_script], true).strip -- - if (!config) - json = [] - -@@ -36,10 +35,29 @@ def use_native_modules!(config = nil) - - config = JSON.parse(json.join("\n")) - end -+end -+ -+def use_native_modules!(config = nil) -+ if (ENV['REACT_NATIVE_DIR']) -+ Dir.chdir(ENV['REACT_NATIVE_DIR']) do -+ config = updateConfig(config) -+ end -+ else -+ config = updateConfig(config) -+ end - - project_root = Pathname.new(config["project"]["ios"]["sourceDir"]) -+ if(ENV["PROJECT_ROOT_DIR"]) -+ project_root = File.join(Dir.pwd, ENV["PROJECT_ROOT_DIR"]) -+ -+ end - - packages = config["dependencies"] -+ -+ if (ENV["NO_FLIPPER"]) -+ packages["react-native-flipper"]["platforms"]["ios"] = nil -+ end -+ - found_pods = [] - - packages.each do |package_name, package| diff --git a/patches/expo+49.0.21.patch b/patches/expo+49.0.21.patch deleted file mode 100644 index 0e246b301c28..000000000000 --- a/patches/expo+49.0.21.patch +++ /dev/null @@ -1,32 +0,0 @@ -diff --git a/node_modules/expo/scripts/autolinking.gradle b/node_modules/expo/scripts/autolinking.gradle -index 0505395..08098e0 100644 ---- a/node_modules/expo/scripts/autolinking.gradle -+++ b/node_modules/expo/scripts/autolinking.gradle -@@ -1,2 +1,3 @@ - def autolinkingPath = ["node", "--print", "require.resolve('expo-modules-autolinking/package.json')"].execute(null, rootDir).text.trim() --apply from: new File(autolinkingPath, "../scripts/android/autolinking_implementation.gradle"); -+ -+apply from: hasProperty("reactNativeProject") ? file('../../expo-modules-autolinking/scripts/android/autolinking_implementation.gradle') : new File(autolinkingPath, "../scripts/android/autolinking_implementation.gradle"); -diff --git a/node_modules/expo/scripts/autolinking.rb b/node_modules/expo/scripts/autolinking.rb -index 0617de5..584364f 100644 ---- a/node_modules/expo/scripts/autolinking.rb -+++ b/node_modules/expo/scripts/autolinking.rb -@@ -2,13 +2,14 @@ require 'json' - require 'pathname' - require 'colored2' # dependency of CocoaPods - --require File.join(File.dirname(`node --print "require.resolve('expo-modules-autolinking/package.json')"`), "scripts/ios/autolinking_manager") --require File.join(File.dirname(`node --print "require.resolve('expo-modules-autolinking/package.json')"`), "scripts/ios/xcode_env_generator") --require File.join(File.dirname(`node --print "require.resolve('expo-modules-autolinking/package.json')"`), "scripts/ios/react_import_patcher") -+AUTOLINKING_PATH = ENV['REACT_NATIVE_DIR'] ? `node --print "require.resolve('#{ENV['REACT_NATIVE_DIR']}/node_modules/expo-modules-autolinking/package.json')"` : `node --print "require.resolve('expo-modules-autolinking/package.json')"` -+ -+require File.join(File.dirname(AUTOLINKING_PATH), "scripts/ios/autolinking_manager") -+require File.join(File.dirname(AUTOLINKING_PATH), "scripts/ios/xcode_env_generator") -+require File.join(File.dirname(AUTOLINKING_PATH), "scripts/ios/react_import_patcher") - - def use_expo_modules!(options = {}) - # When run from the Podfile, `self` points to Pod::Podfile object -- - if @current_target_definition.autolinking_manager.present? - Pod::UI.message 'Expo modules are already being used in this target definition'.red - return diff --git a/patches/expo+50.0.0-preview.7.patch b/patches/expo+50.0.0-preview.7.patch new file mode 100644 index 000000000000..95157e1d73c6 --- /dev/null +++ b/patches/expo+50.0.0-preview.7.patch @@ -0,0 +1,10 @@ +diff --git a/node_modules/expo/scripts/autolinking.gradle b/node_modules/expo/scripts/autolinking.gradle +index 60d6ef8..3ed90a4 100644 +--- a/node_modules/expo/scripts/autolinking.gradle ++++ b/node_modules/expo/scripts/autolinking.gradle +@@ -1,4 +1,4 @@ + // Resolve `expo` > `expo-modules-autolinking` dependency chain + def autolinkingPath = ["node", "--print", "require.resolve('expo-modules-autolinking/package.json', { paths: [require.resolve('expo/package.json')] })"].execute(null, rootDir).text.trim() +-apply from: new File(autolinkingPath, "../scripts/android/autolinking_implementation.gradle"); + ++apply from: hasProperty("reactNativeProject") ? file('../../expo-modules-autolinking/scripts/android/autolinking_implementation.gradle') : new File(autolinkingPath, "../scripts/android/autolinking_implementation.gradle"); diff --git a/patches/expo-modules-autolinking+1.5.1.patch b/patches/expo-modules-autolinking+1.5.1.patch deleted file mode 100644 index 20fed8608ebd..000000000000 --- a/patches/expo-modules-autolinking+1.5.1.patch +++ /dev/null @@ -1,100 +0,0 @@ -diff --git a/node_modules/expo-modules-autolinking/scripts/android/autolinking_implementation.gradle b/node_modules/expo-modules-autolinking/scripts/android/autolinking_implementation.gradle -index 5f4cd8e..bdbea02 100644 ---- a/node_modules/expo-modules-autolinking/scripts/android/autolinking_implementation.gradle -+++ b/node_modules/expo-modules-autolinking/scripts/android/autolinking_implementation.gradle -@@ -135,6 +135,7 @@ class ExpoAutolinkingManager { - } - - static String exec(String[] commandArgs, File dir) { -+ print(dir) - Process proc = commandArgs.execute(null, dir) - StringBuffer outputStream = new StringBuffer() - proc.waitForProcessOutput(outputStream, System.err) -@@ -142,11 +143,12 @@ class ExpoAutolinkingManager { - } - - static private String[] convertOptionsToCommandArgs(String command, Map options) { -+ def expoModulesPath = options.searchPaths ? "../react-native/node_modules/expo-modules-autolinking" : "expo-modules-autolinking" - String[] args = [ - 'node', - '--no-warnings', - '--eval', -- 'require(\'expo-modules-autolinking\')(process.argv.slice(1))', -+ "require(\'${expoModulesPath}\')(process.argv.slice(1))", - '--', - command, - '--platform', -diff --git a/node_modules/expo-modules-autolinking/scripts/ios/autolinking_manager.rb b/node_modules/expo-modules-autolinking/scripts/ios/autolinking_manager.rb -index d570ad2..b9c5350 100644 ---- a/node_modules/expo-modules-autolinking/scripts/ios/autolinking_manager.rb -+++ b/node_modules/expo-modules-autolinking/scripts/ios/autolinking_manager.rb -@@ -26,7 +26,6 @@ module Expo - if has_packages? - return - end -- - global_flags = @options.fetch(:flags, {}) - tests_only = @options.fetch(:testsOnly, false) - include_tests = @options.fetch(:includeTests, false) -@@ -174,11 +173,12 @@ module Expo - end - - private def node_command_args(command_name) -+ autolinking_path = ENV['REACT_NATIVE_DIR'] ? "#{ENV['REACT_NATIVE_DIR']}/node_modules/expo-modules-autolinking" : "expo-modules-autolinking" - eval_command_args = [ - 'node', - '--no-warnings', - '--eval', -- 'require(\'expo-modules-autolinking\')(process.argv.slice(1))', -+ "require(\'#{autolinking_path}\')(process.argv.slice(1))", - command_name, - '--platform', - 'ios' -diff --git a/node_modules/expo-modules-autolinking/scripts/ios/project_integrator.rb b/node_modules/expo-modules-autolinking/scripts/ios/project_integrator.rb -index bddfa0d..3ae2c6c 100644 ---- a/node_modules/expo-modules-autolinking/scripts/ios/project_integrator.rb -+++ b/node_modules/expo-modules-autolinking/scripts/ios/project_integrator.rb -@@ -213,6 +213,7 @@ module Expo - # Generates the support script that is executed by the build script phase. - def self.generate_support_script(autolinking_manager, modules_provider_path) - args = autolinking_manager.base_command_args.map { |arg| "\"#{arg}\"" }.join(' ') -+ autolinking_path = ENV['REACT_NATIVE_DIR'] ? "#{ENV['REACT_NATIVE_DIR']}/node_modules/expo-modules-autolinking" : "expo-modules-autolinking" - - <<~SUPPORT_SCRIPT - #!/usr/bin/env bash -@@ -245,7 +246,6 @@ module Expo - You can set this up quickly by running: - - echo "export NODE_BINARY=\\$(command -v node)" >> .xcode.env -- - in the ios folder of your project. - NODE_NOT_FOUND - -@@ -257,8 +257,7 @@ module Expo - "$NODE_BINARY" "$@" - fi - } -- -- with_node --no-warnings --eval "require(\'expo-modules-autolinking\')(process.argv.slice(1))" generate-package-list #{args} --target "#{modules_provider_path}" -+ with_node --no-warnings --eval "require(\'#{autolinking_path}\')(process.argv.slice(1))" generate-package-list #{args} --target "#{modules_provider_path}" - SUPPORT_SCRIPT - end - -diff --git a/node_modules/expo-modules-autolinking/scripts/ios/react_import_patcher.rb b/node_modules/expo-modules-autolinking/scripts/ios/react_import_patcher.rb -index 4acf193..b54ea94 100644 ---- a/node_modules/expo-modules-autolinking/scripts/ios/react_import_patcher.rb -+++ b/node_modules/expo-modules-autolinking/scripts/ios/react_import_patcher.rb -@@ -10,11 +10,12 @@ module Expo - end - - public def run! -+ autolinking_path = ENV['REACT_NATIVE_DIR'] ? "#{ENV['REACT_NATIVE_DIR']}/node_modules/expo-modules-autolinking" : "expo-modules-autolinking" - args = [ - 'node', - '--no-warnings', - '--eval', -- 'require(\'expo-modules-autolinking\')(process.argv.slice(1))', -+ "require(\'#{autolinking_path}\')(process.argv.slice(1))", - 'patch-react-imports', - '--pods-root', - File.expand_path(@root), diff --git a/patches/expo-modules-autolinking+1.9.0.patch b/patches/expo-modules-autolinking+1.9.0.patch new file mode 100644 index 000000000000..363dd5dc3dab --- /dev/null +++ b/patches/expo-modules-autolinking+1.9.0.patch @@ -0,0 +1,21 @@ +diff --git a/node_modules/expo-modules-autolinking/scripts/ios/project_integrator.rb b/node_modules/expo-modules-autolinking/scripts/ios/project_integrator.rb +index c3047a8..6eff107 100644 +--- a/node_modules/expo-modules-autolinking/scripts/ios/project_integrator.rb ++++ b/node_modules/expo-modules-autolinking/scripts/ios/project_integrator.rb +@@ -212,6 +212,7 @@ module Expo + + # Generates the support script that is executed by the build script phase. + def self.generate_support_script(autolinking_manager, modules_provider_path) ++ expo_path = ENV['REACT_NATIVE_DIR'] ? "#{ENV['REACT_NATIVE_DIR']}/node_modules/expo" : "expo" + args = autolinking_manager.base_command_args.map { |arg| "\"#{arg}\"" }.join(' ') + + <<~SUPPORT_SCRIPT +@@ -258,7 +259,7 @@ module Expo + fi + } + +- with_node --no-warnings --eval "require(require.resolve(\'expo-modules-autolinking\', { paths: [require.resolve(\'expo/package.json\')] }))(process.argv.slice(1))" generate-package-list #{args} --target "#{modules_provider_path}" ++ with_node --no-warnings --eval "require(require.resolve(\'expo-modules-autolinking\', { paths: [require.resolve(\'#{expo_path}/package.json\')] }))(process.argv.slice(1))" generate-package-list #{args} --target "#{modules_provider_path}" + SUPPORT_SCRIPT + end + diff --git a/patches/expo-modules-core+1.5.12.patch b/patches/expo-modules-core+1.11.4.patch similarity index 66% rename from patches/expo-modules-core+1.5.12.patch rename to patches/expo-modules-core+1.11.4.patch index 9025a7c42d9f..fe8c5ed7b9cc 100644 --- a/patches/expo-modules-core+1.5.12.patch +++ b/patches/expo-modules-core+1.11.4.patch @@ -1,9 +1,9 @@ diff --git a/node_modules/expo-modules-core/android/build.gradle b/node_modules/expo-modules-core/android/build.gradle -index 8f9e0dc..af5b5d6 100644 +index 3603ffd..1599a69 100644 --- a/node_modules/expo-modules-core/android/build.gradle +++ b/node_modules/expo-modules-core/android/build.gradle -@@ -54,9 +54,10 @@ def downloadsDir = customDownloadsDir ? new File(customDownloadsDir) : new File( - def thirdPartyNdkDir = new File("$buildDir/third-party-ndk") +@@ -53,9 +53,10 @@ def isExpoModulesCoreTests = { + }.call() def REACT_NATIVE_BUILD_FROM_SOURCE = findProject(":packages:react-native:ReactAndroid") != null +def FALLBACK_REACT_NATIVE_DIR = hasProperty("reactNativeProject") ? file('../../react-native') : new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).parent @@ -11,6 +11,6 @@ index 8f9e0dc..af5b5d6 100644 ? findProject(":packages:react-native:ReactAndroid").getProjectDir().parent - : new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).parent + : FALLBACK_REACT_NATIVE_DIR - def REACT_NATIVE_SO_DIR = REACT_NATIVE_BUILD_FROM_SOURCE - ? Paths.get(findProject(":packages:react-native:ReactAndroid").getProjectDir().toString(), "build", "intermediates", "library_*", "*", "jni") - : "${buildDir}/react/jni" + + def reactProperties = new Properties() + file("$REACT_NATIVE_DIR/ReactAndroid/gradle.properties").withInputStream { reactProperties.load(it) } diff --git a/patches/react-native-vision-camera+2.16.2+001+fix-boost-dependency.patch b/patches/react-native-vision-camera+2.16.5+001+fix-boost-dependency.patch similarity index 56% rename from patches/react-native-vision-camera+2.16.2+001+fix-boost-dependency.patch rename to patches/react-native-vision-camera+2.16.5+001+fix-boost-dependency.patch index ef4fbf1d5084..3afc4573985d 100644 --- a/patches/react-native-vision-camera+2.16.2+001+fix-boost-dependency.patch +++ b/patches/react-native-vision-camera+2.16.5+001+fix-boost-dependency.patch @@ -1,8 +1,17 @@ diff --git a/node_modules/react-native-vision-camera/android/build.gradle b/node_modules/react-native-vision-camera/android/build.gradle -index d308e15..2d87d8e 100644 +index ddfa243..bafffc3 100644 --- a/node_modules/react-native-vision-camera/android/build.gradle +++ b/node_modules/react-native-vision-camera/android/build.gradle -@@ -347,7 +347,7 @@ if (ENABLE_FRAME_PROCESSORS) { +@@ -334,7 +334,7 @@ if (ENABLE_FRAME_PROCESSORS) { + def thirdPartyVersions = new Properties() + thirdPartyVersions.load(new FileInputStream(thirdPartyVersionsFile)) + +- def BOOST_VERSION = thirdPartyVersions["BOOST_VERSION"] ++ def BOOST_VERSION = thirdPartyVersions["BOOST_VERSION"] ?: "1.83.0" + def boost_file = new File(downloadsDir, "boost_${BOOST_VERSION}.tar.gz") + def DOUBLE_CONVERSION_VERSION = thirdPartyVersions["DOUBLE_CONVERSION_VERSION"] + def double_conversion_file = new File(downloadsDir, "double-conversion-${DOUBLE_CONVERSION_VERSION}.tar.gz") +@@ -352,7 +352,7 @@ if (ENABLE_FRAME_PROCESSORS) { task downloadBoost(dependsOn: createNativeDepsDirectories, type: Download) { def transformedVersion = BOOST_VERSION.replace("_", ".") diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index 32a98fa47a65..156e01be6ec6 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -9,6 +9,8 @@ import * as SessionUtils from '@libs/SessionUtils'; import * as Session from '@userActions/Session'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import Navigation from '@libs/Navigation/Navigation'; const propTypes = { /** The details about the account that the user is signing in with */ @@ -59,11 +61,19 @@ function LogOutPreviousUserPage(props) { const shortLivedAuthToken = lodashGet(props, 'route.params.shortLivedAuthToken', ''); Session.signInWithShortLivedAuthToken(email, shortLivedAuthToken); } - }); - // We only want to run this effect once on mount (when the page first loads after transitioning from OldDot) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [initUrl]); + const exitTo = lodashGet(props, 'route.params.exitTo', ''); + // We don't want to navigate to the exitTo route when creating a new workspace from a deep link, + // because we already handle creating the optimistic policy and navigating to it in App.setUpPoliciesAndNavigate, + // which is already called when AuthScreens mounts. + if (exitTo && exitTo !== ROUTES.WORKSPACE_NEW && !props.account.isLoading && !isLoggingInAsNewUser) { + Navigation.isNavigationReady().then(() => { + Navigation.goBack(); + Navigation.navigate(exitTo); + }); + } + }); + }, [initUrl, props]); return ; } From fcae238b58cffc976cceafa20ddcaa00aeddcf9c Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 16 Jan 2024 10:38:38 +0100 Subject: [PATCH 104/660] fix nav --- src/pages/LogOutPreviousUserPage.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index 156e01be6ec6..a69403d8c916 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -68,8 +68,7 @@ function LogOutPreviousUserPage(props) { // which is already called when AuthScreens mounts. if (exitTo && exitTo !== ROUTES.WORKSPACE_NEW && !props.account.isLoading && !isLoggingInAsNewUser) { Navigation.isNavigationReady().then(() => { - Navigation.goBack(); - Navigation.navigate(exitTo); + Navigation.navigate(exitTo, "FORCED_UP"); }); } }); From 6f91c905b1a12495f8a16eca8190dde7f2f3cb8d Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 16 Jan 2024 10:44:35 +0100 Subject: [PATCH 105/660] fix lint type --- src/libs/InitialUrlContext/index.ts | 2 +- src/pages/LogOutPreviousUserPage.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/InitialUrlContext/index.ts b/src/libs/InitialUrlContext/index.ts index 989f3369585f..a87417fe4cc6 100644 --- a/src/libs/InitialUrlContext/index.ts +++ b/src/libs/InitialUrlContext/index.ts @@ -1,5 +1,5 @@ import {createContext} from 'react'; -import {Route} from '@src/ROUTES'; +import type {Route} from '@src/ROUTES'; /** Initial url that will be opened when NewDot is embedded into Hybrid App. */ const InitialUrlContext = createContext(undefined); diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index a69403d8c916..5efe9926e270 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -5,12 +5,12 @@ import {Linking, NativeModules} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import InitialUrlContext from '@libs/InitialUrlContext'; +import Navigation from '@libs/Navigation/Navigation'; import * as SessionUtils from '@libs/SessionUtils'; import * as Session from '@userActions/Session'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import Navigation from '@libs/Navigation/Navigation'; const propTypes = { /** The details about the account that the user is signing in with */ @@ -68,7 +68,7 @@ function LogOutPreviousUserPage(props) { // which is already called when AuthScreens mounts. if (exitTo && exitTo !== ROUTES.WORKSPACE_NEW && !props.account.isLoading && !isLoggingInAsNewUser) { Navigation.isNavigationReady().then(() => { - Navigation.navigate(exitTo, "FORCED_UP"); + Navigation.navigate(exitTo, 'FORCED_UP'); }); } }); From f6d681cb66cf6df5dbc1fbe068b67b0b5e21c14a Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 16 Jan 2024 11:06:27 +0100 Subject: [PATCH 106/660] fix nav --- src/pages/LogOutPreviousUserPage.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index 5efe9926e270..801e8352e8e1 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -68,7 +68,9 @@ function LogOutPreviousUserPage(props) { // which is already called when AuthScreens mounts. if (exitTo && exitTo !== ROUTES.WORKSPACE_NEW && !props.account.isLoading && !isLoggingInAsNewUser) { Navigation.isNavigationReady().then(() => { - Navigation.navigate(exitTo, 'FORCED_UP'); + // remove this screen and navigate to exit route + Navigation.goBack() + Navigation.navigate(exitTo); }); } }); From b8aedfc659d66a827c01cfc585954eb157a96ae6 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 16 Jan 2024 11:13:17 +0100 Subject: [PATCH 107/660] Update getMoneyRequestInformation params order --- src/libs/actions/IOU.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index ae6c13163fad..49ee8f612feb 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -682,16 +682,16 @@ function getMoneyRequestInformation( currency: string, created: string, merchant: string, + receipt: Receipt | undefined, + existingTransactionID: string | undefined, + category: string | undefined, + tag: string | undefined, + billable: boolean | undefined, + policy: OnyxTypes.Policy | EmptyObject | undefined, + policyTags: OnyxTypes.PolicyTags | undefined, + policyCategories: OnyxTypes.PolicyCategories | undefined, payeeAccountID = userAccountID, payeeEmail = currentUserEmail, - receipt?: Receipt, - existingTransactionID?: string, - category?: string, - tag?: string, - billable?: boolean, - policy?: OnyxTypes.Policy | EmptyObject, - policyTags?: OnyxTypes.PolicyTags, - policyCategories?: OnyxTypes.PolicyCategories, ): MoneyRequestInformation { const payerEmail = OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login ?? ''); const payerAccountID = Number(participant.accountID); @@ -920,8 +920,6 @@ function createDistanceRequest( currency, created, merchant, - userAccountID, - currentUserEmail, optimisticReceipt, undefined, category, @@ -930,6 +928,8 @@ function createDistanceRequest( policy, policyTags, policyCategories, + userAccountID, + currentUserEmail, ); type CreateDistanceRequestParams = { @@ -1244,8 +1244,6 @@ function requestMoney( currency, created, merchant, - payeeAccountID, - payeeEmail, receipt, undefined, category, @@ -1254,6 +1252,8 @@ function requestMoney( policy, policyTags, policyCategories, + payeeAccountID, + payeeEmail, ); const activeReportID = isMoneyRequestReport ? report.reportID : chatReport.reportID; From 20cef421721c3bbfe48ef638eb5ea132ccca99db Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 16 Jan 2024 11:14:43 +0100 Subject: [PATCH 108/660] prettier --- src/pages/LogOutPreviousUserPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index 801e8352e8e1..e38c41aed29d 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -69,7 +69,7 @@ function LogOutPreviousUserPage(props) { if (exitTo && exitTo !== ROUTES.WORKSPACE_NEW && !props.account.isLoading && !isLoggingInAsNewUser) { Navigation.isNavigationReady().then(() => { // remove this screen and navigate to exit route - Navigation.goBack() + Navigation.goBack(); Navigation.navigate(exitTo); }); } From 7e7d69752bf13a4a2eebf46a9ecfbcb7fd75816b Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 16 Jan 2024 11:26:03 +0100 Subject: [PATCH 109/660] Migrate to TS new parts of the code --- src/libs/actions/IOU.ts | 44 +++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 14e2b2ecff19..9bf65b5c8891 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1089,28 +1089,26 @@ function getUpdateMoneyRequestParams( }, }); - if (isScanning && (_.has(transactionChanges, 'amount') || _.has(transactionChanges, 'currency'))) { + if (isScanning && ('amount' in transactionChanges || 'currency' in transactionChanges)) { optimisticData.push( - ...[ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.reportID}`, - value: { - [transactionThread.parentReportActionID]: { - whisperedToAccountIDs: [], - }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport?.reportID}`, + value: { + [transactionThread?.parentReportActionID ?? '']: { + whisperedToAccountIDs: [], }, }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport.parentReportID}`, - value: { - [iouReport.parentReportActionID]: { - whisperedToAccountIDs: [], - }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${iouReport?.parentReportID}`, + value: { + [iouReport?.parentReportActionID ?? '']: { + whisperedToAccountIDs: [], }, }, - ], + }, ); } @@ -1227,15 +1225,9 @@ function updateMoneyRequestTag(transactionID: string, transactionThreadReportID: API.write('UpdateMoneyRequestTag', params, onyxData); } -/** - * Updates the description of a money request - * - * @param {String} transactionID - * @param {Number} transactionThreadReportID - * @param {String} comment - */ -function updateMoneyRequestDescription(transactionID, transactionThreadReportID, comment) { - const transactionChanges = { +/** Updates the description of a money request */ +function updateMoneyRequestDescription(transactionID: string, transactionThreadReportID: string, comment: string) { + const transactionChanges: TransactionChanges = { comment, }; const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, true); From af0257b967be6bc46ea5c4a6f4dce23d712680bb Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 16 Jan 2024 11:27:53 +0100 Subject: [PATCH 110/660] adjustments --- .../@oguzhnatly+react-native-image-manipulator+1.0.5.patch | 4 ++-- src/pages/settings/InitialSettingsPage.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/patches/@oguzhnatly+react-native-image-manipulator+1.0.5.patch b/patches/@oguzhnatly+react-native-image-manipulator+1.0.5.patch index eb5ef2da6247..d5a390daf201 100644 --- a/patches/@oguzhnatly+react-native-image-manipulator+1.0.5.patch +++ b/patches/@oguzhnatly+react-native-image-manipulator+1.0.5.patch @@ -7,13 +7,13 @@ index 3a1a548..fe030bb 100644 android { - compileSdkVersion 28 -+ compileSdkVersion 33 ++ compileSdkVersion 34 buildToolsVersion "28.0.3" defaultConfig { minSdkVersion 16 - targetSdkVersion 28 -+ targetSdkVersion 33 ++ targetSdkVersion 34 versionCode 1 versionName "1.0" } diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index ffecf9cd2095..e01157b53570 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -294,7 +294,7 @@ function InitialSettingsPage(props) { }, ...defaultMenuItems, ], - (item) => item.translationKey !== 'initialSettingsPage.signOut', + (item) => item.translationKey !== 'initialSettingsPage.signOut' && item.translationKey !== 'initialSettingsPage.goToExpensifyClassic', ); return hybridAppMenuItems; From a41114b54e5d82296fb5b871f8f9d0834da625be Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 16 Jan 2024 13:17:56 +0100 Subject: [PATCH 111/660] fix patches --- .../@react-native+gradle-plugin+0.72.11.patch | 83 ------------------- ...ve-community+cli-platform-ios+12.3.0.patch | 52 ++++++++++++ patches/expo-modules-autolinking+1.9.0.patch | 19 +++++ 3 files changed, 71 insertions(+), 83 deletions(-) delete mode 100644 patches/@react-native+gradle-plugin+0.72.11.patch create mode 100644 patches/@react-native-community+cli-platform-ios+12.3.0.patch diff --git a/patches/@react-native+gradle-plugin+0.72.11.patch b/patches/@react-native+gradle-plugin+0.72.11.patch deleted file mode 100644 index 8609c992b4d6..000000000000 --- a/patches/@react-native+gradle-plugin+0.72.11.patch +++ /dev/null @@ -1,83 +0,0 @@ -diff --git a/node_modules/@react-native/gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt b/node_modules/@react-native/gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt -index 062dcc7..cb400ec 100644 ---- a/node_modules/@react-native/gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt -+++ b/node_modules/@react-native/gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt -@@ -15,6 +15,7 @@ import com.facebook.react.tasks.GenerateCodegenArtifactsTask - import com.facebook.react.tasks.GenerateCodegenSchemaTask - import com.facebook.react.utils.AgpConfiguratorUtils.configureBuildConfigFields - import com.facebook.react.utils.AgpConfiguratorUtils.configureDevPorts -+import com.facebook.react.utils.AgpConfiguratorUtils.configureNamespaceForLibraries - import com.facebook.react.utils.BackwardCompatUtils.configureBackwardCompatibilityReactMap - import com.facebook.react.utils.DependencyUtils.configureDependencies - import com.facebook.react.utils.DependencyUtils.configureRepositories -@@ -64,6 +65,7 @@ class ReactPlugin : Plugin { - - configureReactNativeNdk(project, extension) - configureBuildConfigFields(project) -+ configureNamespaceForLibraries(project) - configureDevPorts(project) - configureBackwardCompatibilityReactMap(project) - -diff --git a/node_modules/@react-native/gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt b/node_modules/@react-native/gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt -index 3bf0085..59397b4 100644 ---- a/node_modules/@react-native/gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt -+++ b/node_modules/@react-native/gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt -@@ -8,11 +8,17 @@ - package com.facebook.react.utils - - import com.android.build.api.variant.AndroidComponentsExtension -+import com.facebook.react.ReactExtension - import com.facebook.react.utils.ProjectUtils.isHermesEnabled - import com.facebook.react.utils.ProjectUtils.isNewArchEnabled - import org.gradle.api.Action - import org.gradle.api.Project - import org.gradle.api.plugins.AppliedPlugin -+import java.io.File -+import com.android.build.gradle.LibraryExtension -+import javax.xml.parsers.DocumentBuilder -+import javax.xml.parsers.DocumentBuilderFactory -+import org.w3c.dom.Element - - @Suppress("UnstableApiUsage") - internal object AgpConfiguratorUtils { -@@ -49,6 +55,40 @@ internal object AgpConfiguratorUtils { - project.pluginManager.withPlugin("com.android.application", action) - project.pluginManager.withPlugin("com.android.library", action) - } -+ -+ fun configureNamespaceForLibraries(appProject: Project) { -+ appProject.rootProject.allprojects { subproject -> -+ subproject.pluginManager.withPlugin("com.android.library") { -+ subproject.extensions.getByType(AndroidComponentsExtension::class.java).finalizeDsl { ext -> -+ if(ext.namespace == null){ -+ val android = subproject.extensions.getByType(LibraryExtension::class.java) -+ val manifestFile = android.sourceSets.getByName("main").manifest.srcFile -+ -+ manifestFile.takeIf { it.exists() }?.let { file -> -+ getPackageNameFromManifest(file)?.let { packageName -> -+ ext.namespace = packageName -+ } -+ } -+ } -+ } -+ } -+ } -+ } -+ fun getPackageNameFromManifest(manifest: File): String? { -+ val factory: DocumentBuilderFactory = DocumentBuilderFactory.newInstance() -+ val builder: DocumentBuilder = factory.newDocumentBuilder() -+ -+ try { -+ val xmlDocument = builder.parse(manifest) -+ -+ val manifestElement = xmlDocument.getElementsByTagName("manifest").item(0) as? Element -+ val packageName = manifestElement?.getAttribute("package") -+ -+ return if (packageName.isNullOrEmpty()) null else packageName -+ } catch (e: Exception) { -+ return null -+ } -+ } - } - - const val DEFAULT_DEV_SERVER_PORT = "8081" 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 new file mode 100644 index 000000000000..cfae504e44fa --- /dev/null +++ b/patches/@react-native-community+cli-platform-ios+12.3.0.patch @@ -0,0 +1,52 @@ +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 +--- 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 @@ + require 'pathname' + require 'cocoapods' + +-def use_native_modules!(config = nil) ++def updateConfig(config = nil) + if (config.is_a? String) + Pod::UI.warn("Passing custom root to use_native_modules! is deprecated.", + [ +@@ -24,7 +24,6 @@ def use_native_modules!(config = nil) + # Resolving the path the RN CLI. The `@react-native-community/cli` module may not be there for certain package managers, so we fall back to resolving it through `react-native` package, that's always present in RN projects + cli_resolve_script = "try {console.log(require('@react-native-community/cli').bin);} catch (e) {console.log(require('react-native/cli').bin);}" + cli_bin = Pod::Executable.execute_command("node", ["-e", cli_resolve_script], true).strip +- + if (!config) + json = [] + +@@ -36,10 +35,30 @@ def use_native_modules!(config = nil) + + config = JSON.parse(json.join("\n")) + end ++end ++ ++def use_native_modules!(config = nil) ++ if (ENV['REACT_NATIVE_DIR']) ++ Dir.chdir(ENV['REACT_NATIVE_DIR']) do ++ config = updateConfig(config) ++ end ++ else ++ config = updateConfig(config) ++ end + + project_root = Pathname.new(config["project"]["ios"]["sourceDir"]) + ++ if(ENV["PROJECT_ROOT_DIR"]) ++ project_root = File.join(Dir.pwd, ENV["PROJECT_ROOT_DIR"]) ++ ++ 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/patches/expo-modules-autolinking+1.9.0.patch b/patches/expo-modules-autolinking+1.9.0.patch index 363dd5dc3dab..7b171339efc5 100644 --- a/patches/expo-modules-autolinking+1.9.0.patch +++ b/patches/expo-modules-autolinking+1.9.0.patch @@ -1,3 +1,22 @@ +diff --git a/node_modules/expo-modules-autolinking/scripts/android/autolinking_implementation.gradle b/node_modules/expo-modules-autolinking/scripts/android/autolinking_implementation.gradle +index 92f1fd6..ada01ad 100644 +--- a/node_modules/expo-modules-autolinking/scripts/android/autolinking_implementation.gradle ++++ b/node_modules/expo-modules-autolinking/scripts/android/autolinking_implementation.gradle +@@ -149,12 +149,13 @@ class ExpoAutolinkingManager { + } + + static private String[] convertOptionsToCommandArgs(String command, Map options) { ++ def expoPath = options.searchPaths ? "../react-native/node_modules/expo" : "expo" + String[] args = [ + 'node', + '--no-warnings', + '--eval', + // Resolve the `expo` > `expo-modules-autolinking` chain from the project root +- 'require(require.resolve(\'expo-modules-autolinking\', { paths: [require.resolve(\'expo\')] }))(process.argv.slice(1))', ++ "require(require.resolve(\'expo-modules-autolinking\', { paths: [require.resolve(\'${expoPath}\')] }))(process.argv.slice(1))", + '--', + command, + '--platform', diff --git a/node_modules/expo-modules-autolinking/scripts/ios/project_integrator.rb b/node_modules/expo-modules-autolinking/scripts/ios/project_integrator.rb index c3047a8..6eff107 100644 --- a/node_modules/expo-modules-autolinking/scripts/ios/project_integrator.rb From cb27d13570a371ee539f1f170b09c34ed0bc3999 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 16 Jan 2024 13:29:28 +0100 Subject: [PATCH 112/660] fix cli patch --- ...ct-native-community+cli-platform-android+12.3.0.patch | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/patches/@react-native-community+cli-platform-android+12.3.0.patch b/patches/@react-native-community+cli-platform-android+12.3.0.patch index b6805a4fd93e..7289f652232a 100644 --- a/patches/@react-native-community+cli-platform-android+12.3.0.patch +++ b/patches/@react-native-community+cli-platform-android+12.3.0.patch @@ -1,5 +1,5 @@ diff --git a/node_modules/@react-native-community/cli-platform-android/native_modules.gradle b/node_modules/@react-native-community/cli-platform-android/native_modules.gradle -index bbfa7f7..6e024fd 100644 +index bbfa7f7..00d14dd 100644 --- a/node_modules/@react-native-community/cli-platform-android/native_modules.gradle +++ b/node_modules/@react-native-community/cli-platform-android/native_modules.gradle @@ -140,6 +140,7 @@ class ReactNativeModules { @@ -22,12 +22,9 @@ index bbfa7f7..6e024fd 100644 def (nativeModules, reactNativeModulesBuildVariants, androidProject, reactNativeVersion) = this.getReactNativeConfig() this.reactNativeModules = nativeModules -@@ -414,12 +416,12 @@ class ReactNativeModules { - * - * @todo: `fastlane` has been reported to not work too. +@@ -416,10 +418,10 @@ class ReactNativeModules { */ -- def cliResolveScript = "try {console.log(require('@react-native-community/cli').bin);} catch (e) {console.log(require('react-native/cli').bin);}" -+ def cliResolveScript = "try {console.log(require('../react-native/node_modules/@react-native-community/cli').bin);} catch (e) {console.log(require('../react-native/node_modules/react-native/cli').bin);}" + def cliResolveScript = "try {console.log(require('@react-native-community/cli').bin);} catch (e) {console.log(require('react-native/cli').bin);}" String[] nodeCommand = ["node", "-e", cliResolveScript] - def cliPath = this.getCommandOutput(nodeCommand, this.root) + def cliPath = this.getCommandOutput(nodeCommand, this.rnRoot) From 3bec4a39b8b9e0a50f1d4877bf0855f7815e1d96 Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Tue, 16 Jan 2024 15:23:52 +0100 Subject: [PATCH 113/660] Migrate ShareCodePage to functional component and to TS --- src/libs/UserUtils.ts | 4 +- src/pages/ShareCodePage.js | 137 ------------------ src/pages/ShareCodePage.tsx | 121 ++++++++++++++++ .../home/report/ReportDetailsShareCodePage.js | 31 ---- .../report/ReportDetailsShareCodePage.tsx | 17 +++ 5 files changed, 140 insertions(+), 170 deletions(-) delete mode 100644 src/pages/ShareCodePage.js create mode 100644 src/pages/ShareCodePage.tsx delete mode 100644 src/pages/home/report/ReportDetailsShareCodePage.js create mode 100644 src/pages/home/report/ReportDetailsShareCodePage.tsx diff --git a/src/libs/UserUtils.ts b/src/libs/UserUtils.ts index 6ec386679a32..f5973e854fd4 100644 --- a/src/libs/UserUtils.ts +++ b/src/libs/UserUtils.ts @@ -176,8 +176,8 @@ function getAvatar(avatarSource?: AvatarSource, accountID?: number): AvatarSourc * @param avatarURL - the avatar source from user's personalDetails * @param accountID - the accountID of the user */ -function getAvatarUrl(avatarURL: string, accountID: number): string { - return isDefaultAvatar(avatarURL) ? getDefaultAvatarURL(accountID) : avatarURL; +function getAvatarUrl(avatarSource: AvatarSource, accountID: number): AvatarSource { + return isDefaultAvatar(avatarSource) ? getDefaultAvatarURL(accountID) : avatarSource; } /** diff --git a/src/pages/ShareCodePage.js b/src/pages/ShareCodePage.js deleted file mode 100644 index 344e7f4bc886..000000000000 --- a/src/pages/ShareCodePage.js +++ /dev/null @@ -1,137 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import {ScrollView, View} from 'react-native'; -import _ from 'underscore'; -import expensifyLogo from '@assets/images/expensify-logo-round-transparent.png'; -import ContextMenuItem from '@components/ContextMenuItem'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import * as Expensicons from '@components/Icon/Expensicons'; -import MenuItem from '@components/MenuItem'; -import QRShareWithDownload from '@components/QRShare/QRShareWithDownload'; -import ScreenWrapper from '@components/ScreenWrapper'; -import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails'; -import withEnvironment from '@components/withEnvironment'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; -import withThemeStyles, {withThemeStylesPropTypes} from '@components/withThemeStyles'; -import Clipboard from '@libs/Clipboard'; -import compose from '@libs/compose'; -import getPlatform from '@libs/getPlatform'; -import Navigation from '@libs/Navigation/Navigation'; -import * as ReportUtils from '@libs/ReportUtils'; -import * as Url from '@libs/Url'; -import * as UserUtils from '@libs/UserUtils'; -import CONST from '@src/CONST'; -import ROUTES from '@src/ROUTES'; -import reportPropTypes from './reportPropTypes'; - -const propTypes = { - /** The report currently being looked at */ - report: reportPropTypes, - - /** The string value representing the URL of the current environment */ - environmentURL: PropTypes.string.isRequired, - - ...withLocalizePropTypes, - ...withCurrentUserPersonalDetailsPropTypes, - ...withThemeStylesPropTypes, -}; - -const defaultProps = { - report: undefined, - ...withCurrentUserPersonalDetailsDefaultProps, -}; - -// eslint-disable-next-line react/prefer-stateless-function -class ShareCodePage extends React.Component { - qrCodeRef = React.createRef(); - - /** - * @param {Boolean} isReport - * @return {String|string|*} - */ - getSubtitle(isReport) { - if (ReportUtils.isExpenseReport(this.props.report)) { - return ReportUtils.getPolicyName(this.props.report); - } - if (ReportUtils.isMoneyRequestReport(this.props.report)) { - // generate subtitle from participants - return _.map(ReportUtils.getVisibleMemberIDs(this.props.report), (accountID) => ReportUtils.getDisplayNameForParticipant(accountID)).join(' & '); - } - - if (isReport) { - return ReportUtils.getParentNavigationSubtitle(this.props.report).workspaceName || ReportUtils.getChatRoomSubtitle(this.props.report); - } - - return this.props.formatPhoneNumber(this.props.session.email); - } - - render() { - const isReport = this.props.report != null && this.props.report.reportID != null; - const title = isReport ? ReportUtils.getReportName(this.props.report) : this.props.currentUserPersonalDetails.displayName; - const subtitle = this.getSubtitle(isReport); - const urlWithTrailingSlash = Url.addTrailingForwardSlash(this.props.environmentURL); - const url = isReport - ? `${urlWithTrailingSlash}${ROUTES.REPORT_WITH_ID.getRoute(this.props.report.reportID)}` - : `${urlWithTrailingSlash}${ROUTES.PROFILE.getRoute(this.props.session.accountID)}`; - - const platform = getPlatform(); - const isNative = platform === CONST.PLATFORM.IOS || platform === CONST.PLATFORM.ANDROID; - - return ( - - Navigation.goBack(isReport ? ROUTES.REPORT_WITH_ID_DETAILS.getRoute(this.props.report.reportID) : ROUTES.SETTINGS)} - /> - - - - - - - - Clipboard.setString(url)} - /> - - {isNative && ( - this.qrCodeRef.current?.download()} - /> - )} - - Navigation.navigate(ROUTES.REFERRAL_DETAILS_MODAL.getRoute(CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SHARE_CODE))} - /> - - - - ); - } -} - -ShareCodePage.propTypes = propTypes; -ShareCodePage.defaultProps = defaultProps; -ShareCodePage.displayName = 'ShareCodePage'; - -export default compose(withEnvironment, withLocalize, withCurrentUserPersonalDetails, withThemeStyles)(ShareCodePage); diff --git a/src/pages/ShareCodePage.tsx b/src/pages/ShareCodePage.tsx new file mode 100644 index 000000000000..9adeffedefac --- /dev/null +++ b/src/pages/ShareCodePage.tsx @@ -0,0 +1,121 @@ +import React, {useRef} from 'react'; +import {ScrollView, View} from 'react-native'; +import type {ImageSourcePropType} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; +import expensifyLogo from '@assets/images/expensify-logo-round-transparent.png'; +import ContextMenuItem from '@components/ContextMenuItem'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import * as Expensicons from '@components/Icon/Expensicons'; +import MenuItem from '@components/MenuItem'; +import QRShareWithDownload from '@components/QRShare/QRShareWithDownload'; +import type QRShareWithDownloadHandle from '@components/QRShare/QRShareWithDownload/types'; +import ScreenWrapper from '@components/ScreenWrapper'; +import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; +import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; +import useEnvironment from '@hooks/useEnvironment'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import Clipboard from '@libs/Clipboard'; +import getPlatform from '@libs/getPlatform'; +import Navigation from '@libs/Navigation/Navigation'; +import * as ReportUtils from '@libs/ReportUtils'; +import * as Url from '@libs/Url'; +import * as UserUtils from '@libs/UserUtils'; +import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; +import type {Report, Session} from '@src/types/onyx'; + +type ShareCodePageOnyxProps = WithCurrentUserPersonalDetailsProps & { + session: OnyxEntry; +}; + +type ShareCodePageProps = ShareCodePageOnyxProps & { + /** The report currently being looked at */ + report?: Report; +}; + +function ShareCodePage({report, session, currentUserPersonalDetails}: ShareCodePageProps) { + const themeStyles = useThemeStyles(); + const {translate} = useLocalize(); + const {environmentURL} = useEnvironment(); + const qrCodeRef = useRef(null); + + const getSubtitle = () => { + if (report) { + if (ReportUtils.isExpenseReport(report)) { + return ReportUtils.getPolicyName(report); + } + if (ReportUtils.isMoneyRequestReport(report)) { + // generate subtitle from participants + return ReportUtils.getVisibleMemberIDs(report) + .map((accountID) => ReportUtils.getDisplayNameForParticipant(accountID)) + .join(' & '); + } + + return ReportUtils.getParentNavigationSubtitle(report).workspaceName ?? ReportUtils.getChatRoomSubtitle(report); + } + + return session?.email; + }; + + const isReport = !!report?.reportID; + const title = isReport ? ReportUtils.getReportName(report) : currentUserPersonalDetails.displayName ?? ''; + const subtitle = getSubtitle(); + const urlWithTrailingSlash = Url.addTrailingForwardSlash(environmentURL); + const url = isReport ? `${urlWithTrailingSlash}${ROUTES.REPORT_WITH_ID.getRoute(report.reportID)}` : `${urlWithTrailingSlash}${ROUTES.PROFILE.getRoute(session?.accountID ?? '')}`; + const platform = getPlatform(); + const isNative = platform === CONST.PLATFORM.IOS || platform === CONST.PLATFORM.ANDROID; + + return ( + + Navigation.goBack(isReport ? ROUTES.REPORT_WITH_ID_DETAILS.getRoute(report.reportID) : ROUTES.SETTINGS)} + /> + + + + + + + + Clipboard.setString(url)} + /> + + {isNative && ( + + )} + + Navigation.navigate(ROUTES.REFERRAL_DETAILS_MODAL.getRoute(CONST.REFERRAL_PROGRAM.CONTENT_TYPES.SHARE_CODE))} + /> + + + + ); +} + +ShareCodePage.displayName = 'ShareCodePage'; + +export default withCurrentUserPersonalDetails(ShareCodePage); diff --git a/src/pages/home/report/ReportDetailsShareCodePage.js b/src/pages/home/report/ReportDetailsShareCodePage.js deleted file mode 100644 index 0ef2d46029ea..000000000000 --- a/src/pages/home/report/ReportDetailsShareCodePage.js +++ /dev/null @@ -1,31 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import reportPropTypes from '@pages/reportPropTypes'; -import ShareCodePage from '@pages/ShareCodePage'; -import withReportOrNotFound from './withReportOrNotFound'; - -const propTypes = { - /** Navigation route context info provided by react navigation */ - route: PropTypes.shape({ - /** Route specific parameters used on this screen */ - params: PropTypes.shape({ - reportID: PropTypes.string, - }).isRequired, - }).isRequired, - - /** The report currently being looked at */ - report: reportPropTypes, -}; - -const defaultProps = { - report: undefined, -}; - -function ReportDetailsShareCodePage(props) { - return ; -} - -ReportDetailsShareCodePage.propTypes = propTypes; -ReportDetailsShareCodePage.defaultProps = defaultProps; - -export default withReportOrNotFound()(ReportDetailsShareCodePage); diff --git a/src/pages/home/report/ReportDetailsShareCodePage.tsx b/src/pages/home/report/ReportDetailsShareCodePage.tsx new file mode 100644 index 000000000000..f11895ede5a2 --- /dev/null +++ b/src/pages/home/report/ReportDetailsShareCodePage.tsx @@ -0,0 +1,17 @@ +import type {StackScreenProps} from '@react-navigation/stack'; +import React from 'react'; +import type {ReportDetailsNavigatorParamList} from '@libs/Navigation/types'; +import ShareCodePage from '@pages/ShareCodePage'; +import type SCREENS from '@src/SCREENS'; +import type {Report} from '@src/types/onyx'; +import withReportOrNotFound from './withReportOrNotFound'; + +type ReportDetailsShareCodePageProps = StackScreenProps & { + report: Report; +}; + +function ReportDetailsShareCodePage({report}: ReportDetailsShareCodePageProps) { + return ; +} + +export default withReportOrNotFound()(ReportDetailsShareCodePage); From 10ef66b7bee3d3b6ad0932bdf2f3c0a046dc7744 Mon Sep 17 00:00:00 2001 From: someone-here Date: Tue, 16 Jan 2024 20:19:01 +0530 Subject: [PATCH 114/660] Update spanish translations --- src/languages/es.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/languages/es.ts b/src/languages/es.ts index 29688d4a19ae..fb872528d916 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1033,9 +1033,9 @@ export default { }, }, reportDescriptionPage: { - roomDescription: 'Descripción de la habitación', - roomDescriptionOptional: 'Descripción de la habitación (opcional)', - explainerText: 'Establece una descripción personalizada para la habitación.', + roomDescription: 'Descripción de la sala de chat', + roomDescriptionOptional: 'Descripción de la sala de chat (opcional)', + explainerText: 'Establece una descripción personalizada para la sala de chat.', }, languagePage: { language: 'Idioma', From ff6fc296d10ae2a8c800de76fd93464f7f5ece61 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 16 Jan 2024 15:59:41 +0100 Subject: [PATCH 115/660] Minor code improvements --- src/libs/actions/IOU.ts | 58 ++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 9bf65b5c8891..40a51de1ba7d 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -131,14 +131,14 @@ type SendMoneyParamsData = { let betas: OnyxTypes.Beta[] = []; Onyx.connect({ key: ONYXKEYS.BETAS, - callback: (val) => (betas = val ?? []), + callback: (value) => (betas = value ?? []), }); let allPersonalDetails: OnyxTypes.PersonalDetailsList = {}; Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, - callback: (val) => { - allPersonalDetails = val ?? {}; + callback: (value) => { + allPersonalDetails = value ?? {}; }, }); @@ -146,20 +146,20 @@ let allReports: OnyxCollection = null; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, waitForCollectionCallback: true, - callback: (val) => (allReports = val), + callback: (value) => (allReports = value), }); let allTransactions: NonNullable> = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION, waitForCollectionCallback: true, - callback: (val) => { - if (!val) { + callback: (value) => { + if (!value) { allTransactions = {}; return; } - allTransactions = val; + allTransactions = value; }, }); @@ -167,8 +167,8 @@ let allTransactionDrafts: NonNullable> = { Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, waitForCollectionCallback: true, - callback: (val) => { - allTransactionDrafts = val ?? {}; + callback: (value) => { + allTransactionDrafts = value ?? {}; }, }); @@ -176,13 +176,13 @@ let allTransactionViolations: NonNullable { - if (!val) { + callback: (value) => { + if (!value) { allTransactionViolations = {}; return; } - allTransactionViolations = val; + allTransactionViolations = value; }, }); @@ -190,8 +190,8 @@ let allDraftSplitTransactions: NonNullable Onyx.connect({ key: ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT, waitForCollectionCallback: true, - callback: (val) => { - allDraftSplitTransactions = val ?? {}; + callback: (value) => { + allDraftSplitTransactions = value ?? {}; }, }); @@ -199,8 +199,8 @@ let allNextSteps: NonNullable> = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.NEXT_STEP, waitForCollectionCallback: true, - callback: (val) => { - allNextSteps = val ?? {}; + callback: (value) => { + allNextSteps = value ?? {}; }, }); @@ -208,25 +208,25 @@ let userAccountID = -1; let currentUserEmail = ''; Onyx.connect({ key: ONYXKEYS.SESSION, - callback: (val) => { - currentUserEmail = val?.email ?? ''; - userAccountID = val?.accountID ?? -1; + callback: (value) => { + currentUserEmail = value?.email ?? ''; + userAccountID = value?.accountID ?? -1; }, }); let currentUserPersonalDetails: OnyxTypes.PersonalDetails | EmptyObject = {}; Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, - callback: (val) => { - currentUserPersonalDetails = val?.[userAccountID] ?? {}; + callback: (value) => { + currentUserPersonalDetails = value?.[userAccountID] ?? {}; }, }); let currentDate: OnyxEntry = ''; Onyx.connect({ key: ONYXKEYS.CURRENT_DATE, - callback: (val) => { - currentDate = val; + callback: (value) => { + currentDate = value; }, }); @@ -1190,27 +1190,27 @@ function getUpdateMoneyRequestParams( /** * Updates the created date of a money request */ -function updateMoneyRequestDate(transactionID: string, transactionThreadReportID: string, val: string) { +function updateMoneyRequestDate(transactionID: string, transactionThreadReportID: string, value: string) { const transactionChanges: TransactionChanges = { - created: val, + created: value, }; const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, true); API.write('UpdateMoneyRequestDate', params, onyxData); } /** Updates the billable field of a money request */ -function updateMoneyRequestBillable(transactionID: string, transactionThreadReportID: string, val: boolean) { +function updateMoneyRequestBillable(transactionID: string, transactionThreadReportID: string, value: boolean) { const transactionChanges: TransactionChanges = { - billable: val, + billable: value, }; const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, true); API.write('UpdateMoneyRequestBillable', params, onyxData); } /** Updates the merchant field of a money request */ -function updateMoneyRequestMerchant(transactionID: string, transactionThreadReportID: string, val: string) { +function updateMoneyRequestMerchant(transactionID: string, transactionThreadReportID: string, value: string) { const transactionChanges: TransactionChanges = { - merchant: val, + merchant: value, }; const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, true); API.write('UpdateMoneyRequestMerchant', params, onyxData); From 03567bd9ddcc1bf61fd11b89927f1c7422857893 Mon Sep 17 00:00:00 2001 From: someone-here Date: Tue, 16 Jan 2024 22:25:27 +0530 Subject: [PATCH 116/660] Fix header for tasks --- src/pages/home/HeaderView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 72013c3f046d..f18a52ed2bb4 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -313,7 +313,7 @@ function HeaderView(props) { {subtitle} )} - {!_.isEmpty(reportDescription) && ( + {!_.isEmpty(reportDescription) && _.isEmpty(parentNavigationSubtitleData) && ( Date: Tue, 16 Jan 2024 18:35:30 +0100 Subject: [PATCH 117/660] Refactor types --- src/components/ButtonWithDropdownMenu.tsx | 9 ++++++--- src/components/Popover/types.ts | 2 +- src/components/PopoverMenu.tsx | 2 +- src/components/ProcessMoneyRequestHoldMenu.tsx | 2 +- src/components/SettlementButton.tsx | 10 +++++----- src/types/onyx/index.ts | 2 -- src/types/{onyx => utils}/AnchorAlignment.ts | 0 7 files changed, 14 insertions(+), 13 deletions(-) rename src/types/{onyx => utils}/AnchorAlignment.ts (100%) diff --git a/src/components/ButtonWithDropdownMenu.tsx b/src/components/ButtonWithDropdownMenu.tsx index 466c68229a32..011089af8ab9 100644 --- a/src/components/ButtonWithDropdownMenu.tsx +++ b/src/components/ButtonWithDropdownMenu.tsx @@ -9,15 +9,18 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import type {AnchorPosition} from '@styles/index'; import CONST from '@src/CONST'; +import type AnchorAlignment from '@src/types/utils/AnchorAlignment'; +import type DeepValueOf from '@src/types/utils/DeepValueOf'; import type IconAsset from '@src/types/utils/IconAsset'; import Button from './Button'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; -import type {AnchorAlignment} from './Popover/types'; import PopoverMenu from './PopoverMenu'; +type PaymentType = DeepValueOf; + type DropdownOption = { - value: string; + value: PaymentType; text: string; icon: IconAsset; iconWidth?: number; @@ -30,7 +33,7 @@ type ButtonWithDropdownMenuProps = { menuHeaderText?: string; /** Callback to execute when the main button is pressed */ - onPress: (event: GestureResponderEvent | KeyboardEvent | undefined, value: string) => void; + onPress: (event: GestureResponderEvent | KeyboardEvent | undefined, value: PaymentType) => void; /** Call the onPress function on main button when Enter key is pressed */ pressOnEnter?: boolean; diff --git a/src/components/Popover/types.ts b/src/components/Popover/types.ts index 298c95eeca76..4409e8a7e296 100644 --- a/src/components/Popover/types.ts +++ b/src/components/Popover/types.ts @@ -1,7 +1,7 @@ import type {PopoverAnchorPosition} from '@components/Modal/types'; import type BaseModalProps from '@components/Modal/types'; import type {WindowDimensionsProps} from '@components/withWindowDimensions/types'; -import type {AnchorAlignment} from '@src/types/onyx'; +import type AnchorAlignment from '@src/types/utils/AnchorAlignment'; type PopoverDimensions = { width: number; diff --git a/src/components/PopoverMenu.tsx b/src/components/PopoverMenu.tsx index 10599330945f..6990a453a931 100644 --- a/src/components/PopoverMenu.tsx +++ b/src/components/PopoverMenu.tsx @@ -9,7 +9,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import CONST from '@src/CONST'; import type {AnchorPosition} from '@src/styles'; -import type {AnchorAlignment} from '@src/types/onyx'; +import type AnchorAlignment from '@src/types/utils/AnchorAlignment'; import type IconAsset from '@src/types/utils/IconAsset'; import MenuItem from './MenuItem'; import PopoverWithMeasuredContent from './PopoverWithMeasuredContent'; diff --git a/src/components/ProcessMoneyRequestHoldMenu.tsx b/src/components/ProcessMoneyRequestHoldMenu.tsx index 1fd37b82afc8..a069bcfa1a07 100644 --- a/src/components/ProcessMoneyRequestHoldMenu.tsx +++ b/src/components/ProcessMoneyRequestHoldMenu.tsx @@ -2,7 +2,7 @@ import React from 'react'; import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import type {AnchorAlignment} from '@src/types/onyx'; +import type AnchorAlignment from '@src/types/utils/AnchorAlignment'; import Button from './Button'; import HoldMenuSectionList from './HoldMenuSectionList'; import type {PopoverAnchorPosition} from './Modal/types'; diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton.tsx index 4a4abea14c70..82da026d4014 100644 --- a/src/components/SettlementButton.tsx +++ b/src/components/SettlementButton.tsx @@ -1,4 +1,4 @@ -import type {MutableRefObject} from 'react'; +import type {RefObject} from 'react'; import React, {useEffect, useMemo} from 'react'; import type {GestureResponderEvent, StyleProp, View, ViewStyle} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; @@ -13,14 +13,15 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {ButtonSizeValue} from '@src/styles/utils/types'; -import type {AnchorAlignment, LastPaymentMethod, Report} from '@src/types/onyx'; +import type {LastPaymentMethod, Report} from '@src/types/onyx'; +import type AnchorAlignment from '@src/types/utils/AnchorAlignment'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import ButtonWithDropdownMenu from './ButtonWithDropdownMenu'; import * as Expensicons from './Icon/Expensicons'; import KYCWall from './KYCWall'; -type KYCFlowEvent = GestureResponderEvent | KeyboardEvent; +type KYCFlowEvent = GestureResponderEvent | KeyboardEvent | undefined; type TriggerKYCFlow = (event: KYCFlowEvent, iouPaymentType: string) => void; @@ -211,7 +212,7 @@ function SettlementButton({ anchorAlignment={kycWallAnchorAlignment} shouldShowPersonalBankAccountOption={shouldShowPersonalBankAccountOption} > - {(triggerKYCFlow: TriggerKYCFlow, buttonRef: MutableRefObject) => ( + {(triggerKYCFlow: TriggerKYCFlow, buttonRef: RefObject) => ( selectPaymentType(event, iouPaymentType, triggerKYCFlow)} pressOnEnter={pressOnEnter} options={paymentButtonOptions} - // @ts-expect-error TODO: Remove this once OptionsListUtils (https://github.com/Expensify/App/issues/25065) is migrated to TypeScript. style={style} buttonSize={buttonSize} anchorAlignment={paymentMethodDropdownAnchorAlignment} diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 8b2586db597a..3dd083d47865 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -1,6 +1,5 @@ import type Account from './Account'; import type AccountData from './AccountData'; -import type AnchorAlignment from './AnchorAlignment'; import type {BankAccountList} from './BankAccount'; import type BankAccount from './BankAccount'; import type Beta from './Beta'; @@ -142,5 +141,4 @@ export type { PolicyReportField, RecentlyUsedReportFields, LastPaymentMethod, - AnchorAlignment, }; diff --git a/src/types/onyx/AnchorAlignment.ts b/src/types/utils/AnchorAlignment.ts similarity index 100% rename from src/types/onyx/AnchorAlignment.ts rename to src/types/utils/AnchorAlignment.ts From 2ecaf16e8b9ba9024845c583715b290d7c949953 Mon Sep 17 00:00:00 2001 From: Fitsum Abebe Date: Tue, 16 Jan 2024 22:49:22 +0300 Subject: [PATCH 118/660] fix error message display bug on google signin --- src/pages/signin/LoginForm/BaseLoginForm.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/pages/signin/LoginForm/BaseLoginForm.js b/src/pages/signin/LoginForm/BaseLoginForm.js index 8fcea461eacd..5a6c8aed5050 100644 --- a/src/pages/signin/LoginForm/BaseLoginForm.js +++ b/src/pages/signin/LoginForm/BaseLoginForm.js @@ -276,13 +276,15 @@ function LoginForm(props) { textContentType="username" id="username" name="username" - onBlur={() => { - if (firstBlurred.current || !Visibility.isVisible() || !Visibility.hasFocus()) { - return; - } - firstBlurred.current = true; - validate(login); - }} + onBlur={() => + setTimeout(() => { + if (firstBlurred.current || !Visibility.isVisible() || !Visibility.hasFocus()) { + return; + } + firstBlurred.current = true; + validate(login); + }, 500) + } onChangeText={onTextInput} onSubmitEditing={validateAndSubmitForm} autoCapitalize="none" @@ -332,10 +334,10 @@ function LoginForm(props) { - e.preventDefault()}> + - e.preventDefault()}> + From d615392f8e14448a6a38f7b0607753dbb46210ff Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Tue, 16 Jan 2024 21:25:54 +0100 Subject: [PATCH 119/660] Fix types --- src/pages/ShareCodePage.tsx | 7 ++++--- .../home/report/ReportDetailsShareCodePage.tsx | 9 ++------- src/pages/home/report/withReportOrNotFound.tsx | 17 +++++++++++------ 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/pages/ShareCodePage.tsx b/src/pages/ShareCodePage.tsx index 9adeffedefac..670d6bed6cef 100644 --- a/src/pages/ShareCodePage.tsx +++ b/src/pages/ShareCodePage.tsx @@ -26,14 +26,15 @@ import ROUTES from '@src/ROUTES'; import type {Report, Session} from '@src/types/onyx'; type ShareCodePageOnyxProps = WithCurrentUserPersonalDetailsProps & { + /** Session info for the currently logged in user. */ session: OnyxEntry; -}; -type ShareCodePageProps = ShareCodePageOnyxProps & { /** The report currently being looked at */ - report?: Report; + report?: OnyxEntry; }; +type ShareCodePageProps = ShareCodePageOnyxProps; + function ShareCodePage({report, session, currentUserPersonalDetails}: ShareCodePageProps) { const themeStyles = useThemeStyles(); const {translate} = useLocalize(); diff --git a/src/pages/home/report/ReportDetailsShareCodePage.tsx b/src/pages/home/report/ReportDetailsShareCodePage.tsx index f11895ede5a2..28b1d5cd71d7 100644 --- a/src/pages/home/report/ReportDetailsShareCodePage.tsx +++ b/src/pages/home/report/ReportDetailsShareCodePage.tsx @@ -1,14 +1,9 @@ -import type {StackScreenProps} from '@react-navigation/stack'; import React from 'react'; -import type {ReportDetailsNavigatorParamList} from '@libs/Navigation/types'; import ShareCodePage from '@pages/ShareCodePage'; -import type SCREENS from '@src/SCREENS'; -import type {Report} from '@src/types/onyx'; +import type {WithReportOrNotFoundProps} from './withReportOrNotFound'; import withReportOrNotFound from './withReportOrNotFound'; -type ReportDetailsShareCodePageProps = StackScreenProps & { - report: Report; -}; +type ReportDetailsShareCodePageProps = WithReportOrNotFoundProps; function ReportDetailsShareCodePage({report}: ReportDetailsShareCodePageProps) { return ; diff --git a/src/pages/home/report/withReportOrNotFound.tsx b/src/pages/home/report/withReportOrNotFound.tsx index 7613bafeacdc..6a523a0e13ed 100644 --- a/src/pages/home/report/withReportOrNotFound.tsx +++ b/src/pages/home/report/withReportOrNotFound.tsx @@ -11,27 +11,30 @@ import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import ONYXKEYS from '@src/ONYXKEYS'; import type * as OnyxTypes from '@src/types/onyx'; -type OnyxProps = { +type WithReportOrNotFoundOnyxProps = { /** The report currently being looked at */ report: OnyxEntry; + /** The policies which the user has access to */ policies: OnyxCollection; + /** Beta features list */ betas: OnyxEntry; + /** Indicated whether the report data is loading */ isLoadingReportData: OnyxEntry; }; -type ComponentProps = OnyxProps & { +type WithReportOrNotFoundProps = WithReportOrNotFoundOnyxProps & { route: RouteProp<{params: {reportID: string}}>; }; export default function ( shouldRequireReportID = true, -): ( +): ( WrappedComponent: React.ComponentType>, -) => React.ComponentType, keyof OnyxProps>> { - return function (WrappedComponent: ComponentType>) { +) => React.ComponentType, keyof WithReportOrNotFoundOnyxProps>> { + return function (WrappedComponent: ComponentType>) { function WithReportOrNotFound(props: TProps, ref: ForwardedRef) { const contentShown = React.useRef(false); @@ -73,7 +76,7 @@ export default function ( WithReportOrNotFound.displayName = `withReportOrNotFound(${getComponentDisplayName(WrappedComponent)})`; - return withOnyx, OnyxProps>({ + return withOnyx, WithReportOrNotFoundOnyxProps>({ report: { key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`, }, @@ -89,3 +92,5 @@ export default function ( })(React.forwardRef(WithReportOrNotFound)); }; } + +export type {WithReportOrNotFoundProps}; From 46dec5946ecb854a0c32b6c6724606bac09089cc Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Tue, 16 Jan 2024 22:48:17 +0100 Subject: [PATCH 120/660] Rename isLoadingDistanceRequest -> isDistanceBeingCalculated --- src/components/ReportActionItem/ReportPreview.js | 2 +- src/libs/ReportUtils.ts | 2 +- src/libs/TransactionUtils.ts | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js index 4772ec8a55ac..332e45d32cb1 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.js @@ -166,7 +166,7 @@ function ReportPreview(props) { const lastThreeTransactionsWithReceipts = transactionsWithReceipts.slice(-3); const lastThreeReceipts = _.map(lastThreeTransactionsWithReceipts, (transaction) => ReceiptUtils.getThumbnailAndImageURIs(transaction)); const formattedMerchant = numberOfRequests === 1 && hasReceipts ? TransactionUtils.getMerchant(transactionsWithReceipts[0]) : null; - const hasOnlyLoadingDistanceRequests = hasOnlyDistanceRequests && _.every(transactionsWithReceipts, (transaction) => TransactionUtils.isLoadingDistanceRequest(transaction)); + const hasOnlyLoadingDistanceRequests = hasOnlyDistanceRequests && _.every(transactionsWithReceipts, (transaction) => TransactionUtils.isDistanceBeingCalculated(transaction)); const previewSubtitle = formattedMerchant || props.translate('iou.requestCount', { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 6458ae7368c7..b3218d4747d8 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1857,7 +1857,7 @@ function getTransactionDetails(transaction: OnyxEntry, createdDateF const currency = TransactionUtils.getCurrency(transaction); let formattedAmount; - if (TransactionUtils.isLoadingDistanceRequest(transaction)) { + if (TransactionUtils.isDistanceBeingCalculated(transaction)) { formattedAmount = Localize.translateLocal('common.tbd'); } else { formattedAmount = amount ? CurrencyUtils.convertToDisplayString(amount, currency) : ''; diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 9ab09a1978e1..e91ffb9490b8 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -309,11 +309,11 @@ function getOriginalAmount(transaction: Transaction): number { } /** - * Verify if the transaction is of Distance request and is not fully ready: + * Verify if the transaction is of Distance request and is expecting the distance to be calculated on the server: * - it has a zero amount, which means the request was created offline and expects the distance calculation from the server * - it is in `isLoading` state, which means the waypoints were updated offline and the distance requires re-calculation */ -function isLoadingDistanceRequest(transaction: OnyxEntry): boolean { +function isDistanceBeingCalculated(transaction: OnyxEntry): boolean { if (!transaction) { return false; } @@ -331,7 +331,7 @@ function getMerchant(transaction: OnyxEntry): string { } const merchant = transaction.modifiedMerchant ? transaction.modifiedMerchant : transaction.merchant ?? ''; - return isLoadingDistanceRequest(transaction) ? merchant.replace(CONST.REGEX.FIRST_SPACE, Localize.translateLocal('common.tbd')) : merchant; + return isDistanceBeingCalculated(transaction) ? merchant.replace(CONST.REGEX.FIRST_SPACE, Localize.translateLocal('common.tbd')) : merchant; } function getDistance(transaction: Transaction): number { @@ -586,7 +586,7 @@ export { isReceiptBeingScanned, getValidWaypoints, isDistanceRequest, - isLoadingDistanceRequest, + isDistanceBeingCalculated, isExpensifyCardTransaction, isCardTransaction, isPending, From 99fac30a5392b79446d95510727e618de489c03e Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Wed, 17 Jan 2024 07:32:21 +0100 Subject: [PATCH 121/660] Reuse existing logic --- src/pages/ShareCodePage.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pages/ShareCodePage.tsx b/src/pages/ShareCodePage.tsx index 670d6bed6cef..7b374810a4b3 100644 --- a/src/pages/ShareCodePage.tsx +++ b/src/pages/ShareCodePage.tsx @@ -41,8 +41,10 @@ function ShareCodePage({report, session, currentUserPersonalDetails}: ShareCodeP const {environmentURL} = useEnvironment(); const qrCodeRef = useRef(null); + const isReport = !!report?.reportID; + const getSubtitle = () => { - if (report) { + if (isReport) { if (ReportUtils.isExpenseReport(report)) { return ReportUtils.getPolicyName(report); } @@ -59,7 +61,6 @@ function ShareCodePage({report, session, currentUserPersonalDetails}: ShareCodeP return session?.email; }; - const isReport = !!report?.reportID; const title = isReport ? ReportUtils.getReportName(report) : currentUserPersonalDetails.displayName ?? ''; const subtitle = getSubtitle(); const urlWithTrailingSlash = Url.addTrailingForwardSlash(environmentURL); From 12970726137467b155756e1aa942e7ef112d0c92 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Wed, 17 Jan 2024 08:29:22 +0100 Subject: [PATCH 122/660] TS fixes after merging main --- .../home/report/ContextMenu/PopoverReportActionContextMenu.tsx | 2 +- src/types/onyx/Report.ts | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx index 46b783bca3f9..728baca6f425 100644 --- a/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx +++ b/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx @@ -237,7 +237,7 @@ function PopoverReportActionContextMenu(_props: never, ref: ForwardedRef (onComfirmDeleteModal.current = runAndResetCallback(onComfirmDeleteModal.current)); const reportAction = reportActionRef.current; if (ReportActionsUtils.isMoneyRequestAction(reportAction) && reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU) { - IOU.deleteMoneyRequest(reportAction?.originalMessage?.IOUTransactionID, reportAction); + IOU.deleteMoneyRequest(reportAction?.originalMessage?.IOUTransactionID ?? '', reportAction); } else if (reportAction) { Report.deleteReportComment(reportIDRef.current, reportAction); } diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index cd0f339382ce..57a36be1d99a 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -104,9 +104,6 @@ type Report = { /** The report visibility */ visibility?: ValueOf; - /** Whether the report has outstanding IOU */ - hasOutstandingIOU?: boolean; - /** Report cached total */ cachedTotal?: string; From 772370cf1c5e5c11f82f282b99fdc16256e69e39 Mon Sep 17 00:00:00 2001 From: Filip Solecki Date: Wed, 17 Jan 2024 09:41:24 +0100 Subject: [PATCH 123/660] Fix download code button --- src/components/MenuItem.tsx | 2 +- src/pages/ShareCodePage.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index 334fa9895205..5162a8227277 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -57,7 +57,7 @@ type NoIcon = { type MenuItemProps = (IconProps | AvatarProps | NoIcon) & { /** Function to fire when component is pressed */ - onPress?: (event: GestureResponderEvent | KeyboardEvent) => void; + onPress?: (event: GestureResponderEvent | KeyboardEvent) => void | Promise; /** Whether the menu item should be interactive at all */ interactive?: boolean; diff --git a/src/pages/ShareCodePage.tsx b/src/pages/ShareCodePage.tsx index 7b374810a4b3..62368b2520e6 100644 --- a/src/pages/ShareCodePage.tsx +++ b/src/pages/ShareCodePage.tsx @@ -103,7 +103,7 @@ function ShareCodePage({report, session, currentUserPersonalDetails}: ShareCodeP isAnonymousAction title={translate('common.download')} icon={Expensicons.Download} - onPress={qrCodeRef.current?.download} + onPress={() => qrCodeRef.current?.download()} /> )} From 2e905f70c40b9368674b23b90a5e1cca68ecca47 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Wed, 17 Jan 2024 11:36:15 +0100 Subject: [PATCH 124/660] fix nav for smart scan hybrid --- src/pages/LogInWithShortLivedAuthTokenPage.js | 9 ++++++++- src/pages/LogOutPreviousUserPage.js | 7 ++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/pages/LogInWithShortLivedAuthTokenPage.js b/src/pages/LogInWithShortLivedAuthTokenPage.js index 1fe9b67eef16..3be9ad64e8eb 100644 --- a/src/pages/LogInWithShortLivedAuthTokenPage.js +++ b/src/pages/LogInWithShortLivedAuthTokenPage.js @@ -13,8 +13,11 @@ import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; +import * as ReportUtils from '@libs/ReportUtils'; import * as Session from '@userActions/Session'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; const propTypes = { /** The parameters needed to authenticate with a short-lived token are in the URL */ @@ -71,7 +74,11 @@ function LogInWithShortLivedAuthTokenPage(props) { const exitTo = lodashGet(props, 'route.params.exitTo', ''); if (exitTo) { Navigation.isNavigationReady().then(() => { - Navigation.navigate(exitTo); + if (exitTo === '/request/new/scan') { + Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.TYPE.REQUEST, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, ReportUtils.generateReportID())); + } else { + Navigation.navigate(exitTo); + } }); } // The only dependencies of the effect are based on props.route diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index e38c41aed29d..9438b8663a14 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -6,6 +6,7 @@ import {withOnyx} from 'react-native-onyx'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import InitialUrlContext from '@libs/InitialUrlContext'; import Navigation from '@libs/Navigation/Navigation'; +import * as ReportUtils from '@libs/ReportUtils'; import * as SessionUtils from '@libs/SessionUtils'; import * as Session from '@userActions/Session'; import CONST from '@src/CONST'; @@ -70,7 +71,11 @@ function LogOutPreviousUserPage(props) { Navigation.isNavigationReady().then(() => { // remove this screen and navigate to exit route Navigation.goBack(); - Navigation.navigate(exitTo); + if (exitTo === '/request/new/scan') { + Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.TYPE.REQUEST, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, ReportUtils.generateReportID())) + } else { + Navigation.navigate(exitTo); + } }); } }); From 1621e4bf61dad08d35629265fdea5ac92c9ea3dd Mon Sep 17 00:00:00 2001 From: VickyStash Date: Wed, 17 Jan 2024 16:10:46 +0100 Subject: [PATCH 125/660] Reuse existing types instead of creating new one --- src/libs/ErrorUtils.ts | 4 ++-- src/libs/ReportActionsUtils.ts | 8 +++----- src/libs/actions/IOU.ts | 4 ++-- src/types/onyx/OnyxCommon.ts | 4 +--- src/types/onyx/ReportAction.ts | 2 +- 5 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/libs/ErrorUtils.ts b/src/libs/ErrorUtils.ts index 6c1540ccaa55..e985ab5fab64 100644 --- a/src/libs/ErrorUtils.ts +++ b/src/libs/ErrorUtils.ts @@ -1,6 +1,6 @@ import CONST from '@src/CONST'; import type {TranslationFlatObject, TranslationPaths} from '@src/languages/types'; -import type {ErrorFields, Errors, ErrorsObject} from '@src/types/onyx/OnyxCommon'; +import type {ErrorFields, Errors} from '@src/types/onyx/OnyxCommon'; import type Response from '@src/types/onyx/Response'; import DateUtils from './DateUtils'; import * as Localize from './Localize'; @@ -46,7 +46,7 @@ function getMicroSecondOnyxError(error: string | null): Errors { * Method used to get an error object with microsecond as the key and an object as the value. * @param error - error key or message to be saved */ -function getMicroSecondOnyxErrorObject(error: Errors): ErrorsObject { +function getMicroSecondOnyxErrorObject(error: Errors): ErrorFields { return {[DateUtils.getMicroseconds()]: error}; } diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 4d7424843d41..6668e3ac628e 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -39,8 +39,6 @@ type MemberChangeMessageRoomReferenceElement = { type MemberChangeMessageElement = MessageTextElement | MemberChangeMessageUserMentionElement | MemberChangeMessageRoomReferenceElement; -type ActionsToMerge = Record; - const allReports: OnyxCollection = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, @@ -444,8 +442,8 @@ function replaceBaseURL(reportAction: ReportAction): ReportAction { /** */ -function getLastVisibleAction(reportID: string, actionsToMerge: ActionsToMerge = {}): OnyxEntry { - const reportActions = Object.values(OnyxUtils.fastMerge(allReportActions?.[reportID] ?? {}, actionsToMerge)); +function getLastVisibleAction(reportID: string, actionsToMerge: OnyxCollection = {}): OnyxEntry { + const reportActions = Object.values(OnyxUtils.fastMerge(allReportActions?.[reportID] ?? {}, actionsToMerge ?? {})); const visibleReportActions = Object.values(reportActions ?? {}).filter((action): action is ReportAction => shouldReportActionBeVisibleAsLastAction(action)); const sortedReportActions = getSortedReportActions(visibleReportActions, true); if (sortedReportActions.length === 0) { @@ -454,7 +452,7 @@ function getLastVisibleAction(reportID: string, actionsToMerge: ActionsToMerge = return sortedReportActions[0]; } -function getLastVisibleMessage(reportID: string, actionsToMerge: ActionsToMerge = {}): LastVisibleMessage { +function getLastVisibleMessage(reportID: string, actionsToMerge: OnyxCollection = {}): LastVisibleMessage { const lastVisibleAction = getLastVisibleAction(reportID, actionsToMerge); const message = lastVisibleAction?.message?.[0]; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index f0c54ec7ea9b..5dee77d31b47 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -31,7 +31,7 @@ import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; import type {Participant, Split} from '@src/types/onyx/IOU'; -import type {Errors, ErrorsObject} from '@src/types/onyx/OnyxCommon'; +import type {ErrorFields, Errors} from '@src/types/onyx/OnyxCommon'; import type ReportAction from '@src/types/onyx/ReportAction'; import type {OnyxData} from '@src/types/onyx/Request'; import type {Comment, Receipt, TaxRate, TransactionChanges, WaypointCollection} from '@src/types/onyx/Transaction'; @@ -361,7 +361,7 @@ function resetMoneyRequestInfo(id = '') { /** * Helper function to get the receipt error for money requests, or the generic error if there's no receipt */ -function getReceiptError(receipt?: Receipt, filename?: string, isScanRequest = true): Errors | ErrorsObject { +function getReceiptError(receipt?: Receipt, filename?: string, isScanRequest = true): Errors | ErrorFields { return isEmptyObject(receipt) || !isScanRequest ? ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage') : ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source: receipt.source ?? '', filename: filename ?? ''}); diff --git a/src/types/onyx/OnyxCommon.ts b/src/types/onyx/OnyxCommon.ts index cff72ed94e10..27c90cca2753 100644 --- a/src/types/onyx/OnyxCommon.ts +++ b/src/types/onyx/OnyxCommon.ts @@ -10,8 +10,6 @@ type ErrorFields = Record; -type ErrorsObject = Record; - type AvatarType = typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; type Icon = { @@ -31,4 +29,4 @@ type Icon = { fallbackIcon?: AvatarSource; }; -export type {Icon, PendingAction, PendingFields, ErrorFields, Errors, AvatarType, ErrorsObject}; +export type {Icon, PendingAction, PendingFields, ErrorFields, Errors, AvatarType}; diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index cfa85b6b174c..c6f19c0bf93f 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -187,7 +187,7 @@ type ReportActionBase = { delegateAccountID?: string; /** Server side errors keyed by microtime */ - errors?: OnyxCommon.Errors | OnyxCommon.ErrorsObject; + errors?: OnyxCommon.Errors | OnyxCommon.ErrorFields; /** Whether the report action is attachment */ isAttachment?: boolean; From 5e48ce9169df7d03e903e9bc65ff3d2d067a4778 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Wed, 17 Jan 2024 16:34:54 +0100 Subject: [PATCH 126/660] Code improvements --- src/libs/ViolationsUtils.ts | 6 +++--- src/libs/actions/IOU.ts | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/libs/ViolationsUtils.ts b/src/libs/ViolationsUtils.ts index d014030c7b3e..ef277549dfaf 100644 --- a/src/libs/ViolationsUtils.ts +++ b/src/libs/ViolationsUtils.ts @@ -24,7 +24,7 @@ const ViolationsUtils = { if (policyRequiresCategories) { const hasCategoryOutOfPolicyViolation = transactionViolations.some((violation) => violation.name === 'categoryOutOfPolicy'); const hasMissingCategoryViolation = transactionViolations.some((violation) => violation.name === 'missingCategory'); - const isCategoryInPolicy = Boolean(policyCategories[transaction.category ?? '']?.enabled); + const isCategoryInPolicy = !!policyCategories[transaction.category ?? '']?.enabled; // Add 'categoryOutOfPolicy' violation if category is not in policy if (!hasCategoryOutOfPolicyViolation && transaction.category && !isCategoryInPolicy) { @@ -50,7 +50,7 @@ const ViolationsUtils = { if (policyRequiresTags) { const hasTagOutOfPolicyViolation = transactionViolations.some((violation) => violation.name === 'tagOutOfPolicy'); const hasMissingTagViolation = transactionViolations.some((violation) => violation.name === 'missingTag'); - const isTagInPolicy = Boolean(policyTags[transaction.tag ?? '']?.enabled); + const isTagInPolicy = !!policyTags[transaction.tag ?? '']?.enabled; // Add 'tagOutOfPolicy' violation if tag is not in policy if (!hasTagOutOfPolicyViolation && transaction.tag && !isTagInPolicy) { @@ -152,7 +152,7 @@ const ViolationsUtils = { brokenBankConnection: violation.data?.brokenBankConnection ?? false, isAdmin: violation.data?.isAdmin ?? false, email: violation.data?.email, - isTransactionOlderThan7Days: Boolean(violation.data?.isTransactionOlderThan7Days), + isTransactionOlderThan7Days: !!violation.data?.isTransactionOlderThan7Days, member: violation.data?.member, }); case 'smartscanFailed': diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 5dee77d31b47..f0f9c3f39651 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -472,7 +472,7 @@ function buildOnyxDataForMoneyRequest( }); } - if (Object.keys(optimisticPolicyRecentlyUsedTags).length) { + if (!isEmptyObject(optimisticPolicyRecentlyUsedTags)) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${iouReport.policyID}`, @@ -480,7 +480,7 @@ function buildOnyxDataForMoneyRequest( }); } - if (optimisticPersonalDetailListAction && Object.keys(optimisticPersonalDetailListAction).length) { + if (!isEmptyObject(optimisticPersonalDetailListAction)) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.PERSONAL_DETAILS_LIST, @@ -1377,7 +1377,7 @@ function createSplitsAndOnyxData( ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${existingSplitChatReportID || participants[0].reportID}`] : ReportUtils.getChatByParticipants(participantAccountIDs); const splitChatReport = existingSplitChatReport ?? ReportUtils.buildOptimisticChatReport(participantAccountIDs); - const isOwnPolicyExpenseChat = splitChatReport.isOwnPolicyExpenseChat; + const isOwnPolicyExpenseChat = !!splitChatReport.isOwnPolicyExpenseChat; const splitTransaction = TransactionUtils.buildOptimisticTransaction( amount, @@ -1542,8 +1542,8 @@ function createSplitsAndOnyxData( // In case the participant is a workspace, email & accountID should remain undefined and won't be used in the rest of this code // participant.login is undefined when the request is initiated from a group DM with an unknown user, so we need to add a default - const email = !!isOwnPolicyExpenseChat || isPolicyExpenseChat ? '' : OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login ?? '').toLowerCase(); - const accountID = !!isOwnPolicyExpenseChat || isPolicyExpenseChat ? 0 : Number(participant.accountID); + const email = isOwnPolicyExpenseChat || isPolicyExpenseChat ? '' : OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login ?? '').toLowerCase(); + const accountID = isOwnPolicyExpenseChat || isPolicyExpenseChat ? 0 : Number(participant.accountID); if (email === currentUserEmailForIOUSplit) { return; } @@ -1678,7 +1678,7 @@ function createSplitsAndOnyxData( optimisticPolicyRecentlyUsedCategories, optimisticPolicyRecentlyUsedTags, isNewOneOnOneChatReport, - !!shouldCreateNewOneOnOneIOUReport, + shouldCreateNewOneOnOneIOUReport, ); const individualSplit = { From c218af52030a491f4563f239053e99a4eb076dca Mon Sep 17 00:00:00 2001 From: VickyStash Date: Wed, 17 Jan 2024 16:44:54 +0100 Subject: [PATCH 127/660] Update comments to go in one line --- src/libs/actions/IOU.ts | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index f0f9c3f39651..e06a4423ffb2 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -333,9 +333,7 @@ function setMoneyRequestReceipt(transactionID: string, source: string, filename: }); } -/** - * Reset money request info from the store with its initial value - */ +/** Reset money request info from the store with its initial value */ function resetMoneyRequestInfo(id = '') { // Disabling this line since currentDate can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing @@ -358,9 +356,7 @@ function resetMoneyRequestInfo(id = '') { }); } -/** - * Helper function to get the receipt error for money requests, or the generic error if there's no receipt - */ +/** Helper function to get the receipt error for money requests, or the generic error if there's no receipt */ function getReceiptError(receipt?: Receipt, filename?: string, isScanRequest = true): Errors | ErrorFields { return isEmptyObject(receipt) || !isScanRequest ? ErrorUtils.getMicroSecondOnyxError('iou.error.genericCreateFailureMessage') @@ -1187,9 +1183,7 @@ function getUpdateMoneyRequestParams( }; } -/** - * Updates the created date of a money request - */ +/** Updates the created date of a money request */ function updateMoneyRequestDate(transactionID: string, transactionThreadReportID: string, value: string) { const transactionChanges: TransactionChanges = { created: value, @@ -1242,7 +1236,6 @@ function updateDistanceRequest(transactionID: string, transactionThreadReportID: /** * Request money from another user - * * @param amount - always in the smallest unit of the currency */ function requestMoney( @@ -2599,9 +2592,7 @@ function editMoneyRequest(transaction: OnyxTypes.Transaction, transactionThreadR } } -/** - * Updates the amount and currency fields of a money request - */ +/** Updates the amount and currency fields of a money request */ function updateMoneyRequestAmountAndCurrency(transactionID: string, transactionThreadReportID: string, currency: string, amount: number) { const transactionChanges = { amount, @@ -3695,9 +3686,7 @@ function setMoneyRequestParticipantsFromReport(transactionID: string, report: On Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {participants, participantsAutoAssigned: true}); } -/** - * Initialize money request info and navigate to the MoneyRequest page - */ +/** Initialize money request info and navigate to the MoneyRequest page */ function startMoneyRequest(iouType: string, reportID = '') { resetMoneyRequestInfo(`${iouType}${reportID}`); Navigation.navigate(ROUTES.MONEY_REQUEST.getRoute(iouType, reportID)); @@ -3768,9 +3757,7 @@ function setUpDistanceTransaction() { Onyx.merge(ONYXKEYS.IOU, {transactionID}); } -/** - * Navigates to the next IOU page based on where the IOU request was started - */ +/** Navigates to the next IOU page based on where the IOU request was started */ function navigateToNextPage(iou: OnyxEntry, iouType: string, report?: OnyxTypes.Report, path = '') { const moneyRequestID = `${iouType}${report?.reportID ?? ''}`; const shouldReset = iou?.id !== moneyRequestID && !!report?.reportID; From 7043ef4d5de0fc4238417b38d591541e5dc7e95d Mon Sep 17 00:00:00 2001 From: VickyStash Date: Wed, 17 Jan 2024 17:00:35 +0100 Subject: [PATCH 128/660] Remove not necessary anymore TODO --- src/components/MoneyReportHeader.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index a712438bb07c..2795267a9f02 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -69,7 +69,6 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt const [isConfirmModalVisible, setIsConfirmModalVisible] = useState(false); const cancelPayment = useCallback(() => { - // @ts-expect-error TODO: Remove this once IOU (https://github.com/Expensify/App/issues/24926) is migrated to TypeScript. IOU.cancelPayment(moneyRequestReport, chatReport); setIsConfirmModalVisible(false); }, [moneyRequestReport, chatReport]); From fad567c1071c523ca2b432477b56731a146f0aed Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Wed, 17 Jan 2024 16:00:38 +0000 Subject: [PATCH 129/660] refactor: move methods to personalDetailsUtils, remove unused one --- src/libs/PersonalDetailsUtils.ts | 69 +++++++++++++++++ src/libs/actions/PersonalDetails.ts | 74 +------------------ src/libs/actions/User.ts | 4 +- .../EnablePayments/AdditionalDetailsStep.js | 6 +- 4 files changed, 75 insertions(+), 78 deletions(-) diff --git a/src/libs/PersonalDetailsUtils.ts b/src/libs/PersonalDetailsUtils.ts index 3346094adeec..10b966a1a688 100644 --- a/src/libs/PersonalDetailsUtils.ts +++ b/src/libs/PersonalDetailsUtils.ts @@ -1,5 +1,6 @@ import type {OnyxEntry} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; +import Str from 'expensify-common/lib/str'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetails, PersonalDetailsList, PrivatePersonalDetails} from '@src/types/onyx'; @@ -7,6 +8,12 @@ import * as LocalePhoneNumber from './LocalePhoneNumber'; import * as Localize from './Localize'; import * as UserUtils from './UserUtils'; + +type FirstAndLastName = { + firstName: string; + lastName: string; +}; + let personalDetails: Array = []; let allPersonalDetails: OnyxEntry = {}; Onyx.connect({ @@ -198,6 +205,66 @@ function getEffectiveDisplayName(personalDetail?: PersonalDetails): string | und return undefined; } +/** + * Creates a new displayName for a user based on passed personal details or login. + * + * @param login - user's login + * @param passedPersonalDetails - details object with firstName and lastName + * @returns - The effective display name + */ +function createDisplayName(login: string, passedPersonalDetails: Pick | OnyxEntry): string { + // If we have a number like +15857527441@expensify.sms then let's remove @expensify.sms and format it + // so that the option looks cleaner in our UI. + const userLogin = LocalePhoneNumber.formatPhoneNumber(login); + + if (!passedPersonalDetails) { + return userLogin; + } + + const firstName = passedPersonalDetails.firstName ?? ''; + const lastName = passedPersonalDetails.lastName ?? ''; + const fullName = `${firstName} ${lastName}`.trim(); + + // It's possible for fullName to be empty string, so we must use "||" to fallback to userLogin. + return fullName || userLogin; +} + +/** + * Gets the first and last name from the user's personal details. + * If the login is the same as the displayName, then they don't exist, + * so we return empty strings instead. + * + * @param login - user's login + * @param displayName - user display name + * @param firstName + * @param lastName + */ +function extractFirstAndLastNameFromAvailableDetails({login, displayName, firstName, lastName}: PersonalDetails): FirstAndLastName { + // It's possible for firstName to be empty string, so we must use "||" to consider lastName instead. + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + if (firstName || lastName) { + return {firstName: firstName ?? '', lastName: lastName ?? ''}; + } + if (login && Str.removeSMSDomain(login) === displayName) { + return {firstName: '', lastName: ''}; + } + + if (displayName) { + const firstSpaceIndex = displayName.indexOf(' '); + const lastSpaceIndex = displayName.lastIndexOf(' '); + if (firstSpaceIndex === -1) { + return {firstName: displayName, lastName: ''}; + } + + return { + firstName: displayName.substring(0, firstSpaceIndex).trim(), + lastName: displayName.substring(lastSpaceIndex).trim(), + }; + } + + return {firstName: '', lastName: ''}; +} + export { getDisplayNameOrDefault, getPersonalDetailsByIDs, @@ -208,4 +275,6 @@ export { getFormattedStreet, getStreetLines, getEffectiveDisplayName, + createDisplayName, + extractFirstAndLastNameFromAvailableDetails }; diff --git a/src/libs/actions/PersonalDetails.ts b/src/libs/actions/PersonalDetails.ts index 508cca34fb88..b09b664ff745 100644 --- a/src/libs/actions/PersonalDetails.ts +++ b/src/libs/actions/PersonalDetails.ts @@ -1,10 +1,8 @@ -import Str from 'expensify-common/lib/str'; import type {OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as API from '@libs/API'; import type {CustomRNImageManipulatorResult} from '@libs/cropOrRotateImage/types'; import DateUtils from '@libs/DateUtils'; -import * as LocalePhoneNumber from '@libs/LocalePhoneNumber'; import Navigation from '@libs/Navigation/Navigation'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as UserUtils from '@libs/UserUtils'; @@ -15,11 +13,6 @@ import type {DateOfBirthForm, PersonalDetails, PersonalDetailsList, PrivatePerso import type {SelectedTimezone, Timezone} from '@src/types/onyx/PersonalDetails'; import * as Session from './Session'; -type FirstAndLastName = { - firstName: string; - lastName: string; -}; - let currentUserEmail = ''; let currentUserAccountID = -1; Onyx.connect({ @@ -42,68 +35,6 @@ Onyx.connect({ callback: (val) => (privatePersonalDetails = val), }); -/** - * Creates a new displayName for a user based on passed personal details or login. - */ -function createDisplayName(login: string, personalDetails: Pick | OnyxEntry): string { - // If we have a number like +15857527441@expensify.sms then let's remove @expensify.sms and format it - // so that the option looks cleaner in our UI. - const userLogin = LocalePhoneNumber.formatPhoneNumber(login); - - if (!personalDetails) { - return userLogin; - } - - const firstName = personalDetails.firstName ?? ''; - const lastName = personalDetails.lastName ?? ''; - const fullName = `${firstName} ${lastName}`.trim(); - - // It's possible for fullName to be empty string, so we must use "||" to fallback to userLogin. - return fullName || userLogin; -} - -/** - * Gets the first and last name from the user's personal details. - * If the login is the same as the displayName, then they don't exist, - * so we return empty strings instead. - */ -function extractFirstAndLastNameFromAvailableDetails({login, displayName, firstName, lastName}: PersonalDetails): FirstAndLastName { - // It's possible for firstName to be empty string, so we must use "||" to consider lastName instead. - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - if (firstName || lastName) { - return {firstName: firstName ?? '', lastName: lastName ?? ''}; - } - if (login && Str.removeSMSDomain(login) === displayName) { - return {firstName: '', lastName: ''}; - } - - if (displayName) { - const firstSpaceIndex = displayName.indexOf(' '); - const lastSpaceIndex = displayName.lastIndexOf(' '); - if (firstSpaceIndex === -1) { - return {firstName: displayName, lastName: ''}; - } - - return { - firstName: displayName.substring(0, firstSpaceIndex).trim(), - lastName: displayName.substring(lastSpaceIndex).trim(), - }; - } - - return {firstName: '', lastName: ''}; -} - -/** - * Convert country names obtained from the backend to their respective ISO codes - * This is for backward compatibility of stored data before E/App#15507 - */ -function getCountryISO(countryName: string): string { - if (!countryName || countryName.length === 2) { - return countryName; - } - - return Object.entries(CONST.ALL_COUNTRIES).find(([, value]) => value === countryName)?.[0] ?? ''; -} function updatePronouns(pronouns: string) { if (currentUserAccountID) { @@ -149,7 +80,7 @@ function updateDisplayName(firstName: string, lastName: string) { [currentUserAccountID]: { firstName, lastName, - displayName: createDisplayName(currentUserEmail ?? '', { + displayName: PersonalDetailsUtils.createDisplayName(currentUserEmail ?? '', { firstName, lastName, }), @@ -563,9 +494,6 @@ function getPrivatePersonalDetails(): OnyxEntry { export { clearAvatarErrors, deleteAvatar, - extractFirstAndLastNameFromAvailableDetails, - getCountryISO, - createDisplayName, getPrivatePersonalDetails, openPersonalDetailsPage, openPublicProfilePage, diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index df5709ac68e2..1766b1a29677 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -10,6 +10,7 @@ import * as SequentialQueue from '@libs/Network/SequentialQueue'; import * as Pusher from '@libs/Pusher/pusher'; import PusherUtils from '@libs/PusherUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; +import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -23,7 +24,6 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import * as Link from './Link'; import * as OnyxUpdates from './OnyxUpdates'; -import * as PersonalDetails from './PersonalDetails'; import * as Report from './Report'; import * as Session from './Session'; @@ -716,7 +716,7 @@ function setContactMethodAsDefault(newDefaultContactMethod: string) { value: { [currentUserAccountID]: { login: newDefaultContactMethod, - displayName: PersonalDetails.createDisplayName(newDefaultContactMethod, myPersonalDetails), + displayName: PersonalDetailsUtils.createDisplayName(newDefaultContactMethod, myPersonalDetails), }, }, }, diff --git a/src/pages/EnablePayments/AdditionalDetailsStep.js b/src/pages/EnablePayments/AdditionalDetailsStep.js index 5ca51e208b49..0371a70ede4b 100644 --- a/src/pages/EnablePayments/AdditionalDetailsStep.js +++ b/src/pages/EnablePayments/AdditionalDetailsStep.js @@ -18,8 +18,8 @@ import useThemeStyles from '@hooks/useThemeStyles'; import compose from '@libs/compose'; import {parsePhoneNumber} from '@libs/PhoneNumber'; import * as ValidationUtils from '@libs/ValidationUtils'; +import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import AddressForm from '@pages/ReimbursementAccount/AddressForm'; -import * as PersonalDetails from '@userActions/PersonalDetails'; import * as Wallet from '@userActions/Wallet'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -194,7 +194,7 @@ function AdditionalDetailsStep({walletAdditionalDetails, translate, currentUserP label={translate(fieldNameTranslationKeys.legalFirstName)} accessibilityLabel={translate(fieldNameTranslationKeys.legalFirstName)} role={CONST.ROLE.PRESENTATION} - defaultValue={PersonalDetails.extractFirstAndLastNameFromAvailableDetails(currentUserPersonalDetails).firstName} + defaultValue={PersonalDetailsUtils.extractFirstAndLastNameFromAvailableDetails(currentUserPersonalDetails).firstName} shouldSaveDraft /> Date: Wed, 17 Jan 2024 18:05:34 +0100 Subject: [PATCH 130/660] Update Receipt source type, turn MERGE into SET --- src/libs/ErrorUtils.ts | 2 +- src/libs/ReceiptUtils.ts | 2 +- src/libs/ValidationUtils.ts | 2 +- src/libs/actions/IOU.ts | 12 ++++++------ src/types/onyx/OnyxCommon.ts | 2 +- src/types/onyx/Transaction.ts | 6 ++++-- 6 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/libs/ErrorUtils.ts b/src/libs/ErrorUtils.ts index e985ab5fab64..fe7d65d72c6c 100644 --- a/src/libs/ErrorUtils.ts +++ b/src/libs/ErrorUtils.ts @@ -54,7 +54,7 @@ type OnyxDataWithErrors = { errors?: Errors; }; -function getLatestErrorMessage(onyxData: TOnyxData): string | null { +function getLatestErrorMessage(onyxData: TOnyxData): string | number | null { const errors = onyxData.errors ?? {}; if (Object.keys(errors).length === 0) { diff --git a/src/libs/ReceiptUtils.ts b/src/libs/ReceiptUtils.ts index bcba68a3a0bd..a44fe39f5b31 100644 --- a/src/libs/ReceiptUtils.ts +++ b/src/libs/ReceiptUtils.ts @@ -43,7 +43,7 @@ function getThumbnailAndImageURIs(transaction: Transaction, receiptPath: string } // For local files, we won't have a thumbnail yet - if (isReceiptImage && (path.startsWith('blob:') || path.startsWith('file:'))) { + if (isReceiptImage && typeof path === 'string' && (path.startsWith('blob:') || path.startsWith('file:'))) { return {thumbnail: null, image: path, isLocalFile: true}; } diff --git a/src/libs/ValidationUtils.ts b/src/libs/ValidationUtils.ts index 444c2618ee1b..fa04176f93f8 100644 --- a/src/libs/ValidationUtils.ts +++ b/src/libs/ValidationUtils.ts @@ -73,7 +73,7 @@ function isValidPastDate(date: string | Date): boolean { /** * Used to validate a value that is "required". */ -function isRequiredFulfilled(value: string | Date | unknown[] | Record | null): boolean { +function isRequiredFulfilled(value: string | number | Date | unknown[] | Record | null): boolean { if (typeof value === 'string') { return !StringUtils.isEmptyString(value); } diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index e06a4423ffb2..31ed4ecb90fb 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -34,7 +34,7 @@ import type {Participant, Split} from '@src/types/onyx/IOU'; import type {ErrorFields, Errors} from '@src/types/onyx/OnyxCommon'; import type ReportAction from '@src/types/onyx/ReportAction'; import type {OnyxData} from '@src/types/onyx/Request'; -import type {Comment, Receipt, TaxRate, TransactionChanges, WaypointCollection} from '@src/types/onyx/Transaction'; +import type {Comment, Receipt, ReceiptSource, TaxRate, TransactionChanges, WaypointCollection} from '@src/types/onyx/Transaction'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; @@ -905,7 +905,7 @@ function createDistanceRequest( const currentChatReport = isMoneyRequestReport ? ReportUtils.getReport(report.chatReportID) : report; const optimisticReceipt: Receipt = { - source: ReceiptGeneric as string, + source: ReceiptGeneric as ReceiptSource, state: CONST.IOU.RECEIPT_STATE.OPEN, }; const {iouReport, chatReport, transaction, iouAction, createdChatReportActionID, createdIOUReportActionID, reportPreviewAction, onyxData} = getMoneyRequestInformation( @@ -1462,9 +1462,9 @@ function createSplitsAndOnyxData( value: {pendingAction: null}, }, { - onyxMethod: Onyx.METHOD.MERGE, + onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${CONST.IOU.OPTIMISTIC_TRANSACTION_ID}`, - value: {}, + value: null, }, ]; @@ -1485,9 +1485,9 @@ function createSplitsAndOnyxData( }, }, { - onyxMethod: Onyx.METHOD.MERGE, + onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${CONST.IOU.OPTIMISTIC_TRANSACTION_ID}`, - value: {}, + value: null, }, ]; diff --git a/src/types/onyx/OnyxCommon.ts b/src/types/onyx/OnyxCommon.ts index 27c90cca2753..15e40173e185 100644 --- a/src/types/onyx/OnyxCommon.ts +++ b/src/types/onyx/OnyxCommon.ts @@ -8,7 +8,7 @@ type PendingFields = Record = Record; -type Errors = Record; +type Errors = Record; type AvatarType = typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 7117158216e4..56dfdc2e5c26 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -38,11 +38,13 @@ type Geometry = { type?: GeometryType; }; +type ReceiptSource = string | number; + type Receipt = { receiptID?: number; path?: string; name?: string; - source?: string; + source?: ReceiptSource; filename?: string; state?: ValueOf; }; @@ -200,4 +202,4 @@ type AdditionalTransactionChanges = { type TransactionChanges = Partial & AdditionalTransactionChanges; export default Transaction; -export type {WaypointCollection, Comment, Receipt, Waypoint, TransactionChanges, TaxRate}; +export type {WaypointCollection, Comment, Receipt, Waypoint, TransactionChanges, TaxRate, ReceiptSource}; From 9ba4d4060f2fde956e4bc1662c534c6be70d969b Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 18 Jan 2024 09:20:53 +0100 Subject: [PATCH 131/660] Remove unnecessary TODOs from IOU file --- src/libs/actions/IOU.ts | 44 +++++++++----------------------------- src/libs/actions/Policy.ts | 4 ++-- 2 files changed, 12 insertions(+), 36 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index b94400d9de92..ad86a6cc9e52 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -41,12 +41,6 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject'; import * as Policy from './Policy'; import * as Report from './Report'; -// TODO: Remove this once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. -type OptimisticPolicyRecentlyUsedCategories = string[]; - -// TODO: Remove this once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. -type OptimisticPolicyRecentlyUsedTags = Record; - type MoneyRequestRoute = StackScreenProps< MoneyRequestNavigatorParamList, typeof SCREENS.MONEY_REQUEST.CATEGORY | typeof SCREENS.MONEY_REQUEST.TAG | typeof SCREENS.MONEY_REQUEST.CONFIRMATION @@ -373,8 +367,8 @@ function buildOnyxDataForMoneyRequest( iouAction: OptimisticIOUReportAction, optimisticPersonalDetailListAction: OnyxTypes.PersonalDetailsList, reportPreviewAction: ReportAction, - optimisticPolicyRecentlyUsedCategories: OptimisticPolicyRecentlyUsedCategories, - optimisticPolicyRecentlyUsedTags: OptimisticPolicyRecentlyUsedTags, + optimisticPolicyRecentlyUsedCategories: string[], + optimisticPolicyRecentlyUsedTags: OnyxTypes.RecentlyUsedTags, isNewChatReport: boolean, isNewIOUReport: boolean, policy?: OnyxTypes.Policy | EmptyObject, @@ -773,11 +767,9 @@ function getMoneyRequestInformation( billable, ); - // TODO: Remove assertion once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. - const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories(iouReport.policyID, category) as OptimisticPolicyRecentlyUsedCategories; + const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories(iouReport.policyID, category); - // TODO: Remove assertion once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. - const optimisticPolicyRecentlyUsedTags = Policy.buildOptimisticPolicyRecentlyUsedTags(iouReport.policyID, tag) as OptimisticPolicyRecentlyUsedTags; + const optimisticPolicyRecentlyUsedTags = Policy.buildOptimisticPolicyRecentlyUsedTags(iouReport.policyID, tag); // If there is an existing transaction (which is the case for distance requests), then the data from the existing transaction // needs to be manually merged into the optimistic transaction. This is because buildOnyxDataForMoneyRequest() uses `Onyx.set()` for the transaction @@ -1110,11 +1102,7 @@ function getUpdateMoneyRequestParams( // Update recently used categories if the category is changed if ('category' in transactionChanges) { - // TODO: Remove assertion once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. - const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories( - iouReport?.policyID, - transactionChanges.category, - ) as OptimisticPolicyRecentlyUsedCategories; + const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories(iouReport?.policyID, transactionChanges.category); if (optimisticPolicyRecentlyUsedCategories.length) { optimisticData.push({ onyxMethod: Onyx.METHOD.SET, @@ -1126,8 +1114,7 @@ function getUpdateMoneyRequestParams( // Update recently used categories if the tag is changed if ('tag' in transactionChanges) { - // TODO: Remove assertion once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. - const optimisticPolicyRecentlyUsedTags = Policy.buildOptimisticPolicyRecentlyUsedTags(iouReport?.policyID, transactionChanges.tag) as OptimisticPolicyRecentlyUsedTags; + const optimisticPolicyRecentlyUsedTags = Policy.buildOptimisticPolicyRecentlyUsedTags(iouReport?.policyID, transactionChanges.tag); if (!isEmptyObject(optimisticPolicyRecentlyUsedTags)) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, @@ -1647,16 +1634,10 @@ function createSplitsAndOnyxData( } // Add category to optimistic policy recently used categories when a participant is a workspace - const optimisticPolicyRecentlyUsedCategories = isPolicyExpenseChat - ? // TODO: Remove assertion once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. - (Policy.buildOptimisticPolicyRecentlyUsedCategories(participant.policyID, category) as OptimisticPolicyRecentlyUsedCategories) - : []; + const optimisticPolicyRecentlyUsedCategories = isPolicyExpenseChat ? Policy.buildOptimisticPolicyRecentlyUsedCategories(participant.policyID, category) : []; // Add tag to optimistic policy recently used tags when a participant is a workspace - const optimisticPolicyRecentlyUsedTags = isPolicyExpenseChat - ? // TODO: Remove assertion once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. - (Policy.buildOptimisticPolicyRecentlyUsedTags(participant.policyID, tag) as OptimisticPolicyRecentlyUsedTags) - : {}; + const optimisticPolicyRecentlyUsedTags = isPolicyExpenseChat ? Policy.buildOptimisticPolicyRecentlyUsedTags(participant.policyID, tag) : {}; // STEP 5: Build Onyx Data const [oneOnOneOptimisticData, oneOnOneSuccessData, oneOnOneFailureData] = buildOnyxDataForMoneyRequest( @@ -2448,11 +2429,7 @@ function editRegularMoneyRequest(transactionID: string, transactionThreadReportI // Update recently used categories if the category is changed if ('category' in transactionChanges) { - // TODO: Remove assertion once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. - const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories( - iouReport?.policyID, - transactionChanges.category, - ) as OptimisticPolicyRecentlyUsedCategories; + const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories(iouReport?.policyID, transactionChanges.category); if (optimisticPolicyRecentlyUsedCategories.length) { optimisticData.push({ onyxMethod: Onyx.METHOD.SET, @@ -2464,8 +2441,7 @@ function editRegularMoneyRequest(transactionID: string, transactionThreadReportI // Update recently used categories if the tag is changed if ('tag' in transactionChanges) { - // TODO: Remove assertion once Policy.js (https://github.com/Expensify/App/issues/24918) is migrated to TypeScript. - const optimisticPolicyRecentlyUsedTags = Policy.buildOptimisticPolicyRecentlyUsedTags(iouReport?.policyID, transactionChanges.tag) as OptimisticPolicyRecentlyUsedTags; + const optimisticPolicyRecentlyUsedTags = Policy.buildOptimisticPolicyRecentlyUsedTags(iouReport?.policyID, transactionChanges.tag); if (!isEmptyObject(optimisticPolicyRecentlyUsedTags)) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 263d5fb68529..6357476126c1 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -1556,7 +1556,7 @@ function dismissAddedWithPrimaryLoginMessages(policyID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {primaryLoginsInvited: null}); } -function buildOptimisticPolicyRecentlyUsedCategories(policyID: string, category: string) { +function buildOptimisticPolicyRecentlyUsedCategories(policyID?: string, category?: string) { if (!policyID || !category) { return []; } @@ -1566,7 +1566,7 @@ function buildOptimisticPolicyRecentlyUsedCategories(policyID: string, category: return lodashUnion([category], policyRecentlyUsedCategories); } -function buildOptimisticPolicyRecentlyUsedTags(policyID: string, tag: string): RecentlyUsedTags { +function buildOptimisticPolicyRecentlyUsedTags(policyID?: string, tag?: string): RecentlyUsedTags { if (!policyID || !tag) { return {}; } From a3c1eab307999e0efc6bfd3071c2f05d03b5a5d3 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Thu, 18 Jan 2024 11:01:48 +0100 Subject: [PATCH 132/660] clean up --- src/ROUTES.ts | 17 +++++++++++++++-- src/languages/en.ts | 2 +- src/libs/Navigation/Navigation.ts | 19 +++++++++++++++++-- src/pages/LogInWithShortLivedAuthTokenPage.js | 9 +++------ src/pages/LogOutPreviousUserPage.js | 7 ++----- 5 files changed, 38 insertions(+), 16 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 37003a09a0cd..69e8f534e395 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -474,7 +474,16 @@ const ROUTES = { PROCESS_MONEY_REQUEST_HOLD: 'hold-request-educational', } as const; -export {getUrlWithBackToParam}; +/** + * Proxy routes can be used to generate a correct url with dynamic values + * + * It will be used by HybridApp, that has no access to methods generating dynamic routes in NewDot + */ +const HYBRID_APP_ROUTES = { + MONEY_REQUEST_CREATE: '/request/new/scan', +} + +export {getUrlWithBackToParam, HYBRID_APP_ROUTES}; export default ROUTES; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -494,4 +503,8 @@ type RouteIsPlainString = IsEqual; */ type Route = RouteIsPlainString extends true ? never : AllRoutes; -export type {Route}; +type HybridAppRoute = { + [K in keyof typeof HYBRID_APP_ROUTES]: ExtractRouteName<(typeof HYBRID_APP_ROUTES)[K]>; +}[keyof typeof HYBRID_APP_ROUTES]; + +export type {Route, HybridAppRoute}; diff --git a/src/languages/en.ts b/src/languages/en.ts index 49749fd79a5a..3be5336c9aa5 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -800,7 +800,7 @@ export default { phrase3: 'and', phrase4: 'Privacy', }, - returnToClassic: 'Return to Expensify Classic', + returnToClassic: 'Switch to Expensify Classic', help: 'Help', }, closeAccountPage: { diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index 91285821fe9f..083039332215 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -2,10 +2,11 @@ import {findFocusedRoute} from '@react-navigation/core'; import type {EventArg, NavigationContainerEventMap, NavigationState, PartialState} from '@react-navigation/native'; import {CommonActions, getPathFromState, StackActions} from '@react-navigation/native'; import Log from '@libs/Log'; +import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; -import type {Route} from '@src/ROUTES'; -import ROUTES from '@src/ROUTES'; +import type {HybridAppRoute, Route} from '@src/ROUTES'; +import ROUTES, {HYBRID_APP_ROUTES} from '@src/ROUTES'; import {PROTECTED_SCREENS} from '@src/SCREENS'; import originalDismissModal from './dismissModal'; import originalGetTopmostReportActionId from './getTopmostReportActionID'; @@ -67,6 +68,18 @@ function getActiveRouteIndex(stateOrRoute: StateOrRoute, index?: number): number return index; } +/** + * Function that generates dynamic urls from paths passed from OldDot + */ +function parseHybridAppUrl(url: HybridAppRoute | Route): Route { + switch (url) { + case HYBRID_APP_ROUTES.MONEY_REQUEST_CREATE: + return ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.TYPE.REQUEST, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, ReportUtils.generateReportID()); + default: + return url as Route; + } +} + /** * Gets distance from the path in root navigator. In other words how much screen you have to pop to get to the route with this path. * The search is limited to 5 screens from the top for performance reasons. @@ -134,6 +147,7 @@ function isActiveRoute(routePath: Route): boolean { * @param [type] - Type of action to perform. Currently UP is supported. */ function navigate(route: Route = ROUTES.HOME, type?: string) { + console.log('route', route); if (!canNavigate('navigate', {route})) { // Store intended route if the navigator is not yet available, // we will try again after the NavigationContainer is ready @@ -313,6 +327,7 @@ export default { getRouteNameFromStateEvent, getTopmostReportActionId, waitForProtectedRoutes, + parseHybridAppUrl, }; export {navigationRef}; diff --git a/src/pages/LogInWithShortLivedAuthTokenPage.js b/src/pages/LogInWithShortLivedAuthTokenPage.js index 3be9ad64e8eb..2f937fe50c9a 100644 --- a/src/pages/LogInWithShortLivedAuthTokenPage.js +++ b/src/pages/LogInWithShortLivedAuthTokenPage.js @@ -1,7 +1,7 @@ import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {useEffect} from 'react'; -import {View} from 'react-native'; +import {NativeModules, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import Icon from '@components/Icon'; @@ -74,11 +74,8 @@ function LogInWithShortLivedAuthTokenPage(props) { const exitTo = lodashGet(props, 'route.params.exitTo', ''); if (exitTo) { Navigation.isNavigationReady().then(() => { - if (exitTo === '/request/new/scan') { - Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.TYPE.REQUEST, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, ReportUtils.generateReportID())); - } else { - Navigation.navigate(exitTo); - } + const url = NativeModules.HybridAppModule ? Navigation.parseHybridAppUrl(exitTo) : exitTo + Navigation.navigate(url); }); } // The only dependencies of the effect are based on props.route diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index 9438b8663a14..50e584a28c78 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -70,12 +70,9 @@ function LogOutPreviousUserPage(props) { if (exitTo && exitTo !== ROUTES.WORKSPACE_NEW && !props.account.isLoading && !isLoggingInAsNewUser) { Navigation.isNavigationReady().then(() => { // remove this screen and navigate to exit route + const exitUrl = NativeModules.HybridAppModule ? Navigation.parseHybridAppUrl(exitTo) : exitTo Navigation.goBack(); - if (exitTo === '/request/new/scan') { - Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.TYPE.REQUEST, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, ReportUtils.generateReportID())) - } else { - Navigation.navigate(exitTo); - } + Navigation.navigate(exitUrl); }); } }); From ec972f632875fea77a358f411eb703bd0aee21bb Mon Sep 17 00:00:00 2001 From: staszekscp Date: Thu, 18 Jan 2024 11:03:13 +0100 Subject: [PATCH 133/660] remove console.log --- src/libs/Navigation/Navigation.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index 083039332215..3f41a38ba221 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -147,7 +147,6 @@ function isActiveRoute(routePath: Route): boolean { * @param [type] - Type of action to perform. Currently UP is supported. */ function navigate(route: Route = ROUTES.HOME, type?: string) { - console.log('route', route); if (!canNavigate('navigate', {route})) { // Store intended route if the navigator is not yet available, // we will try again after the NavigationContainer is ready From c564b5c38f362fa3d2821d3a355e8de3e6956a7d Mon Sep 17 00:00:00 2001 From: staszekscp Date: Thu, 18 Jan 2024 12:29:10 +0100 Subject: [PATCH 134/660] prettier --- src/ROUTES.ts | 2 +- src/pages/LogInWithShortLivedAuthTokenPage.tsx | 2 +- src/pages/LogOutPreviousUserPage.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 69e8f534e395..1897661c794d 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -481,7 +481,7 @@ const ROUTES = { */ const HYBRID_APP_ROUTES = { MONEY_REQUEST_CREATE: '/request/new/scan', -} +}; export {getUrlWithBackToParam, HYBRID_APP_ROUTES}; export default ROUTES; diff --git a/src/pages/LogInWithShortLivedAuthTokenPage.tsx b/src/pages/LogInWithShortLivedAuthTokenPage.tsx index c1dd039229ff..55739977f5f3 100644 --- a/src/pages/LogInWithShortLivedAuthTokenPage.tsx +++ b/src/pages/LogInWithShortLivedAuthTokenPage.tsx @@ -50,7 +50,7 @@ function LogInWithShortLivedAuthTokenPage({route, account}: LogInWithShortLivedA if (exitTo) { Navigation.isNavigationReady().then(() => { - const url = NativeModules.HybridAppModule ? Navigation.parseHybridAppUrl(exitTo) : exitTo + const url = NativeModules.HybridAppModule ? Navigation.parseHybridAppUrl(exitTo) : exitTo; Navigation.navigate(url); }); } diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index 50e584a28c78..9bd28f76de31 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -70,7 +70,7 @@ function LogOutPreviousUserPage(props) { if (exitTo && exitTo !== ROUTES.WORKSPACE_NEW && !props.account.isLoading && !isLoggingInAsNewUser) { Navigation.isNavigationReady().then(() => { // remove this screen and navigate to exit route - const exitUrl = NativeModules.HybridAppModule ? Navigation.parseHybridAppUrl(exitTo) : exitTo + const exitUrl = NativeModules.HybridAppModule ? Navigation.parseHybridAppUrl(exitTo) : exitTo; Navigation.goBack(); Navigation.navigate(exitUrl); }); From cafec680f97ba700f60e7c8279578b524da1263b Mon Sep 17 00:00:00 2001 From: staszekscp Date: Thu, 18 Jan 2024 15:50:21 +0100 Subject: [PATCH 135/660] fiox linter --- src/ROUTES.ts | 6 ++---- src/libs/Navigation/Navigation.ts | 2 +- src/pages/LogInWithShortLivedAuthTokenPage.tsx | 1 - src/pages/LogOutPreviousUserPage.js | 1 - 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 1897661c794d..dc856f5ca782 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -481,7 +481,7 @@ const ROUTES = { */ const HYBRID_APP_ROUTES = { MONEY_REQUEST_CREATE: '/request/new/scan', -}; +} as const; export {getUrlWithBackToParam, HYBRID_APP_ROUTES}; export default ROUTES; @@ -503,8 +503,6 @@ type RouteIsPlainString = IsEqual; */ type Route = RouteIsPlainString extends true ? never : AllRoutes; -type HybridAppRoute = { - [K in keyof typeof HYBRID_APP_ROUTES]: ExtractRouteName<(typeof HYBRID_APP_ROUTES)[K]>; -}[keyof typeof HYBRID_APP_ROUTES]; +type HybridAppRoute = typeof HYBRID_APP_ROUTES[keyof typeof HYBRID_APP_ROUTES]; export type {Route, HybridAppRoute}; diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index 3f41a38ba221..5dfd3c5d145b 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -71,7 +71,7 @@ function getActiveRouteIndex(stateOrRoute: StateOrRoute, index?: number): number /** * Function that generates dynamic urls from paths passed from OldDot */ -function parseHybridAppUrl(url: HybridAppRoute | Route): Route { +function parseHybridAppUrl(url: HybridAppRoute & Route): Route { switch (url) { case HYBRID_APP_ROUTES.MONEY_REQUEST_CREATE: return ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.TYPE.REQUEST, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, ReportUtils.generateReportID()); diff --git a/src/pages/LogInWithShortLivedAuthTokenPage.tsx b/src/pages/LogInWithShortLivedAuthTokenPage.tsx index 55739977f5f3..cce487a188b7 100644 --- a/src/pages/LogInWithShortLivedAuthTokenPage.tsx +++ b/src/pages/LogInWithShortLivedAuthTokenPage.tsx @@ -15,7 +15,6 @@ import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import type {PublicScreensParamList} from '@libs/Navigation/types'; import * as Session from '@userActions/Session'; -import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type {Account} from '@src/types/onyx'; diff --git a/src/pages/LogOutPreviousUserPage.js b/src/pages/LogOutPreviousUserPage.js index 9bd28f76de31..db0c7a6d806c 100644 --- a/src/pages/LogOutPreviousUserPage.js +++ b/src/pages/LogOutPreviousUserPage.js @@ -6,7 +6,6 @@ import {withOnyx} from 'react-native-onyx'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import InitialUrlContext from '@libs/InitialUrlContext'; import Navigation from '@libs/Navigation/Navigation'; -import * as ReportUtils from '@libs/ReportUtils'; import * as SessionUtils from '@libs/SessionUtils'; import * as Session from '@userActions/Session'; import CONST from '@src/CONST'; From 8d79efbb23a375dad0588c389cff5488ea2c80aa Mon Sep 17 00:00:00 2001 From: staszekscp Date: Thu, 18 Jan 2024 15:57:22 +0100 Subject: [PATCH 136/660] fix tsc? --- src/libs/Navigation/Navigation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index 5dfd3c5d145b..3f41a38ba221 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -71,7 +71,7 @@ function getActiveRouteIndex(stateOrRoute: StateOrRoute, index?: number): number /** * Function that generates dynamic urls from paths passed from OldDot */ -function parseHybridAppUrl(url: HybridAppRoute & Route): Route { +function parseHybridAppUrl(url: HybridAppRoute | Route): Route { switch (url) { case HYBRID_APP_ROUTES.MONEY_REQUEST_CREATE: return ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.TYPE.REQUEST, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, ReportUtils.generateReportID()); From b6b42325d8612a87b5a10aacdffd3f9d3c6481a6 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Thu, 18 Jan 2024 16:03:32 +0100 Subject: [PATCH 137/660] one more fix --- src/ROUTES.ts | 2 +- src/libs/Navigation/Navigation.ts | 2 +- src/libs/Navigation/types.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index dc856f5ca782..3269dac6d124 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -503,6 +503,6 @@ type RouteIsPlainString = IsEqual; */ type Route = RouteIsPlainString extends true ? never : AllRoutes; -type HybridAppRoute = typeof HYBRID_APP_ROUTES[keyof typeof HYBRID_APP_ROUTES]; +type HybridAppRoute = (typeof HYBRID_APP_ROUTES)[keyof typeof HYBRID_APP_ROUTES]; export type {Route, HybridAppRoute}; diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts index 3f41a38ba221..15c2ea289b3d 100644 --- a/src/libs/Navigation/Navigation.ts +++ b/src/libs/Navigation/Navigation.ts @@ -76,7 +76,7 @@ function parseHybridAppUrl(url: HybridAppRoute | Route): Route { case HYBRID_APP_ROUTES.MONEY_REQUEST_CREATE: return ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.TYPE.REQUEST, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, ReportUtils.generateReportID()); default: - return url as Route; + return url; } } diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 8d227fa6f697..a3470e39b2c0 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -3,7 +3,7 @@ import type {CommonActions, NavigationContainerRefWithCurrent, NavigationHelpers import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; import type NAVIGATORS from '@src/NAVIGATORS'; -import type {Route as Routes} from '@src/ROUTES'; +import type {HybridAppRoute, Route as Routes} from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; type NavigationRef = NavigationContainerRefWithCurrent; @@ -383,7 +383,7 @@ type PublicScreensParamList = { error?: string; shortLivedAuthToken?: string; shortLivedToken?: string; - exitTo?: Routes; + exitTo?: Routes | HybridAppRoute; }; [SCREENS.VALIDATE_LOGIN]: { accountID: string; From 556b831303bbc594b0c682f1bb993b2623fd0d27 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 19 Jan 2024 01:36:30 +0700 Subject: [PATCH 138/660] set default category only when unset --- src/pages/iou/request/step/IOURequestStepConfirmation.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.js b/src/pages/iou/request/step/IOURequestStepConfirmation.js index ee2a151c7876..0f535cb60e14 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.js +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.js @@ -121,6 +121,9 @@ function IOURequestStepConfirmation({ '', ); useEffect(() => { + if (!_.isEmpty(transaction.category)) { + return; + } IOU.setMoneyRequestCategory_temporaryForRefactor(transactionID, defaultCategory); }, [transactionID, defaultCategory]); From dc7694036da1a085e0bfb1ed391b34ede581cef7 Mon Sep 17 00:00:00 2001 From: Agata Kosior Date: Fri, 19 Jan 2024 10:04:55 +0000 Subject: [PATCH 139/660] fix: run prettier --- src/libs/PersonalDetailsUtils.ts | 5 ++--- src/libs/actions/PersonalDetails.ts | 1 - src/libs/actions/User.ts | 2 +- src/pages/EnablePayments/AdditionalDetailsStep.js | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/libs/PersonalDetailsUtils.ts b/src/libs/PersonalDetailsUtils.ts index 85c439c11736..945c0b581b16 100644 --- a/src/libs/PersonalDetailsUtils.ts +++ b/src/libs/PersonalDetailsUtils.ts @@ -1,6 +1,6 @@ +import Str from 'expensify-common/lib/str'; import type {OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; -import Str from 'expensify-common/lib/str'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetails, PersonalDetailsList, PrivatePersonalDetails} from '@src/types/onyx'; @@ -9,7 +9,6 @@ import * as LocalePhoneNumber from './LocalePhoneNumber'; import * as Localize from './Localize'; import * as UserUtils from './UserUtils'; - type FirstAndLastName = { firstName: string; lastName: string; @@ -273,5 +272,5 @@ export { getStreetLines, getEffectiveDisplayName, createDisplayName, - extractFirstAndLastNameFromAvailableDetails + extractFirstAndLastNameFromAvailableDetails, }; diff --git a/src/libs/actions/PersonalDetails.ts b/src/libs/actions/PersonalDetails.ts index b09b664ff745..afd0dc6037e8 100644 --- a/src/libs/actions/PersonalDetails.ts +++ b/src/libs/actions/PersonalDetails.ts @@ -35,7 +35,6 @@ Onyx.connect({ callback: (val) => (privatePersonalDetails = val), }); - function updatePronouns(pronouns: string) { if (currentUserAccountID) { type UpdatePronounsParams = { diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index 1766b1a29677..b304be62b407 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -7,10 +7,10 @@ import * as API from '@libs/API'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as SequentialQueue from '@libs/Network/SequentialQueue'; +import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as Pusher from '@libs/Pusher/pusher'; import PusherUtils from '@libs/PusherUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; -import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; diff --git a/src/pages/EnablePayments/AdditionalDetailsStep.js b/src/pages/EnablePayments/AdditionalDetailsStep.js index 0371a70ede4b..cf769bf58fd3 100644 --- a/src/pages/EnablePayments/AdditionalDetailsStep.js +++ b/src/pages/EnablePayments/AdditionalDetailsStep.js @@ -16,9 +16,9 @@ import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultPro import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import compose from '@libs/compose'; +import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import {parsePhoneNumber} from '@libs/PhoneNumber'; import * as ValidationUtils from '@libs/ValidationUtils'; -import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import AddressForm from '@pages/ReimbursementAccount/AddressForm'; import * as Wallet from '@userActions/Wallet'; import CONST from '@src/CONST'; From c978bd5caa0be32ffc290de9d884d00172e2fbe0 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Fri, 19 Jan 2024 12:22:08 +0100 Subject: [PATCH 140/660] start migrating jest files to TypeScript --- jest/{setup.js => setup.ts} | 2 +- jest/{setupAfterEnv.js => setupAfterEnv.ts} | 0 jest/{setupMockImages.js => setupMockImages.ts} | 8 ++------ src/types/modules/react-native-clipboard.d.ts | 16 ++++++++++++++++ 4 files changed, 19 insertions(+), 7 deletions(-) rename jest/{setup.js => setup.ts} (96%) rename jest/{setupAfterEnv.js => setupAfterEnv.ts} (100%) rename jest/{setupMockImages.js => setupMockImages.ts} (87%) create mode 100644 src/types/modules/react-native-clipboard.d.ts diff --git a/jest/setup.js b/jest/setup.ts similarity index 96% rename from jest/setup.js rename to jest/setup.ts index 38b4b55a68b3..ff53f957331d 100644 --- a/jest/setup.js +++ b/jest/setup.ts @@ -34,6 +34,6 @@ jest.spyOn(console, 'debug').mockImplementation((...params) => { // This mock is required for mocking file systems when running tests jest.mock('react-native-fs', () => ({ - unlink: jest.fn(() => new Promise((res) => res())), + unlink: jest.fn(() => new Promise((res) => res())), CachesDirectoryPath: jest.fn(), })); diff --git a/jest/setupAfterEnv.js b/jest/setupAfterEnv.ts similarity index 100% rename from jest/setupAfterEnv.js rename to jest/setupAfterEnv.ts diff --git a/jest/setupMockImages.js b/jest/setupMockImages.ts similarity index 87% rename from jest/setupMockImages.js rename to jest/setupMockImages.ts index 10925aca8736..c48797b3c07b 100644 --- a/jest/setupMockImages.js +++ b/jest/setupMockImages.ts @@ -1,14 +1,10 @@ import fs from 'fs'; import path from 'path'; -import _ from 'underscore'; -/** - * @param {String} imagePath - */ -function mockImages(imagePath) { +function mockImages(imagePath: string) { const imageFilenames = fs.readdirSync(path.resolve(__dirname, `../assets/${imagePath}/`)); // eslint-disable-next-line rulesdir/prefer-early-return - _.each(imageFilenames, (fileName) => { + imageFilenames.forEach((fileName) => { if (/\.svg/.test(fileName)) { jest.mock(`../assets/${imagePath}/${fileName}`, () => () => ''); } diff --git a/src/types/modules/react-native-clipboard.d.ts b/src/types/modules/react-native-clipboard.d.ts new file mode 100644 index 000000000000..14f418a3f8b9 --- /dev/null +++ b/src/types/modules/react-native-clipboard.d.ts @@ -0,0 +1,16 @@ +declare module '@react-native-clipboard/clipboard/jest/clipboard-mock' { + const mockClipboard: { + getString: jest.MockedFunction<() => Promise>; + getImagePNG: jest.MockedFunction<() => void>; + getImageJPG: jest.MockedFunction<() => void>; + setImage: jest.MockedFunction<() => void>; + setString: jest.MockedFunction<() => void>; + hasString: jest.MockedFunction<() => Promise>; + hasImage: jest.MockedFunction<() => Promise>; + hasURL: jest.MockedFunction<() => Promise>; + addListener: jest.MockedFunction<() => void>; + removeAllListeners: jest.MockedFunction<() => void>; + useClipboard: jest.MockedFunction<() => [string, jest.MockedFunction<() => void>]>; + }; + export default mockClipboard; +} From b87642aad54f4f134e799b1a6406baa88ccb2caa Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Fri, 19 Jan 2024 13:29:28 +0100 Subject: [PATCH 141/660] Use "Route pending" instead of "TBD" --- src/components/DistanceEReceipt.js | 4 +- .../MoneyRequestConfirmationList.js | 7 ++- ...oraryForRefactorRequestConfirmationList.js | 7 ++- .../ReportActionItem/MoneyReportView.tsx | 2 +- .../ReportActionItem/MoneyRequestPreview.js | 18 +++---- .../ReportActionItem/MoneyRequestView.js | 4 +- .../ReportActionItem/ReportPreview.js | 11 ++-- src/languages/en.ts | 1 + src/languages/es.ts | 1 + src/libs/CurrencyUtils.ts | 8 +-- src/libs/DistanceRequestUtils.ts | 5 +- src/libs/ReportUtils.ts | 38 ++++++-------- src/libs/TransactionUtils.ts | 19 ++----- src/libs/actions/IOU.js | 52 +++++++++++++++++-- src/libs/actions/Transaction.ts | 12 +++-- 15 files changed, 119 insertions(+), 70 deletions(-) diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index f566fb77b912..75457f2a7cc9 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -7,6 +7,7 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import * as CurrencyUtils from '@libs/CurrencyUtils'; import * as ReceiptUtils from '@libs/ReceiptUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; @@ -34,7 +35,8 @@ function DistanceEReceipt({transaction}) { const {translate} = useLocalize(); const {isOffline} = useNetwork(); const {thumbnail} = TransactionUtils.hasReceipt(transaction) ? ReceiptUtils.getThumbnailAndImageURIs(transaction) : {}; - const {formattedAmount: formattedTransactionAmount, merchant: transactionMerchant, created: transactionDate} = ReportUtils.getTransactionDetails(transaction); + const {amount: transactionAmount, currency: transactionCurrency, merchant: transactionMerchant, created: transactionDate} = ReportUtils.getTransactionDetails(transaction); + const formattedTransactionAmount = transactionAmount ? CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency) : ''; const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); const waypoints = lodashGet(transaction, 'comment.waypoints', {}); const sortedWaypoints = useMemo( diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index f66e73a2ef02..a54ca8c7a424 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -256,7 +256,7 @@ function MoneyRequestConfirmationList(props) { const hasRoute = TransactionUtils.hasRoute(transaction); const isDistanceRequestWithoutRoute = props.isDistanceRequest && !hasRoute; const formattedAmount = isDistanceRequestWithoutRoute - ? translate('common.tbd') + ? '' : CurrencyUtils.convertToDisplayString( shouldCalculateDistanceAmount ? DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate) : props.iouAmount, props.isDistanceRequest ? currency : props.iouCurrencyCode, @@ -425,6 +425,11 @@ function MoneyRequestConfirmationList(props) { if (!props.isDistanceRequest) { return; } + + if (!hasRoute) { + IOU.setMoneyRequestPendingFields_temporaryForRefactor(props.transactionID, {waypoints: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}); + } + const distanceMerchant = DistanceRequestUtils.getDistanceMerchant(hasRoute, distance, unit, rate, currency, translate, toLocaleDigit); IOU.setMoneyRequestMerchant_temporaryForRefactor(props.transactionID, distanceMerchant); }, [hasRoute, distance, unit, rate, currency, translate, toLocaleDigit, props.isDistanceRequest, props.transactionID]); diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 36d424ea28f2..c6e044b12918 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -285,7 +285,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ const hasRoute = TransactionUtils.hasRoute(transaction); const isDistanceRequestWithoutRoute = isDistanceRequest && !hasRoute; const formattedAmount = isDistanceRequestWithoutRoute - ? translate('common.tbd') + ? '' : CurrencyUtils.convertToDisplayString( shouldCalculateDistanceAmount ? DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate) : iouAmount, isDistanceRequest ? currency : iouCurrencyCode, @@ -472,6 +472,11 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ if (!isDistanceRequest) { return; } + + if (!hasRoute) { + IOU.setMoneyRequestPendingFields_temporaryForRefactor(transaction.transactionID, {waypoints: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}); + } + const distanceMerchant = DistanceRequestUtils.getDistanceMerchant(hasRoute, distance, unit, rate, currency, translate, toLocaleDigit); IOU.setMoneyRequestMerchant_temporaryForRefactor(transaction.transactionID, distanceMerchant); }, [hasRoute, distance, unit, rate, currency, translate, toLocaleDigit, isDistanceRequest, transaction]); diff --git a/src/components/ReportActionItem/MoneyReportView.tsx b/src/components/ReportActionItem/MoneyReportView.tsx index 4fcca3e518a5..f092822b94e8 100644 --- a/src/components/ReportActionItem/MoneyReportView.tsx +++ b/src/components/ReportActionItem/MoneyReportView.tsx @@ -42,7 +42,7 @@ function MoneyReportView({report, policyReportFields, shouldShowHorizontalRule}: const {totalDisplaySpend, nonReimbursableSpend, reimbursableSpend} = ReportUtils.getMoneyRequestSpendBreakdown(report); const shouldShowBreakdown = nonReimbursableSpend && reimbursableSpend; - const formattedTotalAmount = CurrencyUtils.convertToDisplayString(totalDisplaySpend, report.currency, ReportUtils.hasOnlyDistanceRequestTransactions(report.reportID)); + const formattedTotalAmount = CurrencyUtils.convertToDisplayString(totalDisplaySpend, report.currency); const formattedOutOfPocketAmount = CurrencyUtils.convertToDisplayString(reimbursableSpend, report.currency); const formattedCompanySpendAmount = CurrencyUtils.convertToDisplayString(nonReimbursableSpend, report.currency); diff --git a/src/components/ReportActionItem/MoneyRequestPreview.js b/src/components/ReportActionItem/MoneyRequestPreview.js index f8ddef88663a..fd182b1d96ae 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview.js +++ b/src/components/ReportActionItem/MoneyRequestPreview.js @@ -155,25 +155,21 @@ function MoneyRequestPreview(props) { // Pay button should only be visible to the manager of the report. const isCurrentUserManager = managerID === sessionAccountID; - const { - amount: requestAmount, - formattedAmount: formattedRequestAmount, - currency: requestCurrency, - comment: requestComment, - merchant, - } = ReportUtils.getTransactionDetails(props.transaction); + const {amount: requestAmount, currency: requestCurrency, comment: requestComment, merchant} = ReportUtils.getTransactionDetails(props.transaction); const description = truncate(requestComment, {length: CONST.REQUEST_PREVIEW.MAX_LENGTH}); const requestMerchant = truncate(merchant, {length: CONST.REQUEST_PREVIEW.MAX_LENGTH}); const hasReceipt = TransactionUtils.hasReceipt(props.transaction); const isScanning = hasReceipt && TransactionUtils.isReceiptBeingScanned(props.transaction); const hasFieldErrors = TransactionUtils.hasMissingSmartscanFields(props.transaction); const isDistanceRequest = TransactionUtils.isDistanceRequest(props.transaction); + const hasPendingRoute = TransactionUtils.hasPendingRoute(props.transaction); const isExpensifyCardTransaction = TransactionUtils.isExpensifyCardTransaction(props.transaction); const isSettled = ReportUtils.isSettled(props.iouReport.reportID); const isDeleted = lodashGet(props.action, 'pendingAction', null) === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; // Show the merchant for IOUs and expenses only if they are custom or not related to scanning smartscan - const shouldShowMerchant = !_.isEmpty(requestMerchant) && requestMerchant !== CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT && requestMerchant !== CONST.TRANSACTION.DEFAULT_MERCHANT; + const shouldShowMerchant = + !_.isEmpty(requestMerchant) && requestMerchant !== CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT && requestMerchant !== CONST.TRANSACTION.DEFAULT_MERCHANT && !hasPendingRoute; const shouldShowDescription = !_.isEmpty(description) && !shouldShowMerchant && !isScanning; let merchantOrDescription = requestMerchant; @@ -231,11 +227,15 @@ function MoneyRequestPreview(props) { return translate('iou.receiptScanning'); } + if (hasPendingRoute) { + return translate('iou.routePending'); + } + if (TransactionUtils.hasMissingSmartscanFields(props.transaction)) { return Localize.translateLocal('iou.receiptMissingDetails'); } - return formattedRequestAmount; + return CurrencyUtils.convertToDisplayString(requestAmount, requestCurrency); }; const getDisplayDeleteAmountText = () => { diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index 677a75453f0e..7703940ea13f 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -128,7 +128,7 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate const { created: transactionDate, amount: transactionAmount, - formattedAmount: formattedTransactionAmount, + currency: transactionCurrency, comment: transactionDescription, merchant: transactionMerchant, billable: transactionBillable, @@ -140,6 +140,8 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate } = ReportUtils.getTransactionDetails(transaction); const isEmptyMerchant = transactionMerchant === '' || transactionMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; const isDistanceRequest = TransactionUtils.isDistanceRequest(transaction); + const hasPendingRoute = TransactionUtils.hasPendingRoute(transaction); + const formattedTransactionAmount = transactionAmount && !hasPendingRoute ? CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency) : ''; const formattedOriginalAmount = transactionOriginalAmount && transactionOriginalCurrency && CurrencyUtils.convertToDisplayString(transactionOriginalAmount, transactionOriginalCurrency); const isCardTransaction = TransactionUtils.isCardTransaction(transaction); const cardProgramName = isCardTransaction ? CardUtils.getCardDescription(transactionCardID) : ''; diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js index 6c76b49a89eb..cd682d814ba5 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.js @@ -141,11 +141,11 @@ function ReportPreview(props) { const {translate} = useLocalize(); const {canUseViolations} = usePermissions(); - const {hasMissingSmartscanFields, areAllRequestsBeingSmartScanned, hasOnlyDistanceRequests, hasNonReimbursableTransactions} = useMemo( + const {hasMissingSmartscanFields, areAllRequestsBeingSmartScanned, hasOnlyPendingDistanceRequests, hasNonReimbursableTransactions} = useMemo( () => ({ hasMissingSmartscanFields: ReportUtils.hasMissingSmartscanFields(props.iouReportID), areAllRequestsBeingSmartScanned: ReportUtils.areAllRequestsBeingSmartScanned(props.iouReportID, props.action), - hasOnlyDistanceRequests: ReportUtils.hasOnlyDistanceRequestTransactions(props.iouReportID), + hasOnlyPendingDistanceRequests: ReportUtils.hasOnlyTransactionsWithPendingRoutes(props.iouReportID), hasNonReimbursableTransactions: ReportUtils.hasNonReimbursableTransactions(props.iouReportID), }), // When transactions get updated these status may have changed, so that is a case where we also want to run this. @@ -174,11 +174,10 @@ function ReportPreview(props) { const hasErrors = (hasReceipts && hasMissingSmartscanFields) || (canUseViolations && ReportUtils.hasViolations(props.iouReportID, props.transactionViolations)); const lastThreeTransactionsWithReceipts = transactionsWithReceipts.slice(-3); const lastThreeReceipts = _.map(lastThreeTransactionsWithReceipts, (transaction) => ReceiptUtils.getThumbnailAndImageURIs(transaction)); - const formattedMerchant = numberOfRequests === 1 && hasReceipts ? TransactionUtils.getMerchant(transactionsWithReceipts[0]) : null; + let formattedMerchant = numberOfRequests === 1 && hasReceipts && !hasOnlyPendingDistanceRequests ? TransactionUtils.getMerchant(transactionsWithReceipts[0]) : null; if (TransactionUtils.isPartialMerchant(formattedMerchant)) { formattedMerchant = null; } - const hasOnlyLoadingDistanceRequests = hasOnlyDistanceRequests && _.every(transactionsWithReceipts, (transaction) => TransactionUtils.isDistanceBeingCalculated(transaction)); const previewSubtitle = formattedMerchant || props.translate('iou.requestCount', { @@ -195,8 +194,8 @@ function ReportPreview(props) { ); const getDisplayAmount = () => { - if (hasOnlyLoadingDistanceRequests) { - return props.translate('common.tbd'); + if (hasOnlyPendingDistanceRequests) { + return props.translate('iou.routePending'); } if (totalDisplaySpend) { return CurrencyUtils.convertToDisplayString(totalDisplaySpend, props.iouReport.currency); diff --git a/src/languages/en.ts b/src/languages/en.ts index b6da38df21a0..f1fe2c0367de 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -580,6 +580,7 @@ export default { canceled: 'Canceled', posted: 'Posted', deleteReceipt: 'Delete receipt', + routePending: 'Route pending...', receiptScanning: 'Receipt scan in progress…', receiptMissingDetails: 'Receipt missing details', receiptStatusTitle: 'Scanning…', diff --git a/src/languages/es.ts b/src/languages/es.ts index 2478c8ba8bd2..d163a7bc2fa0 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -573,6 +573,7 @@ export default { canceled: 'Canceló', posted: 'Contabilizado', deleteReceipt: 'Eliminar recibo', + routePending: 'Ruta pendiente...', receiptScanning: 'Escaneo de recibo en curso…', receiptMissingDetails: 'Recibo con campos vacíos', receiptStatusTitle: 'Escaneando…', diff --git a/src/libs/CurrencyUtils.ts b/src/libs/CurrencyUtils.ts index 42387e03c80b..cec9d1e09088 100644 --- a/src/libs/CurrencyUtils.ts +++ b/src/libs/CurrencyUtils.ts @@ -2,7 +2,6 @@ import Onyx from 'react-native-onyx'; import CONST from '@src/CONST'; import type {OnyxValues} from '@src/ONYXKEYS'; import ONYXKEYS from '@src/ONYXKEYS'; -import * as Localize from './Localize'; import BaseLocaleListener from './Localize/LocaleListener/BaseLocaleListener'; import * as NumberFormatUtils from './NumberFormatUtils'; @@ -98,13 +97,8 @@ function convertToFrontendAmount(amountAsInt: number): number { * * @param amountInCents – should be an integer. Anything after a decimal place will be dropped. * @param currency - IOU currency - * @param shouldFallbackToTbd - whether to return 'TBD' instead of a falsy value (e.g. 0.00) */ -function convertToDisplayString(amountInCents = 0, currency: string = CONST.CURRENCY.USD, shouldFallbackToTbd = false): string { - if (shouldFallbackToTbd && !amountInCents) { - return Localize.translateLocal('common.tbd'); - } - +function convertToDisplayString(amountInCents = 0, currency: string = CONST.CURRENCY.USD): string { const convertedAmount = convertToFrontendAmount(amountInCents); return NumberFormatUtils.format(BaseLocaleListener.getPreferredLocale(), convertedAmount, { style: 'currency', diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index c92e9bfd3f67..2309d517df6d 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -95,8 +95,11 @@ function getDistanceMerchant( translate: LocaleContextProps['translate'], toLocaleDigit: LocaleContextProps['toLocaleDigit'], ): string { - const distanceInUnits = hasRoute ? getRoundedDistanceInUnits(distanceInMeters, unit) : translate('common.tbd'); + if (!hasRoute) { + return translate('iou.routePending'); + } + const distanceInUnits = getRoundedDistanceInUnits(distanceInMeters, unit); const distanceUnit = unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.miles') : translate('common.kilometers'); const singularDistanceUnit = unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.mile') : translate('common.kilometer'); const unitString = distanceInUnits === '1' ? singularDistanceUnit : distanceUnit; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 9260d39bde56..64985920454d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -335,7 +335,6 @@ type TransactionDetails = cardID: number; originalAmount: number; originalCurrency: string; - formattedAmount: string; } | undefined; @@ -1094,10 +1093,9 @@ function hasSingleParticipant(report: OnyxEntry): boolean { } /** - * Checks whether all the transactions linked to the IOU report are of the Distance Request type - * + * Checks whether all the transactions linked to the IOU report are of the Distance Request type with pending routes */ -function hasOnlyDistanceRequestTransactions(iouReportID: string | undefined): boolean { +function hasOnlyTransactionsWithPendingRoutes(iouReportID: string | undefined): boolean { const transactions = TransactionUtils.getAllReportTransactions(iouReportID); // Early return false in case not having any transaction @@ -1105,7 +1103,7 @@ function hasOnlyDistanceRequestTransactions(iouReportID: string | undefined): bo return false; } - return transactions.every((transaction) => TransactionUtils.isDistanceRequest(transaction)); + return transactions.every((transaction) => TransactionUtils.hasPendingRoute(transaction)); } /** @@ -1902,7 +1900,7 @@ function getPolicyExpenseChatName(report: OnyxEntry, policy: OnyxEntry

, policy: OnyxEntry | undefined = undefined): string { const moneyRequestTotal = getMoneyRequestReimbursableTotal(report); - const formattedAmount = CurrencyUtils.convertToDisplayString(moneyRequestTotal, report?.currency, hasOnlyDistanceRequestTransactions(report?.reportID)); + const formattedAmount = CurrencyUtils.convertToDisplayString(moneyRequestTotal, report?.currency); const payerOrApproverName = isExpenseReport(report) ? getPolicyName(report, false, policy) : getDisplayNameForParticipant(report?.managerID) ?? ''; const payerPaidAmountMessage = Localize.translateLocal('iou.payerPaidAmount', { payer: payerOrApproverName, @@ -1944,18 +1942,7 @@ function getTransactionDetails(transaction: OnyxEntry, createdDateF if (!transaction) { return; } - const report = getReport(transaction?.reportID); - const amount = TransactionUtils.getAmount(transaction, isNotEmptyObject(report) && isExpenseReport(report)); - const currency = TransactionUtils.getCurrency(transaction); - - let formattedAmount; - if (TransactionUtils.isDistanceBeingCalculated(transaction)) { - formattedAmount = Localize.translateLocal('common.tbd'); - } else { - formattedAmount = amount ? CurrencyUtils.convertToDisplayString(amount, currency) : ''; - } - return { created: TransactionUtils.getCreated(transaction, createdDateFormat), amount: TransactionUtils.getAmount(transaction, !isEmptyObject(report) && isExpenseReport(report)), @@ -2153,6 +2140,11 @@ function getTransactionReportName(reportAction: OnyxEntry): string // Transaction data might be empty on app's first load, if so we fallback to Request return Localize.translateLocal('iou.request'); } + + if (TransactionUtils.hasPendingRoute(transaction)) { + return Localize.translateLocal('iou.routePending'); + } + if (TransactionUtils.hasReceipt(transaction) && TransactionUtils.isReceiptBeingScanned(transaction)) { return Localize.translateLocal('iou.receiptScanning'); } @@ -2164,7 +2156,7 @@ function getTransactionReportName(reportAction: OnyxEntry): string const transactionDetails = getTransactionDetails(transaction); return Localize.translateLocal(ReportActionsUtils.isSentMoneyReportAction(reportAction) ? 'iou.threadSentMoneyReportName' : 'iou.threadRequestReportName', { - formattedAmount: CurrencyUtils.convertToDisplayString(transactionDetails?.amount ?? 0, transactionDetails?.currency, TransactionUtils.isDistanceRequest(transaction)) ?? '', + formattedAmount: CurrencyUtils.convertToDisplayString(transactionDetails?.amount ?? 0, transactionDetails?.currency) ?? '', comment: transactionDetails?.comment ?? '', }); } @@ -2177,7 +2169,7 @@ function getTransactionReportName(reportAction: OnyxEntry): string function getReportPreviewMessage( report: OnyxEntry | EmptyObject, reportAction: OnyxEntry | EmptyObject = {}, - shouldConsiderReceiptBeingScanned = false, + shouldConsiderScanningReceiptOrPendingRoute = false, isPreviewMessageForParentChatReport = false, policy: OnyxEntry = null, isForListPreview = false, @@ -2225,12 +2217,16 @@ function getReportPreviewMessage( }); } - if (!isEmptyObject(reportAction) && shouldConsiderReceiptBeingScanned && reportAction && ReportActionsUtils.isMoneyRequestAction(reportAction)) { + if (!isEmptyObject(reportAction) && shouldConsiderScanningReceiptOrPendingRoute && reportAction && ReportActionsUtils.isMoneyRequestAction(reportAction)) { const linkedTransaction = TransactionUtils.getLinkedTransaction(reportAction); if (!isEmptyObject(linkedTransaction) && TransactionUtils.hasReceipt(linkedTransaction) && TransactionUtils.isReceiptBeingScanned(linkedTransaction)) { return Localize.translateLocal('iou.receiptScanning'); } + + if (!isEmptyObject(linkedTransaction) && TransactionUtils.hasPendingRoute(linkedTransaction)) { + return Localize.translateLocal('iou.routePending'); + } } const originalMessage = reportAction?.originalMessage as IOUMessage | undefined; @@ -4717,7 +4713,7 @@ export { buildTransactionThread, areAllRequestsBeingSmartScanned, getTransactionsWithReceipts, - hasOnlyDistanceRequestTransactions, + hasOnlyTransactionsWithPendingRoutes, hasNonReimbursableTransactions, hasMissingSmartscanFields, getIOUReportActionDisplayMessage, diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index bdb37a3ccd76..f6b60b35bce8 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -11,7 +11,6 @@ import type {Comment, Receipt, Waypoint, WaypointCollection} from '@src/types/on import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isCorporateCard, isExpensifyCard} from './CardUtils'; import DateUtils from './DateUtils'; -import * as Localize from './Localize'; import * as NumberUtils from './NumberUtils'; type AdditionalTransactionChanges = {comment?: string; waypoints?: WaypointCollection}; @@ -317,29 +316,21 @@ function getOriginalAmount(transaction: Transaction): number { } /** - * Verify if the transaction is of Distance request and is expecting the distance to be calculated on the server: - * - it has a zero amount, which means the request was created offline and expects the distance calculation from the server - * - it is in `isLoading` state, which means the waypoints were updated offline and the distance requires re-calculation + * Verify if the transaction is of Distance request and is expecting the distance to be calculated on the server */ -function isDistanceBeingCalculated(transaction: OnyxEntry): boolean { +function hasPendingRoute(transaction: OnyxEntry): boolean { if (!transaction) { return false; } - const amount = getAmount(transaction, false); - return isDistanceRequest(transaction) && (!!transaction?.isLoading || amount === 0); + return isDistanceRequest(transaction) && !!transaction.pendingFields?.waypoints; } /** * Return the merchant field from the transaction, return the modifiedMerchant if present. */ function getMerchant(transaction: OnyxEntry): string { - if (!transaction) { - return ''; - } - - const merchant = transaction.modifiedMerchant ? transaction.modifiedMerchant : transaction.merchant ?? ''; - return isDistanceBeingCalculated(transaction) ? merchant.replace(CONST.REGEX.FIRST_SPACE, Localize.translateLocal('common.tbd')) : merchant; + return transaction?.modifiedMerchant ? transaction.modifiedMerchant : transaction?.merchant ?? ''; } function getDistance(transaction: Transaction): number { @@ -601,7 +592,7 @@ export { isReceiptBeingScanned, getValidWaypoints, isDistanceRequest, - isDistanceBeingCalculated, + hasPendingRoute, isExpensifyCardTransaction, isCardTransaction, isPending, diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 7ee752a1f0ef..1a9030c89cc4 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -214,6 +214,14 @@ function setMoneyRequestMerchant_temporaryForRefactor(transactionID, merchant) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {merchant: merchant.trim()}); } +/** + * @param {String} transactionID + * @param {Object} pendingFields + */ +function setMoneyRequestPendingFields_temporaryForRefactor(transactionID, pendingFields) { + Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {pendingFields}); +} + /** * @param {String} transactionID * @param {String} category @@ -964,7 +972,8 @@ function getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, t // We don't create a modified report action if we're updating the waypoints, // since there isn't actually any optimistic data we can create for them and the report action is created on the server // with the response from the MapBox API - if (!_.has(transactionChanges, 'waypoints')) { + const hasPendingWaypoints = _.has(transactionChanges, 'waypoints'); + if (!hasPendingWaypoints) { const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(transactionThread, transaction, transactionChanges, isFromExpenseReport); params.reportActionID = updatedReportAction.reportActionID; @@ -1026,7 +1035,7 @@ function getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, t value: { ...updatedTransaction, pendingFields, - isLoading: _.has(transactionChanges, 'waypoints'), + isLoading: hasPendingWaypoints, errorFields: null, }, }); @@ -1090,13 +1099,47 @@ function getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, t }, }); - if (_.has(transactionChanges, 'waypoints')) { + if (hasPendingWaypoints) { + // When updating waypoints, we need to explicitly set the transaction's amount and IOU report's total to 0. + // These values must be calculated on the server because they depend on the distance. + optimisticData.push( + ...[ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, + value: { + total: CONST.IOU.DEFAULT_AMOUNT, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + value: { + amount: CONST.IOU.DEFAULT_AMOUNT, + modifiedAmount: CONST.IOU.DEFAULT_AMOUNT, + modifiedMerchant: Localize.translateLocal('iou.routePending'), + }, + }, + ], + ); + // Delete the draft transaction when editing waypoints when the server responds successfully and there are no errors successData.push({ onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, value: null, }); + + // Revert the transaction's amount to the original value on failure. + // The IOU Report will be fully reverted in the failureData further below. + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + value: { + amount: transaction.amount, + modifiedAmount: transaction.modifiedAmount, + }, + }); } // Clear out loading states, pending fields, and add the error fields @@ -1110,7 +1153,7 @@ function getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, t }, }); - // Reset the iouReport to it's original state + // Reset the iouReport to its original state failureData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, @@ -3672,6 +3715,7 @@ export { setMoneyRequestDescription_temporaryForRefactor, setMoneyRequestMerchant_temporaryForRefactor, setMoneyRequestParticipants_temporaryForRefactor, + setMoneyRequestPendingFields_temporaryForRefactor, setMoneyRequestReceipt, setMoneyRequestTag_temporaryForRefactor, setMoneyRequestAmount, diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index 430de0557674..8dc83ed37a75 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -64,7 +64,9 @@ function saveWaypoint(transactionID: string, index: string, waypoint: RecentWayp [`waypoint${index}`]: waypoint, }, }, - amount: CONST.IOU.DEFAULT_AMOUNT, + // We want to reset the amount only for draft transactions (when creating the request). + // When modifying an existing transaction, the amount will be updated on the actual IOU update operation. + ...(isDraft && {amount: CONST.IOU.DEFAULT_AMOUNT}), // Empty out errors when we're saving a new waypoint as this indicates the user is updating their input errorFields: { route: null, @@ -133,7 +135,9 @@ function removeWaypoint(transaction: Transaction, currentIndex: string, isDraft: ...transaction.comment, waypoints: reIndexedWaypoints, }, - amount: CONST.IOU.DEFAULT_AMOUNT, + // We want to reset the amount only for draft transactions (when creating the request). + // When modifying an existing transaction, the amount will be updated on the actual IOU update operation. + ...(isDraft && {amount: CONST.IOU.DEFAULT_AMOUNT}), }; if (!isRemovedWaypointEmpty) { @@ -246,7 +250,9 @@ function updateWaypoints(transactionID: string, waypoints: WaypointCollection, i comment: { waypoints, }, - amount: CONST.IOU.DEFAULT_AMOUNT, + // We want to reset the amount only for draft transactions (when creating the request). + // When modifying an existing transaction, the amount will be updated on the actual IOU update operation. + ...(isDraft && {amount: CONST.IOU.DEFAULT_AMOUNT}), // Empty out errors when we're saving new waypoints as this indicates the user is updating their input errorFields: { route: null, From 5a24138c6aad5b5a2ed01a511ccd5a2e53dd82fa Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Fri, 19 Jan 2024 13:49:52 +0100 Subject: [PATCH 142/660] Fix the pending route logic in Distance e-Receipt --- src/components/DistanceEReceipt.js | 11 ++++---- .../ReportActionItem/ReportPreview.js | 12 +++++---- src/libs/ReceiptUtils.ts | 26 ++++++++++--------- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index 75457f2a7cc9..3f337281c548 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -4,7 +4,6 @@ import {ScrollView, View} from 'react-native'; import _ from 'underscore'; import EReceiptBackground from '@assets/images/eReceipt_background.svg'; import useLocalize from '@hooks/useLocalize'; -import useNetwork from '@hooks/useNetwork'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import * as CurrencyUtils from '@libs/CurrencyUtils'; @@ -33,10 +32,10 @@ function DistanceEReceipt({transaction}) { const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); - const {isOffline} = useNetwork(); const {thumbnail} = TransactionUtils.hasReceipt(transaction) ? ReceiptUtils.getThumbnailAndImageURIs(transaction) : {}; const {amount: transactionAmount, currency: transactionCurrency, merchant: transactionMerchant, created: transactionDate} = ReportUtils.getTransactionDetails(transaction); - const formattedTransactionAmount = transactionAmount ? CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency) : ''; + const formattedTransactionAmount = CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency); + const hasPendingRoute = TransactionUtils.hasPendingRoute(transaction); const thumbnailSource = tryResolveUrlFromApiRoot(thumbnail || ''); const waypoints = lodashGet(transaction, 'comment.waypoints', {}); const sortedWaypoints = useMemo( @@ -64,7 +63,7 @@ function DistanceEReceipt({transaction}) { /> - {isOffline || !thumbnailSource ? ( + {hasPendingRoute || !thumbnailSource ? ( ) : ( - {formattedTransactionAmount} + {!hasPendingRoute && ( + {formattedTransactionAmount} + )} {transactionMerchant} diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js index cd682d814ba5..2e5c73b04f54 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.js @@ -179,11 +179,13 @@ function ReportPreview(props) { formattedMerchant = null; } const previewSubtitle = - formattedMerchant || - props.translate('iou.requestCount', { - count: numberOfRequests - numberOfScanningReceipts, - scanningReceipts: numberOfScanningReceipts, - }); + numberOfRequests === 1 && hasOnlyPendingDistanceRequests + ? '' + : formattedMerchant || + props.translate('iou.requestCount', { + count: numberOfRequests - numberOfScanningReceipts, + scanningReceipts: numberOfScanningReceipts, + }); const shouldShowSubmitButton = isDraftExpenseReport && reimbursableSpend !== 0; diff --git a/src/libs/ReceiptUtils.ts b/src/libs/ReceiptUtils.ts index bcba68a3a0bd..9e985f0bd60e 100644 --- a/src/libs/ReceiptUtils.ts +++ b/src/libs/ReceiptUtils.ts @@ -8,6 +8,7 @@ import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type {Transaction} from '@src/types/onyx'; import * as FileUtils from './fileDownload/FileUtils'; +import * as TransactionUtils from './TransactionUtils'; type ThumbnailAndImageURI = { image: ImageSourcePropType | string; @@ -29,27 +30,28 @@ type FileNameAndExtension = { * @param receiptFileName */ function getThumbnailAndImageURIs(transaction: Transaction, receiptPath: string | null = null, receiptFileName: string | null = null): ThumbnailAndImageURI { + if (TransactionUtils.hasPendingRoute(transaction)) { + return {thumbnail: null, image: ReceiptGeneric, isLocalFile: true}; + } + // URI to image, i.e. blob:new.expensify.com/9ef3a018-4067-47c6-b29f-5f1bd35f213d or expensify.com/receipts/w_e616108497ef940b7210ec6beb5a462d01a878f4.jpg const path = transaction?.receipt?.source ?? receiptPath ?? ''; // filename of uploaded image or last part of remote URI const filename = transaction?.filename ?? receiptFileName ?? ''; const isReceiptImage = Str.isImage(filename); - const hasEReceipt = transaction?.hasEReceipt; - if (!Object.hasOwn(transaction?.pendingFields ?? {}, 'waypoints')) { - if (hasEReceipt) { - return {thumbnail: null, image: ROUTES.ERECEIPT.getRoute(transaction.transactionID), transaction}; - } + if (hasEReceipt) { + return {thumbnail: null, image: ROUTES.ERECEIPT.getRoute(transaction.transactionID), transaction}; + } - // For local files, we won't have a thumbnail yet - if (isReceiptImage && (path.startsWith('blob:') || path.startsWith('file:'))) { - return {thumbnail: null, image: path, isLocalFile: true}; - } + // For local files, we won't have a thumbnail yet + if (isReceiptImage && (path.startsWith('blob:') || path.startsWith('file:'))) { + return {thumbnail: null, image: path, isLocalFile: true}; + } - if (isReceiptImage) { - return {thumbnail: `${path}.1024.jpg`, image: path}; - } + if (isReceiptImage) { + return {thumbnail: `${path}.1024.jpg`, image: path}; } const {fileExtension} = FileUtils.splitExtensionFromFileName(filename) as FileNameAndExtension; From c77614e859ced32948a8a4084113c3ecb8f61345 Mon Sep 17 00:00:00 2001 From: rayane-djouah <77965000+rayane-djouah@users.noreply.github.com> Date: Fri, 19 Jan 2024 17:02:59 +0100 Subject: [PATCH 143/660] address review comments --- .github/workflows/failureNotifier.yml | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/.github/workflows/failureNotifier.yml b/.github/workflows/failureNotifier.yml index 82b0b0fbe6ed..667fe3a737b2 100644 --- a/.github/workflows/failureNotifier.yml +++ b/.github/workflows/failureNotifier.yml @@ -14,9 +14,6 @@ jobs: runs-on: ubuntu-latest if: ${{ github.event.workflow_run.conclusion == 'failure' }} steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Fetch Workflow Run Jobs id: fetch-workflow-jobs uses: actions/github-script@v7 @@ -29,34 +26,26 @@ jobs: run_id: runId, }); return jobsData.data; - - - name: Get merged pull request number - id: getMergedPullRequestNumber - uses: actions-ecosystem/action-get-merged-pull-request@59afe90821bb0b555082ce8ff1e36b03f91553d9 - with: - github_token: ${{ github.token }} - - name: Process Each Failed Job uses: actions/github-script@v7 with: script: | - const prNumber = ${{ steps.getMergedPullRequestNumber.outputs.number }}; - const jobs = ${{ steps.fetch-workflow-jobs.outputs.result }}; - const failureLabel = 'workflow-failure'; + const jobs = ${{steps.fetch-workflow-jobs.outputs.result}}; - const prData = await github.rest.pulls.get({ + const headCommit = "${{ github.event.workflow_run.head_commit.id }}"; + const prData = await github.rest.repos.listPullRequestsAssociatedWithCommit({ owner: context.repo.owner, repo: context.repo.repo, - pull_number: prNumber, + commit_sha: headCommit, }); - const pr = prData.data; - + const pr = prData.data[0]; const prLink = pr.html_url; const prAuthor = pr.user.login; - const prMerger = pr.merged_by.login; + const prMerger = "${{ github.event.workflow_run.actor.login }}"; + const failureLabel = 'workflow-failure'; for (let i = 0; i < jobs.total_count; i++) { if (jobs.jobs[i].conclusion == 'failure') { const jobName = jobs.jobs[i].name; From 6685a9a4615bd356a2a95813cfd3d98b6f4dae8a Mon Sep 17 00:00:00 2001 From: someone-here Date: Fri, 19 Jan 2024 22:29:24 +0530 Subject: [PATCH 144/660] Reconcile description route and permission --- src/ROUTES.ts | 6 +- src/SCREENS.ts | 1 - src/components/ReportActionItem/TaskView.js | 2 +- src/libs/Navigation/linkingConfig.ts | 2 - src/libs/ReportUtils.ts | 2 +- src/pages/ReportDescriptionPage.js | 123 ++--------------- src/pages/RoomDescriptionPage.js | 138 ++++++++++++++++++++ 7 files changed, 152 insertions(+), 122 deletions(-) create mode 100644 src/pages/RoomDescriptionPage.js diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 6e07666c052e..4690637a4098 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -195,10 +195,6 @@ const ROUTES = { route: 'r/:reportID/settings/who-can-post', getRoute: (reportID: string) => `r/${reportID}/settings/who-can-post` as const, }, - REPORT_DESCRIPTION: { - route: 'r/:reportID/roomDescription', - getRoute: (reportID: string) => `r/${reportID}/roomDescription` as const, - }, SPLIT_BILL_DETAILS: { route: 'r/:reportID/split/:reportActionID', getRoute: (reportID: string, reportActionID: string) => `r/${reportID}/split/${reportActionID}` as const, @@ -216,7 +212,7 @@ const ROUTES = { route: 'r/:reportID/title', getRoute: (reportID: string) => `r/${reportID}/title` as const, }, - TASK_DESCRIPTION: { + REPORT_DESCRIPTION: { route: 'r/:reportID/description', getRoute: (reportID: string) => `r/${reportID}/description` as const, }, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index b053782d43be..ca33c8b9da15 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -175,7 +175,6 @@ const SCREENS = { TASK: { TITLE: 'Task_Title', - DESCRIPTION: 'Task_Description', ASSIGNEE: 'Task_Assignee', }, diff --git a/src/components/ReportActionItem/TaskView.js b/src/components/ReportActionItem/TaskView.js index 7f7b177136ed..03b631313750 100644 --- a/src/components/ReportActionItem/TaskView.js +++ b/src/components/ReportActionItem/TaskView.js @@ -150,7 +150,7 @@ function TaskView(props) { shouldParseTitle description={props.translate('task.description')} title={props.report.description || ''} - onPress={() => Navigation.navigate(ROUTES.TASK_DESCRIPTION.getRoute(props.report.reportID))} + onPress={() => Navigation.navigate(ROUTES.REPORT_DESCRIPTION.getRoute(props.report.reportID))} shouldShowRightIcon={isOpen} disabled={disableState} wrapperStyle={[styles.pv2, styles.taskDescriptionMenuItem]} diff --git a/src/libs/Navigation/linkingConfig.ts b/src/libs/Navigation/linkingConfig.ts index de1ef75fcefd..7a4e49be9372 100644 --- a/src/libs/Navigation/linkingConfig.ts +++ b/src/libs/Navigation/linkingConfig.ts @@ -335,7 +335,6 @@ const linkingConfig: LinkingOptions = { [SCREENS.NEW_TASK.TASK_SHARE_DESTINATION_SELECTOR]: ROUTES.NEW_TASK_SHARE_DESTINATION, [SCREENS.NEW_TASK.DETAILS]: ROUTES.NEW_TASK_DETAILS, [SCREENS.NEW_TASK.TITLE]: ROUTES.NEW_TASK_TITLE, - [SCREENS.NEW_TASK.DESCRIPTION]: ROUTES.NEW_TASK_DESCRIPTION, }, }, [SCREENS.RIGHT_MODAL.TEACHERS_UNITE]: { @@ -450,7 +449,6 @@ const linkingConfig: LinkingOptions = { [SCREENS.RIGHT_MODAL.TASK_DETAILS]: { screens: { [SCREENS.TASK.TITLE]: ROUTES.TASK_TITLE.route, - [SCREENS.TASK.DESCRIPTION]: ROUTES.TASK_DESCRIPTION.route, [SCREENS.TASK.ASSIGNEE]: ROUTES.TASK_ASSIGNEE.route, }, }, diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index b2da8426e256..7fb640f55168 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4339,7 +4339,7 @@ function getRoom(type: ValueOf, policyID: string) * We only want policy owners and admins to be able to modify the report description, but not in thread chat. */ function canEditReportDescription(report: OnyxEntry, policy: OnyxEntry): boolean { - return !isMoneyRequestReport(report) && !isArchivedRoom(report) && isChatRoom(report) && !isChatThread(report) && PolicyUtils.isPolicyAdmin(policy); + return !isMoneyRequestReport(report) && !isArchivedRoom(report) && isChatRoom(report) && !isChatThread(report) && isEmpty(policy); } /** * Checks if report action has error when smart scanning diff --git a/src/pages/ReportDescriptionPage.js b/src/pages/ReportDescriptionPage.js index 9093d165b876..9b9680e1e81d 100644 --- a/src/pages/ReportDescriptionPage.js +++ b/src/pages/ReportDescriptionPage.js @@ -1,34 +1,12 @@ -import {useFocusEffect} from '@react-navigation/native'; -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import PropTypes from 'prop-types'; -import React, {useCallback, useRef, useState} from 'react'; -import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; -import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; -import FormProvider from '@components/Form/FormProvider'; -import InputWrapper from '@components/Form/InputWrapper'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import ScreenWrapper from '@components/ScreenWrapper'; -import Text from '@components/Text'; -import TextInput from '@components/TextInput'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; -import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; -import Navigation from '@libs/Navigation/Navigation'; +import React from 'react'; import * as ReportUtils from '@libs/ReportUtils'; -import updateMultilineInputRange from '@libs/updateMultilineInputRange'; -import * as Report from '@userActions/Report'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; import withReportOrNotFound from './home/report/withReportOrNotFound'; import reportPropTypes from './reportPropTypes'; -import {policyDefaultProps, policyPropTypes} from './workspace/withPolicy'; +import RoomDescriptionPage from './RoomDescriptionPage'; +import TaskDescriptionPage from './tasks/TaskDescriptionPage'; const propTypes = { - ...withLocalizePropTypes, - ...policyPropTypes, - /** The report currently being looked at */ report: reportPropTypes.isRequired, @@ -41,98 +19,19 @@ const propTypes = { }).isRequired, }; -const defaultProps = { - ...policyDefaultProps, -}; - function ReportDescriptionPage(props) { - const styles = useThemeStyles(); - const parser = new ExpensiMark(); - const [description, setdescription] = useState(() => parser.htmlToMarkdown(props.report.description)); - const reportDescriptionInputRef = useRef(null); - const focusTimeoutRef = useRef(null); - - const handleReportDescriptionChange = useCallback((value) => { - setdescription(value); - }, []); - - const submitForm = useCallback(() => { - Report.updateDescription(props.report.reportID, props.report.description, description.trim()); - }, [props.report.reportID, props.report.description, description]); + const isTask = ReportUtils.isTaskReport(props.report); - useFocusEffect( - useCallback(() => { - focusTimeoutRef.current = setTimeout(() => { - if (reportDescriptionInputRef.current) { - reportDescriptionInputRef.current.focus(); - } - return () => { - if (!focusTimeoutRef.current) { - return; - } - clearTimeout(focusTimeoutRef.current); - }; - }, CONST.ANIMATED_TRANSITION); - }, []), - ); + if (isTask) { + // eslint-disable-next-line react/jsx-props-no-spreading + return ; + } - return ( - - - Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(props.report.reportID))} - /> - - {props.translate('reportDescriptionPage.explainerText')} - - { - if (!el) { - return; - } - reportDescriptionInputRef.current = el; - updateMultilineInputRange(reportDescriptionInputRef.current); - }} - value={description} - onChangeText={handleReportDescriptionChange} - autoCapitalize="none" - containerStyles={[styles.autoGrowHeightMultilineInput]} - /> - - - - - ); + // eslint-disable-next-line react/jsx-props-no-spreading + return ; } ReportDescriptionPage.displayName = 'ReportDescriptionPage'; ReportDescriptionPage.propTypes = propTypes; -ReportDescriptionPage.defaultProps = defaultProps; -export default compose( - withLocalize, - withReportOrNotFound(), - withOnyx({ - policy: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, - }, - }), -)(ReportDescriptionPage); +export default withReportOrNotFound()(ReportDescriptionPage); diff --git a/src/pages/RoomDescriptionPage.js b/src/pages/RoomDescriptionPage.js new file mode 100644 index 000000000000..f7c6e621454b --- /dev/null +++ b/src/pages/RoomDescriptionPage.js @@ -0,0 +1,138 @@ +import {useFocusEffect} from '@react-navigation/native'; +import ExpensiMark from 'expensify-common/lib/ExpensiMark'; +import PropTypes from 'prop-types'; +import React, {useCallback, useRef, useState} from 'react'; +import {View} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; +import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; +import FormProvider from '@components/Form/FormProvider'; +import InputWrapper from '@components/Form/InputWrapper'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import ScreenWrapper from '@components/ScreenWrapper'; +import Text from '@components/Text'; +import TextInput from '@components/TextInput'; +import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import compose from '@libs/compose'; +import Navigation from '@libs/Navigation/Navigation'; +import * as ReportUtils from '@libs/ReportUtils'; +import updateMultilineInputRange from '@libs/updateMultilineInputRange'; +import * as Report from '@userActions/Report'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import withReportOrNotFound from './home/report/withReportOrNotFound'; +import reportPropTypes from './reportPropTypes'; +import {policyDefaultProps, policyPropTypes} from './workspace/withPolicy'; + +const propTypes = { + ...withLocalizePropTypes, + ...policyPropTypes, + + /** The report currently being looked at */ + report: reportPropTypes.isRequired, + + /** Route params */ + route: PropTypes.shape({ + params: PropTypes.shape({ + /** Report ID passed via route r/:reportID/roomDescription */ + reportID: PropTypes.string, + }), + }).isRequired, +}; + +const defaultProps = { + ...policyDefaultProps, +}; + +function RoomDescriptionPage(props) { + const styles = useThemeStyles(); + const parser = new ExpensiMark(); + const [description, setdescription] = useState(() => parser.htmlToMarkdown(props.report.description)); + const reportDescriptionInputRef = useRef(null); + const focusTimeoutRef = useRef(null); + + const handleReportDescriptionChange = useCallback((value) => { + setdescription(value); + }, []); + + const submitForm = useCallback(() => { + Report.updateDescription(props.report.reportID, props.report.description, description.trim()); + }, [props.report.reportID, props.report.description, description]); + + useFocusEffect( + useCallback(() => { + focusTimeoutRef.current = setTimeout(() => { + if (reportDescriptionInputRef.current) { + reportDescriptionInputRef.current.focus(); + } + return () => { + if (!focusTimeoutRef.current) { + return; + } + clearTimeout(focusTimeoutRef.current); + }; + }, CONST.ANIMATED_TRANSITION); + }, []), + ); + + return ( + + + Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(props.report.reportID))} + /> + + {props.translate('reportDescriptionPage.explainerText')} + + { + if (!el) { + return; + } + reportDescriptionInputRef.current = el; + updateMultilineInputRange(reportDescriptionInputRef.current); + }} + value={description} + onChangeText={handleReportDescriptionChange} + autoCapitalize="none" + containerStyles={[styles.autoGrowHeightMultilineInput]} + /> + + + + + ); +} + +RoomDescriptionPage.displayName = 'RoomDescriptionPage'; +RoomDescriptionPage.propTypes = propTypes; +RoomDescriptionPage.defaultProps = defaultProps; + +export default compose( + withLocalize, + withReportOrNotFound(), + withOnyx({ + policy: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, + }, + }), +)(RoomDescriptionPage); From dd340521434d2ac37e0f5cc1c96309f846e7fe22 Mon Sep 17 00:00:00 2001 From: someone-here Date: Fri, 19 Jan 2024 22:32:20 +0530 Subject: [PATCH 145/660] Fix lint --- src/libs/ReportUtils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 06d63e552acc..7761ab9cdfed 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4730,7 +4730,6 @@ export { canEditReportDescription, doesTransactionThreadHaveViolations, hasViolations, - shouldDisableWelcomeMessage, navigateToPrivateNotes, canEditWriteCapability, hasSmartscanError, From 0c6a9e4f12c4fa431b8bc1d60452891570039caf Mon Sep 17 00:00:00 2001 From: someone-here Date: Fri, 19 Jan 2024 22:42:54 +0530 Subject: [PATCH 146/660] Fix TS --- src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx | 1 - src/libs/Navigation/types.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index 39b977d99946..1b28de32671a 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -136,7 +136,6 @@ const ReportSettingsModalStackNavigator = createModalStackNavigator({ [SCREENS.TASK.TITLE]: () => require('../../../pages/tasks/TaskTitlePage').default as React.ComponentType, - [SCREENS.TASK.DESCRIPTION]: () => require('../../../pages/tasks/TaskDescriptionPage').default as React.ComponentType, [SCREENS.TASK.ASSIGNEE]: () => require('../../../pages/tasks/TaskAssigneeSelectorModal').default as React.ComponentType, }); diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 7f47b95b3562..ae121c6083c0 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -270,7 +270,6 @@ type TeachersUniteNavigatorParamList = { type TaskDetailsNavigatorParamList = { [SCREENS.TASK.TITLE]: undefined; - [SCREENS.TASK.DESCRIPTION]: undefined; [SCREENS.TASK.ASSIGNEE]: { reportID: string; }; From 87131a3bb3473a1439b6e8595b34f08fe347f5b8 Mon Sep 17 00:00:00 2001 From: someone-here Date: Sat, 20 Jan 2024 00:03:24 +0530 Subject: [PATCH 147/660] Fix permission --- src/libs/ReportUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 7761ab9cdfed..0f87e6e73f5e 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4453,7 +4453,7 @@ function getRoom(type: ValueOf, policyID: string) * We only want policy owners and admins to be able to modify the report description, but not in thread chat. */ function canEditReportDescription(report: OnyxEntry, policy: OnyxEntry): boolean { - return !isMoneyRequestReport(report) && !isArchivedRoom(report) && isChatRoom(report) && !isChatThread(report) && isEmpty(policy); + return !isMoneyRequestReport(report) && !isArchivedRoom(report) && isChatRoom(report) && !isChatThread(report) && !isEmpty(policy); } /** * Checks if report action has error when smart scanning From d078c1051b3b1cf6d77cbea025f4bbffd757e4d6 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Fri, 19 Jan 2024 19:39:12 +0100 Subject: [PATCH 148/660] Update types PART-1 --- src/components/KYCWall/types.ts | 9 +++------ src/components/SettlementButton.tsx | 5 +++-- src/libs/actions/Policy.ts | 7 +++++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/components/KYCWall/types.ts b/src/components/KYCWall/types.ts index aee5b569cc46..2144dc1a878e 100644 --- a/src/components/KYCWall/types.ts +++ b/src/components/KYCWall/types.ts @@ -5,6 +5,8 @@ import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; import type {Route} from '@src/ROUTES'; import type {Report} from '@src/types/onyx'; +import type AnchorAlignment from '@src/types/utils/AnchorAlignment'; +import type {EmptyObject} from '@src/types/utils/EmptyObject'; type Source = ValueOf; @@ -14,11 +16,6 @@ type DOMRectProperties = 'top' | 'bottom' | 'left' | 'right' | 'height' | 'x' | type DomRect = Pick; -type AnchorAlignment = { - horizontal: ValueOf; - vertical: ValueOf; -}; - type AnchorPosition = { anchorPositionVertical: number; anchorPositionHorizontal: number; @@ -49,7 +46,7 @@ type KYCWallProps = { chatReportID?: string; /** The IOU/Expense report we are paying */ - iouReport?: OnyxEntry; + iouReport?: OnyxEntry | EmptyObject; /** Where the popover should be positioned relative to the anchor points. */ anchorAlignment?: AnchorAlignment; diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton.tsx index 82da026d4014..32dd4ffec7e7 100644 --- a/src/components/SettlementButton.tsx +++ b/src/components/SettlementButton.tsx @@ -12,6 +12,7 @@ import * as PaymentMethods from '@userActions/PaymentMethods'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type {Route} from '@src/ROUTES'; import type {ButtonSizeValue} from '@src/styles/utils/types'; import type {LastPaymentMethod, Report} from '@src/types/onyx'; import type AnchorAlignment from '@src/types/utils/AnchorAlignment'; @@ -72,10 +73,10 @@ type SettlementButtonProps = SettlementButtonOnyxProps & { buttonSize?: ButtonSizeValue; /** Route for the Add Bank Account screen for a given navigation stack */ - addBankAccountRoute?: string; + addBankAccountRoute?: Route; /** Route for the Add Debit Card screen for a given navigation stack */ - addDebitCardRoute?: string; + addDebitCardRoute?: Route; /** Whether the button should be disabled */ isDisabled?: boolean; diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 263d5fb68529..b6e729d3698e 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -22,6 +22,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetailsList, Policy, PolicyMember, PolicyTags, RecentlyUsedCategories, RecentlyUsedTags, ReimbursementAccount, Report, ReportAction, Transaction} from '@src/types/onyx'; import type {Errors} from '@src/types/onyx/OnyxCommon'; import type {CustomUnit} from '@src/types/onyx/Policy'; +import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; type AnnounceRoomMembersOnyxData = { @@ -1589,12 +1590,14 @@ function buildOptimisticPolicyRecentlyUsedTags(policyID: string, tag: string): R * * @returns policyID of the workspace we have created */ -function createWorkspaceFromIOUPayment(iouReport: Report): string | undefined { +function createWorkspaceFromIOUPayment(iouReportParam: Report | EmptyObject): string | undefined { // This flow only works for IOU reports - if (!ReportUtils.isIOUReport(iouReport)) { + if (!ReportUtils.isIOUReport(iouReportParam)) { return; } + const iouReport = iouReportParam as Report; + // Generate new variables for the policy const policyID = generatePolicyID(); const workspaceName = generateDefaultWorkspaceName(sessionEmail); From dd94a01030f0d6ef3050f34dfb2db0b8e67ccb78 Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Sat, 20 Jan 2024 14:12:24 +0100 Subject: [PATCH 149/660] Show the "Pending route..." as merchant for requests with manual amount --- src/components/DistanceEReceipt.js | 4 +--- .../ReportActionItem/MoneyRequestPreview.js | 7 ++++-- .../ReportActionItem/MoneyRequestView.js | 3 +-- .../ReportActionItem/ReportPreview.js | 24 +++++++++---------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index 3f337281c548..c507e713b546 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -75,9 +75,7 @@ function DistanceEReceipt({transaction}) { )} - {!hasPendingRoute && ( - {formattedTransactionAmount} - )} + {!hasPendingRoute && {formattedTransactionAmount}} {transactionMerchant} diff --git a/src/components/ReportActionItem/MoneyRequestPreview.js b/src/components/ReportActionItem/MoneyRequestPreview.js index fd182b1d96ae..5f555e6993d4 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview.js +++ b/src/components/ReportActionItem/MoneyRequestPreview.js @@ -169,7 +169,10 @@ function MoneyRequestPreview(props) { // Show the merchant for IOUs and expenses only if they are custom or not related to scanning smartscan const shouldShowMerchant = - !_.isEmpty(requestMerchant) && requestMerchant !== CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT && requestMerchant !== CONST.TRANSACTION.DEFAULT_MERCHANT && !hasPendingRoute; + !_.isEmpty(requestMerchant) && + requestMerchant !== CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT && + requestMerchant !== CONST.TRANSACTION.DEFAULT_MERCHANT && + !(hasPendingRoute && !requestAmount); const shouldShowDescription = !_.isEmpty(description) && !shouldShowMerchant && !isScanning; let merchantOrDescription = requestMerchant; @@ -227,7 +230,7 @@ function MoneyRequestPreview(props) { return translate('iou.receiptScanning'); } - if (hasPendingRoute) { + if (hasPendingRoute && !requestAmount) { return translate('iou.routePending'); } diff --git a/src/components/ReportActionItem/MoneyRequestView.js b/src/components/ReportActionItem/MoneyRequestView.js index 7703940ea13f..5a84a5e6de21 100644 --- a/src/components/ReportActionItem/MoneyRequestView.js +++ b/src/components/ReportActionItem/MoneyRequestView.js @@ -140,8 +140,7 @@ function MoneyRequestView({report, parentReport, parentReportActions, policyCate } = ReportUtils.getTransactionDetails(transaction); const isEmptyMerchant = transactionMerchant === '' || transactionMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; const isDistanceRequest = TransactionUtils.isDistanceRequest(transaction); - const hasPendingRoute = TransactionUtils.hasPendingRoute(transaction); - const formattedTransactionAmount = transactionAmount && !hasPendingRoute ? CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency) : ''; + const formattedTransactionAmount = transactionAmount ? CurrencyUtils.convertToDisplayString(transactionAmount, transactionCurrency) : ''; const formattedOriginalAmount = transactionOriginalAmount && transactionOriginalCurrency && CurrencyUtils.convertToDisplayString(transactionOriginalAmount, transactionOriginalCurrency); const isCardTransaction = TransactionUtils.isCardTransaction(transaction); const cardProgramName = isCardTransaction ? CardUtils.getCardDescription(transactionCardID) : ''; diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js index 2e5c73b04f54..1f9755c2a3e8 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.js @@ -174,18 +174,16 @@ function ReportPreview(props) { const hasErrors = (hasReceipts && hasMissingSmartscanFields) || (canUseViolations && ReportUtils.hasViolations(props.iouReportID, props.transactionViolations)); const lastThreeTransactionsWithReceipts = transactionsWithReceipts.slice(-3); const lastThreeReceipts = _.map(lastThreeTransactionsWithReceipts, (transaction) => ReceiptUtils.getThumbnailAndImageURIs(transaction)); - let formattedMerchant = numberOfRequests === 1 && hasReceipts && !hasOnlyPendingDistanceRequests ? TransactionUtils.getMerchant(transactionsWithReceipts[0]) : null; + let formattedMerchant = numberOfRequests === 1 && hasReceipts ? TransactionUtils.getMerchant(transactionsWithReceipts[0]) : null; if (TransactionUtils.isPartialMerchant(formattedMerchant)) { formattedMerchant = null; } const previewSubtitle = - numberOfRequests === 1 && hasOnlyPendingDistanceRequests - ? '' - : formattedMerchant || - props.translate('iou.requestCount', { - count: numberOfRequests - numberOfScanningReceipts, - scanningReceipts: numberOfScanningReceipts, - }); + formattedMerchant || + props.translate('iou.requestCount', { + count: numberOfRequests - numberOfScanningReceipts, + scanningReceipts: numberOfScanningReceipts, + }); const shouldShowSubmitButton = isDraftExpenseReport && reimbursableSpend !== 0; @@ -196,15 +194,15 @@ function ReportPreview(props) { ); const getDisplayAmount = () => { - if (hasOnlyPendingDistanceRequests) { - return props.translate('iou.routePending'); - } if (totalDisplaySpend) { return CurrencyUtils.convertToDisplayString(totalDisplaySpend, props.iouReport.currency); } if (isScanning) { return props.translate('iou.receiptScanning'); } + if (hasOnlyPendingDistanceRequests) { + return props.translate('iou.routePending'); + } // If iouReport is not available, get amount from the action message (Ex: "Domain20821's Workspace owes $33.00" or "paid ₫60" or "paid -₫60 elsewhere") let displayAmount = ''; @@ -253,6 +251,8 @@ function ReportPreview(props) { return isCurrentUserManager && !isDraftExpenseReport && !isApproved && !iouSettled; }, [isPaidGroupPolicy, isCurrentUserManager, isDraftExpenseReport, isApproved, iouSettled]); const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton; + const shouldShowSubtitle = + !isScanning && (numberOfRequests > 1 || (hasReceipts && numberOfRequests === 1 && formattedMerchant && !(hasOnlyPendingDistanceRequests && !totalDisplaySpend))); return ( @@ -301,7 +301,7 @@ function ReportPreview(props) { )} - {!isScanning && (numberOfRequests > 1 || (hasReceipts && numberOfRequests === 1 && formattedMerchant)) && ( + {shouldShowSubtitle && ( {previewSubtitle || moneyRequestComment} From 373c670b35dd8d332503c042c267bf9d4dc06c2c Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Sat, 20 Jan 2024 15:53:13 +0100 Subject: [PATCH 150/660] Update hasPendingRoute logic --- src/components/ReportActionItem/MoneyRequestPreview.js | 2 +- src/libs/TransactionUtils.ts | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestPreview.js b/src/components/ReportActionItem/MoneyRequestPreview.js index 97f97877aa10..f8484373cad8 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview.js +++ b/src/components/ReportActionItem/MoneyRequestPreview.js @@ -172,7 +172,7 @@ function MoneyRequestPreview(props) { !_.isEmpty(requestMerchant) && requestMerchant !== CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT && requestMerchant !== CONST.TRANSACTION.DEFAULT_MERCHANT && - !(hasPendingRoute && !requestAmount); + !(isDistanceRequest && hasPendingRoute && !requestAmount); const shouldShowDescription = !_.isEmpty(description) && !shouldShowMerchant && !isScanning; let merchantOrDescription = requestMerchant; diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index f6b60b35bce8..f4b3b464d9d1 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -316,14 +316,10 @@ function getOriginalAmount(transaction: Transaction): number { } /** - * Verify if the transaction is of Distance request and is expecting the distance to be calculated on the server + * Verify if the transaction is expecting the distance to be calculated on the server */ function hasPendingRoute(transaction: OnyxEntry): boolean { - if (!transaction) { - return false; - } - - return isDistanceRequest(transaction) && !!transaction.pendingFields?.waypoints; + return !!transaction?.pendingFields?.waypoints; } /** From 634b5fa8f1b1f1dd7c1122b1ad9e391f2d4174e6 Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Sat, 20 Jan 2024 21:17:59 +0100 Subject: [PATCH 151/660] Update "Route pending" display based on amount --- src/components/DistanceEReceipt.js | 2 +- src/libs/ReportUtils.ts | 2 +- src/libs/TransactionUtils.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/DistanceEReceipt.js b/src/components/DistanceEReceipt.js index c507e713b546..55ac6d8d6979 100644 --- a/src/components/DistanceEReceipt.js +++ b/src/components/DistanceEReceipt.js @@ -75,7 +75,7 @@ function DistanceEReceipt({transaction}) { )} - {!hasPendingRoute && {formattedTransactionAmount}} + {!!transactionAmount && {formattedTransactionAmount}} {transactionMerchant} diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 2a7fd4c74338..e88a62953b3e 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2225,7 +2225,7 @@ function getReportPreviewMessage( return Localize.translateLocal('iou.receiptScanning'); } - if (!isEmptyObject(linkedTransaction) && TransactionUtils.hasPendingRoute(linkedTransaction)) { + if (!isEmptyObject(linkedTransaction) && TransactionUtils.hasPendingRoute(linkedTransaction) && !TransactionUtils.getAmount(linkedTransaction)) { return Localize.translateLocal('iou.routePending'); } } diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index f4b3b464d9d1..103f66d7a683 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -266,8 +266,8 @@ function getDescription(transaction: OnyxEntry): string { /** * Return the amount field from the transaction, return the modifiedAmount if present. */ -function getAmount(transaction: OnyxEntry, isFromExpenseReport: boolean): number { - // IOU requests cannot have negative values but they can be stored as negative values, let's return absolute value +function getAmount(transaction: OnyxEntry, isFromExpenseReport = false): number { + // IOU requests cannot have negative values, but they can be stored as negative values, let's return absolute value if (!isFromExpenseReport) { const amount = transaction?.modifiedAmount ?? 0; if (amount) { From f2a7a06d85a3cfff8e11bbcf8e4b30c205f95ef7 Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Sat, 20 Jan 2024 21:46:18 +0100 Subject: [PATCH 152/660] Revert some changes --- src/libs/ReceiptUtils.ts | 26 ++++++++++++-------------- src/types/onyx/Transaction.ts | 2 -- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/libs/ReceiptUtils.ts b/src/libs/ReceiptUtils.ts index 9e985f0bd60e..bcba68a3a0bd 100644 --- a/src/libs/ReceiptUtils.ts +++ b/src/libs/ReceiptUtils.ts @@ -8,7 +8,6 @@ import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type {Transaction} from '@src/types/onyx'; import * as FileUtils from './fileDownload/FileUtils'; -import * as TransactionUtils from './TransactionUtils'; type ThumbnailAndImageURI = { image: ImageSourcePropType | string; @@ -30,28 +29,27 @@ type FileNameAndExtension = { * @param receiptFileName */ function getThumbnailAndImageURIs(transaction: Transaction, receiptPath: string | null = null, receiptFileName: string | null = null): ThumbnailAndImageURI { - if (TransactionUtils.hasPendingRoute(transaction)) { - return {thumbnail: null, image: ReceiptGeneric, isLocalFile: true}; - } - // URI to image, i.e. blob:new.expensify.com/9ef3a018-4067-47c6-b29f-5f1bd35f213d or expensify.com/receipts/w_e616108497ef940b7210ec6beb5a462d01a878f4.jpg const path = transaction?.receipt?.source ?? receiptPath ?? ''; // filename of uploaded image or last part of remote URI const filename = transaction?.filename ?? receiptFileName ?? ''; const isReceiptImage = Str.isImage(filename); + const hasEReceipt = transaction?.hasEReceipt; - if (hasEReceipt) { - return {thumbnail: null, image: ROUTES.ERECEIPT.getRoute(transaction.transactionID), transaction}; - } + if (!Object.hasOwn(transaction?.pendingFields ?? {}, 'waypoints')) { + if (hasEReceipt) { + return {thumbnail: null, image: ROUTES.ERECEIPT.getRoute(transaction.transactionID), transaction}; + } - // For local files, we won't have a thumbnail yet - if (isReceiptImage && (path.startsWith('blob:') || path.startsWith('file:'))) { - return {thumbnail: null, image: path, isLocalFile: true}; - } + // For local files, we won't have a thumbnail yet + if (isReceiptImage && (path.startsWith('blob:') || path.startsWith('file:'))) { + return {thumbnail: null, image: path, isLocalFile: true}; + } - if (isReceiptImage) { - return {thumbnail: `${path}.1024.jpg`, image: path}; + if (isReceiptImage) { + return {thumbnail: `${path}.1024.jpg`, image: path}; + } } const {fileExtension} = FileUtils.splitExtensionFromFileName(filename) as FileNameAndExtension; diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 4b8a0a1cf9b3..8b7e26280305 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -94,8 +94,6 @@ type Transaction = { /** If the transaction was made in a foreign currency, we send the original amount and currency */ originalAmount?: number; originalCurrency?: string; - - isLoading?: boolean; }; export default Transaction; From d2f5a1d8c92fa2443dc6e9f7ca4ea91b7aa1643f Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 22 Jan 2024 01:42:49 +0700 Subject: [PATCH 153/660] context approach --- src/components/MenuItem.tsx | 14 +++++-- src/components/MenuItemGroup.js | 35 ---------------- src/components/MenuItemGroup.tsx | 30 ++++++++++++++ .../PersonalDetailsInitialPage.js | 40 +++++++++---------- 4 files changed, 61 insertions(+), 58 deletions(-) delete mode 100644 src/components/MenuItemGroup.js create mode 100644 src/components/MenuItemGroup.tsx diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index 334fa9895205..31c07724975a 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -1,7 +1,7 @@ import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import type {ImageContentFit} from 'expo-image'; import type {ForwardedRef, ReactNode} from 'react'; -import React, {forwardRef, useEffect, useMemo, useRef, useState} from 'react'; +import React, {forwardRef, useContext, useEffect, useMemo, useRef, useState} from 'react'; import type {GestureResponderEvent, StyleProp, TextStyle, ViewStyle} from 'react-native'; import {View} from 'react-native'; import type {AnimatedStyle} from 'react-native-reanimated'; @@ -14,6 +14,8 @@ import ControlSelection from '@libs/ControlSelection'; import convertToLTR from '@libs/convertToLTR'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import getButtonState from '@libs/getButtonState'; +// import useWaitForNavigation from '@hooks/useWaitForNavigation'; +import Navigation from '@libs/Navigation/Navigation'; import type {AvatarSource} from '@libs/UserUtils'; import variables from '@styles/variables'; import * as Session from '@userActions/Session'; @@ -29,6 +31,7 @@ import Hoverable from './Hoverable'; import Icon from './Icon'; import * as Expensicons from './Icon/Expensicons'; import * as defaultWorkspaceAvatars from './Icon/WorkspaceDefaultAvatars'; +import {MenuItemGroupContext} from './MenuItemGroup'; import MultipleAvatars from './MultipleAvatars'; import PressableWithSecondaryInteraction from './PressableWithSecondaryInteraction'; import RenderHTML from './RenderHTML'; @@ -295,6 +298,7 @@ function MenuItem( const {isSmallScreenWidth} = useWindowDimensions(); const [html, setHtml] = useState(''); const titleRef = useRef(''); + const {isExecuting, singleExecution, waitForNavigate} = useContext(MenuItemGroupContext) ?? {}; const isDeleted = style && Array.isArray(style) ? style.includes(styles.offlineFeedback.deleted) : false; const descriptionVerticalMargin = shouldShowDescriptionOnTop ? styles.mb1 : styles.mt1; @@ -370,7 +374,11 @@ function MenuItem( } if (onPress && event) { - onPress(event); + if (!singleExecution || !waitForNavigate) { + onPress(event); + return; + } + singleExecution(waitForNavigate(() => onPress(event))); } }; @@ -394,7 +402,7 @@ function MenuItem( shouldGreyOutWhenDisabled && disabled && styles.buttonOpacityDisabled, ] as StyleProp } - disabled={disabled} + disabled={disabled || isExecuting} ref={ref} role={CONST.ROLE.MENUITEM} accessibilityLabel={title ? title.toString() : ''} diff --git a/src/components/MenuItemGroup.js b/src/components/MenuItemGroup.js deleted file mode 100644 index 4fe5adf989b6..000000000000 --- a/src/components/MenuItemGroup.js +++ /dev/null @@ -1,35 +0,0 @@ -import useSingleExecution from '@hooks/useSingleExecution'; -import PropTypes from 'prop-types'; -import React, {Children, cloneElement} from 'react'; - -const propTypes = { - /* Actual content wrapped by this component */ - children: PropTypes.node.isRequired, - - /** Whether or not to use the single execution hook */ - shouldUseSingleExecution: PropTypes.bool, -}; -const defaultProps = { - shouldUseSingleExecution: true -}; - -function MenuItemGroup(props){ - const {isExecuting, singleExecution} = useSingleExecution(); - const arrayChildren = Children.toArray(props.children); - - return <> - {Children.map(arrayChildren, (child, index) => { - return cloneElement(child,{ - ...child.props, - onPress: props.shouldUseSingleExecution ? singleExecution(child.props.onPress) : child.props.onPress, - disabled: child.props.disabled || isExecuting - }) - })} - -} - -MenuItemGroup.displayName = 'MenuItemGroup'; -MenuItemGroup.propTypes = propTypes; -MenuItemGroup.defaultProps = defaultProps; - -export default MenuItemGroup; \ No newline at end of file diff --git a/src/components/MenuItemGroup.tsx b/src/components/MenuItemGroup.tsx new file mode 100644 index 000000000000..995efc962921 --- /dev/null +++ b/src/components/MenuItemGroup.tsx @@ -0,0 +1,30 @@ +import React, {createContext} from 'react'; +import useSingleExecution from '@hooks/useSingleExecution'; +import type {Action} from '@hooks/useSingleExecution'; +import useWaitForNavigation from '@hooks/useWaitForNavigation'; + +type MenuItemGroupContextProps = { + isExecuting: boolean; + singleExecution: (action?: Action | undefined) => (...params: T) => void; + waitForNavigate: ReturnType; +}; + +const MenuItemGroupContext = createContext(undefined); + +interface MenuItemGroupProps { + /* Actual content wrapped by this component */ + children: React.ReactNode; + + /** Whether or not to use the single execution hook */ + shouldUseSingleExecution: boolean; +} + +const MenuItemGroup: React.FC = ({children}) => { + const {isExecuting, singleExecution} = useSingleExecution(); + const waitForNavigate = useWaitForNavigation(); + + return {children}; +}; + +export {MenuItemGroupContext}; +export default MenuItemGroup; diff --git a/src/pages/settings/Profile/PersonalDetails/PersonalDetailsInitialPage.js b/src/pages/settings/Profile/PersonalDetails/PersonalDetailsInitialPage.js index a35ad78836b8..ee633cbaff1d 100644 --- a/src/pages/settings/Profile/PersonalDetails/PersonalDetailsInitialPage.js +++ b/src/pages/settings/Profile/PersonalDetails/PersonalDetailsInitialPage.js @@ -5,6 +5,7 @@ import {ScrollView, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import MenuItemGroup from '@components/MenuItemGroup'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import {withNetwork} from '@components/OnyxProvider'; import ScreenWrapper from '@components/ScreenWrapper'; @@ -17,7 +18,6 @@ import Navigation from '@libs/Navigation/Navigation'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import MenuItemGroup from '@components/MenuItemGroup'; const propTypes = { /* Onyx Props */ @@ -79,25 +79,25 @@ function PersonalDetailsInitialPage(props) { {props.translate('privatePersonalDetails.privateDataMessage')} - Navigation.navigate(ROUTES.SETTINGS_PERSONAL_DETAILS_LEGAL_NAME)} - /> - Navigation.navigate(ROUTES.SETTINGS_PERSONAL_DETAILS_DATE_OF_BIRTH)} - titleStyle={[styles.flex1]} - /> - Navigation.navigate(ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS)} - /> + Navigation.navigate(ROUTES.SETTINGS_PERSONAL_DETAILS_LEGAL_NAME)} + /> + Navigation.navigate(ROUTES.SETTINGS_PERSONAL_DETAILS_DATE_OF_BIRTH)} + titleStyle={[styles.flex1]} + /> + Navigation.navigate(ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS)} + /> From ddffc2531e8c658771d80ff4ea2b6fbc001a1311 Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 22 Jan 2024 01:43:36 +0700 Subject: [PATCH 154/660] remove redundant imports --- src/components/MenuItem.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index 31c07724975a..e701c9e21c00 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -14,8 +14,6 @@ import ControlSelection from '@libs/ControlSelection'; import convertToLTR from '@libs/convertToLTR'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import getButtonState from '@libs/getButtonState'; -// import useWaitForNavigation from '@hooks/useWaitForNavigation'; -import Navigation from '@libs/Navigation/Navigation'; import type {AvatarSource} from '@libs/UserUtils'; import variables from '@styles/variables'; import * as Session from '@userActions/Session'; From 5b440b7ae7db307cdf559f96064c05341051e8dd Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 22 Jan 2024 01:50:23 +0700 Subject: [PATCH 155/660] fix lint --- src/components/MenuItemGroup.tsx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/components/MenuItemGroup.tsx b/src/components/MenuItemGroup.tsx index 995efc962921..e8c7a62bf1a6 100644 --- a/src/components/MenuItemGroup.tsx +++ b/src/components/MenuItemGroup.tsx @@ -1,4 +1,4 @@ -import React, {createContext} from 'react'; +import React, {createContext, useMemo} from 'react'; import useSingleExecution from '@hooks/useSingleExecution'; import type {Action} from '@hooks/useSingleExecution'; import useWaitForNavigation from '@hooks/useWaitForNavigation'; @@ -11,20 +11,25 @@ type MenuItemGroupContextProps = { const MenuItemGroupContext = createContext(undefined); -interface MenuItemGroupProps { +type MenuItemGroupProps = { /* Actual content wrapped by this component */ children: React.ReactNode; /** Whether or not to use the single execution hook */ shouldUseSingleExecution: boolean; -} +}; -const MenuItemGroup: React.FC = ({children}) => { +function MenuItemGroup({children, shouldUseSingleExecution = true}: MenuItemGroupProps) { const {isExecuting, singleExecution} = useSingleExecution(); const waitForNavigate = useWaitForNavigation(); - return {children}; -}; + const value = useMemo( + () => (shouldUseSingleExecution ? {isExecuting, singleExecution, waitForNavigate} : undefined), + [shouldUseSingleExecution, isExecuting, singleExecution, waitForNavigate], + ); + + return {children}; +} export {MenuItemGroupContext}; export default MenuItemGroup; From f96ae97de7e196fae79a26b8cdb205c1bb0f360e Mon Sep 17 00:00:00 2001 From: tienifr Date: Mon, 22 Jan 2024 02:01:12 +0700 Subject: [PATCH 156/660] execute singleExecution --- src/components/MenuItem.tsx | 2 +- src/components/MenuItemGroup.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index e701c9e21c00..f009d3f83939 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -376,7 +376,7 @@ function MenuItem( onPress(event); return; } - singleExecution(waitForNavigate(() => onPress(event))); + singleExecution(waitForNavigate(() => onPress(event)))(); } }; diff --git a/src/components/MenuItemGroup.tsx b/src/components/MenuItemGroup.tsx index e8c7a62bf1a6..8dc8586028d8 100644 --- a/src/components/MenuItemGroup.tsx +++ b/src/components/MenuItemGroup.tsx @@ -16,7 +16,7 @@ type MenuItemGroupProps = { children: React.ReactNode; /** Whether or not to use the single execution hook */ - shouldUseSingleExecution: boolean; + shouldUseSingleExecution?: boolean; }; function MenuItemGroup({children, shouldUseSingleExecution = true}: MenuItemGroupProps) { From ef7ee749731703376bf2c046e62081ca06a9ebeb Mon Sep 17 00:00:00 2001 From: Fitsum Abebe Date: Sun, 21 Jan 2024 22:49:12 +0300 Subject: [PATCH 157/660] add comment --- src/pages/signin/LoginForm/BaseLoginForm.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/signin/LoginForm/BaseLoginForm.js b/src/pages/signin/LoginForm/BaseLoginForm.js index 5a6c8aed5050..c0f8abee710a 100644 --- a/src/pages/signin/LoginForm/BaseLoginForm.js +++ b/src/pages/signin/LoginForm/BaseLoginForm.js @@ -277,6 +277,8 @@ function LoginForm(props) { id="username" name="username" onBlur={() => + // This delay is to avoid the validate being called before google iframe is rendered to + // avoid error message appearing after pressing google signin button. setTimeout(() => { if (firstBlurred.current || !Visibility.isVisible() || !Visibility.hasFocus()) { return; From 98ec065bba2d026e96fb8242cc607279b42905c9 Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Sun, 21 Jan 2024 21:47:33 +0100 Subject: [PATCH 158/660] Update IOU Report total on waypoints change --- src/libs/actions/IOU.js | 85 +++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 49 deletions(-) diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 1a9030c89cc4..e5e45dd5de32 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -954,7 +954,7 @@ function getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, t const iouReport = allReports[`${ONYXKEYS.COLLECTION.REPORT}${transactionThread.parentReportID}`]; const isFromExpenseReport = ReportUtils.isExpenseReport(iouReport); const isScanning = TransactionUtils.hasReceipt(transaction) && TransactionUtils.isReceiptBeingScanned(transaction); - const updatedTransaction = TransactionUtils.getUpdatedTransaction(transaction, transactionChanges, isFromExpenseReport); + let updatedTransaction = TransactionUtils.getUpdatedTransaction(transaction, transactionChanges, isFromExpenseReport); const transactionDetails = ReportUtils.getTransactionDetails(updatedTransaction); // This needs to be a JSON string since we're sending this to the MapBox API @@ -968,13 +968,22 @@ function getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, t transactionID, }; + const hasPendingWaypoints = _.has(transactionChanges, 'waypoints'); + if (hasPendingWaypoints) { + updatedTransaction = { + ...updatedTransaction, + amount: CONST.IOU.DEFAULT_AMOUNT, + modifiedAmount: CONST.IOU.DEFAULT_AMOUNT, + modifiedMerchant: Localize.translateLocal('iou.routePending'), + }; + } + // Step 3: Build the modified expense report actions // We don't create a modified report action if we're updating the waypoints, // since there isn't actually any optimistic data we can create for them and the report action is created on the server // with the response from the MapBox API - const hasPendingWaypoints = _.has(transactionChanges, 'waypoints'); + const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(transactionThread, transaction, transactionChanges, isFromExpenseReport); if (!hasPendingWaypoints) { - const updatedReportAction = ReportUtils.buildOptimisticModifiedExpenseReportAction(transactionThread, transaction, transactionChanges, isFromExpenseReport); params.reportActionID = updatedReportAction.reportActionID; optimisticData.push({ @@ -1001,31 +1010,31 @@ function getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, t }, }, }); + } - // Step 4: Compute the IOU total and update the report preview message (and report header) so LHN amount owed is correct. - // Should only update if the transaction matches the currency of the report, else we wait for the update - // from the server with the currency conversion - let updatedMoneyRequestReport = {...iouReport}; - if (updatedTransaction.currency === iouReport.currency && updatedTransaction.modifiedAmount) { - const diff = TransactionUtils.getAmount(transaction, true) - TransactionUtils.getAmount(updatedTransaction, true); - if (ReportUtils.isExpenseReport(iouReport)) { - updatedMoneyRequestReport.total += diff; - } else { - updatedMoneyRequestReport = IOUUtils.updateIOUOwnerAndTotal(iouReport, updatedReportAction.actorAccountID, diff, TransactionUtils.getCurrency(transaction), false); - } - - updatedMoneyRequestReport.cachedTotal = CurrencyUtils.convertToDisplayString(updatedMoneyRequestReport.total, updatedTransaction.currency); - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, - value: updatedMoneyRequestReport, - }); - successData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, - value: {pendingAction: null}, - }); + // Step 4: Compute the IOU total and update the report preview message (and report header) so LHN amount owed is correct. + // Should only update if the transaction matches the currency of the report, else we wait for the update + // from the server with the currency conversion + let updatedMoneyRequestReport = {...iouReport}; + if (updatedTransaction.currency === iouReport.currency && (updatedTransaction.modifiedAmount || hasPendingWaypoints)) { + const diff = TransactionUtils.getAmount(transaction, true) - TransactionUtils.getAmount(updatedTransaction, true); + if (ReportUtils.isExpenseReport(iouReport)) { + updatedMoneyRequestReport.total += diff; + } else { + updatedMoneyRequestReport = IOUUtils.updateIOUOwnerAndTotal(iouReport, updatedReportAction.actorAccountID, diff, TransactionUtils.getCurrency(transaction), false); } + + updatedMoneyRequestReport.cachedTotal = CurrencyUtils.convertToDisplayString(updatedMoneyRequestReport.total, updatedTransaction.currency); + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, + value: updatedMoneyRequestReport, + }); + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, + value: {pendingAction: null}, + }); } // Optimistically modify the transaction @@ -1100,29 +1109,6 @@ function getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, t }); if (hasPendingWaypoints) { - // When updating waypoints, we need to explicitly set the transaction's amount and IOU report's total to 0. - // These values must be calculated on the server because they depend on the distance. - optimisticData.push( - ...[ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`, - value: { - total: CONST.IOU.DEFAULT_AMOUNT, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, - value: { - amount: CONST.IOU.DEFAULT_AMOUNT, - modifiedAmount: CONST.IOU.DEFAULT_AMOUNT, - modifiedMerchant: Localize.translateLocal('iou.routePending'), - }, - }, - ], - ); - // Delete the draft transaction when editing waypoints when the server responds successfully and there are no errors successData.push({ onyxMethod: Onyx.METHOD.SET, @@ -1138,6 +1124,7 @@ function getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, t value: { amount: transaction.amount, modifiedAmount: transaction.modifiedAmount, + modifiedMerchant: transaction.modifiedMerchant, }, }); } From 2d7f408065cf2135a9ef7aecccca55dd0d65c69f Mon Sep 17 00:00:00 2001 From: Yauheni Date: Sun, 21 Jan 2024 21:52:18 +0100 Subject: [PATCH 159/660] Update types PART-2 --- src/components/MoneyReportHeader.tsx | 2 -- src/libs/ReportUtils.ts | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index afdc62218f95..88e368a8bbbb 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -127,7 +127,6 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt {shouldShowSettlementButton && !isSmallScreenWidth && ( ): boolean { * If the report is a policy expense, the route should be for adding bank account for that policy * else since the report is a personal IOU, the route should be for personal bank account. */ -function getBankAccountRoute(report: OnyxEntry): string { +function getBankAccountRoute(report: OnyxEntry): Route { return isPolicyExpenseChat(report) ? ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute('', report?.policyID) : ROUTES.SETTINGS_ADD_BANK_ACCOUNT; } From 44e688c476bae6216f883c85cd1362566edb1e0a Mon Sep 17 00:00:00 2001 From: Yauheni Date: Mon, 22 Jan 2024 10:12:48 +0100 Subject: [PATCH 160/660] Update types PART-3 --- src/components/KYCWall/BaseKYCWall.tsx | 2 +- src/components/KYCWall/types.ts | 4 ++-- src/components/SettlementButton.tsx | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/KYCWall/BaseKYCWall.tsx b/src/components/KYCWall/BaseKYCWall.tsx index 04c8397bc33b..17e00af3b2ce 100644 --- a/src/components/KYCWall/BaseKYCWall.tsx +++ b/src/components/KYCWall/BaseKYCWall.tsx @@ -203,7 +203,7 @@ function KYCWall({ Log.info('[KYC Wallet] User has valid payment method and passed KYC checks or did not need them'); - onSuccessfulKYC(currentSource, iouPaymentType); + onSuccessfulKYC(iouPaymentType, currentSource); }, [ bankAccountList, diff --git a/src/components/KYCWall/types.ts b/src/components/KYCWall/types.ts index 2144dc1a878e..7147f86814fa 100644 --- a/src/components/KYCWall/types.ts +++ b/src/components/KYCWall/types.ts @@ -10,7 +10,7 @@ import type {EmptyObject} from '@src/types/utils/EmptyObject'; type Source = ValueOf; -type TransferMethod = ValueOf; +type TransferMethod = ValueOf; type DOMRectProperties = 'top' | 'bottom' | 'left' | 'right' | 'height' | 'x' | 'y'; @@ -61,7 +61,7 @@ type KYCWallProps = { shouldShowPersonalBankAccountOption?: boolean; /** Callback for the end of the onContinue trigger on option selection */ - onSuccessfulKYC: (currentSource?: Source, iouPaymentType?: TransferMethod) => void; + onSuccessfulKYC: (iouPaymentType?: TransferMethod, currentSource?: Source) => void; /** Children to build the KYC */ children: (continueAction: (event: SyntheticEvent, method: TransferMethod) => void, anchorRef: ForwardedRef) => void; diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton.tsx index 32dd4ffec7e7..82b9805620cc 100644 --- a/src/components/SettlementButton.tsx +++ b/src/components/SettlementButton.tsx @@ -26,7 +26,7 @@ type KYCFlowEvent = GestureResponderEvent | KeyboardEvent | undefined; type TriggerKYCFlow = (event: KYCFlowEvent, iouPaymentType: string) => void; -type PaymentType = DeepValueOf; +type PaymentType = DeepValueOf; type EnablePaymentsRoute = typeof ROUTES.ENABLE_PAYMENTS | typeof ROUTES.IOU_SEND_ENABLE_PAYMENTS | typeof ROUTES.SETTINGS_ENABLE_PAYMENTS; @@ -37,7 +37,7 @@ type SettlementButtonOnyxProps = { type SettlementButtonProps = SettlementButtonOnyxProps & { /** Callback to execute when this button is pressed. Receives a single payment type argument. */ - onPress: (paymentType: PaymentType) => void; + onPress: (paymentType?: PaymentType) => void; /** The route to redirect if user does not have a payment method setup */ enablePaymentsRoute: EnablePaymentsRoute; From 3fadd8aaff92edff3dbd8ce6194d12e55997bd89 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Mon, 22 Jan 2024 10:50:11 +0100 Subject: [PATCH 161/660] Update types PART-4 --- src/components/KYCWall/BaseKYCWall.tsx | 6 +++--- src/components/KYCWall/types.ts | 10 ++++++---- src/components/SettlementButton.tsx | 10 +++++----- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/components/KYCWall/BaseKYCWall.tsx b/src/components/KYCWall/BaseKYCWall.tsx index 17e00af3b2ce..939b87e22376 100644 --- a/src/components/KYCWall/BaseKYCWall.tsx +++ b/src/components/KYCWall/BaseKYCWall.tsx @@ -1,7 +1,7 @@ import React, {useCallback, useEffect, useRef, useState} from 'react'; import type {SyntheticEvent} from 'react'; import {Dimensions} from 'react-native'; -import type {EmitterSubscription, NativeTouchEvent} from 'react-native'; +import type {EmitterSubscription, GestureResponderEvent, NativeTouchEvent} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; import AddPaymentMethodMenu from '@components/AddPaymentMethodMenu'; @@ -146,7 +146,7 @@ function KYCWall({ * */ const continueAction = useCallback( - (event?: SyntheticEvent, iouPaymentType?: TransferMethod) => { + (event?: GestureResponderEvent | KeyboardEvent | SyntheticEvent, iouPaymentType?: TransferMethod) => { const currentSource = walletTerms?.source ?? source; /** @@ -161,7 +161,7 @@ function KYCWall({ } // Use event target as fallback if anchorRef is null for safety - const targetElement = anchorRef.current ?? (event?.nativeEvent.target as HTMLDivElement); + const targetElement = anchorRef.current ?? ((event as SyntheticEvent)?.nativeEvent.target as HTMLDivElement); transferBalanceButtonRef.current = targetElement; diff --git a/src/components/KYCWall/types.ts b/src/components/KYCWall/types.ts index 7147f86814fa..c0b738746985 100644 --- a/src/components/KYCWall/types.ts +++ b/src/components/KYCWall/types.ts @@ -1,16 +1,17 @@ -import type {ForwardedRef, SyntheticEvent} from 'react'; -import type {NativeTouchEvent} from 'react-native'; +import type {ForwardedRef} from 'react'; +import type {GestureResponderEvent} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; import type {Route} from '@src/ROUTES'; import type {Report} from '@src/types/onyx'; import type AnchorAlignment from '@src/types/utils/AnchorAlignment'; +import type DeepValueOf from '@src/types/utils/DeepValueOf'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; type Source = ValueOf; -type TransferMethod = ValueOf; +type TransferMethod = DeepValueOf; type DOMRectProperties = 'top' | 'bottom' | 'left' | 'right' | 'height' | 'x' | 'y'; @@ -64,7 +65,8 @@ type KYCWallProps = { onSuccessfulKYC: (iouPaymentType?: TransferMethod, currentSource?: Source) => void; /** Children to build the KYC */ - children: (continueAction: (event: SyntheticEvent, method: TransferMethod) => void, anchorRef: ForwardedRef) => void; + children: (continueAction: (event?: GestureResponderEvent | KeyboardEvent, method?: TransferMethod) => void, anchorRef: ForwardedRef) => void; + // }; export type {AnchorPosition, KYCWallProps, PaymentMethod, TransferMethod, DomRect}; diff --git a/src/components/SettlementButton.tsx b/src/components/SettlementButton.tsx index 82b9805620cc..8124509ec4f3 100644 --- a/src/components/SettlementButton.tsx +++ b/src/components/SettlementButton.tsx @@ -1,4 +1,4 @@ -import type {RefObject} from 'react'; +import type {ForwardedRef, RefObject} from 'react'; import React, {useEffect, useMemo} from 'react'; import type {GestureResponderEvent, StyleProp, View, ViewStyle} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; @@ -24,10 +24,10 @@ import KYCWall from './KYCWall'; type KYCFlowEvent = GestureResponderEvent | KeyboardEvent | undefined; -type TriggerKYCFlow = (event: KYCFlowEvent, iouPaymentType: string) => void; - type PaymentType = DeepValueOf; +type TriggerKYCFlow = (event: KYCFlowEvent, iouPaymentType: PaymentType) => void; + type EnablePaymentsRoute = typeof ROUTES.ENABLE_PAYMENTS | typeof ROUTES.IOU_SEND_ENABLE_PAYMENTS | typeof ROUTES.SETTINGS_ENABLE_PAYMENTS; type SettlementButtonOnyxProps = { @@ -213,9 +213,9 @@ function SettlementButton({ anchorAlignment={kycWallAnchorAlignment} shouldShowPersonalBankAccountOption={shouldShowPersonalBankAccountOption} > - {(triggerKYCFlow: TriggerKYCFlow, buttonRef: RefObject) => ( + {(triggerKYCFlow: TriggerKYCFlow, buttonRef: ForwardedRef) => ( } isDisabled={isDisabled} isLoading={isLoading} onPress={(event: KYCFlowEvent, iouPaymentType: PaymentType) => selectPaymentType(event, iouPaymentType, triggerKYCFlow)} From 907ece0712d4ae652cd6452fea6a10b9193a5778 Mon Sep 17 00:00:00 2001 From: Yauheni Date: Mon, 22 Jan 2024 10:51:35 +0100 Subject: [PATCH 162/660] Update types PART-5 --- src/components/KYCWall/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/KYCWall/types.ts b/src/components/KYCWall/types.ts index c0b738746985..ebf9cd3b0a43 100644 --- a/src/components/KYCWall/types.ts +++ b/src/components/KYCWall/types.ts @@ -65,7 +65,7 @@ type KYCWallProps = { onSuccessfulKYC: (iouPaymentType?: TransferMethod, currentSource?: Source) => void; /** Children to build the KYC */ - children: (continueAction: (event?: GestureResponderEvent | KeyboardEvent, method?: TransferMethod) => void, anchorRef: ForwardedRef) => void; + children: (continueAction: (event?: GestureResponderEvent | KeyboardEvent, method?: TransferMethod) => void, anchorRef: ForwardedRef) => void; // }; From 9cb7f8a41b67877a69fd1a1310962538cdfdeb1f Mon Sep 17 00:00:00 2001 From: Yauheni Date: Mon, 22 Jan 2024 10:52:13 +0100 Subject: [PATCH 163/660] Update types PART-6 --- src/components/KYCWall/types.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/KYCWall/types.ts b/src/components/KYCWall/types.ts index ebf9cd3b0a43..d0e5c2e32e2f 100644 --- a/src/components/KYCWall/types.ts +++ b/src/components/KYCWall/types.ts @@ -66,7 +66,6 @@ type KYCWallProps = { /** Children to build the KYC */ children: (continueAction: (event?: GestureResponderEvent | KeyboardEvent, method?: TransferMethod) => void, anchorRef: ForwardedRef) => void; - // }; export type {AnchorPosition, KYCWallProps, PaymentMethod, TransferMethod, DomRect}; From 0b5eccdaf9c92fcbba6734c2da34893049e239aa Mon Sep 17 00:00:00 2001 From: VickyStash Date: Mon, 22 Jan 2024 11:12:43 +0100 Subject: [PATCH 164/660] Minor code improvements --- src/ONYXKEYS.ts | 1 - src/libs/actions/IOU.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 7fd7af0393d8..e1f356bd383f 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -460,7 +460,6 @@ type OnyxValues = { [ONYXKEYS.COLLECTION.TRANSACTION]: OnyxTypes.Transaction; [ONYXKEYS.COLLECTION.TRANSACTION_DRAFT]: OnyxTypes.Transaction; [ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT]: OnyxTypes.Transaction; - [ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS]: OnyxTypes.TransactionViolations; [ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS]: OnyxTypes.RecentlyUsedTags; [ONYXKEYS.COLLECTION.SELECTED_TAB]: string; [ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS]: OnyxTypes.TransactionViolations; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 6c5b64cbcc14..9eb1b628fa21 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3203,7 +3203,7 @@ function getPayMoneyRequestParams(chatReport: OnyxTypes.Report, iouReport: OnyxT }, ]; - if (currentNextStep !== null) { + if (currentNextStep) { optimisticData.push({ onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${iouReport.reportID}`, From d7fdf8010bea5f2aa1222b79365d70d57552c29e Mon Sep 17 00:00:00 2001 From: Shubham Agrawal Date: Mon, 22 Jan 2024 16:59:38 +0530 Subject: [PATCH 165/660] added full screen loader for better UX --- .../step/IOURequestStepConfirmation.js | 80 +++++++++++-------- 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.js b/src/pages/iou/request/step/IOURequestStepConfirmation.js index 9df2564ae38d..84adf87bbae2 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.js +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.js @@ -5,6 +5,7 @@ import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import categoryPropTypes from '@components/categoryPropTypes'; +import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; import MoneyRequestConfirmationList from '@components/MoneyTemporaryForRefactorRequestConfirmationList'; @@ -103,6 +104,15 @@ function IOURequestStepConfirmation({ ); const isPolicyExpenseChat = useMemo(() => ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(report)), [report]); + useEffect(() => { + if (!transaction || !transaction.originalCurrency) { + return; + } + // If user somehow lands on this page without the currency reset, then reset it here. + IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, transaction.originalCurrency, true); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useEffect(() => { const policyExpenseChat = _.find(participants, (participant) => participant.isPolicyExpenseChat); if (policyExpenseChat) { @@ -318,6 +328,8 @@ function IOURequestStepConfirmation({ IOU.setMoneyRequestBillable_temporaryForRefactor(transactionID, billable); }; + const isLoading = !!(transaction && transaction.originalCurrency); + return ( - + {isLoading ? ( + + ) : ( + + )} )} From c7925f5820f36f06abd46bd3461f8cac70774355 Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 23 Jan 2024 02:05:19 +0700 Subject: [PATCH 166/660] fix lint --- src/pages/iou/request/step/IOURequestStepConfirmation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.js b/src/pages/iou/request/step/IOURequestStepConfirmation.js index 0f535cb60e14..029836d0c654 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.js +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.js @@ -125,7 +125,7 @@ function IOURequestStepConfirmation({ return; } IOU.setMoneyRequestCategory_temporaryForRefactor(transactionID, defaultCategory); - }, [transactionID, defaultCategory]); + }, [transactionID, transaction.category, defaultCategory]); const navigateBack = useCallback(() => { // If there is not a report attached to the IOU with a reportID, then the participants were manually selected and the user needs taken From 127b8a7a3fefa712dedd13ed2ab04987d9683567 Mon Sep 17 00:00:00 2001 From: rayane-djouah <77965000+rayane-djouah@users.noreply.github.com> Date: Mon, 22 Jan 2024 22:16:11 +0100 Subject: [PATCH 167/660] add a warning comment --- .github/workflows/preDeploy.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/preDeploy.yml b/.github/workflows/preDeploy.yml index 8f9512062e9d..f09865de0194 100644 --- a/.github/workflows/preDeploy.yml +++ b/.github/workflows/preDeploy.yml @@ -1,3 +1,4 @@ +# Reminder: If this workflow's name changes, update the name in the dependent workflow at .github/workflows/failureNotifier.yml. name: Process new code merged to main on: From 9dde0a3e9a42374c4cfd661ba5f12633db2b8415 Mon Sep 17 00:00:00 2001 From: rayane-djouah <77965000+rayane-djouah@users.noreply.github.com> Date: Tue, 23 Jan 2024 10:26:31 +0100 Subject: [PATCH 168/660] Update issueTitle Co-authored-by: Joel Davies --- .github/workflows/failureNotifier.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/failureNotifier.yml b/.github/workflows/failureNotifier.yml index 667fe3a737b2..2377cd71d4d8 100644 --- a/.github/workflows/failureNotifier.yml +++ b/.github/workflows/failureNotifier.yml @@ -58,7 +58,7 @@ jobs: }); const existingIssue = issues.data.find(issue => issue.title.includes(jobName)); if (!existingIssue) { - const issueTitle = `🔍 Investigation Needed: ${ jobName } Failure due to PR Merge 🔍`; + const issueTitle = `Investigate workflow job failing on main: ${ jobName }`; const issueBody = `🚨 **Failure Summary** 🚨:\n\n` + `- **📋 Job Name**: [${ jobName }](${ jobLink })\n` + `- **🔧 Failure in Workflow**: Main branch\n` + From 92449cabf9dbe28c0f9bbe9184972f6128cc2aaf Mon Sep 17 00:00:00 2001 From: rayane-djouah <77965000+rayane-djouah@users.noreply.github.com> Date: Tue, 23 Jan 2024 10:27:04 +0100 Subject: [PATCH 169/660] spacing Co-authored-by: Joel Davies --- .github/workflows/failureNotifier.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/failureNotifier.yml b/.github/workflows/failureNotifier.yml index 2377cd71d4d8..96718a16c29f 100644 --- a/.github/workflows/failureNotifier.yml +++ b/.github/workflows/failureNotifier.yml @@ -31,7 +31,7 @@ jobs: uses: actions/github-script@v7 with: script: | - const jobs = ${{steps.fetch-workflow-jobs.outputs.result}}; + const jobs = ${{ steps.fetch-workflow-jobs.outputs.result }}; const headCommit = "${{ github.event.workflow_run.head_commit.id }}"; const prData = await github.rest.repos.listPullRequestsAssociatedWithCommit({ From 6aef1a7916c445a500df43ac891f5f8e2cbf4eee Mon Sep 17 00:00:00 2001 From: rayane-djouah <77965000+rayane-djouah@users.noreply.github.com> Date: Tue, 23 Jan 2024 10:28:31 +0100 Subject: [PATCH 170/660] Update issueBody --- .github/workflows/failureNotifier.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/failureNotifier.yml b/.github/workflows/failureNotifier.yml index 96718a16c29f..476f27737d6f 100644 --- a/.github/workflows/failureNotifier.yml +++ b/.github/workflows/failureNotifier.yml @@ -61,7 +61,7 @@ jobs: const issueTitle = `Investigate workflow job failing on main: ${ jobName }`; const issueBody = `🚨 **Failure Summary** 🚨:\n\n` + `- **📋 Job Name**: [${ jobName }](${ jobLink })\n` + - `- **🔧 Failure in Workflow**: Main branch\n` + + `- **🔧 Failure in Workflow**: Process new code merged to main\n` + `- **🔗 Triggered by PR**: [PR Link](${ prLink })\n` + `- **👤 PR Author**: @${ prAuthor }\n` + `- **🤝 Merged by**: @${ prMerger }\n\n` + From 7a620e8d84b4133a0557b954a040a9876224a038 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 23 Jan 2024 10:48:11 +0100 Subject: [PATCH 171/660] patch fix --- .../@react-native-community+cli-platform-android+12.3.0.patch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/patches/@react-native-community+cli-platform-android+12.3.0.patch b/patches/@react-native-community+cli-platform-android+12.3.0.patch index 7289f652232a..d94baf0f9c6e 100644 --- a/patches/@react-native-community+cli-platform-android+12.3.0.patch +++ b/patches/@react-native-community+cli-platform-android+12.3.0.patch @@ -1,5 +1,5 @@ diff --git a/node_modules/@react-native-community/cli-platform-android/native_modules.gradle b/node_modules/@react-native-community/cli-platform-android/native_modules.gradle -index bbfa7f7..00d14dd 100644 +index bbfa7f7..ed53872 100644 --- a/node_modules/@react-native-community/cli-platform-android/native_modules.gradle +++ b/node_modules/@react-native-community/cli-platform-android/native_modules.gradle @@ -140,6 +140,7 @@ class ReactNativeModules { @@ -45,7 +45,7 @@ index bbfa7f7..00d14dd 100644 +if(this.hasProperty('reactNativeProject')){ + autoModules = new ReactNativeModules(logger, projectRoot, new File(projectRoot, reactNativeProject)) +} else { -+ autoModules = new ReactNativeModules(logger, projectRoot) ++ autoModules = new ReactNativeModules(logger, projectRoot, projectRoot) +} def reactNativeVersionRequireNewArchEnabled(autoModules) { From 74eb19989385ead4d84568062c89b54134e643a1 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 23 Jan 2024 11:35:38 +0100 Subject: [PATCH 172/660] Fix TS issues in the new part of code --- src/libs/actions/IOU.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 00bcc44c1ef0..2f283609fa96 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -2037,7 +2037,7 @@ function startSplitBill( }); }); - _.each(participants, (participant) => { + participants.forEach((participant) => { const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(participant); if (!isPolicyExpenseChat) { return; @@ -2046,7 +2046,7 @@ function startSplitBill( const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories(participant.policyID, category); const optimisticPolicyRecentlyUsedTags = Policy.buildOptimisticPolicyRecentlyUsedTags(participant.policyID, tag); - if (!_.isEmpty(optimisticPolicyRecentlyUsedCategories)) { + if (optimisticPolicyRecentlyUsedCategories.length > 0) { optimisticData.push({ onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${participant.policyID}`, @@ -2054,7 +2054,7 @@ function startSplitBill( }); } - if (!_.isEmpty(optimisticPolicyRecentlyUsedTags)) { + if (!isEmptyObject(optimisticPolicyRecentlyUsedTags)) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${participant.policyID}`, @@ -3499,7 +3499,7 @@ function submitReport(expenseReport: OnyxTypes.Report) { const parameters: SubmitReportParams = { reportID: expenseReport.reportID, - managerAccountID: policy.submitsTo || expenseReport.managerID, + managerAccountID: policy.submitsTo ?? expenseReport.managerID, reportActionID: optimisticSubmittedReportAction.reportActionID, }; From c6ed187f899e58c632c89ce60633bd9d5f27ed17 Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Tue, 23 Jan 2024 12:37:34 +0100 Subject: [PATCH 173/660] Use TransactionUtils.hasPendingRoute to check pending route for receipt --- src/libs/ReceiptUtils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/ReceiptUtils.ts b/src/libs/ReceiptUtils.ts index 7fca9f54b744..9e985f0bd60e 100644 --- a/src/libs/ReceiptUtils.ts +++ b/src/libs/ReceiptUtils.ts @@ -8,6 +8,7 @@ import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type {Transaction} from '@src/types/onyx'; import * as FileUtils from './fileDownload/FileUtils'; +import * as TransactionUtils from './TransactionUtils'; type ThumbnailAndImageURI = { image: ImageSourcePropType | string; @@ -29,7 +30,7 @@ type FileNameAndExtension = { * @param receiptFileName */ function getThumbnailAndImageURIs(transaction: Transaction, receiptPath: string | null = null, receiptFileName: string | null = null): ThumbnailAndImageURI { - if (Object.hasOwn(transaction?.pendingFields ?? {}, 'waypoints')) { + if (TransactionUtils.hasPendingRoute(transaction)) { return {thumbnail: null, image: ReceiptGeneric, isLocalFile: true}; } From 0068fbf6a58ac1a4193dee45e6e814237c0612b9 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Tue, 23 Jan 2024 15:06:37 +0100 Subject: [PATCH 174/660] add required changes --- android/app/src/main/res/values/colors.xml | 2 +- src/App.js | 6 +++--- src/libs/Navigation/AppNavigator/index.tsx | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml index 8d5bfbf4eed7..94065c5b9d19 100644 --- a/android/app/src/main/res/values/colors.xml +++ b/android/app/src/main/res/values/colors.xml @@ -1,5 +1,5 @@ - + #03D47C #FFFFFF #03D47C diff --git a/src/App.js b/src/App.js index 87de6a2e160a..4ccb49a6c6cf 100644 --- a/src/App.js +++ b/src/App.js @@ -38,7 +38,7 @@ const propTypes = { }; const defaultProps = { - url: null, + url: undefined, }; // For easier debugging and development, when we are in web we expose Onyx to the window, so you can more easily set data into Onyx @@ -56,11 +56,11 @@ LogBox.ignoreLogs([ const fill = {flex: 1}; -function App(props) { +function App({url}) { useDefaultDragAndDrop(); OnyxUpdateManager(); return ( - + { - if (!NativeModules.HybridAppModule || initUrl === undefined) { + if (!NativeModules.HybridAppModule || !initUrl) { return; } From c3fc9052143d349d30891adc307c1211de40903f Mon Sep 17 00:00:00 2001 From: rayane-djouah <77965000+rayane-djouah@users.noreply.github.com> Date: Tue, 23 Jan 2024 15:16:09 +0100 Subject: [PATCH 175/660] Update issue label Co-authored-by: Joel Davies --- .github/workflows/failureNotifier.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/failureNotifier.yml b/.github/workflows/failureNotifier.yml index 476f27737d6f..c8901d499982 100644 --- a/.github/workflows/failureNotifier.yml +++ b/.github/workflows/failureNotifier.yml @@ -77,7 +77,7 @@ jobs: repo: context.repo.repo, title: issueTitle, body: issueBody, - labels: [failureLabel, 'daily'], + labels: [failureLabel, 'Daily'], assignees: [prMerger, prAuthor] }); } From 2aa59017fb9e43831aaf8b2f91ba2c20c2ece9a1 Mon Sep 17 00:00:00 2001 From: rayane-djouah <77965000+rayane-djouah@users.noreply.github.com> Date: Tue, 23 Jan 2024 20:28:20 +0100 Subject: [PATCH 176/660] update workflow failure label name --- .github/workflows/failureNotifier.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/failureNotifier.yml b/.github/workflows/failureNotifier.yml index c8901d499982..79de3d3f5c7d 100644 --- a/.github/workflows/failureNotifier.yml +++ b/.github/workflows/failureNotifier.yml @@ -45,7 +45,7 @@ jobs: const prAuthor = pr.user.login; const prMerger = "${{ github.event.workflow_run.actor.login }}"; - const failureLabel = 'workflow-failure'; + const failureLabel = 'Workflow Failure'; for (let i = 0; i < jobs.total_count; i++) { if (jobs.jobs[i].conclusion == 'failure') { const jobName = jobs.jobs[i].name; From 3ad76b18a649ae5d13373b71b125cd2048725020 Mon Sep 17 00:00:00 2001 From: rayane-djouah <77965000+rayane-djouah@users.noreply.github.com> Date: Tue, 23 Jan 2024 20:33:31 +0100 Subject: [PATCH 177/660] add error message to the issue body --- .github/workflows/failureNotifier.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/failureNotifier.yml b/.github/workflows/failureNotifier.yml index 79de3d3f5c7d..604770eff4d7 100644 --- a/.github/workflows/failureNotifier.yml +++ b/.github/workflows/failureNotifier.yml @@ -58,13 +58,24 @@ jobs: }); const existingIssue = issues.data.find(issue => issue.title.includes(jobName)); if (!existingIssue) { + const annotations = await github.rest.checks.listAnnotations({ + owner: context.repo.owner, + repo: context.repo.repo, + check_run_id: jobs.jobs[i].id, + }); + let errorMessage = ""; + for(let j = 0; j < annotations.data.length; j++) { + errorMessage += annotations.data[j].annotation_level + ": "; + errorMessage += annotations.data[j].message + "\n"; + } const issueTitle = `Investigate workflow job failing on main: ${ jobName }`; const issueBody = `🚨 **Failure Summary** 🚨:\n\n` + `- **📋 Job Name**: [${ jobName }](${ jobLink })\n` + `- **🔧 Failure in Workflow**: Process new code merged to main\n` + `- **🔗 Triggered by PR**: [PR Link](${ prLink })\n` + `- **👤 PR Author**: @${ prAuthor }\n` + - `- **🤝 Merged by**: @${ prMerger }\n\n` + + `- **🤝 Merged by**: @${ prMerger }\n` + + `- **🐛 Error Message**: \n ${errorMessage}\n\n` + `⚠️ **Action Required** ⚠️:\n\n` + `🛠️ A recent merge appears to have caused a failure in the job named [${ jobName }](${ jobLink }).\n` + `This issue has been automatically created and labeled with \`${ failureLabel }\` for investigation. \n\n` + From c1ccade52293b555868c34f7c23350a0448f208e Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 24 Jan 2024 00:52:37 +0100 Subject: [PATCH 178/660] add olddot-wireframe to Expensicons --- assets/images/olddot-wireframe.svg | 3422 ++++++++++++++++++++++++++++ src/components/Icon/Expensicons.ts | 4 +- 2 files changed, 3424 insertions(+), 2 deletions(-) create mode 100644 assets/images/olddot-wireframe.svg diff --git a/assets/images/olddot-wireframe.svg b/assets/images/olddot-wireframe.svg new file mode 100644 index 000000000000..ee9aa93be255 --- /dev/null +++ b/assets/images/olddot-wireframe.svg @@ -0,0 +1,3422 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts index 364fb03a2055..33bfa1855e74 100644 --- a/src/components/Icon/Expensicons.ts +++ b/src/components/Icon/Expensicons.ts @@ -93,6 +93,7 @@ import NewWindow from '@assets/images/new-window.svg'; import NewWorkspace from '@assets/images/new-workspace.svg'; import OfflineCloud from '@assets/images/offline-cloud.svg'; import Offline from '@assets/images/offline.svg'; +import OldDotWireframe from '@assets/images/olddot-wireframe.svg'; import Paperclip from '@assets/images/paperclip.svg'; import Paycheck from '@assets/images/paycheck.svg'; import Pencil from '@assets/images/pencil.svg'; @@ -106,7 +107,6 @@ import QuestionMark from '@assets/images/question-mark-circle.svg'; import ReceiptSearch from '@assets/images/receipt-search.svg'; import Receipt from '@assets/images/receipt.svg'; import Rotate from '@assets/images/rotate-image.svg'; -import RotateLeft from '@assets/images/rotate-left.svg'; import Scan from '@assets/images/scan.svg'; import Send from '@assets/images/send.svg'; import Shield from '@assets/images/shield.svg'; @@ -230,6 +230,7 @@ export { NewWorkspace, Offline, OfflineCloud, + OldDotWireframe, Paperclip, Paycheck, Pencil, @@ -243,7 +244,6 @@ export { Receipt, ReceiptSearch, Rotate, - RotateLeft, Scan, Send, Shield, From 8c802edccf7f9221e06508086db22e94ba806afd Mon Sep 17 00:00:00 2001 From: dukenv0307 Date: Wed, 24 Jan 2024 11:32:22 +0700 Subject: [PATCH 179/660] refactor task preview for hidden case --- .../HTMLRenderers/MentionUserRenderer.js | 2 +- src/components/ReportActionItem/TaskPreview.tsx | 10 +--------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js index 11ffabe4fe6a..f31fd304d47f 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.js @@ -41,7 +41,7 @@ function MentionUserRenderer(props) { if (!_.isEmpty(htmlAttribAccountID)) { const user = lodashGet(personalDetails, htmlAttribAccountID); accountID = parseInt(htmlAttribAccountID, 10); - displayNameOrLogin = LocalePhoneNumber.formatPhoneNumber(lodashGet(user, 'login', '')) || lodashGet(user, 'displayName', '') || translate('common.hidden'); + displayNameOrLogin = lodashGet(user, 'displayName', '') || LocalePhoneNumber.formatPhoneNumber(lodashGet(user, 'login', '')) || translate('common.hidden'); navigationRoute = ROUTES.PROFILE.getRoute(htmlAttribAccountID); } else if (!_.isEmpty(props.tnode.data)) { // We need to remove the LTR unicode and leading @ from data as it is not part of the login diff --git a/src/components/ReportActionItem/TaskPreview.tsx b/src/components/ReportActionItem/TaskPreview.tsx index cbd166d79d3a..bbf2525198be 100644 --- a/src/components/ReportActionItem/TaskPreview.tsx +++ b/src/components/ReportActionItem/TaskPreview.tsx @@ -8,7 +8,6 @@ import type {OnyxEntry} from 'react-native-onyx'; import Checkbox from '@components/Checkbox'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; -import {usePersonalDetails} from '@components/OnyxProvider'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import RenderHTML from '@components/RenderHTML'; import {showContextMenuForReport} from '@components/ShowContextMenuContext'; @@ -20,7 +19,6 @@ import useThemeStyles from '@hooks/useThemeStyles'; import ControlSelection from '@libs/ControlSelection'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import getButtonState from '@libs/getButtonState'; -import * as LocalePhoneNumber from '@libs/LocalePhoneNumber'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; import * as TaskUtils from '@libs/TaskUtils'; @@ -84,7 +82,6 @@ function TaskPreview({ }: TaskPreviewProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); - const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; const {translate} = useLocalize(); // The reportAction might not contain details regarding the taskReport @@ -95,13 +92,8 @@ function TaskPreview({ : action?.childStateNum === CONST.REPORT.STATE_NUM.APPROVED && action?.childStatusNum === CONST.REPORT.STATUS_NUM.APPROVED; const taskTitle = Str.htmlEncode(TaskUtils.getTaskTitle(taskReportID, action?.childReportName ?? '')); const taskAssigneeAccountID = Task.getTaskAssigneeAccountID(taskReport) ?? action?.childManagerAccountID ?? ''; - const assigneeLogin = personalDetails[taskAssigneeAccountID]?.login ?? ''; - const assigneeDisplayName = personalDetails[taskAssigneeAccountID]?.displayName ?? ''; - const taskAssignee = assigneeDisplayName || LocalePhoneNumber.formatPhoneNumber(assigneeLogin); const htmlForTaskPreview = - taskAssignee && taskAssigneeAccountID !== 0 - ? `@${taskAssignee} ${taskTitle}` - : `${taskTitle}`; + taskAssigneeAccountID !== 0 ? ` ${taskTitle}` : `${taskTitle}`; const isDeletedParentAction = ReportUtils.isCanceledTaskReport(taskReport, action); if (isDeletedParentAction) { From 752e63fa7e9852eb8b71a63d907eff8a573d31e4 Mon Sep 17 00:00:00 2001 From: staszekscp Date: Wed, 24 Jan 2024 09:04:26 +0100 Subject: [PATCH 180/660] rename patches --- ...reanimated+3.5.4.patch => react-native-reanimated+3.6.1.patch} | 0 ...amera+2.16.2.patch => react-native-vision-camera+2.16.5.patch} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename patches/{react-native-reanimated+3.5.4.patch => react-native-reanimated+3.6.1.patch} (100%) rename patches/{react-native-vision-camera+2.16.2.patch => react-native-vision-camera+2.16.5.patch} (100%) diff --git a/patches/react-native-reanimated+3.5.4.patch b/patches/react-native-reanimated+3.6.1.patch similarity index 100% rename from patches/react-native-reanimated+3.5.4.patch rename to patches/react-native-reanimated+3.6.1.patch diff --git a/patches/react-native-vision-camera+2.16.2.patch b/patches/react-native-vision-camera+2.16.5.patch similarity index 100% rename from patches/react-native-vision-camera+2.16.2.patch rename to patches/react-native-vision-camera+2.16.5.patch From 61d6fcf64a5e6eb0a645fcfd4fa16a8b6388ddba Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Wed, 24 Jan 2024 12:08:27 +0100 Subject: [PATCH 181/660] feat: add offline handling for money reports with various currencies --- src/components/MoneyReportHeader.tsx | 3 +++ src/components/ReportActionItem/MoneyReportView.tsx | 3 ++- src/libs/ReportUtils.ts | 13 +++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index afdc62218f95..d072dea34356 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -55,6 +55,7 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt const reimbursableTotal = ReportUtils.getMoneyRequestReimbursableTotal(moneyRequestReport); const isApproved = ReportUtils.isReportApproved(moneyRequestReport); const isSettled = ReportUtils.isSettled(moneyRequestReport.reportID); + const canAllowSettlement = ReportUtils.hasUpdatedTotal(moneyRequestReport); const policyType = policy?.type; const isPolicyAdmin = policyType !== CONST.POLICY.TYPE.PERSONAL && policy?.role === CONST.POLICY.ROLE.ADMIN; const isPaidGroupPolicy = ReportUtils.isPaidGroupPolicy(moneyRequestReport); @@ -140,6 +141,7 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt shouldShowApproveButton={shouldShowApproveButton} style={[styles.pv2]} formattedAmount={formattedAmount} + isDisabled={!canAllowSettlement} /> )} @@ -171,6 +173,7 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt shouldHidePaymentOptions={!shouldShowPayButton} shouldShowApproveButton={shouldShowApproveButton} formattedAmount={formattedAmount} + isDisabled={!canAllowSettlement} /> )} diff --git a/src/components/ReportActionItem/MoneyReportView.tsx b/src/components/ReportActionItem/MoneyReportView.tsx index 3d1710de1432..ecc5ef0df831 100644 --- a/src/components/ReportActionItem/MoneyReportView.tsx +++ b/src/components/ReportActionItem/MoneyReportView.tsx @@ -40,6 +40,7 @@ function MoneyReportView({report, policyReportFields, shouldShowHorizontalRule}: const {isSmallScreenWidth} = useWindowDimensions(); const {canUseReportFields} = usePermissions(); const isSettled = ReportUtils.isSettled(report.reportID); + const isTotalUpdated = ReportUtils.hasUpdatedTotal(report); const {totalDisplaySpend, nonReimbursableSpend, reimbursableSpend} = ReportUtils.getMoneyRequestSpendBreakdown(report); @@ -110,7 +111,7 @@ function MoneyReportView({report, policyReportFields, shouldShowHorizontalRule}: )} {formattedTotalAmount} diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 91fa7b35e824..10b7ce8965dd 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4540,6 +4540,18 @@ function shouldDisplayThreadReplies(reportAction: OnyxEntry, repor return hasReplies && !!reportAction?.childCommenterCount && !isThreadFirstChat(reportAction, reportID); } +/** + * Check if money report has any transactions updated optimistically + */ +function hasUpdatedTotal(report: Report): boolean { + const transactions = TransactionUtils.getAllReportTransactions(report.reportID); + + const hasPendingTransaction = transactions.some((transaction) => !!transaction.pendingAction); + const hasTransactionWithDifferentCurrency = transactions.some((transaction) => transaction.currency !== report.currency); + + return !(hasPendingTransaction && hasTransactionWithDifferentCurrency); +} + /** * Disable reply in thread action if: * @@ -4748,6 +4760,7 @@ export { doesReportBelongToWorkspace, getChildReportNotificationPreference, isReportFieldOfTypeTitle, + hasUpdatedTotal, }; export type { From 6a8161a267ab1228f8735b0ef4dd557d9a5365ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Wed, 24 Jan 2024 14:30:14 +0100 Subject: [PATCH 182/660] delete the old component --- src/pages/SearchPage.js | 223 ---------------------------------------- 1 file changed, 223 deletions(-) delete mode 100755 src/pages/SearchPage.js diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js deleted file mode 100755 index c420371f5a65..000000000000 --- a/src/pages/SearchPage.js +++ /dev/null @@ -1,223 +0,0 @@ -import PropTypes from 'prop-types'; -import React, {useCallback, useEffect, useRef, useState} from 'react'; -import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; -import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import OptionsSelector from '@components/OptionsSelector'; -import ScreenWrapper from '@components/ScreenWrapper'; -import useLocalize from '@hooks/useLocalize'; -import useNetwork from '@hooks/useNetwork'; -import useThemeStyles from '@hooks/useThemeStyles'; -import Navigation from '@libs/Navigation/Navigation'; -import * as OptionsListUtils from '@libs/OptionsListUtils'; -import Performance from '@libs/Performance'; -import * as ReportUtils from '@libs/ReportUtils'; -import * as Report from '@userActions/Report'; -import Timing from '@userActions/Timing'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import personalDetailsPropType from './personalDetailsPropType'; -import reportPropTypes from './reportPropTypes'; - -const propTypes = { - /* Onyx Props */ - - /** Beta features list */ - betas: PropTypes.arrayOf(PropTypes.string), - - /** All of the personal details for everyone */ - personalDetails: PropTypes.objectOf(personalDetailsPropType), - - /** All reports shared with the user */ - reports: PropTypes.objectOf(reportPropTypes), - - /** Whether we are searching for reports in the server */ - isSearchingForReports: PropTypes.bool, - - /** - * The navigation prop passed by the navigator. - * - * This is required because transitionEnd event doesn't trigger in the automated testing environment. - */ - navigation: PropTypes.shape({}), -}; - -const defaultProps = { - betas: [], - personalDetails: {}, - reports: {}, - isSearchingForReports: false, - navigation: {}, -}; - -function SearchPage({betas, personalDetails, reports, isSearchingForReports, navigation}) { - const [searchValue, setSearchValue] = useState(''); - const [searchOptions, setSearchOptions] = useState({ - recentReports: {}, - personalDetails: {}, - userToInvite: {}, - }); - - const {isOffline} = useNetwork(); - const {translate} = useLocalize(); - const themeStyles = useThemeStyles(); - const isMounted = useRef(false); - - const updateOptions = useCallback(() => { - const { - recentReports: localRecentReports, - personalDetails: localPersonalDetails, - userToInvite: localUserToInvite, - } = OptionsListUtils.getSearchOptions(reports, personalDetails, searchValue.trim(), betas); - - setSearchOptions({ - recentReports: localRecentReports, - personalDetails: localPersonalDetails, - userToInvite: localUserToInvite, - }); - }, [reports, personalDetails, searchValue, betas]); - - useEffect(() => { - Timing.start(CONST.TIMING.SEARCH_RENDER); - Performance.markStart(CONST.TIMING.SEARCH_RENDER); - }, []); - - useEffect(() => { - updateOptions(); - }, [reports, personalDetails, betas, updateOptions]); - - useEffect(() => { - if (!isMounted.current) { - isMounted.current = true; - return; - } - - updateOptions(); - // Ignoring the rule intentionally, we want to run the code only when search Value changes to prevent additional runs. - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [searchValue]); - - /** - * Returns the sections needed for the OptionsSelector - * - * @returns {Array} - */ - const getSections = () => { - const sections = []; - let indexOffset = 0; - - if (searchOptions.recentReports.length > 0) { - sections.push({ - data: searchOptions.recentReports, - shouldShow: true, - indexOffset, - }); - indexOffset += searchOptions.recentReports.length; - } - - if (searchOptions.personalDetails.length > 0) { - sections.push({ - data: searchOptions.personalDetails, - shouldShow: true, - indexOffset, - }); - indexOffset += searchOptions.recentReports.length; - } - - if (searchOptions.userToInvite) { - sections.push({ - data: [searchOptions.userToInvite], - shouldShow: true, - indexOffset, - }); - } - - return sections; - }; - - const searchRendered = () => { - Timing.end(CONST.TIMING.SEARCH_RENDER); - Performance.markEnd(CONST.TIMING.SEARCH_RENDER); - }; - - const onChangeText = (value = '') => { - Report.searchInServer(searchValue); - setSearchValue(value); - }; - - /** - * Reset the search value and redirect to the selected report - * - * @param {Object} option - */ - const selectReport = (option) => { - if (!option) { - return; - } - if (option.reportID) { - Navigation.dismissModal(option.reportID); - } else { - Report.navigateToAndOpenReport([option.login]); - } - }; - - const isOptionsDataReady = ReportUtils.isReportDataReady() && OptionsListUtils.isPersonalDetailsReady(personalDetails); - const headerMessage = OptionsListUtils.getHeaderMessage( - searchOptions.recentReports.length + searchOptions.personalDetails.length !== 0, - Boolean(searchOptions.userToInvite), - searchValue, - ); - - return ( - - {({didScreenTransitionEnd, safeAreaPaddingBottomStyle}) => ( - <> - - - - - - )} - - ); -} - -SearchPage.propTypes = propTypes; -SearchPage.defaultProps = defaultProps; -SearchPage.displayName = 'SearchPage'; -export default withOnyx({ - reports: { - key: ONYXKEYS.COLLECTION.REPORT, - }, - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, - betas: { - key: ONYXKEYS.BETAS, - }, - isSearchingForReports: { - key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, - initWithStoredValues: false, - }, -})(SearchPage); From c82dbc35ed0caac73b195339112bff99f65bf102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20M=C3=B3rawski?= Date: Wed, 24 Jan 2024 14:30:51 +0100 Subject: [PATCH 183/660] search debounce and loading indicator added --- .../SelectionList/BaseSelectionList.tsx | 2 ++ src/components/SelectionList/types.ts | 3 +++ src/pages/SearchPage/index.js | 16 ++++++++++------ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index d97c47c84ee7..2c929d2f4f9c 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -58,6 +58,7 @@ function BaseSelectionList( shouldShowTooltips = true, shouldUseDynamicMaxToRenderPerBatch = false, rightHandSideComponent, + isLoadingNewOptions = false, }: BaseSelectionListProps, inputRef: ForwardedRef, ) { @@ -422,6 +423,7 @@ function BaseSelectionList( spellCheck={false} onSubmitEditing={selectFocusedOption} blurOnSubmit={!!flattenedSections.allOptions.length} + isLoading={isLoadingNewOptions} /> )} diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index a82ddef6febb..222c818dd66d 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -230,6 +230,9 @@ type BaseSelectionListProps = Partial ReactElement) | ReactElement | null; + + /** Whether to show the loading indicator for new options */ + isLoadingNewOptions?: boolean; }; type ItemLayout = { diff --git a/src/pages/SearchPage/index.js b/src/pages/SearchPage/index.js index 211f3622e06c..8a06d54a1f45 100644 --- a/src/pages/SearchPage/index.js +++ b/src/pages/SearchPage/index.js @@ -29,11 +29,15 @@ const propTypes = { /** All reports shared with the user */ reports: PropTypes.objectOf(reportPropTypes), + + /** Whether or not we are searching for reports on the server */ + isSearchingForReports: PropTypes.bool, }; const defaultProps = { betas: [], reports: {}, + isSearchingForReports: false, }; const setPerformanceTimersEnd = () => { @@ -43,7 +47,7 @@ const setPerformanceTimersEnd = () => { const SearchPageFooterInstance = ; -function SearchPage({betas, reports}) { +function SearchPage({betas, reports, isSearchingForReports}) { const [isScreenTransitionEnd, setIsScreenTransitionEnd] = useState(false); const {translate} = useLocalize(); const {isOffline} = useNetwork(); @@ -59,10 +63,9 @@ function SearchPage({betas, reports}) { Performance.markStart(CONST.TIMING.SEARCH_RENDER); }, []); - const onChangeText = (text = '') => { - Report.searchInServer(text); - setSearchValue(text); - }; + useEffect(() => { + Report.searchInServer(debouncedSearchValue.trim()); + }, [debouncedSearchValue]); const { recentReports, @@ -150,13 +153,14 @@ function SearchPage({betas, reports}) { textInputValue={searchValue} textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')} textInputHint={offlineMessage} - onChangeText={onChangeText} + onChangeText={setSearchValue} headerMessage={headerMessage} onLayout={setPerformanceTimersEnd} autoFocus onSelectRow={selectReport} showLoadingPlaceholder={!didScreenTransitionEnd || !isOptionsDataReady} footerContent={SearchPageFooterInstance} + isLoadingNewOptions={isSearchingForReports} /> From aa34234694a467d0fbc637052a31960d1f107973 Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Wed, 24 Jan 2024 16:05:50 +0100 Subject: [PATCH 184/660] fix: change the way settlement button on ReportPreview is handled --- src/components/ReportActionItem/ReportPreview.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js index 204c9b5e31d4..f6d8ba34a8bc 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.js @@ -166,6 +166,8 @@ function ReportPreview(props) { const isDraftExpenseReport = isPolicyExpenseChat && ReportUtils.isDraftExpenseReport(props.iouReport); const isApproved = ReportUtils.isReportApproved(props.iouReport); + const canAllowSettlement = ReportUtils.hasUpdatedTotal(props.iouReport); + console.log('%%%%%\n', 'canAllowSettlement: ', canAllowSettlement); const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(props.iouReport); const transactionsWithReceipts = ReportUtils.getTransactionsWithReceipts(props.iouReportID); const numberOfScanningReceipts = _.filter(transactionsWithReceipts, (transaction) => TransactionUtils.isReceiptBeingScanned(transaction)).length; @@ -333,6 +335,7 @@ function ReportPreview(props) { horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM, }} + isDisabled={!canAllowSettlement} /> )} {shouldShowSubmitButton && ( From 870502329845d9d031137c4968a0609a1037d14b Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Wed, 24 Jan 2024 16:13:20 +0100 Subject: [PATCH 185/660] fix: remove console logs --- src/components/ReportActionItem/ReportPreview.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/ReportActionItem/ReportPreview.js b/src/components/ReportActionItem/ReportPreview.js index f6d8ba34a8bc..52847bf0366b 100644 --- a/src/components/ReportActionItem/ReportPreview.js +++ b/src/components/ReportActionItem/ReportPreview.js @@ -167,7 +167,6 @@ function ReportPreview(props) { const isApproved = ReportUtils.isReportApproved(props.iouReport); const canAllowSettlement = ReportUtils.hasUpdatedTotal(props.iouReport); - console.log('%%%%%\n', 'canAllowSettlement: ', canAllowSettlement); const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(props.iouReport); const transactionsWithReceipts = ReportUtils.getTransactionsWithReceipts(props.iouReportID); const numberOfScanningReceipts = _.filter(transactionsWithReceipts, (transaction) => TransactionUtils.isReceiptBeingScanned(transaction)).length; From 4f29dce373fae4b93ab512dbb698e00855dde428 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 24 Jan 2024 16:56:03 +0100 Subject: [PATCH 186/660] add ManageTeamsExpensesModal --- src/components/ManageTeamsExpensesModal.tsx | 114 ++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 src/components/ManageTeamsExpensesModal.tsx diff --git a/src/components/ManageTeamsExpensesModal.tsx b/src/components/ManageTeamsExpensesModal.tsx new file mode 100644 index 000000000000..81dd25511060 --- /dev/null +++ b/src/components/ManageTeamsExpensesModal.tsx @@ -0,0 +1,114 @@ +import {useNavigation} from '@react-navigation/native'; +import React, {useCallback, useMemo, useState} from 'react'; +import {ScrollView, View} from 'react-native'; +import Button from '@components/Button'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import useWindowDimensions from '@hooks/useWindowDimensions'; +import * as DeviceCapabilities from '@libs/DeviceCapabilities'; +import * as Report from '@userActions/Report'; +import CONST from '@src/CONST'; +import HeaderWithBackButton from './HeaderWithBackButton'; +import * as Expensicons from './Icon/Expensicons'; +import type {MenuItemProps} from './MenuItem'; +import MenuItemList from './MenuItemList'; +import Modal from './Modal'; +import Text from './Text'; + +const TEAMS_EXPENSE_CHOICE = { + MULTI_LEVEL: 'Multi level approval', + CUSTOM_EXPENSE: 'Custom expense coding', + CARD_TRACKING: 'Company Card Tracking', + ACCOUNTING: 'Accounting integrations', + RULE: 'Rule enforcement', +}; + +const menuIcons = { + [TEAMS_EXPENSE_CHOICE.MULTI_LEVEL]: Expensicons.Task, + [TEAMS_EXPENSE_CHOICE.CUSTOM_EXPENSE]: Expensicons.ReceiptSearch, + [TEAMS_EXPENSE_CHOICE.CARD_TRACKING]: Expensicons.CreditCard, + [TEAMS_EXPENSE_CHOICE.ACCOUNTING]: Expensicons.Sync, + [TEAMS_EXPENSE_CHOICE.RULE]: Expensicons.Gear, +}; + +function ManageTeamsExpensesModal() { + const styles = useThemeStyles(); + const {isSmallScreenWidth, isExtraSmallScreenHeight} = useWindowDimensions(); + const canUseTouchScreen = DeviceCapabilities.canUseTouchScreen(); + const [isModalOpen, setIsModalOpen] = useState(true); + const theme = useTheme(); + + const closeModal = useCallback(() => { + Report.dismissEngagementModal(); + setIsModalOpen(false); + }, []); + + const menuItems: MenuItemProps[] = useMemo( + () => + Object.values(TEAMS_EXPENSE_CHOICE).map((choice) => { + const translationKey = `${choice}` as const; + return { + key: translationKey, + title: translationKey, + icon: menuIcons[choice], + numberOfLinesTitle: 2, + interactive: false, + }; + }), + [], + ); + + return ( + + + + + + + + Do you require any of the following features + + + + + + + +