Measure all rects for embeds

This commit is contained in:
Dan Abramov
2024-11-07 01:59:10 +00:00
parent a6d009d06b
commit a3b037af5c
7 changed files with 49 additions and 22 deletions
+1 -1
View File
@@ -59,6 +59,7 @@ let ProfileHeaderShell = ({
{
uri: profile.avatar,
thumbUri: profile.avatar,
thumbRect: null,
dimensions: {
// It's fine if it's actually smaller but we know it's 1:1.
height: 1000,
@@ -68,7 +69,6 @@ let ProfileHeaderShell = ({
},
],
index: 0,
thumbDims: null,
})
}
}, [openLightbox, profile, moderation])
-2
View File
@@ -1,5 +1,4 @@
import React from 'react'
import type {MeasuredDimensions} from 'react-native-reanimated'
import {nanoid} from 'nanoid/non-secure'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
@@ -8,7 +7,6 @@ import {ImageSource} from '#/view/com/lightbox/ImageViewing/@types'
export type Lightbox = {
id: string
images: ImageSource[]
thumbDims: MeasuredDimensions | null
index: number
}
@@ -6,6 +6,8 @@
*
*/
import {MeasuredDimensions} from 'react-native-reanimated'
export type Dimensions = {
width: number
height: number
@@ -19,6 +21,7 @@ export type Position = {
export type ImageSource = {
uri: string
thumbUri: string
thumbRect: MeasuredDimensions | null
alt?: string
dimensions: Dimensions | null
type: 'image' | 'circle-avi' | 'rect-avi'
@@ -75,6 +75,7 @@ export function ProfileSubpageHeader({
{
uri: avatar,
thumbUri: avatar,
thumbRect: null,
dimensions: {
// It's fine if it's actually smaller but we know it's 1:1.
height: 1000,
@@ -84,7 +85,6 @@ export function ProfileSubpageHeader({
},
],
index: 0,
thumbDims: null,
})
}
}, [openLightbox, avatar])
+6 -5
View File
@@ -1,6 +1,6 @@
import React from 'react'
import {Pressable, StyleProp, View, ViewStyle} from 'react-native'
import Animated, {AnimatedRef, useAnimatedRef} from 'react-native-reanimated'
import Animated, {AnimatedRef} from 'react-native-reanimated'
import {Image, ImageStyle} from 'expo-image'
import {AppBskyEmbedImages} from '@atproto/api'
import {msg} from '@lingui/macro'
@@ -19,13 +19,14 @@ interface Props {
index: number
onPress?: (
index: number,
containerRef: AnimatedRef<React.Component<{}, {}, any>>,
containerRefs: AnimatedRef<React.Component<{}, {}, any>>[],
) => void
onLongPress?: EventFunction
onPressIn?: EventFunction
imageStyle?: StyleProp<ImageStyle>
viewContext?: PostEmbedViewContext
insetBorderStyle?: StyleProp<ViewStyle>
containerRefs: AnimatedRef<React.Component<{}, {}, any>>[]
}
export function GalleryItem({
@@ -37,6 +38,7 @@ export function GalleryItem({
onLongPress,
viewContext,
insetBorderStyle,
containerRefs,
}: Props) {
const t = useTheme()
const {_} = useLingui()
@@ -45,11 +47,10 @@ export function GalleryItem({
const hasAlt = !!image.alt
const hideBadges =
viewContext === PostEmbedViewContext.FeedEmbedRecordWithMedia
const containerRef = useAnimatedRef()
return (
<Animated.View style={a.flex_1} ref={containerRef}>
<Animated.View style={a.flex_1} ref={containerRefs[index]}>
<Pressable
onPress={onPress ? () => onPress(index, containerRef) : undefined}
onPress={onPress ? () => onPress(index, containerRefs) : undefined}
onPressIn={onPressIn ? () => onPressIn(index) : undefined}
onLongPress={onLongPress ? () => onLongPress(index) : undefined}
style={[
+31 -6
View File
@@ -1,6 +1,6 @@
import React from 'react'
import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
import {AnimatedRef} from 'react-native-reanimated'
import {AnimatedRef, useAnimatedRef} from 'react-native-reanimated'
import {AppBskyEmbedImages} from '@atproto/api'
import {PostEmbedViewContext} from '#/view/com/util/post-embeds/types'
@@ -11,7 +11,7 @@ interface ImageLayoutGridProps {
images: AppBskyEmbedImages.ViewImage[]
onPress?: (
index: number,
containerRef: AnimatedRef<React.Component<{}, {}, any>>,
containerRefs: AnimatedRef<React.Component<{}, {}, any>>[],
) => void
onLongPress?: (index: number) => void
onPressIn?: (index: number) => void
@@ -41,7 +41,7 @@ interface ImageLayoutGridInnerProps {
images: AppBskyEmbedImages.ViewImage[]
onPress?: (
index: number,
containerRef: AnimatedRef<React.Component<{}, {}, any>>,
containerRefs: AnimatedRef<React.Component<{}, {}, any>>[],
) => void
onLongPress?: (index: number) => void
onPressIn?: (index: number) => void
@@ -53,8 +53,14 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) {
const gap = props.gap
const count = props.images.length
const containerRef1 = useAnimatedRef()
const containerRef2 = useAnimatedRef()
const containerRef3 = useAnimatedRef()
const containerRef4 = useAnimatedRef()
switch (count) {
case 2:
case 2: {
const containerRefs = [containerRef1, containerRef2]
return (
<View style={[a.flex_1, a.flex_row, gap]}>
<View style={[a.flex_1, {aspectRatio: 1}]}>
@@ -62,6 +68,7 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) {
{...props}
index={0}
insetBorderStyle={noCorners(['topRight', 'bottomRight'])}
containerRefs={containerRefs}
/>
</View>
<View style={[a.flex_1, {aspectRatio: 1}]}>
@@ -69,12 +76,15 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) {
{...props}
index={1}
insetBorderStyle={noCorners(['topLeft', 'bottomLeft'])}
containerRefs={containerRefs}
/>
</View>
</View>
)
}
case 3:
case 3: {
const containerRefs = [containerRef1, containerRef2, containerRef3]
return (
<View style={[a.flex_1, a.flex_row, gap]}>
<View style={[a.flex_1, {aspectRatio: 1}]}>
@@ -82,6 +92,7 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) {
{...props}
index={0}
insetBorderStyle={noCorners(['topRight', 'bottomRight'])}
containerRefs={containerRefs}
/>
</View>
<View style={[a.flex_1, {aspectRatio: 1}, gap]}>
@@ -94,6 +105,7 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) {
'bottomLeft',
'bottomRight',
])}
containerRefs={containerRefs}
/>
</View>
<View style={[a.flex_1]}>
@@ -105,13 +117,21 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) {
'bottomLeft',
'topRight',
])}
containerRefs={containerRefs}
/>
</View>
</View>
</View>
)
}
case 4:
case 4: {
const containerRefs = [
containerRef1,
containerRef2,
containerRef3,
containerRef4,
]
return (
<>
<View style={[a.flex_row, gap]}>
@@ -124,6 +144,7 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) {
'topRight',
'bottomRight',
])}
containerRefs={containerRefs}
/>
</View>
<View style={[a.flex_1, {aspectRatio: 1.5}]}>
@@ -135,6 +156,7 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) {
'bottomLeft',
'bottomRight',
])}
containerRefs={containerRefs}
/>
</View>
</View>
@@ -148,6 +170,7 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) {
'topRight',
'bottomRight',
])}
containerRefs={containerRefs}
/>
</View>
<View style={[a.flex_1, {aspectRatio: 1.5}]}>
@@ -159,11 +182,13 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) {
'bottomLeft',
'topRight',
])}
containerRefs={containerRefs}
/>
</View>
</View>
</>
)
}
default:
return null
+7 -7
View File
@@ -149,25 +149,25 @@ export function PostEmbeds({
}))
const _openLightbox = (
index: number,
thumbDims: MeasuredDimensions | null,
thumbRects: (MeasuredDimensions | null)[],
) => {
openLightbox({
images: items.map(item => ({
images: items.map((item, i) => ({
...item,
thumbRect: thumbRects[i] ?? null,
type: 'image',
})),
index,
thumbDims,
})
}
const onPress = (
index: number,
ref: AnimatedRef<React.Component<{}, {}, any>>,
refs: AnimatedRef<React.Component<{}, {}, any>>[],
) => {
runOnUI(() => {
'worklet'
const dims = measure(ref)
runOnJS(_openLightbox)(index, dims)
const rects = refs.map(ref => (ref ? measure(ref) : null))
runOnJS(_openLightbox)(index, rects)
})()
}
const onPressIn = (_: number) => {
@@ -191,7 +191,7 @@ export function PostEmbeds({
: 'constrained'
}
image={image}
onPress={() => onPress(0, containerRef)}
onPress={() => onPress(0, [containerRef])}
onPressIn={() => onPressIn(0)}
hideBadge={
viewContext === PostEmbedViewContext.FeedEmbedRecordWithMedia