forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import React from 'react'
2import {Dimensions, ScrollView, View} from 'react-native'
3import {msg, Plural} from '@lingui/macro'
4import {useLingui} from '@lingui/react'
5
6import {useEnableSquareButtons} from '#/state/preferences/enable-square-buttons'
7import {type FeedPostSlice} from '#/state/queries/post-feed'
8import {BlockDrawerGesture} from '#/view/shell/BlockDrawerGesture'
9import {atoms as a, useTheme} from '#/alf'
10import {Button, ButtonIcon} from '#/components/Button'
11import {
12 ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft,
13 ChevronRight_Stroke2_Corner0_Rounded as ChevronRight,
14} from '#/components/icons/Chevron'
15import {Text} from '#/components/Typography'
16import {PostFeedItem} from './PostFeedItem'
17
18const CARD_WIDTH = 320
19const CARD_INTERVAL = CARD_WIDTH + a.gap_md.gap
20
21export function PostFeedItemCarousel({items}: {items: FeedPostSlice[]}) {
22 const t = useTheme()
23 const {_} = useLingui()
24 const ref = React.useRef<ScrollView>(null)
25 const [scrollX, setScrollX] = React.useState(0)
26
27 const enableSquareButtons = useEnableSquareButtons()
28
29 const scrollTo = React.useCallback(
30 (item: number) => {
31 setScrollX(item)
32
33 ref.current?.scrollTo({
34 x: item * CARD_INTERVAL,
35 y: 0,
36 animated: true,
37 })
38 },
39 [ref],
40 )
41
42 const scrollLeft = React.useCallback(() => {
43 const newPos = scrollX > 0 ? scrollX - 1 : items.length - 1
44 scrollTo(newPos)
45 }, [scrollTo, scrollX, items.length])
46
47 const scrollRight = React.useCallback(() => {
48 const newPos = scrollX < items.length - 1 ? scrollX + 1 : 0
49 scrollTo(newPos)
50 }, [scrollTo, scrollX, items.length])
51
52 return (
53 <View
54 style={[a.border_t, t.atoms.border_contrast_low, t.atoms.bg_contrast_25]}>
55 <View
56 style={[
57 a.py_lg,
58 a.px_md,
59 a.pb_xs,
60 a.flex_row,
61 a.align_center,
62 a.justify_between,
63 ]}>
64 <Text style={[a.text_sm, a.font_bold, t.atoms.text_contrast_medium]}>
65 {items.length}{' '}
66 <Plural value={items.length} one="reskeet" other="reskeets" />
67 </Text>
68 <View style={[a.gap_md, a.flex_row, a.align_end]}>
69 <Button
70 label={_(msg`Scroll carousel left`)}
71 size="tiny"
72 variant="ghost"
73 color="secondary"
74 shape={enableSquareButtons ? 'square' : 'round'}
75 onPress={() => scrollLeft()}>
76 <ButtonIcon icon={ChevronLeft} />
77 </Button>
78 <Button
79 label={_(msg`Scroll carousel right`)}
80 size="tiny"
81 variant="ghost"
82 color="secondary"
83 shape={enableSquareButtons ? 'square' : 'round'}
84 onPress={() => scrollRight()}>
85 <ButtonIcon icon={ChevronRight} />
86 </Button>
87 </View>
88 </View>
89 <BlockDrawerGesture>
90 <View>
91 <ScrollView
92 horizontal
93 snapToInterval={CARD_INTERVAL}
94 decelerationRate="fast"
95 /* TODO: figure out how to not get this to break on the last item
96 onScroll={e => {
97 setScrollX(Math.floor(e.nativeEvent.contentOffset.x / CARD_INTERVAL))
98 }}
99*/
100 ref={ref}>
101 <View
102 style={[
103 a.px_md,
104 a.pt_sm,
105 a.pb_lg,
106 a.flex_row,
107 a.gap_md,
108 a.align_start,
109 ]}>
110 {items.map(slice => {
111 const item = slice.items[0]
112
113 return (
114 <View
115 style={[
116 {
117 maxHeight: Dimensions.get('window').height * 0.65,
118 width: CARD_WIDTH,
119 },
120 a.rounded_md,
121 a.border,
122 t.atoms.bg,
123 t.atoms.border_contrast_low,
124 a.flex_shrink_0,
125 a.overflow_hidden,
126 ]}
127 key={item._reactKey}>
128 <PostFeedItem
129 post={item.post}
130 record={item.record}
131 reason={slice.reason}
132 feedContext={slice.feedContext}
133 moderation={item.moderation}
134 parentAuthor={item.parentAuthor}
135 isParentBlocked={item.isParentBlocked}
136 isParentNotFound={item.isParentNotFound}
137 hideTopBorder={true}
138 isCarouselItem={true}
139 rootPost={slice.items[0].post}
140 showReplyTo={false}
141 reqId={undefined}
142 />
143 </View>
144 )
145 })}
146 </View>
147 </ScrollView>
148 </View>
149 </BlockDrawerGesture>
150 </View>
151 )
152}