forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import {useRef} from 'react'
2import {type StyleProp, View, type ViewStyle} from 'react-native'
3import {type AnimatedRef, useAnimatedRef} from 'react-native-reanimated'
4import {type AppBskyEmbedImages} from '@atproto/api'
5
6import {type Dimensions} from '#/view/com/lightbox/ImageViewing/@types'
7import {atoms as a, useBreakpoints} from '#/alf'
8import {PostEmbedViewContext} from '#/components/Post/Embed/types'
9import {GalleryItem} from './Gallery'
10
11interface ImageLayoutGridProps {
12 images: AppBskyEmbedImages.ViewImage[]
13 onPress?: (
14 index: number,
15 containerRefs: AnimatedRef<any>[],
16 fetchedDims: (Dimensions | null)[],
17 ) => void
18 onLongPress?: (index: number) => void
19 onPressIn?: (index: number) => void
20 style?: StyleProp<ViewStyle>
21 viewContext?: PostEmbedViewContext
22}
23
24export function ImageLayoutGrid({style, ...props}: ImageLayoutGridProps) {
25 const {gtMobile} = useBreakpoints()
26 const gap =
27 props.viewContext === PostEmbedViewContext.FeedEmbedRecordWithMedia
28 ? gtMobile
29 ? a.gap_xs
30 : a.gap_2xs
31 : a.gap_xs
32
33 return (
34 <View style={style}>
35 <View style={[gap, a.rounded_md, a.overflow_hidden]}>
36 <ImageLayoutGridInner {...props} gap={gap} />
37 </View>
38 </View>
39 )
40}
41
42interface ImageLayoutGridInnerProps {
43 images: AppBskyEmbedImages.ViewImage[]
44 onPress?: (
45 index: number,
46 containerRefs: AnimatedRef<any>[],
47 fetchedDims: (Dimensions | null)[],
48 ) => void
49 onLongPress?: (index: number) => void
50 onPressIn?: (index: number) => void
51 viewContext?: PostEmbedViewContext
52 gap: {gap: number}
53}
54
55function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) {
56 const gap = props.gap
57 const count = props.images.length
58
59 const containerRef1 = useAnimatedRef()
60 const containerRef2 = useAnimatedRef()
61 const containerRef3 = useAnimatedRef()
62 const containerRef4 = useAnimatedRef()
63 const thumbDimsRef = useRef<(Dimensions | null)[]>([])
64
65 switch (count) {
66 case 2: {
67 const containerRefs = [containerRef1, containerRef2]
68 return (
69 <View style={[a.flex_1, a.flex_row, gap]}>
70 <View style={[a.flex_1, a.aspect_square]}>
71 <GalleryItem
72 {...props}
73 index={0}
74 insetBorderStyle={noCorners(['topRight', 'bottomRight'])}
75 containerRefs={containerRefs}
76 thumbDimsRef={thumbDimsRef}
77 />
78 </View>
79 <View style={[a.flex_1, a.aspect_square]}>
80 <GalleryItem
81 {...props}
82 index={1}
83 insetBorderStyle={noCorners(['topLeft', 'bottomLeft'])}
84 containerRefs={containerRefs}
85 thumbDimsRef={thumbDimsRef}
86 />
87 </View>
88 </View>
89 )
90 }
91
92 case 3: {
93 const containerRefs = [containerRef1, containerRef2, containerRef3]
94 return (
95 <View style={[a.flex_1, a.flex_row, gap]}>
96 <View style={[a.flex_1, a.aspect_square]}>
97 <GalleryItem
98 {...props}
99 index={0}
100 insetBorderStyle={noCorners(['topRight', 'bottomRight'])}
101 containerRefs={containerRefs}
102 thumbDimsRef={thumbDimsRef}
103 />
104 </View>
105 <View style={[a.flex_1, a.aspect_square, gap]}>
106 <View style={[a.flex_1]}>
107 <GalleryItem
108 {...props}
109 index={1}
110 insetBorderStyle={noCorners([
111 'topLeft',
112 'bottomLeft',
113 'bottomRight',
114 ])}
115 containerRefs={containerRefs}
116 thumbDimsRef={thumbDimsRef}
117 />
118 </View>
119 <View style={[a.flex_1]}>
120 <GalleryItem
121 {...props}
122 index={2}
123 insetBorderStyle={noCorners([
124 'topLeft',
125 'bottomLeft',
126 'topRight',
127 ])}
128 containerRefs={containerRefs}
129 thumbDimsRef={thumbDimsRef}
130 />
131 </View>
132 </View>
133 </View>
134 )
135 }
136
137 case 4: {
138 const containerRefs = [
139 containerRef1,
140 containerRef2,
141 containerRef3,
142 containerRef4,
143 ]
144 return (
145 <>
146 <View style={[a.flex_row, gap]}>
147 <View style={[a.flex_1, {aspectRatio: 1.5}]}>
148 <GalleryItem
149 {...props}
150 index={0}
151 insetBorderStyle={noCorners([
152 'bottomLeft',
153 'topRight',
154 'bottomRight',
155 ])}
156 containerRefs={containerRefs}
157 thumbDimsRef={thumbDimsRef}
158 />
159 </View>
160 <View style={[a.flex_1, {aspectRatio: 1.5}]}>
161 <GalleryItem
162 {...props}
163 index={1}
164 insetBorderStyle={noCorners([
165 'topLeft',
166 'bottomLeft',
167 'bottomRight',
168 ])}
169 containerRefs={containerRefs}
170 thumbDimsRef={thumbDimsRef}
171 />
172 </View>
173 </View>
174 <View style={[a.flex_row, gap]}>
175 <View style={[a.flex_1, {aspectRatio: 1.5}]}>
176 <GalleryItem
177 {...props}
178 index={2}
179 insetBorderStyle={noCorners([
180 'topLeft',
181 'topRight',
182 'bottomRight',
183 ])}
184 containerRefs={containerRefs}
185 thumbDimsRef={thumbDimsRef}
186 />
187 </View>
188 <View style={[a.flex_1, {aspectRatio: 1.5}]}>
189 <GalleryItem
190 {...props}
191 index={3}
192 insetBorderStyle={noCorners([
193 'topLeft',
194 'bottomLeft',
195 'topRight',
196 ])}
197 containerRefs={containerRefs}
198 thumbDimsRef={thumbDimsRef}
199 />
200 </View>
201 </View>
202 </>
203 )
204 }
205
206 default:
207 return null
208 }
209}
210
211function noCorners(
212 corners: ('topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight')[],
213) {
214 const styles: StyleProp<ViewStyle>[] = []
215 if (corners.includes('topLeft')) {
216 styles.push({borderTopLeftRadius: 0})
217 }
218 if (corners.includes('topRight')) {
219 styles.push({borderTopRightRadius: 0})
220 }
221 if (corners.includes('bottomLeft')) {
222 styles.push({borderBottomLeftRadius: 0})
223 }
224 if (corners.includes('bottomRight')) {
225 styles.push({borderBottomRightRadius: 0})
226 }
227 return styles
228}