Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

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

at ece6dc251cdb7eaf260819a4005b3a3e3e74ac8b 361 lines 9.1 kB view raw
1import {useMemo} from 'react' 2import { 3 type DimensionValue, 4 type StyleProp, 5 StyleSheet, 6 View, 7 type ViewStyle, 8} from 'react-native' 9 10import {s} from '#/lib/styles' 11import {atoms as a, useTheme} from '#/alf' 12import {Bubble_Stroke2_Corner2_Rounded as Bubble} from '#/components/icons/Bubble' 13import { 14 Heart2_Filled_Stroke2_Corner0_Rounded as HeartIconFilled, 15 Heart2_Stroke2_Corner0_Rounded as HeartIconOutline, 16} from '#/components/icons/Heart2' 17import {Repost_Stroke2_Corner2_Rounded as Repost} from '#/components/icons/Repost' 18 19export function LoadingPlaceholder({ 20 width, 21 height, 22 style, 23}: { 24 width: DimensionValue 25 height: DimensionValue | undefined 26 style?: StyleProp<ViewStyle> 27}) { 28 const t = useTheme() 29 return ( 30 <View 31 style={[ 32 styles.loadingPlaceholder, 33 { 34 width, 35 height, 36 backgroundColor: t.palette.contrast_50, 37 }, 38 style, 39 ]} 40 /> 41 ) 42} 43 44export function PostLoadingPlaceholder({ 45 style, 46}: { 47 style?: StyleProp<ViewStyle> 48}) { 49 const t = useTheme() 50 return ( 51 <View style={[styles.post, style]}> 52 <LoadingPlaceholder 53 width={42} 54 height={42} 55 style={[ 56 styles.avatar, 57 { 58 position: 'relative', 59 top: -6, 60 }, 61 ]} 62 /> 63 <View style={[s.flex1]}> 64 <LoadingPlaceholder width={100} height={6} style={{marginBottom: 10}} /> 65 <LoadingPlaceholder width="95%" height={6} style={{marginBottom: 8}} /> 66 <LoadingPlaceholder width="95%" height={6} style={{marginBottom: 8}} /> 67 <LoadingPlaceholder width="80%" height={6} style={{marginBottom: 11}} /> 68 <View style={styles.postCtrls}> 69 <View style={[styles.postCtrl, {marginLeft: -6}]}> 70 <View style={styles.postBtn}> 71 <Bubble 72 style={[ 73 { 74 color: t.palette.contrast_500, 75 }, 76 {pointerEvents: 'none'}, 77 ]} 78 width={18} 79 /> 80 </View> 81 </View> 82 <View style={styles.postCtrl}> 83 <View style={styles.postBtn}> 84 <Repost 85 style={[ 86 { 87 color: t.palette.contrast_500, 88 }, 89 {pointerEvents: 'none'}, 90 ]} 91 width={18} 92 /> 93 </View> 94 </View> 95 <View style={styles.postCtrl}> 96 <View style={styles.postBtn}> 97 <HeartIconOutline 98 style={[ 99 { 100 color: t.palette.contrast_500, 101 }, 102 {pointerEvents: 'none'}, 103 ]} 104 width={18} 105 /> 106 </View> 107 </View> 108 <View style={styles.postCtrl}> 109 <View style={[styles.postBtn, {minHeight: 30}]} /> 110 </View> 111 </View> 112 </View> 113 </View> 114 ) 115} 116 117export function PostFeedLoadingPlaceholder() { 118 return ( 119 <View> 120 <PostLoadingPlaceholder /> 121 <PostLoadingPlaceholder /> 122 <PostLoadingPlaceholder /> 123 <PostLoadingPlaceholder /> 124 <PostLoadingPlaceholder /> 125 <PostLoadingPlaceholder /> 126 <PostLoadingPlaceholder /> 127 <PostLoadingPlaceholder /> 128 </View> 129 ) 130} 131 132export function NotificationLoadingPlaceholder({ 133 style, 134}: { 135 style?: StyleProp<ViewStyle> 136}) { 137 const t = useTheme() 138 return ( 139 <View style={[styles.notification, style]}> 140 <View style={[{width: 60}, a.align_end, a.pr_sm, a.pt_2xs]}> 141 <HeartIconFilled size="xl" style={{color: t.palette.contrast_50}} /> 142 </View> 143 <View style={{flex: 1}}> 144 <View style={[a.flex_row, s.mb10]}> 145 <LoadingPlaceholder 146 width={35} 147 height={35} 148 style={styles.smallAvatar} 149 /> 150 </View> 151 <LoadingPlaceholder width="90%" height={6} style={[s.mb5]} /> 152 <LoadingPlaceholder width="70%" height={6} style={[s.mb5]} /> 153 </View> 154 </View> 155 ) 156} 157 158export function NotificationFeedLoadingPlaceholder() { 159 return ( 160 <> 161 <NotificationLoadingPlaceholder /> 162 <NotificationLoadingPlaceholder /> 163 <NotificationLoadingPlaceholder /> 164 <NotificationLoadingPlaceholder /> 165 <NotificationLoadingPlaceholder /> 166 <NotificationLoadingPlaceholder /> 167 <NotificationLoadingPlaceholder /> 168 <NotificationLoadingPlaceholder /> 169 <NotificationLoadingPlaceholder /> 170 <NotificationLoadingPlaceholder /> 171 <NotificationLoadingPlaceholder /> 172 </> 173 ) 174} 175 176export function ProfileCardLoadingPlaceholder({ 177 style, 178}: { 179 style?: StyleProp<ViewStyle> 180}) { 181 return ( 182 <View style={[styles.profileCard, style]}> 183 <LoadingPlaceholder 184 width={40} 185 height={40} 186 style={styles.profileCardAvi} 187 /> 188 <View> 189 <LoadingPlaceholder width={140} height={8} style={[s.mb5]} /> 190 <LoadingPlaceholder width={120} height={8} style={[s.mb10]} /> 191 <LoadingPlaceholder width={220} height={8} style={[s.mb5]} /> 192 </View> 193 </View> 194 ) 195} 196 197export function ProfileCardFeedLoadingPlaceholder() { 198 return ( 199 <> 200 <ProfileCardLoadingPlaceholder /> 201 <ProfileCardLoadingPlaceholder /> 202 <ProfileCardLoadingPlaceholder /> 203 <ProfileCardLoadingPlaceholder /> 204 <ProfileCardLoadingPlaceholder /> 205 <ProfileCardLoadingPlaceholder /> 206 <ProfileCardLoadingPlaceholder /> 207 <ProfileCardLoadingPlaceholder /> 208 <ProfileCardLoadingPlaceholder /> 209 <ProfileCardLoadingPlaceholder /> 210 <ProfileCardLoadingPlaceholder /> 211 </> 212 ) 213} 214 215export function FeedLoadingPlaceholder({ 216 style, 217 showLowerPlaceholder = true, 218 showTopBorder = true, 219}: { 220 style?: StyleProp<ViewStyle> 221 showTopBorder?: boolean 222 showLowerPlaceholder?: boolean 223}) { 224 const t = useTheme() 225 return ( 226 <View 227 style={[ 228 { 229 padding: 16, 230 borderTopWidth: showTopBorder ? StyleSheet.hairlineWidth : 0, 231 }, 232 t.atoms.border_contrast_low, 233 style, 234 ]}> 235 <View style={[{flexDirection: 'row'}]}> 236 <LoadingPlaceholder 237 width={36} 238 height={36} 239 style={[styles.avatar, {borderRadius: 8}]} 240 /> 241 <View style={[s.flex1]}> 242 <LoadingPlaceholder width={100} height={8} style={[s.mt5, s.mb10]} /> 243 <LoadingPlaceholder width={120} height={8} /> 244 </View> 245 </View> 246 {showLowerPlaceholder && ( 247 <View style={{marginTop: 12}}> 248 <LoadingPlaceholder width={120} height={8} /> 249 </View> 250 )} 251 </View> 252 ) 253} 254 255export function FeedFeedLoadingPlaceholder() { 256 return ( 257 <> 258 <FeedLoadingPlaceholder /> 259 <FeedLoadingPlaceholder /> 260 <FeedLoadingPlaceholder /> 261 <FeedLoadingPlaceholder /> 262 <FeedLoadingPlaceholder /> 263 <FeedLoadingPlaceholder /> 264 <FeedLoadingPlaceholder /> 265 <FeedLoadingPlaceholder /> 266 <FeedLoadingPlaceholder /> 267 <FeedLoadingPlaceholder /> 268 <FeedLoadingPlaceholder /> 269 </> 270 ) 271} 272 273export function ChatListItemLoadingPlaceholder({ 274 style, 275}: { 276 style?: StyleProp<ViewStyle> 277}) { 278 const t = useTheme() 279 const random = useMemo(() => Math.random(), []) 280 return ( 281 <View style={[a.flex_row, a.gap_md, a.px_lg, a.mt_lg, t.atoms.bg, style]}> 282 <LoadingPlaceholder width={52} height={52} style={a.rounded_full} /> 283 <View> 284 <LoadingPlaceholder width={140} height={12} style={a.mt_xs} /> 285 <LoadingPlaceholder width={120} height={8} style={a.mt_sm} /> 286 <LoadingPlaceholder 287 width={80 + random * 100} 288 height={8} 289 style={a.mt_sm} 290 /> 291 </View> 292 </View> 293 ) 294} 295 296export function ChatListLoadingPlaceholder() { 297 return ( 298 <> 299 <ChatListItemLoadingPlaceholder /> 300 <ChatListItemLoadingPlaceholder /> 301 <ChatListItemLoadingPlaceholder /> 302 <ChatListItemLoadingPlaceholder /> 303 <ChatListItemLoadingPlaceholder /> 304 <ChatListItemLoadingPlaceholder /> 305 <ChatListItemLoadingPlaceholder /> 306 <ChatListItemLoadingPlaceholder /> 307 <ChatListItemLoadingPlaceholder /> 308 <ChatListItemLoadingPlaceholder /> 309 <ChatListItemLoadingPlaceholder /> 310 </> 311 ) 312} 313 314const styles = StyleSheet.create({ 315 loadingPlaceholder: { 316 borderRadius: 6, 317 }, 318 post: { 319 flexDirection: 'row', 320 alignItems: 'flex-start', 321 paddingHorizontal: 10, 322 paddingTop: 20, 323 paddingBottom: 5, 324 paddingRight: 15, 325 }, 326 postCtrls: { 327 opacity: 0.5, 328 flexDirection: 'row', 329 justifyContent: 'space-between', 330 }, 331 postCtrl: { 332 flex: 1, 333 }, 334 postBtn: { 335 flex: 1, 336 flexDirection: 'row', 337 alignItems: 'center', 338 padding: 5, 339 }, 340 avatar: { 341 borderRadius: 999, 342 marginRight: 12, 343 }, 344 notification: { 345 flexDirection: 'row', 346 padding: 10, 347 }, 348 profileCard: { 349 flexDirection: 'row', 350 padding: 10, 351 margin: 1, 352 }, 353 profileCardAvi: { 354 borderRadius: 999, 355 marginRight: 10, 356 }, 357 smallAvatar: { 358 borderRadius: 999, 359 marginRight: 10, 360 }, 361})