diff --git a/.eslintrc.js b/.eslintrc.js index 0661183101ab..33be8cb62fcd 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -51,6 +51,10 @@ const restrictedImportPaths = [ name: '@styles/theme/illustrations', message: 'Do not import theme illustrations directly. Please use the `useThemeIllustrations` hook instead.', }, + { + name: 'date-fns/locale', + message: "Do not import 'date-fns/locale' directly. Please use the submodule import instead, like 'date-fns/locale/en-GB'.", + }, ]; const restrictedImportPatterns = [ diff --git a/android/app/build.gradle b/android/app/build.gradle index c919f731795e..b6d3f43cff98 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -98,8 +98,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001045302 - versionName "1.4.53-2" + versionCode 1001045400 + versionName "1.4.54-0" } flavorDimensions "default" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 2e4c69affd9b..14047e7a7f40 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.4.53 + 1.4.54 CFBundleSignature ???? CFBundleURLTypes @@ -40,7 +40,7 @@ CFBundleVersion - 1.4.53.2 + 1.4.54.0 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index b6aab5371ba4..a1e74152930b 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.4.53 + 1.4.54 CFBundleSignature ???? CFBundleVersion - 1.4.53.2 + 1.4.54.0 diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 6b0ad08aad65..33651bb71380 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -11,9 +11,9 @@ CFBundleName $(PRODUCT_NAME) CFBundleShortVersionString - 1.4.53 + 1.4.54 CFBundleVersion - 1.4.53.2 + 1.4.54.0 NSExtension NSExtensionPointIdentifier diff --git a/package-lock.json b/package-lock.json index a84a72dd5167..15297a6df18c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.53-2", + "version": "1.4.54-0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.53-2", + "version": "1.4.54-0", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -56,7 +56,6 @@ "expo-av": "~13.10.4", "expo-image": "1.10.1", "expo-image-manipulator": "11.8.0", - "fbjs": "^3.0.2", "htmlparser2": "^7.2.0", "idb-keyval": "^6.2.1", "jest-expo": "50.0.1", @@ -65,7 +64,6 @@ "lottie-react-native": "6.4.1", "mapbox-gl": "^2.15.0", "onfido-sdk-ui": "14.15.0", - "patch-package": "^8.0.0", "process": "^0.11.10", "prop-types": "^15.7.2", "pusher-js": "8.3.0", @@ -89,7 +87,6 @@ "react-native-gesture-handler": "2.14.1", "react-native-google-places-autocomplete": "2.5.6", "react-native-haptic-feedback": "^2.2.0", - "react-native-image-pan-zoom": "^2.1.12", "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#8393b7e58df6ff65fd41f60aee8ece8822c91e2b", "react-native-key-command": "^1.0.6", @@ -127,7 +124,6 @@ "react-web-config": "^1.0.0", "react-webcam": "^7.1.1", "react-window": "^1.8.9", - "save": "^2.4.0", "semver": "^7.5.2", "shim-keyboard-event-key": "^1.0.3", "underscore": "^1.13.1" @@ -225,6 +221,7 @@ "jest-transformer-svg": "^2.0.1", "memfs": "^4.6.0", "onchange": "^7.1.0", + "patch-package": "^8.0.0", "portfinder": "^1.0.28", "prettier": "^2.8.8", "pusher-js-mock": "^0.3.3", @@ -19786,6 +19783,9 @@ }, "node_modules/@yarnpkg/lockfile": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true, "license": "BSD-2-Clause" }, "node_modules/7zip-bin": { @@ -20721,6 +20721,9 @@ }, "node_modules/async": { "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "dev": true, "license": "MIT" }, "node_modules/async-each": { @@ -25442,6 +25445,9 @@ }, "node_modules/duplexer": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true, "license": "MIT" }, "node_modules/duplexify": { @@ -27058,19 +27064,6 @@ "node": ">= 0.6" } }, - "node_modules/event-stream": { - "version": "4.0.1", - "license": "MIT", - "dependencies": { - "duplexer": "^0.1.1", - "from": "^0.1.7", - "map-stream": "0.0.7", - "pause-stream": "^0.0.11", - "split": "^1.0.1", - "stream-combiner": "^0.2.2", - "through": "^2.3.8" - } - }, "node_modules/event-target-shim": { "version": "5.0.1", "license": "MIT", @@ -28551,10 +28544,6 @@ "node": ">= 0.6" } }, - "node_modules/from": { - "version": "0.1.7", - "license": "MIT" - }, "node_modules/from2": { "version": "2.3.0", "dev": true, @@ -34135,6 +34124,9 @@ }, "node_modules/klaw-sync": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", + "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.1.11" @@ -34413,10 +34405,6 @@ "version": "4.17.21", "license": "MIT" }, - "node_modules/lodash.assign": { - "version": "4.2.0", - "license": "MIT" - }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "dev": true, @@ -34855,10 +34843,6 @@ "dev": true, "license": "MIT" }, - "node_modules/map-stream": { - "version": "0.0.7", - "license": "MIT" - }, "node_modules/map-visit": { "version": "1.0.0", "devOptional": true, @@ -35996,10 +35980,6 @@ "node": ">=4" } }, - "node_modules/mingo": { - "version": "1.3.3", - "license": "MIT" - }, "node_modules/minimalistic-assert": { "version": "1.0.1", "license": "ISC" @@ -37409,6 +37389,9 @@ }, "node_modules/patch-package": { "version": "8.0.0", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz", + "integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==", + "dev": true, "license": "MIT", "dependencies": { "@yarnpkg/lockfile": "^1.1.0", @@ -37437,6 +37420,9 @@ }, "node_modules/patch-package/node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -37450,6 +37436,9 @@ }, "node_modules/patch-package/node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -37464,6 +37453,9 @@ }, "node_modules/patch-package/node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -37474,10 +37466,16 @@ }, "node_modules/patch-package/node_modules/color-name": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, "license": "MIT" }, "node_modules/patch-package/node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -37485,6 +37483,9 @@ }, "node_modules/patch-package/node_modules/open": { "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dev": true, "license": "MIT", "dependencies": { "is-docker": "^2.0.0", @@ -37499,6 +37500,9 @@ }, "node_modules/patch-package/node_modules/rimraf": { "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, "license": "ISC", "dependencies": { "glob": "^7.1.3" @@ -37509,6 +37513,9 @@ }, "node_modules/patch-package/node_modules/slash": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -37516,6 +37523,9 @@ }, "node_modules/patch-package/node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -37526,6 +37536,9 @@ }, "node_modules/patch-package/node_modules/tmp": { "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, "license": "MIT", "dependencies": { "os-tmpdir": "~1.0.2" @@ -37622,16 +37635,6 @@ "node": ">=8" } }, - "node_modules/pause-stream": { - "version": "0.0.11", - "license": [ - "MIT", - "Apache2" - ], - "dependencies": { - "through": "~2.3" - } - }, "node_modules/pbf": { "version": "3.2.1", "license": "BSD-3-Clause", @@ -39089,14 +39092,6 @@ "react-native": ">=0.60.0" } }, - "node_modules/react-native-image-pan-zoom": { - "version": "2.1.12", - "license": "ISC", - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, "node_modules/react-native-image-picker": { "version": "7.0.3", "license": "MIT", @@ -41261,16 +41256,6 @@ "truncate-utf8-bytes": "^1.0.0" } }, - "node_modules/save": { - "version": "2.5.0", - "license": "ISC", - "dependencies": { - "async": "^3.2.2", - "event-stream": "^4.0.1", - "lodash.assign": "^4.2.0", - "mingo": "1" - } - }, "node_modules/sax": { "version": "1.2.4", "license": "ISC" @@ -42461,14 +42446,6 @@ "node": ">= 0.10.0" } }, - "node_modules/stream-combiner": { - "version": "0.2.2", - "license": "MIT", - "dependencies": { - "duplexer": "~0.1.1", - "through": "~2.3.4" - } - }, "node_modules/stream-each": { "version": "1.2.3", "dev": true, diff --git a/package.json b/package.json index e0f357fd0f8a..096c3584913a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.53-2", + "version": "1.4.54-0", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", @@ -107,7 +107,6 @@ "expo-av": "~13.10.4", "expo-image": "1.10.1", "expo-image-manipulator": "11.8.0", - "fbjs": "^3.0.2", "htmlparser2": "^7.2.0", "idb-keyval": "^6.2.1", "jest-expo": "50.0.1", @@ -116,7 +115,6 @@ "lottie-react-native": "6.4.1", "mapbox-gl": "^2.15.0", "onfido-sdk-ui": "14.15.0", - "patch-package": "^8.0.0", "process": "^0.11.10", "prop-types": "^15.7.2", "pusher-js": "8.3.0", @@ -140,7 +138,6 @@ "react-native-gesture-handler": "2.14.1", "react-native-google-places-autocomplete": "2.5.6", "react-native-haptic-feedback": "^2.2.0", - "react-native-image-pan-zoom": "^2.1.12", "react-native-image-picker": "^7.0.3", "react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#8393b7e58df6ff65fd41f60aee8ece8822c91e2b", "react-native-key-command": "^1.0.6", @@ -178,7 +175,6 @@ "react-web-config": "^1.0.0", "react-webcam": "^7.1.1", "react-window": "^1.8.9", - "save": "^2.4.0", "semver": "^7.5.2", "shim-keyboard-event-key": "^1.0.3", "underscore": "^1.13.1" @@ -276,6 +272,7 @@ "jest-transformer-svg": "^2.0.1", "memfs": "^4.6.0", "onchange": "^7.1.0", + "patch-package": "^8.0.0", "portfinder": "^1.0.28", "prettier": "^2.8.8", "pusher-js-mock": "^0.3.3", diff --git a/patches/react-native-web+0.19.9+005+image-header-support.patch b/patches/react-native-web+0.19.9+005+image-header-support.patch new file mode 100644 index 000000000000..4652e22662f0 --- /dev/null +++ b/patches/react-native-web+0.19.9+005+image-header-support.patch @@ -0,0 +1,200 @@ +diff --git a/node_modules/react-native-web/dist/exports/Image/index.js b/node_modules/react-native-web/dist/exports/Image/index.js +index 95355d5..19109fc 100644 +--- a/node_modules/react-native-web/dist/exports/Image/index.js ++++ b/node_modules/react-native-web/dist/exports/Image/index.js +@@ -135,7 +135,22 @@ function resolveAssetUri(source) { + } + return uri; + } +-var Image = /*#__PURE__*/React.forwardRef((props, ref) => { ++function raiseOnErrorEvent(uri, _ref) { ++ var onError = _ref.onError, ++ onLoadEnd = _ref.onLoadEnd; ++ if (onError) { ++ onError({ ++ nativeEvent: { ++ error: "Failed to load resource " + uri + " (404)" ++ } ++ }); ++ } ++ if (onLoadEnd) onLoadEnd(); ++} ++function hasSourceDiff(a, b) { ++ return a.uri !== b.uri || JSON.stringify(a.headers) !== JSON.stringify(b.headers); ++} ++var BaseImage = /*#__PURE__*/React.forwardRef((props, ref) => { + var ariaLabel = props['aria-label'], + blurRadius = props.blurRadius, + defaultSource = props.defaultSource, +@@ -236,16 +251,10 @@ var Image = /*#__PURE__*/React.forwardRef((props, ref) => { + } + }, function error() { + updateState(ERRORED); +- if (onError) { +- onError({ +- nativeEvent: { +- error: "Failed to load resource " + uri + " (404)" +- } +- }); +- } +- if (onLoadEnd) { +- onLoadEnd(); +- } ++ raiseOnErrorEvent(uri, { ++ onError, ++ onLoadEnd ++ }); + }); + } + function abortPendingRequest() { +@@ -277,10 +286,78 @@ var Image = /*#__PURE__*/React.forwardRef((props, ref) => { + suppressHydrationWarning: true + }), hiddenImage, createTintColorSVG(tintColor, filterRef.current)); + }); +-Image.displayName = 'Image'; ++BaseImage.displayName = 'Image'; ++ ++/** ++ * This component handles specifically loading an image source with headers ++ * default source is never loaded using headers ++ */ ++var ImageWithHeaders = /*#__PURE__*/React.forwardRef((props, ref) => { ++ // $FlowIgnore: This component would only be rendered when `source` matches `ImageSource` ++ var nextSource = props.source; ++ var _React$useState3 = React.useState(''), ++ blobUri = _React$useState3[0], ++ setBlobUri = _React$useState3[1]; ++ var request = React.useRef({ ++ cancel: () => {}, ++ source: { ++ uri: '', ++ headers: {} ++ }, ++ promise: Promise.resolve('') ++ }); ++ var onError = props.onError, ++ onLoadStart = props.onLoadStart, ++ onLoadEnd = props.onLoadEnd; ++ React.useEffect(() => { ++ if (!hasSourceDiff(nextSource, request.current.source)) { ++ return; ++ } ++ ++ // When source changes we want to clean up any old/running requests ++ request.current.cancel(); ++ if (onLoadStart) { ++ onLoadStart(); ++ } ++ ++ // Store a ref for the current load request so we know what's the last loaded source, ++ // and so we can cancel it if a different source is passed through props ++ request.current = ImageLoader.loadWithHeaders(nextSource); ++ request.current.promise.then(uri => setBlobUri(uri)).catch(() => raiseOnErrorEvent(request.current.source.uri, { ++ onError, ++ onLoadEnd ++ })); ++ }, [nextSource, onLoadStart, onError, onLoadEnd]); ++ ++ // Cancel any request on unmount ++ React.useEffect(() => request.current.cancel, []); ++ var propsToPass = _objectSpread(_objectSpread({}, props), {}, { ++ // `onLoadStart` is called from the current component ++ // We skip passing it down to prevent BaseImage raising it a 2nd time ++ onLoadStart: undefined, ++ // Until the current component resolves the request (using headers) ++ // we skip forwarding the source so the base component doesn't attempt ++ // to load the original source ++ source: blobUri ? _objectSpread(_objectSpread({}, nextSource), {}, { ++ uri: blobUri ++ }) : undefined ++ }); ++ return /*#__PURE__*/React.createElement(BaseImage, _extends({ ++ ref: ref ++ }, propsToPass)); ++}); + + // $FlowIgnore: This is the correct type, but casting makes it unhappy since the variables aren't defined yet +-var ImageWithStatics = Image; ++var ImageWithStatics = /*#__PURE__*/React.forwardRef((props, ref) => { ++ if (props.source && props.source.headers) { ++ return /*#__PURE__*/React.createElement(ImageWithHeaders, _extends({ ++ ref: ref ++ }, props)); ++ } ++ return /*#__PURE__*/React.createElement(BaseImage, _extends({ ++ ref: ref ++ }, props)); ++}); + ImageWithStatics.getSize = function (uri, success, failure) { + ImageLoader.getSize(uri, success, failure); + }; +diff --git a/node_modules/react-native-web/dist/modules/ImageLoader/index.js b/node_modules/react-native-web/dist/modules/ImageLoader/index.js +index bc06a87..e309394 100644 +--- a/node_modules/react-native-web/dist/modules/ImageLoader/index.js ++++ b/node_modules/react-native-web/dist/modules/ImageLoader/index.js +@@ -76,7 +76,7 @@ var ImageLoader = { + var image = requests["" + requestId]; + if (image) { + var naturalHeight = image.naturalHeight, +- naturalWidth = image.naturalWidth; ++ naturalWidth = image.naturalWidth; + if (naturalHeight && naturalWidth) { + success(naturalWidth, naturalHeight); + complete = true; +@@ -102,11 +102,19 @@ var ImageLoader = { + id += 1; + var image = new window.Image(); + image.onerror = onError; +- image.onload = e => { ++ image.onload = nativeEvent => { + // avoid blocking the main thread +- var onDecode = () => onLoad({ +- nativeEvent: e +- }); ++ var onDecode = () => { ++ // Append `source` to match RN's ImageLoadEvent interface ++ nativeEvent.source = { ++ uri: image.src, ++ width: image.naturalWidth, ++ height: image.naturalHeight ++ }; ++ onLoad({ ++ nativeEvent ++ }); ++ }; + if (typeof image.decode === 'function') { + // Safari currently throws exceptions when decoding svgs. + // We want to catch that error and allow the load handler +@@ -120,6 +128,32 @@ var ImageLoader = { + requests["" + id] = image; + return id; + }, ++ loadWithHeaders(source) { ++ var uri; ++ var abortController = new AbortController(); ++ var request = new Request(source.uri, { ++ headers: source.headers, ++ signal: abortController.signal ++ }); ++ request.headers.append('accept', 'image/*'); ++ var promise = fetch(request).then(response => response.blob()).then(blob => { ++ uri = URL.createObjectURL(blob); ++ return uri; ++ }).catch(error => { ++ if (error.name === 'AbortError') { ++ return ''; ++ } ++ throw error; ++ }); ++ return { ++ promise, ++ source, ++ cancel: () => { ++ abortController.abort(); ++ URL.revokeObjectURL(uri); ++ } ++ }; ++ }, + prefetch(uri) { + return new Promise((resolve, reject) => { + ImageLoader.load(uri, () => { diff --git a/src/CONST.ts b/src/CONST.ts index c3e61c0c75d8..132b49c16ef7 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -43,10 +43,21 @@ const keyInputRightArrow = KeyCommand?.constants?.keyInputRightArrow ?? 'keyInpu // describes if a shortcut key can cause navigation const KEYBOARD_SHORTCUT_NAVIGATION_TYPE = 'NAVIGATION_SHORTCUT'; +const chatTypes = { + POLICY_ANNOUNCE: 'policyAnnounce', + POLICY_ADMINS: 'policyAdmins', + DOMAIN_ALL: 'domainAll', + POLICY_ROOM: 'policyRoom', + POLICY_EXPENSE_CHAT: 'policyExpenseChat', + SELF_DM: 'selfDM', +} as const; + // Explicit type annotation is required const cardActiveStates: number[] = [2, 3, 4, 7]; const CONST = { + MERGED_ACCOUNT_PREFIX: 'MERGED_', + DEFAULT_POLICY_ROOM_CHAT_TYPES: [chatTypes.POLICY_ADMINS, chatTypes.POLICY_ANNOUNCE, chatTypes.DOMAIN_ALL], ANDROID_PACKAGE_NAME, ANIMATED_TRANSITION: 300, ANIMATED_TRANSITION_FROM_VALUE: 100, @@ -346,6 +357,9 @@ const CONST = { INSTALLED: 'installed', NOT_INSTALLED: 'not-installed', }, + TAX_RATES: { + NAME_MAX_LENGTH: 50, + }, PLATFORM: { IOS: 'ios', ANDROID: 'android', @@ -732,14 +746,7 @@ const CONST = { IOU: 'iou', TASK: 'task', }, - CHAT_TYPE: { - POLICY_ANNOUNCE: 'policyAnnounce', - POLICY_ADMINS: 'policyAdmins', - DOMAIN_ALL: 'domainAll', - POLICY_ROOM: 'policyRoom', - POLICY_EXPENSE_CHAT: 'policyExpenseChat', - SELF_DM: 'selfDM', - }, + CHAT_TYPE: chatTypes, WORKSPACE_CHAT_ROOMS: { ANNOUNCE: '#announce', ADMINS: '#admins', diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index d7f3104cd8b4..e91b4d491423 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -339,6 +339,8 @@ const ONYXKEYS = { WORKSPACE_DESCRIPTION_FORM_DRAFT: 'workspaceDescriptionFormDraft', WORKSPACE_RATE_AND_UNIT_FORM: 'workspaceRateAndUnitForm', WORKSPACE_RATE_AND_UNIT_FORM_DRAFT: 'workspaceRateAndUnitFormDraft', + WORKSPACE_TAX_CUSTOM_NAME: 'workspaceTaxCustomName', + WORKSPACE_TAX_CUSTOM_NAME_DRAFT: 'workspaceTaxCustomNameDraft', POLICY_CREATE_DISTANCE_RATE_FORM: 'policyCreateDistanceRateForm', POLICY_CREATE_DISTANCE_RATE_FORM_DRAFT: 'policyCreateDistanceRateFormDraft', CLOSE_ACCOUNT_FORM: 'closeAccount', @@ -411,6 +413,8 @@ const ONYXKEYS = { EXIT_SURVEY_RESPONSE_FORM_DRAFT: 'exitSurveyResponseFormDraft', POLICY_TAG_NAME_FORM: 'policyTagNameForm', POLICY_TAG_NAME_FORM_DRAFT: 'policyTagNameFormDraft', + WORKSPACE_NEW_TAX_FORM: 'workspaceNewTaxForm', + WORKSPACE_NEW_TAX_FORM_DRAFT: 'workspaceNewTaxFormDraft', }, } as const; @@ -422,6 +426,7 @@ type OnyxFormValuesMapping = { [ONYXKEYS.FORMS.WORKSPACE_CATEGORY_FORM]: FormTypes.WorkspaceCategoryForm; [ONYXKEYS.FORMS.WORKSPACE_TAG_CREATE_FORM]: FormTypes.WorkspaceTagCreateForm; [ONYXKEYS.FORMS.WORKSPACE_RATE_AND_UNIT_FORM]: FormTypes.WorkspaceRateAndUnitForm; + [ONYXKEYS.FORMS.WORKSPACE_TAX_CUSTOM_NAME]: FormTypes.WorkspaceTaxCustomName; [ONYXKEYS.FORMS.CLOSE_ACCOUNT_FORM]: FormTypes.CloseAccountForm; [ONYXKEYS.FORMS.PROFILE_SETTINGS_FORM]: FormTypes.ProfileSettingsForm; [ONYXKEYS.FORMS.DISPLAY_NAME_FORM]: FormTypes.DisplayNameForm; @@ -458,6 +463,7 @@ type OnyxFormValuesMapping = { [ONYXKEYS.FORMS.PERSONAL_BANK_ACCOUNT]: FormTypes.PersonalBankAccountForm; [ONYXKEYS.FORMS.WORKSPACE_DESCRIPTION_FORM]: FormTypes.WorkspaceDescriptionForm; [ONYXKEYS.FORMS.POLICY_TAG_NAME_FORM]: FormTypes.PolicyTagNameForm; + [ONYXKEYS.FORMS.WORKSPACE_NEW_TAX_FORM]: FormTypes.WorkspaceNewTaxForm; [ONYXKEYS.FORMS.POLICY_CREATE_DISTANCE_RATE_FORM]: FormTypes.PolicyCreateDistanceRateForm; }; diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 1bcf6fc02032..5769b60a8284 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -276,12 +276,6 @@ const ROUTES = { route: 'r/:reportID/invite', getRoute: (reportID: string) => `r/${reportID}/invite` as const, }, - - // To see the available iouType, please refer to CONST.IOU.TYPE - MONEY_REQUEST: { - route: ':iouType/new/:reportID?', - getRoute: (iouType: string, reportID = '') => `${iouType}/new/${reportID}` as const, - }, MONEY_REQUEST_AMOUNT: { route: ':iouType/new/amount/:reportID?', getRoute: (iouType: string, reportID = '') => `${iouType}/new/amount/${reportID}` as const, @@ -314,13 +308,6 @@ const ROUTES = { route: ':iouType/new/address/:reportID?', getRoute: (iouType: string, reportID = '') => `${iouType}/new/address/${reportID}` as const, }, - MONEY_REQUEST_DISTANCE_TAB: { - route: ':iouType/new/:reportID?/distance', - getRoute: (iouType: string, reportID = '') => `${iouType}/new/${reportID}/distance` as const, - }, - MONEY_REQUEST_MANUAL_TAB: ':iouType/new/:reportID?/manual', - MONEY_REQUEST_SCAN_TAB: ':iouType/new/:reportID?/scan', - MONEY_REQUEST_CREATE: { route: 'create/:iouType/start/:transactionID/:reportID', getRoute: (iouType: ValueOf, transactionID: string, reportID: string) => `create/${iouType}/start/${transactionID}/${reportID}` as const, @@ -597,6 +584,22 @@ const ROUTES = { route: 'settings/workspaces/:policyID/taxes', getRoute: (policyID: string) => `settings/workspaces/${policyID}/taxes` as const, }, + WORKSPACE_TAXES_SETTINGS: { + route: 'settings/workspaces/:policyID/taxes/settings', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/taxes/settings` as const, + }, + WORKSPACE_TAXES_SETTINGS_WORKSPACE_CURRENCY_DEFAULT: { + route: 'settings/workspaces/:policyID/taxes/settings/workspace-currency', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/taxes/settings/workspace-currency` as const, + }, + WORKSPACE_TAXES_SETTINGS_FOREIGN_CURRENCY_DEFAULT: { + route: 'settings/workspaces/:policyID/taxes/settings/foreign-currency', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/taxes/settings/foreign-currency` as const, + }, + WORKSPACE_TAXES_SETTINGS_CUSTOM_TAX_NAME: { + route: 'settings/workspaces/:policyID/taxes/settings/tax-name', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/taxes/settings/tax-name` as const, + }, WORKSPACE_MEMBER_DETAILS: { route: 'settings/workspaces/:policyID/members/:accountID', getRoute: (policyID: string, accountID: number, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/members/${accountID}`, backTo), @@ -605,6 +608,10 @@ const ROUTES = { route: 'settings/workspaces/:policyID/members/:accountID/role-selection', getRoute: (policyID: string, accountID: number, backTo?: string) => getUrlWithBackToParam(`settings/workspaces/${policyID}/members/${accountID}/role-selection`, backTo), }, + WORKSPACE_TAX_CREATE: { + route: 'settings/workspaces/:policyID/taxes/new', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/taxes/new` as const, + }, WORKSPACE_DISTANCE_RATES: { route: 'settings/workspaces/:policyID/distance-rates', getRoute: (policyID: string) => `settings/workspaces/${policyID}/distance-rates` as const, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 5d872d194dc5..2fbd122f9972 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -132,9 +132,6 @@ const SCREENS = { WORKSPACE_JOIN_USER: 'WorkspaceJoinUser', MONEY_REQUEST: { - MANUAL_TAB: 'manual', - SCAN_TAB: 'scan', - DISTANCE_TAB: 'distance', CREATE: 'Money_Request_Create', HOLD: 'Money_Request_Hold_Reason', STEP_CONFIRMATION: 'Money_Request_Step_Confirmation', @@ -152,7 +149,6 @@ const SCREENS = { STEP_WAYPOINT: 'Money_Request_Step_Waypoint', STEP_TAX_AMOUNT: 'Money_Request_Step_Tax_Amount', STEP_TAX_RATE: 'Money_Request_Step_Tax_Rate', - ROOT: 'Money_Request', AMOUNT: 'Money_Request_Amount', PARTICIPANTS: 'Money_Request_Participants', CONFIRMATION: 'Money_Request_Confirmation', @@ -220,6 +216,11 @@ const SCREENS = { TAGS_SETTINGS: 'Tags_Settings', TAGS_EDIT: 'Tags_Edit', TAXES: 'Workspace_Taxes', + TAXES_SETTINGS: 'Workspace_Taxes_Settings', + TAXES_SETTINGS_CUSTOM_TAX_NAME: 'Workspace_Taxes_Settings_CustomTaxName', + TAXES_SETTINGS_WORKSPACE_CURRENCY_DEFAULT: 'Workspace_Taxes_Settings_WorkspaceCurrency', + TAXES_SETTINGS_FOREIGN_CURRENCY_DEFAULT: 'Workspace_Taxes_Settings_ForeignCurrency', + TAX_CREATE: 'Workspace_Tax_Create', TAG_CREATE: 'Tag_Create', TAG_SETTINGS: 'Tag_Settings', CURRENCY: 'Workspace_Profile_Currency', diff --git a/src/components/AmountForm.tsx b/src/components/AmountForm.tsx index 8ae8f0674012..48035dd884bd 100644 --- a/src/components/AmountForm.tsx +++ b/src/components/AmountForm.tsx @@ -12,8 +12,9 @@ import * as MoneyRequestUtils from '@libs/MoneyRequestUtils'; import CONST from '@src/CONST'; import BigNumberPad from './BigNumberPad'; import FormHelpMessage from './FormHelpMessage'; -import type {BaseTextInputRef} from './TextInput/BaseTextInput/types'; +import type {BaseTextInputProps, BaseTextInputRef} from './TextInput/BaseTextInput/types'; import TextInputWithCurrencySymbol from './TextInputWithCurrencySymbol'; +import type TextInputWithCurrencySymbolProps from './TextInputWithCurrencySymbol/types'; type AmountFormProps = { /** Amount supplied by the FormProvider */ @@ -36,7 +37,8 @@ type AmountFormProps = { /** Whether the currency symbol is pressable */ isCurrencyPressable?: boolean; -}; +} & Pick & + Pick; /** * Returns the new selection object based on the updated amount's length @@ -51,7 +53,7 @@ const NUM_PAD_CONTAINER_VIEW_ID = 'numPadContainerView'; const NUM_PAD_VIEW_ID = 'numPadView'; function AmountForm( - {value: amount, currency = CONST.CURRENCY.USD, extraDecimals = 0, errorText, onInputChange, onCurrencyButtonPress, isCurrencyPressable = true}: AmountFormProps, + {value: amount, currency = CONST.CURRENCY.USD, extraDecimals = 0, errorText, onInputChange, onCurrencyButtonPress, isCurrencyPressable = true, ...rest}: AmountFormProps, forwardedRef: ForwardedRef, ) { const styles = useThemeStyles(); @@ -214,6 +216,8 @@ function AmountForm( }} onKeyPress={textInputKeyPress} isCurrencyPressable={isCurrencyPressable} + // eslint-disable-next-line react/jsx-props-no-spreading + {...rest} /> {!!errorText && ( + + + + + + +