mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
at samuel/exp-cli 375 lines 9.6 kB view raw
1import {useMemo} from 'react' 2import { 3 DimensionValue, 4 StyleProp, 5 StyleSheet, 6 View, 7 ViewStyle, 8} from 'react-native' 9 10import {usePalette} from '#/lib/hooks/usePalette' 11import {s} from '#/lib/styles' 12import {useTheme} from '#/lib/ThemeContext' 13import {atoms as a, useTheme as useTheme_NEW} 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 theme = useTheme() 31 return ( 32 <View 33 style={[ 34 styles.loadingPlaceholder, 35 { 36 width, 37 height, 38 backgroundColor: theme.palette.default.backgroundLight, 39 }, 40 style, 41 ]} 42 /> 43 ) 44} 45 46export function PostLoadingPlaceholder({ 47 style, 48}: { 49 style?: StyleProp<ViewStyle> 50}) { 51 const t = useTheme_NEW() 52 const pal = usePalette('default') 53 return ( 54 <View style={[styles.post, pal.view, style]}> 55 <LoadingPlaceholder 56 width={42} 57 height={42} 58 style={[ 59 styles.avatar, 60 { 61 position: 'relative', 62 top: -6, 63 }, 64 ]} 65 /> 66 <View style={[s.flex1]}> 67 <LoadingPlaceholder width={100} height={6} style={{marginBottom: 10}} /> 68 <LoadingPlaceholder width="95%" height={6} style={{marginBottom: 8}} /> 69 <LoadingPlaceholder width="95%" height={6} style={{marginBottom: 8}} /> 70 <LoadingPlaceholder width="80%" height={6} style={{marginBottom: 11}} /> 71 <View style={styles.postCtrls}> 72 <View style={[styles.postCtrl, {marginLeft: -6}]}> 73 <View style={styles.postBtn}> 74 <Bubble 75 style={[ 76 { 77 color: t.palette.contrast_500, 78 }, 79 {pointerEvents: 'none'}, 80 ]} 81 width={18} 82 /> 83 </View> 84 </View> 85 <View style={styles.postCtrl}> 86 <View style={styles.postBtn}> 87 <Repost 88 style={[ 89 { 90 color: t.palette.contrast_500, 91 }, 92 {pointerEvents: 'none'}, 93 ]} 94 width={18} 95 /> 96 </View> 97 </View> 98 <View style={styles.postCtrl}> 99 <View style={styles.postBtn}> 100 <HeartIconOutline 101 style={[ 102 { 103 color: t.palette.contrast_500, 104 }, 105 {pointerEvents: 'none'}, 106 ]} 107 width={18} 108 /> 109 </View> 110 </View> 111 <View style={styles.postCtrl}> 112 <View style={[styles.postBtn, {minHeight: 30}]} /> 113 </View> 114 </View> 115 </View> 116 </View> 117 ) 118} 119 120export function PostFeedLoadingPlaceholder() { 121 return ( 122 <View> 123 <PostLoadingPlaceholder /> 124 <PostLoadingPlaceholder /> 125 <PostLoadingPlaceholder /> 126 <PostLoadingPlaceholder /> 127 <PostLoadingPlaceholder /> 128 <PostLoadingPlaceholder /> 129 <PostLoadingPlaceholder /> 130 <PostLoadingPlaceholder /> 131 </View> 132 ) 133} 134 135export function NotificationLoadingPlaceholder({ 136 style, 137}: { 138 style?: StyleProp<ViewStyle> 139}) { 140 const pal = usePalette('default') 141 return ( 142 <View style={[styles.notification, pal.view, style]}> 143 <View style={[{width: 60}, a.align_end, a.pr_sm, a.pt_2xs]}> 144 <HeartIconFilled 145 size="xl" 146 style={{color: pal.colors.backgroundLight}} 147 /> 148 </View> 149 <View style={{flex: 1}}> 150 <View style={[a.flex_row, s.mb10]}> 151 <LoadingPlaceholder 152 width={35} 153 height={35} 154 style={styles.smallAvatar} 155 /> 156 </View> 157 <LoadingPlaceholder width="90%" height={6} style={[s.mb5]} /> 158 <LoadingPlaceholder width="70%" height={6} style={[s.mb5]} /> 159 </View> 160 </View> 161 ) 162} 163 164export function NotificationFeedLoadingPlaceholder() { 165 return ( 166 <> 167 <NotificationLoadingPlaceholder /> 168 <NotificationLoadingPlaceholder /> 169 <NotificationLoadingPlaceholder /> 170 <NotificationLoadingPlaceholder /> 171 <NotificationLoadingPlaceholder /> 172 <NotificationLoadingPlaceholder /> 173 <NotificationLoadingPlaceholder /> 174 <NotificationLoadingPlaceholder /> 175 <NotificationLoadingPlaceholder /> 176 <NotificationLoadingPlaceholder /> 177 <NotificationLoadingPlaceholder /> 178 </> 179 ) 180} 181 182export function ProfileCardLoadingPlaceholder({ 183 style, 184}: { 185 style?: StyleProp<ViewStyle> 186}) { 187 const pal = usePalette('default') 188 return ( 189 <View style={[styles.profileCard, pal.view, style]}> 190 <LoadingPlaceholder 191 width={40} 192 height={40} 193 style={styles.profileCardAvi} 194 /> 195 <View> 196 <LoadingPlaceholder width={140} height={8} style={[s.mb5]} /> 197 <LoadingPlaceholder width={120} height={8} style={[s.mb10]} /> 198 <LoadingPlaceholder width={220} height={8} style={[s.mb5]} /> 199 </View> 200 </View> 201 ) 202} 203 204export function ProfileCardFeedLoadingPlaceholder() { 205 return ( 206 <> 207 <ProfileCardLoadingPlaceholder /> 208 <ProfileCardLoadingPlaceholder /> 209 <ProfileCardLoadingPlaceholder /> 210 <ProfileCardLoadingPlaceholder /> 211 <ProfileCardLoadingPlaceholder /> 212 <ProfileCardLoadingPlaceholder /> 213 <ProfileCardLoadingPlaceholder /> 214 <ProfileCardLoadingPlaceholder /> 215 <ProfileCardLoadingPlaceholder /> 216 <ProfileCardLoadingPlaceholder /> 217 <ProfileCardLoadingPlaceholder /> 218 </> 219 ) 220} 221 222export function FeedLoadingPlaceholder({ 223 style, 224 showLowerPlaceholder = true, 225 showTopBorder = true, 226}: { 227 style?: StyleProp<ViewStyle> 228 showTopBorder?: boolean 229 showLowerPlaceholder?: boolean 230}) { 231 const pal = usePalette('default') 232 return ( 233 <View 234 style={[ 235 { 236 paddingHorizontal: 12, 237 paddingVertical: 18, 238 borderTopWidth: showTopBorder ? StyleSheet.hairlineWidth : 0, 239 }, 240 pal.border, 241 style, 242 ]}> 243 <View style={[pal.view, {flexDirection: 'row'}]}> 244 <LoadingPlaceholder 245 width={36} 246 height={36} 247 style={[styles.avatar, {borderRadius: 6}]} 248 /> 249 <View style={[s.flex1]}> 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={{paddingHorizontal: 5, marginTop: 10}}> 256 <LoadingPlaceholder 257 width={260} 258 height={8} 259 style={{marginVertical: 12}} 260 /> 261 <LoadingPlaceholder width={120} height={8} /> 262 </View> 263 )} 264 </View> 265 ) 266} 267 268export function FeedFeedLoadingPlaceholder() { 269 return ( 270 <> 271 <FeedLoadingPlaceholder /> 272 <FeedLoadingPlaceholder /> 273 <FeedLoadingPlaceholder /> 274 <FeedLoadingPlaceholder /> 275 <FeedLoadingPlaceholder /> 276 <FeedLoadingPlaceholder /> 277 <FeedLoadingPlaceholder /> 278 <FeedLoadingPlaceholder /> 279 <FeedLoadingPlaceholder /> 280 <FeedLoadingPlaceholder /> 281 <FeedLoadingPlaceholder /> 282 </> 283 ) 284} 285 286export function ChatListItemLoadingPlaceholder({ 287 style, 288}: { 289 style?: StyleProp<ViewStyle> 290}) { 291 const t = useTheme_NEW() 292 const random = useMemo(() => Math.random(), []) 293 return ( 294 <View style={[a.flex_row, a.gap_md, a.px_lg, a.mt_lg, t.atoms.bg, style]}> 295 <LoadingPlaceholder width={52} height={52} style={a.rounded_full} /> 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: 10, 356 marginLeft: 8, 357 }, 358 notification: { 359 flexDirection: 'row', 360 padding: 10, 361 }, 362 profileCard: { 363 flexDirection: 'row', 364 padding: 10, 365 margin: 1, 366 }, 367 profileCardAvi: { 368 borderRadius: 999, 369 marginRight: 10, 370 }, 371 smallAvatar: { 372 borderRadius: 999, 373 marginRight: 10, 374 }, 375})