mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import React, {memo} from 'react'
2import {StyleSheet, View} from 'react-native'
3import Svg, {Circle, Line} from 'react-native-svg'
4import {AtUri} from '@atproto/api'
5import {Trans} from '@lingui/macro'
6
7import {usePalette} from '#/lib/hooks/usePalette'
8import {makeProfileLink} from '#/lib/routes/links'
9import {FeedPostSlice} from '#/state/queries/post-feed'
10import {Link} from '../util/Link'
11import {Text} from '../util/text/Text'
12import {FeedItem} from './FeedItem'
13
14let FeedSlice = ({
15 slice,
16 hideTopBorder,
17}: {
18 slice: FeedPostSlice
19 hideTopBorder?: boolean
20}): React.ReactNode => {
21 if (slice.isIncompleteThread && slice.items.length >= 3) {
22 const beforeLast = slice.items.length - 2
23 const last = slice.items.length - 1
24 return (
25 <>
26 <FeedItem
27 key={slice.items[0]._reactKey}
28 post={slice.items[0].post}
29 record={slice.items[0].record}
30 reason={slice.reason}
31 feedContext={slice.feedContext}
32 parentAuthor={slice.items[0].parentAuthor}
33 showReplyTo={false}
34 moderation={slice.items[0].moderation}
35 isThreadParent={isThreadParentAt(slice.items, 0)}
36 isThreadChild={isThreadChildAt(slice.items, 0)}
37 hideTopBorder={hideTopBorder}
38 isParentBlocked={slice.items[0].isParentBlocked}
39 isParentNotFound={slice.items[0].isParentNotFound}
40 rootPost={slice.items[0].post}
41 />
42 <ViewFullThread uri={slice.items[0].uri} />
43 <FeedItem
44 key={slice.items[beforeLast]._reactKey}
45 post={slice.items[beforeLast].post}
46 record={slice.items[beforeLast].record}
47 reason={undefined}
48 feedContext={slice.feedContext}
49 parentAuthor={slice.items[beforeLast].parentAuthor}
50 showReplyTo={
51 slice.items[beforeLast].parentAuthor?.did !==
52 slice.items[beforeLast].post.author.did
53 }
54 moderation={slice.items[beforeLast].moderation}
55 isThreadParent={isThreadParentAt(slice.items, beforeLast)}
56 isThreadChild={isThreadChildAt(slice.items, beforeLast)}
57 isParentBlocked={slice.items[beforeLast].isParentBlocked}
58 isParentNotFound={slice.items[beforeLast].isParentNotFound}
59 rootPost={slice.items[0].post}
60 />
61 <FeedItem
62 key={slice.items[last]._reactKey}
63 post={slice.items[last].post}
64 record={slice.items[last].record}
65 reason={undefined}
66 feedContext={slice.feedContext}
67 parentAuthor={slice.items[last].parentAuthor}
68 showReplyTo={false}
69 moderation={slice.items[last].moderation}
70 isThreadParent={isThreadParentAt(slice.items, last)}
71 isThreadChild={isThreadChildAt(slice.items, last)}
72 isParentBlocked={slice.items[last].isParentBlocked}
73 isParentNotFound={slice.items[last].isParentNotFound}
74 isThreadLastChild
75 rootPost={slice.items[0].post}
76 />
77 </>
78 )
79 }
80
81 return (
82 <>
83 {slice.items.map((item, i) => (
84 <FeedItem
85 key={item._reactKey}
86 post={slice.items[i].post}
87 record={slice.items[i].record}
88 reason={i === 0 ? slice.reason : undefined}
89 feedContext={slice.feedContext}
90 moderation={slice.items[i].moderation}
91 parentAuthor={slice.items[i].parentAuthor}
92 showReplyTo={i === 0}
93 isThreadParent={isThreadParentAt(slice.items, i)}
94 isThreadChild={isThreadChildAt(slice.items, i)}
95 isThreadLastChild={
96 isThreadChildAt(slice.items, i) && slice.items.length === i + 1
97 }
98 isParentBlocked={slice.items[i].isParentBlocked}
99 isParentNotFound={slice.items[i].isParentNotFound}
100 hideTopBorder={hideTopBorder && i === 0}
101 rootPost={slice.items[0].post}
102 />
103 ))}
104 </>
105 )
106}
107FeedSlice = memo(FeedSlice)
108export {FeedSlice}
109
110function ViewFullThread({uri}: {uri: string}) {
111 const pal = usePalette('default')
112 const itemHref = React.useMemo(() => {
113 const urip = new AtUri(uri)
114 return makeProfileLink({did: urip.hostname, handle: ''}, 'post', urip.rkey)
115 }, [uri])
116
117 return (
118 <Link style={[styles.viewFullThread]} href={itemHref} asAnchor noFeedback>
119 <View style={styles.viewFullThreadDots}>
120 <Svg width="4" height="40">
121 <Line
122 x1="2"
123 y1="0"
124 x2="2"
125 y2="15"
126 stroke={pal.colors.replyLine}
127 strokeWidth="2"
128 />
129 <Circle cx="2" cy="22" r="1.5" fill={pal.colors.replyLineDot} />
130 <Circle cx="2" cy="28" r="1.5" fill={pal.colors.replyLineDot} />
131 <Circle cx="2" cy="34" r="1.5" fill={pal.colors.replyLineDot} />
132 </Svg>
133 </View>
134
135 <Text type="md" style={[pal.link, {paddingTop: 18, paddingBottom: 4}]}>
136 <Trans>View full thread</Trans>
137 </Text>
138 </Link>
139 )
140}
141
142const styles = StyleSheet.create({
143 viewFullThread: {
144 flexDirection: 'row',
145 gap: 10,
146 paddingLeft: 18,
147 },
148 viewFullThreadDots: {
149 width: 42,
150 alignItems: 'center',
151 },
152})
153
154function isThreadParentAt<T>(arr: Array<T>, i: number) {
155 if (arr.length === 1) {
156 return false
157 }
158 return i < arr.length - 1
159}
160
161function isThreadChildAt<T>(arr: Array<T>, i: number) {
162 if (arr.length === 1) {
163 return false
164 }
165 return i > 0
166}