Add <DeferReveal> to defer expensive views

This commit is contained in:
Dan Abramov
2024-11-24 21:52:58 +00:00
parent b9406aa011
commit df9a0a075f
6 changed files with 202 additions and 159 deletions
+23
View File
@@ -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 {HandleRef, useHandleRef} from '#/lib/hooks/useHandleRef'
import type {Dimensions} from '#/lib/media/types' import type {Dimensions} from '#/lib/media/types'
import {isNative} from '#/platform/detection' import {isNative} from '#/platform/detection'
import {isAndroid} from '#/platform/detection'
import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge' import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
import {atoms as a, useBreakpoints, useTheme} from '#/alf' 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 {ArrowsDiagonalOut_Stroke2_Corner0_Rounded as Fullscreen} from '#/components/icons/ArrowsDiagonal'
import {MediaInsetBorder} from '#/components/MediaInsetBorder' import {MediaInsetBorder} from '#/components/MediaInsetBorder'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
@@ -107,6 +109,7 @@ export function AutoSizedImage({
const contents = ( const contents = (
<View ref={containerRef} collapsable={false} style={{flex: 1}}> <View ref={containerRef} collapsable={false} style={{flex: 1}}>
<DeferReveal defer={isAndroid} iVerifiedThereAreNoLayoutJumps>
<Image <Image
style={[a.w_full, a.h_full]} style={[a.w_full, a.h_full]}
source={image.thumb} source={image.thumb}
@@ -185,6 +188,7 @@ export function AutoSizedImage({
)} )}
</View> </View>
) : null} ) : null}
</DeferReveal>
</View> </View>
) )
+4
View File
@@ -7,9 +7,11 @@ import {useLingui} from '@lingui/react'
import {HandleRef} from '#/lib/hooks/useHandleRef' import {HandleRef} from '#/lib/hooks/useHandleRef'
import {Dimensions} from '#/lib/media/types' import {Dimensions} from '#/lib/media/types'
import {isAndroid} from '#/platform/detection'
import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge' import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
import {PostEmbedViewContext} from '#/view/com/util/post-embeds/types' import {PostEmbedViewContext} from '#/view/com/util/post-embeds/types'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {DeferReveal} from '#/components/DeferReveal'
import {MediaInsetBorder} from '#/components/MediaInsetBorder' import {MediaInsetBorder} from '#/components/MediaInsetBorder'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
@@ -70,6 +72,7 @@ export function GalleryItem({
accessibilityRole="button" accessibilityRole="button"
accessibilityLabel={image.alt || _(msg`Image`)} accessibilityLabel={image.alt || _(msg`Image`)}
accessibilityHint=""> accessibilityHint="">
<DeferReveal defer={isAndroid} iVerifiedThereAreNoLayoutJumps>
<Image <Image
source={{uri: image.thumb}} source={{uri: image.thumb}}
style={[a.flex_1]} style={[a.flex_1]}
@@ -84,6 +87,7 @@ export function GalleryItem({
} }
}} }}
/> />
</DeferReveal>
<MediaInsetBorder style={insetBorderStyle} /> <MediaInsetBorder style={insetBorderStyle} />
</Pressable> </Pressable>
{hasAlt && !hideBadges ? ( {hasAlt && !hideBadges ? (
@@ -9,12 +9,13 @@ import {parseAltFromGIFDescription} from '#/lib/gif-alt-text'
import {shareUrl} from '#/lib/sharing' import {shareUrl} from '#/lib/sharing'
import {parseEmbedPlayerFromUrl} from '#/lib/strings/embed-player' import {parseEmbedPlayerFromUrl} from '#/lib/strings/embed-player'
import {toNiceDomain} from '#/lib/strings/url-helpers' import {toNiceDomain} from '#/lib/strings/url-helpers'
import {isNative} from '#/platform/detection' import {isAndroid, isNative} from '#/platform/detection'
import {useExternalEmbedsPrefs} from '#/state/preferences' import {useExternalEmbedsPrefs} from '#/state/preferences'
import {ExternalGifEmbed} from '#/view/com/util/post-embeds/ExternalGifEmbed' import {ExternalGifEmbed} from '#/view/com/util/post-embeds/ExternalGifEmbed'
import {ExternalPlayer} from '#/view/com/util/post-embeds/ExternalPlayerEmbed' import {ExternalPlayer} from '#/view/com/util/post-embeds/ExternalPlayerEmbed'
import {GifEmbed} from '#/view/com/util/post-embeds/GifEmbed' import {GifEmbed} from '#/view/com/util/post-embeds/GifEmbed'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {DeferReveal} from '#/components/DeferReveal'
import {Divider} from '#/components/Divider' import {Divider} from '#/components/Divider'
import {Earth_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe' import {Earth_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe'
import {Link} from '#/components/Link' import {Link} from '#/components/Link'
@@ -87,13 +88,18 @@ export const ExternalLinkEmbed = ({
: t.atoms.border_contrast_low, : t.atoms.border_contrast_low,
]}> ]}>
{imageUri && !embedPlayerParams ? ( {imageUri && !embedPlayerParams ? (
<Image <View
style={{ style={{
aspectRatio: 1.91, aspectRatio: 1.91,
}} }}>
<DeferReveal defer={isAndroid} iVerifiedThereAreNoLayoutJumps>
<Image
style={{flex: 1}}
source={{uri: imageUri}} source={{uri: imageUri}}
accessibilityIgnoresInvertColors accessibilityIgnoresInvertColors
/> />
</DeferReveal>
</View>
) : undefined} ) : undefined}
{embedPlayerParams?.isGif ? ( {embedPlayerParams?.isGif ? (
@@ -23,9 +23,10 @@ import {useNavigation} from '@react-navigation/native'
import {NavigationProp} from '#/lib/routes/types' import {NavigationProp} from '#/lib/routes/types'
import {EmbedPlayerParams, getPlayerAspect} from '#/lib/strings/embed-player' 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 {useExternalEmbedsPrefs} from '#/state/preferences'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {DeferReveal} from '#/components/DeferReveal'
import {useDialogControl} from '#/components/Dialog' import {useDialogControl} from '#/components/Dialog'
import {EmbedConsentDialog} from '#/components/dialogs/EmbedConsent' import {EmbedConsentDialog} from '#/components/dialogs/EmbedConsent'
import {Fill} from '#/components/Fill' import {Fill} from '#/components/Fill'
@@ -217,6 +218,7 @@ export function ExternalPlayer({
ref={viewRef} ref={viewRef}
collapsable={false} collapsable={false}
style={[aspect, a.overflow_hidden]}> style={[aspect, a.overflow_hidden]}>
<DeferReveal defer={isAndroid} iVerifiedThereAreNoLayoutJumps>
{link.thumb && (!isPlayerActive || isLoading) ? ( {link.thumb && (!isPlayerActive || isLoading) ? (
<> <>
<Image <Image
@@ -254,6 +256,7 @@ export function ExternalPlayer({
params={params} params={params}
onLoad={onLoad} onLoad={onLoad}
/> />
</DeferReveal>
</Animated.View> </Animated.View>
</> </>
) )
+4 -1
View File
@@ -12,10 +12,11 @@ import {useLingui} from '@lingui/react'
import {HITSLOP_20} from '#/lib/constants' import {HITSLOP_20} from '#/lib/constants'
import {EmbedPlayerParams} from '#/lib/strings/embed-player' import {EmbedPlayerParams} from '#/lib/strings/embed-player'
import {isWeb} from '#/platform/detection' import {isAndroid, isWeb} from '#/platform/detection'
import {useAutoplayDisabled} from '#/state/preferences' import {useAutoplayDisabled} from '#/state/preferences'
import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge' import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {DeferReveal} from '#/components/DeferReveal'
import {Fill} from '#/components/Fill' import {Fill} from '#/components/Fill'
import {Loader} from '#/components/Loader' import {Loader} from '#/components/Loader'
import * as Prompt from '#/components/Prompt' import * as Prompt from '#/components/Prompt'
@@ -121,6 +122,7 @@ export function GifEmbed({
{aspectRatio: params.dimensions!.width / params.dimensions!.height}, {aspectRatio: params.dimensions!.width / params.dimensions!.height},
style, style,
]}> ]}>
<DeferReveal defer={isAndroid} iVerifiedThereAreNoLayoutJumps>
<View <View
style={[ style={[
a.absolute, a.absolute,
@@ -161,6 +163,7 @@ export function GifEmbed({
)} )}
{!hideAlt && isPreferredAltText && <AltText text={altText} />} {!hideAlt && isPreferredAltText && <AltText text={altText} />}
</View> </View>
</DeferReveal>
</View> </View>
) )
} }