about summary refs log tree commit diff
path: root/src/components/Post/Embed/ImageEmbed.tsx
diff options
context:
space:
mode:
authorEric Bailey <git@esb.lol>2025-06-13 12:05:41 -0500
committerGitHub <noreply@github.com>2025-06-13 12:05:41 -0500
commit45f0f7eefecae1922c2f30d4e7760d2b93b1ae56 (patch)
treea2fd6917867f18fe334b54dd3289775c2930bc85 /src/components/Post/Embed/ImageEmbed.tsx
parentba0f5a9bdef5bd0447ded23cab1af222b65511cc (diff)
downloadvoidsky-45f0f7eefecae1922c2f30d4e7760d2b93b1ae56.tar.zst
Port post embeds to new arch (#7408)
* Direct port of embeds to new arch

(cherry picked from commit cc3fa1f6cea396dd9222486c633a508bfee1ecd6)

* Re-org

* Split out ListEmbed and FeedEmbed

* Split out ImageEmbed

* DRY up a bit

* Port over ExternalLinkEmbed

* Port over Player and Gif embeds

* Migrate ComposerReplyTo

* Replace other usages of old post-embeds

* Migrate view contexts

* Copy pasta VideoEmbed

* Copy pasta GifEmbed

* Swap in new file location

* Clean up

* Fix up native

* Add back in correct moderation on List and Feed embeds

* Format

* Prettier

* delete old video utils

* move bandwidth-estimate.ts

* Remove log

* Add LazyQuoteEmbed for composer use

* Clean up unused things

* Remove remaining items

* Prettier

* Fix imports

* Handle nested quotes same as prod

* Add back silenced error handling

* Fix lint

---------

Co-authored-by: Samuel Newman <mozzius@protonmail.com>
Diffstat (limited to 'src/components/Post/Embed/ImageEmbed.tsx')
-rw-r--r--src/components/Post/Embed/ImageEmbed.tsx106
1 files changed, 106 insertions, 0 deletions
diff --git a/src/components/Post/Embed/ImageEmbed.tsx b/src/components/Post/Embed/ImageEmbed.tsx
new file mode 100644
index 000000000..030d237a0
--- /dev/null
+++ b/src/components/Post/Embed/ImageEmbed.tsx
@@ -0,0 +1,106 @@
+import {InteractionManager, View} from 'react-native'
+import {
+  type AnimatedRef,
+  measure,
+  type MeasuredDimensions,
+  runOnJS,
+  runOnUI,
+} from 'react-native-reanimated'
+import {Image} from 'expo-image'
+
+import {useLightboxControls} from '#/state/lightbox'
+import {type Dimensions} from '#/view/com/lightbox/ImageViewing/@types'
+import {AutoSizedImage} from '#/view/com/util/images/AutoSizedImage'
+import {ImageLayoutGrid} from '#/view/com/util/images/ImageLayoutGrid'
+import {atoms as a} from '#/alf'
+import {PostEmbedViewContext} from '#/components/Post/Embed/types'
+import {type EmbedType} from '#/types/bsky/post'
+import {type CommonProps} from './types'
+
+export function ImageEmbed({
+  embed,
+  ...rest
+}: CommonProps & {
+  embed: EmbedType<'images'>
+}) {
+  const {openLightbox} = useLightboxControls()
+  const {images} = embed.view
+
+  if (images.length > 0) {
+    const items = images.map(img => ({
+      uri: img.fullsize,
+      thumbUri: img.thumb,
+      alt: img.alt,
+      dimensions: img.aspectRatio ?? null,
+    }))
+    const _openLightbox = (
+      index: number,
+      thumbRects: (MeasuredDimensions | null)[],
+      fetchedDims: (Dimensions | null)[],
+    ) => {
+      openLightbox({
+        images: items.map((item, i) => ({
+          ...item,
+          thumbRect: thumbRects[i] ?? null,
+          thumbDimensions: fetchedDims[i] ?? null,
+          type: 'image',
+        })),
+        index,
+      })
+    }
+    const onPress = (
+      index: number,
+      refs: AnimatedRef<any>[],
+      fetchedDims: (Dimensions | null)[],
+    ) => {
+      runOnUI(() => {
+        'worklet'
+        const rects: (MeasuredDimensions | null)[] = []
+        for (const r of refs) {
+          rects.push(measure(r))
+        }
+        runOnJS(_openLightbox)(index, rects, fetchedDims)
+      })()
+    }
+    const onPressIn = (_: number) => {
+      InteractionManager.runAfterInteractions(() => {
+        Image.prefetch(items.map(i => i.uri))
+      })
+    }
+
+    if (images.length === 1) {
+      const image = images[0]
+      return (
+        <View style={[a.mt_sm, rest.style]}>
+          <AutoSizedImage
+            crop={
+              rest.viewContext === PostEmbedViewContext.ThreadHighlighted
+                ? 'none'
+                : rest.viewContext ===
+                  PostEmbedViewContext.FeedEmbedRecordWithMedia
+                ? 'square'
+                : 'constrained'
+            }
+            image={image}
+            onPress={(containerRef, dims) => onPress(0, [containerRef], [dims])}
+            onPressIn={() => onPressIn(0)}
+            hideBadge={
+              rest.viewContext === PostEmbedViewContext.FeedEmbedRecordWithMedia
+            }
+          />
+        </View>
+      )
+    }
+
+    return (
+      <View style={[a.mt_sm, rest.style]}>
+        <ImageLayoutGrid
+          images={images}
+          onPress={onPress}
+          onPressIn={onPressIn}
+          viewContext={rest.viewContext}
+        />
+      </View>
+    )
+  }
+}