[RN] Replace cached image implementation

Use react-native-fastimage, which uses 2 full-native image impleentations using
well known and mature (native) libraries.

This gets us rid of 2 libraries which were observerd as a source of bugs and
created trouble with dependencies: react-native-fetch-blob and
react-native-img-cache. They are also no longer well maintained.
This commit is contained in:
Saúl Ibarra Corretgé 2018-07-31 11:44:48 +02:00 committed by Paweł Domas
parent f5a667ad9e
commit 27021ea271
17 changed files with 160 additions and 323 deletions

View File

@ -68,3 +68,9 @@
-dontwarn java.nio.file.* -dontwarn java.nio.file.*
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
-dontwarn okio.** -dontwarn okio.**
# FastImage
-keep public class com.dylanvann.fastimage.* {*;}
-keep public class com.dylanvann.fastimage.** {*;}

View File

@ -25,7 +25,7 @@ dependencies {
compile 'com.facebook.react:react-native:+' compile 'com.facebook.react:react-native:+'
compile project(':react-native-background-timer') compile project(':react-native-background-timer')
compile project(':react-native-fetch-blob') compile project(':react-native-fast-image')
compile project(':react-native-immersive') compile project(':react-native-immersive')
compile project(':react-native-keep-awake') compile project(':react-native-keep-awake')
compile project(':react-native-linear-gradient') compile project(':react-native-linear-gradient')

View File

@ -122,12 +122,12 @@ class ReactInstanceManagerHolder {
.addPackage(new com.BV.LinearGradient.LinearGradientPackage()) .addPackage(new com.BV.LinearGradient.LinearGradientPackage())
.addPackage(new com.calendarevents.CalendarEventsPackage()) .addPackage(new com.calendarevents.CalendarEventsPackage())
.addPackage(new com.corbt.keepawake.KCKeepAwakePackage()) .addPackage(new com.corbt.keepawake.KCKeepAwakePackage())
.addPackage(new com.dylanvann.fastimage.FastImageViewPackage())
.addPackage(new com.facebook.react.shell.MainReactPackage()) .addPackage(new com.facebook.react.shell.MainReactPackage())
.addPackage(new com.i18n.reactnativei18n.ReactNativeI18n()) .addPackage(new com.i18n.reactnativei18n.ReactNativeI18n())
.addPackage(new com.oblador.vectoricons.VectorIconsPackage()) .addPackage(new com.oblador.vectoricons.VectorIconsPackage())
.addPackage(new com.ocetnik.timer.BackgroundTimerPackage()) .addPackage(new com.ocetnik.timer.BackgroundTimerPackage())
.addPackage(new com.oney.WebRTCModule.WebRTCModulePackage()) .addPackage(new com.oney.WebRTCModule.WebRTCModulePackage())
.addPackage(new com.RNFetchBlob.RNFetchBlobPackage())
.addPackage(new com.rnimmersive.RNImmersivePackage()) .addPackage(new com.rnimmersive.RNImmersivePackage())
.addPackage(new com.zmxv.RNSound.RNSoundPackage()) .addPackage(new com.zmxv.RNSound.RNSoundPackage())
.addPackage(new ReactPackageAdapter() { .addPackage(new ReactPackageAdapter() {

View File

@ -3,8 +3,8 @@ rootProject.name = 'jitsi-meet'
include ':app', ':sdk' include ':app', ':sdk'
include ':react-native-background-timer' include ':react-native-background-timer'
project(':react-native-background-timer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-background-timer/android') project(':react-native-background-timer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-background-timer/android')
include ':react-native-fetch-blob' include ':react-native-fast-image'
project(':react-native-fetch-blob').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fetch-blob/android') project(':react-native-fast-image').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fast-image/android')
include ':react-native-immersive' include ':react-native-immersive'
project(':react-native-immersive').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-immersive/android') project(':react-native-immersive').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-immersive/android')
include ':react-native-keep-awake' include ':react-native-keep-awake'

View File

@ -28,8 +28,8 @@ target 'JitsiMeet' do
pod 'react-native-background-timer', pod 'react-native-background-timer',
:path => '../node_modules/react-native-background-timer' :path => '../node_modules/react-native-background-timer'
pod 'react-native-fetch-blob', pod 'react-native-fast-image',
:path => '../node_modules/react-native-fetch-blob' :path => '../node_modules/react-native-fast-image'
pod 'react-native-keep-awake', pod 'react-native-keep-awake',
:path => '../node_modules/react-native-keep-awake' :path => '../node_modules/react-native-keep-awake'
pod 'react-native-locale-detector', pod 'react-native-locale-detector',

View File

@ -1,6 +1,7 @@
PODS: PODS:
- boost-for-react-native (1.63.0) - boost-for-react-native (1.63.0)
- DoubleConversion (1.1.5) - DoubleConversion (1.1.5)
- FLAnimatedImage (1.0.12)
- Folly (2016.09.26.00): - Folly (2016.09.26.00):
- boost-for-react-native - boost-for-react-native
- DoubleConversion - DoubleConversion
@ -12,8 +13,11 @@ PODS:
- React - React
- react-native-calendar-events (1.6.0): - react-native-calendar-events (1.6.0):
- React - React
- react-native-fetch-blob (0.10.6): - react-native-fast-image (4.0.14):
- React/Core - FLAnimatedImage
- React
- SDWebImage/Core
- SDWebImage/GIF
- react-native-keep-awake (2.0.6): - react-native-keep-awake (2.0.6):
- React - React
- react-native-locale-detector (1.0.0): - react-native-locale-detector (1.0.0):
@ -68,6 +72,10 @@ PODS:
- React/Core - React/Core
- RNVectorIcons (4.4.2): - RNVectorIcons (4.4.2):
- React - React
- SDWebImage/Core (4.4.1)
- SDWebImage/GIF (4.4.1):
- FLAnimatedImage (~> 1.0)
- SDWebImage/Core
- yoga (0.55.4.React) - yoga (0.55.4.React)
DEPENDENCIES: DEPENDENCIES:
@ -76,7 +84,7 @@ DEPENDENCIES:
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- react-native-background-timer (from `../node_modules/react-native-background-timer`) - react-native-background-timer (from `../node_modules/react-native-background-timer`)
- react-native-calendar-events (from `../node_modules/react-native-calendar-events`) - react-native-calendar-events (from `../node_modules/react-native-calendar-events`)
- react-native-fetch-blob (from `../node_modules/react-native-fetch-blob`) - react-native-fast-image (from `../node_modules/react-native-fast-image`)
- react-native-keep-awake (from `../node_modules/react-native-keep-awake`) - react-native-keep-awake (from `../node_modules/react-native-keep-awake`)
- react-native-locale-detector (from `../node_modules/react-native-locale-detector`) - react-native-locale-detector (from `../node_modules/react-native-locale-detector`)
- react-native-webrtc (from `../node_modules/react-native-webrtc`) - react-native-webrtc (from `../node_modules/react-native-webrtc`)
@ -98,6 +106,8 @@ DEPENDENCIES:
SPEC REPOS: SPEC REPOS:
https://github.com/cocoapods/specs.git: https://github.com/cocoapods/specs.git:
- boost-for-react-native - boost-for-react-native
- FLAnimatedImage
- SDWebImage
EXTERNAL SOURCES: EXTERNAL SOURCES:
DoubleConversion: DoubleConversion:
@ -112,8 +122,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-background-timer" :path: "../node_modules/react-native-background-timer"
react-native-calendar-events: react-native-calendar-events:
:path: "../node_modules/react-native-calendar-events" :path: "../node_modules/react-native-calendar-events"
react-native-fetch-blob: react-native-fast-image:
:path: "../node_modules/react-native-fetch-blob" :path: "../node_modules/react-native-fast-image"
react-native-keep-awake: react-native-keep-awake:
:path: "../node_modules/react-native-keep-awake" :path: "../node_modules/react-native-keep-awake"
react-native-locale-detector: react-native-locale-detector:
@ -132,20 +142,22 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
DoubleConversion: e22e0762848812a87afd67ffda3998d9ef29170c DoubleConversion: e22e0762848812a87afd67ffda3998d9ef29170c
FLAnimatedImage: 4a0b56255d9b05f18b6dd7ee06871be5d3b89e31
Folly: 211775e49d8da0ca658aebc8eab89d642935755c Folly: 211775e49d8da0ca658aebc8eab89d642935755c
glog: 1de0bb937dccdc981596d3b5825ebfb765017ded glog: 1de0bb937dccdc981596d3b5825ebfb765017ded
React: aa2040dbb6f317b95314968021bd2888816e03d5 React: aa2040dbb6f317b95314968021bd2888816e03d5
react-native-background-timer: 63dcbf37dbcf294b5c6c071afcdc661fa06a7594 react-native-background-timer: 63dcbf37dbcf294b5c6c071afcdc661fa06a7594
react-native-calendar-events: fe6fbc8ed337a7423c98f2c9012b25f20444de09 react-native-calendar-events: fe6fbc8ed337a7423c98f2c9012b25f20444de09
react-native-fetch-blob: 63394b1d7b0781547b3e4463b3195790177b1222 react-native-fast-image: cba3d9bf9c2cf8ddb643d887a686c53a5dd90a2c
react-native-keep-awake: 0de4bd66de0c23178107dce0c2fcc3354b2a8e94 react-native-keep-awake: 0de4bd66de0c23178107dce0c2fcc3354b2a8e94
react-native-locale-detector: d1b2c6fe5abb56e3a1efb6c2d6f308c05c4251f1 react-native-locale-detector: d1b2c6fe5abb56e3a1efb6c2d6f308c05c4251f1
react-native-webrtc: 31b6d3f1e3e2ce373aa43fd682b04367250f807d react-native-webrtc: 31b6d3f1e3e2ce373aa43fd682b04367250f807d
ReactNativePermissions: 9f2d9c45c98800795e6c2ed330e25d11a66a8169 ReactNativePermissions: 9f2d9c45c98800795e6c2ed330e25d11a66a8169
RNSound: b360b3862d3118ed1c74bb9825696b5957686ac4 RNSound: b360b3862d3118ed1c74bb9825696b5957686ac4
RNVectorIcons: c0dbfbf6068fefa240c37b0f71bd03b45dddac44 RNVectorIcons: c0dbfbf6068fefa240c37b0f71bd03b45dddac44
SDWebImage: 47e9b5b925cbce75946c23f0c42dd19464189af4
yoga: a23273df0088bf7f2bb7e5d7b00044ea57a2a54a yoga: a23273df0088bf7f2bb7e5d7b00044ea57a2a54a
PODFILE CHECKSUM: e24d0131e937934fbe4d1f0b7ad5947ee0192f58 PODFILE CHECKSUM: 1d5c8382f73d9540fac68d93b32e1d3b58d069ee
COCOAPODS: 1.5.3 COCOAPODS: 1.5.3

38
package-lock.json generated
View File

@ -5573,11 +5573,6 @@
"randomfill": "^1.0.3" "randomfill": "^1.0.3"
} }
}, },
"crypto-js": {
"version": "3.1.9-1",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.9-1.tgz",
"integrity": "sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg="
},
"css-color-list": { "css-color-list": {
"version": "0.0.1", "version": "0.0.1",
"resolved": "https://registry.npmjs.org/css-color-list/-/css-color-list-0.0.1.tgz", "resolved": "https://registry.npmjs.org/css-color-list/-/css-color-list-0.0.1.tgz",
@ -12715,35 +12710,12 @@
"jssha": "^2.2.0" "jssha": "^2.2.0"
} }
}, },
"react-native-fetch-blob": { "react-native-fast-image": {
"version": "github:joltup/react-native-fetch-blob#1f9a1761aea4e37bd672bd0d233f3adf0e113a11", "version": "4.0.14",
"from": "github:joltup/react-native-fetch-blob#1f9a1761aea4e37bd672bd0d233f3adf0e113a11", "resolved": "https://registry.npmjs.org/react-native-fast-image/-/react-native-fast-image-4.0.14.tgz",
"integrity": "sha512-MeRgL70JxoY/hn8ZRGBsDED9SGvTEeznneL//fWZyLaG0CM+w2CH4QXAMvADnIvu2RFd8WQWNii6c6VOpVe4Tg==",
"requires": { "requires": {
"base-64": "0.1.0", "prop-types": "^15.5.10"
"glob": "7.0.6"
},
"dependencies": {
"glob": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz",
"integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=",
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.2",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
}
}
},
"react-native-img-cache": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/react-native-img-cache/-/react-native-img-cache-1.5.2.tgz",
"integrity": "sha1-6HG4MJk3t/mSbgmwFuTk5nWKOfo=",
"requires": {
"crypto-js": "^3.1.9-1"
} }
}, },
"react-native-immersive": { "react-native-immersive": {

View File

@ -60,8 +60,7 @@
"react-native-background-timer": "2.0.0", "react-native-background-timer": "2.0.0",
"react-native-calendar-events": "github:jitsi/react-native-calendar-events#cad37355f36d17587d84af72b0095e8cc5fd3df9", "react-native-calendar-events": "github:jitsi/react-native-calendar-events#cad37355f36d17587d84af72b0095e8cc5fd3df9",
"react-native-callstats": "3.52.0", "react-native-callstats": "3.52.0",
"react-native-fetch-blob": "github:joltup/react-native-fetch-blob#1f9a1761aea4e37bd672bd0d233f3adf0e113a11", "react-native-fast-image": "4.0.14",
"react-native-img-cache": "1.5.2",
"react-native-immersive": "1.1.0", "react-native-immersive": "1.1.0",
"react-native-keep-awake": "2.0.6", "react-native-keep-awake": "2.0.6",
"react-native-linear-gradient": "2.4.0", "react-native-linear-gradient": "2.4.0",

View File

@ -1,10 +1,9 @@
// @flow // @flow
import React, { Component } from 'react'; import React, { Component, Fragment } from 'react';
import { Image, View } from 'react-native'; import { Image, View } from 'react-native';
import FastImage from 'react-native-fast-image';
import { CachedImage, ImageCache } from '../../../mobile/image-cache';
import { Platform } from '../../react';
import { ColorPalette } from '../../styles'; import { ColorPalette } from '../../styles';
import styles from './styles'; import styles from './styles';
@ -46,7 +45,8 @@ type Props = {
*/ */
type State = { type State = {
backgroundColor: string, backgroundColor: string,
source: number | { uri: string } source: ?{ uri: string },
useDefaultAvatar: boolean
}; };
/** /**
@ -68,6 +68,9 @@ export default class Avatar extends Component<Props, State> {
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);
// Bind event handlers so they are only bound once per instance.
this._onAvatarLoaded = this._onAvatarLoaded.bind(this);
// Fork (in Facebook/React speak) the prop uri because Image will // Fork (in Facebook/React speak) the prop uri because Image will
// receive it through a source object. Additionally, other props may be // receive it through a source object. Additionally, other props may be
// forked as well. // forked as well.
@ -94,18 +97,8 @@ export default class Avatar extends Component<Props, State> {
if (prevURI !== nextURI || assignState) { if (prevURI !== nextURI || assignState) {
const nextState = { const nextState = {
backgroundColor: this._getBackgroundColor(nextProps), backgroundColor: this._getBackgroundColor(nextProps),
source: undefined,
/** useDefaultAvatar: true
* The source of the {@link Image} which is the actual
* representation of this {@link Avatar}. The state
* {@code source} was explicitly introduced in order to reduce
* unnecessary renders.
*
* @type {{
* uri: string
* }}
*/
source: _DEFAULT_SOURCE
}; };
if (assignState) { if (assignState) {
@ -130,7 +123,14 @@ export default class Avatar extends Component<Props, State> {
// an image retrieval action. // an image retrieval action.
if (nextURI && !nextURI.startsWith('#')) { if (nextURI && !nextURI.startsWith('#')) {
const nextSource = { uri: nextURI }; const nextSource = { uri: nextURI };
const observer = () => {
if (assignState) {
// eslint-disable-next-line react/no-direct-mutation-state
this.state = {
...this.state,
source: nextSource
};
} else {
this._unmounted || this.setState((prevState, props) => { this._unmounted || this.setState((prevState, props) => {
if (props.uri === nextURI if (props.uri === nextURI
&& (!prevState.source && (!prevState.source
@ -140,22 +140,6 @@ export default class Avatar extends Component<Props, State> {
return {}; return {};
}); });
};
// Wait for the source/URI to load.
if (ImageCache) {
ImageCache.get().on(
nextSource,
observer,
/* immutable */ true);
} else if (assignState) {
// eslint-disable-next-line react/no-direct-mutation-state
this.state = {
...this.state,
source: nextSource
};
} else {
observer();
} }
} }
} }
@ -204,56 +188,54 @@ export default class Avatar extends Component<Props, State> {
} }
/** /**
* Implements React's {@link Component#render()}. * Helper which computes the style for the {@code Image} / {@code FastImage}
* component.
* *
* @inheritdoc * @private
* @returns {Object}
*/ */
render() { _getImageStyle() {
// Propagate all props of this Avatar but the ones consumed by this const { size } = this.props;
// Avatar to the Image it renders.
const {
/* eslint-disable no-unused-vars */
// The following are forked in state: return {
uri: forked0,
/* eslint-enable no-unused-vars */
size,
...props
} = this.props;
const {
backgroundColor,
source
} = this.state;
// Compute the base style
const borderRadius = size / 2;
const style = {
...styles.avatar, ...styles.avatar,
borderRadius: size / 2,
// XXX Workaround for Android: for radii < 80 the border radius
// doesn't work properly, but applying a radius twice as big seems
// to do the trick.
borderRadius:
Platform.OS === 'android' && borderRadius < 80
? size * 2
: borderRadius,
height: size, height: size,
width: size width: size
}; };
}
// If we're rendering the _DEFAULT_SOURCE, then we want to do some _onAvatarLoaded: () => void;
// additional fu like having automagical colors generated per
// participant, transparency to make the intermediate state while
// downloading the remote image a little less "in your face", etc.
let styleWithBackgroundColor;
if (source === _DEFAULT_SOURCE && backgroundColor) { /**
styleWithBackgroundColor = { * Handler called when the remote image was loaded. When this happens we
...style, * show that instead of the default locally generated one.
*
* @private
* @returns {void}
*/
_onAvatarLoaded() {
this._unmounted || this.setState({ useDefaultAvatar: false });
}
/**
* Renders a default, locally generated avatar image.
*
* @private
* @returns {ReactElement}
*/
_renderDefaultAvatar() {
// When using a local image, react-native-fastimage falls back to a
// regular Image, so we need to wrap it in a view to make it round.
// https://github.com/facebook/react-native/issues/3198
const { backgroundColor, useDefaultAvatar } = this.state;
const imageStyle = this._getImageStyle();
const viewStyle = {
...imageStyle,
backgroundColor, backgroundColor,
display: useDefaultAvatar ? 'flex' : 'none',
// FIXME @lyubomir: Without the opacity bellow I feel like the // FIXME @lyubomir: Without the opacity bellow I feel like the
// avatar colors are too strong. Besides, we use opacity for the // avatar colors are too strong. Besides, we use opacity for the
@ -263,47 +245,57 @@ export default class Avatar extends Component<Props, State> {
opacity: 0.1, opacity: 0.1,
overflow: 'hidden' overflow: 'hidden'
}; };
}
// If we're styling with backgroundColor, we need to wrap the Image in a return (
// View because of a bug in React Native for Android: <View style = { viewStyle }>
// https://github.com/facebook/react-native/issues/3198 <Image
let imageStyle;
let viewStyle;
if (styleWithBackgroundColor) {
if (Platform.OS === 'android') {
imageStyle = style;
viewStyle = styleWithBackgroundColor;
} else {
imageStyle = styleWithBackgroundColor;
}
} else {
imageStyle = style;
}
let element
= React.createElement(
// XXX CachedImage removed support for images which clearly do
// not need caching.
typeof source === 'number' ? Image : CachedImage,
{
...props,
// The Image adds a fade effect without asking, so lets // The Image adds a fade effect without asking, so lets
// explicitly disable it. More info here: // explicitly disable it. More info here:
// https://github.com/facebook/react-native/issues/10194 // https://github.com/facebook/react-native/issues/10194
fadeDuration: 0, fadeDuration = { 0 }
resizeMode: 'contain', resizeMode = 'contain'
source, source = { _DEFAULT_SOURCE }
style: imageStyle style = { imageStyle } />
}); </View>
);
if (viewStyle) {
element = React.createElement(View, { style: viewStyle }, element);
} }
return element; /**
* Renders an avatar using a remote image.
*
* @private
* @returns {ReactElement}
*/
_renderAvatar() {
const { source, useDefaultAvatar } = this.state;
const style = {
...this._getImageStyle(),
display: useDefaultAvatar ? 'none' : 'flex'
};
return (
<FastImage
onLoad = { this._onAvatarLoaded }
resizeMode = 'contain'
source = { source }
style = { style } />
);
}
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
*/
render() {
const { source, useDefaultAvatar } = this.state;
return (
<Fragment>
{ source && this._renderAvatar() }
{ useDefaultAvatar && this._renderDefaultAvatar() }
</Fragment>
);
} }
} }

View File

@ -2,6 +2,7 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Text, View } from 'react-native'; import { Text, View } from 'react-native';
import FastImage from 'react-native-fast-image';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { translate } from '../../i18n'; import { translate } from '../../i18n';
@ -11,7 +12,6 @@ import {
shouldRenderVideoTrack, shouldRenderVideoTrack,
VideoTrack VideoTrack
} from '../../media'; } from '../../media';
import { prefetch } from '../../../mobile/image-cache';
import { Container, TintedView } from '../../react'; import { Container, TintedView } from '../../react';
import { TestHint } from '../../testing/components'; import { TestHint } from '../../testing/components';
import { getTrackByMediaTypeAndParticipant } from '../../tracks'; import { getTrackByMediaTypeAndParticipant } from '../../tracks';
@ -303,7 +303,8 @@ function _mapStateToProps(state, ownProps) {
// ParticipantView knows before Avatar that an avatar URL will be used // ParticipantView knows before Avatar that an avatar URL will be used
// so it's advisable to prefetch here. // so it's advisable to prefetch here.
avatar && prefetch({ uri: avatar }); avatar && !avatar.startsWith('#')
&& FastImage.preload([ { uri: avatar } ]);
} }
return { return {

View File

@ -1,31 +0,0 @@
import { ImageCache } from './';
/**
* Notifies about the successful download of an {@code Image} source. The name
* is inspired by {@code Image}. The downloaded {@code Image} source is not
* available because (1) I do not know how to get it from {@link ImageCache} and
* (2) we do not need it bellow. The function was explicitly introduced to cut
* down on unnecessary {@code ImageCache} {@code observer} instances.
*
* @private
* @returns {void}
*/
function _onLoad() {
// ImageCache requires an observer; otherwise, we do not need it because we
// merely want to initiate the download and do not care what happens with it
// afterwards.
}
/**
* Initiates the retrieval of a specific {@code Image} source (if it has not
* been initiated already). Due to limitations of {@link ImageCache}, the source
* may have at most one {@code uri}. The name is inspired by {@code Image}.
*
* @param {Object} source - The {@code Image} source with preferably exactly
* one {@code uri}.
* @public
* @returns {void}
*/
export function prefetch(source) {
ImageCache && ImageCache.get().on(source, _onLoad, /* immutable */ true);
}

View File

@ -1,4 +0,0 @@
export * from './functions';
export * from './react-native-img-cache';
import './middleware';

View File

@ -1,91 +0,0 @@
/* @flow */
import { APP_WILL_MOUNT } from '../../base/app';
import {
getAvatarURL,
getLocalParticipant,
getParticipantById,
PARTICIPANT_ID_CHANGED,
PARTICIPANT_JOINED,
PARTICIPANT_UPDATED
} from '../../base/participants';
import { MiddlewareRegistry } from '../../base/redux';
import { ImageCache, prefetch } from './';
/**
* The indicator which determines whether avatar URLs are to be prefetched in
* the middleware here. Unless/until the implementation starts observing the
* redux store instead of the respective redux actions, the value should very
* likely be {@code false} because the middleware here is pretty much the last
* to get a chance to figure out that an avatar URL may be used. Besides, it is
* somewhat uninformed to download just about anything that may eventually be
* used or not.
*
* @private
* @type {boolean}
*/
const _PREFETCH_AVATAR_URLS = false;
/**
* Middleware which captures app startup and conference actions in order to
* clear the image cache.
*
* @returns {Function}
*/
MiddlewareRegistry.register(({ getState }) => next => action => {
switch (action.type) {
case APP_WILL_MOUNT:
// XXX CONFERENCE_FAILED/LEFT are no longer used here because they
// are tricky to get right as detectors of the moments in time at which
// CachedImage is not used. Anyway, if ImageCache is to be cleared from
// time to time, SET_LOCATION_URL is a much easier detector of such
// opportune times. Fixes at least one 100%-reproducible case of
// "TypeError: Cannot read property handlers of undefined." Anyway, in
// order to reduce the re-downloading of the same avatars, eventually we
// decided to not clear during the runtime of the app (other that at the
// beginning that is).
ImageCache && ImageCache.get().clear();
break;
case PARTICIPANT_ID_CHANGED:
case PARTICIPANT_JOINED:
case PARTICIPANT_UPDATED: {
if (!_PREFETCH_AVATAR_URLS) {
break;
}
const result = next(action);
// Initiate the downloads of participants' avatars as soon as possible.
// 1. Figure out the participant (instance).
let { participant } = action;
if (participant) {
if (participant.id) {
participant = getParticipantById(getState, participant.id);
} else if (participant.local) {
participant = getLocalParticipant(getState);
} else {
participant = undefined;
}
} else if (action.oldValue && action.newValue) {
participant = getParticipantById(getState, action.newValue);
}
if (participant) {
// 2. Get the participant's avatar URL.
const uri = getAvatarURL(participant);
if (uri) {
// 3. Initiate the download of the participant's avatar.
prefetch({ uri });
}
}
return result;
}
}
return next(action);
});

View File

@ -1 +0,0 @@
export * from './react-native-img-cache.yes';

View File

@ -1 +0,0 @@
export * from './react-native-img-cache.yes';

View File

@ -1,16 +0,0 @@
// XXX The third-party react-native modules react-native-fetch-blob utilizes the
// same HTTP library as react-native i.e. okhttp. Unfortunately, that means that
// the versions of okhttp on which react-native and react-native-fetch-blob
// depend may have incompatible APIs. Such an incompatibility will be made
// apparent at compile time and the developer doing the compilation may choose
// to not compile react-native-fetch-blob's source code.
// XXX The choice between the use of react-native-img-cache could've been done
// at runtime based on whether NativeModules.RNFetchBlob is defined if only
// react-native-fetch-blob would've completely protected itself. At the time of
// this writing its source code appears to be attempting to protect itself from
// missing native binaries but that protection is incomplete and there's a
// TypeError.
import { Image } from 'react-native';
export { Image as CachedImage, undefined as ImageCache };

View File

@ -1 +0,0 @@
export { CachedImage, ImageCache } from 'react-native-img-cache';