about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/screens/Profile/Header/Shell.tsx1
-rw-r--r--src/state/lightbox.tsx3
-rw-r--r--src/view/com/lightbox/ImageViewing/index.tsx3
-rw-r--r--src/view/com/lightbox/Lightbox.tsx2
-rw-r--r--src/view/com/profile/ProfileSubpageHeader.tsx1
-rw-r--r--src/view/com/util/images/Gallery.tsx13
-rw-r--r--src/view/com/util/images/ImageLayoutGrid.tsx11
-rw-r--r--src/view/com/util/post-embeds/index.tsx33
8 files changed, 56 insertions, 11 deletions
diff --git a/src/screens/Profile/Header/Shell.tsx b/src/screens/Profile/Header/Shell.tsx
index ac98deed7..26e940688 100644
--- a/src/screens/Profile/Header/Shell.tsx
+++ b/src/screens/Profile/Header/Shell.tsx
@@ -57,6 +57,7 @@ let ProfileHeaderShell = ({
       openLightbox({
         type: 'profile-image',
         profile: profile,
+        thumbDims: null,
       })
     }
   }, [openLightbox, profile, moderation])
diff --git a/src/state/lightbox.tsx b/src/state/lightbox.tsx
index 0760d2c96..eb5a88864 100644
--- a/src/state/lightbox.tsx
+++ b/src/state/lightbox.tsx
@@ -1,4 +1,5 @@
 import React from 'react'
+import type {MeasuredDimensions} from 'react-native-reanimated'
 import {AppBskyActorDefs} from '@atproto/api'
 
 import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
