From 88b7ef89bcaa07ff52a1f1dfd9505ebf2b338a0a Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 21 Dec 2023 18:11:06 +0100 Subject: [PATCH 001/176] connect local react-fast-pdf --- package-lock.json | 429 +++++++++++++++++++++++++++++--- package.json | 1 + src/components/PDFView/index.js | 266 ++------------------ src/styles/index.ts | 19 -- 4 files changed, 426 insertions(+), 289 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1c2ae325575d..cf30d9f6fb3f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -69,6 +69,7 @@ "react-content-loader": "^6.1.0", "react-dom": "18.1.0", "react-error-boundary": "^4.0.11", + "react-fast-pdf": "git+https://github.com/rezkiy37/react-fast-pdf#ecb6779e8442d433ca421b7e05acedaa0725a30a", "react-map-gl": "^7.1.3", "react-native": "0.72.4", "react-native-android-location-enabler": "^1.2.2", @@ -5380,6 +5381,50 @@ "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-2.0.1.tgz", "integrity": "sha512-HP6XvfNIzfoMVfyGjBckjiAOQK9WfX0ywdLubuPMPv+Vqf5fj0uCbgBQYpiqcWZT6cbyyRnTSXDheT1ugvF6UQ==" }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "optional": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "optional": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@mapbox/point-geometry": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", @@ -20854,6 +20899,12 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "optional": true + }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -20964,7 +21015,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "debug": "4" @@ -21416,7 +21467,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true + "devOptional": true }, "node_modules/are-docs-informative": { "version": "0.0.2", @@ -21431,7 +21482,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "delegates": "^1.0.0", @@ -21445,7 +21496,7 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, + "devOptional": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -24072,6 +24123,21 @@ } ] }, + "node_modules/canvas": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz", + "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.0", + "nan": "^2.17.0", + "simple-get": "^3.0.3" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/canvas-size": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/canvas-size/-/canvas-size-1.2.6.tgz", @@ -24746,7 +24812,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true, + "devOptional": true, "license": "ISC", "bin": { "color-support": "bin.js" @@ -25081,7 +25147,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/constants-browserify": { @@ -26630,7 +26696,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/denodeify": { @@ -26706,6 +26772,15 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -31212,7 +31287,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "aproba": "^1.0.3 || ^2.0.0", @@ -31729,7 +31804,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/has-value": { @@ -32467,7 +32542,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "agent-base": "6", @@ -37782,8 +37857,9 @@ } }, "node_modules/make-cancellable-promise": { - "version": "1.1.0", - "license": "MIT", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/make-cancellable-promise/-/make-cancellable-promise-1.3.2.tgz", + "integrity": "sha512-GCXh3bq/WuMbS+Ky4JBPW1hYTOU+znU+Q5m9Pu+pI8EoUqIHk9+tviOKC6/qhHh8C4/As3tzJ69IF32kdz85ww==", "funding": { "url": "https://github.com/wojtekmaj/make-cancellable-promise?sponsor=1" } @@ -41068,7 +41144,6 @@ "version": "2.17.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", - "dev": true, "optional": true }, "node_modules/nanoid": { @@ -41330,6 +41405,21 @@ "url": "https://github.com/sponsors/antelle" } }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "optional": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -41412,7 +41502,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "are-we-there-yet": "^2.0.0", @@ -42615,6 +42705,15 @@ "node": ">=8" } }, + "node_modules/path2d-polyfill": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path2d-polyfill/-/path2d-polyfill-2.0.1.tgz", + "integrity": "sha512-ad/3bsalbbWhmBo0D6FZ4RNMwsLsPpL6gnvhuSaU5Vm7b06Kr5ubSltQQ0T7YKsiJQO+g22zJ4dJKNTXIyOXtA==", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/pause-stream": { "version": "0.0.11", "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", @@ -44004,6 +44103,74 @@ "react": ">=16.13.1" } }, + "node_modules/react-fast-pdf": { + "version": "1.0.3", + "resolved": "git+ssh://git@github.com/rezkiy37/react-fast-pdf.git#ecb6779e8442d433ca421b7e05acedaa0725a30a", + "integrity": "sha512-kmdkx9Oh6gvMyt6Jiv+xSi5fQEybxXFD6l+woscs6Lt6m3nLweYP1KUSUn03+ocpVzLKIbLiMbW8D/D9R18AJA==", + "license": "MIT", + "dependencies": { + "react-pdf": "^7.3.3", + "react-window": "^1.8.9" + }, + "engines": { + "node": "20.9.0", + "npm": "10.1.0" + }, + "peerDependencies": { + "lodash": "4.x", + "prop-types": "15.x", + "react": "18.x", + "react-dom": "18.x" + } + }, + "node_modules/react-fast-pdf/node_modules/clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/react-fast-pdf/node_modules/pdfjs-dist": { + "version": "3.11.174", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-3.11.174.tgz", + "integrity": "sha512-TdTZPf1trZ8/UFu5Cx/GXB7GZM30LT+wWUNfsi6Bq8ePLnb+woNKtDymI2mxZYBpMbonNFqKmiz684DIfnd8dA==", + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "canvas": "^2.11.2", + "path2d-polyfill": "^2.0.1" + } + }, + "node_modules/react-fast-pdf/node_modules/react-pdf": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-7.6.0.tgz", + "integrity": "sha512-b2/8V6xhe5pn4Y01ELKTQZ+RhdZl1KpSAMGbN+HCZ/kwhYTIc4Pn5ctz1wRQUu1gOJbIEG4CcjMD9vTCzNdwjw==", + "dependencies": { + "clsx": "^2.0.0", + "make-cancellable-promise": "^1.3.1", + "make-event-props": "^1.6.0", + "merge-refs": "^1.2.1", + "pdfjs-dist": "3.11.174", + "prop-types": "^15.6.2", + "tiny-invariant": "^1.0.0", + "tiny-warning": "^1.0.0" + }, + "funding": { + "url": "https://github.com/wojtekmaj/react-pdf?sponsor=1" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-freeze": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.3.tgz", @@ -47571,6 +47738,61 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true + }, + "node_modules/simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "optional": true, + "dependencies": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-get/node_modules/decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "optional": true, + "dependencies": { + "mimic-response": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/simple-get/node_modules/mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "optional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/simple-git": { "version": "3.19.0", "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.19.0.tgz", @@ -52409,7 +52631,7 @@ "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" @@ -56611,6 +56833,42 @@ "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-2.0.1.tgz", "integrity": "sha512-HP6XvfNIzfoMVfyGjBckjiAOQK9WfX0ywdLubuPMPv+Vqf5fj0uCbgBQYpiqcWZT6cbyyRnTSXDheT1ugvF6UQ==" }, + "@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "optional": true, + "requires": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "dependencies": { + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "optional": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "optional": true + } + } + } + } + }, "@mapbox/point-geometry": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", @@ -67822,6 +68080,12 @@ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", "dev": true }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "optional": true + }, "abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -67896,7 +68160,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, + "devOptional": true, "requires": { "debug": "4" } @@ -68235,7 +68499,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true + "devOptional": true }, "are-docs-informative": { "version": "0.0.2", @@ -68247,7 +68511,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "dev": true, + "devOptional": true, "requires": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" @@ -68257,7 +68521,7 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, + "devOptional": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -70191,6 +70455,17 @@ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001505.tgz", "integrity": "sha512-jaAOR5zVtxHfL0NjZyflVTtXm3D3J9P15zSJ7HmQF8dSKGA6tqzQq+0ZI3xkjyQj46I4/M0K2GbMpcAFOcbr3A==" }, + "canvas": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz", + "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==", + "optional": true, + "requires": { + "@mapbox/node-pre-gyp": "^1.0.0", + "nan": "^2.17.0", + "simple-get": "^3.0.3" + } + }, "canvas-size": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/canvas-size/-/canvas-size-1.2.6.tgz", @@ -70668,7 +70943,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true + "devOptional": true }, "colorette": { "version": "1.4.0", @@ -70910,7 +71185,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "dev": true + "devOptional": true }, "constants-browserify": { "version": "1.0.0", @@ -72010,7 +72285,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "dev": true + "devOptional": true }, "denodeify": { "version": "1.2.1", @@ -72065,6 +72340,12 @@ "repeat-string": "^1.5.4" } }, + "detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "optional": true + }, "detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -75344,7 +75625,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "dev": true, + "devOptional": true, "requires": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.2", @@ -75694,7 +75975,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "dev": true + "devOptional": true }, "has-value": { "version": "1.0.0", @@ -76234,7 +76515,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, + "devOptional": true, "requires": { "agent-base": "6", "debug": "4" @@ -79943,7 +80224,9 @@ } }, "make-cancellable-promise": { - "version": "1.1.0" + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/make-cancellable-promise/-/make-cancellable-promise-1.3.2.tgz", + "integrity": "sha512-GCXh3bq/WuMbS+Ky4JBPW1hYTOU+znU+Q5m9Pu+pI8EoUqIHk9+tviOKC6/qhHh8C4/As3tzJ69IF32kdz85ww==" }, "make-dir": { "version": "2.1.0", @@ -82337,7 +82620,6 @@ "version": "2.17.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", - "dev": true, "optional": true }, "nanoid": { @@ -82537,6 +82819,15 @@ "resolved": "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.15.0.tgz", "integrity": "sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==" }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "optional": true, + "requires": { + "abbrev": "1" + } + }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -82594,7 +82885,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "dev": true, + "devOptional": true, "requires": { "are-we-there-yet": "^2.0.0", "console-control-strings": "^1.1.0", @@ -83434,6 +83725,12 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, + "path2d-polyfill": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path2d-polyfill/-/path2d-polyfill-2.0.1.tgz", + "integrity": "sha512-ad/3bsalbbWhmBo0D6FZ4RNMwsLsPpL6gnvhuSaU5Vm7b06Kr5ubSltQQ0T7YKsiJQO+g22zJ4dJKNTXIyOXtA==", + "optional": true + }, "pause-stream": { "version": "0.0.11", "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", @@ -84415,6 +84712,46 @@ "@babel/runtime": "^7.12.5" } }, + "react-fast-pdf": { + "version": "git+ssh://git@github.com/rezkiy37/react-fast-pdf.git#ecb6779e8442d433ca421b7e05acedaa0725a30a", + "integrity": "sha512-kmdkx9Oh6gvMyt6Jiv+xSi5fQEybxXFD6l+woscs6Lt6m3nLweYP1KUSUn03+ocpVzLKIbLiMbW8D/D9R18AJA==", + "from": "react-fast-pdf@git+https://github.com/rezkiy37/react-fast-pdf#ecb6779e8442d433ca421b7e05acedaa0725a30a", + "requires": { + "react-pdf": "^7.3.3", + "react-window": "^1.8.9" + }, + "dependencies": { + "clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==" + }, + "pdfjs-dist": { + "version": "3.11.174", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-3.11.174.tgz", + "integrity": "sha512-TdTZPf1trZ8/UFu5Cx/GXB7GZM30LT+wWUNfsi6Bq8ePLnb+woNKtDymI2mxZYBpMbonNFqKmiz684DIfnd8dA==", + "requires": { + "canvas": "^2.11.2", + "path2d-polyfill": "^2.0.1" + } + }, + "react-pdf": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-7.6.0.tgz", + "integrity": "sha512-b2/8V6xhe5pn4Y01ELKTQZ+RhdZl1KpSAMGbN+HCZ/kwhYTIc4Pn5ctz1wRQUu1gOJbIEG4CcjMD9vTCzNdwjw==", + "requires": { + "clsx": "^2.0.0", + "make-cancellable-promise": "^1.3.1", + "make-event-props": "^1.6.0", + "merge-refs": "^1.2.1", + "pdfjs-dist": "3.11.174", + "prop-types": "^15.6.2", + "tiny-invariant": "^1.0.0", + "tiny-warning": "^1.0.0" + } + } + } + }, "react-freeze": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.3.tgz", @@ -86952,6 +87289,40 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "optional": true + }, + "simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "optional": true, + "requires": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + }, + "dependencies": { + "decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "optional": true, + "requires": { + "mimic-response": "^2.0.0" + } + }, + "mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "optional": true + } + } + }, "simple-git": { "version": "3.19.0", "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.19.0.tgz", @@ -90443,7 +90814,7 @@ "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dev": true, + "devOptional": true, "requires": { "string-width": "^1.0.2 || 2 || 3 || 4" } diff --git a/package.json b/package.json index 7281ab12cfa7..cde7a9460002 100644 --- a/package.json +++ b/package.json @@ -117,6 +117,7 @@ "react-content-loader": "^6.1.0", "react-dom": "18.1.0", "react-error-boundary": "^4.0.11", + "react-fast-pdf": "git+https://github.com/rezkiy37/react-fast-pdf#ecb6779e8442d433ca421b7e05acedaa0725a30a", "react-map-gl": "^7.1.3", "react-native": "0.72.4", "react-native-android-location-enabler": "^1.2.2", diff --git a/src/components/PDFView/index.js b/src/components/PDFView/index.js index e18c52b06972..7f74b919469c 100644 --- a/src/components/PDFView/index.js +++ b/src/components/PDFView/index.js @@ -1,10 +1,8 @@ import 'core-js/features/array/at'; -import pdfWorkerSource from 'pdfjs-dist/legacy/build/pdf.worker'; import React, {Component} from 'react'; +import {PDFPreviewer} from 'react-fast-pdf'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import {Document, Page, pdfjs} from 'react-pdf/dist/esm/entry.webpack'; -import {VariableSizeList as List} from 'react-window'; import _ from 'underscore'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; @@ -13,7 +11,6 @@ import withLocalize from '@components/withLocalize'; import withThemeStyles from '@components/withThemeStyles'; import withWindowDimensions from '@components/withWindowDimensions'; import compose from '@libs/compose'; -import Log from '@libs/Log'; import variables from '@styles/variables'; import * as CanvasSize from '@userActions/CanvasSize'; import CONST from '@src/CONST'; @@ -21,41 +18,15 @@ import ONYXKEYS from '@src/ONYXKEYS'; import PDFPasswordForm from './PDFPasswordForm'; import * as pdfViewPropTypes from './pdfViewPropTypes'; -/** - * Each page has a default border. The app should take this size into account - * when calculates the page width and height. - */ -const PAGE_BORDER = 9; -/** - * Pages should be more narrow than the container on large screens. The app should take this size into account - * when calculates the page width. - */ -const LARGE_SCREEN_SIDE_SPACING = 40; - class PDFView extends Component { constructor(props) { super(props); this.state = { - numPages: null, - pageViewports: [], - containerWidth: props.windowWidth, - containerHeight: props.windowHeight, - shouldRequestPassword: false, - isPasswordInvalid: false, isKeyboardOpen: false, }; - this.onDocumentLoadSuccess = this.onDocumentLoadSuccess.bind(this); - this.initiatePasswordChallenge = this.initiatePasswordChallenge.bind(this); - this.attemptPDFLoad = this.attemptPDFLoad.bind(this); this.toggleKeyboardOnSmallScreens = this.toggleKeyboardOnSmallScreens.bind(this); - this.calculatePageHeight = this.calculatePageHeight.bind(this); - this.calculatePageWidth = this.calculatePageWidth.bind(this); - this.renderPage = this.renderPage.bind(this); this.getDevicePixelRatio = _.memoize(this.getDevicePixelRatio); - this.setListAttributes = this.setListAttributes.bind(this); - const workerBlob = new Blob([pdfWorkerSource], {type: 'text/javascript'}); - pdfjs.GlobalWorkerOptions.workerSrc = URL.createObjectURL(workerBlob); this.retrieveCanvasLimits(); } @@ -73,136 +44,6 @@ class PDFView extends Component { } } - /** - * Upon successful document load, combine an array of page viewports, - * set the number of pages on PDF, - * hide/reset PDF password form, and notify parent component that - * user input is no longer required. - * - * @param {Object} pdf - The PDF file instance - * @param {Number} pdf.numPages - Number of pages of the PDF file - * @param {Function} pdf.getPage - A method to get page by its number. It requires to have the context. It should be the pdf itself. - * @memberof PDFView - */ - onDocumentLoadSuccess(pdf) { - const {numPages} = pdf; - - Promise.all( - _.times(numPages, (index) => { - const pageNumber = index + 1; - - return pdf.getPage(pageNumber).then((page) => page.getViewport({scale: 1})); - }), - ).then((pageViewports) => { - this.setState({ - pageViewports, - numPages, - shouldRequestPassword: false, - isPasswordInvalid: false, - }); - }); - } - - /** - * Sets attributes to list container. - * It unblocks a default scroll by keyboard of browsers. - * @param {Object|undefined} ref - */ - setListAttributes(ref) { - if (!ref) { - return; - } - - // Useful for elements that should not be navigated to directly using the "Tab" key, - // but need to have keyboard focus set to them. - // eslint-disable-next-line no-param-reassign - ref.tabIndex = -1; - } - - /** - * Calculate the devicePixelRatio the page should be rendered with - * Each platform has a different default devicePixelRatio and different canvas limits, we need to verify that - * with the default devicePixelRatio it will be able to diplay the pdf correctly, if not we must change the devicePixelRatio. - * @param {Number} width of the page - * @param {Number} height of the page - * @returns {Number} devicePixelRatio for this page on this platform - */ - getDevicePixelRatio(width, height) { - const nbPixels = width * height; - const ratioHeight = this.props.maxCanvasHeight / height; - const ratioWidth = this.props.maxCanvasWidth / width; - const ratioArea = Math.sqrt(this.props.maxCanvasArea / nbPixels); - const ratio = Math.min(ratioHeight, ratioArea, ratioWidth); - return ratio > window.devicePixelRatio ? undefined : ratio; - } - - /** - * Calculates a proper page height. The method should be called only when there are page viewports. - * It is based on a ratio between the specific page viewport width and provided page width. - * Also, the app should take into account the page borders. - * @param {Number} pageIndex - * @returns {Number} - */ - calculatePageHeight(pageIndex) { - if (this.state.pageViewports.length === 0) { - Log.warn('Dev error: calculatePageHeight() in PDFView called too early'); - - return 0; - } - - const pageViewport = this.state.pageViewports[pageIndex]; - const pageWidth = this.calculatePageWidth(); - const scale = pageWidth / pageViewport.width; - const actualHeight = pageViewport.height * scale + PAGE_BORDER * 2; - - return actualHeight; - } - - /** - * Calculates a proper page width. - * It depends on a screen size. Also, the app should take into account the page borders. - * @returns {Number} - */ - calculatePageWidth() { - const pdfContainerWidth = this.state.containerWidth; - const pageWidthOnLargeScreen = Math.min(pdfContainerWidth - LARGE_SCREEN_SIDE_SPACING * 2, variables.pdfPageMaxWidth); - const pageWidth = this.props.isSmallScreenWidth ? this.state.containerWidth : pageWidthOnLargeScreen; - - return pageWidth + PAGE_BORDER * 2; - } - - /** - * Initiate password challenge process. The react-pdf/Document - * component calls this handler to indicate that a PDF requires a - * password, or to indicate that a previously provided password was - * invalid. - * - * The PasswordResponses constants used below were copied from react-pdf - * because they're not exported in entry.webpack. - * - * @param {Function} callback Callback used to send password to react-pdf - * @param {Number} reason Reason code for password request - */ - initiatePasswordChallenge(callback, reason) { - this.onPasswordCallback = callback; - - if (reason === CONST.PDF_PASSWORD_FORM.REACT_PDF_PASSWORD_RESPONSES.NEED_PASSWORD) { - this.setState({shouldRequestPassword: true}); - } else if (reason === CONST.PDF_PASSWORD_FORM.REACT_PDF_PASSWORD_RESPONSES.INCORRECT_PASSWORD) { - this.setState({shouldRequestPassword: true, isPasswordInvalid: true}); - } - } - - /** - * Send password to react-pdf via its callback so that it can attempt to load - * the PDF. - * - * @param {String} password Password to send via callback to react-pdf - */ - attemptPDFLoad(password) { - this.onPasswordCallback(password); - } - /** * On small screens notify parent that the keyboard has opened or closed. * @@ -233,92 +74,35 @@ class PDFView extends Component { } } - /** - * Render a specific page based on its index. - * The method includes a wrapper to apply virtualized styles. - * @param {Object} page item object of the List - * @param {Number} page.index index of the page - * @param {Object} page.style virtualized styles - * @returns {JSX.Element} - */ - renderPage({index, style}) { - const pageWidth = this.calculatePageWidth(); - const pageHeight = this.calculatePageHeight(index); - const devicePixelRatio = this.getDevicePixelRatio(pageWidth, pageHeight); - - return ( - - - - ); - } - renderPDFView() { const styles = this.props.themeStyles; - const pageWidth = this.calculatePageWidth(); const outerContainerStyle = [styles.w100, styles.h100, styles.justifyContentCenter, styles.alignItemsCenter]; - // If we're requesting a password then we need to hide - but still render - - // the PDF component. - const pdfContainerStyle = this.state.shouldRequestPassword - ? [styles.PDFView, styles.noSelect, this.props.style, styles.invisible] - : [styles.PDFView, styles.noSelect, this.props.style]; - return ( - - this.setState({containerWidth: width, containerHeight: height})} - > - {this.props.translate('attachmentView.failedToLoadPDF')}} - loading={} - file={this.props.sourceURL} - options={{ - cMapUrl: 'cmaps/', - cMapPacked: true, - }} - externalLinkTarget="_blank" - onLoadSuccess={this.onDocumentLoadSuccess} - onPassword={this.initiatePasswordChallenge} - > - {this.state.pageViewports.length > 0 && ( - - {this.renderPage} - - )} - - - {this.state.shouldRequestPassword && ( - this.setState({isPasswordInvalid: false})} - isPasswordInvalid={this.state.isPasswordInvalid} - onPasswordFieldFocused={this.toggleKeyboardOnSmallScreens} - /> - )} + + } + ErrorComponent={{this.props.translate('attachmentView.failedToLoadPDF')}} + renderPasswordForm={({isPasswordInvalid, onSubmit, onPasswordChange}) => ( + + )} + /> ); } diff --git a/src/styles/index.ts b/src/styles/index.ts index aececf93beb9..2f34c91d3fa6 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -2208,25 +2208,6 @@ const styles = (theme: ThemeColors) => backgroundColor: theme.modalBackground, }, - PDFView: { - // `display: grid` is not supported in native platforms! - // It's being used on Web/Desktop only to vertically center short PDFs, - // while preventing the overflow of the top of long PDF files. - ...display.dGrid, - width: '100%', - height: '100%', - justifyContent: 'center', - overflow: 'hidden', - alignItems: 'center', - }, - - PDFViewList: { - overflowX: 'hidden', - // There properties disable "focus" effect on list - boxShadow: 'none', - outline: 'none', - }, - getPDFPasswordFormStyle: (isSmallScreenWidth: boolean) => ({ width: isSmallScreenWidth ? '100%' : 350, From 2c725ca00033fbc698998ea06d64a96c612d83e8 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 21 Dec 2023 18:25:03 +0100 Subject: [PATCH 002/176] align password block --- src/styles/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/styles/index.ts b/src/styles/index.ts index 1a029f2f4240..ad791838f125 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -2205,6 +2205,7 @@ const styles = (theme: ThemeColors) => ({ width: isSmallScreenWidth ? '100%' : 350, ...(isSmallScreenWidth && flex.flex1), + alignSelf: 'flex-start', } satisfies ViewStyle), centeredModalStyles: (isSmallScreenWidth: boolean, isFullScreenWhenSmall: boolean) => From 183f9621101cb83b011f6cad5998f1ac4c325553 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 21 Dec 2023 19:20:45 +0100 Subject: [PATCH 003/176] align password form --- src/styles/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index ad791838f125..aefbd47f4fa4 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -2203,8 +2203,8 @@ const styles = (theme: ThemeColors) => getPDFPasswordFormStyle: (isSmallScreenWidth: boolean) => ({ - width: isSmallScreenWidth ? '100%' : 350, - ...(isSmallScreenWidth && flex.flex1), + flexBasis: isSmallScreenWidth ? '100%' : 350, + flexGrow: 0, alignSelf: 'flex-start', } satisfies ViewStyle), From 44c41df41b81ab483fe0c3a13505b01fab0084fa Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Wed, 31 Jan 2024 15:34:26 +0100 Subject: [PATCH 004/176] back changes --- src/components/PDFView/index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/PDFView/index.js b/src/components/PDFView/index.js index 5d505bd318fe..4e748669df1b 100644 --- a/src/components/PDFView/index.js +++ b/src/components/PDFView/index.js @@ -4,7 +4,9 @@ import {PDFPreviewer} from 'react-fast-pdf'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; +import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; +import Text from '@components/Text'; import withLocalize from '@components/withLocalize'; import withThemeStyles from '@components/withThemeStyles'; import withWindowDimensions from '@components/withWindowDimensions'; @@ -93,6 +95,7 @@ class PDFView extends Component { maxCanvasWidth={this.props.maxCanvasWidth} maxCanvasHeight={this.props.maxCanvasHeight} maxCanvasArea={this.props.maxCanvasArea} + LoadingComponent={} ErrorComponent={{this.props.translate('attachmentView.failedToLoadPDF')}} renderPasswordForm={({isPasswordInvalid, onSubmit, onPasswordChange}) => ( Date: Wed, 31 Jan 2024 18:29:12 +0100 Subject: [PATCH 005/176] use latest react-fast-pdf versions --- package-lock.json | 34 +++++++++++++++++++++------------- package.json | 2 +- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index 04409926e0b1..b829a7208f72 100644 --- a/package-lock.json +++ b/package-lock.json @@ -73,7 +73,7 @@ "react-content-loader": "^6.1.0", "react-dom": "18.1.0", "react-error-boundary": "^4.0.11", - "react-fast-pdf": "git+https://github.com/rezkiy37/react-fast-pdf#b7b8ccb6a21a2322d0567bd448d57e54d71238fb", + "react-fast-pdf": "git+https://github.com/rezkiy37/react-fast-pdf#b925199ddbc94eb17197bfbe285e764d95832206", "react-map-gl": "^7.1.3", "react-native": "0.73.2", "react-native-android-location-enabler": "^1.2.2", @@ -28041,7 +28041,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, "engines": { "node": ">=6" } @@ -44622,12 +44621,12 @@ }, "node_modules/react-fast-pdf": { "version": "1.0.3", - "resolved": "git+ssh://git@github.com/rezkiy37/react-fast-pdf.git#b7b8ccb6a21a2322d0567bd448d57e54d71238fb", - "integrity": "sha512-qTePMIJJX1fKe/o83pu/zHYkGforwT2HnHK3pRBaZ1xe5CD3xCM5iQ6K0xRNTxQ/F4saLD0oQtiIjaKZdmdIBg==", + "resolved": "git+ssh://git@github.com/rezkiy37/react-fast-pdf.git#b925199ddbc94eb17197bfbe285e764d95832206", + "integrity": "sha512-CRPbAXZkIYc9e3Dk93qzGD63jfqAvzg57ay2S36Do1V1UwKUFTkwwM+kbbC+kwejVq05tnIC5CRthU75PnlP1A==", "license": "MIT", "dependencies": { - "react-pdf": "^7.3.3", - "react-window": "^1.8.9" + "react-pdf": "^7.7.0", + "react-window": "^1.8.10" }, "engines": { "node": "20.10.0", @@ -44653,18 +44652,19 @@ } }, "node_modules/react-fast-pdf/node_modules/react-pdf": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-7.6.0.tgz", - "integrity": "sha512-b2/8V6xhe5pn4Y01ELKTQZ+RhdZl1KpSAMGbN+HCZ/kwhYTIc4Pn5ctz1wRQUu1gOJbIEG4CcjMD9vTCzNdwjw==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-7.7.0.tgz", + "integrity": "sha512-704ObLnRDm5lixL4e6NXNLaincBHGNLo+NGdbO3rEXE963NlNzwLxFpmKcbdXHAMQL4rYJQWb1L0w5IL6y8Osw==", "dependencies": { "clsx": "^2.0.0", + "dequal": "^2.0.3", "make-cancellable-promise": "^1.3.1", "make-event-props": "^1.6.0", "merge-refs": "^1.2.1", "pdfjs-dist": "3.11.174", "prop-types": "^15.6.2", "tiny-invariant": "^1.0.0", - "tiny-warning": "^1.0.0" + "warning": "^4.0.0" }, "funding": { "url": "https://github.com/wojtekmaj/react-pdf?sponsor=1" @@ -44680,6 +44680,14 @@ } } }, + "node_modules/react-fast-pdf/node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/react-freeze": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.3.tgz", @@ -46301,9 +46309,9 @@ } }, "node_modules/react-window": { - "version": "1.8.9", - "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.9.tgz", - "integrity": "sha512-+Eqx/fj1Aa5WnhRfj9dJg4VYATGwIUP2ItwItiJ6zboKWA6EX3lYDAXfGF2hyNqplEprhbtjbipiADEcwQ823Q==", + "version": "1.8.10", + "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.10.tgz", + "integrity": "sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==", "dependencies": { "@babel/runtime": "^7.0.0", "memoize-one": ">=3.1.1 <6" diff --git a/package.json b/package.json index 56d74250d929..2ce9f9bbe492 100644 --- a/package.json +++ b/package.json @@ -121,7 +121,7 @@ "react-content-loader": "^6.1.0", "react-dom": "18.1.0", "react-error-boundary": "^4.0.11", - "react-fast-pdf": "git+https://github.com/rezkiy37/react-fast-pdf#b7b8ccb6a21a2322d0567bd448d57e54d71238fb", + "react-fast-pdf": "git+https://github.com/rezkiy37/react-fast-pdf#b925199ddbc94eb17197bfbe285e764d95832206", "react-map-gl": "^7.1.3", "react-native": "0.73.2", "react-native-android-location-enabler": "^1.2.2", From 55a4a1115a0124c585b6f991f5e59371c245206b Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Wed, 31 Jan 2024 18:37:56 +0100 Subject: [PATCH 006/176] use latest react-fast-pdf versions --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index b829a7208f72..df4c333af633 100644 --- a/package-lock.json +++ b/package-lock.json @@ -73,7 +73,7 @@ "react-content-loader": "^6.1.0", "react-dom": "18.1.0", "react-error-boundary": "^4.0.11", - "react-fast-pdf": "git+https://github.com/rezkiy37/react-fast-pdf#b925199ddbc94eb17197bfbe285e764d95832206", + "react-fast-pdf": "git+https://github.com/rezkiy37/react-fast-pdf#d5627b27a2d73048c2d12aa2044e44332f92bcf3", "react-map-gl": "^7.1.3", "react-native": "0.73.2", "react-native-android-location-enabler": "^1.2.2", @@ -44621,8 +44621,8 @@ }, "node_modules/react-fast-pdf": { "version": "1.0.3", - "resolved": "git+ssh://git@github.com/rezkiy37/react-fast-pdf.git#b925199ddbc94eb17197bfbe285e764d95832206", - "integrity": "sha512-CRPbAXZkIYc9e3Dk93qzGD63jfqAvzg57ay2S36Do1V1UwKUFTkwwM+kbbC+kwejVq05tnIC5CRthU75PnlP1A==", + "resolved": "git+ssh://git@github.com/rezkiy37/react-fast-pdf.git#d5627b27a2d73048c2d12aa2044e44332f92bcf3", + "integrity": "sha512-YeEIjzBpxEmELZqQg6Ve3qJngr81fYA+b1or4EHR0dnx6/oGElEipAB17e+ZuCJ/NnPc3xKbUEY8UpqA/7+ssw==", "license": "MIT", "dependencies": { "react-pdf": "^7.7.0", diff --git a/package.json b/package.json index 2ce9f9bbe492..b546405d2917 100644 --- a/package.json +++ b/package.json @@ -121,7 +121,7 @@ "react-content-loader": "^6.1.0", "react-dom": "18.1.0", "react-error-boundary": "^4.0.11", - "react-fast-pdf": "git+https://github.com/rezkiy37/react-fast-pdf#b925199ddbc94eb17197bfbe285e764d95832206", + "react-fast-pdf": "git+https://github.com/rezkiy37/react-fast-pdf#d5627b27a2d73048c2d12aa2044e44332f92bcf3", "react-map-gl": "^7.1.3", "react-native": "0.73.2", "react-native-android-location-enabler": "^1.2.2", From 55fceb1ad731bd95461db946f13c342f860ff233 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Mon, 12 Feb 2024 21:19:20 +0100 Subject: [PATCH 007/176] update config --- metro.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metro.config.js b/metro.config.js index 2422d29aaacf..68ed72d52ba0 100644 --- a/metro.config.js +++ b/metro.config.js @@ -7,7 +7,7 @@ require('dotenv').config(); const defaultConfig = getDefaultConfig(__dirname); const isE2ETesting = process.env.E2E_TESTING === 'true'; -const e2eSourceExts = ['e2e.js', 'e2e.ts']; +const e2eSourceExts = ['e2e.js', 'e2e.ts', 'e2e.tsx']; /** * Metro configuration From 82a1aaadb2f29a4a2a6a6386cd6ad466fad651d7 Mon Sep 17 00:00:00 2001 From: Taras Perun Date: Tue, 13 Feb 2024 09:23:21 +0100 Subject: [PATCH 008/176] add onViewableItemsChanged --- .../BaseInvertedFlatList/index.e2e.tsx | 50 +++++++++++++++++++ .../index.tsx} | 1 + 2 files changed, 51 insertions(+) create mode 100644 src/components/InvertedFlatList/BaseInvertedFlatList/index.e2e.tsx rename src/components/InvertedFlatList/{BaseInvertedFlatList.tsx => BaseInvertedFlatList/index.tsx} (96%) diff --git a/src/components/InvertedFlatList/BaseInvertedFlatList/index.e2e.tsx b/src/components/InvertedFlatList/BaseInvertedFlatList/index.e2e.tsx new file mode 100644 index 000000000000..0553312eae32 --- /dev/null +++ b/src/components/InvertedFlatList/BaseInvertedFlatList/index.e2e.tsx @@ -0,0 +1,50 @@ +import type {ForwardedRef} from 'react'; +import React, {forwardRef, useMemo} from 'react'; +import type {FlatListProps, ScrollViewProps} from 'react-native'; +import FlatList from '@components/FlatList'; + +type BaseInvertedFlatListProps = FlatListProps & { + shouldEnableAutoScrollToTopThreshold?: boolean; +}; + +const AUTOSCROLL_TO_TOP_THRESHOLD = 128; + +let localViewableItems: unknown; +const getViewableItems = () => localViewableItems; + +function BaseInvertedFlatListE2e(props: BaseInvertedFlatListProps, ref: ForwardedRef) { + const {shouldEnableAutoScrollToTopThreshold, ...rest} = props; + + const handleViewableItemsChanged = ({viewableItems}: { viewableItems: unknown }) => { + localViewableItems = viewableItems; + }; + + const maintainVisibleContentPosition = useMemo(() => { + const config: ScrollViewProps['maintainVisibleContentPosition'] = { + // This needs to be 1 to avoid using loading views as anchors. + minIndexForVisible: 1, + }; + + if (shouldEnableAutoScrollToTopThreshold) { + config.autoscrollToTopThreshold = AUTOSCROLL_TO_TOP_THRESHOLD; + } + + return config; + }, [shouldEnableAutoScrollToTopThreshold]); + + return ( + + ); +} + +BaseInvertedFlatListE2e.displayName = 'BaseInvertedFlatListE2e'; + +export default forwardRef(BaseInvertedFlatListE2e); +export {getViewableItems}; diff --git a/src/components/InvertedFlatList/BaseInvertedFlatList.tsx b/src/components/InvertedFlatList/BaseInvertedFlatList/index.tsx similarity index 96% rename from src/components/InvertedFlatList/BaseInvertedFlatList.tsx rename to src/components/InvertedFlatList/BaseInvertedFlatList/index.tsx index d83e54f74d66..c92203afdc74 100644 --- a/src/components/InvertedFlatList/BaseInvertedFlatList.tsx +++ b/src/components/InvertedFlatList/BaseInvertedFlatList/index.tsx @@ -25,6 +25,7 @@ function BaseInvertedFlatList(props: BaseInvertedFlatListProps, ref: Forwa return config; }, [shouldEnableAutoScrollToTopThreshold]); + console.debug(`[E2E] BaseInverted.NOT`); return ( Date: Tue, 13 Feb 2024 09:28:41 +0100 Subject: [PATCH 009/176] linking test --- src/ROUTES.ts | 4 ++ src/libs/E2E/reactNativeLaunchingTest.ts | 1 + src/libs/E2E/tests/linkingTest.e2e.ts | 69 ++++++++++++++++++++++ src/pages/home/report/ReportActionsView.js | 4 +- tests/e2e/config.js | 11 ++++ 5 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 src/libs/E2E/tests/linkingTest.e2e.ts diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 5a2ab8cfc7de..996136d12e6d 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -169,6 +169,10 @@ const ROUTES = { route: 'r/:reportID/avatar', getRoute: (reportID: string) => `r/${reportID}/avatar` as const, }, + REPORT_WITH_ID_AND_ACTION_ID: { + route: 'r/:reportID?/:reportActionID?', + getRoute: (reportID: string, reportActionID: string) => `r/${reportID}/${reportActionID}` as const, + }, EDIT_REQUEST: { route: 'r/:threadReportID/edit/:field', getRoute: (threadReportID: string, field: ValueOf) => `r/${threadReportID}/edit/${field}` as const, diff --git a/src/libs/E2E/reactNativeLaunchingTest.ts b/src/libs/E2E/reactNativeLaunchingTest.ts index 79276e7a5d75..931b41524696 100644 --- a/src/libs/E2E/reactNativeLaunchingTest.ts +++ b/src/libs/E2E/reactNativeLaunchingTest.ts @@ -38,6 +38,7 @@ const tests: Tests = { [E2EConfig.TEST_NAMES.OpenSearchPage]: require('./tests/openSearchPageTest.e2e').default, [E2EConfig.TEST_NAMES.ChatOpening]: require('./tests/chatOpeningTest.e2e').default, [E2EConfig.TEST_NAMES.ReportTyping]: require('./tests/reportTypingTest.e2e').default, + [E2EConfig.TEST_NAMES.Linking]: require('./tests/linkingTest.e2e').default, }; // Once we receive the TII measurement we know that the app is initialized and ready to be used: diff --git a/src/libs/E2E/tests/linkingTest.e2e.ts b/src/libs/E2E/tests/linkingTest.e2e.ts new file mode 100644 index 000000000000..ef370aaecf17 --- /dev/null +++ b/src/libs/E2E/tests/linkingTest.e2e.ts @@ -0,0 +1,69 @@ +import Config from 'react-native-config'; +import {getViewableItems} from '@components/InvertedFlatList/BaseInvertedFlatList/index.e2e'; +import Timing from '@libs/actions/Timing'; +import E2ELogin from '@libs/E2E/actions/e2eLogin'; +import waitForAppLoaded from '@libs/E2E/actions/waitForAppLoaded'; +import E2EClient from '@libs/E2E/client'; +import type {TestConfig} from '@libs/E2E/types'; +import getConfigValueOrThrow from '@libs/E2E/utils/getConfigValueOrThrow'; +import Navigation from '@libs/Navigation/Navigation'; +import Performance from '@libs/Performance'; +import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; + +const test = (config: TestConfig) => { + console.debug('[E2E] Logging in for comment linking'); + + const reportID = getConfigValueOrThrow('reportID', config); + const linkedReportID = getConfigValueOrThrow('linkedReportID', config); + const linkedReportActionID = getConfigValueOrThrow('linkedReportActionID', config); + + E2ELogin().then((neededLogin) => { + if (neededLogin) { + return waitForAppLoaded().then(() => E2EClient.submitTestDone()); + } + + Performance.subscribeToMeasurements((entry) => { + if (entry.name === CONST.TIMING.SIDEBAR_LOADED) { + console.debug('[E2E] Sidebar loaded, navigating to a report…'); + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(reportID)); + return; + } + + if (entry.name === CONST.TIMING.REPORT_INITIAL_RENDER) { + console.debug('[E2E] Navigating to linked report action…'); + Timing.start(CONST.TIMING.SWITCH_REPORT); + Performance.markStart(CONST.TIMING.SWITCH_REPORT); + + Navigation.navigate(ROUTES.REPORT_WITH_ID_AND_ACTION_ID.getRoute(linkedReportID, linkedReportActionID)); + return; + } + + if (entry.name === CONST.TIMING.SWITCH_REPORT) { + setTimeout(() => { + const res = getViewableItems(); + console.debug('[E2E] Viewable items retrieved, verifying correct message…'); + + if (res[0]?.item?.reportActionID === linkedReportActionID) { + E2EClient.submitTestResults({ + branch: Config.E2E_BRANCH, + name: 'Comment linking', + duration: entry.duration, + }) + .then(() => { + console.debug('[E2E] Test completed successfully, exiting…'); + E2EClient.submitTestDone(); + }) + .catch((err) => { + console.debug('[E2E] Error while submitting test results:', err); + }); + } else { + console.debug('[E2E] Message verification failed'); + } + }, 3000); + } + }); + }); +}; + +export default test; diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index 56f204ef6ffb..bcec51dba4e1 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -384,15 +384,15 @@ function ReportActionsView({reportActions: allReportActions, ...props}) { } didLayout.current = true; - Timing.end(CONST.TIMING.SWITCH_REPORT, hasCachedActions ? CONST.TIMING.WARM : CONST.TIMING.COLD); - // Capture the init measurement only once not per each chat switch as the value gets overwritten if (!ReportActionsView.initMeasured) { Performance.markEnd(CONST.TIMING.REPORT_INITIAL_RENDER); + Timing.end(CONST.TIMING.REPORT_INITIAL_RENDER); ReportActionsView.initMeasured = true; } else { Performance.markEnd(CONST.TIMING.SWITCH_REPORT); } + Timing.end(CONST.TIMING.SWITCH_REPORT, hasCachedActions ? CONST.TIMING.WARM : CONST.TIMING.COLD); }, [hasCachedActions], ); diff --git a/tests/e2e/config.js b/tests/e2e/config.js index a7447a29c954..79d4c7c4bdfa 100644 --- a/tests/e2e/config.js +++ b/tests/e2e/config.js @@ -6,6 +6,7 @@ const TEST_NAMES = { OpenSearchPage: 'Open search page TTI', ReportTyping: 'Report typing', ChatOpening: 'Chat opening', + Linking: 'Linking', }; /** @@ -85,5 +86,15 @@ module.exports = { // #announce Chat with many messages reportID: '5421294415618529', }, + [TEST_NAMES.Linking]: { + name: TEST_NAMES.Linking, + reportScreen: { + autoFocus: true, + }, + // Crowded Policy (Do Not Delete) Report, has a input bar available: + reportID: '8268282951170052', + linkedReportID: '5421294415618529', + linkedReportActionID: '2845024374735019929', + }, }, }; From a9315c4e0158438aa4bc8a2935c707828e7c2cb6 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Thu, 15 Feb 2024 16:42:37 +0100 Subject: [PATCH 010/176] migrate AttachmentView --- src/CONST.ts | 4 +- .../{index.js => index.tsx} | 30 +++--- .../AttachmentViewImage/propTypes.js | 21 ---- ...ntViewPdf.js => BaseAttachmentViewPdf.tsx} | 32 +++---- .../{index.android.js => index.android.tsx} | 10 +- .../{index.ios.js => index.ios.tsx} | 7 +- .../AttachmentViewPdf/{index.js => index.tsx} | 8 +- .../AttachmentViewPdf/propTypes.js | 28 ------ .../AttachmentView/AttachmentViewPdf/types.ts | 21 ++++ .../AttachmentView/{index.js => index.tsx} | 95 ++++++++----------- .../Attachments/AttachmentView/propTypes.js | 52 ---------- .../Attachments/AttachmentView/types.ts | 38 ++++++++ src/components/Attachments/propTypes.js | 21 ---- src/components/Attachments/types.ts | 32 +++++++ src/components/ImageView/types.ts | 2 +- 15 files changed, 171 insertions(+), 230 deletions(-) rename src/components/Attachments/AttachmentView/AttachmentViewImage/{index.js => index.tsx} (67%) mode change 100755 => 100644 delete mode 100644 src/components/Attachments/AttachmentView/AttachmentViewImage/propTypes.js rename src/components/Attachments/AttachmentView/AttachmentViewPdf/{BaseAttachmentViewPdf.js => BaseAttachmentViewPdf.tsx} (83%) rename src/components/Attachments/AttachmentView/AttachmentViewPdf/{index.android.js => index.android.tsx} (94%) rename src/components/Attachments/AttachmentView/AttachmentViewPdf/{index.ios.js => index.ios.tsx} (54%) rename src/components/Attachments/AttachmentView/AttachmentViewPdf/{index.js => index.tsx} (73%) delete mode 100644 src/components/Attachments/AttachmentView/AttachmentViewPdf/propTypes.js create mode 100644 src/components/Attachments/AttachmentView/AttachmentViewPdf/types.ts rename src/components/Attachments/AttachmentView/{index.js => index.tsx} (76%) mode change 100755 => 100644 delete mode 100644 src/components/Attachments/AttachmentView/propTypes.js create mode 100644 src/components/Attachments/AttachmentView/types.ts delete mode 100644 src/components/Attachments/propTypes.js create mode 100644 src/components/Attachments/types.ts diff --git a/src/CONST.ts b/src/CONST.ts index 5c99c5877559..fbcabdd64014 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -437,7 +437,7 @@ const CONST = { }, }, ARROW_LEFT: { - descriptionKey: null, + descriptionKey: 'arrowLeft', shortcutKey: 'ArrowLeft', modifiers: [], trigger: { @@ -447,7 +447,7 @@ const CONST = { }, }, ARROW_RIGHT: { - descriptionKey: null, + descriptionKey: 'arrowRight', shortcutKey: 'ArrowRight', modifiers: [], trigger: { diff --git a/src/components/Attachments/AttachmentView/AttachmentViewImage/index.js b/src/components/Attachments/AttachmentView/AttachmentViewImage/index.tsx old mode 100755 new mode 100644 similarity index 67% rename from src/components/Attachments/AttachmentView/AttachmentViewImage/index.js rename to src/components/Attachments/AttachmentView/AttachmentViewImage/index.tsx index 14c60458b044..9a28dfd82bc4 --- a/src/components/Attachments/AttachmentView/AttachmentViewImage/index.js +++ b/src/components/Attachments/AttachmentView/AttachmentViewImage/index.tsx @@ -1,16 +1,18 @@ import React, {memo} from 'react'; +import type AttachmentViewBaseProps from '@components/Attachments/AttachmentView/types'; import ImageView from '@components/ImageView'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; import CONST from '@src/CONST'; -import {attachmentViewImageDefaultProps, attachmentViewImagePropTypes} from './propTypes'; -const propTypes = { - ...attachmentViewImagePropTypes, - ...withLocalizePropTypes, -}; +type AttachmentViewImageProps = { + url: string | number; + + loadComplete: boolean; + + isImage: boolean; +} & AttachmentViewBaseProps; function AttachmentViewImage({ url, @@ -20,21 +22,19 @@ function AttachmentViewImage({ isSingleCarouselItem, carouselItemIndex, carouselActiveItemIndex, - isFocused, loadComplete, onPress, onError, isImage, - translate, -}) { +}: AttachmentViewImageProps) { + const {translate} = useLocalize(); const styles = useThemeStyles(); const children = ( {children} @@ -57,8 +57,6 @@ function AttachmentViewImage({ ); } -AttachmentViewImage.propTypes = propTypes; -AttachmentViewImage.defaultProps = attachmentViewImageDefaultProps; AttachmentViewImage.displayName = 'AttachmentViewImage'; -export default compose(memo, withLocalize)(AttachmentViewImage); +export default memo(AttachmentViewImage); diff --git a/src/components/Attachments/AttachmentView/AttachmentViewImage/propTypes.js b/src/components/Attachments/AttachmentView/AttachmentViewImage/propTypes.js deleted file mode 100644 index f2a275fc9a21..000000000000 --- a/src/components/Attachments/AttachmentView/AttachmentViewImage/propTypes.js +++ /dev/null @@ -1,21 +0,0 @@ -import PropTypes from 'prop-types'; -import {attachmentViewDefaultProps, attachmentViewPropTypes} from '@components/Attachments/AttachmentView/propTypes'; - -const attachmentViewImagePropTypes = { - ...attachmentViewPropTypes, - - url: PropTypes.string.isRequired, - - loadComplete: PropTypes.bool.isRequired, - - isImage: PropTypes.bool.isRequired, -}; - -const attachmentViewImageDefaultProps = { - ...attachmentViewDefaultProps, - - loadComplete: false, - isImage: false, -}; - -export {attachmentViewImagePropTypes, attachmentViewImageDefaultProps}; diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.js b/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.tsx similarity index 83% rename from src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.js rename to src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.tsx index 2f16b63aacc6..213a28d830e7 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.js +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/BaseAttachmentViewPdf.tsx @@ -1,21 +1,13 @@ -import PropTypes from 'prop-types'; import React, {memo, useCallback, useContext, useEffect} from 'react'; +import type {GestureResponderEvent} from 'react-native'; import AttachmentCarouselPagerContext from '@components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPagerContext'; import PDFView from '@components/PDFView'; -import {attachmentViewPdfDefaultProps, attachmentViewPdfPropTypes} from './propTypes'; - -const baseAttachmentViewPdfPropTypes = { - ...attachmentViewPdfPropTypes, +import type AttachmentViewPdfProps from './types'; +type BaseAttachmentViewPdfProps = { /** Triggered when the PDF's onScaleChanged event is triggered */ - onScaleChanged: PropTypes.func, -}; - -const baseAttachmentViewPdfDefaultProps = { - ...attachmentViewPdfDefaultProps, - - onScaleChanged: undefined, -}; + onScaleChanged: (scale: number) => void; +} & AttachmentViewPdfProps; function BaseAttachmentViewPdf({ file, @@ -28,7 +20,7 @@ function BaseAttachmentViewPdf({ onLoadComplete, errorLabelStyles, style, -}) { +}: BaseAttachmentViewPdfProps) { const attachmentCarouselPagerContext = useContext(AttachmentCarouselPagerContext); const isScrollEnabled = attachmentCarouselPagerContext === null ? undefined : attachmentCarouselPagerContext.isScrollEnabled; @@ -46,7 +38,7 @@ function BaseAttachmentViewPdf({ * as well as call the onScaleChanged prop of the AttachmentViewPdf component if defined. */ const onScaleChanged = useCallback( - (newScale) => { + (newScale: number) => { if (onScaleChangedProp !== undefined) { onScaleChangedProp(newScale); } @@ -66,13 +58,13 @@ function BaseAttachmentViewPdf({ * Otherwise it means that the PDF is currently zoomed in, therefore the onTap callback should be ignored */ const onPress = useCallback( - (e) => { + (e?: GestureResponderEvent | KeyboardEvent) => { if (onPressProp !== undefined) { onPressProp(e); } - if (attachmentCarouselPagerContext !== null && isScrollEnabled.value) { - attachmentCarouselPagerContext.onTap(e); + if (attachmentCarouselPagerContext !== null && isScrollEnabled?.value) { + attachmentCarouselPagerContext.onTap(); } }, [attachmentCarouselPagerContext, isScrollEnabled, onPressProp], @@ -80,6 +72,7 @@ function BaseAttachmentViewPdf({ return ( { isPanGestureActive.value = false; + if (!isScrollEnabled) { + return; + } isScrollEnabled.value = true; }); @@ -93,7 +96,4 @@ function AttachmentViewPdf(props) { ); } -AttachmentViewPdf.propTypes = attachmentViewPdfPropTypes; -AttachmentViewPdf.defaultProps = attachmentViewPdfDefaultProps; - export default memo(AttachmentViewPdf); diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.ios.js b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.ios.tsx similarity index 54% rename from src/components/Attachments/AttachmentView/AttachmentViewPdf/index.ios.js rename to src/components/Attachments/AttachmentView/AttachmentViewPdf/index.ios.tsx index 103ff292760f..79c9974bc8ce 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.ios.js +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.ios.tsx @@ -1,8 +1,8 @@ import React, {memo} from 'react'; +import type {BaseAttachmentViewPdfProps} from './BaseAttachmentViewPdf'; import BaseAttachmentViewPdf from './BaseAttachmentViewPdf'; -import {attachmentViewPdfDefaultProps, attachmentViewPdfPropTypes} from './propTypes'; -function AttachmentViewPdf(props) { +function AttachmentViewPdf(props: BaseAttachmentViewPdfProps) { return ( void; + onLoadComplete: () => void; + + /** Additional style props */ + style?: StyleProp; + + /** Styles for the error label */ + errorLabelStyles?: StyleProp; + + /** Whether this view is the active screen */ + isFocused?: boolean; +} & AttachmentViewBaseProps & + Attachment; + +export default AttachmentViewPdfProps; diff --git a/src/components/Attachments/AttachmentView/index.js b/src/components/Attachments/AttachmentView/index.tsx old mode 100755 new mode 100644 similarity index 76% rename from src/components/Attachments/AttachmentView/index.js rename to src/components/Attachments/AttachmentView/index.tsx index 33eab13f3851..033515621092 --- a/src/components/Attachments/AttachmentView/index.js +++ b/src/components/Attachments/AttachmentView/index.tsx @@ -1,82 +1,74 @@ import Str from 'expensify-common/lib/str'; -import PropTypes from 'prop-types'; import React, {memo, useState} from 'react'; +import type {StyleProp, ViewStyle} from 'react-native'; import {ActivityIndicator, ScrollView, View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; -import * as AttachmentsPropTypes from '@components/Attachments/propTypes'; +import type {Attachment, AttachmentSource} from '@components/Attachments/types'; import DistanceEReceipt from '@components/DistanceEReceipt'; import EReceipt from '@components/EReceipt'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import Text from '@components/Text'; import Tooltip from '@components/Tooltip'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import addEncryptedAuthTokenToURL from '@libs/addEncryptedAuthTokenToURL'; -import compose from '@libs/compose'; import * as TransactionUtils from '@libs/TransactionUtils'; +import type {ColorValue} from '@styles/utils/types'; import variables from '@styles/variables'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {Transaction} from '@src/types/onyx'; import AttachmentViewImage from './AttachmentViewImage'; import AttachmentViewPdf from './AttachmentViewPdf'; -import {attachmentViewDefaultProps, attachmentViewPropTypes} from './propTypes'; +import type AttachmentViewBaseProps from './types'; -const propTypes = { - ...attachmentViewPropTypes, - ...withLocalizePropTypes, +type AttachmentViewOnyxProps = { + transaction: OnyxEntry; +}; +type AttachmentViewProps = { /** URL to full-sized attachment, SVG function, or numeric static image on native platforms */ - source: AttachmentsPropTypes.attachmentSourcePropType.isRequired, + source: AttachmentSource; /** Flag to show/hide download icon */ - shouldShowDownloadIcon: PropTypes.bool, + shouldShowDownloadIcon?: boolean; /** Flag to show the loading indicator */ - shouldShowLoadingSpinnerIcon: PropTypes.bool, + shouldShowLoadingSpinnerIcon?: boolean; /** Notify parent that the UI should be modified to accommodate keyboard */ - onToggleKeyboard: PropTypes.func, + onToggleKeyboard?: () => void; /** Extra styles to pass to View wrapper */ - // eslint-disable-next-line react/forbid-prop-types - containerStyles: PropTypes.arrayOf(PropTypes.object), + containerStyles?: Array>; /** Denotes whether it is a workspace avatar or not */ - isWorkspaceAvatar: PropTypes.bool, + isWorkspaceAvatar?: boolean; /** Denotes whether it is an icon (ex: SVG) */ - maybeIcon: PropTypes.bool, + maybeIcon?: boolean; /** The id of the transaction related to the attachment */ - // eslint-disable-next-line react/no-unused-prop-types - transactionID: PropTypes.string, -}; + transactionID?: string; -const defaultProps = { - ...attachmentViewDefaultProps, - shouldShowDownloadIcon: false, - shouldShowLoadingSpinnerIcon: false, - onToggleKeyboard: () => {}, - containerStyles: [], - isWorkspaceAvatar: false, - maybeIcon: false, - transactionID: '', -}; + fallbackSource?: string | number; +} & AttachmentViewOnyxProps & + AttachmentViewBaseProps & + Attachment; function AttachmentView({ source, - file, + file = {name: ''}, isAuthTokenRequired, onPress, shouldShowLoadingSpinnerIcon, shouldShowDownloadIcon, containerStyles, onToggleKeyboard, - translate, isFocused, isUsedInCarousel, isSingleCarouselItem, @@ -87,7 +79,8 @@ function AttachmentView({ maybeIcon, fallbackSource, transaction, -}) { +}: AttachmentViewProps) { + const {translate} = useLocalize(); const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -98,10 +91,10 @@ function AttachmentView({ // Handles case where source is a component (ex: SVG) or a number // Number may represent a SVG or an image - if ((maybeIcon && typeof source === 'number') || _.isFunction(source)) { - let iconFillColor = ''; - let additionalStyles = []; - if (isWorkspaceAvatar) { + if ((maybeIcon && typeof source === 'number') ?? typeof source === 'function') { + let iconFillColor: ColorValue | undefined = ''; + let additionalStyles: ViewStyle[] = []; + if (isWorkspaceAvatar && file) { const defaultWorkspaceAvatarColor = StyleUtils.getDefaultWorkspaceAvatarColor(file.name); iconFillColor = defaultWorkspaceAvatarColor.fill; additionalStyles = [defaultWorkspaceAvatarColor]; @@ -118,7 +111,7 @@ function AttachmentView({ ); } - if (TransactionUtils.hasEReceipt(transaction)) { + if (TransactionUtils.hasEReceipt(transaction) && transaction) { return ( + - {file && file.name} + {file?.name} {!shouldShowLoadingSpinnerIcon && shouldShowDownloadIcon && ( @@ -223,16 +216,10 @@ function AttachmentView({ ); } -AttachmentView.propTypes = propTypes; -AttachmentView.defaultProps = defaultProps; AttachmentView.displayName = 'AttachmentView'; -export default compose( - memo, - withLocalize, - withOnyx({ - transaction: { - key: ({transactionID}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, - }, - }), -)(AttachmentView); +export default withOnyx({ + transaction: { + key: ({transactionID}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, + }, +})(memo(AttachmentView)); diff --git a/src/components/Attachments/AttachmentView/propTypes.js b/src/components/Attachments/AttachmentView/propTypes.js deleted file mode 100644 index d78bed8526b8..000000000000 --- a/src/components/Attachments/AttachmentView/propTypes.js +++ /dev/null @@ -1,52 +0,0 @@ -import PropTypes from 'prop-types'; -import * as AttachmentsPropTypes from '@components/Attachments/propTypes'; - -const attachmentViewPropTypes = { - /** Whether source url requires authentication */ - isAuthTokenRequired: PropTypes.bool, - - /** File object can be an instance of File or Object */ - file: AttachmentsPropTypes.attachmentFilePropType, - - /** Whether this view is the active screen */ - isFocused: PropTypes.bool, - - /** Whether this AttachmentView is shown as part of a AttachmentCarousel */ - isUsedInCarousel: PropTypes.bool, - - /** When "isUsedInCarousel" is set to true, determines whether there is only one item in the carousel */ - isSingleCarouselItem: PropTypes.bool, - - /** Whether this AttachmentView is shown as part of an AttachmentModal */ - isUsedInAttachmentModal: PropTypes.bool, - - /** The index of the carousel item */ - carouselItemIndex: PropTypes.number, - - /** The index of the currently active carousel item */ - carouselActiveItemIndex: PropTypes.number, - - /** Function for handle on press */ - onPress: PropTypes.func, - - /** Handles scale changed event */ - onScaleChanged: PropTypes.func, -}; - -const attachmentViewDefaultProps = { - isAuthTokenRequired: false, - file: { - name: '', - }, - isFocused: false, - isUsedInCarousel: false, - isSingleCarouselItem: false, - carouselItemIndex: 0, - carouselActiveItemIndex: 0, - isSingleElement: false, - isUsedInAttachmentModal: false, - onPress: undefined, - onScaleChanged: () => {}, -}; - -export {attachmentViewPropTypes, attachmentViewDefaultProps}; diff --git a/src/components/Attachments/AttachmentView/types.ts b/src/components/Attachments/AttachmentView/types.ts new file mode 100644 index 000000000000..21b3a0c01cfd --- /dev/null +++ b/src/components/Attachments/AttachmentView/types.ts @@ -0,0 +1,38 @@ +import type {GestureResponderEvent} from 'react-native'; +import type {AttachmentFile} from '@components/Attachments/types'; + +type AttachmentViewBaseProps = { + /** Whether this view is the active screen */ + isFocused?: boolean; + + /** Whether this AttachmentView is shown as part of a AttachmentCarousel */ + isUsedInCarousel?: boolean; + + /** File object can be an instance of File or Object */ + file: AttachmentFile; + + isAuthTokenRequired?: boolean; + + /** When "isUsedInCarousel" is set to true, determines whether there is only one item in the carousel */ + isSingleCarouselItem?: boolean; + + /** Whether this AttachmentView is shown as part of an AttachmentModal */ + isUsedInAttachmentModal?: boolean; + + /** The index of the carousel item */ + carouselItemIndex?: number; + + /** The index of the currently active carousel item */ + carouselActiveItemIndex?: number; + + /** Function for handle on press */ + onPress?: (e?: GestureResponderEvent | KeyboardEvent) => void; + + /** Function for handle on error */ + onError?: () => void; + + /** Handles scale changed event */ + onScaleChanged?: (scale: number) => void; +}; + +export default AttachmentViewBaseProps; diff --git a/src/components/Attachments/propTypes.js b/src/components/Attachments/propTypes.js deleted file mode 100644 index 13adc468ce64..000000000000 --- a/src/components/Attachments/propTypes.js +++ /dev/null @@ -1,21 +0,0 @@ -import PropTypes from 'prop-types'; - -const attachmentSourcePropType = PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.number]); -const attachmentFilePropType = PropTypes.shape({ - name: PropTypes.string.isRequired, -}); - -const attachmentPropType = PropTypes.shape({ - /** Whether source url requires authentication */ - isAuthTokenRequired: PropTypes.bool, - - /** URL to full-sized attachment, SVG function, or numeric static image on native platforms */ - source: attachmentSourcePropType.isRequired, - - /** File object can be an instance of File or Object */ - file: attachmentFilePropType.isRequired, -}); - -const attachmentsPropType = PropTypes.arrayOf(attachmentPropType); - -export {attachmentSourcePropType, attachmentFilePropType, attachmentPropType, attachmentsPropType}; diff --git a/src/components/Attachments/types.ts b/src/components/Attachments/types.ts new file mode 100644 index 000000000000..bb3848e7bbe6 --- /dev/null +++ b/src/components/Attachments/types.ts @@ -0,0 +1,32 @@ +// This can be either a string, function, or number +type AttachmentSource = string | number | React.FC; + +// Object shape for file where name is a required string +type AttachmentFile = { + name: string; +}; + +// The object shape for the attachment +type Attachment = { + /** Report action ID of the attachment */ + reportActionID?: string; + + /** Whether source url requires authentication */ + isAuthTokenRequired?: boolean; + + /** URL to full-sized attachment, SVG function, or numeric static image on native platforms */ + source: AttachmentSource; + + /** File object can be an instance of File or Object */ + file: AttachmentFile; + + /** Whether the attachment has been flagged */ + hasBeenFlagged?: boolean; + + /** The id of the transaction related to the attachment */ + transactionID?: string; + + isReceipt?: boolean; +}; + +export type {AttachmentSource, AttachmentFile, Attachment}; diff --git a/src/components/ImageView/types.ts b/src/components/ImageView/types.ts index b85115874a5a..9ff983c3609a 100644 --- a/src/components/ImageView/types.ts +++ b/src/components/ImageView/types.ts @@ -6,7 +6,7 @@ type ImageViewProps = { isAuthTokenRequired?: boolean; /** URL to full-sized image */ - url: string; + url: string | number; /** image file name */ fileName: string; From 0f696ce583c5757e6cab8be8f1e34a5c6aa23ab6 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Thu, 15 Feb 2024 16:43:29 +0100 Subject: [PATCH 011/176] migrate AttachmentCarousel --- ....js => AttachmentCarouselCellRenderer.tsx} | 14 ++--- ...CarouselActions.js => CarouselActions.tsx} | 22 +++----- ...CarouselButtons.js => CarouselButtons.tsx} | 32 +++++------- .../{CarouselItem.js => CarouselItem.tsx} | 51 +++++-------------- ...ort.js => extractAttachmentsFromReport.ts} | 23 ++++----- ...CarouselArrows.js => useCarouselArrows.ts} | 11 ++-- 6 files changed, 51 insertions(+), 102 deletions(-) rename src/components/Attachments/AttachmentCarousel/{AttachmentCarouselCellRenderer.js => AttachmentCarouselCellRenderer.tsx} (71%) rename src/components/Attachments/AttachmentCarousel/{CarouselActions.js => CarouselActions.tsx} (63%) rename src/components/Attachments/AttachmentCarousel/{CarouselButtons.js => CarouselButtons.tsx} (76%) rename src/components/Attachments/AttachmentCarousel/{CarouselItem.js => CarouselItem.tsx} (70%) rename src/components/Attachments/AttachmentCarousel/{extractAttachmentsFromReport.js => extractAttachmentsFromReport.ts} (73%) rename src/components/Attachments/AttachmentCarousel/{useCarouselArrows.js => useCarouselArrows.ts} (77%) diff --git a/src/components/Attachments/AttachmentCarousel/AttachmentCarouselCellRenderer.js b/src/components/Attachments/AttachmentCarousel/AttachmentCarouselCellRenderer.tsx similarity index 71% rename from src/components/Attachments/AttachmentCarousel/AttachmentCarouselCellRenderer.js rename to src/components/Attachments/AttachmentCarousel/AttachmentCarouselCellRenderer.tsx index f4cbffc0e1e4..08d0b7f271d4 100644 --- a/src/components/Attachments/AttachmentCarousel/AttachmentCarouselCellRenderer.js +++ b/src/components/Attachments/AttachmentCarousel/AttachmentCarouselCellRenderer.tsx @@ -1,19 +1,15 @@ -import PropTypes from 'prop-types'; import React from 'react'; +import type {StyleProp, ViewStyle} from 'react-native'; import {PixelRatio, View} from 'react-native'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -const propTypes = { +type AttachmentCarouselCellRendererProps = { /** Cell Container styles */ - style: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]), + style: StyleProp; }; -const defaultProps = { - style: [], -}; - -function AttachmentCarouselCellRenderer(props) { +function AttachmentCarouselCellRenderer(props: AttachmentCarouselCellRendererProps) { const styles = useThemeStyles(); const {windowWidth, isSmallScreenWidth} = useWindowDimensions(); const modalStyles = styles.centeredModalStyles(isSmallScreenWidth, true); @@ -28,8 +24,6 @@ function AttachmentCarouselCellRenderer(props) { ); } -AttachmentCarouselCellRenderer.propTypes = propTypes; -AttachmentCarouselCellRenderer.defaultProps = defaultProps; AttachmentCarouselCellRenderer.displayName = 'AttachmentCarouselCellRenderer'; export default React.memo(AttachmentCarouselCellRenderer); diff --git a/src/components/Attachments/AttachmentCarousel/CarouselActions.js b/src/components/Attachments/AttachmentCarousel/CarouselActions.tsx similarity index 63% rename from src/components/Attachments/AttachmentCarousel/CarouselActions.js rename to src/components/Attachments/AttachmentCarousel/CarouselActions.tsx index cf5309222c4e..45fed45e1670 100644 --- a/src/components/Attachments/AttachmentCarousel/CarouselActions.js +++ b/src/components/Attachments/AttachmentCarousel/CarouselActions.tsx @@ -1,24 +1,19 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; import {useEffect} from 'react'; import KeyboardShortcut from '@libs/KeyboardShortcut'; import CONST from '@src/CONST'; -const propTypes = { +type CarouselActionsProps = { /** Callback to cycle through attachments */ - onCycleThroughAttachments: PropTypes.func.isRequired, + onCycleThroughAttachments: (deltaSlide: number) => void; }; -function CarouselActions({onCycleThroughAttachments}) { +function CarouselActions({onCycleThroughAttachments}: CarouselActionsProps) { useEffect(() => { const shortcutLeftConfig = CONST.KEYBOARD_SHORTCUTS.ARROW_LEFT; const unsubscribeLeftKey = KeyboardShortcut.subscribe( shortcutLeftConfig.shortcutKey, - (e) => { - if (lodashGet(e, 'target.blur')) { - // prevents focus from highlighting around the modal - e.target.blur(); - } + (e?: KeyboardEvent) => { + (e as unknown as React.FocusEvent)?.target?.blur(); onCycleThroughAttachments(-1); }, @@ -30,10 +25,7 @@ function CarouselActions({onCycleThroughAttachments}) { const unsubscribeRightKey = KeyboardShortcut.subscribe( shortcutRightConfig.shortcutKey, (e) => { - if (lodashGet(e, 'target.blur')) { - // prevents focus from highlighting around the modal - e.target.blur(); - } + (e as unknown as React.FocusEvent)?.target?.blur(); onCycleThroughAttachments(1); }, @@ -50,6 +42,4 @@ function CarouselActions({onCycleThroughAttachments}) { return null; } -CarouselActions.propTypes = propTypes; - export default CarouselActions; diff --git a/src/components/Attachments/AttachmentCarousel/CarouselButtons.js b/src/components/Attachments/AttachmentCarousel/CarouselButtons.tsx similarity index 76% rename from src/components/Attachments/AttachmentCarousel/CarouselButtons.js rename to src/components/Attachments/AttachmentCarousel/CarouselButtons.tsx index 1847d30ede22..efa6a7979ada 100644 --- a/src/components/Attachments/AttachmentCarousel/CarouselButtons.js +++ b/src/components/Attachments/AttachmentCarousel/CarouselButtons.tsx @@ -1,8 +1,6 @@ -import PropTypes from 'prop-types'; import React from 'react'; import {View} from 'react-native'; -import _ from 'underscore'; -import * as AttachmentCarouselViewPropTypes from '@components/Attachments/propTypes'; +import type {Attachment} from '@components/Attachments/types'; import Button from '@components/Button'; import * as Expensicons from '@components/Icon/Expensicons'; import Tooltip from '@components/Tooltip'; @@ -11,36 +9,32 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -const propTypes = { +type CarouselButtonsProps = { /** Where the arrows should be visible */ - shouldShowArrows: PropTypes.bool.isRequired, + shouldShowArrows: boolean; /** The current page index */ - page: PropTypes.number.isRequired, + page: number; /** The attachments from the carousel */ - attachments: AttachmentCarouselViewPropTypes.attachmentsPropType.isRequired, + attachments: Attachment[]; /** Callback to go one page back */ - onBack: PropTypes.func.isRequired, + onBack: () => void; + /** Callback to go one page forward */ - onForward: PropTypes.func.isRequired, + onForward: () => void; - autoHideArrow: PropTypes.func, - cancelAutoHideArrow: PropTypes.func, -}; + autoHideArrow?: () => void; -const defaultProps = { - autoHideArrow: () => {}, - cancelAutoHideArrow: () => {}, + cancelAutoHideArrow?: () => void; }; -function CarouselButtons({page, attachments, shouldShowArrows, onBack, onForward, cancelAutoHideArrow, autoHideArrow}) { +function CarouselButtons({page, attachments, shouldShowArrows, onBack, onForward, cancelAutoHideArrow, autoHideArrow}: CarouselButtonsProps) { const theme = useTheme(); const styles = useThemeStyles(); const isBackDisabled = page === 0; - const isForwardDisabled = page === _.size(attachments) - 1; - + const isForwardDisabled = page === attachments.length - 1; const {translate} = useLocalize(); const {isSmallScreenWidth} = useWindowDimensions(); @@ -82,8 +76,6 @@ function CarouselButtons({page, attachments, shouldShowArrows, onBack, onForward ) : null; } -CarouselButtons.propTypes = propTypes; -CarouselButtons.defaultProps = defaultProps; CarouselButtons.displayName = 'CarouselButtons'; export default CarouselButtons; diff --git a/src/components/Attachments/AttachmentCarousel/CarouselItem.js b/src/components/Attachments/AttachmentCarousel/CarouselItem.tsx similarity index 70% rename from src/components/Attachments/AttachmentCarousel/CarouselItem.js rename to src/components/Attachments/AttachmentCarousel/CarouselItem.tsx index 5552f15320f3..973dfa96dddf 100644 --- a/src/components/Attachments/AttachmentCarousel/CarouselItem.js +++ b/src/components/Attachments/AttachmentCarousel/CarouselItem.tsx @@ -1,8 +1,8 @@ -import PropTypes from 'prop-types'; import React, {useContext, useState} from 'react'; +import type {StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; import AttachmentView from '@components/Attachments/AttachmentView'; -import * as AttachmentsPropTypes from '@components/Attachments/propTypes'; +import type {Attachment} from '@components/Attachments/types'; import Button from '@components/Button'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import SafeAreaConsumer from '@components/SafeAreaConsumer'; @@ -12,56 +12,31 @@ import useThemeStyles from '@hooks/useThemeStyles'; import ReportAttachmentsContext from '@pages/home/report/ReportAttachmentsContext'; import CONST from '@src/CONST'; -const propTypes = { +type CarouselItemProps = { /** Attachment required information such as the source and file name */ - item: PropTypes.shape({ - /** Report action ID of the attachment */ - reportActionID: PropTypes.string, - - /** Whether source URL requires authentication */ - isAuthTokenRequired: PropTypes.bool, - - /** URL to full-sized attachment or SVG function */ - source: AttachmentsPropTypes.attachmentSourcePropType.isRequired, - - /** Additional information about the attachment file */ - file: PropTypes.shape({ - /** File name of the attachment */ - name: PropTypes.string.isRequired, - }).isRequired, - - /** Whether the attachment has been flagged */ - hasBeenFlagged: PropTypes.bool, - - /** The id of the transaction related to the attachment */ - transactionID: PropTypes.string, - }).isRequired, + item: Attachment; /** Whether there is only one element in the attachment carousel */ - isSingleItem: PropTypes.bool.isRequired, + isSingleItem: boolean; /** The index of the carousel item */ - index: PropTypes.number.isRequired, + index?: number; /** The index of the currently active carousel item */ - activeIndex: PropTypes.number.isRequired, + activeIndex?: number; /** onPress callback */ - onPress: PropTypes.func, -}; - -const defaultProps = { - onPress: undefined, + onPress?: () => void; }; -function CarouselItem({item, index, activeIndex, isSingleItem, onPress}) { +function CarouselItem({item, index, activeIndex, isSingleItem, onPress}: CarouselItemProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const {isAttachmentHidden} = useContext(ReportAttachmentsContext); // eslint-disable-next-line es/no-nullish-coalescing-operators - const [isHidden, setIsHidden] = useState(() => isAttachmentHidden(item.reportActionID) ?? item.hasBeenFlagged); + const [isHidden, setIsHidden] = useState(() => (item.reportActionID ? isAttachmentHidden(item.reportActionID) : item.hasBeenFlagged)); - const renderButton = (style) => ( + const renderButton = (style: StyleProp) => (