about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/view/com/util/PostEmbeds.tsx87
-rw-r--r--src/view/com/util/images/AutoSizedImage.tsx36
-rw-r--r--src/view/com/util/images/ImageLayoutGrid.tsx148
3 files changed, 192 insertions, 79 deletions
diff --git a/src/view/com/util/PostEmbeds.tsx b/src/view/com/util/PostEmbeds.tsx
index 870df50a7..1d5c690f6 100644
--- a/src/view/com/util/PostEmbeds.tsx
+++ b/src/view/com/util/PostEmbeds.tsx
@@ -3,11 +3,10 @@ import {ImageStyle, StyleSheet, StyleProp, View, ViewStyle} from 'react-native'
 import {AppBskyEmbedImages, AppBskyEmbedExternal} from '@atproto/api'
 import {Link} from '../util/Link'
 import {Text} from './text/Text'
-import {colors} from '../../lib/styles'
 import {AutoSizedImage} from './images/AutoSizedImage'
+import {ImageLayoutGrid} from './images/ImageLayoutGrid'
 import {ImagesLightbox} from '../../../state/models/shell-ui'
 import {useStores} from '../../../state'
-import {useTheme} from '../../lib/ThemeContext'
 import {usePalette} from '../../lib/hooks/usePalette'
 
 type Embed =
