this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at e28f6d2f370b4e882ed6f23d08ca0f8d94dbac5f 232 lines 7.1 kB view raw
1import {useCallback, useMemo, useState} from 'react' 2import {LayoutAnimation, Pressable, View} from 'react-native' 3import {Image} from 'expo-image' 4import { 5 AppBskyEmbedImages, 6 AppBskyEmbedRecord, 7 AppBskyEmbedRecordWithMedia, 8 AppBskyFeedPost, 9} from '@atproto/api' 10import {msg} from '@lingui/core/macro' 11import {useLingui} from '@lingui/react' 12 13import {sanitizeDisplayName} from '#/lib/strings/display-names' 14import {sanitizeHandle} from '#/lib/strings/handles' 15import {type ComposerOptsPostRef} from '#/state/shell/composer' 16import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar' 17import {atoms as a, useTheme, web} from '#/alf' 18import {QuoteEmbed} from '#/components/Post/Embed' 19import {ProfileBadges} from '#/components/ProfileBadges' 20import {Text} from '#/components/Typography' 21import {parseEmbed} from '#/types/bsky/post' 22 23export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) { 24 const t = useTheme() 25 const {_} = useLingui() 26 const {embed} = replyTo 27 28 const [showFull, setShowFull] = useState(false) 29 30 const onPress = useCallback(() => { 31 setShowFull(prev => !prev) 32 LayoutAnimation.configureNext({ 33 duration: 350, 34 update: {type: 'spring', springDamping: 0.7}, 35 }) 36 }, []) 37 38 const quoteEmbed = useMemo(() => { 39 if ( 40 AppBskyEmbedRecord.isView(embed) && 41 AppBskyEmbedRecord.isViewRecord(embed.record) && 42 AppBskyFeedPost.isRecord(embed.record.value) 43 ) { 44 return embed 45 } else if ( 46 AppBskyEmbedRecordWithMedia.isView(embed) && 47 AppBskyEmbedRecord.isViewRecord(embed.record.record) && 48 AppBskyFeedPost.isRecord(embed.record.record.value) 49 ) { 50 return embed.record 51 } 52 return null 53 }, [embed]) 54 const parsedQuoteEmbed = quoteEmbed 55 ? parseEmbed({ 56 $type: 'app.bsky.embed.record#view', 57 ...quoteEmbed, 58 }) 59 : null 60 61 const images = useMemo(() => { 62 if (AppBskyEmbedImages.isView(embed)) { 63 return embed.images 64 } else if ( 65 AppBskyEmbedRecordWithMedia.isView(embed) && 66 AppBskyEmbedImages.isView(embed.media) 67 ) { 68 return embed.media.images 69 } 70 }, [embed]) 71 72 return ( 73 <Pressable 74 style={[ 75 a.flex_row, 76 a.align_start, 77 a.pt_xs, 78 a.pb_lg, 79 a.mb_md, 80 a.mx_lg, 81 a.border_b, 82 t.atoms.border_contrast_medium, 83 web(a.user_select_text), 84 ]} 85 onPress={onPress} 86 accessibilityRole="button" 87 accessibilityLabel={_( 88 msg`Expand or collapse the full post you are replying to`, 89 )} 90 accessibilityHint=""> 91 <PreviewableUserAvatar 92 size={42} 93 profile={replyTo.author} 94 moderation={replyTo.moderation?.ui('avatar')} 95 type={replyTo.author.associated?.labeler ? 'labeler' : 'user'} 96 disableNavigation={true} 97 /> 98 <View style={[a.flex_1, a.pl_md, a.pr_sm, a.gap_2xs]}> 99 <View style={[a.flex_row, a.align_center, a.pr_xs]}> 100 <Text 101 style={[a.font_semi_bold, a.text_md, a.leading_snug, a.flex_shrink]} 102 numberOfLines={1} 103 emoji> 104 {sanitizeDisplayName( 105 replyTo.author.displayName || 106 sanitizeHandle(replyTo.author.handle), 107 )} 108 </Text> 109 <ProfileBadges profile={replyTo.author} size="sm" style={[a.pl_xs]} /> 110 </View> 111 <View style={[a.flex_row, a.gap_md]}> 112 <View style={[a.flex_1, a.flex_grow]}> 113 <Text 114 style={[a.text_md, a.leading_snug, t.atoms.text_contrast_high]} 115 numberOfLines={!showFull ? 6 : undefined} 116 emoji> 117 {replyTo.text} 118 </Text> 119 </View> 120 {images && !replyTo.moderation?.ui('contentMedia').blur && ( 121 <ComposerReplyToImages images={images} showFull={showFull} /> 122 )} 123 </View> 124 {showFull && parsedQuoteEmbed && parsedQuoteEmbed.type === 'post' && ( 125 <QuoteEmbed embed={parsedQuoteEmbed} linkDisabled /> 126 )} 127 </View> 128 </Pressable> 129 ) 130} 131 132function ComposerReplyToImages({ 133 images, 134}: { 135 images: AppBskyEmbedImages.ViewImage[] 136 showFull: boolean 137}) { 138 return ( 139 <View 140 style={[ 141 a.rounded_xs, 142 a.overflow_hidden, 143 a.mt_2xs, 144 a.mx_xs, 145 { 146 height: 64, 147 width: 64, 148 }, 149 ]}> 150 {(images.length === 1 && ( 151 <Image 152 source={{uri: images[0].thumb}} 153 style={[a.flex_1]} 154 cachePolicy="memory-disk" 155 accessibilityIgnoresInvertColors 156 /> 157 )) || 158 (images.length === 2 && ( 159 <View style={[a.flex_1, a.flex_row, a.gap_2xs]}> 160 <Image 161 source={{uri: images[0].thumb}} 162 style={[a.flex_1]} 163 cachePolicy="memory-disk" 164 accessibilityIgnoresInvertColors 165 /> 166 <Image 167 source={{uri: images[1].thumb}} 168 style={[a.flex_1]} 169 cachePolicy="memory-disk" 170 accessibilityIgnoresInvertColors 171 /> 172 </View> 173 )) || 174 (images.length === 3 && ( 175 <View style={[a.flex_1, a.flex_row, a.gap_2xs]}> 176 <Image 177 source={{uri: images[0].thumb}} 178 style={[a.flex_1]} 179 cachePolicy="memory-disk" 180 accessibilityIgnoresInvertColors 181 /> 182 <View style={[a.flex_1, a.gap_2xs]}> 183 <Image 184 source={{uri: images[1].thumb}} 185 style={[a.flex_1]} 186 cachePolicy="memory-disk" 187 accessibilityIgnoresInvertColors 188 /> 189 <Image 190 source={{uri: images[2].thumb}} 191 style={[a.flex_1]} 192 cachePolicy="memory-disk" 193 accessibilityIgnoresInvertColors 194 /> 195 </View> 196 </View> 197 )) || 198 (images.length === 4 && ( 199 <View style={[a.flex_1, a.gap_2xs]}> 200 <View style={[a.flex_1, a.flex_row, a.gap_2xs]}> 201 <Image 202 source={{uri: images[0].thumb}} 203 style={[a.flex_1]} 204 cachePolicy="memory-disk" 205 accessibilityIgnoresInvertColors 206 /> 207 <Image 208 source={{uri: images[1].thumb}} 209 style={[a.flex_1]} 210 cachePolicy="memory-disk" 211 accessibilityIgnoresInvertColors 212 /> 213 </View> 214 <View style={[a.flex_1, a.flex_row, a.gap_2xs]}> 215 <Image 216 source={{uri: images[2].thumb}} 217 style={[a.flex_1]} 218 cachePolicy="memory-disk" 219 accessibilityIgnoresInvertColors 220 /> 221 <Image 222 source={{uri: images[3].thumb}} 223 style={[a.flex_1]} 224 cachePolicy="memory-disk" 225 accessibilityIgnoresInvertColors 226 /> 227 </View> 228 </View> 229 ))} 230 </View> 231 ) 232}