Something went wrong. Try again.
hardened bluesky app fork with microcosm
Something went wrong. Try again.
6.1 kB · 234 lines
TSX
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235import {ScrollView, View} from 'react-native'import { type ChatBskyActorDefs, moderateProfile, type ModerationOpts,} from '@atproto/api'import {msg} from '@lingui/core/macro'import {useLingui} from '@lingui/react'import {Trans} from '@lingui/react/macro'import {useNavigation} from '@react-navigation/native'
import {isBlockedOrBlocking, isMuted} from '#/lib/moderation/blocked-and-muted'import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'import {type NavigationProp} from '#/lib/routes/types'import {useProfileShadow} from '#/state/cache/profile-shadow'import {useModerationOpts} from '#/state/preferences/moderation-opts'import {useListConvosQuery} from '#/state/queries/messages/list-conversations'import {useSession} from '#/state/session'import {UserAvatar} from '#/view/com/util/UserAvatar'import {atoms as a, tokens, useTheme} from '#/alf'import {AvatarBubbles} from '#/components/AvatarBubbles'import {Button} from '#/components/Button'import {useDialogContext} from '#/components/Dialog'import {type ConvoWithDetails, parseConvoView} from '#/components/dms/util'import {ProfileBadges} from '#/components/ProfileBadges'import {Text} from '#/components/Typography'export function RecentChats({ postUri, onBeforePress,}: { postUri: string onBeforePress?: () => void}) { const control = useDialogContext() const {currentAccount} = useSession() const {data} = useListConvosQuery({ status: 'accepted', lockStatus: 'unlocked', }) const convos = data?.pages[0]?.convos?.slice(0, 10) const moderationOpts = useModerationOpts() const navigation = useNavigation<NavigationProp>()
const onSelectChat = (convoId: string) => { onBeforePress?.() control.close(() => { navigation.navigate('MessagesConversation', { conversation: convoId, embed: postUri, }) }) }
if (!moderationOpts) return null
return ( <View style={[a.relative, a.flex_1, {marginHorizontal: tokens.space.md * -1}]}> <ScrollView horizontal style={[a.flex_1, a.pt_2xs, {minHeight: 98}]} contentContainerStyle={[a.gap_sm, a.px_md]} showsHorizontalScrollIndicator={false} nestedScrollEnabled> {convos && convos.length > 0 ? ( convos.map(c => { const convo = parseConvoView(c, currentAccount?.did)
if (!convo) return null
if ( !convo.primaryMember || convo.primaryMember.handle === 'missing.invalid' || convo.view.muted ) { return null }
return ( <RecentChatItem key={convo.view.id} convo={convo} primaryMember={convo.primaryMember} onPress={() => onSelectChat(convo.view.id)} moderationOpts={moderationOpts} /> ) }) ) : ( <> <ConvoSkeleton /> <ConvoSkeleton /> <ConvoSkeleton /> <ConvoSkeleton /> <ConvoSkeleton /> </> )} </ScrollView> {convos && convos.length === 0 && <NoConvos />} </View> )}
const WIDTH = 80
function RecentChatItem({ onPress, moderationOpts, convo, primaryMember,}: { onPress: () => void moderationOpts: ModerationOpts convo: ConvoWithDetails primaryMember: ChatBskyActorDefs.ProfileViewBasic}) { const {_} = useLingui() const t = useTheme()
const primaryProfile = useProfileShadow(primaryMember)
const moderation = moderateProfile(primaryProfile, moderationOpts) const name = convo.kind === 'group' ? convo.details.name : createSanitizedDisplayName( primaryProfile, true, moderation.ui('displayName'), )
if ( convo.kind === 'direct' && (isBlockedOrBlocking(primaryProfile) || isMuted(primaryProfile)) ) { return null }
return ( <Button onPress={onPress} label={_(msg`Send post to ${name}`)} style={[ a.flex_col, {width: WIDTH}, a.gap_sm, a.justify_start, a.align_center, ]}> {convo.kind === 'group' ? ( <AvatarBubbles profiles={convo.members} size={WIDTH - 8} /> ) : ( <UserAvatar avatar={primaryProfile.avatar} size={WIDTH - 8} type={primaryProfile.associated?.labeler ? 'labeler' : 'user'} moderation={moderation.ui('avatar')} /> )} <View style={[a.flex_row, a.align_center, a.justify_center, a.w_full]}> <Text emoji style={[a.text_xs, a.leading_snug, t.atoms.text_contrast_medium]} numberOfLines={1}> {name} </Text> {convo.kind === 'direct' && ( <ProfileBadges profile={primaryProfile} size="xs" style={[a.pl_2xs]} /> )} </View> </Button> )}
function ConvoSkeleton() { const t = useTheme() return ( <View style={[ a.flex_col, {width: WIDTH, height: WIDTH + 15}, a.gap_xs, a.justify_start, a.align_center, ]}> <View style={[ t.atoms.bg_contrast_50, {width: WIDTH - 8, height: WIDTH - 8}, a.rounded_full, ]} /> <View style={[ t.atoms.bg_contrast_50, {width: WIDTH - 8, height: 10}, a.rounded_xs, ]} /> </View> )}
function NoConvos() { const t = useTheme()
return ( <View style={[ a.absolute, a.inset_0, a.justify_center, a.align_center, a.px_2xl, ]}> <View style={[a.absolute, a.inset_0, t.atoms.bg_contrast_25, {opacity: 0.5}]} /> <Text style={[ a.text_sm, t.atoms.text_contrast_high, a.text_center, a.font_semi_bold, ]}> <Trans>Start a conversation, and it will appear here.</Trans> </Text> </View> )}