Add <DeferReveal> to defer expensive views
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
import {ReactNode, useEffect, useState} from 'react'
|
||||
|
||||
export function DeferReveal({
|
||||
children,
|
||||
defer,
|
||||
// To verify, toggle `forceReveal` between `true` and `false`.
|
||||
iVerifiedThereAreNoLayoutJumps: _unused,
|
||||
forceReveal,
|
||||
}: {
|
||||
children: ReactNode
|
||||
defer: boolean
|
||||
iVerifiedThereAreNoLayoutJumps: true
|
||||
forceReveal?: never // DEV-only
|
||||
}) {
|
||||
const [isReady, setIsReady] = useState(!defer)
|
||||
useEffect(() => {
|
||||
if (!isReady) {
|
||||
setIsReady(true)
|
||||
}
|
||||
}, [isReady])
|
||||
const finalIsReady = forceReveal ?? isReady
|
||||
return finalIsReady ? children : null
|
||||
}
|
||||
@@ -8,8 +8,10 @@ import {useLingui} from '@lingui/react'
|
||||
import {HandleRef, useHandleRef} from '#/lib/hooks/useHandleRef'
|
||||
import type {Dimensions} from '#/lib/media/types'
|
||||
import {isNative} from '#/platform/detection'
|
||||
import {isAndroid} from '#/platform/detection'
|
||||
import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
|
||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||
import {DeferReveal} from '#/components/DeferReveal'
|
||||
import {ArrowsDiagonalOut_Stroke2_Corner0_Rounded as Fullscreen} from '#/components/icons/ArrowsDiagonal'
|
||||
import {MediaInsetBorder} from '#/components/MediaInsetBorder'
|
||||
import {Text} from '#/components/Typography'
|
||||
@@ -107,84 +109,86 @@ export function AutoSizedImage({
|
||||
|
||||
const contents = (
|
||||
<View ref={containerRef} collapsable={false} style={{flex: 1}}>
|
||||
<Image
|
||||
style={[a.w_full, a.h_full]}
|
||||
source={image.thumb}
|
||||
accessible={true} // Must set for `accessibilityLabel` to work
|
||||
accessibilityIgnoresInvertColors
|
||||
accessibilityLabel={image.alt}
|
||||
accessibilityHint=""
|
||||
onLoad={e => {
|
||||
fetchedDimsRef.current = {
|
||||
width: e.source.width,
|
||||
height: e.source.height,
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<MediaInsetBorder />
|
||||
<DeferReveal defer={isAndroid} iVerifiedThereAreNoLayoutJumps>
|
||||
<Image
|
||||
style={[a.w_full, a.h_full]}
|
||||
source={image.thumb}
|
||||
accessible={true} // Must set for `accessibilityLabel` to work
|
||||
accessibilityIgnoresInvertColors
|
||||
accessibilityLabel={image.alt}
|
||||
accessibilityHint=""
|
||||
onLoad={e => {
|
||||
fetchedDimsRef.current = {
|
||||
width: e.source.width,
|
||||
height: e.source.height,
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<MediaInsetBorder />
|
||||
|
||||
{(hasAlt || isCropped) && !hideBadge ? (
|
||||
<View
|
||||
accessible={false}
|
||||
style={[
|
||||
a.absolute,
|
||||
a.flex_row,
|
||||
{
|
||||
bottom: a.p_xs.padding,
|
||||
right: a.p_xs.padding,
|
||||
gap: 3,
|
||||
},
|
||||
largeAlt && [
|
||||
{(hasAlt || isCropped) && !hideBadge ? (
|
||||
<View
|
||||
accessible={false}
|
||||
style={[
|
||||
a.absolute,
|
||||
a.flex_row,
|
||||
{
|
||||
gap: 4,
|
||||
bottom: a.p_xs.padding,
|
||||
right: a.p_xs.padding,
|
||||
gap: 3,
|
||||
},
|
||||
],
|
||||
]}>
|
||||
{isCropped && (
|
||||
<View
|
||||
style={[
|
||||
a.rounded_xs,
|
||||
t.atoms.bg_contrast_25,
|
||||
largeAlt && [
|
||||
{
|
||||
padding: 3,
|
||||
opacity: 0.8,
|
||||
gap: 4,
|
||||
},
|
||||
largeAlt && [
|
||||
],
|
||||
]}>
|
||||
{isCropped && (
|
||||
<View
|
||||
style={[
|
||||
a.rounded_xs,
|
||||
t.atoms.bg_contrast_25,
|
||||
{
|
||||
padding: 5,
|
||||
padding: 3,
|
||||
opacity: 0.8,
|
||||
},
|
||||
],
|
||||
]}>
|
||||
<Fullscreen
|
||||
fill={t.atoms.text_contrast_high.color}
|
||||
width={largeAlt ? 18 : 12}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
{hasAlt && (
|
||||
<View
|
||||
style={[
|
||||
a.justify_center,
|
||||
a.rounded_xs,
|
||||
t.atoms.bg_contrast_25,
|
||||
{
|
||||
padding: 3,
|
||||
opacity: 0.8,
|
||||
},
|
||||
largeAlt && [
|
||||
largeAlt && [
|
||||
{
|
||||
padding: 5,
|
||||
},
|
||||
],
|
||||
]}>
|
||||
<Fullscreen
|
||||
fill={t.atoms.text_contrast_high.color}
|
||||
width={largeAlt ? 18 : 12}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
{hasAlt && (
|
||||
<View
|
||||
style={[
|
||||
a.justify_center,
|
||||
a.rounded_xs,
|
||||
t.atoms.bg_contrast_25,
|
||||
{
|
||||
padding: 5,
|
||||
padding: 3,
|
||||
opacity: 0.8,
|
||||
},
|
||||
],
|
||||
]}>
|
||||
<Text
|
||||
style={[a.font_heavy, largeAlt ? a.text_xs : {fontSize: 8}]}>
|
||||
ALT
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
) : null}
|
||||
largeAlt && [
|
||||
{
|
||||
padding: 5,
|
||||
},
|
||||
],
|
||||
]}>
|
||||
<Text
|
||||
style={[a.font_heavy, largeAlt ? a.text_xs : {fontSize: 8}]}>
|
||||
ALT
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
) : null}
|
||||
</DeferReveal>
|
||||
</View>
|
||||
)
|
||||
|
||||
|
||||
@@ -7,9 +7,11 @@ import {useLingui} from '@lingui/react'
|
||||
|
||||
import {HandleRef} from '#/lib/hooks/useHandleRef'
|
||||
import {Dimensions} from '#/lib/media/types'
|
||||
import {isAndroid} from '#/platform/detection'
|
||||
import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
|
||||
import {PostEmbedViewContext} from '#/view/com/util/post-embeds/types'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {DeferReveal} from '#/components/DeferReveal'
|
||||
import {MediaInsetBorder} from '#/components/MediaInsetBorder'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
||||
@@ -70,20 +72,22 @@ export function GalleryItem({
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={image.alt || _(msg`Image`)}
|
||||
accessibilityHint="">
|
||||
<Image
|
||||
source={{uri: image.thumb}}
|
||||
style={[a.flex_1]}
|
||||
accessible={true}
|
||||
accessibilityLabel={image.alt}
|
||||
accessibilityHint=""
|
||||
accessibilityIgnoresInvertColors
|
||||
onLoad={e => {
|
||||
thumbDimsRef.current[index] = {
|
||||
width: e.source.width,
|
||||
height: e.source.height,
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<DeferReveal defer={isAndroid} iVerifiedThereAreNoLayoutJumps>
|
||||
<Image
|
||||
source={{uri: image.thumb}}
|
||||
style={[a.flex_1]}
|
||||
accessible={true}
|
||||
accessibilityLabel={image.alt}
|
||||
accessibilityHint=""
|
||||
accessibilityIgnoresInvertColors
|
||||
onLoad={e => {
|
||||
thumbDimsRef.current[index] = {
|
||||
width: e.source.width,
|
||||
height: e.source.height,
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</DeferReveal>
|
||||
<MediaInsetBorder style={insetBorderStyle} />
|
||||
</Pressable>
|
||||
{hasAlt && !hideBadges ? (
|
||||
|
||||
@@ -9,12 +9,13 @@ import {parseAltFromGIFDescription} from '#/lib/gif-alt-text'
|
||||
import {shareUrl} from '#/lib/sharing'
|
||||
import {parseEmbedPlayerFromUrl} from '#/lib/strings/embed-player'
|
||||
import {toNiceDomain} from '#/lib/strings/url-helpers'
|
||||
import {isNative} from '#/platform/detection'
|
||||
import {isAndroid, isNative} from '#/platform/detection'
|
||||
import {useExternalEmbedsPrefs} from '#/state/preferences'
|
||||
import {ExternalGifEmbed} from '#/view/com/util/post-embeds/ExternalGifEmbed'
|
||||
import {ExternalPlayer} from '#/view/com/util/post-embeds/ExternalPlayerEmbed'
|
||||
import {GifEmbed} from '#/view/com/util/post-embeds/GifEmbed'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {DeferReveal} from '#/components/DeferReveal'
|
||||
import {Divider} from '#/components/Divider'
|
||||
import {Earth_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe'
|
||||
import {Link} from '#/components/Link'
|
||||
@@ -87,13 +88,18 @@ export const ExternalLinkEmbed = ({
|
||||
: t.atoms.border_contrast_low,
|
||||
]}>
|
||||
{imageUri && !embedPlayerParams ? (
|
||||
<Image
|
||||
<View
|
||||
style={{
|
||||
aspectRatio: 1.91,
|
||||
}}
|
||||
source={{uri: imageUri}}
|
||||
accessibilityIgnoresInvertColors
|
||||
/>
|
||||
}}>
|
||||
<DeferReveal defer={isAndroid} iVerifiedThereAreNoLayoutJumps>
|
||||
<Image
|
||||
style={{flex: 1}}
|
||||
source={{uri: imageUri}}
|
||||
accessibilityIgnoresInvertColors
|
||||
/>
|
||||
</DeferReveal>
|
||||
</View>
|
||||
) : undefined}
|
||||
|
||||
{embedPlayerParams?.isGif ? (
|
||||
|
||||
@@ -23,9 +23,10 @@ import {useNavigation} from '@react-navigation/native'
|
||||
|
||||
import {NavigationProp} from '#/lib/routes/types'
|
||||
import {EmbedPlayerParams, getPlayerAspect} from '#/lib/strings/embed-player'
|
||||
import {isNative} from '#/platform/detection'
|
||||
import {isAndroid, isNative} from '#/platform/detection'
|
||||
import {useExternalEmbedsPrefs} from '#/state/preferences'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {DeferReveal} from '#/components/DeferReveal'
|
||||
import {useDialogControl} from '#/components/Dialog'
|
||||
import {EmbedConsentDialog} from '#/components/dialogs/EmbedConsent'
|
||||
import {Fill} from '#/components/Fill'
|
||||
@@ -217,43 +218,45 @@ export function ExternalPlayer({
|
||||
ref={viewRef}
|
||||
collapsable={false}
|
||||
style={[aspect, a.overflow_hidden]}>
|
||||
{link.thumb && (!isPlayerActive || isLoading) ? (
|
||||
<>
|
||||
<Image
|
||||
style={[a.flex_1]}
|
||||
source={{uri: link.thumb}}
|
||||
accessibilityIgnoresInvertColors
|
||||
/>
|
||||
<DeferReveal defer={isAndroid} iVerifiedThereAreNoLayoutJumps>
|
||||
{link.thumb && (!isPlayerActive || isLoading) ? (
|
||||
<>
|
||||
<Image
|
||||
style={[a.flex_1]}
|
||||
source={{uri: link.thumb}}
|
||||
accessibilityIgnoresInvertColors
|
||||
/>
|
||||
<Fill
|
||||
style={[
|
||||
t.name === 'light' ? t.atoms.bg_contrast_975 : t.atoms.bg,
|
||||
{
|
||||
opacity: 0.3,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<Fill
|
||||
style={[
|
||||
t.name === 'light' ? t.atoms.bg_contrast_975 : t.atoms.bg,
|
||||
{
|
||||
backgroundColor:
|
||||
t.name === 'light' ? t.palette.contrast_975 : 'black',
|
||||
opacity: 0.3,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<Fill
|
||||
style={[
|
||||
{
|
||||
backgroundColor:
|
||||
t.name === 'light' ? t.palette.contrast_975 : 'black',
|
||||
opacity: 0.3,
|
||||
},
|
||||
]}
|
||||
)}
|
||||
<PlaceholderOverlay
|
||||
isLoading={isLoading}
|
||||
isPlayerActive={isPlayerActive}
|
||||
onPress={onPlayPress}
|
||||
/>
|
||||
)}
|
||||
<PlaceholderOverlay
|
||||
isLoading={isLoading}
|
||||
isPlayerActive={isPlayerActive}
|
||||
onPress={onPlayPress}
|
||||
/>
|
||||
<Player
|
||||
isPlayerActive={isPlayerActive}
|
||||
params={params}
|
||||
onLoad={onLoad}
|
||||
/>
|
||||
<Player
|
||||
isPlayerActive={isPlayerActive}
|
||||
params={params}
|
||||
onLoad={onLoad}
|
||||
/>
|
||||
</DeferReveal>
|
||||
</Animated.View>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -12,10 +12,11 @@ import {useLingui} from '@lingui/react'
|
||||
|
||||
import {HITSLOP_20} from '#/lib/constants'
|
||||
import {EmbedPlayerParams} from '#/lib/strings/embed-player'
|
||||
import {isWeb} from '#/platform/detection'
|
||||
import {isAndroid, isWeb} from '#/platform/detection'
|
||||
import {useAutoplayDisabled} from '#/state/preferences'
|
||||
import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {DeferReveal} from '#/components/DeferReveal'
|
||||
import {Fill} from '#/components/Fill'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import * as Prompt from '#/components/Prompt'
|
||||
@@ -121,46 +122,48 @@ export function GifEmbed({
|
||||
{aspectRatio: params.dimensions!.width / params.dimensions!.height},
|
||||
style,
|
||||
]}>
|
||||
<View
|
||||
style={[
|
||||
a.absolute,
|
||||
/*
|
||||
* Aspect ratio was being clipped weirdly on web -esb
|
||||
*/
|
||||
{
|
||||
top: -2,
|
||||
bottom: -2,
|
||||
left: -2,
|
||||
right: -2,
|
||||
},
|
||||
]}>
|
||||
<PlaybackControls
|
||||
onPress={onPress}
|
||||
isPlaying={playerState.isPlaying}
|
||||
isLoaded={playerState.isLoaded}
|
||||
/>
|
||||
<GifView
|
||||
source={params.playerUri}
|
||||
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,
|
||||
},
|
||||
]}
|
||||
<DeferReveal defer={isAndroid} iVerifiedThereAreNoLayoutJumps>
|
||||
<View
|
||||
style={[
|
||||
a.absolute,
|
||||
/*
|
||||
* Aspect ratio was being clipped weirdly on web -esb
|
||||
*/
|
||||
{
|
||||
top: -2,
|
||||
bottom: -2,
|
||||
left: -2,
|
||||
right: -2,
|
||||
},
|
||||
]}>
|
||||
<PlaybackControls
|
||||
onPress={onPress}
|
||||
isPlaying={playerState.isPlaying}
|
||||
isLoaded={playerState.isLoaded}
|
||||
/>
|
||||
)}
|
||||
{!hideAlt && isPreferredAltText && <AltText text={altText} />}
|
||||
</View>
|
||||
<GifView
|
||||
source={params.playerUri}
|
||||
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,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
{!hideAlt && isPreferredAltText && <AltText text={altText} />}
|
||||
</View>
|
||||
</DeferReveal>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user