mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import {ScrollView, View} from 'react-native'
2import {moderateProfile, type ModerationOpts} from '@atproto/api'
3import {msg, Trans} from '@lingui/macro'
4import {useLingui} from '@lingui/react'
5import {useNavigation} from '@react-navigation/native'
6
7import {type NavigationProp} from '#/lib/routes/types'
8import {sanitizeDisplayName} from '#/lib/strings/display-names'
9import {sanitizeHandle} from '#/lib/strings/handles'
10import {logger} from '#/logger'
11import {useModerationOpts} from '#/state/preferences/moderation-opts'
12import {useListConvosQuery} from '#/state/queries/messages/list-conversations'
13import {useSession} from '#/state/session'
14import {UserAvatar} from '#/view/com/util/UserAvatar'
15import {atoms as a, tokens, useTheme} from '#/alf'
16import {Button} from '#/components/Button'
17import {useDialogContext} from '#/components/Dialog'
18import {Text} from '#/components/Typography'
19import {useSimpleVerificationState} from '#/components/verification'
20import {VerificationCheck} from '#/components/verification/VerificationCheck'
21import type * as bsky from '#/types/bsky'
22
23export function RecentChats({postUri}: {postUri: string}) {
24 const control = useDialogContext()
25 const {_} = useLingui()
26 const {currentAccount} = useSession()
27 const {data} = useListConvosQuery({status: 'accepted'})
28 const convos = data?.pages[0]?.convos?.slice(0, 10)
29 const moderationOpts = useModerationOpts()
30 const navigation = useNavigation<NavigationProp>()
31
32 const onSelectChat = (convoId: string) => {
33 control.close(() => {
34 logger.metric('share:press:recentDm', {}, {statsig: true})
35 navigation.navigate('MessagesConversation', {
36 conversation: convoId,
37 embed: postUri,
38 })
39 })
40 }
41
42 if (!moderationOpts) return null
43
44 return (
45 <View
46 style={[a.relative, a.flex_1, {marginHorizontal: tokens.space.md * -1}]}>
47 <ScrollView
48 horizontal
49 style={[a.flex_1, a.pt_2xs, {minHeight: 98}]}
50 contentContainerStyle={[a.gap_sm, a.px_md]}
51 showsHorizontalScrollIndicator={false}
52 fadingEdgeLength={64}
53 nestedScrollEnabled>
54 {convos && convos.length > 0 ? (
55 convos.map(convo => {
56 const otherMember = convo.members.find(
57 member => member.did !== currentAccount?.did,
58 )
59
60 if (!otherMember || otherMember.handle === 'missing.invalid')
61 return null
62
63 return (
64 <RecentChatItem
65 key={convo.id}
66 profile={otherMember}
67 onPress={() => onSelectChat(convo.id)}
68 moderationOpts={moderationOpts}
69 />
70 )
71 })
72 ) : (
73 <>
74 <ConvoSkeleton />
75 <ConvoSkeleton />
76 <ConvoSkeleton />
77 <ConvoSkeleton />
78 <ConvoSkeleton />
79 </>
80 )}
81 </ScrollView>
82 {convos && convos.length === 0 && <NoConvos />}
83 </View>
84 )
85}
86
87const WIDTH = 80
88
89function RecentChatItem({
90 profile,
91 onPress,
92 moderationOpts,
93}: {
94 profile: bsky.profile.AnyProfileView
95 onPress: () => void
96 moderationOpts: ModerationOpts
97}) {
98 const {_} = useLingui()
99 const t = useTheme()
100
101 const moderation = moderateProfile(profile, moderationOpts)
102 const name = sanitizeDisplayName(
103 profile.displayName || sanitizeHandle(profile.handle),
104 moderation.ui('displayName'),
105 )
106 const verification = useSimpleVerificationState({profile})
107
108 return (
109 <Button
110 onPress={onPress}
111 label={_(msg`Send post to ${name}`)}
112 style={[
113 a.flex_col,
114 {width: WIDTH},
115 a.gap_sm,
116 a.justify_start,
117 a.align_center,
118 ]}>
119 <UserAvatar
120 avatar={profile.avatar}
121 size={WIDTH - 8}
122 type={profile.associated?.labeler ? 'labeler' : 'user'}
123 moderation={moderation.ui('avatar')}
124 />
125 <View style={[a.flex_row, a.align_center, a.justify_center, a.w_full]}>
126 <Text
127 emoji
128 style={[a.text_xs, a.leading_snug, t.atoms.text_contrast_medium]}
129 numberOfLines={1}>
130 {name}
131 </Text>
132 {verification.showBadge && (
133 <View style={[a.pl_2xs]}>
134 <VerificationCheck
135 width={10}
136 verifier={verification.role === 'verifier'}
137 />
138 </View>
139 )}
140 </View>
141 </Button>
142 )
143}
144
145function ConvoSkeleton() {
146 const t = useTheme()
147 return (
148 <View
149 style={[
150 a.flex_col,
151 {width: WIDTH, height: WIDTH + 15},
152 a.gap_xs,
153 a.justify_start,
154 a.align_center,
155 ]}>
156 <View
157 style={[
158 t.atoms.bg_contrast_50,
159 {width: WIDTH - 8, height: WIDTH - 8},
160 a.rounded_full,
161 ]}
162 />
163 <View
164 style={[
165 t.atoms.bg_contrast_50,
166 {width: WIDTH - 8, height: 10},
167 a.rounded_xs,
168 ]}
169 />
170 </View>
171 )
172}
173
174function NoConvos() {
175 const t = useTheme()
176
177 return (
178 <View
179 style={[
180 a.absolute,
181 a.inset_0,
182 a.justify_center,
183 a.align_center,
184 a.px_2xl,
185 ]}>
186 <View
187 style={[a.absolute, a.inset_0, t.atoms.bg_contrast_25, {opacity: 0.5}]}
188 />
189 <Text
190 style={[
191 a.text_sm,
192 t.atoms.text_contrast_high,
193 a.text_center,
194 a.font_bold,
195 ]}>
196 <Trans>Start a conversation, and it will appear here.</Trans>
197 </Text>
198 </View>
199 )
200}