@@ -22,7 +21,6 @@ export function PostEmbeds({
   embed?: Embed
   style?: StyleProp<ViewStyle>
 }) {
-  const theme = useTheme()
   const pal = usePalette('default')
   const store = useStores()
   if (embed?.$type === 'app.bsky.embed.images#presented') {
@@ -32,59 +30,44 @@ export function PostEmbeds({
       const openLightbox = (index: number) => {
         store.shell.openLightbox(new ImagesLightbox(uris, index))
       }
-      const Thumb = ({i, style}: {i: number; style: StyleProp<ImageStyle>}) => (
-        <AutoSizedImage
-          style={style}
-          uri={imgEmbed.images[i].thumb}
-          onPress={() => openLightbox(i)}
-        />
-      )
       if (imgEmbed.images.length === 4) {
         return (
           <View style={styles.imagesContainer}>
-            <View style={styles.imagePair}>
-              <Thumb i={0} style={styles.imagePairItem} />
-              <View style={styles.imagesWidthSpacer} />
-              <Thumb i={1} style={styles.imagePairItem} />
-            </View>
-            <View style={styles.imagesHeightSpacer} />
-            <View style={styles.imagePair}>
-              <Thumb i={2} style={styles.imagePairItem} />
-              <View style={styles.imagesWidthSpacer} />
-              <Thumb i={3} style={styles.imagePairItem} />
-            </View>
+            <ImageLayoutGrid
+              type="four"
+              uris={imgEmbed.images.map(img => img.thumb)}
+              onPress={openLightbox}
+            />
           </View>
         )
       } else if (imgEmbed.images.length === 3) {
         return (
           <View style={styles.imagesContainer}>
-            <View style={styles.imageWide}>
-              <Thumb i={0} style={styles.imageWideItem} />
-            </View>
-            <View style={styles.imagesHeightSpacer} />
-            <View style={styles.imagePair}>
-              <Thumb i={1} style={styles.imagePairItem} />
-              <View style={styles.imagesWidthSpacer} />
-              <Thumb i={2} style={styles.imagePairItem} />
-            </View>
+            <ImageLayoutGrid
+              type="three"
+              uris={imgEmbed.images.map(img => img.thumb)}
+              onPress={openLightbox}
+            />
           </View>
         )
       } else if (imgEmbed.images.length === 2) {
         return (
           <View style={styles.imagesContainer}>
-            <View style={styles.imagePair}>
-              <Thumb i={0} style={styles.imagePairItem} />
-              <View style={styles.imagesWidthSpacer} />
-              <Thumb i={1} style={styles.imagePairItem} />
-            </View>
+            <ImageLayoutGrid
+              type="two"
+              uris={imgEmbed.images.map(img => img.thumb)}
+              onPress={openLightbox}
+            />
           </View>
         )
       } else {
         return (
           <View style={styles.imagesContainer}>
-            <View style={styles.imageBig}>
-              <Thumb i={0} style={styles.imageBigItem} />
-            </View>
+            <AutoSizedImage
+              uri={imgEmbed.images[0].thumb}
+              onPress={() => openLightbox(0)}
+              containerStyle={{borderRadius: 4}}
+            />
           </View>
         )
       }
@@ -99,7 +82,7 @@ export function PostEmbeds({
         href={link.uri}
         noFeedback>
         {link.thumb ? (
-          <AutoSizedImage style={style} uri={link.thumb} />
+          <AutoSizedImage uri={link.thumb} containerStyle={{borderRadius: 4}} />
         ) : undefined}
         <Text type="h5" numberOfLines={1} style={pal.text}>
           {link.title || link.uri}
@@ -123,34 +106,10 @@ export function PostEmbeds({
 
 const styles = StyleSheet.create({
   imagesContainer: {
+    marginTop: 4,
     marginBottom: 6,
   },
-  imagesWidthSpacer: {
-    width: 5,
-  },
-  imagesHeightSpacer: {
-    height: 5,
-  },
-  imagePair: {
-    flexDirection: 'row',
-  },
-  imagePairItem: {
-    resizeMode: 'contain',
-    flex: 1,
-    borderRadius: 4,
-  },
-  imageWide: {},
-  imageWideItem: {
-    resizeMode: 'contain',
-    borderRadius: 4,
-  },
-  imageBig: {},
-  imageBigItem: {
-    borderRadius: 4,
-  },
-
   extOuter: {
-    borderRadius: 8,
     padding: 10,
   },
   extDescription: {
diff --git a/src/view/com/util/images/AutoSizedImage.tsx b/src/view/com/util/images/AutoSizedImage.tsx
index 9de443b7f..a711323a9 100644
--- a/src/view/com/util/images/AutoSizedImage.tsx
+++ b/src/view/com/util/images/AutoSizedImage.tsx
@@ -10,7 +10,8 @@ import {
   ViewStyle,
 } from 'react-native'
 import {Text} from '../text/Text'
-import {colors} from '../../../lib/styles'
+import {useTheme} from '../../../lib/ThemeContext'
+import {usePalette} from '../../../lib/hooks/usePalette'
 
 const MAX_HEIGHT = 300
 
@@ -23,12 +24,16 @@ export function AutoSizedImage({
   uri,
   onPress,
   style,
+  containerStyle,
 }: {
   uri: string
   onPress?: () => void
-  style: StyleProp<ImageStyle>
+  style?: StyleProp<ImageStyle>
+  containerStyle?: StyleProp<ViewStyle>
 }) {
-  const [error, setError] = useState<string | undefined>()
+  const theme = useTheme()
+  const errPal = usePalette('error')
+  const [error, setError] = useState<string | undefined>('')
   const [imgInfo, setImgInfo] = useState<Dim | undefined>()
   const [containerInfo, setContainerInfo] = useState<Dim | undefined>()
 
@@ -77,15 +82,22 @@ export function AutoSizedImage({
     <View style={style}>
       <TouchableWithoutFeedback onPress={onPress}>
         {error ? (
-          <View style={[styles.container, styles.errorContainer]}>
-            <Text style={styles.error}>{error}</Text>
+          <View style={[styles.errorContainer, errPal.view, containerStyle]}>
+            <Text style={errPal.text}>{error}</Text>
           </View>
         ) : calculatedStyle ? (
-          <View style={styles.container}>
+          <View style={[styles.container, containerStyle]}>
             <Image style={calculatedStyle} source={{uri}} />
           </View>
         ) : (
-          <View style={[style, styles.placeholder]} onLayout={onLayout} />
+          <View
+            style={[
+              style,
+              styles.placeholder,
+              {backgroundColor: theme.palette.default.backgroundLight},
+            ]}
+            onLayout={onLayout}
+          />
         )}
       </TouchableWithoutFeedback>
     </View>
@@ -96,18 +108,12 @@ const styles = StyleSheet.create({
   placeholder: {
     width: '100%',
     aspectRatio: 1,
-    backgroundColor: colors.gray1,
   },
   errorContainer: {
-    backgroundColor: colors.red1,
-    paddingHorizontal: 8,
-    paddingVertical: 4,
+    paddingHorizontal: 12,
+    paddingVertical: 8,
   },
   container: {
-    borderRadius: 8,
     overflow: 'hidden',
   },
-  error: {
-    color: colors.red5,
-  },
 })
diff --git a/src/view/com/util/images/ImageLayoutGrid.tsx b/src/view/com/util/images/ImageLayoutGrid.tsx
new file mode 100644
index 000000000..cb560dd35
--- /dev/null
+++ b/src/view/com/util/images/ImageLayoutGrid.tsx
@@ -0,0 +1,148 @@
+import React from 'react'
+import {
+  Image,
+  ImageStyle,
+  LayoutChangeEvent,
+  StyleProp,
+  StyleSheet,
+  TouchableWithoutFeedback,
+  View,
+  ViewStyle,
+} from 'react-native'
+
+interface Dim {
+  width: number
+  height: number
+}
+
+export type ImageLayoutGridType = 'two' | 'three' | 'four'
+
+export function ImageLayoutGrid({
+  type,
+  uris,
+  onPress,
+  style,
+}: {
+  type: ImageLayoutGridType
+  uris: string
+  onPress?: (index: number) => void
+  style?: StyleProp<ViewStyle>
+}) {
+  const [containerInfo, setContainerInfo] = React.useState<Dim | undefined>()
+
+  const onLayout = (evt: LayoutChangeEvent) => {
+    setContainerInfo({
+      width: evt.nativeEvent.layout.width,
+      height: evt.nativeEvent.layout.height,
+    })
+  }
+
+  return (
+    <View style={style} onLayout={onLayout}>
+      {containerInfo ? (
+        <ImageLayoutGridInner
+          type={type}
+          uris={uris}
+          onPress={onPress}
+          containerInfo={containerInfo}
+        />
+      ) : undefined}
+    </View>
+  )
+}
+
+function ImageLayoutGridInner({
+  type,
+  uris,
+  onPress,
+  containerInfo,
+}: {
+  type: ImageLayoutGridType
+  uris: string
+  onPress?: (index: number) => void
+  containerInfo: Dim
+}) {
+  const size1 = React.useMemo<ImageStyle>(() => {
+    if (type === 'three') {
+      const size = (containerInfo.width - 10) / 3
+      return {width: size, height: size, resizeMode: 'cover', borderRadius: 4}
+    } else {
+      const size = (containerInfo.width - 5) / 2
+      return {width: size, height: size, resizeMode: 'cover', borderRadius: 4}
+    }
+  }, [type, containerInfo])
+  const size2 = React.useMemo<ImageStyle>(() => {
+    if (type === 'three') {
+      const size = ((containerInfo.width - 10) / 3) * 2 + 5
+      return {width: size, height: size, resizeMode: 'cover', borderRadius: 4}
+    } else {
+      const size = (containerInfo.width - 5) / 2
+      return {width: size, height: size, resizeMode: 'cover', borderRadius: 4}
+    }
+  }, [type, containerInfo])
+
+  if (type === 'two') {
+    return (
+      <View style={styles.flexRow}>
+        <TouchableWithoutFeedback onPress={() => onPress?.(0)}>
+          <Image source={{uri: uris[0]}} style={size1} />
+        </TouchableWithoutFeedback>
+        <View style={styles.wSpace} />
+        <TouchableWithoutFeedback onPress={() => onPress?.(1)}>
+          <Image source={{uri: uris[1]}} style={size1} />
+        </TouchableWithoutFeedback>
+      </View>
+    )
+  }
+  if (type === 'three') {
+    return (
+      <View style={styles.flexRow}>
+        <TouchableWithoutFeedback onPress={() => onPress?.(0)}>
+          <Image source={{uri: uris[0]}} style={size2} />
+        </TouchableWithoutFeedback>
+        <View style={styles.wSpace} />
+        <View>
+          <TouchableWithoutFeedback onPress={() => onPress?.(1)}>
+            <Image source={{uri: uris[1]}} style={size1} />
+          </TouchableWithoutFeedback>
+          <View style={{height: 5}} />
+          <TouchableWithoutFeedback onPress={() => onPress?.(2)}>
+            <Image source={{uri: uris[2]}} style={size1} />
+          </TouchableWithoutFeedback>
+        </View>
+      </View>
+    )
+  }
+  if (type === 'four') {
+    return (
+      <View style={styles.flexRow}>
+        <View>
+          <TouchableWithoutFeedback onPress={() => onPress?.(0)}>
+            <Image source={{uri: uris[0]}} style={size1} />
+          </TouchableWithoutFeedback>
+          <View style={styles.hSpace} />
+          <TouchableWithoutFeedback onPress={() => onPress?.(1)}>
+            <Image source={{uri: uris[1]}} style={size1} />
+          </TouchableWithoutFeedback>
+        </View>
+        <View style={styles.wSpace} />
+        <View>
+          <TouchableWithoutFeedback onPress={() => onPress?.(2)}>
+            <Image source={{uri: uris[2]}} style={size1} />
+          </TouchableWithoutFeedback>
+          <View style={styles.hSpace} />
+          <TouchableWithoutFeedback onPress={() => onPress?.(3)}>
+            <Image source={{uri: uris[3]}} style={size1} />
+          </TouchableWithoutFeedback>
+        </View>
+      </View>
+    )
+  }
+  return <View />
+}
+
+const styles = StyleSheet.create({
+  flexRow: {flexDirection: 'row'},
+  wSpace: {width: 5},
+  hSpace: {height: 5},
+})