forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import {type RefObject, useCallback} from 'react'
2import {View} from 'react-native'
3import {type AppBskyFeedDefs} from '@atproto/api'
4import {Trans, useLingui} from '@lingui/react/macro'
5import {useQueryClient} from '@tanstack/react-query'
6
7import {cleanError} from '#/lib/strings/errors'
8import {unstableCacheProfileView} from '#/state/queries/profile'
9import {
10 buildPostSourceKey,
11 setUnstablePostSource,
12} from '#/state/unstable-post-source'
13import {Post} from '#/view/com/post/Post'
14import {PostLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
15import {atoms as a, useTheme} from '#/alf'
16import * as Button from '#/components/Button'
17import {
18 ChevronBottom_Stroke2_Corner0_Rounded as ChevronDownIcon,
19 ChevronTop_Stroke2_Corner0_Rounded as ChevronUpIcon,
20} from '#/components/icons/Chevron'
21import {Text} from '#/components/Typography'
22
23export function ThreadAlsoLiked({
24 posts,
25 visible,
26 collapsed,
27 isLoading: _isLoading,
28 showLoadingState,
29 isFetchingNextPage,
30 error,
31 onRetry,
32 headerRef,
33 onToggleCollapsed,
34 spacerHeight,
35 isTombstoneView,
36}: {
37 posts: AppBskyFeedDefs.PostView[]
38 visible: boolean
39 collapsed: boolean
40 isLoading: boolean
41 showLoadingState: boolean
42 isFetchingNextPage: boolean
43 error: unknown
44 onRetry: () => void
45 headerRef: RefObject<View | null>
46 onToggleCollapsed: () => void
47 spacerHeight: number | undefined
48 isTombstoneView: boolean
49}) {
50 const {t: l} = useLingui()
51 const t = useTheme()
52 const queryClient = useQueryClient()
53 const hasSection = visible
54 const onBeforePress = useCallback(
55 (post: AppBskyFeedDefs.PostView) => {
56 unstableCacheProfileView(queryClient, post.author)
57 setUnstablePostSource(buildPostSourceKey(post.uri, post.author.handle), {
58 post: {post},
59 })
60 },
61 [queryClient],
62 )
63
64 return (
65 <View>
66 {hasSection && (
67 <View style={[a.border_t, t.atoms.border_contrast_low]}>
68 <Button.Button
69 ref={headerRef}
70 label={
71 collapsed
72 ? l`Expand also liked posts`
73 : l`Collapse also liked posts`
74 }
75 onPress={onToggleCollapsed}
76 style={[a.w_full]}>
77 {({hovered, pressed}) => (
78 <View
79 style={[
80 a.w_full,
81 a.flex_row,
82 a.align_center,
83 a.justify_between,
84 a.gap_sm,
85 a.px_lg,
86 a.py_md,
87 (hovered || pressed) && t.atoms.bg_contrast_25,
88 ]}>
89 <View style={[a.flex_1, a.gap_2xs]}>
90 <Text style={[a.text_xl, a.font_bold]}>
91 <Trans>Also liked</Trans>
92 </Text>
93 <Text style={[a.text_sm, t.atoms.text_contrast_medium]}>
94 <Trans>Posts liked by people who liked this post</Trans>
95 </Text>
96 </View>
97 {collapsed ? (
98 <ChevronDownIcon
99 size="sm"
100 style={t.atoms.text_contrast_medium}
101 />
102 ) : (
103 <ChevronUpIcon
104 size="sm"
105 style={t.atoms.text_contrast_medium}
106 />
107 )}
108 </View>
109 )}
110 </Button.Button>
111
112 {!collapsed && (
113 <>
114 {posts.map((post, index) => (
115 <Post
116 key={post.uri}
117 post={post}
118 hideTopBorder={index === 0}
119 onBeforePress={() => onBeforePress(post)}
120 />
121 ))}
122
123 {showLoadingState && posts.length === 0 && (
124 <>
125 <PostLoadingPlaceholder
126 style={[a.border_t, t.atoms.border_contrast_low]}
127 />
128 <PostLoadingPlaceholder
129 style={[a.border_t, t.atoms.border_contrast_low]}
130 />
131 </>
132 )}
133
134 {isFetchingNextPage && (
135 <PostLoadingPlaceholder
136 style={[a.border_t, t.atoms.border_contrast_low]}
137 />
138 )}
139
140 {Boolean(error) && !showLoadingState && !isFetchingNextPage && (
141 <View style={[a.px_lg, a.pb_xl, a.gap_md]}>
142 <Text style={[a.text_sm, t.atoms.text_contrast_medium]}>
143 {cleanError(error)}
144 </Text>
145 <View style={[a.flex_row]}>
146 <Button.Button
147 label={l`Retry loading also liked posts`}
148 onPress={onRetry}
149 variant="solid"
150 color="secondary_inverted"
151 size="small">
152 <Button.ButtonText>
153 <Trans>Retry</Trans>
154 </Button.ButtonText>
155 </Button.Button>
156 </View>
157 </View>
158 )}
159 </>
160 )}
161 </View>
162 )}
163
164 <View
165 style={[
166 a.w_full,
167 !hasSection &&
168 !isTombstoneView && [a.border_t, t.atoms.border_contrast_low],
169 {height: spacerHeight ?? 180},
170 ]}
171 />
172 </View>
173 )
174}