diff --git a/src/lib/hooks/useHandleRef.ts b/src/lib/hooks/useHandleRef.ts
deleted file mode 100644
index 167ba270b6..0000000000
--- a/src/lib/hooks/useHandleRef.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import {useState} from 'react'
-import {AnimatedRef, measure, MeasuredDimensions} from 'react-native-reanimated'
-
-export type HandleRef = {
- (node: any): void
- current: null | number
-}
-
-// This is a lighterweight alternative to `useAnimatedRef()` for imperative UI thread actions.
-// Render it like , then pass `ref.current` to `measureHandle()` and such.
-export function useHandleRef(): HandleRef {
- return useState(() => {
- const ref = (node: any) => {
- if (node) {
- ref.current =
- node._nativeTag ??
- node.__nativeTag ??
- node.canonical?.nativeTag ??
- null
- } else {
- ref.current = null
- }
- }
- ref.current = null
- return ref
- })[0] as HandleRef
-}
-
-// When using this version, you need to read ref.current on the JS thread, and pass it to UI.
-export function measureHandle(
- current: number | null,
-): MeasuredDimensions | null {
- 'worklet'
- if (current !== null) {
- return measure((() => current) as AnimatedRef)
- } else {
- return null
- }
-}
diff --git a/src/screens/Profile/Header/Shell.tsx b/src/screens/Profile/Header/Shell.tsx
index a3efdedf5e..21d33d84b0 100644
--- a/src/screens/Profile/Header/Shell.tsx
+++ b/src/screens/Profile/Header/Shell.tsx
@@ -1,17 +1,22 @@
import React, {memo} from 'react'
import {StyleSheet, TouchableWithoutFeedback, View} from 'react-native'
-import {MeasuredDimensions, runOnJS, runOnUI} from 'react-native-reanimated'
+import Animated, {
+ measure,
+ type MeasuredDimensions,
+ runOnJS,
+ runOnUI,
+ useAnimatedRef,
+} from 'react-native-reanimated'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
-import {AppBskyActorDefs, ModerationDecision} from '@atproto/api'
+import {type AppBskyActorDefs, type ModerationDecision} from '@atproto/api'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useNavigation} from '@react-navigation/native'
import {BACK_HITSLOP} from '#/lib/constants'
-import {measureHandle, useHandleRef} from '#/lib/hooks/useHandleRef'
-import {NavigationProp} from '#/lib/routes/types'
+import {type NavigationProp} from '#/lib/routes/types'
import {isIOS} from '#/platform/detection'
-import {Shadow} from '#/state/cache/types'
+import {type Shadow} from '#/state/cache/types'
import {useLightboxControls} from '#/state/lightbox'
import {useSession} from '#/state/session'
import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
@@ -46,7 +51,7 @@ let ProfileHeaderShell = ({
const navigation = useNavigation()
const {top: topInset} = useSafeAreaInsets()
- const aviRef = useHandleRef()
+ const aviRef = useAnimatedRef()
const onPressBack = React.useCallback(() => {
if (navigation.canGoBack()) {
@@ -83,10 +88,9 @@ let ProfileHeaderShell = ({
const modui = moderation.ui('avatar')
const avatar = profile.avatar
if (avatar && !(modui.blur && modui.noOverride)) {
- const aviHandle = aviRef.current
runOnUI(() => {
'worklet'
- const rect = measureHandle(aviHandle)
+ const rect = measure(aviRef)
runOnJS(_openLightbox)(avatar, rect)
})()
}
@@ -174,14 +178,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 b0cf4d10e4..02a6704bf9 100644
--- a/src/view/com/profile/ProfileSubpageHeader.tsx
+++ b/src/view/com/profile/ProfileSubpageHeader.tsx
@@ -1,23 +1,28 @@
import React from 'react'
import {Pressable, View} from 'react-native'
-import {MeasuredDimensions, runOnJS, runOnUI} from 'react-native-reanimated'
-import {AppBskyGraphDefs} from '@atproto/api'
+import Animated, {
+ measure,
+ type MeasuredDimensions,
+ runOnJS,
+ runOnUI,
+ useAnimatedRef,
+} from 'react-native-reanimated'
+import {type AppBskyGraphDefs} from '@atproto/api'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useNavigation} from '@react-navigation/native'
-import {measureHandle, useHandleRef} from '#/lib/hooks/useHandleRef'
import {usePalette} from '#/lib/hooks/usePalette'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {makeProfileLink} from '#/lib/routes/links'
-import {NavigationProp} from '#/lib/routes/types'
+import {type NavigationProp} from '#/lib/routes/types'
import {sanitizeHandle} from '#/lib/strings/handles'
import {emitSoftReset} from '#/state/events'
import {useLightboxControls} from '#/state/lightbox'
import {TextLink} from '#/view/com/util/Link'
import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
import {Text} from '#/view/com/util/text/Text'
-import {UserAvatar, UserAvatarType} from '#/view/com/util/UserAvatar'
+import {UserAvatar, type UserAvatarType} from '#/view/com/util/UserAvatar'
import {StarterPack} from '#/components/icons/StarterPack'
import * as Layout from '#/components/Layout'
@@ -52,7 +57,7 @@ export function ProfileSubpageHeader({
const {openLightbox} = useLightboxControls()
const pal = usePalette('default')
const canGoBack = navigation.canGoBack()
- const aviRef = useHandleRef()
+ const aviRef = useAnimatedRef()
const _openLightbox = React.useCallback(
(uri: string, thumbRect: MeasuredDimensions | null) => {
@@ -81,10 +86,9 @@ export function ProfileSubpageHeader({
if (
avatar // TODO && !(view.moderation.avatar.blur && view.moderation.avatar.noOverride)
) {
- const aviHandle = aviRef.current
runOnUI(() => {
'worklet'
- const rect = measureHandle(aviHandle)
+ const rect = measure(aviRef)
runOnJS(_openLightbox)(avatar, rect)
})()
}
@@ -111,7 +115,7 @@ export function ProfileSubpageHeader({
paddingBottom: 14,
paddingHorizontal: isMobile ? 12 : 14,
}}>
-
+
)}
-
+
{isLoading ? (
void
+ onPress?: (
+ containerRef: AnimatedRef,
+ fetchedDims: Dimensions | null,
+ ) => void
onLongPress?: () => void
onPressIn?: () => void
}) {
const t = useTheme()
const {_} = useLingui()
const largeAlt = useLargeAltBadgeEnabled()
- const containerRef = useHandleRef()
+ const containerRef = useAnimatedRef()
const fetchedDimsRef = useRef<{width: number; height: number} | null>(null)
let aspectRatio: number | undefined
@@ -103,7 +106,7 @@ export function AutoSizedImage({
const hasAlt = !!image.alt
const contents = (
-
+
) : null}
-
+
)
if (cropDisabled) {
diff --git a/src/view/com/util/images/Gallery.tsx b/src/view/com/util/images/Gallery.tsx
index cc3eda68d2..1d35c88c59 100644
--- a/src/view/com/util/images/Gallery.tsx
+++ b/src/view/com/util/images/Gallery.tsx
@@ -1,12 +1,12 @@
-import React from 'react'
-import {Pressable, StyleProp, View, ViewStyle} from 'react-native'
-import {Image, ImageStyle} from 'expo-image'
-import {AppBskyEmbedImages} from '@atproto/api'
+import {Pressable, type StyleProp, View, type ViewStyle} from 'react-native'
+import {type AnimatedRef} from 'react-native-reanimated'
+import {Image, type ImageStyle} from 'expo-image'
+import {type AppBskyEmbedImages} from '@atproto/api'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
+import type React from 'react'
-import {HandleRef} from '#/lib/hooks/useHandleRef'
-import {Dimensions} from '#/lib/media/types'
+import {type Dimensions} from '#/lib/media/types'
import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
import {PostEmbedViewContext} from '#/view/com/util/post-embeds/types'
import {atoms as a, useTheme} from '#/alf'
@@ -20,7 +20,7 @@ interface Props {
index: number
onPress?: (
index: number,
- containerRefs: HandleRef[],
+ containerRefs: AnimatedRef[],
fetchedDims: (Dimensions | null)[],
) => void
onLongPress?: EventFunction
@@ -28,7 +28,7 @@ interface Props {
imageStyle?: StyleProp
viewContext?: PostEmbedViewContext
insetBorderStyle?: StyleProp
- containerRefs: HandleRef[]
+ containerRefs: AnimatedRef[]
thumbDimsRef: React.MutableRefObject<(Dimensions | null)[]>
}
diff --git a/src/view/com/util/images/ImageLayoutGrid.tsx b/src/view/com/util/images/ImageLayoutGrid.tsx
index 16ea9d453f..b91d7a7adb 100644
--- a/src/view/com/util/images/ImageLayoutGrid.tsx
+++ b/src/view/com/util/images/ImageLayoutGrid.tsx
@@ -1,18 +1,18 @@
import React from 'react'
-import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
-import {AppBskyEmbedImages} from '@atproto/api'
+import {type StyleProp, StyleSheet, View, type ViewStyle} from 'react-native'
+import {type AnimatedRef, useAnimatedRef} from 'react-native-reanimated'
+import {type AppBskyEmbedImages} from '@atproto/api'
-import {HandleRef, useHandleRef} from '#/lib/hooks/useHandleRef'
import {PostEmbedViewContext} from '#/view/com/util/post-embeds/types'
import {atoms as a, useBreakpoints} from '#/alf'
-import {Dimensions} from '../../lightbox/ImageViewing/@types'
+import {type Dimensions} from '../../lightbox/ImageViewing/@types'
import {GalleryItem} from './Gallery'
interface ImageLayoutGridProps {
images: AppBskyEmbedImages.ViewImage[]
onPress?: (
index: number,
- containerRefs: HandleRef[],
+ containerRefs: AnimatedRef[],
fetchedDims: (Dimensions | null)[],
) => void
onLongPress?: (index: number) => void
@@ -43,7 +43,7 @@ interface ImageLayoutGridInnerProps {
images: AppBskyEmbedImages.ViewImage[]
onPress?: (
index: number,
- containerRefs: HandleRef[],
+ containerRefs: AnimatedRef[],
fetchedDims: (Dimensions | null)[],
) => void
onLongPress?: (index: number) => void
@@ -56,10 +56,10 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) {
const gap = props.gap
const count = props.images.length
- const containerRef1 = useHandleRef()
- const containerRef2 = useHandleRef()
- const containerRef3 = useHandleRef()
- const containerRef4 = useHandleRef()
+ const containerRef1 = useAnimatedRef()
+ const containerRef2 = useAnimatedRef()
+ const containerRef3 = useAnimatedRef()
+ const containerRef4 = useAnimatedRef()
const thumbDimsRef = React.useRef<(Dimensions | null)[]>([])
switch (count) {
diff --git a/src/view/com/util/post-embeds/index.tsx b/src/view/com/util/post-embeds/index.tsx
index 6f5f9d3ab9..4cf71f9486 100644
--- a/src/view/com/util/post-embeds/index.tsx
+++ b/src/view/com/util/post-embeds/index.tsx
@@ -1,12 +1,18 @@
import React from 'react'
import {
InteractionManager,
- StyleProp,
+ type StyleProp,
StyleSheet,
View,
- ViewStyle,
+ type ViewStyle,
} from 'react-native'
-import {MeasuredDimensions, runOnJS, runOnUI} from 'react-native-reanimated'
+import {
+ type AnimatedRef,
+ measure,
+ type MeasuredDimensions,
+ runOnJS,
+ runOnUI,
+} from 'react-native-reanimated'
import {Image} from 'expo-image'
import {
AppBskyEmbedExternal,
@@ -18,10 +24,9 @@ import {
AppBskyGraphDefs,
moderateFeedGenerator,
moderateUserList,
- ModerationDecision,
+ type ModerationDecision,
} from '@atproto/api'
-import {HandleRef, measureHandle} from '#/lib/hooks/useHandleRef'
import {usePalette} from '#/lib/hooks/usePalette'
import {useLightboxControls} from '#/state/lightbox'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
@@ -30,7 +35,7 @@ import {atoms as a, useTheme} from '#/alf'
import * as ListCard from '#/components/ListCard'
import {Embed as StarterPackCard} from '#/components/StarterPack/StarterPackCard'
import {ContentHider} from '../../../../components/moderation/ContentHider'
-import {Dimensions} from '../../lightbox/ImageViewing/@types'
+import {type Dimensions} from '../../lightbox/ImageViewing/@types'
import {AutoSizedImage} from '../images/AutoSizedImage'
import {ImageLayoutGrid} from '../images/ImageLayoutGrid'
import {ExternalLinkEmbed} from './ExternalLinkEmbed'
@@ -158,13 +163,15 @@ export function PostEmbeds({
}
const onPress = (
index: number,
- refs: HandleRef[],
+ refs: AnimatedRef[],
fetchedDims: (Dimensions | null)[],
) => {
- const handles = refs.map(r => r.current)
runOnUI(() => {
'worklet'
- const rects = handles.map(measureHandle)
+ const rects: (MeasuredDimensions | null)[] = []
+ for (const r of refs) {
+ rects.push(measure(r))
+ }
runOnJS(_openLightbox)(index, rects, fetchedDims)
})()
}