From ffbb21948822a432565e394a5132c908a100592d Mon Sep 17 00:00:00 2001 From: Thomas Coldwell <31568400+thomas-coldwell@users.noreply.github.com> Date: Thu, 1 Dec 2022 17:02:05 +0000 Subject: [PATCH 01/18] fix: naming `FastImage` -> `Image` --- src/components/Avatar.js | 4 +-- src/components/FastImage/index.native.js | 9 ------ src/components/{FastImage => Image}/index.js | 12 ++++---- src/components/Image/index.native.js | 9 ++++++ src/components/ImageWithSizeCalculation.js | 30 ++++---------------- 5 files changed, 23 insertions(+), 41 deletions(-) delete mode 100644 src/components/FastImage/index.native.js rename src/components/{FastImage => Image}/index.js (84%) create mode 100644 src/components/Image/index.native.js diff --git a/src/components/Avatar.js b/src/components/Avatar.js index d0a6c648acfe..de64295274fe 100644 --- a/src/components/Avatar.js +++ b/src/components/Avatar.js @@ -9,7 +9,7 @@ import CONST from '../CONST'; import * as StyleUtils from '../styles/StyleUtils'; import * as Expensicons from './Icon/Expensicons'; import getAvatarDefaultSource from '../libs/getAvatarDefaultSource'; -import FastImage from './FastImage'; +import Image from './Image'; const propTypes = { /** Source for the avatar. Can be a URL or an icon. */ @@ -73,7 +73,7 @@ class Avatar extends PureComponent { /> ) : ( - ; - -FastImage.displayName = 'FastImage'; -FastImage.propTypes = RNFastImage.propTypes; -FastImage.resizeMode = RNFastImage.resizeMode; -export default FastImage; diff --git a/src/components/FastImage/index.js b/src/components/Image/index.js similarity index 84% rename from src/components/FastImage/index.js rename to src/components/Image/index.js index 4df8137bb526..3a34e21ea1ba 100644 --- a/src/components/FastImage/index.js +++ b/src/components/Image/index.js @@ -1,5 +1,5 @@ import React from 'react'; -import {Image} from 'react-native'; +import {Image as RNImage} from 'react-native'; import addEncryptedAuthTokenToURL from '../../libs/addEncryptedAuthTokenToURL'; const RESIZE_MODES = { @@ -9,7 +9,7 @@ const RESIZE_MODES = { center: 'center', }; -class FastImage extends React.Component { +class Image extends React.Component { constructor(props) { super(props); @@ -52,10 +52,10 @@ class FastImage extends React.Component { const { source, onLoad, ...rest } = this.props; // eslint-disable-next-line - return ; + return ; } } -FastImage.propTypes = Image.propTypes; -FastImage.resizeMode = RESIZE_MODES; -export default FastImage; +Image.propTypes = RNImage.propTypes; +Image.resizeMode = RESIZE_MODES; +export default Image; diff --git a/src/components/Image/index.native.js b/src/components/Image/index.native.js new file mode 100644 index 000000000000..c68120f3c397 --- /dev/null +++ b/src/components/Image/index.native.js @@ -0,0 +1,9 @@ +import RNFastImage from '@pieter-pot/react-native-fast-image'; + +// eslint-disable-next-line +const Image = (props) => ; + +Image.displayName = 'FastImage'; +Image.propTypes = RNFastImage.propTypes; +Image.resizeMode = RNFastImage.resizeMode; +export default Image; diff --git a/src/components/ImageWithSizeCalculation.js b/src/components/ImageWithSizeCalculation.js index f7a32be95368..56412a5e6c59 100644 --- a/src/components/ImageWithSizeCalculation.js +++ b/src/components/ImageWithSizeCalculation.js @@ -1,14 +1,11 @@ import React, {PureComponent} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; -import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; import Log from '../libs/Log'; import styles from '../styles/styles'; import FullscreenLoadingIndicator from './FullscreenLoadingIndicator'; -import ONYXKEYS from '../ONYXKEYS'; -import chatAttachmentTokenHeaders from '../libs/chatAttachmentTokenHeaders'; -import FastImage from './FastImage'; +import Image from './Image'; const propTypes = { /** Url for image to display */ @@ -23,22 +20,12 @@ const propTypes = { /** Does the image require an authToken? */ isAuthTokenRequired: PropTypes.bool, - - /* Onyx props */ - /** Session object */ - session: PropTypes.shape({ - /** An error message to display to the user */ - encryptedAuthToken: PropTypes.string, - }), }; const defaultProps = { style: {}, onMeasure: () => {}, isAuthTokenRequired: false, - session: { - encryptedAuthToken: false, - }, }; /** @@ -83,7 +70,6 @@ class ImageWithSizeCalculation extends PureComponent { } render() { - const headers = this.props.isAuthTokenRequired ? chatAttachmentTokenHeaders() : undefined; return ( - Date: Thu, 1 Dec 2022 19:01:40 +0000 Subject: [PATCH 02/18] fix: Add auth token at Image component level --- src/components/Image/imagePropTypes.js | 56 ++++++++++++++++++++ src/components/Image/index.js | 37 ++++++++----- src/components/Image/index.native.js | 67 +++++++++++++++++++++--- src/components/Image/resizeModes.js | 8 +++ src/components/ImageView/index.js | 31 ++++------- src/components/ImageView/index.native.js | 11 ++-- 6 files changed, 162 insertions(+), 48 deletions(-) create mode 100644 src/components/Image/imagePropTypes.js create mode 100644 src/components/Image/resizeModes.js diff --git a/src/components/Image/imagePropTypes.js b/src/components/Image/imagePropTypes.js new file mode 100644 index 000000000000..e4c4ef0d9461 --- /dev/null +++ b/src/components/Image/imagePropTypes.js @@ -0,0 +1,56 @@ +import PropTypes from 'prop-types'; +import stylePropTypes from '../../styles/stylePropTypes'; +import RESIZE_MODES from './resizeModes'; + +const propTypes = { + + /** Styles for the Image */ + style: stylePropTypes, + + /** The static asset or URI source of the image */ + source: PropTypes.oneOfType([ + PropTypes.number.isRequired, + PropTypes.shape({ + uri: PropTypes.string.isRequired, + // eslint-disable-next-line react/forbid-prop-types + headers: PropTypes.object, + }), + ]), + + /** Should an auth token be included in the image request */ + isAuthTokenRequired: PropTypes.bool, + + /** How should the image fit within its container */ + resizeMode: PropTypes.string, + + /** Event for when the image begins loading */ + onLoadStart: PropTypes.func, + + /** Event for when the image finishes loading */ + onLoadEnd: PropTypes.func, + + /** Event for when the image is fully loaded and returns the natural dimensions of the image */ + onLoad: PropTypes.func, + + /* Onyx Props */ + /** Session info for the currently logged in user. */ + session: PropTypes.shape({ + + /** Currently logged in user authToken */ + authToken: PropTypes.string, + }), +}; + +const defaultProps = { + session: { + authToken: null, + }, + isAuthTokenRequired: false, + source: null, + resizeMode: RESIZE_MODES.cover, + onLoadStart: () => {}, + onLoadEnd: () => {}, + onLoad: () => {}, +}; + +export {propTypes, defaultProps}; diff --git a/src/components/Image/index.js b/src/components/Image/index.js index 3a34e21ea1ba..ff2f6041db45 100644 --- a/src/components/Image/index.js +++ b/src/components/Image/index.js @@ -1,13 +1,10 @@ import React from 'react'; import {Image as RNImage} from 'react-native'; -import addEncryptedAuthTokenToURL from '../../libs/addEncryptedAuthTokenToURL'; - -const RESIZE_MODES = { - contain: 'contain', - cover: 'cover', - stretch: 'stretch', - center: 'center', -}; +import {withOnyx} from 'react-native-onyx'; +import lodashGet from 'lodash/get'; +import ONYXKEYS from '../../ONYXKEYS'; +import {defaultProps, propTypes} from './imagePropTypes'; +import RESIZE_MODES from './resizeModes'; class Image extends React.Component { constructor(props) { @@ -31,18 +28,23 @@ class Image extends React.Component { configureImageSource() { const source = this.props.source; + const isAuthTokenRequired = this.props.isAuthTokenRequired; let imageSource = source; - if (typeof source !== 'number' && source.headers != null) { - imageSource = {uri: addEncryptedAuthTokenToURL(source.uri)}; + if (typeof source !== 'number' && isAuthTokenRequired) { + const authToken = lodashGet(this.props, 'session.encryptedAuthToken', null); + imageSource = {uri: `${source.uri}?encryptedAuthToken=${encodeURIComponent(authToken)}`}; } this.setState({imageSource}); + + // If an onLoad callback was specified then manually call it and pass + // the natural image dimensions to match the native API if (this.props.onLoad == null) { return; } const uri = typeof imageSource === 'number' ? Image.resolveAssetSource(imageSource).uri : imageSource.uri; - Image.getSize(uri, (width, height) => { + RNImage.getSize(uri, (width, height) => { this.props.onLoad({nativeEvent: {width, height}}); }); } @@ -56,6 +58,13 @@ class Image extends React.Component { } } -Image.propTypes = RNImage.propTypes; -Image.resizeMode = RESIZE_MODES; -export default Image; +Image.propTypes = propTypes; +Image.defaultProps = defaultProps; + +const ImageWithOnyx = withOnyx({ + session: { + key: ONYXKEYS.SESSION, + }, +})(Image); +ImageWithOnyx.resizeMode = RESIZE_MODES; +export default ImageWithOnyx; diff --git a/src/components/Image/index.native.js b/src/components/Image/index.native.js index c68120f3c397..1c05d37b9915 100644 --- a/src/components/Image/index.native.js +++ b/src/components/Image/index.native.js @@ -1,9 +1,64 @@ +import React from 'react'; import RNFastImage from '@pieter-pot/react-native-fast-image'; +import {withOnyx} from 'react-native-onyx'; +import lodashGet from 'lodash/get'; +import CONST from '../../CONST'; +import ONYXKEYS from '../../ONYXKEYS'; +import {defaultProps, propTypes} from './imagePropTypes'; +import RESIZE_MODES from './resizeModes'; -// eslint-disable-next-line -const Image = (props) => ; +class Image extends React.Component { + constructor(props) { + super(props); -Image.displayName = 'FastImage'; -Image.propTypes = RNFastImage.propTypes; -Image.resizeMode = RNFastImage.resizeMode; -export default Image; + this.state = { + imageSource: undefined, + }; + } + + componentDidMount() { + this.configureImageSource(); + } + + componentDidUpdate(prevProps) { + if (prevProps.source === this.props.source) { + return; + } + this.configureImageSource(); + } + + configureImageSource() { + const source = this.props.source; + const isAuthTokenRequired = this.props.isAuthTokenRequired; + let imageSource = source; + if (typeof source !== 'number' && isAuthTokenRequired) { + const authToken = lodashGet(this.props, 'session.encryptedAuthToken', null); + imageSource = { + ...source, + headers: authToken ? { + [CONST.CHAT_ATTACHMENT_TOKEN_KEY]: authToken, + } : null, + }; + } + this.setState({imageSource}); + } + + render() { + // eslint-disable-next-line + const { source, ...rest } = this.props; + + // eslint-disable-next-line + return ; + } +} + +Image.propTypes = propTypes; +Image.defaultProps = defaultProps; + +const ImageWithOnyx = withOnyx({ + session: { + key: ONYXKEYS.SESSION, + }, +})(Image); +ImageWithOnyx.resizeMode = RESIZE_MODES; +export default ImageWithOnyx; diff --git a/src/components/Image/resizeModes.js b/src/components/Image/resizeModes.js new file mode 100644 index 000000000000..e6cc699a2fe3 --- /dev/null +++ b/src/components/Image/resizeModes.js @@ -0,0 +1,8 @@ +const RESIZE_MODES = { + contain: 'contain', + cover: 'cover', + stretch: 'stretch', + center: 'center', +}; + +export default RESIZE_MODES; diff --git a/src/components/ImageView/index.js b/src/components/ImageView/index.js index ebf8d9faee95..67a639e33185 100644 --- a/src/components/ImageView/index.js +++ b/src/components/ImageView/index.js @@ -3,16 +3,12 @@ import PropTypes from 'prop-types'; import { View, Pressable, } from 'react-native'; -import {withOnyx} from 'react-native-onyx'; -import FastImage from '../FastImage'; +import Image from '../Image'; import styles from '../../styles/styles'; import * as StyleUtils from '../../styles/StyleUtils'; import canUseTouchScreen from '../../libs/canUseTouchscreen'; import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions'; import FullscreenLoadingIndicator from '../FullscreenLoadingIndicator'; -import compose from '../../libs/compose'; -import ONYXKEYS from '../../ONYXKEYS'; -import chatAttachmentTokenHeaders from '../../libs/chatAttachmentTokenHeaders'; const propTypes = { @@ -231,18 +227,15 @@ class ImageView extends PureComponent { } render() { - const headers = this.props.isAuthTokenRequired ? chatAttachmentTokenHeaders() : undefined; if (this.canUseTouchScreen) { return ( - 1 ? FastImage.resizeMode.center : FastImage.resizeMode.contain} + resizeMode={this.state.zoomScale > 1 ? Image.resizeMode.center : Image.resizeMode.contain} onLoadStart={this.imageLoadingStart} onLoadEnd={this.imageLoadingEnd} onLoad={this.imageLoad} @@ -285,16 +278,14 @@ class ImageView extends PureComponent { onPressIn={this.onContainerPressIn} onPress={this.onContainerPress} > - - From a219d1b2bd2a0d9333c72d9a50e4339ac81c1839 Mon Sep 17 00:00:00 2001 From: Thomas Coldwell <31568400+thomas-coldwell@users.noreply.github.com> Date: Tue, 3 Jan 2023 12:32:59 -0800 Subject: [PATCH 03/18] fix: Change to upstream `react-native-fast-image` package --- ios/Podfile.lock | 4 ++-- package-lock.json | 32 ++++++++++++++-------------- package.json | 2 +- src/components/Image/index.native.js | 2 +- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index c696014a9b31..b4226a18fb71 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -472,7 +472,7 @@ PODS: - React-Core - react-native-image-manipulator (1.0.5): - React - - react-native-image-picker (4.10.1): + - react-native-image-picker (4.10.2): - React-Core - react-native-netinfo (8.3.1): - React-Core @@ -980,7 +980,7 @@ SPEC CHECKSUMS: react-native-document-picker: f68191637788994baed5f57d12994aa32cf8bf88 react-native-flipper: dc5290261fbeeb2faec1bdc57ae6dd8d562e1de4 react-native-image-manipulator: c48f64221cfcd46e9eec53619c4c0374f3328a56 - react-native-image-picker: f2ab1215d17bcfe27b0eb6417cc236fd1f4775e7 + react-native-image-picker: bf34f3f516d139ed3e24c5f5a381a91819e349ea react-native-netinfo: 1a6035d3b9780221d407c277ebfb5722ace00658 react-native-pdf: 33c622cbdf776a649929e8b9d1ce2d313347c4fa react-native-plaid-link-sdk: 77052f329310ff5a36ddda276793f40d27c02bc4 diff --git a/package-lock.json b/package-lock.json index 518fbe4f0b6f..19a22a3f676a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,6 @@ "@gorhom/portal": "^1.0.14", "@oguzhnatly/react-native-image-manipulator": "github:Expensify/react-native-image-manipulator#c5f654fc9d0ad7cc5b89d50b34ecf8b0e3f4d050", "@onfido/react-native-sdk": "7.0.1", - "@pieter-pot/react-native-fast-image": "8.5.11", "@react-native-async-storage/async-storage": "^1.17.10", "@react-native-community/cameraroll": "git+https://github.com/react-native-cameraroll/react-native-cameraroll.git#3f0aed96db68e134f199171c7b06c1b4d6cb382b", "@react-native-community/clipboard": "^1.5.1", @@ -62,6 +61,7 @@ "react-native-collapsible": "^1.6.0", "react-native-config": "^1.4.5", "react-native-document-picker": "^8.0.0", + "react-native-fast-image": "^8.6.3", "react-native-gesture-handler": "2.6.0", "react-native-google-places-autocomplete": "git+https://github.com/Expensify/react-native-google-places-autocomplete.git#3bbd17d63e6c38d38d857b50f6037c1c0376ff06", "react-native-haptic-feedback": "^1.13.0", @@ -4179,15 +4179,6 @@ "react-native": ">=0.68.2 <1.0.x" } }, - "node_modules/@pieter-pot/react-native-fast-image": { - "version": "8.5.11", - "resolved": "https://registry.npmjs.org/@pieter-pot/react-native-fast-image/-/react-native-fast-image-8.5.11.tgz", - "integrity": "sha512-7kcUi3UuKpVVyk32dHZsaxSbU+xKkQUscyG4umNvHXoTxDFPx0RsmkKdaDd8EPAKaBxT1Y7ljb7veYAkcXlRQg==", - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, "node_modules/@pmmmwh/react-refresh-webpack-plugin": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.7.tgz", @@ -35353,6 +35344,15 @@ } } }, + "node_modules/react-native-fast-image": { + "version": "8.6.3", + "resolved": "https://registry.npmjs.org/react-native-fast-image/-/react-native-fast-image-8.6.3.tgz", + "integrity": "sha512-Sdw4ESidXCXOmQ9EcYguNY2swyoWmx53kym2zRsvi+VeFCHEdkO+WG1DK+6W81juot40bbfLNhkc63QnWtesNg==", + "peerDependencies": { + "react": "^17 || ^18", + "react-native": ">=0.60.0" + } + }, "node_modules/react-native-flipper": { "version": "0.159.0", "resolved": "https://gitpkg.now.sh/facebook/flipper/react-native/react-native-flipper?9cacc9b59402550eae866e0e81e5f0c2f8203e6b", @@ -45726,12 +45726,6 @@ "integrity": "sha512-nhjByw/YyTACvkDWX2QtCzYmqkrDtSBJxYYgJjPuKvPRVIJhrny3bIm0DzAi1hWyIM2ZsKW/MSQxerGhR9FQaw==", "requires": {} }, - "@pieter-pot/react-native-fast-image": { - "version": "8.5.11", - "resolved": "https://registry.npmjs.org/@pieter-pot/react-native-fast-image/-/react-native-fast-image-8.5.11.tgz", - "integrity": "sha512-7kcUi3UuKpVVyk32dHZsaxSbU+xKkQUscyG4umNvHXoTxDFPx0RsmkKdaDd8EPAKaBxT1Y7ljb7veYAkcXlRQg==", - "requires": {} - }, "@pmmmwh/react-refresh-webpack-plugin": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.7.tgz", @@ -69747,6 +69741,12 @@ "invariant": "^2.2.4" } }, + "react-native-fast-image": { + "version": "8.6.3", + "resolved": "https://registry.npmjs.org/react-native-fast-image/-/react-native-fast-image-8.6.3.tgz", + "integrity": "sha512-Sdw4ESidXCXOmQ9EcYguNY2swyoWmx53kym2zRsvi+VeFCHEdkO+WG1DK+6W81juot40bbfLNhkc63QnWtesNg==", + "requires": {} + }, "react-native-flipper": { "version": "https://gitpkg.now.sh/facebook/flipper/react-native/react-native-flipper?9cacc9b59402550eae866e0e81e5f0c2f8203e6b", "integrity": "sha512-M784S/qPuN/HqjdvXg98HIDmfm0sF8mACc56YNg87nzEF90zKSKp0XyOE83SEW+UJX2Gq/rf9BvM2GZeXlrhnQ==", diff --git a/package.json b/package.json index 19486968d73e..f3d28daed3ad 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,6 @@ "@gorhom/portal": "^1.0.14", "@oguzhnatly/react-native-image-manipulator": "github:Expensify/react-native-image-manipulator#c5f654fc9d0ad7cc5b89d50b34ecf8b0e3f4d050", "@onfido/react-native-sdk": "7.0.1", - "@pieter-pot/react-native-fast-image": "8.5.11", "@react-native-async-storage/async-storage": "^1.17.10", "@react-native-community/cameraroll": "git+https://github.com/react-native-cameraroll/react-native-cameraroll.git#3f0aed96db68e134f199171c7b06c1b4d6cb382b", "@react-native-community/clipboard": "^1.5.1", @@ -93,6 +92,7 @@ "react-native-collapsible": "^1.6.0", "react-native-config": "^1.4.5", "react-native-document-picker": "^8.0.0", + "react-native-fast-image": "^8.6.3", "react-native-gesture-handler": "2.6.0", "react-native-google-places-autocomplete": "git+https://github.com/Expensify/react-native-google-places-autocomplete.git#3bbd17d63e6c38d38d857b50f6037c1c0376ff06", "react-native-haptic-feedback": "^1.13.0", diff --git a/src/components/Image/index.native.js b/src/components/Image/index.native.js index 1c05d37b9915..15acd39bc0df 100644 --- a/src/components/Image/index.native.js +++ b/src/components/Image/index.native.js @@ -1,5 +1,5 @@ import React from 'react'; -import RNFastImage from '@pieter-pot/react-native-fast-image'; +import RNFastImage from 'react-native-fast-image'; import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; import CONST from '../../CONST'; From 8327006b4e1a9db25a5f570fb1c96ec2c679f459 Mon Sep 17 00:00:00 2001 From: Thomas Coldwell <31568400+thomas-coldwell@users.noreply.github.com> Date: Tue, 3 Jan 2023 12:33:42 -0800 Subject: [PATCH 04/18] fix: Patch for upstream PR https://github.com/DylanVann/react-native-fast-image/pull/953/files --- patches/react-native-fast-image+8.6.3.patch | 176 ++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 patches/react-native-fast-image+8.6.3.patch diff --git a/patches/react-native-fast-image+8.6.3.patch b/patches/react-native-fast-image+8.6.3.patch new file mode 100644 index 000000000000..c41155766646 --- /dev/null +++ b/patches/react-native-fast-image+8.6.3.patch @@ -0,0 +1,176 @@ +diff --git a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/BitmapSizeDecoder.java b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/BitmapSizeDecoder.java +new file mode 100644 +index 0000000..03ad017 +--- /dev/null ++++ b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/BitmapSizeDecoder.java +@@ -0,0 +1,31 @@ ++package com.dylanvann.fastimage; ++ ++import android.graphics.BitmapFactory; ++ ++import androidx.annotation.NonNull; ++import androidx.annotation.Nullable; ++ ++import com.bumptech.glide.load.Options; ++import com.bumptech.glide.load.ResourceDecoder; ++import com.bumptech.glide.load.engine.Resource; ++import com.bumptech.glide.load.resource.SimpleResource; ++ ++import java.io.IOException; ++import java.io.InputStream; ++ ++public class BitmapSizeDecoder implements ResourceDecoder { ++ ++ @Override ++ public boolean handles(@NonNull InputStream source, @NonNull Options options) throws IOException { ++ return true; ++ } ++ ++ @Nullable ++ @Override ++ public Resource decode(@NonNull InputStream source, int width, int height, @NonNull Options options) throws IOException { ++ BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); ++ bitmapOptions.inJustDecodeBounds = true; ++ BitmapFactory.decodeStream(source, null, bitmapOptions); ++ return new SimpleResource(bitmapOptions); ++ } ++} +\ No newline at end of file +diff --git a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/BitmapSizeTranscoder.java b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/BitmapSizeTranscoder.java +new file mode 100644 +index 0000000..7d208d1 +--- /dev/null ++++ b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/BitmapSizeTranscoder.java +@@ -0,0 +1,23 @@ ++package com.dylanvann.fastimage; ++ ++import android.graphics.BitmapFactory; ++ ++import androidx.annotation.NonNull; ++import androidx.annotation.Nullable; ++ ++import com.bumptech.glide.load.Options; ++import com.bumptech.glide.load.engine.Resource; ++import com.bumptech.glide.load.resource.SimpleResource; ++import com.bumptech.glide.load.resource.transcode.ResourceTranscoder; ++ ++public class BitmapSizeTranscoder implements ResourceTranscoder { ++ @Nullable ++ @Override ++ public Resource transcode(@NonNull Resource toTranscode, @NonNull Options options) { ++ BitmapFactory.Options bitmap = toTranscode.get(); ++ Size size = new Size(); ++ size.width = bitmap.outWidth; ++ size.height = bitmap.outHeight; ++ return new SimpleResource(size); ++ } ++} +\ No newline at end of file +diff --git a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageOkHttpProgressGlideModule.java b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageOkHttpProgressGlideModule.java +index 811292a..f60b87c 100644 +--- a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageOkHttpProgressGlideModule.java ++++ b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageOkHttpProgressGlideModule.java +@@ -2,6 +2,7 @@ package com.dylanvann.fastimage; + + import android.content.Context; + import androidx.annotation.NonNull; ++import android.graphics.BitmapFactory; + + import com.bumptech.glide.Glide; + import com.bumptech.glide.Registry; +@@ -47,6 +48,9 @@ public class FastImageOkHttpProgressGlideModule extends LibraryGlideModule { + .build(); + OkHttpUrlLoader.Factory factory = new OkHttpUrlLoader.Factory(client); + registry.replace(GlideUrl.class, InputStream.class, factory); ++ // Decoder + Transcoder pair for InputStream -> Size ++ registry.prepend(InputStream.class, BitmapFactory.Options.class, new BitmapSizeDecoder()); ++ registry.register(BitmapFactory.Options.class, Size.class, new BitmapSizeTranscoder()); + } + + private static Interceptor createInterceptor(final ResponseProgressListener listener) { +diff --git a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageRequestListener.java b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageRequestListener.java +index dbeb813..bf8f21c 100644 +--- a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageRequestListener.java ++++ b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageRequestListener.java +@@ -22,13 +22,6 @@ public class FastImageRequestListener implements RequestListener { + this.key = key; + } + +- private static WritableMap mapFromResource(Drawable resource) { +- WritableMap resourceData = new WritableNativeMap(); +- resourceData.putInt("width", resource.getIntrinsicWidth()); +- resourceData.putInt("height", resource.getIntrinsicHeight()); +- return resourceData; +- } +- + @Override + public boolean onLoadFailed(@androidx.annotation.Nullable GlideException e, Object model, Target target, boolean isFirstResource) { + FastImageOkHttpProgressGlideModule.forget(key); +@@ -53,7 +46,6 @@ public class FastImageRequestListener implements RequestListener { + ThemedReactContext context = (ThemedReactContext) view.getContext(); + RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class); + int viewId = view.getId(); +- eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_EVENT, mapFromResource(resource)); + eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_END_EVENT, new WritableNativeMap()); + return false; + } +diff --git a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java +index 34fcf89..e51e4a2 100644 +--- a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java ++++ b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java +@@ -2,6 +2,7 @@ package com.dylanvann.fastimage; + + import static com.dylanvann.fastimage.FastImageRequestListener.REACT_ON_ERROR_EVENT; + ++import androidx.annotation.NonNull; + import android.annotation.SuppressLint; + import android.content.Context; + import android.graphics.drawable.Drawable; +@@ -13,6 +14,8 @@ import com.bumptech.glide.RequestBuilder; + import com.bumptech.glide.RequestManager; + import com.bumptech.glide.load.model.GlideUrl; + import com.bumptech.glide.request.Request; ++import com.bumptech.glide.request.target.SimpleTarget; ++import com.bumptech.glide.request.transition.Transition; + import com.facebook.react.bridge.ReadableMap; + import com.facebook.react.bridge.WritableMap; + import com.facebook.react.bridge.WritableNativeMap; +@@ -148,6 +151,25 @@ class FastImageViewWithUrl extends AppCompatImageView { + builder.listener(new FastImageRequestListener(key)); + + builder.into(this); ++ ++ // Used specifically to handle the `onLoad` event for the image ++ RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class); ++ int viewId = this.getId(); ++ requestManager ++ .as(Size.class) ++ .load(imageSource == null ? null : imageSource.getSourceForLoad()) ++ .into(new SimpleTarget() { ++ @Override ++ public void onResourceReady(@NonNull Size resource, @Nullable Transition transition) { ++ WritableMap resourceData = new WritableNativeMap(); ++ resourceData.putInt("width", resource.width); ++ resourceData.putInt("height", resource.height); ++ eventEmitter.receiveEvent(viewId, ++ "onFastImageLoad", ++ resourceData ++ ); ++ } ++ }); + } + } + +diff --git a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/Size.java b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/Size.java +new file mode 100644 +index 0000000..2fe8a47 +--- /dev/null ++++ b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/Size.java +@@ -0,0 +1,6 @@ ++package com.dylanvann.fastimage; ++ ++public class Size { ++ int width; ++ int height; ++} +\ No newline at end of file From a5894b5dd0158e14449da65eeeae69416b024646 Mon Sep 17 00:00:00 2001 From: Thomas Coldwell <31568400+thomas-coldwell@users.noreply.github.com> Date: Tue, 3 Jan 2023 15:07:06 -0800 Subject: [PATCH 05/18] pod install --- ios/Podfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index b4226a18fb71..1bda8baba485 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -569,7 +569,7 @@ PODS: - React-Core - RNDateTimePicker (3.5.2): - React-Core - - RNFastImage (8.5.11): + - RNFastImage (8.6.3): - React-Core - SDWebImage (~> 5.11.1) - SDWebImageWebPCoder (~> 0.8.4) @@ -722,7 +722,7 @@ DEPENDENCIES: - "RNCClipboard (from `../node_modules/@react-native-community/clipboard`)" - "RNCPicker (from `../node_modules/@react-native-picker/picker`)" - "RNDateTimePicker (from `../node_modules/@react-native-community/datetimepicker`)" - - "RNFastImage (from `../node_modules/@pieter-pot/react-native-fast-image`)" + - RNFastImage (from `../node_modules/react-native-fast-image`) - "RNFBAnalytics (from `../node_modules/@react-native-firebase/analytics`)" - "RNFBApp (from `../node_modules/@react-native-firebase/app`)" - "RNFBCrashlytics (from `../node_modules/@react-native-firebase/crashlytics`)" @@ -890,7 +890,7 @@ EXTERNAL SOURCES: RNDateTimePicker: :path: "../node_modules/@react-native-community/datetimepicker" RNFastImage: - :path: "../node_modules/@pieter-pot/react-native-fast-image" + :path: "../node_modules/react-native-fast-image" RNFBAnalytics: :path: "../node_modules/@react-native-firebase/analytics" RNFBApp: @@ -1005,7 +1005,7 @@ SPEC CHECKSUMS: RNCClipboard: 41d8d918092ae8e676f18adada19104fa3e68495 RNCPicker: 0b65be85fe7954fbb2062ef079e3d1cde252d888 RNDateTimePicker: 7658208086d86d09e1627b5c34ba0cf237c60140 - RNFastImage: 1f2cab428712a4baaf78d6169eaec7f622556dd7 + RNFastImage: 5c9c9fed9c076e521b3f509fe79e790418a544e8 RNFBAnalytics: f76bfa164ac235b00505deb9fc1776634056898c RNFBApp: 729c0666395b1953198dc4a1ec6deb8fbe1c302e RNFBCrashlytics: 2061ca863e8e2fa1aae9b12477d7dfa8e88ca0f9 From f97273b6700252e1431250d7c7a71f91e5d65fe9 Mon Sep 17 00:00:00 2001 From: Thomas Coldwell <31568400+thomas-coldwell@users.noreply.github.com> Date: Wed, 4 Jan 2023 13:11:07 -0800 Subject: [PATCH 06/18] fix: nabs --- src/components/AttachmentView.js | 3 +-- src/components/Image/imagePropTypes.js | 1 - src/components/ImageView/index.native.js | 3 +-- src/components/ImageWithSizeCalculation.js | 2 +- src/components/ThumbnailImage.js | 2 +- 5 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/components/AttachmentView.js b/src/components/AttachmentView.js index 274f4acdb2e2..8b6877738574 100755 --- a/src/components/AttachmentView.js +++ b/src/components/AttachmentView.js @@ -15,8 +15,7 @@ import themeColors from '../styles/themes/default'; import addEncryptedAuthTokenToURL from '../libs/addEncryptedAuthTokenToURL'; const propTypes = { - - /** Do the urls require an authToken? */ + /** Whether the urls require an authToken */ isAuthTokenRequired: PropTypes.bool, /** URL to full-sized attachment */ diff --git a/src/components/Image/imagePropTypes.js b/src/components/Image/imagePropTypes.js index e4c4ef0d9461..11f9ab23f6d6 100644 --- a/src/components/Image/imagePropTypes.js +++ b/src/components/Image/imagePropTypes.js @@ -3,7 +3,6 @@ import stylePropTypes from '../../styles/stylePropTypes'; import RESIZE_MODES from './resizeModes'; const propTypes = { - /** Styles for the Image */ style: stylePropTypes, diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index 74c72f2bc640..c3c6324530f3 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -15,8 +15,7 @@ import Image from '../Image'; * On the native layer, we use a image library to handle zoom functionality */ const propTypes = { - - /** Do the urls require an authToken? */ + /** Whether the urls require an authToken */ isAuthTokenRequired: PropTypes.bool, /** URL to full-sized image */ diff --git a/src/components/ImageWithSizeCalculation.js b/src/components/ImageWithSizeCalculation.js index 56412a5e6c59..04bedb6392d6 100644 --- a/src/components/ImageWithSizeCalculation.js +++ b/src/components/ImageWithSizeCalculation.js @@ -18,7 +18,7 @@ const propTypes = { /** Callback fired when the image has been measured. */ onMeasure: PropTypes.func, - /** Does the image require an authToken? */ + /** Whether the image require an authToken */ isAuthTokenRequired: PropTypes.bool, }; diff --git a/src/components/ThumbnailImage.js b/src/components/ThumbnailImage.js index a0c8abfb844f..f8265a8510e7 100644 --- a/src/components/ThumbnailImage.js +++ b/src/components/ThumbnailImage.js @@ -15,7 +15,7 @@ const propTypes = { // eslint-disable-next-line react/forbid-prop-types style: PropTypes.any, - /** Does the image require an authToken? */ + /** Whether the image require an authToken */ isAuthTokenRequired: PropTypes.bool.isRequired, /** Width of the thumbnail image */ From 4f85de36c5108a6022c2a66cf759b9b9d9f78888 Mon Sep 17 00:00:00 2001 From: Thomas Coldwell <31568400+thomas-coldwell@users.noreply.github.com> Date: Thu, 5 Jan 2023 06:00:57 -0800 Subject: [PATCH 07/18] fix: Review comments --- src/CONST.js | 1 - src/components/Image/imagePropTypes.js | 3 ++- src/components/Image/index.native.js | 3 +-- src/components/ImageView/index.js | 1 - src/components/ImageView/index.native.js | 4 +++- src/components/ImageWithSizeCalculation.js | 2 +- src/components/ThumbnailImage.js | 6 ++---- 7 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/CONST.js b/src/CONST.js index 859363b67257..e1df6557772f 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -874,7 +874,6 @@ const CONST = { }, TFA_CODE_LENGTH: 6, - CHAT_ATTACHMENT_TOKEN_KEY: 'X-Chat-Attachment-Token', }; diff --git a/src/components/Image/imagePropTypes.js b/src/components/Image/imagePropTypes.js index 11f9ab23f6d6..de2613836338 100644 --- a/src/components/Image/imagePropTypes.js +++ b/src/components/Image/imagePropTypes.js @@ -13,7 +13,7 @@ const propTypes = { uri: PropTypes.string.isRequired, // eslint-disable-next-line react/forbid-prop-types headers: PropTypes.object, - }), + }).isRequired, ]), /** Should an auth token be included in the image request */ @@ -41,6 +41,7 @@ const propTypes = { }; const defaultProps = { + style: [], session: { authToken: null, }, diff --git a/src/components/Image/index.native.js b/src/components/Image/index.native.js index 15acd39bc0df..0f032876dcd3 100644 --- a/src/components/Image/index.native.js +++ b/src/components/Image/index.native.js @@ -29,9 +29,8 @@ class Image extends React.Component { configureImageSource() { const source = this.props.source; - const isAuthTokenRequired = this.props.isAuthTokenRequired; let imageSource = source; - if (typeof source !== 'number' && isAuthTokenRequired) { + if (typeof source !== 'number' && this.props.isAuthTokenRequired) { const authToken = lodashGet(this.props, 'session.encryptedAuthToken', null); imageSource = { ...source, diff --git a/src/components/ImageView/index.js b/src/components/ImageView/index.js index 87dc22a729f9..9cabd6dfe049 100644 --- a/src/components/ImageView/index.js +++ b/src/components/ImageView/index.js @@ -9,7 +9,6 @@ import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDime import FullscreenLoadingIndicator from '../FullscreenLoadingIndicator'; const propTypes = { - /** Do the urls require an authToken? */ isAuthTokenRequired: PropTypes.bool, diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index c3c6324530f3..944bb021820a 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -1,7 +1,7 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import { - View, PanResponder, InteractionManager, + View, InteractionManager, PanResponder, } from 'react-native'; import ImageZoom from 'react-native-image-pan-zoom'; import _ from 'underscore'; @@ -82,6 +82,8 @@ class ImageView extends PureComponent { return false; } + // Handles the `onLoad` event when the image loads providing the natural + // image dimensions required for layout calculations imageLoad({nativeEvent}) { // Wait till animations are over to prevent stutter in navigation animation this.state.interactionPromise = InteractionManager.runAfterInteractions(() => { diff --git a/src/components/ImageWithSizeCalculation.js b/src/components/ImageWithSizeCalculation.js index 04bedb6392d6..3140632cc1ee 100644 --- a/src/components/ImageWithSizeCalculation.js +++ b/src/components/ImageWithSizeCalculation.js @@ -18,7 +18,7 @@ const propTypes = { /** Callback fired when the image has been measured. */ onMeasure: PropTypes.func, - /** Whether the image require an authToken */ + /** Whether the image requires an authToken */ isAuthTokenRequired: PropTypes.bool, }; diff --git a/src/components/ThumbnailImage.js b/src/components/ThumbnailImage.js index f8265a8510e7..dedd320d1c91 100644 --- a/src/components/ThumbnailImage.js +++ b/src/components/ThumbnailImage.js @@ -15,7 +15,7 @@ const propTypes = { // eslint-disable-next-line react/forbid-prop-types style: PropTypes.any, - /** Whether the image require an authToken */ + /** Whether the image requires an authToken */ isAuthTokenRequired: PropTypes.bool.isRequired, /** Width of the thumbnail image */ @@ -86,8 +86,6 @@ class ThumbnailImage extends PureComponent { } render() { - const url = this.props.previewSourceURL; - return ( From 410e28341127d5db7e7692a64b154dcb5778191f Mon Sep 17 00:00:00 2001 From: Thomas Coldwell <31568400+thomas-coldwell@users.noreply.github.com> Date: Thu, 5 Jan 2023 09:49:35 -0800 Subject: [PATCH 08/18] fix: prop type changes --- src/components/Image/imagePropTypes.js | 4 ++-- src/components/Image/index.js | 26 +++++++++++++++++++------- src/components/Image/index.native.js | 4 ++-- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/components/Image/imagePropTypes.js b/src/components/Image/imagePropTypes.js index de2613836338..326cf2674646 100644 --- a/src/components/Image/imagePropTypes.js +++ b/src/components/Image/imagePropTypes.js @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import stylePropTypes from '../../styles/stylePropTypes'; import RESIZE_MODES from './resizeModes'; -const propTypes = { +const imagePropTypes = { /** Styles for the Image */ style: stylePropTypes, @@ -53,4 +53,4 @@ const defaultProps = { onLoad: () => {}, }; -export {propTypes, defaultProps}; +export {imagePropTypes, defaultProps}; diff --git a/src/components/Image/index.js b/src/components/Image/index.js index ff2f6041db45..9d9a811b323c 100644 --- a/src/components/Image/index.js +++ b/src/components/Image/index.js @@ -2,10 +2,20 @@ import React from 'react'; import {Image as RNImage} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; +import PropTypes from 'prop-types'; import ONYXKEYS from '../../ONYXKEYS'; -import {defaultProps, propTypes} from './imagePropTypes'; +import {defaultProps, imagePropTypes} from './imagePropTypes'; import RESIZE_MODES from './resizeModes'; +const propTypes = { + ...imagePropTypes, + + /** The URI source of the image */ + source: PropTypes.shape({ + uri: PropTypes.string.isRequired, + }).isRequired, +}; + class Image extends React.Component { constructor(props) { super(props); @@ -26,11 +36,16 @@ class Image extends React.Component { this.configureImageSource(); } + // Check if the image source is a URL - if so the `encryptedAuthToken` is appended + // to the source. The natural image dimensions can then be retrieved using this source + // and as a result the `onLoad` event needs to be maunually invoked to return these dimensions configureImageSource() { const source = this.props.source; - const isAuthTokenRequired = this.props.isAuthTokenRequired; let imageSource = source; - if (typeof source !== 'number' && isAuthTokenRequired) { + if (this.props.isAuthTokenRequired) { + // There is currently a `react-native-web` bug preventing the authToken being passed + // in the headers of the image request so the authToken is added as a query param. + // On native the authToken IS passed in the image request headers const authToken = lodashGet(this.props, 'session.encryptedAuthToken', null); imageSource = {uri: `${source.uri}?encryptedAuthToken=${encodeURIComponent(authToken)}`}; } @@ -41,10 +56,7 @@ class Image extends React.Component { if (this.props.onLoad == null) { return; } - const uri = typeof imageSource === 'number' - ? Image.resolveAssetSource(imageSource).uri - : imageSource.uri; - RNImage.getSize(uri, (width, height) => { + RNImage.getSize(imageSource.uri, (width, height) => { this.props.onLoad({nativeEvent: {width, height}}); }); } diff --git a/src/components/Image/index.native.js b/src/components/Image/index.native.js index 0f032876dcd3..3af03b1b223d 100644 --- a/src/components/Image/index.native.js +++ b/src/components/Image/index.native.js @@ -4,7 +4,7 @@ import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; import CONST from '../../CONST'; import ONYXKEYS from '../../ONYXKEYS'; -import {defaultProps, propTypes} from './imagePropTypes'; +import {defaultProps, imagePropTypes} from './imagePropTypes'; import RESIZE_MODES from './resizeModes'; class Image extends React.Component { @@ -51,7 +51,7 @@ class Image extends React.Component { } } -Image.propTypes = propTypes; +Image.propTypes = imagePropTypes; Image.defaultProps = defaultProps; const ImageWithOnyx = withOnyx({ From af6a53f730c3c7584308715a793e1c5c778e3b76 Mon Sep 17 00:00:00 2001 From: Thomas Coldwell <31568400+thomas-coldwell@users.noreply.github.com> Date: Thu, 5 Jan 2023 11:58:30 -0800 Subject: [PATCH 09/18] fix: Only show loading indictator when actually downloading image in attachment view --- src/components/Image/imagePropTypes.js | 3 +++ src/components/ImageView/index.native.js | 12 ++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/components/Image/imagePropTypes.js b/src/components/Image/imagePropTypes.js index 326cf2674646..3b2e980bea33 100644 --- a/src/components/Image/imagePropTypes.js +++ b/src/components/Image/imagePropTypes.js @@ -31,6 +31,9 @@ const imagePropTypes = { /** Event for when the image is fully loaded and returns the natural dimensions of the image */ onLoad: PropTypes.func, + /** Progress events while the image is downloading */ + onProgress: PropTypes.func, + /* Onyx Props */ /** Session info for the currently logged in user. */ session: PropTypes.shape({ diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index 944bb021820a..805eb68c662e 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -55,7 +55,7 @@ class ImageView extends PureComponent { onStartShouldSetPanResponder: this.updatePanResponderTouches.bind(this), }); - this.imageLoadingStart = this.imageLoadingStart.bind(this); + this.imageProgress = this.imageProgress.bind(this); this.imageLoad = this.imageLoad.bind(this); } @@ -82,6 +82,10 @@ class ImageView extends PureComponent { return false; } + imageProgress() { + this.setState({isLoading: true}); + } + // Handles the `onLoad` event when the image loads providing the natural // image dimensions required for layout calculations imageLoad({nativeEvent}) { @@ -108,10 +112,6 @@ class ImageView extends PureComponent { }); } - imageLoadingStart() { - this.setState({isLoading: true}); - } - render() { // Default windowHeight accounts for the modal header height const windowHeight = this.props.windowHeight - variables.contentHeaderHeight; @@ -180,7 +180,7 @@ class ImageView extends PureComponent { source={{uri: this.props.url}} isAuthTokenRequired={this.props.isAuthTokenRequired} resizeMode={Image.resizeMode.contain} - onLoadStart={this.imageLoadingStart} + onProgress={this.imageProgress} onLoad={this.imageLoad} /> {/** From 8ab3c7d48ed0f4bd82c6ada08d44ea32d50449f1 Mon Sep 17 00:00:00 2001 From: Thomas Coldwell <31568400+thomas-coldwell@users.noreply.github.com> Date: Fri, 6 Jan 2023 11:12:46 -0800 Subject: [PATCH 10/18] fix: Review comments --- src/components/AttachmentModal.js | 2 +- src/components/AttachmentView.js | 2 +- src/components/Image/imagePropTypes.js | 7 ++- src/components/Image/index.native.js | 59 ++++++++---------------- src/components/ImageView/index.js | 2 +- src/components/ImageView/index.native.js | 10 ++-- 6 files changed, 31 insertions(+), 51 deletions(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index e11a9e507d93..bb63a1d01a28 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -44,7 +44,7 @@ const propTypes = { /** A function as a child to pass modal launching methods to */ children: PropTypes.func.isRequired, - /** Do the urls require an authToken? */ + /** Whether source url requires authentication */ isAuthTokenRequired: PropTypes.bool, /** Determines if download Button should be shown or not */ diff --git a/src/components/AttachmentView.js b/src/components/AttachmentView.js index 8b6877738574..445c26109122 100755 --- a/src/components/AttachmentView.js +++ b/src/components/AttachmentView.js @@ -15,7 +15,7 @@ import themeColors from '../styles/themes/default'; import addEncryptedAuthTokenToURL from '../libs/addEncryptedAuthTokenToURL'; const propTypes = { - /** Whether the urls require an authToken */ + /** Whether source url requires authentication */ isAuthTokenRequired: PropTypes.bool, /** URL to full-sized attachment */ diff --git a/src/components/Image/imagePropTypes.js b/src/components/Image/imagePropTypes.js index 3b2e980bea33..645cbaada894 100644 --- a/src/components/Image/imagePropTypes.js +++ b/src/components/Image/imagePropTypes.js @@ -8,13 +8,13 @@ const imagePropTypes = { /** The static asset or URI source of the image */ source: PropTypes.oneOfType([ - PropTypes.number.isRequired, + PropTypes.number, PropTypes.shape({ uri: PropTypes.string.isRequired, // eslint-disable-next-line react/forbid-prop-types headers: PropTypes.object, - }).isRequired, - ]), + }), + ]).isRequired, /** Should an auth token be included in the image request */ isAuthTokenRequired: PropTypes.bool, @@ -49,7 +49,6 @@ const defaultProps = { authToken: null, }, isAuthTokenRequired: false, - source: null, resizeMode: RESIZE_MODES.cover, onLoadStart: () => {}, onLoadEnd: () => {}, diff --git a/src/components/Image/index.native.js b/src/components/Image/index.native.js index 3af03b1b223d..4dfbe0c86d92 100644 --- a/src/components/Image/index.native.js +++ b/src/components/Image/index.native.js @@ -7,53 +7,30 @@ import ONYXKEYS from '../../ONYXKEYS'; import {defaultProps, imagePropTypes} from './imagePropTypes'; import RESIZE_MODES from './resizeModes'; -class Image extends React.Component { - constructor(props) { - super(props); - - this.state = { - imageSource: undefined, +const Image = (props) => { + // eslint-disable-next-line react/destructuring-assignment + const { + source, isAuthTokenRequired, session, ...rest + } = props; + + let imageSource = source; + if (typeof source !== 'number' && isAuthTokenRequired) { + const authToken = lodashGet(props, 'session.encryptedAuthToken', null); + imageSource = { + ...source, + headers: authToken ? { + [CONST.CHAT_ATTACHMENT_TOKEN_KEY]: authToken, + } : null, }; } - componentDidMount() { - this.configureImageSource(); - } - - componentDidUpdate(prevProps) { - if (prevProps.source === this.props.source) { - return; - } - this.configureImageSource(); - } - - configureImageSource() { - const source = this.props.source; - let imageSource = source; - if (typeof source !== 'number' && this.props.isAuthTokenRequired) { - const authToken = lodashGet(this.props, 'session.encryptedAuthToken', null); - imageSource = { - ...source, - headers: authToken ? { - [CONST.CHAT_ATTACHMENT_TOKEN_KEY]: authToken, - } : null, - }; - } - this.setState({imageSource}); - } - - render() { - // eslint-disable-next-line - const { source, ...rest } = this.props; - - // eslint-disable-next-line - return ; - } -} + // eslint-disable-next-line react/jsx-props-no-spreading + return ; +}; Image.propTypes = imagePropTypes; Image.defaultProps = defaultProps; - +Image.displayName = 'Image'; const ImageWithOnyx = withOnyx({ session: { key: ONYXKEYS.SESSION, diff --git a/src/components/ImageView/index.js b/src/components/ImageView/index.js index 9cabd6dfe049..9bf74cff571e 100644 --- a/src/components/ImageView/index.js +++ b/src/components/ImageView/index.js @@ -9,7 +9,7 @@ import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDime import FullscreenLoadingIndicator from '../FullscreenLoadingIndicator'; const propTypes = { - /** Do the urls require an authToken? */ + /** Whether source url requires authentication */ isAuthTokenRequired: PropTypes.bool, /** URL to full-sized image */ diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index 805eb68c662e..95bec1a7434b 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -15,7 +15,7 @@ import Image from '../Image'; * On the native layer, we use a image library to handle zoom functionality */ const propTypes = { - /** Whether the urls require an authToken */ + /** Whether source url requires authentication */ isAuthTokenRequired: PropTypes.bool, /** URL to full-sized image */ @@ -86,8 +86,12 @@ class ImageView extends PureComponent { this.setState({isLoading: true}); } - // Handles the `onLoad` event when the image loads providing the natural - // image dimensions required for layout calculations + /** + * Handles the `onLoad` event when the image loads providing the natural + * image dimensions required for layout calculations + * + * @param {Object} nativeEvent + */ imageLoad({nativeEvent}) { // Wait till animations are over to prevent stutter in navigation animation this.state.interactionPromise = InteractionManager.runAfterInteractions(() => { From eab7f6a6b5f9487b11fd82a16020a246b821391d Mon Sep 17 00:00:00 2001 From: Thomas Coldwell <31568400+thomas-coldwell@users.noreply.github.com> Date: Fri, 6 Jan 2023 11:32:36 -0800 Subject: [PATCH 11/18] fix: improve naming --- src/components/ImageView/index.native.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index 95bec1a7434b..af04ec078c4a 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -56,7 +56,7 @@ class ImageView extends PureComponent { }); this.imageProgress = this.imageProgress.bind(this); - this.imageLoad = this.imageLoad.bind(this); + this.configureImageZoom = this.configureImageZoom.bind(this); } componentWillUnmount() { @@ -87,12 +87,13 @@ class ImageView extends PureComponent { } /** - * Handles the `onLoad` event when the image loads providing the natural - * image dimensions required for layout calculations + * The `ImageZoom` component requires image dimensions which + * are calculated here from the natural image dimensions produced by + * the `onLoad` event * * @param {Object} nativeEvent */ - imageLoad({nativeEvent}) { + configureImageZoom({nativeEvent}) { // Wait till animations are over to prevent stutter in navigation animation this.state.interactionPromise = InteractionManager.runAfterInteractions(() => { let imageWidth = nativeEvent.width; @@ -185,7 +186,7 @@ class ImageView extends PureComponent { isAuthTokenRequired={this.props.isAuthTokenRequired} resizeMode={Image.resizeMode.contain} onProgress={this.imageProgress} - onLoad={this.imageLoad} + onLoad={this.configureImageZoom} /> {/** Create an invisible view on top of the image so we can capture and set the amount of touches before From 5b9db070e9e2dec88f756076d5bd8d6f8226f3e8 Mon Sep 17 00:00:00 2001 From: Thomas Coldwell <31568400+thomas-coldwell@users.noreply.github.com> Date: Fri, 6 Jan 2023 18:23:20 -0800 Subject: [PATCH 12/18] fix: Only set loading state if image is being downloaded --- patches/react-native-fast-image+8.6.3.patch | 116 +++++++++++++++++++- src/components/ImageView/index.native.js | 11 +- 2 files changed, 121 insertions(+), 6 deletions(-) diff --git a/patches/react-native-fast-image+8.6.3.patch b/patches/react-native-fast-image+8.6.3.patch index c41155766646..a873b841342c 100644 --- a/patches/react-native-fast-image+8.6.3.patch +++ b/patches/react-native-fast-image+8.6.3.patch @@ -115,7 +115,7 @@ index dbeb813..bf8f21c 100644 return false; } diff --git a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java -index 34fcf89..e51e4a2 100644 +index 34fcf89..40d78cc 100644 --- a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java +++ b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java @@ -2,6 +2,7 @@ package com.dylanvann.fastimage; @@ -126,16 +126,69 @@ index 34fcf89..e51e4a2 100644 import android.annotation.SuppressLint; import android.content.Context; import android.graphics.drawable.Drawable; -@@ -13,6 +14,8 @@ import com.bumptech.glide.RequestBuilder; +@@ -9,16 +10,24 @@ import android.graphics.drawable.Drawable; + import androidx.annotation.Nullable; + import androidx.appcompat.widget.AppCompatImageView; + ++import com.bumptech.glide.Glide; + import com.bumptech.glide.RequestBuilder; import com.bumptech.glide.RequestManager; ++import com.bumptech.glide.load.DataSource; ++import com.bumptech.glide.load.engine.GlideException; import com.bumptech.glide.load.model.GlideUrl; import com.bumptech.glide.request.Request; ++import com.bumptech.glide.request.RequestListener; +import com.bumptech.glide.request.target.SimpleTarget; ++import com.bumptech.glide.request.target.Target; +import com.bumptech.glide.request.transition.Transition; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeMap; -@@ -148,6 +151,25 @@ class FastImageViewWithUrl extends AppCompatImageView { + import com.facebook.react.uimanager.ThemedReactContext; + import com.facebook.react.uimanager.events.RCTEventEmitter; + ++import java.io.File; + import java.util.ArrayList; + import java.util.Collections; + import java.util.List; +@@ -124,9 +133,33 @@ class FastImageViewWithUrl extends AppCompatImageView { + RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class); + int viewId = this.getId(); + +- eventEmitter.receiveEvent(viewId, +- FastImageViewManager.REACT_ON_LOAD_START_EVENT, +- new WritableNativeMap()); ++ // Request the URL from cache to see if it exists there and if so pass the cache ++ // path as an argument in the onLoadStart event ++ requestManager ++ .asFile() ++ .load(glideUrl) ++ .onlyRetrieveFromCache(true) ++ .listener(new RequestListener() { ++ @Override ++ public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { ++ WritableNativeMap result = new WritableNativeMap(); ++ result.putNull("cachePath"); ++ eventEmitter.receiveEvent(viewId, ++ FastImageViewManager.REACT_ON_LOAD_START_EVENT, ++ result); ++ return false; ++ } ++ ++ @Override ++ public boolean onResourceReady(File resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { ++ WritableNativeMap result = new WritableNativeMap(); ++ result.putString("cachePath", resource.getAbsolutePath()); ++ eventEmitter.receiveEvent(viewId, ++ FastImageViewManager.REACT_ON_LOAD_START_EVENT, ++ result); ++ return false; ++ } ++ }); + } + + if (requestManager != null) { +@@ -148,6 +181,25 @@ class FastImageViewWithUrl extends AppCompatImageView { builder.listener(new FastImageRequestListener(key)); builder.into(this); @@ -174,3 +227,60 @@ index 0000000..2fe8a47 + int height; +} \ No newline at end of file +diff --git a/node_modules/react-native-fast-image/dist/index.d.ts b/node_modules/react-native-fast-image/dist/index.d.ts +index 5abb7c9..a2672c6 100644 +--- a/node_modules/react-native-fast-image/dist/index.d.ts ++++ b/node_modules/react-native-fast-image/dist/index.d.ts +@@ -27,6 +27,11 @@ export declare type Source = { + priority?: Priority; + cache?: Cache; + }; ++export interface OnLoadStartEvent { ++ nativeEvent: { ++ cachePath: string | null; ++ }; ++} + export interface OnLoadEvent { + nativeEvent: { + width: number; +@@ -57,7 +62,7 @@ export interface FastImageProps extends AccessibilityProps, ViewProps { + defaultSource?: ImageRequireSource; + resizeMode?: ResizeMode; + fallback?: boolean; +- onLoadStart?(): void; ++ onLoadStart?(event: OnLoadStartEvent): void; + onProgress?(event: OnProgressEvent): void; + onLoad?(event: OnLoadEvent): void; + onError?(): void; +diff --git a/node_modules/react-native-fast-image/ios/FastImage/FFFastImageView.m b/node_modules/react-native-fast-image/ios/FastImage/FFFastImageView.m +index f710081..391ef92 100644 +--- a/node_modules/react-native-fast-image/ios/FastImage/FFFastImageView.m ++++ b/node_modules/react-native-fast-image/ios/FastImage/FFFastImageView.m +@@ -54,7 +54,6 @@ - (void) setOnFastImageError: (RCTDirectEventBlock)onFastImageError { + - (void) setOnFastImageLoadStart: (RCTDirectEventBlock)onFastImageLoadStart { + if (_source && !self.hasSentOnLoadStart) { + _onFastImageLoadStart = onFastImageLoadStart; +- onFastImageLoadStart(@{}); + self.hasSentOnLoadStart = YES; + } else { + _onFastImageLoadStart = onFastImageLoadStart; +@@ -188,7 +187,18 @@ - (void) reloadImage { + } + + if (self.onFastImageLoadStart) { +- self.onFastImageLoadStart(@{}); ++ NSString* cachePath = [[SDImageCache sharedImageCache] cachePathForKey:url]; ++ BOOL isCached = [[SDImageCache sharedImageCache] diskImageDataExistsWithKey:url]; ++ if (isCached) { ++ self.onFastImageLoadStart(@{ ++ @"cachePath": cachePath ++ }); ++ } ++ else { ++ self.onFastImageLoadStart(@{ ++ @"cachePath": [NSNull null] ++ }); ++ } + self.hasSentOnLoadStart = YES; + } else { + self.hasSentOnLoadStart = NO; diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index af04ec078c4a..22c22e404096 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -55,7 +55,7 @@ class ImageView extends PureComponent { onStartShouldSetPanResponder: this.updatePanResponderTouches.bind(this), }); - this.imageProgress = this.imageProgress.bind(this); + this.imageLoadStart = this.imageLoadStart.bind(this); this.configureImageZoom = this.configureImageZoom.bind(this); } @@ -82,7 +82,12 @@ class ImageView extends PureComponent { return false; } - imageProgress() { + imageLoadStart({nativeEvent}) { + // Only show a loading state if the image is not from cache + // and thus has to be downloaded before it can be displayed + if (nativeEvent.cachePath != null) { + return; + } this.setState({isLoading: true}); } @@ -185,7 +190,7 @@ class ImageView extends PureComponent { source={{uri: this.props.url}} isAuthTokenRequired={this.props.isAuthTokenRequired} resizeMode={Image.resizeMode.contain} - onProgress={this.imageProgress} + onLoadStart={this.imageLoadStart} onLoad={this.configureImageZoom} /> {/** From 72f52b379baba84dae86c69d48b3b9930863349d Mon Sep 17 00:00:00 2001 From: Thomas Coldwell <31568400+thomas-coldwell@users.noreply.github.com> Date: Sat, 7 Jan 2023 21:36:00 -0800 Subject: [PATCH 13/18] fix: Layout shift for small images --- src/components/ImageView/index.native.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index 22c22e404096..a7d069abcc49 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -34,6 +34,7 @@ class ImageView extends PureComponent { this.state = { isLoading: false, + isConfiguringImageZoom: false, // Default to large image width and height to prevent // small, blurry image being present by react-native-image-pan-zoom @@ -85,10 +86,10 @@ class ImageView extends PureComponent { imageLoadStart({nativeEvent}) { // Only show a loading state if the image is not from cache // and thus has to be downloaded before it can be displayed - if (nativeEvent.cachePath != null) { - return; - } - this.setState({isLoading: true}); + this.setState({ + isLoading: nativeEvent.cachePath == null, + isConfiguringImageZoom: true, + }); } /** @@ -118,7 +119,12 @@ class ImageView extends PureComponent { const maxDimensionsScale = 11; imageHeight = Math.min(imageHeight, (this.props.windowHeight * maxDimensionsScale)); imageWidth = Math.min(imageWidth, (this.props.windowWidth * maxDimensionsScale)); - this.setState({imageHeight, imageWidth, isLoading: false}); + this.setState({ + imageHeight, + imageWidth, + isLoading: false, + isConfiguringImageZoom: false, + }); }); } @@ -185,7 +191,7 @@ class ImageView extends PureComponent { // Hide image while loading so ImageZoom can get the image // size before presenting - preventing visual glitches or shift // due to ImageZoom - this.state.isLoading ? styles.opacity0 : styles.opacity1, + this.state.isLoading || this.state.isConfiguringImageZoom ? styles.opacity0 : styles.opacity1, ]} source={{uri: this.props.url}} isAuthTokenRequired={this.props.isAuthTokenRequired} From ed67ec490263b093eb848e11849cb59cdb76b270 Mon Sep 17 00:00:00 2001 From: Thomas Coldwell <31568400+thomas-coldwell@users.noreply.github.com> Date: Sat, 7 Jan 2023 21:45:34 -0800 Subject: [PATCH 14/18] fix: Update patch --- patches/react-native-fast-image+8.6.3.patch | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/patches/react-native-fast-image+8.6.3.patch b/patches/react-native-fast-image+8.6.3.patch index a873b841342c..fc7e59c17c2e 100644 --- a/patches/react-native-fast-image+8.6.3.patch +++ b/patches/react-native-fast-image+8.6.3.patch @@ -115,7 +115,7 @@ index dbeb813..bf8f21c 100644 return false; } diff --git a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java -index 34fcf89..40d78cc 100644 +index 34fcf89..1339f5c 100644 --- a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java +++ b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java @@ -2,6 +2,7 @@ package com.dylanvann.fastimage; @@ -151,7 +151,7 @@ index 34fcf89..40d78cc 100644 import java.util.ArrayList; import java.util.Collections; import java.util.List; -@@ -124,9 +133,33 @@ class FastImageViewWithUrl extends AppCompatImageView { +@@ -124,9 +133,34 @@ class FastImageViewWithUrl extends AppCompatImageView { RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class); int viewId = this.getId(); @@ -184,11 +184,12 @@ index 34fcf89..40d78cc 100644 + result); + return false; + } -+ }); ++ }) ++ .submit(); } if (requestManager != null) { -@@ -148,6 +181,25 @@ class FastImageViewWithUrl extends AppCompatImageView { +@@ -148,6 +182,25 @@ class FastImageViewWithUrl extends AppCompatImageView { builder.listener(new FastImageRequestListener(key)); builder.into(this); From 16d773818ef3ac0a791c121a90ec5f2ce44b4fde Mon Sep 17 00:00:00 2001 From: Thomas Coldwell <31568400+thomas-coldwell@users.noreply.github.com> Date: Wed, 11 Jan 2023 08:28:51 -0800 Subject: [PATCH 15/18] fix: Show loading state for image load + layout calculation --- src/components/ImageView/index.native.js | 27 ++++++++---------------- 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index a7d069abcc49..866f335a5ebd 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -34,12 +34,11 @@ class ImageView extends PureComponent { this.state = { isLoading: false, - isConfiguringImageZoom: false, // Default to large image width and height to prevent // small, blurry image being present by react-native-image-pan-zoom - imageWidth: props.windowWidth, - imageHeight: props.windowHeight, + imageWidth: 0, + imageHeight: 0, interactionPromise: undefined, containerHeight: undefined, }; @@ -83,13 +82,8 @@ class ImageView extends PureComponent { return false; } - imageLoadStart({nativeEvent}) { - // Only show a loading state if the image is not from cache - // and thus has to be downloaded before it can be displayed - this.setState({ - isLoading: nativeEvent.cachePath == null, - isConfiguringImageZoom: true, - }); + imageLoadStart() { + this.setState({isLoading: true}); } /** @@ -119,18 +113,15 @@ class ImageView extends PureComponent { const maxDimensionsScale = 11; imageHeight = Math.min(imageHeight, (this.props.windowHeight * maxDimensionsScale)); imageWidth = Math.min(imageWidth, (this.props.windowWidth * maxDimensionsScale)); - this.setState({ - imageHeight, - imageWidth, - isLoading: false, - isConfiguringImageZoom: false, - }); + this.setState({imageHeight, imageWidth, isLoading: false}); }); } render() { // Default windowHeight accounts for the modal header height const windowHeight = this.props.windowHeight - variables.contentHeaderHeight; + const hasImageDimensions = this.state.imageWidth !== 0 && this.state.imageHeight !== 0; + const shouldShowLoadingIndicator = this.state.isLoading || !hasImageDimensions; // Zoom view should be loaded only after measuring actual image dimensions, otherwise it causes blurred images on Android return ( @@ -191,7 +182,7 @@ class ImageView extends PureComponent { // Hide image while loading so ImageZoom can get the image // size before presenting - preventing visual glitches or shift // due to ImageZoom - this.state.isLoading || this.state.isConfiguringImageZoom ? styles.opacity0 : styles.opacity1, + shouldShowLoadingIndicator ? styles.opacity0 : styles.opacity1, ]} source={{uri: this.props.url}} isAuthTokenRequired={this.props.isAuthTokenRequired} @@ -214,7 +205,7 @@ class ImageView extends PureComponent { ]} /> - {this.state.isLoading && ( + {shouldShowLoadingIndicator && ( From 38107565ee2962365da78dc5f2984a6acd2b3370 Mon Sep 17 00:00:00 2001 From: Thomas Coldwell <31568400+thomas-coldwell@users.noreply.github.com> Date: Wed, 11 Jan 2023 08:37:17 -0800 Subject: [PATCH 16/18] fix: Remove obsolete comment --- src/components/ImageView/index.native.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index 866f335a5ebd..b5fcaa4a41e1 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -34,9 +34,6 @@ class ImageView extends PureComponent { this.state = { isLoading: false, - - // Default to large image width and height to prevent - // small, blurry image being present by react-native-image-pan-zoom imageWidth: 0, imageHeight: 0, interactionPromise: undefined, From c262265a38024028aa0ab31613ca943872dc08d5 Mon Sep 17 00:00:00 2001 From: Thomas Coldwell <31568400+thomas-coldwell@users.noreply.github.com> Date: Thu, 12 Jan 2023 11:50:12 +0000 Subject: [PATCH 17/18] fix: Remove duplicate prop-type --- src/components/Image/index.js | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/components/Image/index.js b/src/components/Image/index.js index 9d9a811b323c..1c67dcf35129 100644 --- a/src/components/Image/index.js +++ b/src/components/Image/index.js @@ -2,20 +2,10 @@ import React from 'react'; import {Image as RNImage} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; import ONYXKEYS from '../../ONYXKEYS'; import {defaultProps, imagePropTypes} from './imagePropTypes'; import RESIZE_MODES from './resizeModes'; -const propTypes = { - ...imagePropTypes, - - /** The URI source of the image */ - source: PropTypes.shape({ - uri: PropTypes.string.isRequired, - }).isRequired, -}; - class Image extends React.Component { constructor(props) { super(props); @@ -36,9 +26,11 @@ class Image extends React.Component { this.configureImageSource(); } - // Check if the image source is a URL - if so the `encryptedAuthToken` is appended - // to the source. The natural image dimensions can then be retrieved using this source - // and as a result the `onLoad` event needs to be maunually invoked to return these dimensions + /** + * Check if the image source is a URL - if so the `encryptedAuthToken` is appended + * to the source. The natural image dimensions can then be retrieved using this source + * and as a result the `onLoad` event needs to be maunually invoked to return these dimensions + */ configureImageSource() { const source = this.props.source; let imageSource = source; @@ -70,7 +62,7 @@ class Image extends React.Component { } } -Image.propTypes = propTypes; +Image.propTypes = imagePropTypes; Image.defaultProps = defaultProps; const ImageWithOnyx = withOnyx({ From 0a17bfac6a191518a12ac25c7999a50e899a90f0 Mon Sep 17 00:00:00 2001 From: Thomas Coldwell <31568400+thomas-coldwell@users.noreply.github.com> Date: Tue, 17 Jan 2023 09:01:44 +0000 Subject: [PATCH 18/18] fix: Remove unncessary image load check --- src/components/ImageWithSizeCalculation.js | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/components/ImageWithSizeCalculation.js b/src/components/ImageWithSizeCalculation.js index 3140632cc1ee..64e1cf7f7d7e 100644 --- a/src/components/ImageWithSizeCalculation.js +++ b/src/components/ImageWithSizeCalculation.js @@ -1,7 +1,6 @@ import React, {PureComponent} from 'react'; import {View} from 'react-native'; import PropTypes from 'prop-types'; -import lodashGet from 'lodash/get'; import Log from '../libs/Log'; import styles from '../styles/styles'; import FullscreenLoadingIndicator from './FullscreenLoadingIndicator'; @@ -45,7 +44,7 @@ class ImageWithSizeCalculation extends PureComponent { this.imageLoadingStart = this.imageLoadingStart.bind(this); this.imageLoadingEnd = this.imageLoadingEnd.bind(this); this.onError = this.onError.bind(this); - this.imageLoadedSuccessfuly = this.imageLoadedSuccessfuly.bind(this); + this.imageLoadedSuccessfully = this.imageLoadedSuccessfully.bind(this); } onError() { @@ -60,13 +59,11 @@ class ImageWithSizeCalculation extends PureComponent { this.setState({isLoading: false}); } - imageLoadedSuccessfuly(event) { - if (!lodashGet(event, 'nativeEvent.width', false) || !lodashGet(event, 'nativeEvent.height', false)) { - // Image didn't load properly - return; - } - - this.props.onMeasure({width: event.nativeEvent.width, height: event.nativeEvent.height}); + imageLoadedSuccessfully(event) { + this.props.onMeasure({ + width: event.nativeEvent.width, + height: event.nativeEvent.height, + }); } render() { @@ -89,7 +86,7 @@ class ImageWithSizeCalculation extends PureComponent { onLoadStart={this.imageLoadingStart} onLoadEnd={this.imageLoadingEnd} onError={this.onError} - onLoad={this.imageLoadedSuccessfuly} + onLoad={this.imageLoadedSuccessfully} /> {this.state.isLoading && (