From d7fa2132bb5283905c273ddb359d80a9575776c1 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 7 Nov 2024 02:07:47 +0000 Subject: [PATCH] Measure avi rects too --- src/screens/Profile/Header/Shell.tsx | 48 ++++++++++---- src/view/com/profile/ProfileSubpageHeader.tsx | 63 ++++++++++++------- src/view/com/util/images/Gallery.tsx | 5 +- src/view/com/util/post-embeds/index.tsx | 5 +- 4 files changed, 85 insertions(+), 36 deletions(-) diff --git a/src/screens/Profile/Header/Shell.tsx b/src/screens/Profile/Header/Shell.tsx index ed2b086d36..093b9190a6 100644 --- a/src/screens/Profile/Header/Shell.tsx +++ b/src/screens/Profile/Header/Shell.tsx @@ -1,5 +1,12 @@ import React, {memo} from 'react' import {StyleSheet, TouchableWithoutFeedback, View} from 'react-native' +import Animated, { + measure, + MeasuredDimensions, + runOnJS, + runOnUI, + useAnimatedRef, +} from 'react-native-reanimated' import {AppBskyActorDefs, ModerationDecision} from '@atproto/api' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {msg} from '@lingui/macro' @@ -42,6 +49,7 @@ let ProfileHeaderShell = ({ const {openLightbox} = useLightboxControls() const navigation = useNavigation() const {isDesktop} = useWebMediaQueries() + const aviRef = useAnimatedRef() const onPressBack = React.useCallback(() => { if (navigation.canGoBack()) { @@ -51,15 +59,14 @@ let ProfileHeaderShell = ({ } }, [navigation]) - const onPressAvi = React.useCallback(() => { - const modui = moderation.ui('avatar') - if (profile.avatar && !(modui.blur && modui.noOverride)) { + const _openLightbox = React.useCallback( + (uri: string, thumbRect: MeasuredDimensions | null) => { openLightbox({ images: [ { - uri: profile.avatar, - thumbUri: profile.avatar, - thumbRect: null, + uri, + thumbUri: uri, + thumbRect, dimensions: { // It's fine if it's actually smaller but we know it's 1:1. height: 1000, @@ -70,8 +77,21 @@ let ProfileHeaderShell = ({ ], index: 0, }) + }, + [openLightbox], + ) + + const onPressAvi = React.useCallback(() => { + const modui = moderation.ui('avatar') + const avatar = profile.avatar + if (avatar && !(modui.blur && modui.noOverride)) { + runOnUI(() => { + 'worklet' + const rect = measure(aviRef) + runOnJS(_openLightbox)(avatar, rect) + })() } - }, [openLightbox, profile, moderation]) + }, [profile, moderation, _openLightbox, aviRef]) const isMe = React.useMemo( () => currentAccount?.did === profile.did, @@ -149,12 +169,14 @@ let ProfileHeaderShell = ({ styles.avi, profile.associated?.labeler && styles.aviLabeler, ]}> - + + + diff --git a/src/view/com/profile/ProfileSubpageHeader.tsx b/src/view/com/profile/ProfileSubpageHeader.tsx index e0e9d7dc5e..13d14ec502 100644 --- a/src/view/com/profile/ProfileSubpageHeader.tsx +++ b/src/view/com/profile/ProfileSubpageHeader.tsx @@ -1,5 +1,12 @@ import React from 'react' import {Pressable, StyleSheet, View} from 'react-native' +import Animated, { + measure, + MeasuredDimensions, + runOnJS, + runOnUI, + useAnimatedRef, +} from 'react-native-reanimated' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' @@ -53,6 +60,7 @@ export function ProfileSubpageHeader({ const {openLightbox} = useLightboxControls() const pal = usePalette('default') const canGoBack = navigation.canGoBack() + const aviRef = useAnimatedRef() const onPressBack = React.useCallback(() => { if (navigation.canGoBack()) { @@ -66,16 +74,14 @@ export function ProfileSubpageHeader({ setDrawerOpen(true) }, [setDrawerOpen]) - const onPressAvi = React.useCallback(() => { - if ( - avatar // TODO && !(view.moderation.avatar.blur && view.moderation.avatar.noOverride) - ) { + const _openLightbox = React.useCallback( + (uri: string, thumbRect: MeasuredDimensions | null) => { openLightbox({ images: [ { - uri: avatar, - thumbUri: avatar, - thumbRect: null, + uri, + thumbUri: uri, + thumbRect, dimensions: { // It's fine if it's actually smaller but we know it's 1:1. height: 1000, @@ -86,8 +92,21 @@ export function ProfileSubpageHeader({ ], index: 0, }) + }, + [openLightbox], + ) + + const onPressAvi = React.useCallback(() => { + if ( + avatar // TODO && !(view.moderation.avatar.blur && view.moderation.avatar.noOverride) + ) { + runOnUI(() => { + 'worklet' + const rect = measure(aviRef) + runOnJS(_openLightbox)(avatar, rect) + })() } - }, [openLightbox, avatar]) + }, [_openLightbox, avatar, aviRef]) return ( @@ -135,19 +154,21 @@ export function ProfileSubpageHeader({ paddingBottom: 6, paddingHorizontal: isMobile ? 12 : 14, }}> - - {avatarType === 'starter-pack' ? ( - - ) : ( - - )} - + + + {avatarType === 'starter-pack' ? ( + + ) : ( + + )} + + {isLoading ? ( + onPress(index, containerRefs) : undefined} onPressIn={onPressIn ? () => onPressIn(index) : undefined} diff --git a/src/view/com/util/post-embeds/index.tsx b/src/view/com/util/post-embeds/index.tsx index 9ec04298f0..da3f13fa55 100644 --- a/src/view/com/util/post-embeds/index.tsx +++ b/src/view/com/util/post-embeds/index.tsx @@ -180,7 +180,10 @@ export function PostEmbeds({ const image = images[0] return ( - +