tooot/src/components/GracefullyImage.tsx

137 lines
4.0 KiB
TypeScript
Raw Normal View History

2022-02-12 18:25:53 +01:00
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
2023-01-29 15:32:40 +01:00
import { connectImage } from '@utils/api/helpers/connect'
2021-02-14 00:27:21 +01:00
import { useTheme } from '@utils/styles/ThemeManager'
2023-01-10 14:56:13 +01:00
import React, { useEffect, useState } from 'react'
import {
2021-04-09 21:43:12 +02:00
AccessibilityProps,
Image,
Pressable,
StyleProp,
StyleSheet,
2021-04-01 18:39:53 +02:00
View,
ViewStyle
} from 'react-native'
2021-01-28 01:31:19 +01:00
import { Blurhash } from 'react-native-blurhash'
import FastImage, { ImageStyle } from 'react-native-fast-image'
2021-02-14 00:27:21 +01:00
// blurhas -> if blurhash, show before any loading succeed
// original -> load original
// original, remote -> if original failed, then remote
// preview, original -> first show preview, then original
// preview, original, remote -> first show preview, then original, if original failed, then remote
2021-01-16 00:00:31 +01:00
export interface Props {
2021-04-09 21:43:12 +02:00
accessibilityLabel?: AccessibilityProps['accessibilityLabel']
accessibilityHint?: AccessibilityProps['accessibilityHint']
2021-01-16 00:00:31 +01:00
hidden?: boolean
2022-02-12 18:25:53 +01:00
uri: { preview?: string; original?: string; remote?: string; static?: string }
2021-01-16 00:00:31 +01:00
blurhash?: string
dimension?: { width: number; height: number }
onPress?: () => void
style?: StyleProp<ViewStyle>
2021-01-23 02:41:50 +01:00
imageStyle?: StyleProp<ImageStyle>
2021-02-14 00:27:21 +01:00
// For image viewer when there is no image size available
setImageDimensions?: React.Dispatch<
React.SetStateAction<{
width: number
height: number
}>
>
2023-01-29 17:28:49 +01:00
dim?: boolean
2021-01-16 00:00:31 +01:00
}
2022-04-30 21:29:08 +02:00
const GracefullyImage = ({
accessibilityLabel,
accessibilityHint,
hidden = false,
uri,
blurhash,
dimension,
onPress,
style,
imageStyle,
2023-01-29 17:28:49 +01:00
setImageDimensions,
dim
2022-04-30 21:29:08 +02:00
}: Props) => {
const { reduceMotionEnabled } = useAccessibility()
2023-01-29 17:28:49 +01:00
const { colors, theme } = useTheme()
2022-04-30 21:29:08 +02:00
const [imageLoaded, setImageLoaded] = useState(false)
2021-01-16 00:00:31 +01:00
2023-01-09 22:28:53 +01:00
const [currentUri, setCurrentUri] = useState<string | undefined>(uri.original || uri.remote)
2023-01-29 15:32:40 +01:00
const source: { uri?: string } = {
2023-01-09 22:28:53 +01:00
uri: reduceMotionEnabled && uri.static ? uri.static : currentUri
2022-04-30 21:29:08 +02:00
}
2023-01-10 14:56:13 +01:00
useEffect(() => {
if (
(uri.original ? currentUri !== uri.original : true) &&
(uri.remote ? currentUri !== uri.remote : true)
) {
2023-01-10 14:56:13 +01:00
setCurrentUri(uri.original || uri.remote)
}
}, [currentUri, uri.original, uri.remote])
2021-02-02 22:50:38 +01:00
const blurhashView = () => {
2022-04-30 21:29:08 +02:00
if (hidden || !imageLoaded) {
if (blurhash) {
2022-09-14 21:52:16 +02:00
return <Blurhash decodeAsync blurhash={blurhash} style={styles.placeholder} />
2022-04-30 21:29:08 +02:00
} else {
2022-09-14 21:52:16 +02:00
return <View style={[styles.placeholder, { backgroundColor: colors.shimmerDefault }]} />
2022-04-30 21:29:08 +02:00
}
} else {
return null
}
}
2022-04-30 21:29:08 +02:00
return (
<Pressable
2022-09-14 21:52:16 +02:00
{...(onPress ? { accessibilityRole: 'imagebutton' } : { accessibilityRole: 'image' })}
2022-04-30 21:29:08 +02:00
accessibilityLabel={accessibilityLabel}
accessibilityHint={accessibilityHint}
style={[style, dimension, { backgroundColor: colors.shimmerDefault }]}
2022-09-14 21:52:16 +02:00
{...(onPress ? (hidden ? { disabled: true } : { onPress }) : { disabled: true })}
2022-04-30 21:29:08 +02:00
>
{uri.preview && !imageLoaded ? (
2022-12-15 22:14:59 +01:00
<FastImage
2023-01-29 15:32:40 +01:00
source={connectImage({ uri: uri.preview })}
2022-09-14 21:52:16 +02:00
style={[styles.placeholder, { backgroundColor: colors.shimmerDefault }]}
2021-02-14 00:27:21 +01:00
/>
2022-04-30 21:29:08 +02:00
) : null}
2022-12-15 22:14:59 +01:00
<FastImage
2023-01-29 15:32:40 +01:00
source={connectImage(source)}
2022-12-15 22:14:59 +01:00
style={[{ flex: 1 }, imageStyle]}
2023-01-09 22:28:53 +01:00
onLoad={() => {
setImageLoaded(true)
if (setImageDimensions && source.uri) {
Image.getSize(source.uri, (width, height) => setImageDimensions({ width, height }))
}
}}
onError={() => {
if (uri.original && uri.original === currentUri && uri.remote) {
setCurrentUri(uri.remote)
}
}}
2022-12-15 22:14:59 +01:00
/>
{blurhashView()}
2023-01-29 17:28:49 +01:00
{dim && theme !== 'light' ? (
<View
style={[
styles.placeholder,
{ backgroundColor: 'black', opacity: theme === 'dark_lighter' ? 0.18 : 0.36 }
]}
/>
) : null}
2022-04-30 21:29:08 +02:00
</Pressable>
)
}
2021-01-16 00:00:31 +01:00
const styles = StyleSheet.create({
2021-04-19 17:15:52 +02:00
placeholder: {
2021-02-14 00:27:21 +01:00
width: '100%',
height: '100%',
position: 'absolute'
2021-01-16 00:00:31 +01:00
}
})
export default GracefullyImage