Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

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

Fix missing header on Likes/Reposted By, add missing perf optimizations (#4867)

* fix liked by list

* fix lists

* tweaks to style

* change string

authored by

Hailey and committed by
GitHub
f056cb64 c78e9e31

+127 -125
+51 -52
src/view/com/post-thread/PostLikedBy.tsx
··· 1 1 import React, {useCallback, useMemo, useState} from 'react' 2 - import {ActivityIndicator, StyleSheet, View} from 'react-native' 3 2 import {AppBskyFeedGetLikes as GetLikes} from '@atproto/api' 4 - import {CenteredView} from '../util/Views' 5 - import {List} from '../util/List' 6 - import {ErrorMessage} from '../util/error/ErrorMessage' 7 - import {ProfileCardWithFollowBtn} from '../profile/ProfileCard' 3 + import {msg} from '@lingui/macro' 4 + import {useLingui} from '@lingui/react' 5 + 6 + import {cleanError} from '#/lib/strings/errors' 8 7 import {logger} from '#/logger' 9 - import {LoadingScreen} from '../util/LoadingScreen' 10 - import {useResolveUriQuery} from '#/state/queries/resolve-uri' 11 8 import {useLikedByQuery} from '#/state/queries/post-liked-by' 12 - import {cleanError} from '#/lib/strings/errors' 9 + import {useResolveUriQuery} from '#/state/queries/resolve-uri' 10 + import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender' 11 + import { 12 + ListFooter, 13 + ListHeaderDesktop, 14 + ListMaybePlaceholder, 15 + } from '#/components/Lists' 16 + import {ProfileCardWithFollowBtn} from '../profile/ProfileCard' 17 + import {List} from '../util/List' 18 + 19 + function renderItem({item}: {item: GetLikes.Like}) { 20 + return <ProfileCardWithFollowBtn key={item.actor.did} profile={item.actor} /> 21 + } 22 + 23 + function keyExtractor(item: GetLikes.Like) { 24 + return item.actor.did 25 + } 13 26 14 27 export function PostLikedBy({uri}: {uri: string}) { 28 + const {_} = useLingui() 29 + const initialNumToRender = useInitialNumToRender() 30 + 15 31 const [isPTRing, setIsPTRing] = useState(false) 32 + 16 33 const { 17 34 data: resolvedUri, 18 35 error: resolveError, 19 - isFetching: isFetchingResolvedUri, 36 + isLoading: isLoadingUri, 20 37 } = useResolveUriQuery(uri) 21 38 const { 22 39 data, 23 - isFetching, 24 - isFetched, 40 + isLoading: isLoadingLikes, 25 41 isFetchingNextPage, 26 42 hasNextPage, 27 43 fetchNextPage, 28 - isError, 29 44 error, 30 45 refetch, 31 46 } = useLikedByQuery(resolvedUri?.uri) 47 + 48 + const isError = Boolean(resolveError || error) 49 + 32 50 const likes = useMemo(() => { 33 51 if (data?.pages) { 34 52 return data.pages.flatMap(page => page.likes) 35 53 } 54 + return [] 36 55 }, [data]) 37 56 38 57 const onRefresh = useCallback(async () => { ··· 46 65 }, [refetch, setIsPTRing]) 47 66 48 67 const onEndReached = useCallback(async () => { 49 - if (isFetching || !hasNextPage || isError) return 68 + if (isFetchingNextPage || !hasNextPage || isError) return 50 69 try { 51 70 await fetchNextPage() 52 71 } catch (err) { 53 72 logger.error('Failed to load more likes', {message: err}) 54 73 } 55 - }, [isFetching, hasNextPage, isError, fetchNextPage]) 74 + }, [isFetchingNextPage, hasNextPage, isError, fetchNextPage]) 56 75 57 - const renderItem = useCallback(({item}: {item: GetLikes.Like}) => { 76 + if (likes.length < 1) { 58 77 return ( 59 - <ProfileCardWithFollowBtn key={item.actor.did} profile={item.actor} /> 60 - ) 61 - }, []) 62 - 63 - if (isFetchingResolvedUri || !isFetched) { 64 - return <LoadingScreen /> 65 - } 66 - 67 - // error 68 - // = 69 - if (resolveError || isError) { 70 - return ( 71 - <CenteredView> 72 - <ErrorMessage 73 - message={cleanError(resolveError || error)} 74 - onPressTryAgain={onRefresh} 75 - /> 76 - </CenteredView> 78 + <ListMaybePlaceholder 79 + isLoading={isLoadingUri || isLoadingLikes} 80 + isError={isError} 81 + /> 77 82 ) 78 83 } 79 84 80 - // loaded 81 - // = 82 85 return ( 83 86 <List 84 87 data={likes} 85 - keyExtractor={item => item.actor.did} 88 + renderItem={renderItem} 89 + keyExtractor={keyExtractor} 86 90 refreshing={isPTRing} 87 91 onRefresh={onRefresh} 88 92 onEndReached={onEndReached} 89 - renderItem={renderItem} 90 - initialNumToRender={15} 91 - // FIXME(dan) 92 - // eslint-disable-next-line react/no-unstable-nested-components 93 - ListFooterComponent={() => ( 94 - <View style={styles.footer}> 95 - {(isFetching || isFetchingNextPage) && <ActivityIndicator />} 96 - </View> 97 - )} 93 + onEndReachedThreshold={4} 94 + ListHeaderComponent={<ListHeaderDesktop title={_(msg`Liked By`)} />} 95 + ListFooterComponent={ 96 + <ListFooter 97 + isFetchingNextPage={isFetchingNextPage} 98 + error={cleanError(error)} 99 + onRetry={fetchNextPage} 100 + /> 101 + } 98 102 // @ts-ignore our .web version only -prf 99 103 desktopFixedHeight 104 + initialNumToRender={initialNumToRender} 105 + windowSize={11} 100 106 /> 101 107 ) 102 108 } 103 - 104 - const styles = StyleSheet.create({ 105 - footer: { 106 - height: 200, 107 - paddingTop: 20, 108 - }, 109 - })
+52 -52
src/view/com/post-thread/PostRepostedBy.tsx
··· 1 - import React, {useMemo, useCallback, useState} from 'react' 2 - import {ActivityIndicator, StyleSheet, View} from 'react-native' 1 + import React, {useCallback, useMemo, useState} from 'react' 3 2 import {AppBskyActorDefs as ActorDefs} from '@atproto/api' 4 - import {CenteredView} from '../util/Views' 5 - import {List} from '../util/List' 6 - import {ProfileCardWithFollowBtn} from '../profile/ProfileCard' 7 - import {ErrorMessage} from '../util/error/ErrorMessage' 3 + import {msg} from '@lingui/macro' 4 + import {useLingui} from '@lingui/react' 5 + 6 + import {cleanError} from '#/lib/strings/errors' 8 7 import {logger} from '#/logger' 9 - import {LoadingScreen} from '../util/LoadingScreen' 10 - import {useResolveUriQuery} from '#/state/queries/resolve-uri' 11 8 import {usePostRepostedByQuery} from '#/state/queries/post-reposted-by' 12 - import {cleanError} from '#/lib/strings/errors' 9 + import {useResolveUriQuery} from '#/state/queries/resolve-uri' 10 + import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender' 11 + import { 12 + ListFooter, 13 + ListHeaderDesktop, 14 + ListMaybePlaceholder, 15 + } from '#/components/Lists' 16 + import {ProfileCardWithFollowBtn} from '../profile/ProfileCard' 17 + import {List} from '../util/List' 18 + 19 + function renderItem({item}: {item: ActorDefs.ProfileViewBasic}) { 20 + return <ProfileCardWithFollowBtn key={item.did} profile={item} /> 21 + } 22 + 23 + function keyExtractor(item: ActorDefs.ProfileViewBasic) { 24 + return item.did 25 + } 13 26 14 27 export function PostRepostedBy({uri}: {uri: string}) { 28 + const {_} = useLingui() 29 + const initialNumToRender = useInitialNumToRender() 30 + 15 31 const [isPTRing, setIsPTRing] = useState(false) 32 + 16 33 const { 17 34 data: resolvedUri, 18 35 error: resolveError, 19 - isFetching: isFetchingResolvedUri, 36 + isLoading: isLoadingUri, 20 37 } = useResolveUriQuery(uri) 21 38 const { 22 39 data, 23 - isFetching, 24 - isFetched, 40 + isLoading: isLoadingRepostedBy, 25 41 isFetchingNextPage, 26 42 hasNextPage, 27 43 fetchNextPage, 28 - isError, 29 44 error, 30 45 refetch, 31 46 } = usePostRepostedByQuery(resolvedUri?.uri) 47 + 48 + const isError = Boolean(resolveError || error) 49 + 32 50 const repostedBy = useMemo(() => { 33 51 if (data?.pages) { 34 52 return data.pages.flatMap(page => page.repostedBy) 35 53 } 54 + return [] 36 55 }, [data]) 37 56 38 57 const onRefresh = useCallback(async () => { ··· 46 65 }, [refetch, setIsPTRing]) 47 66 48 67 const onEndReached = useCallback(async () => { 49 - if (isFetching || !hasNextPage || isError) return 68 + if (isFetchingNextPage || !hasNextPage || isError) return 50 69 try { 51 70 await fetchNextPage() 52 71 } catch (err) { 53 72 logger.error('Failed to load more reposts', {message: err}) 54 73 } 55 - }, [isFetching, hasNextPage, isError, fetchNextPage]) 56 - 57 - const renderItem = useCallback( 58 - ({item}: {item: ActorDefs.ProfileViewBasic}) => { 59 - return <ProfileCardWithFollowBtn key={item.did} profile={item} /> 60 - }, 61 - [], 62 - ) 74 + }, [isFetchingNextPage, hasNextPage, isError, fetchNextPage]) 63 75 64 - if (isFetchingResolvedUri || !isFetched) { 65 - return <LoadingScreen /> 66 - } 67 - 68 - // error 69 - // = 70 - if (resolveError || isError) { 76 + if (repostedBy.length < 1) { 71 77 return ( 72 - <CenteredView> 73 - <ErrorMessage 74 - message={cleanError(resolveError || error)} 75 - onPressTryAgain={onRefresh} 76 - /> 77 - </CenteredView> 78 + <ListMaybePlaceholder 79 + isLoading={isLoadingUri || isLoadingRepostedBy} 80 + isError={isError} 81 + /> 78 82 ) 79 83 } 80 84 ··· 83 87 return ( 84 88 <List 85 89 data={repostedBy} 86 - keyExtractor={item => item.did} 90 + renderItem={renderItem} 91 + keyExtractor={keyExtractor} 87 92 refreshing={isPTRing} 88 93 onRefresh={onRefresh} 89 94 onEndReached={onEndReached} 90 - renderItem={renderItem} 91 - initialNumToRender={15} 92 - // FIXME(dan) 93 - // eslint-disable-next-line react/no-unstable-nested-components 94 - ListFooterComponent={() => ( 95 - <View style={styles.footer}> 96 - {(isFetching || isFetchingNextPage) && <ActivityIndicator />} 97 - </View> 98 - )} 95 + onEndReachedThreshold={4} 96 + ListHeaderComponent={<ListHeaderDesktop title={_(msg`Reposted By`)} />} 97 + ListFooterComponent={ 98 + <ListFooter 99 + isFetchingNextPage={isFetchingNextPage} 100 + error={cleanError(error)} 101 + onRetry={fetchNextPage} 102 + /> 103 + } 99 104 // @ts-ignore our .web version only -prf 100 105 desktopFixedHeight 106 + initialNumToRender={initialNumToRender} 107 + windowSize={11} 101 108 /> 102 109 ) 103 110 } 104 - 105 - const styles = StyleSheet.create({ 106 - footer: { 107 - height: 200, 108 - paddingTop: 20, 109 - }, 110 - })
+8 -7
src/view/screens/PostLikedBy.tsx
··· 1 1 import React from 'react' 2 2 import {View} from 'react-native' 3 + import {msg} from '@lingui/macro' 4 + import {useLingui} from '@lingui/react' 3 5 import {useFocusEffect} from '@react-navigation/native' 4 - import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' 6 + 7 + import {useSetMinimalShellMode} from '#/state/shell' 8 + import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' 9 + import {makeRecordUri} from 'lib/strings/url-helpers' 10 + import {PostLikedBy as PostLikedByComponent} from '../com/post-thread/PostLikedBy' 5 11 import {ViewHeader} from '../com/util/ViewHeader' 6 - import {PostLikedBy as PostLikedByComponent} from '../com/post-thread/PostLikedBy' 7 - import {makeRecordUri} from 'lib/strings/url-helpers' 8 - import {useSetMinimalShellMode} from '#/state/shell' 9 - import {msg} from '@lingui/macro' 10 - import {useLingui} from '@lingui/react' 11 12 12 13 type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostLikedBy'> 13 14 export const PostLikedByScreen = ({route}: Props) => { ··· 23 24 ) 24 25 25 26 return ( 26 - <View> 27 + <View style={{flex: 1}}> 27 28 <ViewHeader title={_(msg`Liked By`)} /> 28 29 <PostLikedByComponent uri={uri} /> 29 30 </View>
+8 -7
src/view/screens/PostRepostedBy.tsx
··· 1 1 import React from 'react' 2 2 import {View} from 'react-native' 3 + import {msg} from '@lingui/macro' 4 + import {useLingui} from '@lingui/react' 3 5 import {useFocusEffect} from '@react-navigation/native' 4 - import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' 6 + 7 + import {useSetMinimalShellMode} from '#/state/shell' 8 + import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' 9 + import {makeRecordUri} from 'lib/strings/url-helpers' 10 + import {PostRepostedBy as PostRepostedByComponent} from '../com/post-thread/PostRepostedBy' 5 11 import {ViewHeader} from '../com/util/ViewHeader' 6 - import {PostRepostedBy as PostRepostedByComponent} from '../com/post-thread/PostRepostedBy' 7 - import {makeRecordUri} from 'lib/strings/url-helpers' 8 - import {useSetMinimalShellMode} from '#/state/shell' 9 - import {useLingui} from '@lingui/react' 10 - import {msg} from '@lingui/macro' 11 12 12 13 type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostRepostedBy'> 13 14 export const PostRepostedByScreen = ({route}: Props) => { ··· 23 24 ) 24 25 25 26 return ( 26 - <View> 27 + <View style={{flex: 1}}> 27 28 <ViewHeader title={_(msg`Reposted By`)} /> 28 29 <PostRepostedByComponent uri={uri} /> 29 30 </View>
+8 -7
src/view/screens/ProfileFeedLikedBy.tsx
··· 1 1 import React from 'react' 2 2 import {View} from 'react-native' 3 + import {msg} from '@lingui/macro' 4 + import {useLingui} from '@lingui/react' 3 5 import {useFocusEffect} from '@react-navigation/native' 4 - import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' 6 + 7 + import {useSetMinimalShellMode} from '#/state/shell' 8 + import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' 9 + import {makeRecordUri} from 'lib/strings/url-helpers' 10 + import {PostLikedBy as PostLikedByComponent} from '../com/post-thread/PostLikedBy' 5 11 import {ViewHeader} from '../com/util/ViewHeader' 6 - import {PostLikedBy as PostLikedByComponent} from '../com/post-thread/PostLikedBy' 7 - import {makeRecordUri} from 'lib/strings/url-helpers' 8 - import {useSetMinimalShellMode} from '#/state/shell' 9 - import {useLingui} from '@lingui/react' 10 - import {msg} from '@lingui/macro' 11 12 12 13 type Props = NativeStackScreenProps<CommonNavigatorParams, 'ProfileFeedLikedBy'> 13 14 export const ProfileFeedLikedByScreen = ({route}: Props) => { ··· 23 24 ) 24 25 25 26 return ( 26 - <View> 27 + <View style={{flex: 1}}> 27 28 <ViewHeader title={_(msg`Liked By`)} /> 28 29 <PostLikedByComponent uri={uri} /> 29 30 </View>