about summary refs log tree commit diff
path: root/src/view/com/util/images/ImageLayoutGrid.tsx
blob: ba6c04f5056b5163c78287f634136f7f6748f080 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import React from 'react'
import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
import {AppBskyEmbedImages} from '@atproto/api'
import {GalleryItem} from './Gallery'
import {isWeb} from 'platform/detection'

interface ImageLayoutGridProps {
  images: AppBskyEmbedImages.ViewImage[]
  onPress?: (index: number) => void
  onLongPress?: (index: number) => void
  onPressIn?: (index: number) => void
  style?: StyleProp<ViewStyle>
}

export function ImageLayoutGrid({style, ...props}: ImageLayoutGridProps) {
  return (
    <View style={style}>
      <View style={styles.container}>
        <ImageLayoutGridInner {...props} />
      </View>
    </View>
  )
}

interface ImageLayoutGridInnerProps {
  images: AppBskyEmbedImages.ViewImage[]
  onPress?: (index: number) => void
  onLongPress?: (index: number) => void
  onPressIn?: (index: number) => void
}

function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) {
  const count = props.images.length

  switch (count) {
    case 2:
      return (
        <View style={styles.flexRow}>
          <View style={styles.smallItem}>
            <GalleryItem {...props} index={0} imageStyle={styles.image} />
          </View>
          <View style={styles.smallItem}>
            <GalleryItem {...props} index={1} imageStyle={styles.image} />
          </View>
        </View>
      )

    case 3:
      return (
        <View style={styles.flexRow}>
          <View style={styles.threeSingle}>
            <GalleryItem {...props} index={0} imageStyle={styles.image} />
          </View>
          <View style={styles.threeDouble}>
            <View style={styles.smallItem}>
              <GalleryItem {...props} index={1} imageStyle={styles.image} />
            </View>
            <View style={styles.smallItem}>
              <GalleryItem {...props} index={2} imageStyle={styles.image} />
            </View>
          </View>
        </View>
      )

    case 4:
      return (
        <>
          <View style={styles.flexRow}>
            <View style={styles.smallItem}>
              <GalleryItem {...props} index={0} imageStyle={styles.image} />
            </View>
            <View style={styles.smallItem}>
              <GalleryItem {...props} index={1} imageStyle={styles.image} />
            </View>
          </View>
          <View style={styles.flexRow}>
            <View style={styles.smallItem}>
              <GalleryItem {...props} index={2} imageStyle={styles.image} />
            </View>
            <View style={styles.smallItem}>
              <GalleryItem {...props} index={3} imageStyle={styles.image} />
            </View>
          </View>
        </>
      )

    default:
      return null
  }
}

// On web we use margin to calculate gap, as aspectRatio does not properly size
// all images on web. On native though we cannot rely on margin, since the
// negative margin interferes with the swipe controls on pagers.
// https://github.com/facebook/yoga/issues/1418
// https://github.com/bluesky-social/social-app/issues/2601
const IMAGE_GAP = 5

const styles = StyleSheet.create({
  container: isWeb
    ? {
        marginHorizontal: -IMAGE_GAP / 2,
        marginVertical: -IMAGE_GAP / 2,
      }
    : {
        gap: IMAGE_GAP,
      },
  flexRow: {
    flexDirection: 'row',
    gap: isWeb ? undefined : IMAGE_GAP,
  },
  smallItem: {flex: 1, aspectRatio: 1},
  image: isWeb
    ? {
        margin: IMAGE_GAP / 2,
      }
    : {},
  threeSingle: {
    flex: 2,
    aspectRatio: isWeb ? 1 : undefined,
  },
  threeDouble: {
    flex: 1,
    gap: isWeb ? undefined : IMAGE_GAP,
  },
})