Bluesky app fork with some witchin' additions 馃挮 witchsky.app
bluesky fork client
119
fork

Configure Feed

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

at a876aae44ea07494ebea9727350aa060b81f317b 125 lines 3.3 kB view raw
1import {useCallback, useMemo, useState} from 'react' 2import {type AppBskyActorDefs as ActorDefs} from '@atproto/api' 3import {msg} from '@lingui/core/macro' 4import {useLingui} from '@lingui/react' 5 6import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' 7import {cleanError} from '#/lib/strings/errors' 8import {logger} from '#/logger' 9import {usePostRepostedByQuery} from '#/state/queries/post-reposted-by' 10import {useResolveUriQuery} from '#/state/queries/resolve-uri' 11import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard' 12import {List} from '#/view/com/util/List' 13import {ListFooter, ListMaybePlaceholder} from '#/components/Lists' 14 15function renderItem({ 16 item, 17 index, 18}: { 19 item: ActorDefs.ProfileView 20 index: number 21}) { 22 return ( 23 <ProfileCardWithFollowBtn 24 key={item.did} 25 profile={item} 26 noBorder={index === 0} 27 /> 28 ) 29} 30 31function keyExtractor(item: ActorDefs.ProfileViewBasic) { 32 return item.did 33} 34 35export function PostRepostedBy({uri}: {uri: string}) { 36 const {_} = useLingui() 37 const initialNumToRender = useInitialNumToRender() 38 39 const [isPTRing, setIsPTRing] = useState(false) 40 41 const { 42 data: resolvedUri, 43 error: resolveError, 44 isLoading: isLoadingUri, 45 } = useResolveUriQuery(uri) 46 const { 47 data, 48 isLoading: isLoadingRepostedBy, 49 isFetchingNextPage, 50 hasNextPage, 51 fetchNextPage, 52 error, 53 refetch, 54 } = usePostRepostedByQuery(resolvedUri?.uri) 55 56 const isError = Boolean(resolveError || error) 57 58 const repostedBy = useMemo(() => { 59 if (data?.pages) { 60 return data.pages.flatMap(page => page.repostedBy) 61 } 62 return [] 63 }, [data]) 64 65 const onRefresh = useCallback(async () => { 66 setIsPTRing(true) 67 try { 68 await refetch() 69 } catch (err) { 70 logger.error('Failed to refresh reposts', {message: err}) 71 } 72 setIsPTRing(false) 73 }, [refetch, setIsPTRing]) 74 75 const onEndReached = useCallback(async () => { 76 if (isFetchingNextPage || !hasNextPage || isError) return 77 try { 78 await fetchNextPage() 79 } catch (err) { 80 logger.error('Failed to load more reposts', {message: err}) 81 } 82 }, [isFetchingNextPage, hasNextPage, isError, fetchNextPage]) 83 84 if (repostedBy.length < 1) { 85 return ( 86 <ListMaybePlaceholder 87 isLoading={isLoadingUri || isLoadingRepostedBy} 88 isError={isError} 89 emptyType="results" 90 emptyTitle={_(msg`No reposts yet`)} 91 emptyMessage={_( 92 msg`Nobody has reposted this yet. Maybe you should be the first!`, 93 )} 94 errorMessage={cleanError(resolveError || error)} 95 sideBorders={false} 96 /> 97 ) 98 } 99 100 // loaded 101 // = 102 return ( 103 <List 104 data={repostedBy} 105 renderItem={renderItem} 106 keyExtractor={keyExtractor} 107 refreshing={isPTRing} 108 onRefresh={onRefresh} 109 onEndReached={onEndReached} 110 onEndReachedThreshold={4} 111 ListFooterComponent={ 112 <ListFooter 113 isFetchingNextPage={isFetchingNextPage} 114 error={cleanError(error)} 115 onRetry={fetchNextPage} 116 /> 117 } 118 // @ts-ignore our .web version only -prf 119 desktopFixedHeight 120 initialNumToRender={initialNumToRender} 121 windowSize={11} 122 sideBorders={false} 123 /> 124 ) 125}