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