Skip to content

Commit

Permalink
Merge pull request #6442 from aswin-s/aswin-s/issue_5193
Browse files Browse the repository at this point in the history
Fix: Blurred images in attachment view on mobile devices
  • Loading branch information
iwiznia authored Nov 26, 2021
2 parents 2aaa8fd + d6b6055 commit 85d9a21
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import androidx.multidex.MultiDexApplication;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.existfragger.rnimagesize.RNImageSizePackage;
import com.google.firebase.crashlytics.FirebaseCrashlytics;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
Expand Down
2 changes: 2 additions & 0 deletions android/settings.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
rootProject.name = 'NewExpensify'
include ':react-native-image-size'
project(':react-native-image-size').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-size/android')
include ':react-native-config'
project(':react-native-config').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-config/android')
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
Expand Down
31 changes: 30 additions & 1 deletion ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,15 @@ PODS:
- GoogleUtilities/Logger
- hermes-engine (0.7.2)
- libevent (2.1.12)
- libwebp (1.2.1):
- libwebp/demux (= 1.2.1)
- libwebp/mux (= 1.2.1)
- libwebp/webp (= 1.2.1)
- libwebp/demux (1.2.1):
- libwebp/webp
- libwebp/mux (1.2.1):
- libwebp/demux
- libwebp/webp (1.2.1)
- nanopb (2.30908.0):
- nanopb/decode (= 2.30908.0)
- nanopb/encode (= 2.30908.0)
Expand Down Expand Up @@ -509,6 +518,10 @@ PODS:
- React-Core
- RNDateTimePicker (3.5.2):
- React-Core
- RNFastImage (8.5.11):
- React-Core
- SDWebImage (~> 5.11.1)
- SDWebImageWebPCoder (~> 0.8.4)
- RNFBAnalytics (12.3.0):
- Firebase/Analytics (= 8.4.0)
- React-Core
Expand Down Expand Up @@ -563,6 +576,12 @@ PODS:
- React-Core
- RNSVG (12.1.0):
- React
- SDWebImage (5.11.1):
- SDWebImage/Core (= 5.11.1)
- SDWebImage/Core (5.11.1)
- SDWebImageWebPCoder (0.8.4):
- libwebp (~> 1.0)
- SDWebImage/Core (~> 5.10)
- urbanairship-react-native (11.0.2):
- Airship (= 14.4.2)
- React-Core
Expand Down Expand Up @@ -646,6 +665,7 @@ DEPENDENCIES:
- "RNCMaskedView (from `../node_modules/@react-native-masked-view/masked-view`)"
- "RNCPicker (from `../node_modules/@react-native-picker/picker`)"
- "RNDateTimePicker (from `../node_modules/@react-native-community/datetimepicker`)"
- 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`)"
Expand Down Expand Up @@ -685,12 +705,15 @@ SPEC REPOS:
- GoogleUtilities
- hermes-engine
- libevent
- libwebp
- nanopb
- Onfido
- OpenSSL-Universal
- Plaid
- PromisesObjC
- Protobuf
- SDWebImage
- SDWebImageWebPCoder
- YogaKit

EXTERNAL SOURCES:
Expand Down Expand Up @@ -794,6 +817,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/@react-native-picker/picker"
RNDateTimePicker:
:path: "../node_modules/@react-native-community/datetimepicker"
RNFastImage:
:path: "../node_modules/react-native-fast-image"
RNFBAnalytics:
:path: "../node_modules/@react-native-firebase/analytics"
RNFBApp:
Expand Down Expand Up @@ -825,7 +850,7 @@ SPEC CHECKSUMS:
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
DoubleConversion: cf9b38bf0b2d048436d9a82ad2abe1404f11e7de
FBLazyVector: 7b423f9e248eae65987838148c36eec1dbfe0b53
FBReactNativeSpec: 1d564cbdef3e1546843d1f1ceb0e4463b7993e3a
FBReactNativeSpec: 3930028959767285f556bd187e13b896deee31fe
Firebase: 54cdc8bc9c9b3de54f43dab86e62f5a76b47034f
FirebaseABTesting: c3e48ebf5e7e5c674c5a131c68e941d7921d83dc
FirebaseAnalytics: 4751d6a49598a2b58da678cc07df696bcd809ab9
Expand All @@ -849,6 +874,7 @@ SPEC CHECKSUMS:
GoogleUtilities: 3df19e3c24f7bbc291d8b5809aa6b0d41e642437
hermes-engine: 7d97ba46a1e29bacf3e3c61ecb2804a5ddd02d4f
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
libwebp: 98a37e597e40bfdb4c911fc98f2c53d0b12d05fc
nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
Onfido: 116a268e4cb8b767c15285e8071c2e8304673cdf
onfido-react-native-sdk: b8f1b7cbe1adab6479d735275772390161630dcd
Expand Down Expand Up @@ -900,6 +926,7 @@ SPEC CHECKSUMS:
RNCMaskedView: 138134c4d8a9421b4f2bf39055a79aa05c2d47b1
RNCPicker: 6780c753e9e674065db90d9c965920516402579d
RNDateTimePicker: c9911be59b1f8670b9f244b85af3a7c295e175ed
RNFastImage: 1f2cab428712a4baaf78d6169eaec7f622556dd7
RNFBAnalytics: 8ba84c2d31c64374d054c8621b998f25145ffddc
RNFBApp: 64c90ab78b6010ed5c3ade026dfe5ff6442c21fd
RNFBCrashlytics: 1de18b8cc36d9bcf86407c4a354399228cc84a61
Expand All @@ -910,6 +937,8 @@ SPEC CHECKSUMS:
RNReanimated: 833ebd229b31e18a8933ebd0cd744a0f47d88c42
RNScreens: e8e8dd0588b5da0ab57dcca76ab9b2d8987757e0
RNSVG: ce9d996113475209013317e48b05c21ee988d42e
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
SDWebImageWebPCoder: f93010f3f6c031e2f8fb3081ca4ee6966c539815
urbanairship-react-native: 60b4b4235838ff109a2639b639e2ef01d54ad455
Yoga: a7de31c64fe738607e7a3803e3f591a4b1df7393
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
Expand Down
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,13 @@
"react-native-collapsible": "^1.6.0",
"react-native-config": "^1.4.0",
"react-native-document-picker": "^5.1.0",
"react-native-fast-image": "^8.5.11",
"react-native-gesture-handler": "1.9.0",
"react-native-google-places-autocomplete": "^2.4.1",
"react-native-haptic-feedback": "^1.13.0",
"react-native-image-pan-zoom": "^2.1.12",
"react-native-image-picker": "^4.0.3",
"react-native-image-size": "^1.1.3",
"react-native-keyboard-spacer": "^0.4.1",
"react-native-modal": "^11.10.0",
"react-native-onyx": "git+https://github.com/Expensify/react-native-onyx.git#00bcc1520cf6cf7846d8aa40b5161bd1f407341f",
Expand Down
89 changes: 69 additions & 20 deletions src/components/ImageView/index.native.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {View, PanResponder} from 'react-native';
import {View, InteractionManager, PanResponder} from 'react-native';
import Image from 'react-native-fast-image';
import ImageZoom from 'react-native-image-pan-zoom';
import ImageSize from 'react-native-image-size';
import _ from 'underscore';
import ImageWithSizeCalculation from '../ImageWithSizeCalculation';
import styles from '../../styles/styles';
import * as StyleUtils from '../../styles/styles';
import * as StyleUtils from '../../styles/StyleUtils';
import variables from '../../styles/variables';
import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions';

Expand All @@ -24,8 +25,11 @@ class ImageView extends PureComponent {
super(props);

this.state = {
imageWidth: 100,
imageHeight: 100,
thumbnailWidth: 100,
thumbnailHeight: 100,
imageWidth: undefined,
imageHeight: undefined,
interactionPromise: undefined,
};

// Use the default double click interval from the ImageZoom library
Expand All @@ -41,6 +45,34 @@ class ImageView extends PureComponent {
});
}

componentDidMount() {
// Wait till animations are over to prevent stutter in navigation animation
this.state.interactionPromise = InteractionManager.runAfterInteractions(() => this.calculateImageSize());
}

componentWillUnmount() {
if (!this.state.interactionPromise) {
return;
}
this.state.interactionPromise.cancel();
}

calculateImageSize() {
if (!this.props.url) {
return;
}
ImageSize.getSize(this.props.url).then(({width, height}) => {
let imageWidth = width;
let imageHeight = height;
const aspectRatio = (imageHeight / imageWidth);
if (imageWidth > this.props.windowWidth) {
imageWidth = Math.round(this.props.windowWidth);
imageHeight = Math.round(imageWidth * aspectRatio);
}
this.setState({imageHeight, imageWidth});
});
}

/**
* Updates the amount of active touches on the PanResponder on our ImageZoom overlay View
*
Expand All @@ -60,6 +92,30 @@ class ImageView extends PureComponent {
render() {
// Default windowHeight accounts for the modal header height
const windowHeight = this.props.windowHeight - variables.contentHeaderHeight;

// Display thumbnail until Image size calculation is complete
if (!this.state.imageWidth || !this.state.imageHeight) {
return (
<View
style={[
styles.w100,
styles.h100,
styles.alignItemsCenter,
styles.justifyContentCenter,
styles.overflowHidden,
styles.errorOutline,
]}
>
<Image
source={{uri: this.props.url}}
style={StyleUtils.getWidthAndHeightStyle(this.state.thumbnailWidth, this.state.thumbnailHeight)}
resizeMode={Image.resizeMode.contain}
/>
</View>
);
}

// Zoom view should be loaded only after measuring actual image dimensions, otherwise it causes blurred images on Android
return (
<View
style={[
Expand Down Expand Up @@ -103,21 +159,14 @@ class ImageView extends PureComponent {
this.imageZoomScale = scale;
}}
>
<ImageWithSizeCalculation
style={StyleUtils.getWidthAndHeightStyle(this.state.imageWidth, this.state.imageHeight)}
url={this.props.url}
onMeasure={({width, height}) => {
let imageWidth = width;
let imageHeight = height;

if (width > this.props.windowWidth || height > windowHeight) {
const scaleFactor = Math.max(width / this.props.windowWidth, height / windowHeight);
imageHeight = height / scaleFactor;
imageWidth = width / scaleFactor;
}

this.setState({imageHeight, imageWidth});
}}
<Image
style={[
styles.w100,
styles.h100,
this.props.style,
]}
source={{uri: this.props.url}}
resizeMode={Image.resizeMode.contain}
/>
{/**
Create an invisible view on top of the image so we can capture and set the amount of touches before
Expand Down

0 comments on commit 85d9a21

Please sign in to comment.