@@ -6,6 +7,7 @@ import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
 type ProfileImageLightbox = {
   type: 'profile-image'
   profile: AppBskyActorDefs.ProfileViewDetailed
+  thumbDims: null
 }
 
 type ImagesLightboxItem = {
@@ -17,6 +19,7 @@ type ImagesLightboxItem = {
 type ImagesLightbox = {
   type: 'images'
   images: ImagesLightboxItem[]
+  thumbDims: MeasuredDimensions | null
   index: number
 }
 
diff --git a/src/view/com/lightbox/ImageViewing/index.tsx b/src/view/com/lightbox/ImageViewing/index.tsx
index 1432b34ff..0d0ac4df1 100644
--- a/src/view/com/lightbox/ImageViewing/index.tsx
+++ b/src/view/com/lightbox/ImageViewing/index.tsx
@@ -11,6 +11,7 @@
 import React, {ComponentType, useCallback, useMemo, useState} from 'react'
 import {Platform, StyleSheet, View} from 'react-native'
 import PagerView from 'react-native-pager-view'
+import {MeasuredDimensions} from 'react-native-reanimated'
 import Animated, {useAnimatedStyle, withSpring} from 'react-native-reanimated'
 import {Edge, SafeAreaView} from 'react-native-safe-area-context'
 
@@ -20,6 +21,7 @@ import ImageItem from './components/ImageItem/ImageItem'
 
 type Props = {
   images: ImageSource[]
+  thumbDims: MeasuredDimensions | null
   initialImageIndex: number
   visible: boolean
   onRequestClose: () => void
@@ -32,6 +34,7 @@ const DEFAULT_BG_COLOR = '#000'
 
 function ImageViewing({
   images,
+  thumbDims: _thumbDims, // TODO: Pass down and use for animation.
   initialImageIndex,
   visible,
   onRequestClose,
diff --git a/src/view/com/lightbox/Lightbox.tsx b/src/view/com/lightbox/Lightbox.tsx
index a7f8fed77..891be3f9c 100644
--- a/src/view/com/lightbox/Lightbox.tsx
+++ b/src/view/com/lightbox/Lightbox.tsx
@@ -35,6 +35,7 @@ export function Lightbox() {
           {uri: opts.profile.avatar || '', thumbUri: opts.profile.avatar || ''},
         ]}
         initialImageIndex={0}
+        thumbDims={opts.thumbDims}
         visible
         onRequestClose={onClose}
         FooterComponent={LightboxFooter}
@@ -46,6 +47,7 @@ export function Lightbox() {
       <ImageView
         images={opts.images.map(img => ({...img}))}
         initialImageIndex={opts.index}
+        thumbDims={opts.thumbDims}
         visible
         onRequestClose={onClose}
         FooterComponent={LightboxFooter}
diff --git a/src/view/com/profile/ProfileSubpageHeader.tsx b/src/view/com/profile/ProfileSubpageHeader.tsx
index 09f074e50..785080c4b 100644
--- a/src/view/com/profile/ProfileSubpageHeader.tsx
+++ b/src/view/com/profile/ProfileSubpageHeader.tsx
@@ -74,6 +74,7 @@ export function ProfileSubpageHeader({
         type: 'images',
         images: [{uri: avatar, thumbUri: avatar}],
         index: 0,
+        thumbDims: null,
       })
     }
   }, [openLightbox, avatar])
diff --git a/src/view/com/util/images/Gallery.tsx b/src/view/com/util/images/Gallery.tsx
index e277b7e86..d4d7d223d 100644
--- a/src/view/com/util/images/Gallery.tsx
+++ b/src/view/com/util/images/Gallery.tsx
@@ -1,5 +1,6 @@
 import React from 'react'
 import {Pressable, StyleProp, View, ViewStyle} from 'react-native'
+import Animated, {AnimatedRef, useAnimatedRef} from 'react-native-reanimated'
 import {Image, ImageStyle} from 'expo-image'
 import {AppBskyEmbedImages} from '@atproto/api'
 import {msg} from '@lingui/macro'
@@ -16,7 +17,10 @@ type EventFunction = (index: number) => void
 interface Props {
   images: AppBskyEmbedImages.ViewImage[]
   index: number
-  onPress?: EventFunction
+  onPress?: (
+    index: number,
+    containerRef: AnimatedRef<React.Component<{}, {}, any>>,
+  ) => void
   onLongPress?: EventFunction
   onPressIn?: EventFunction
   imageStyle?: StyleProp<ImageStyle>
@@ -41,10 +45,11 @@ export function GalleryItem({
   const hasAlt = !!image.alt
   const hideBadges =
     viewContext === PostEmbedViewContext.FeedEmbedRecordWithMedia
+  const containerRef = useAnimatedRef()
   return (
-    <View style={a.flex_1}>
+    <Animated.View style={a.flex_1} ref={containerRef}>
       <Pressable
-        onPress={onPress ? () => onPress(index) : undefined}
+        onPress={onPress ? () => onPress(index, containerRef) : undefined}
         onPressIn={onPressIn ? () => onPressIn(index) : undefined}
         onLongPress={onLongPress ? () => onLongPress(index) : undefined}
         style={[
@@ -95,6 +100,6 @@ export function GalleryItem({
           </Text>
         </View>
       ) : null}
-    </View>
+    </Animated.View>
   )
 }
diff --git a/src/view/com/util/images/ImageLayoutGrid.tsx b/src/view/com/util/images/ImageLayoutGrid.tsx
index 7475a96c3..830040ba6 100644
--- a/src/view/com/util/images/ImageLayoutGrid.tsx
+++ b/src/view/com/util/images/ImageLayoutGrid.tsx
@@ -1,5 +1,6 @@
 import React from 'react'
 import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
+import {AnimatedRef} from 'react-native-reanimated'
 import {AppBskyEmbedImages} from '@atproto/api'
 
 import {PostEmbedViewContext} from '#/view/com/util/post-embeds/types'
@@ -8,7 +9,10 @@ import {GalleryItem} from './Gallery'
 
 interface ImageLayoutGridProps {
   images: AppBskyEmbedImages.ViewImage[]
-  onPress?: (index: number) => void
+  onPress?: (
+    index: number,
+    containerRef: AnimatedRef<React.Component<{}, {}, any>>,
+  ) => void
   onLongPress?: (index: number) => void
   onPressIn?: (index: number) => void
   style?: StyleProp<ViewStyle>
@@ -36,7 +40,10 @@ export function ImageLayoutGrid({style, ...props}: ImageLayoutGridProps) {
 
 interface ImageLayoutGridInnerProps {
   images: AppBskyEmbedImages.ViewImage[]
-  onPress?: (index: number) => void
+  onPress?: (
+    index: number,
+    containerRef: AnimatedRef<React.Component<{}, {}, any>>,
+  ) => void
   onLongPress?: (index: number) => void
   onPressIn?: (index: number) => void
   viewContext?: PostEmbedViewContext
diff --git a/src/view/com/util/post-embeds/index.tsx b/src/view/com/util/post-embeds/index.tsx
index 575b26694..19769bcd7 100644
--- a/src/view/com/util/post-embeds/index.tsx
+++ b/src/view/com/util/post-embeds/index.tsx
@@ -6,6 +6,14 @@ import {
   View,
   ViewStyle,
 } from 'react-native'
+import Animated, {
+  AnimatedRef,
+  measure,
+  MeasuredDimensions,
+  runOnJS,
+  runOnUI,
+  useAnimatedRef,
+} from 'react-native-reanimated'
 import {Image} from 'expo-image'
 import {
   AppBskyEmbedExternal,
@@ -61,6 +69,7 @@ export function PostEmbeds({
   viewContext?: PostEmbedViewContext
 }) {
   const {openLightbox} = useLightboxControls()
+  const containerRef = useAnimatedRef()
 
   // quote post with media
   // =
@@ -138,13 +147,27 @@ export function PostEmbeds({
         alt: img.alt,
         aspectRatio: img.aspectRatio,
       }))
-      const _openLightbox = (index: number) => {
+      const _openLightbox = (
+        index: number,
+        thumbDims: MeasuredDimensions | null,
+      ) => {
         openLightbox({
           type: 'images',
           images: items,
           index,
+          thumbDims,
         })
       }
+      const onPress = (
+        index: number,
+        ref: AnimatedRef<React.Component<{}, {}, any>>,
+      ) => {
+        runOnUI(() => {
+          'worklet'
+          const dims = measure(ref)
+          runOnJS(_openLightbox)(index, dims)
+        })()
+      }
       const onPressIn = (_: number) => {
         InteractionManager.runAfterInteractions(() => {
           Image.prefetch(items.map(i => i.uri))
@@ -155,7 +178,7 @@ export function PostEmbeds({
         const image = images[0]
         return (
           <ContentHider modui={moderation?.ui('contentMedia')}>
-            <View style={[a.mt_sm, style]}>
+            <Animated.View ref={containerRef} style={[a.mt_sm, style]}>
               <AutoSizedImage
                 crop={
                   viewContext === PostEmbedViewContext.ThreadHighlighted
@@ -166,13 +189,13 @@ export function PostEmbeds({
                     : 'constrained'
                 }
                 image={image}
-                onPress={() => _openLightbox(0)}
+                onPress={() => onPress(0, containerRef)}
                 onPressIn={() => onPressIn(0)}
                 hideBadge={
                   viewContext === PostEmbedViewContext.FeedEmbedRecordWithMedia
                 }
               />
-            </View>
+            </Animated.View>
           </ContentHider>
         )
       }
@@ -182,7 +205,7 @@ export function PostEmbeds({
           <View style={[a.mt_sm, style]}>
             <ImageLayoutGrid
               images={embed.images}
-              onPress={_openLightbox}
+              onPress={onPress}
               onPressIn={onPressIn}
               viewContext={viewContext}
             />