Image performance improvements (#126)
* Switch out most images for FastImage * Add image loading placeholders * Fix tests
This commit is contained in:
@@ -36,16 +36,14 @@ describe('SelectedPhoto', () => {
|
||||
|
||||
const selectedPhotoImage = await findByTestId('selectedPhotoImage')
|
||||
expect(selectedPhotoImage).toBeTruthy()
|
||||
// @ts-expect-error
|
||||
expect(selectedPhotoImage).toHaveStyle({width: 250})
|
||||
})
|
||||
|
||||
it('has 2 photos to render', async () => {
|
||||
const {findAllByTestId} = render(<SelectedPhoto {...mockedProps} />)
|
||||
const selectedPhotoImage = await findAllByTestId('selectedPhotoImage')
|
||||
expect(selectedPhotoImage[0]).toBeTruthy()
|
||||
// @ts-expect-error
|
||||
expect(selectedPhotoImage[0]).toHaveStyle({width: 175})
|
||||
expect(selectedPhotoImage[1]).toBeTruthy()
|
||||
expect(selectedPhotoImage[2]).toBeFalsy()
|
||||
})
|
||||
|
||||
it('has 3 photos to render', async () => {
|
||||
@@ -57,8 +55,10 @@ describe('SelectedPhoto', () => {
|
||||
)
|
||||
const selectedPhotoImage = await findAllByTestId('selectedPhotoImage')
|
||||
expect(selectedPhotoImage[0]).toBeTruthy()
|
||||
// @ts-expect-error
|
||||
expect(selectedPhotoImage[0]).toHaveStyle({width: 85})
|
||||
expect(selectedPhotoImage[0]).toBeTruthy()
|
||||
expect(selectedPhotoImage[1]).toBeTruthy()
|
||||
expect(selectedPhotoImage[2]).toBeTruthy()
|
||||
expect(selectedPhotoImage[3]).toBeFalsy()
|
||||
})
|
||||
|
||||
it('removes a photo', async () => {
|
||||
|
||||
@@ -17,6 +17,15 @@ PODS:
|
||||
- hermes-engine/Pre-built (= 0.71.0)
|
||||
- hermes-engine/Pre-built (0.71.0)
|
||||
- libevent (2.1.12)
|
||||
- libwebp (1.2.4):
|
||||
- libwebp/demux (= 1.2.4)
|
||||
- libwebp/mux (= 1.2.4)
|
||||
- libwebp/webp (= 1.2.4)
|
||||
- libwebp/demux (1.2.4):
|
||||
- libwebp/webp
|
||||
- libwebp/mux (1.2.4):
|
||||
- libwebp/demux
|
||||
- libwebp/webp (1.2.4)
|
||||
- RCT-Folly (2021.07.22.00):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
@@ -346,6 +355,10 @@ PODS:
|
||||
- React-Core
|
||||
- RNCClipboard (1.11.1):
|
||||
- React-Core
|
||||
- RNFastImage (8.6.3):
|
||||
- React-Core
|
||||
- SDWebImage (~> 5.11.1)
|
||||
- SDWebImageWebPCoder (~> 0.8.4)
|
||||
- RNFS (2.20.0):
|
||||
- React-Core
|
||||
- RNGestureHandler (2.8.0):
|
||||
@@ -400,6 +413,12 @@ PODS:
|
||||
- React-RCTImage
|
||||
- RNSVG (12.5.0):
|
||||
- React-Core
|
||||
- SDWebImage (5.11.1):
|
||||
- SDWebImage/Core (= 5.11.1)
|
||||
- SDWebImage/Core (5.11.1)
|
||||
- SDWebImageWebPCoder (0.8.5):
|
||||
- libwebp (~> 1.0)
|
||||
- SDWebImage/Core (~> 5.10)
|
||||
- segment-analytics-react-native (2.10.1):
|
||||
- React-Core
|
||||
- sovran-react-native
|
||||
@@ -458,6 +477,7 @@ DEPENDENCIES:
|
||||
- RNBackgroundFetch (from `../node_modules/react-native-background-fetch`)
|
||||
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
|
||||
- "RNCClipboard (from `../node_modules/@react-native-clipboard/clipboard`)"
|
||||
- RNFastImage (from `../node_modules/react-native-fast-image`)
|
||||
- RNFS (from `../node_modules/react-native-fs`)
|
||||
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
|
||||
- RNImageCropPicker (from `../node_modules/react-native-image-crop-picker`)
|
||||
@@ -475,6 +495,9 @@ SPEC REPOS:
|
||||
trunk:
|
||||
- fmt
|
||||
- libevent
|
||||
- libwebp
|
||||
- SDWebImage
|
||||
- SDWebImageWebPCoder
|
||||
- Swime
|
||||
- TOCropViewController
|
||||
|
||||
@@ -571,6 +594,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/@react-native-async-storage/async-storage"
|
||||
RNCClipboard:
|
||||
:path: "../node_modules/@react-native-clipboard/clipboard"
|
||||
RNFastImage:
|
||||
:path: "../node_modules/react-native-fast-image"
|
||||
RNFS:
|
||||
:path: "../node_modules/react-native-fs"
|
||||
RNGestureHandler:
|
||||
@@ -606,6 +631,7 @@ SPEC CHECKSUMS:
|
||||
glog: 476ee3e89abb49e07f822b48323c51c57124b572
|
||||
hermes-engine: f6e715aa6c8bd38de6c13bc85e07b0a337edaa89
|
||||
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
|
||||
libwebp: f62cb61d0a484ba548448a4bd52aabf150ff6eef
|
||||
RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1
|
||||
RCTRequired: fd4d923b964658aa0c4091a32c8b2004c6d9e3a6
|
||||
RCTTypeSafety: c276d85975bde3d8448907235c70bf0da257adfd
|
||||
@@ -645,6 +671,7 @@ SPEC CHECKSUMS:
|
||||
RNBackgroundFetch: 8e16176ff415daac743a6eb57afc8e9e14dbe623
|
||||
RNCAsyncStorage: 8616bd5a58af409453ea4e1b246521bb76578d60
|
||||
RNCClipboard: 2834e1c4af68697089cdd455ee4a4cdd198fa7dd
|
||||
RNFastImage: 5c9c9fed9c076e521b3f509fe79e790418a544e8
|
||||
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
|
||||
RNGestureHandler: 62232ba8f562f7dea5ba1b3383494eb5bf97a4d3
|
||||
RNImageCropPicker: 648356d68fbf9911a1016b3e3723885d28373eda
|
||||
@@ -654,6 +681,8 @@ SPEC CHECKSUMS:
|
||||
RNReanimated: d8d9d3d3801bda5e35e85cdffc871577d044dc2e
|
||||
RNScreens: 34cc502acf1b916c582c60003dc3089fa01dc66d
|
||||
RNSVG: 6adc5c52d2488a476248413064b7f2832e639057
|
||||
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
|
||||
SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d
|
||||
segment-analytics-react-native: cb097e393c3560a0d4cfd877044293e37b0050d9
|
||||
sovran-react-native: fd3dc8f1a4b14acdc4ad25fc6b4ac4f52a2a2a15
|
||||
Swime: d7b2c277503b6cea317774aedc2dce05613f8b0b
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
"react-native": "0.71.1",
|
||||
"react-native-appstate-hook": "^1.0.6",
|
||||
"react-native-background-fetch": "^4.1.8",
|
||||
"react-native-fast-image": "^8.6.3",
|
||||
"react-native-fs": "^2.20.0",
|
||||
"react-native-gesture-handler": "^2.5.0",
|
||||
"react-native-haptic-feedback": "^1.14.0",
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -8,7 +8,7 @@ import {
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {BlurView} from '@react-native-community/blur'
|
||||
import LinearGradient from 'react-native-linear-gradient'
|
||||
import {AutoSizedImage} from '../util/images/AutoSizedImage'
|
||||
import {Image} from '../util/images/Image'
|
||||
import {Text} from '../util/text/Text'
|
||||
import {s, gradients} from '../../lib/styles'
|
||||
import {usePalette} from '../../lib/hooks/usePalette'
|
||||
@@ -38,10 +38,7 @@ export const ExternalEmbed = ({
|
||||
<ActivityIndicator size="large" style={styles.spinner} />
|
||||
</View>
|
||||
) : link.localThumb ? (
|
||||
<AutoSizedImage
|
||||
uri={link.localThumb.path}
|
||||
containerStyle={styles.image}
|
||||
/>
|
||||
<Image uri={link.localThumb.path} style={styles.image} />
|
||||
) : (
|
||||
<LinearGradient
|
||||
colors={[gradients.blueDark.start, gradients.blueDark.end]}
|
||||
@@ -98,7 +95,7 @@ const styles = StyleSheet.create({
|
||||
borderTopLeftRadius: 6,
|
||||
borderTopRightRadius: 6,
|
||||
width: '100%',
|
||||
height: 200,
|
||||
maxHeight: 200,
|
||||
},
|
||||
imageFallback: {
|
||||
height: 160,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, {useCallback} from 'react'
|
||||
import {Image, StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import FastImage from 'react-native-fast-image'
|
||||
import {colors} from '../../lib/styles'
|
||||
|
||||
export const SelectedPhoto = ({
|
||||
@@ -42,7 +43,7 @@ export const SelectedPhoto = ({
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
|
||||
<Image
|
||||
<FastImage
|
||||
testID="selectedPhotoImage"
|
||||
style={[styles.image, imageStyle]}
|
||||
source={{uri: item}}
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import React from 'react'
|
||||
import {StyleSheet, StyleProp, View, ViewStyle, Image} from 'react-native'
|
||||
import {
|
||||
StyleSheet,
|
||||
StyleProp,
|
||||
View,
|
||||
ViewStyle,
|
||||
Image as RNImage,
|
||||
} from 'react-native'
|
||||
import {AppBskyEmbedImages, AppBskyEmbedExternal} from '@atproto/api'
|
||||
import LinearGradient from 'react-native-linear-gradient'
|
||||
import {Link} from '../util/Link'
|
||||
import {Text} from './text/Text'
|
||||
import {AutoSizedImage} from './images/AutoSizedImage'
|
||||
import {Image} from './images/Image'
|
||||
import {ImageLayoutGrid} from './images/ImageLayoutGrid'
|
||||
import {ImagesLightbox} from '../../../state/models/shell-ui'
|
||||
import {useStores} from '../../../state'
|
||||
@@ -37,11 +43,11 @@ export function PostEmbeds({
|
||||
}
|
||||
const onPressIn = (index: number) => {
|
||||
const firstImageToShow = uris[index]
|
||||
Image.prefetch(firstImageToShow)
|
||||
RNImage.prefetch(firstImageToShow)
|
||||
uris.forEach(uri => {
|
||||
if (firstImageToShow !== uri) {
|
||||
// First image already prefeched above
|
||||
Image.prefetch(uri)
|
||||
RNImage.prefetch(uri)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -85,12 +91,12 @@ export function PostEmbeds({
|
||||
} else {
|
||||
return (
|
||||
<View style={[styles.imagesContainer, style]}>
|
||||
<AutoSizedImage
|
||||
<Image
|
||||
uri={embed.images[0].thumb}
|
||||
onPress={() => openLightbox(0)}
|
||||
onLongPress={() => onLongPress(0)}
|
||||
onPressIn={() => onPressIn(0)}
|
||||
containerStyle={styles.singleImage}
|
||||
style={styles.singleImage}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
@@ -105,7 +111,7 @@ export function PostEmbeds({
|
||||
href={link.uri}
|
||||
noFeedback>
|
||||
{link.thumb ? (
|
||||
<AutoSizedImage uri={link.thumb} containerStyle={styles.extImage} />
|
||||
<Image uri={link.thumb} style={styles.extImage} />
|
||||
) : (
|
||||
<LinearGradient
|
||||
colors={[gradients.blueDark.start, gradients.blueDark.end]}
|
||||
@@ -145,6 +151,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
singleImage: {
|
||||
borderRadius: 8,
|
||||
maxHeight: 500,
|
||||
},
|
||||
extOuter: {
|
||||
borderWidth: 1,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React from 'react'
|
||||
import {Image, StyleSheet, View} from 'react-native'
|
||||
import {StyleSheet, View} from 'react-native'
|
||||
import Svg, {Circle, Text, Defs, LinearGradient, Stop} from 'react-native-svg'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import FastImage from 'react-native-fast-image'
|
||||
import {
|
||||
openCamera,
|
||||
openCropper,
|
||||
@@ -102,13 +103,16 @@ export function UserAvatar({
|
||||
bottomOffset={-10}
|
||||
menuWidth={170}>
|
||||
{avatar ? (
|
||||
<Image
|
||||
<FastImage
|
||||
style={{
|
||||
width: size,
|
||||
height: size,
|
||||
borderRadius: Math.floor(size / 2),
|
||||
}}
|
||||
source={{uri: avatar}}
|
||||
source={{
|
||||
uri: avatar,
|
||||
priority: FastImage.priority.high,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
renderSvg(size, initials)
|
||||
@@ -122,10 +126,10 @@ export function UserAvatar({
|
||||
</View>
|
||||
</DropdownButton>
|
||||
) : avatar ? (
|
||||
<Image
|
||||
<FastImage
|
||||
style={{width: size, height: size, borderRadius: Math.floor(size / 2)}}
|
||||
resizeMode="stretch"
|
||||
source={{uri: avatar}}
|
||||
source={{uri: avatar, priority: FastImage.priority.high}}
|
||||
/>
|
||||
) : (
|
||||
renderSvg(size, initials)
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React from 'react'
|
||||
import {StyleSheet, View, Image} from 'react-native'
|
||||
import {StyleSheet, View} from 'react-native'
|
||||
import Svg, {Rect, Defs, LinearGradient, Stop} from 'react-native-svg'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {Image as PickedImage} from 'react-native-image-crop-picker'
|
||||
import FastImage from 'react-native-fast-image'
|
||||
import {colors, gradients} from '../../lib/styles'
|
||||
import {
|
||||
openCamera,
|
||||
@@ -95,7 +96,7 @@ export function UserBanner({
|
||||
bottomOffset={-10}
|
||||
menuWidth={170}>
|
||||
{banner ? (
|
||||
<Image style={styles.bannerImage} source={{uri: banner}} />
|
||||
<FastImage style={styles.bannerImage} source={{uri: banner}} />
|
||||
) : (
|
||||
renderSvg()
|
||||
)}
|
||||
@@ -108,7 +109,7 @@ export function UserBanner({
|
||||
</View>
|
||||
</DropdownButton>
|
||||
) : banner ? (
|
||||
<Image
|
||||
<FastImage
|
||||
style={styles.bannerImage}
|
||||
resizeMode="cover"
|
||||
source={{uri: banner}}
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
import React, {useState, useEffect} from 'react'
|
||||
import {
|
||||
Image,
|
||||
ImageStyle,
|
||||
LayoutChangeEvent,
|
||||
StyleProp,
|
||||
StyleSheet,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
ViewStyle,
|
||||
} from 'react-native'
|
||||
import {Text} from '../text/Text'
|
||||
import {useTheme} from '../../../lib/ThemeContext'
|
||||
import {usePalette} from '../../../lib/hooks/usePalette'
|
||||
import {DELAY_PRESS_IN} from './constants'
|
||||
|
||||
const MAX_HEIGHT = 300
|
||||
|
||||
interface Dim {
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
|
||||
export function AutoSizedImage({
|
||||
uri,
|
||||
onPress,
|
||||
onLongPress,
|
||||
onPressIn,
|
||||
style,
|
||||
containerStyle,
|
||||
}: {
|
||||
uri: string
|
||||
onPress?: () => void
|
||||
style?: StyleProp<ImageStyle>
|
||||
containerStyle?: StyleProp<ViewStyle>
|
||||
}) {
|
||||
const theme = useTheme()
|
||||
const errPal = usePalette('error')
|
||||
const [error, setError] = useState<string | undefined>('')
|
||||
const [imgInfo, setImgInfo] = useState<Dim | undefined>()
|
||||
const [containerInfo, setContainerInfo] = useState<Dim | undefined>()
|
||||
|
||||
useEffect(() => {
|
||||
let aborted = false
|
||||
if (!imgInfo) {
|
||||
Image.getSize(
|
||||
uri,
|
||||
(width: number, height: number) => {
|
||||
if (!aborted) {
|
||||
setImgInfo({width, height})
|
||||
}
|
||||
},
|
||||
(err: any) => {
|
||||
if (!aborted) {
|
||||
setError(String(err))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
return () => {
|
||||
aborted = true
|
||||
}
|
||||
}, [uri, imgInfo])
|
||||
|
||||
const onLayout = (evt: LayoutChangeEvent) => {
|
||||
setContainerInfo({
|
||||
width: evt.nativeEvent.layout.width,
|
||||
height: evt.nativeEvent.layout.height,
|
||||
})
|
||||
}
|
||||
|
||||
let calculatedStyle: StyleProp<ViewStyle> | undefined
|
||||
if (imgInfo && containerInfo) {
|
||||
// imgInfo.height / imgInfo.width = x / containerInfo.width
|
||||
// x = imgInfo.height / imgInfo.width * containerInfo.width
|
||||
calculatedStyle = {
|
||||
height: Math.min(
|
||||
MAX_HEIGHT,
|
||||
(imgInfo.height / imgInfo.width) * containerInfo.width,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={style}>
|
||||
<TouchableOpacity
|
||||
onPress={onPress}
|
||||
onLongPress={onLongPress}
|
||||
onPressIn={onPressIn}
|
||||
delayPressIn={DELAY_PRESS_IN}>
|
||||
{error ? (
|
||||
<View style={[styles.errorContainer, errPal.view, containerStyle]}>
|
||||
<Text style={errPal.text}>{error}</Text>
|
||||
</View>
|
||||
) : calculatedStyle ? (
|
||||
<View style={[styles.container, containerStyle]}>
|
||||
<Image style={calculatedStyle} source={{uri}} />
|
||||
</View>
|
||||
) : (
|
||||
<View
|
||||
style={[
|
||||
style,
|
||||
styles.placeholder,
|
||||
{backgroundColor: theme.palette.default.backgroundLight},
|
||||
]}
|
||||
onLayout={onLayout}
|
||||
/>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
placeholder: {
|
||||
width: '100%',
|
||||
aspectRatio: 1,
|
||||
},
|
||||
errorContainer: {
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 8,
|
||||
},
|
||||
container: {
|
||||
overflow: 'hidden',
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,40 @@
|
||||
import React from 'react'
|
||||
import {StyleProp, StyleSheet, TouchableOpacity, ViewStyle} from 'react-native'
|
||||
import FastImage from 'react-native-fast-image'
|
||||
import {DELAY_PRESS_IN} from './constants'
|
||||
import {LOADING} from '../../../lib/assets'
|
||||
|
||||
export function Image({
|
||||
uri,
|
||||
onPress,
|
||||
onLongPress,
|
||||
onPressIn,
|
||||
style,
|
||||
}: {
|
||||
uri: string
|
||||
onPress?: () => void
|
||||
onLongPress?: () => void
|
||||
onPressIn?: () => void
|
||||
style?: StyleProp<ViewStyle>
|
||||
}) {
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={onPress}
|
||||
onLongPress={onLongPress}
|
||||
onPressIn={onPressIn}
|
||||
delayPressIn={DELAY_PRESS_IN}
|
||||
style={[styles.container, style]}>
|
||||
<FastImage style={styles.image} source={{uri}} defaultSource={LOADING} />
|
||||
</TouchableOpacity>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
overflow: 'hidden',
|
||||
},
|
||||
image: {
|
||||
width: '100%',
|
||||
aspectRatio: 1,
|
||||
},
|
||||
})
|
||||
@@ -1,12 +1,12 @@
|
||||
import React from 'react'
|
||||
import {
|
||||
Image,
|
||||
StyleProp,
|
||||
StyleSheet,
|
||||
TouchableWithoutFeedback,
|
||||
View,
|
||||
ViewStyle,
|
||||
} from 'react-native'
|
||||
import FastImage from 'react-native-fast-image'
|
||||
|
||||
export function ImageHorzList({
|
||||
uris,
|
||||
@@ -21,7 +21,7 @@ export function ImageHorzList({
|
||||
<View style={[styles.flexRow, style]}>
|
||||
{uris.map((uri, i) => (
|
||||
<TouchableWithoutFeedback key={i} onPress={() => onPress?.(i)}>
|
||||
<Image source={{uri}} style={styles.image} />
|
||||
<FastImage source={{uri}} style={styles.image} />
|
||||
</TouchableWithoutFeedback>
|
||||
))}
|
||||
</View>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import React from 'react'
|
||||
import {
|
||||
Image,
|
||||
ImageStyle,
|
||||
LayoutChangeEvent,
|
||||
StyleProp,
|
||||
StyleSheet,
|
||||
@@ -9,6 +7,8 @@ import {
|
||||
View,
|
||||
ViewStyle,
|
||||
} from 'react-native'
|
||||
import FastImage, {ImageStyle as FastImageStyle} from 'react-native-fast-image'
|
||||
import {LOADING} from '../../../lib/assets'
|
||||
import {DELAY_PRESS_IN} from './constants'
|
||||
|
||||
interface Dim {
|
||||
@@ -73,7 +73,7 @@ function ImageLayoutGridInner({
|
||||
onPressIn?: (index: number) => void
|
||||
containerInfo: Dim
|
||||
}) {
|
||||
const size1 = React.useMemo<ImageStyle>(() => {
|
||||
const size1 = React.useMemo<StyleProp<FastImageStyle>>(() => {
|
||||
if (type === 'three') {
|
||||
const size = (containerInfo.width - 10) / 3
|
||||
return {width: size, height: size, resizeMode: 'cover', borderRadius: 4}
|
||||
@@ -82,7 +82,7 @@ function ImageLayoutGridInner({
|
||||
return {width: size, height: size, resizeMode: 'cover', borderRadius: 4}
|
||||
}
|
||||
}, [type, containerInfo])
|
||||
const size2 = React.useMemo<ImageStyle>(() => {
|
||||
const size2 = React.useMemo<StyleProp<FastImageStyle>>(() => {
|
||||
if (type === 'three') {
|
||||
const size = ((containerInfo.width - 10) / 3) * 2 + 5
|
||||
return {width: size, height: size, resizeMode: 'cover', borderRadius: 4}
|
||||
@@ -100,7 +100,11 @@ function ImageLayoutGridInner({
|
||||
onPress={() => onPress?.(0)}
|
||||
onPressIn={() => onPressIn?.(0)}
|
||||
onLongPress={() => onLongPress(0)}>
|
||||
<Image source={{uri: uris[0]}} style={size1} />
|
||||
<FastImage
|
||||
source={{uri: uris[0]}}
|
||||
style={size1}
|
||||
defaultSource={LOADING}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.wSpace} />
|
||||
<TouchableOpacity
|
||||
@@ -108,7 +112,11 @@ function ImageLayoutGridInner({
|
||||
onPress={() => onPress?.(1)}
|
||||
onPressIn={() => onPressIn?.(1)}
|
||||
onLongPress={() => onLongPress(1)}>
|
||||
<Image source={{uri: uris[1]}} style={size1} />
|
||||
<FastImage
|
||||
source={{uri: uris[1]}}
|
||||
style={size1}
|
||||
defaultSource={LOADING}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)
|
||||
@@ -121,7 +129,11 @@ function ImageLayoutGridInner({
|
||||
onPress={() => onPress?.(0)}
|
||||
onPressIn={() => onPressIn?.(0)}
|
||||
onLongPress={() => onLongPress(0)}>
|
||||
<Image source={{uri: uris[0]}} style={size2} />
|
||||
<FastImage
|
||||
source={{uri: uris[0]}}
|
||||
style={size2}
|
||||
defaultSource={LOADING}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.wSpace} />
|
||||
<View>
|
||||
@@ -130,7 +142,11 @@ function ImageLayoutGridInner({
|
||||
onPress={() => onPress?.(1)}
|
||||
onPressIn={() => onPressIn?.(1)}
|
||||
onLongPress={() => onLongPress(1)}>
|
||||
<Image source={{uri: uris[1]}} style={size1} />
|
||||
<FastImage
|
||||
source={{uri: uris[1]}}
|
||||
style={size1}
|
||||
defaultSource={LOADING}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.hSpace} />
|
||||
<TouchableOpacity
|
||||
@@ -138,7 +154,11 @@ function ImageLayoutGridInner({
|
||||
onPress={() => onPress?.(2)}
|
||||
onPressIn={() => onPressIn?.(2)}
|
||||
onLongPress={() => onLongPress(2)}>
|
||||
<Image source={{uri: uris[2]}} style={size1} />
|
||||
<FastImage
|
||||
source={{uri: uris[2]}}
|
||||
style={size1}
|
||||
defaultSource={LOADING}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
@@ -153,7 +173,11 @@ function ImageLayoutGridInner({
|
||||
onPress={() => onPress?.(0)}
|
||||
onPressIn={() => onPressIn?.(0)}
|
||||
onLongPress={() => onLongPress(0)}>
|
||||
<Image source={{uri: uris[0]}} style={size1} />
|
||||
<FastImage
|
||||
source={{uri: uris[0]}}
|
||||
style={size1}
|
||||
defaultSource={LOADING}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.hSpace} />
|
||||
<TouchableOpacity
|
||||
@@ -161,7 +185,11 @@ function ImageLayoutGridInner({
|
||||
onPress={() => onPress?.(2)}
|
||||
onPressIn={() => onPressIn?.(2)}
|
||||
onLongPress={() => onLongPress(2)}>
|
||||
<Image source={{uri: uris[2]}} style={size1} />
|
||||
<FastImage
|
||||
source={{uri: uris[2]}}
|
||||
style={size1}
|
||||
defaultSource={LOADING}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<View style={styles.wSpace} />
|
||||
@@ -171,7 +199,11 @@ function ImageLayoutGridInner({
|
||||
onPress={() => onPress?.(1)}
|
||||
onPressIn={() => onPressIn?.(1)}
|
||||
onLongPress={() => onLongPress(1)}>
|
||||
<Image source={{uri: uris[1]}} style={size1} />
|
||||
<FastImage
|
||||
source={{uri: uris[1]}}
|
||||
style={size1}
|
||||
defaultSource={LOADING}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.hSpace} />
|
||||
<TouchableOpacity
|
||||
@@ -179,7 +211,11 @@ function ImageLayoutGridInner({
|
||||
onPress={() => onPress?.(3)}
|
||||
onPressIn={() => onPressIn?.(3)}
|
||||
onLongPress={() => onLongPress(3)}>
|
||||
<Image source={{uri: uris[3]}} style={size1} />
|
||||
<FastImage
|
||||
source={{uri: uris[3]}}
|
||||
style={size1}
|
||||
defaultSource={LOADING}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {ImageSourcePropType} from 'react-native'
|
||||
import {ImageRequireSource} from 'react-native'
|
||||
|
||||
export const DEF_AVATAR: ImageSourcePropType = require('../../../public/img/default-avatar.jpg')
|
||||
export const TABS_EXPLAINER: ImageSourcePropType = require('../../../public/img/tabs-explainer.jpg')
|
||||
export const CLOUD_SPLASH: ImageSourcePropType = require('../../../public/img/cloud-splash.png')
|
||||
export const DEF_AVATAR: ImageRequireSource = require('../../../public/img/default-avatar.jpg')
|
||||
export const TABS_EXPLAINER: ImageRequireSource = require('../../../public/img/tabs-explainer.jpg')
|
||||
export const CLOUD_SPLASH: ImageRequireSource = require('../../../public/img/cloud-splash.png')
|
||||
export const LOADING: ImageRequireSource = require('../../../public/img/loading.png')
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import {ImageSourcePropType} from 'react-native'
|
||||
import {ImageRequireSource} from 'react-native'
|
||||
|
||||
export const DEF_AVATAR: ImageSourcePropType = {uri: '/img/default-avatar.jpg'}
|
||||
export const TABS_EXPLAINER: ImageSourcePropType = {
|
||||
// @ts-ignore we need to pretend -prf
|
||||
export const DEF_AVATAR: ImageRequireSource = {uri: '/img/default-avatar.jpg'}
|
||||
// @ts-ignore we need to pretend -prf
|
||||
export const TABS_EXPLAINER: ImageRequireSource = {
|
||||
uri: '/img/tabs-explainer.jpg',
|
||||
}
|
||||
export const CLOUD_SPLASH: ImageSourcePropType = {uri: '/img/cloud-splash.png'}
|
||||
// @ts-ignore we need to pretend -prf
|
||||
export const CLOUD_SPLASH: ImageRequireSource = {uri: '/img/cloud-splash.png'}
|
||||
// @ts-ignore we need to pretend -prf
|
||||
export const LOADING: ImageRequireSource = {uri: '/img/loading.png'}
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import React, {useState} from 'react'
|
||||
import {
|
||||
Image,
|
||||
SafeAreaView,
|
||||
StyleSheet,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import {SafeAreaView, StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||
import FastImage, {Source as FISource} from 'react-native-fast-image'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import {Signin} from '../com/login/Signin'
|
||||
import {CreateAccount} from '../com/login/CreateAccount'
|
||||
@@ -64,7 +59,7 @@ export const Login = observer(() => {
|
||||
if (screenState === ScreenState.S_SigninOrCreateAccount) {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Image source={CLOUD_SPLASH} style={styles.bgImg} />
|
||||
<FastImage source={CLOUD_SPLASH as FISource} style={styles.bgImg} />
|
||||
<SafeAreaView testID="noSessionView" style={styles.container}>
|
||||
<ErrorBoundary>
|
||||
<SigninOrCreateAccount
|
||||
|
||||
@@ -11076,6 +11076,11 @@ react-native-dotenv@^3.3.1:
|
||||
dependencies:
|
||||
dotenv "^16.0.3"
|
||||
|
||||
react-native-fast-image@^8.6.3:
|
||||
version "8.6.3"
|
||||
resolved "https://registry.yarnpkg.com/react-native-fast-image/-/react-native-fast-image-8.6.3.tgz#6edc3f9190092a909d636d93eecbcc54a8822255"
|
||||
integrity sha512-Sdw4ESidXCXOmQ9EcYguNY2swyoWmx53kym2zRsvi+VeFCHEdkO+WG1DK+6W81juot40bbfLNhkc63QnWtesNg==
|
||||
|
||||
react-native-fs@^2.20.0:
|
||||
version "2.20.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-fs/-/react-native-fs-2.20.0.tgz#05a9362b473bfc0910772c0acbb73a78dbc810f6"
|
||||
|
||||
Reference in New Issue
Block a user