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

Configure Feed

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

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