ProfileFollows and ProfileFollowers cleanup (#3219)
* cleanup PostThread rm some more unnecessary code cleanup some more pieces fix `isLoading` logic few fixes organize refactor `PostThread` allow chaining of `postThreadQuery` Update `Hashtag` screen with the component changes Make some changes to the List components adjust height and padding of bottom loader to account for bottom bar * rm unnecessary chaining logic * maxReplies logic * adjust error logic * use `<` instead of `<=` * add back warning comment * remove unused prop * adjust order * implement list improvements for followers/follows * update prop name * small adjustments fix flex add window size adjust isLoading * remove log * don't show retry for no results * don't show error if `isLoading`zio/stable
parent
addd66b37f
commit
b9474a5d55
|
@ -1,39 +1,66 @@
|
|||
import React from 'react'
|
||||
import {ActivityIndicator, StyleSheet, View} from 'react-native'
|
||||
import {AppBskyActorDefs as ActorDefs} from '@atproto/api'
|
||||
import {CenteredView} from '../util/Views'
|
||||
import {LoadingScreen} from '../util/LoadingScreen'
|
||||
import {List} from '../util/List'
|
||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||
import {ProfileCardWithFollowBtn} from './ProfileCard'
|
||||
import {useProfileFollowersQuery} from '#/state/queries/profile-followers'
|
||||
import {useResolveDidQuery} from '#/state/queries/resolve-uri'
|
||||
import {logger} from '#/logger'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
||||
import {
|
||||
ListFooter,
|
||||
ListHeaderDesktop,
|
||||
ListMaybePlaceholder,
|
||||
} from '#/components/Lists'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useSession} from 'state/session'
|
||||
import {View} from 'react-native'
|
||||
|
||||
function renderItem({item}: {item: ActorDefs.ProfileViewBasic}) {
|
||||
return <ProfileCardWithFollowBtn key={item.did} profile={item} />
|
||||
}
|
||||
|
||||
function keyExtractor(item: ActorDefs.ProfileViewBasic) {
|
||||
return item.did
|
||||
}
|
||||
|
||||
export function ProfileFollowers({name}: {name: string}) {
|
||||
const {_} = useLingui()
|
||||
const initialNumToRender = useInitialNumToRender()
|
||||
const {currentAccount} = useSession()
|
||||
|
||||
const [isPTRing, setIsPTRing] = React.useState(false)
|
||||
const {
|
||||
data: resolvedDid,
|
||||
isLoading: isDidLoading,
|
||||
error: resolveError,
|
||||
isFetching: isFetchingDid,
|
||||
} = useResolveDidQuery(name)
|
||||
const {
|
||||
data,
|
||||
isLoading: isFollowersLoading,
|
||||
isFetching,
|
||||
isFetched,
|
||||
isFetchingNextPage,
|
||||
hasNextPage,
|
||||
fetchNextPage,
|
||||
isError,
|
||||
error,
|
||||
refetch,
|
||||
} = useProfileFollowersQuery(resolvedDid)
|
||||
|
||||
const isError = React.useMemo(
|
||||
() => !!resolveError || !!error,
|
||||
[resolveError, error],
|
||||
)
|
||||
|
||||
const isMe = React.useMemo(() => {
|
||||
return resolvedDid === currentAccount?.did
|
||||
}, [resolvedDid, currentAccount?.did])
|
||||
|
||||
const followers = React.useMemo(() => {
|
||||
if (data?.pages) {
|
||||
return data.pages.flatMap(page => page.followers)
|
||||
}
|
||||
return []
|
||||
}, [data])
|
||||
|
||||
const onRefresh = React.useCallback(async () => {
|
||||
|
@ -47,7 +74,7 @@ export function ProfileFollowers({name}: {name: string}) {
|
|||
}, [refetch, setIsPTRing])
|
||||
|
||||
const onEndReached = async () => {
|
||||
if (isFetching || !hasNextPage || isError) return
|
||||
if (isFetching || !hasNextPage || !!error) return
|
||||
try {
|
||||
await fetchNextPage()
|
||||
} catch (err) {
|
||||
|
@ -55,57 +82,38 @@ export function ProfileFollowers({name}: {name: string}) {
|
|||
}
|
||||
}
|
||||
|
||||
const renderItem = React.useCallback(
|
||||
({item}: {item: ActorDefs.ProfileViewBasic}) => (
|
||||
<ProfileCardWithFollowBtn key={item.did} profile={item} />
|
||||
),
|
||||
[],
|
||||
)
|
||||
|
||||
if (isFetchingDid || !isFetched) {
|
||||
return <LoadingScreen />
|
||||
}
|
||||
|
||||
// error
|
||||
// =
|
||||
if (resolveError || isError) {
|
||||
return (
|
||||
<CenteredView>
|
||||
<ErrorMessage
|
||||
message={cleanError(resolveError || error)}
|
||||
onPressTryAgain={onRefresh}
|
||||
/>
|
||||
</CenteredView>
|
||||
)
|
||||
}
|
||||
|
||||
// loaded
|
||||
// =
|
||||
return (
|
||||
<List
|
||||
data={followers}
|
||||
keyExtractor={item => item.did}
|
||||
refreshing={isPTRing}
|
||||
onRefresh={onRefresh}
|
||||
onEndReached={onEndReached}
|
||||
renderItem={renderItem}
|
||||
initialNumToRender={15}
|
||||
// FIXME(dan)
|
||||
// eslint-disable-next-line react/no-unstable-nested-components
|
||||
ListFooterComponent={() => (
|
||||
<View style={styles.footer}>
|
||||
{(isFetching || isFetchingNextPage) && <ActivityIndicator />}
|
||||
</View>
|
||||
<View style={{flex: 1}}>
|
||||
<ListMaybePlaceholder
|
||||
isLoading={isDidLoading || isFollowersLoading}
|
||||
isEmpty={followers.length < 1}
|
||||
isError={isError}
|
||||
emptyType="results"
|
||||
emptyMessage={
|
||||
isMe
|
||||
? _(msg`You do not have any followers.`)
|
||||
: _(msg`This user doesn't have any followers.`)
|
||||
}
|
||||
errorMessage={cleanError(resolveError || error)}
|
||||
onRetry={isError ? refetch : undefined}
|
||||
/>
|
||||
{followers.length > 0 && (
|
||||
<List
|
||||
data={followers}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={keyExtractor}
|
||||
refreshing={isPTRing}
|
||||
onRefresh={onRefresh}
|
||||
onEndReached={onEndReached}
|
||||
onEndReachedThreshold={4}
|
||||
ListHeaderComponent={<ListHeaderDesktop title={_(msg`Followers`)} />}
|
||||
ListFooterComponent={<ListFooter isFetching={isFetchingNextPage} />}
|
||||
// @ts-ignore our .web version only -prf
|
||||
desktopFixedHeight
|
||||
initialNumToRender={initialNumToRender}
|
||||
windowSize={11}
|
||||
/>
|
||||
)}
|
||||
// @ts-ignore our .web version only -prf
|
||||
desktopFixedHeight
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
footer: {
|
||||
height: 200,
|
||||
paddingTop: 20,
|
||||
},
|
||||
})
|
||||
|
|
|
@ -1,39 +1,65 @@
|
|||
import React from 'react'
|
||||
import {ActivityIndicator, StyleSheet, View} from 'react-native'
|
||||
import {AppBskyActorDefs as ActorDefs} from '@atproto/api'
|
||||
import {CenteredView} from '../util/Views'
|
||||
import {LoadingScreen} from '../util/LoadingScreen'
|
||||
import {List} from '../util/List'
|
||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||
import {ProfileCardWithFollowBtn} from './ProfileCard'
|
||||
import {useProfileFollowsQuery} from '#/state/queries/profile-follows'
|
||||
import {useResolveDidQuery} from '#/state/queries/resolve-uri'
|
||||
import {logger} from '#/logger'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {
|
||||
ListFooter,
|
||||
ListHeaderDesktop,
|
||||
ListMaybePlaceholder,
|
||||
} from '#/components/Lists'
|
||||
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
||||
import {useSession} from 'state/session'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
function renderItem({item}: {item: ActorDefs.ProfileViewBasic}) {
|
||||
return <ProfileCardWithFollowBtn key={item.did} profile={item} />
|
||||
}
|
||||
|
||||
function keyExtractor(item: ActorDefs.ProfileViewBasic) {
|
||||
return item.did
|
||||
}
|
||||
|
||||
export function ProfileFollows({name}: {name: string}) {
|
||||
const {_} = useLingui()
|
||||
const initialNumToRender = useInitialNumToRender()
|
||||
const {currentAccount} = useSession()
|
||||
|
||||
const [isPTRing, setIsPTRing] = React.useState(false)
|
||||
const {
|
||||
data: resolvedDid,
|
||||
isLoading: isDidLoading,
|
||||
error: resolveError,
|
||||
isFetching: isFetchingDid,
|
||||
} = useResolveDidQuery(name)
|
||||
const {
|
||||
data,
|
||||
isLoading: isFollowsLoading,
|
||||
isFetching,
|
||||
isFetched,
|
||||
isFetchingNextPage,
|
||||
hasNextPage,
|
||||
fetchNextPage,
|
||||
isError,
|
||||
error,
|
||||
refetch,
|
||||
} = useProfileFollowsQuery(resolvedDid)
|
||||
|
||||
const isError = React.useMemo(
|
||||
() => !!resolveError || !!error,
|
||||
[resolveError, error],
|
||||
)
|
||||
|
||||
const isMe = React.useMemo(() => {
|
||||
return resolvedDid === currentAccount?.did
|
||||
}, [resolvedDid, currentAccount?.did])
|
||||
|
||||
const follows = React.useMemo(() => {
|
||||
if (data?.pages) {
|
||||
return data.pages.flatMap(page => page.follows)
|
||||
}
|
||||
return []
|
||||
}, [data])
|
||||
|
||||
const onRefresh = React.useCallback(async () => {
|
||||
|
@ -47,7 +73,7 @@ export function ProfileFollows({name}: {name: string}) {
|
|||
}, [refetch, setIsPTRing])
|
||||
|
||||
const onEndReached = async () => {
|
||||
if (isFetching || !hasNextPage || isError) return
|
||||
if (isFetching || !hasNextPage || !!error) return
|
||||
try {
|
||||
await fetchNextPage()
|
||||
} catch (err) {
|
||||
|
@ -55,57 +81,38 @@ export function ProfileFollows({name}: {name: string}) {
|
|||
}
|
||||
}
|
||||
|
||||
const renderItem = React.useCallback(
|
||||
({item}: {item: ActorDefs.ProfileViewBasic}) => (
|
||||
<ProfileCardWithFollowBtn key={item.did} profile={item} />
|
||||
),
|
||||
[],
|
||||
)
|
||||
|
||||
if (isFetchingDid || !isFetched) {
|
||||
return <LoadingScreen />
|
||||
}
|
||||
|
||||
// error
|
||||
// =
|
||||
if (resolveError || isError) {
|
||||
return (
|
||||
<CenteredView>
|
||||
<ErrorMessage
|
||||
message={cleanError(resolveError || error)}
|
||||
onPressTryAgain={onRefresh}
|
||||
/>
|
||||
</CenteredView>
|
||||
)
|
||||
}
|
||||
|
||||
// loaded
|
||||
// =
|
||||
return (
|
||||
<List
|
||||
data={follows}
|
||||
keyExtractor={item => item.did}
|
||||
refreshing={isPTRing}
|
||||
onRefresh={onRefresh}
|
||||
onEndReached={onEndReached}
|
||||
renderItem={renderItem}
|
||||
initialNumToRender={15}
|
||||
// FIXME(dan)
|
||||
// eslint-disable-next-line react/no-unstable-nested-components
|
||||
ListFooterComponent={() => (
|
||||
<View style={styles.footer}>
|
||||
{(isFetching || isFetchingNextPage) && <ActivityIndicator />}
|
||||
</View>
|
||||
<>
|
||||
<ListMaybePlaceholder
|
||||
isLoading={isDidLoading || isFollowsLoading}
|
||||
isEmpty={follows.length < 1}
|
||||
isError={isError}
|
||||
emptyType="results"
|
||||
emptyMessage={
|
||||
isMe
|
||||
? _(msg`You are not following anyone.`)
|
||||
: _(msg`This user isn't following anyone.`)
|
||||
}
|
||||
errorMessage={cleanError(resolveError || error)}
|
||||
onRetry={isError ? refetch : undefined}
|
||||
/>
|
||||
{follows.length > 0 && (
|
||||
<List
|
||||
data={follows}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={keyExtractor}
|
||||
refreshing={isPTRing}
|
||||
onRefresh={onRefresh}
|
||||
onEndReached={onEndReached}
|
||||
onEndReachedThreshold={4}
|
||||
ListHeaderComponent={<ListHeaderDesktop title={_(msg`Following`)} />}
|
||||
ListFooterComponent={<ListFooter isFetching={isFetchingNextPage} />}
|
||||
// @ts-ignore our .web version only -prf
|
||||
desktopFixedHeight
|
||||
initialNumToRender={initialNumToRender}
|
||||
windowSize={11}
|
||||
/>
|
||||
)}
|
||||
// @ts-ignore our .web version only -prf
|
||||
desktopFixedHeight
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
footer: {
|
||||
height: 200,
|
||||
paddingTop: 20,
|
||||
},
|
||||
})
|
||||
|
|
|
@ -21,7 +21,7 @@ export const ProfileFollowersScreen = ({route}: Props) => {
|
|||
)
|
||||
|
||||
return (
|
||||
<View>
|
||||
<View style={{flex: 1}}>
|
||||
<ViewHeader title={_(msg`Followers`)} />
|
||||
<ProfileFollowersComponent name={name} />
|
||||
</View>
|
||||
|
|
|
@ -21,7 +21,7 @@ export const ProfileFollowsScreen = ({route}: Props) => {
|
|||
)
|
||||
|
||||
return (
|
||||
<View>
|
||||
<View style={{flex: 1}}>
|
||||
<ViewHeader title={_(msg`Following`)} />
|
||||
<ProfileFollowsComponent name={name} />
|
||||
</View>
|
||||
|
|
Loading…
Reference in New Issue