diff --git a/src/CONST.js b/src/CONST.js
index fe0876996e66..e1fbe4c194d3 100755
--- a/src/CONST.js
+++ b/src/CONST.js
@@ -862,8 +862,6 @@ const CONST = {
},
TFA_CODE_LENGTH: 6,
-
- CHAT_ATTACHMENT_TOKEN_KEY: 'X-Chat-Attachment-Token',
};
export default CONST;
diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js
index 05f818f337a7..7895b119b726 100755
--- a/src/components/AttachmentModal.js
+++ b/src/components/AttachmentModal.js
@@ -115,7 +115,7 @@ class AttachmentModal extends PureComponent {
* @param {String} sourceURL
*/
downloadAttachment(sourceURL) {
- fileDownload(this.props.isAuthTokenRequired ? addEncryptedAuthTokenToURL(sourceURL) : sourceURL, this.props.originalFileName);
+ fileDownload(sourceURL, this.props.originalFileName);
// At ios, if the keyboard is open while opening the attachment, then after downloading
// the attachment keyboard will show up. So, to fix it we need to dismiss the keyboard.
@@ -229,7 +229,9 @@ class AttachmentModal extends PureComponent {
}
render() {
- const sourceURL = this.state.sourceURL;
+ const sourceURL = this.props.isAuthTokenRequired
+ ? addEncryptedAuthTokenToURL(this.state.sourceURL)
+ : this.state.sourceURL;
const {fileName, fileExtension} = FileUtils.splitExtensionFromFileName(this.props.originalFileName || lodashGet(this.state, 'file.name', ''));
@@ -264,7 +266,6 @@ class AttachmentModal extends PureComponent {
{this.state.sourceURL && (
{
// will appear with a sourceURL that is a blob
if (Str.isPDF(props.sourceURL)
|| (props.file && Str.isPDF(props.file.name || props.translate('attachmentView.unknownFilename')))) {
- const sourceURL = props.isAuthTokenRequired
- ? addEncryptedAuthTokenToURL(props.sourceURL)
- : props.sourceURL;
return (
@@ -70,7 +61,7 @@ const AttachmentView = (props) => {
// both PDFs and images will appear as images when pasted into the the text field
if (Str.isImage(props.sourceURL) || (props.file && Str.isImage(props.file.name))) {
return (
-
+
);
}
diff --git a/src/components/Avatar.js b/src/components/Avatar.js
index bfdc2f8c20e1..05caa431c970 100644
--- a/src/components/Avatar.js
+++ b/src/components/Avatar.js
@@ -1,5 +1,5 @@
import React, {PureComponent} from 'react';
-import {View} from 'react-native';
+import {Image, View} from 'react-native';
import PropTypes from 'prop-types';
import _ from 'underscore';
import stylePropTypes from '../styles/stylePropTypes';
@@ -10,7 +10,6 @@ import * as StyleUtils from '../styles/StyleUtils';
import * as Expensicons from './Icon/Expensicons';
import getAvatarDefaultSource from '../libs/getAvatarDefaultSource';
import styles from '../styles/styles';
-import FastImage from './FastImage';
const propTypes = {
/** Source for the avatar. Can be a URL or an icon. */
@@ -81,7 +80,7 @@ class Avatar extends PureComponent {
)
: (
- {
- this.props.onLoad({nativeEvent: {width, height}});
- });
- }
-
- render() {
- // eslint-disable-next-line
- const { source, onLoad, ...rest } = this.props;
-
- // eslint-disable-next-line
- return ;
- }
-}
-
-FastImage.propTypes = Image.propTypes;
-FastImage.resizeMode = RESIZE_MODES;
-export default FastImage;
diff --git a/src/components/FastImage/index.native.js b/src/components/FastImage/index.native.js
deleted file mode 100644
index bb9501df53b7..000000000000
--- a/src/components/FastImage/index.native.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import RNFastImage from '@pieter-pot/react-native-fast-image';
-
-// eslint-disable-next-line
-const FastImage = (props) => ;
-
-FastImage.displayName = 'FastImage';
-FastImage.propTypes = RNFastImage.propTypes;
-FastImage.resizeMode = RNFastImage.resizeMode;
-export default FastImage;
diff --git a/src/components/ImageView/index.js b/src/components/ImageView/index.js
index ebf8d9faee95..2356ffee8b5f 100644
--- a/src/components/ImageView/index.js
+++ b/src/components/ImageView/index.js
@@ -1,33 +1,20 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
- View, Pressable,
+ View, Image, Pressable,
} from 'react-native';
-import {withOnyx} from 'react-native-onyx';
-import FastImage from '../FastImage';
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 = {
-
- /** Do the urls require an authToken? */
- isAuthTokenRequired: PropTypes.bool,
-
/** URL to full-sized image */
url: PropTypes.string.isRequired,
...windowDimensionsPropTypes,
};
-const defaultProps = {
- isAuthTokenRequired: false,
-};
-
class ImageView extends PureComponent {
constructor(props) {
super(props);
@@ -36,7 +23,6 @@ class ImageView extends PureComponent {
this.onContainerLayoutChanged = this.onContainerLayoutChanged.bind(this);
this.onContainerPressIn = this.onContainerPressIn.bind(this);
this.onContainerPress = this.onContainerPress.bind(this);
- this.imageLoad = this.imageLoad.bind(this);
this.imageLoadingStart = this.imageLoadingStart.bind(this);
this.imageLoadingEnd = this.imageLoadingEnd.bind(this);
this.trackMovement = this.trackMovement.bind(this);
@@ -60,6 +46,9 @@ class ImageView extends PureComponent {
}
componentDidMount() {
+ Image.getSize(this.props.url, (width, height) => {
+ this.setImageRegion(width, height);
+ });
if (this.canUseTouchScreen) {
return;
}
@@ -218,10 +207,6 @@ class ImageView extends PureComponent {
this.setState(prevState => ({isDragging: prevState.isMouseDown}));
}
- imageLoad({nativeEvent}) {
- this.setImageRegion(nativeEvent.width, nativeEvent.height);
- }
-
imageLoadingStart() {
this.setState({isLoading: true});
}
@@ -231,18 +216,14 @@ 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 ? 'center' : 'contain'}
onLoadStart={this.imageLoadingStart}
onLoadEnd={this.imageLoadingEnd}
- onLoad={this.imageLoad}
/>
{this.state.isLoading && (
-
@@ -312,7 +288,4 @@ class ImageView extends PureComponent {
}
ImageView.propTypes = propTypes;
-ImageView.defaultProps = defaultProps;
-export default compose(withWindowDimensions, withOnyx({
- session: {key: ONYXKEYS.SESSION},
-}))(ImageView);
+export default withWindowDimensions(ImageView);
diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js
index 1736af4c53cf..e3b0a6ef0e07 100644
--- a/src/components/ImageView/index.native.js
+++ b/src/components/ImageView/index.native.js
@@ -1,46 +1,35 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
- View, PanResponder, InteractionManager,
+ View, InteractionManager, PanResponder,
} from 'react-native';
+import Image from '@pieter-pot/react-native-fast-image';
import ImageZoom from 'react-native-image-pan-zoom';
+import ImageSize from 'react-native-image-size';
import _ from 'underscore';
import styles from '../../styles/styles';
import variables from '../../styles/variables';
import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions';
import FullscreenLoadingIndicator from '../FullscreenLoadingIndicator';
-import FastImage from '../FastImage';
-import chatAttachmentTokenHeaders from '../../libs/chatAttachmentTokenHeaders';
/**
* On the native layer, we use a image library to handle zoom functionality
*/
const propTypes = {
-
- /** Do the urls require an authToken? */
- isAuthTokenRequired: PropTypes.bool,
-
/** URL to full-sized image */
url: PropTypes.string.isRequired,
...windowDimensionsPropTypes,
};
-const defaultProps = {
- isAuthTokenRequired: false,
-};
-
class ImageView extends PureComponent {
constructor(props) {
super(props);
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: props.windowWidth,
- imageHeight: props.windowHeight,
+ imageWidth: undefined,
+ imageHeight: undefined,
interactionPromise: undefined,
containerHeight: undefined,
};
@@ -58,7 +47,12 @@ class ImageView extends PureComponent {
});
this.imageLoadingStart = this.imageLoadingStart.bind(this);
- this.imageLoad = this.imageLoad.bind(this);
+ this.imageLoadingEnd = this.imageLoadingEnd.bind(this);
+ }
+
+ componentDidMount() {
+ // Wait till animations are over to prevent stutter in navigation animation
+ this.state.interactionPromise = InteractionManager.runAfterInteractions(() => this.calculateImageSize());
}
componentWillUnmount() {
@@ -68,27 +62,13 @@ class ImageView extends PureComponent {
this.state.interactionPromise.cancel();
}
- /**
- * Updates the amount of active touches on the PanResponder on our ImageZoom overlay View
- *
- * @param {Event} e
- * @param {GestureState} gestureState
- * @returns {Boolean}
- */
- updatePanResponderTouches(e, gestureState) {
- if (_.isNumber(gestureState.numberActiveTouches)) {
- this.amountOfTouches = gestureState.numberActiveTouches;
+ calculateImageSize() {
+ if (!this.props.url) {
+ return;
}
-
- // We don't need to set the panResponder since all we care about is checking the gestureState, so return false
- return false;
- }
-
- imageLoad({nativeEvent}) {
- // Wait till animations are over to prevent stutter in navigation animation
- this.state.interactionPromise = InteractionManager.runAfterInteractions(() => {
- let imageWidth = nativeEvent.width;
- let imageHeight = nativeEvent.height;
+ ImageSize.getSize(this.props.url).then(({width, height}) => {
+ let imageWidth = width;
+ let imageHeight = height;
const containerWidth = Math.round(this.props.windowWidth);
const containerHeight = Math.round(this.state.containerHeight);
@@ -104,18 +84,63 @@ 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});
});
}
+ /**
+ * Updates the amount of active touches on the PanResponder on our ImageZoom overlay View
+ *
+ * @param {Event} e
+ * @param {GestureState} gestureState
+ * @returns {Boolean}
+ */
+ updatePanResponderTouches(e, gestureState) {
+ if (_.isNumber(gestureState.numberActiveTouches)) {
+ this.amountOfTouches = gestureState.numberActiveTouches;
+ }
+
+ // We don't need to set the panResponder since all we care about is checking the gestureState, so return false
+ return false;
+ }
+
imageLoadingStart() {
this.setState({isLoading: true});
}
+ imageLoadingEnd() {
+ this.setState({isLoading: false});
+ }
+
render() {
// Default windowHeight accounts for the modal header height
const windowHeight = this.props.windowHeight - variables.contentHeaderHeight;
- const headers = this.props.isAuthTokenRequired ? chatAttachmentTokenHeaders() : undefined;
+
+ // Display thumbnail until Image size calculation is complete
+ if (!this.state.imageWidth || !this.state.imageHeight) {
+ return (
+ {
+ const layout = event.nativeEvent.layout;
+ this.setState({
+ containerHeight: layout.height,
+ });
+ }}
+ >
+
+
+ );
+ }
// Zoom view should be loaded only after measuring actual image dimensions, otherwise it causes blurred images on Android
return (
@@ -127,12 +152,6 @@ class ImageView extends PureComponent {
styles.justifyContentCenter,
styles.overflowHidden,
]}
- onLayout={(event) => {
- const layout = event.nativeEvent.layout;
- this.setState({
- containerHeight: layout.height,
- });
- }}
>
this.zoom = el}
@@ -167,24 +186,16 @@ class ImageView extends PureComponent {
this.imageZoomScale = scale;
}}
>
-
{/**
Create an invisible view on top of the image so we can capture and set the amount of touches before
@@ -212,6 +223,5 @@ class ImageView extends PureComponent {
}
ImageView.propTypes = propTypes;
-ImageView.defaultProps = defaultProps;
export default withWindowDimensions(ImageView);
diff --git a/src/components/ImageWithSizeCalculation.js b/src/components/ImageWithSizeCalculation.js
index f7a32be95368..3b6eee240d11 100644
--- a/src/components/ImageWithSizeCalculation.js
+++ b/src/components/ImageWithSizeCalculation.js
@@ -1,14 +1,10 @@
import React, {PureComponent} from 'react';
-import {View} from 'react-native';
+import {View, Image} 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 makeCancellablePromise from '../libs/MakeCancellablePromise';
import FullscreenLoadingIndicator from './FullscreenLoadingIndicator';
-import ONYXKEYS from '../ONYXKEYS';
-import chatAttachmentTokenHeaders from '../libs/chatAttachmentTokenHeaders';
-import FastImage from './FastImage';
const propTypes = {
/** Url for image to display */
@@ -20,25 +16,11 @@ const propTypes = {
/** Callback fired when the image has been measured. */
onMeasure: PropTypes.func,
-
- /** 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,
- },
};
/**
@@ -57,33 +39,71 @@ 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);
}
- onError() {
- Log.hmmm('Unable to fetch image to calculate size', {url: this.props.url});
+ componentDidMount() {
+ this.calculateImageSize();
}
- imageLoadingStart() {
- this.setState({isLoading: true});
+ componentDidUpdate(prevProps) {
+ if (prevProps.url === this.props.url) {
+ return;
+ }
+
+ this.calculateImageSize();
}
- imageLoadingEnd() {
- this.setState({isLoading: false});
+ componentWillUnmount() {
+ if (!this.getImageSizePromise) {
+ return;
+ }
+
+ this.getImageSizePromise.cancel();
+ }
+
+ /**
+ * @param {String} url
+ * @returns {Promise}
+ */
+ getImageSize(url) {
+ return new Promise((resolve, reject) => {
+ Image.getSize(url, (width, height) => {
+ resolve({width, height});
+ }, (error) => {
+ reject(error);
+ });
+ });
}
- imageLoadedSuccessfuly(event) {
- if (!lodashGet(event, 'nativeEvent.width', false) || !lodashGet(event, 'nativeEvent.height', false)) {
- // Image didn't load properly
+ calculateImageSize() {
+ if (!this.props.url) {
return;
}
- this.props.onMeasure({width: event.nativeEvent.width, height: event.nativeEvent.height});
+ this.getImageSizePromise = makeCancellablePromise(this.getImageSize(this.props.url));
+ this.getImageSizePromise.promise
+ .then(({width, height}) => {
+ if (!width || !height) {
+ // Image didn't load properly
+ return;
+ }
+
+ this.props.onMeasure({width, height});
+ })
+ .catch((error) => {
+ Log.hmmm('Unable to fetch image to calculate size', {error, url: this.props.url});
+ });
+ }
+
+ imageLoadingStart() {
+ this.setState({isLoading: true});
+ }
+
+ imageLoadingEnd() {
+ this.setState({isLoading: false});
}
render() {
- const headers = this.props.isAuthTokenRequired ? chatAttachmentTokenHeaders() : undefined;
return (
-
{this.state.isLoading && (
@@ -100,7 +103,6 @@ class ThumbnailImage extends PureComponent {
diff --git a/src/libs/chatAttachmentTokenHeaders.js b/src/libs/chatAttachmentTokenHeaders.js
deleted file mode 100644
index 60c4d5dc8505..000000000000
--- a/src/libs/chatAttachmentTokenHeaders.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import lodashGet from 'lodash/get';
-import Onyx from 'react-native-onyx';
-import ONYXKEYS from '../ONYXKEYS';
-import CONST from '../CONST';
-
-let encryptedAuthToken = '';
-Onyx.connect({
- key: ONYXKEYS.SESSION,
- callback: session => encryptedAuthToken = lodashGet(session, 'encryptedAuthToken', ''),
-});
-
-/**
- * Create a header object with the encryptedAuthToken for image caching via headers
- *
- * @returns {String}
- */
-export default function () {
- return {
- [CONST.CHAT_ATTACHMENT_TOKEN_KEY]: encryptedAuthToken,
- };
-}