Show almost-instant preview when opening lightbox (#6000)

* Plumb thumbUri down to the lightbox

* Remove onLoad tracking from lightbox

* Hook up placeholder URI to the image

* Fix NaN causing crash on double tap while offline

* Protect against NaNs in the future
This commit is contained in:
dan
2024-10-29 21:00:28 +00:00
committed by GitHub
parent 339f45ccbb
commit ab492cd77a
9 changed files with 56 additions and 78 deletions
@@ -34,7 +34,6 @@ const SCREEN = {
const MIN_DOUBLE_TAP_SCALE = 2
const MAX_ORIGINAL_IMAGE_ZOOM = 2
const AnimatedImage = Animated.createAnimatedComponent(Image)
const initialTransform = createTransform()
type Props = {
@@ -53,7 +52,6 @@ const ImageItem = ({
isScrollViewBeingDragged,
}: Props) => {
const [isScaled, setIsScaled] = useState(false)
const [isLoaded, setIsLoaded] = useState(false)
const imageDimensions = useImageDimensions(imageSrc)
const committedTransform = useSharedValue(initialTransform)
const panTranslation = useSharedValue({x: 0, y: 0})
@@ -313,20 +311,23 @@ const ImageItem = ({
singleTap,
)
const isLoading = !isLoaded || !imageDimensions
return (
<Animated.View ref={containerRef} style={styles.container}>
{isLoading && (
<ActivityIndicator size="small" color="#FFF" style={styles.loading} />
)}
<Animated.View
ref={containerRef}
// Necessary to make opacity work for both children together.
renderToHardwareTextureAndroid
style={[styles.container, animatedStyle]}>
<ActivityIndicator size="small" color="#FFF" style={styles.loading} />
<GestureDetector gesture={composedGesture}>
<AnimatedImage
<Image
contentFit="contain"
source={{uri: imageSrc.uri}}
style={[styles.image, animatedStyle]}
placeholderContentFit="contain"
placeholder={{uri: imageSrc.thumbUri}}
style={styles.image}
accessibilityLabel={imageSrc.alt}
accessibilityHint=""
onLoad={() => setIsLoaded(true)}
accessibilityIgnoresInvertColors
cachePolicy="memory"
/>
</GestureDetector>
@@ -7,9 +7,8 @@
*/
import React, {useState} from 'react'
import {Dimensions, StyleSheet} from 'react-native'
import {Image} from 'expo-image'
import {ActivityIndicator, Dimensions, StyleSheet} from 'react-native'
import {Gesture, GestureDetector} from 'react-native-gesture-handler'
import Animated, {
interpolate,
runOnJS,
@@ -17,14 +16,12 @@ import Animated, {
useAnimatedStyle,
useSharedValue,
} from 'react-native-reanimated'
import {Image} from 'expo-image'
import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED'
import {Gesture, GestureDetector} from 'react-native-gesture-handler'
import {Dimensions as ImageDimensions, ImageSource} from '../../@types'
import useImageDimensions from '../../hooks/useImageDimensions'
import {ImageSource, Dimensions as ImageDimensions} from '../../@types'
import {ImageLoading} from './ImageLoading'
const SWIPE_CLOSE_OFFSET = 75
const SWIPE_CLOSE_VELOCITY = 1
const SCREEN = Dimensions.get('screen')
@@ -40,8 +37,6 @@ type Props = {
showControls: boolean
}
const AnimatedImage = Animated.createAnimatedComponent(Image)
const ImageItem = ({
imageSrc,
onTap,
@@ -51,7 +46,6 @@ const ImageItem = ({
}: Props) => {
const scrollViewRef = useAnimatedRef<Animated.ScrollView>()
const translationY = useSharedValue(0)
const [loaded, setLoaded] = useState(false)
const [scaled, setScaled] = useState(false)
const imageDimensions = useImageDimensions(imageSrc)
const maxZoomScale = imageDimensions
@@ -141,18 +135,21 @@ const ImageItem = ({
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
maximumZoomScale={maxZoomScale}
contentContainerStyle={styles.imageScrollContainer}
onScroll={scrollHandler}>
{(!loaded || !imageDimensions) && <ImageLoading />}
<AnimatedImage
contentFit="contain"
source={{uri: imageSrc.uri}}
style={[styles.image, animatedStyle]}
accessibilityLabel={imageSrc.alt}
accessibilityHint=""
onLoad={() => setLoaded(true)}
enableLiveTextInteraction={showControls && !scaled}
/>
<Animated.View style={[styles.imageScrollContainer, animatedStyle]}>
<ActivityIndicator size="small" color="#FFF" style={styles.loading} />
<Image
contentFit="contain"
source={{uri: imageSrc.uri}}
placeholderContentFit="contain"
placeholder={{uri: imageSrc.thumbUri}}
style={styles.image}
accessibilityLabel={imageSrc.alt}
accessibilityHint=""
enableLiveTextInteraction={showControls && !scaled}
accessibilityIgnoresInvertColors
/>
</Animated.View>
</Animated.ScrollView>
</GestureDetector>
)
@@ -170,6 +167,13 @@ const styles = StyleSheet.create({
width: SCREEN.width,
height: SCREEN.height,
},
loading: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
},
})
const getZoomRectAfterDoubleTap = (
@@ -1,37 +0,0 @@
/**
* Copyright (c) JOB TODAY S.A. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
import React from 'react'
import {ActivityIndicator, Dimensions, StyleSheet, View} from 'react-native'
const SCREEN = Dimensions.get('screen')
const SCREEN_WIDTH = SCREEN.width
const SCREEN_HEIGHT = SCREEN.height
export const ImageLoading = () => (
<View style={styles.loading}>
<ActivityIndicator size="small" color="#FFF" />
</View>
)
const styles = StyleSheet.create({
listItem: {
width: SCREEN_WIDTH,
height: SCREEN_HEIGHT,
},
loading: {
width: SCREEN_WIDTH,
height: SCREEN_HEIGHT,
alignItems: 'center',
justifyContent: 'center',
},
imageScrollContainer: {
height: SCREEN_HEIGHT,
},
})