Enable lightbox for profile banner (#9643)
* allow pressing banner to view * remove special type
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import {useEffect, useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {ActivityIndicator} from 'react-native'
|
||||
import {ActivityIndicator, Pressable, View} from 'react-native'
|
||||
import Animated, {
|
||||
type AnimatedRef,
|
||||
Extrapolation,
|
||||
interpolate,
|
||||
runOnJS,
|
||||
@@ -28,26 +28,39 @@ const AnimatedBlurView = Animated.createAnimatedComponent(BlurView)
|
||||
export function GrowableBanner({
|
||||
backButton,
|
||||
children,
|
||||
onPress,
|
||||
bannerRef,
|
||||
}: {
|
||||
backButton?: React.ReactNode
|
||||
children: React.ReactNode
|
||||
onPress?: () => void
|
||||
bannerRef?: AnimatedRef<Animated.View>
|
||||
}) {
|
||||
const pagerContext = usePagerHeaderContext()
|
||||
|
||||
// plain non-growable mode for Android/Web
|
||||
if (!pagerContext || !isIOS) {
|
||||
return (
|
||||
<View style={[a.w_full, a.h_full]}>
|
||||
{children}
|
||||
<Pressable
|
||||
onPress={onPress}
|
||||
accessibilityRole="image"
|
||||
style={[a.w_full, a.h_full]}>
|
||||
<Animated.View ref={bannerRef} style={[a.w_full, a.h_full]}>
|
||||
{children}
|
||||
</Animated.View>
|
||||
{backButton}
|
||||
</View>
|
||||
</Pressable>
|
||||
)
|
||||
}
|
||||
|
||||
const {scrollY} = pagerContext
|
||||
|
||||
return (
|
||||
<GrowableBannerInner scrollY={scrollY} backButton={backButton}>
|
||||
<GrowableBannerInner
|
||||
scrollY={scrollY}
|
||||
backButton={backButton}
|
||||
onPress={onPress}
|
||||
bannerRef={bannerRef}>
|
||||
{children}
|
||||
</GrowableBannerInner>
|
||||
)
|
||||
@@ -57,10 +70,14 @@ function GrowableBannerInner({
|
||||
scrollY,
|
||||
backButton,
|
||||
children,
|
||||
onPress,
|
||||
bannerRef,
|
||||
}: {
|
||||
scrollY: SharedValue<number>
|
||||
backButton?: React.ReactNode
|
||||
children: React.ReactNode
|
||||
onPress?: () => void
|
||||
bannerRef?: AnimatedRef<Animated.View>
|
||||
}) {
|
||||
const {top: topInset} = useSafeAreaInsets()
|
||||
const isFetching = useIsProfileFetching()
|
||||
@@ -124,14 +141,26 @@ function GrowableBannerInner({
|
||||
{transformOrigin: 'bottom'},
|
||||
animatedStyle,
|
||||
]}>
|
||||
{children}
|
||||
<Pressable
|
||||
onPress={onPress}
|
||||
accessibilityRole="image"
|
||||
style={[a.w_full, a.h_full]}>
|
||||
<Animated.View
|
||||
ref={bannerRef}
|
||||
collapsable={false}
|
||||
style={[a.w_full, a.h_full]}>
|
||||
{children}
|
||||
</Animated.View>
|
||||
</Pressable>
|
||||
<AnimatedBlurView
|
||||
pointerEvents="none"
|
||||
style={[a.absolute, a.inset_0]}
|
||||
tint="dark"
|
||||
animatedProps={animatedBlurViewProps}
|
||||
/>
|
||||
</Animated.View>
|
||||
<View
|
||||
pointerEvents="none"
|
||||
style={[
|
||||
a.absolute,
|
||||
a.inset_0,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {memo, useCallback, useEffect, useMemo} from 'react'
|
||||
import {TouchableWithoutFeedback, View} from 'react-native'
|
||||
import {Pressable, View} from 'react-native'
|
||||
import Animated, {
|
||||
measure,
|
||||
type MeasuredDimensions,
|
||||
@@ -63,6 +63,7 @@ let ProfileHeaderShell = ({
|
||||
const liveStatusControl = useDialogControl()
|
||||
|
||||
const aviRef = useAnimatedRef()
|
||||
const bannerRef = useAnimatedRef<Animated.View>()
|
||||
|
||||
const onPressBack = useCallback(() => {
|
||||
if (navigation.canGoBack()) {
|
||||
@@ -73,20 +74,31 @@ let ProfileHeaderShell = ({
|
||||
}, [navigation])
|
||||
|
||||
const _openLightbox = useCallback(
|
||||
(uri: string, thumbRect: MeasuredDimensions | null) => {
|
||||
(
|
||||
uri: string,
|
||||
thumbRect: MeasuredDimensions | null,
|
||||
type: 'circle-avi' | 'image' = 'circle-avi',
|
||||
) => {
|
||||
openLightbox({
|
||||
images: [
|
||||
{
|
||||
uri,
|
||||
thumbUri: uri,
|
||||
thumbRect,
|
||||
dimensions: {
|
||||
// It's fine if it's actually smaller but we know it's 1:1.
|
||||
height: 1000,
|
||||
width: 1000,
|
||||
},
|
||||
dimensions:
|
||||
type === 'circle-avi'
|
||||
? {
|
||||
// It's fine if it's actually smaller but we know it's 1:1.
|
||||
height: 1000,
|
||||
width: 1000,
|
||||
}
|
||||
: {
|
||||
// Banner aspect ratio is 3:1
|
||||
width: 3000,
|
||||
height: 1000,
|
||||
},
|
||||
thumbDimensions: null,
|
||||
type: 'circle-avi',
|
||||
type,
|
||||
},
|
||||
],
|
||||
index: 0,
|
||||
@@ -142,6 +154,18 @@ let ProfileHeaderShell = ({
|
||||
playHaptic,
|
||||
])
|
||||
|
||||
const onPressBanner = useCallback(() => {
|
||||
const modui = moderation.ui('banner')
|
||||
const banner = profile.banner
|
||||
if (banner && !(modui.blur && modui.noOverride)) {
|
||||
runOnUI(() => {
|
||||
'worklet'
|
||||
const rect = measure(bannerRef)
|
||||
runOnJS(_openLightbox)(banner, rect, 'image')
|
||||
})()
|
||||
}
|
||||
}, [profile.banner, moderation, _openLightbox, bannerRef])
|
||||
|
||||
return (
|
||||
<View style={t.atoms.bg} pointerEvents={isIOS ? 'auto' : 'box-none'}>
|
||||
<View
|
||||
@@ -149,6 +173,8 @@ let ProfileHeaderShell = ({
|
||||
style={[a.relative, {height: 150}]}>
|
||||
<StatusBarShadow />
|
||||
<GrowableBanner
|
||||
onPress={isPlaceholderProfile ? undefined : onPressBanner}
|
||||
bannerRef={bannerRef}
|
||||
backButton={
|
||||
!hideBackButton && (
|
||||
<Button
|
||||
@@ -234,7 +260,7 @@ let ProfileHeaderShell = ({
|
||||
))}
|
||||
|
||||
<GrowableAvatar style={[a.absolute, {top: 104, left: 10}]}>
|
||||
<TouchableWithoutFeedback
|
||||
<Pressable
|
||||
testID="profileHeaderAviButton"
|
||||
onPress={onPressAvi}
|
||||
accessibilityRole="image"
|
||||
@@ -265,7 +291,7 @@ let ProfileHeaderShell = ({
|
||||
{live.isActive && <LiveIndicator size="large" />}
|
||||
</Animated.View>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
</Pressable>
|
||||
</GrowableAvatar>
|
||||
|
||||
{live.isActive &&
|
||||
|
||||
Reference in New Issue
Block a user