[APP-2170] Render GIFs through ConstrainedImage (#10453)

Co-authored-by: vineyardbovines <spencer@thepope.dev>
This commit is contained in:
Spence Pope
2026-05-12 11:17:46 -04:00
committed by GitHub
parent 859e172651
commit a03cbd8b1a
2 changed files with 60 additions and 61 deletions
+53 -51
View File
@@ -1,13 +1,13 @@
import {useRef, useState} from 'react' import {useRef, useState} from 'react'
import {type StyleProp, View, type ViewStyle} from 'react-native' import {View} from 'react-native'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {clamp} from '#/lib/numbers'
import {type EmbedPlayerParams} from '#/lib/strings/embed-player' import {type EmbedPlayerParams} from '#/lib/strings/embed-player'
import {useAutoplayDisabled} from '#/state/preferences' import {useAutoplayDisabled} from '#/state/preferences'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {Fill} from '#/components/Fill' import {Fill} from '#/components/Fill'
import {ConstrainedImage} from '#/components/images/AutoSizedImage'
import {MediaInsetBorder} from '#/components/MediaInsetBorder' import {MediaInsetBorder} from '#/components/MediaInsetBorder'
import {GifView} from '../../../../../modules/expo-bluesky-gif-view' import {GifView} from '../../../../../modules/expo-bluesky-gif-view'
import {type GifViewStateChangeEvent} from '../../../../../modules/expo-bluesky-gif-view/src/GifView.types' import {type GifViewStateChangeEvent} from '../../../../../modules/expo-bluesky-gif-view/src/GifView.types'
@@ -19,14 +19,12 @@ export function GifEmbed({
altText, altText,
isPreferredAltText, isPreferredAltText,
hideAlt, hideAlt,
style = {width: '100%'},
}: { }: {
params: EmbedPlayerParams params: EmbedPlayerParams
thumb: string | undefined thumb: string | undefined
altText: string altText: string
isPreferredAltText: boolean isPreferredAltText: boolean
hideAlt?: boolean hideAlt?: boolean
style?: StyleProp<ViewStyle>
}) { }) {
const t = useTheme() const t = useTheme()
const {_} = useLingui() const {_} = useLingui()
@@ -53,60 +51,64 @@ export function GifEmbed({
let aspectRatio = 1 let aspectRatio = 1
if (params.dimensions) { if (params.dimensions) {
const ratio = params.dimensions.width / params.dimensions.height const ratio = params.dimensions.width / params.dimensions.height
aspectRatio = clamp(ratio, 0.75, 4) if (!Number.isNaN(ratio)) {
aspectRatio = ratio
}
} }
const constrained = Math.max(aspectRatio, 1 / 2)
return ( return (
<View <ConstrainedImage aspectRatio={constrained} minMobileAspectRatio={14 / 9}>
style={[
a.rounded_md,
a.overflow_hidden,
{backgroundColor: t.palette.black},
{aspectRatio},
style,
]}>
<View <View
style={[ style={[
a.absolute, a.flex_1,
/* a.rounded_md,
* Aspect ratio was being clipped weirdly on web -esb a.overflow_hidden,
*/ {backgroundColor: t.palette.black},
{
top: -2,
bottom: -2,
left: -2,
right: -2,
},
]}> ]}>
<MediaInsetBorder /> <View
<GifPresentationControls style={[
onPress={onPress} a.absolute,
isPlaying={playerState.isPlaying} /*
isLoading={!playerState.isLoaded} * Aspect ratio was being clipped weirdly on web -esb
altText={!hideAlt && isPreferredAltText ? altText : undefined} */
/> {
<GifView top: -2,
source={params.playerUri} bottom: -2,
sources={params.playerSources} left: -2,
placeholderSource={thumb} right: -2,
style={[a.flex_1]} },
autoplay={!autoplayDisabled} ]}>
onPlayerStateChange={onPlayerStateChange} <MediaInsetBorder />
ref={playerRef} <GifPresentationControls
accessibilityHint={_(msg`Animated GIF`)} onPress={onPress}
accessibilityLabel={altText} isPlaying={playerState.isPlaying}
/> isLoading={!playerState.isLoaded}
{!playerState.isPlaying && ( altText={!hideAlt && isPreferredAltText ? altText : undefined}
<Fill
style={[
t.name === 'light' ? t.atoms.bg_contrast_975 : t.atoms.bg,
{
opacity: 0.3,
},
]}
/> />
)} <GifView
source={params.playerUri}
sources={params.playerSources}
placeholderSource={thumb}
style={[a.flex_1]}
autoplay={!autoplayDisabled}
onPlayerStateChange={onPlayerStateChange}
ref={playerRef}
accessibilityHint={_(msg`Animated GIF`)}
accessibilityLabel={altText}
/>
{!playerState.isPlaying && (
<Fill
style={[
t.name === 'light' ? t.atoms.bg_contrast_975 : t.atoms.bg,
{
opacity: 0.3,
},
]}
/>
)}
</View>
</View> </View>
</View> </ConstrainedImage>
) )
} }
+7 -10
View File
@@ -216,16 +216,13 @@ function AltTextInner({
style={[a.text_2xl, a.font_semi_bold, a.leading_tight, a.pb_sm]}> style={[a.text_2xl, a.font_semi_bold, a.leading_tight, a.pb_sm]}>
<Trans>Add alt text</Trans> <Trans>Add alt text</Trans>
</Text> </Text>
<View style={[a.align_center]}> <GifEmbed
<GifEmbed thumb={thumb}
thumb={thumb} altText={altText}
altText={altText} isPreferredAltText={true}
isPreferredAltText={true} params={params}
params={params} hideAlt
hideAlt />
style={[{height: 225}]}
/>
</View>
</View> </View>
</View> </View>
<Dialog.Close /> <Dialog.Close />