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 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,6 +109,7 @@ export function AutoSizedImage({
const contents = (
<View ref={containerRef} collapsable={false} style={{flex: 1}}>
<DeferReveal defer={isAndroid} iVerifiedThereAreNoLayoutJumps>
<Image
style={[a.w_full, a.h_full]}
source={image.thumb}
@@ -185,6 +188,7 @@ export function AutoSizedImage({
)}
</View>
) : null}
</DeferReveal>
</View>
)
+4
View File
@@ -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,6 +72,7 @@ export function GalleryItem({
accessibilityRole="button"
accessibilityLabel={image.alt || _(msg`Image`)}
accessibilityHint="">
<DeferReveal defer={isAndroid} iVerifiedThereAreNoLayoutJumps>
<Image
source={{uri: image.thumb}}
style={[a.flex_1]}
@@ -84,6 +87,7 @@ export function GalleryItem({
}
}}
/>
</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,
}}
}}>
<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,6 +218,7 @@ export function ExternalPlayer({
ref={viewRef}
collapsable={false}
style={[aspect, a.overflow_hidden]}>
<DeferReveal defer={isAndroid} iVerifiedThereAreNoLayoutJumps>
{link.thumb && (!isPlayerActive || isLoading) ? (
<>
<Image
@@ -254,6 +256,7 @@ export function ExternalPlayer({
params={params}
onLoad={onLoad}
/>
</DeferReveal>
</Animated.View>
</>
)
+4 -1
View File
@@ -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,6 +122,7 @@ export function GifEmbed({
{aspectRatio: params.dimensions!.width / params.dimensions!.height},
style,
]}>
<DeferReveal defer={isAndroid} iVerifiedThereAreNoLayoutJumps>
<View
style={[
a.absolute,
@@ -161,6 +163,7 @@ export function GifEmbed({
)}
{!hideAlt && isPreferredAltText && <AltText text={altText} />}
</View>
</DeferReveal>
</View>
)
}