about summary refs log tree commit diff
path: root/src/screens/Profile/KnownFollowers.tsx
diff options
context:
space:
mode:
authorEric Bailey <git@esb.lol>2024-06-11 17:42:28 -0500
committerGitHub <noreply@github.com>2024-06-11 15:42:28 -0700
commitbb0a6a4b6c4e86c62d599c424dae35c9ee9d200d (patch)
treedc4732574140d62efe2bb205193c93996b05a98e /src/screens/Profile/KnownFollowers.tsx
parent7011ac8f72ed18153ea485b6cce2e18040de2dc9 (diff)
downloadvoidsky-bb0a6a4b6c4e86c62d599c424dae35c9ee9d200d.tar.zst
Add KnownFollowers component to standard profile header (#4420)
* Add KnownFollowers component to standard profile header

* Prep for known followers screen

* Add known followers screen

* Tighten space

* Add pressed state

* Edit title

* Vertically center

* Don't show if no known followers

* Bump sdk

* Use actual followers.length to show

* Updates to show logic, space

* Prevent fresh data from applying to cached screens

* Tighten space

* Better label

* Oxford comma

* Fix count logic

* Add bskyweb route

* Useless ternary

* Minor spacing tweak

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
Diffstat (limited to 'src/screens/Profile/KnownFollowers.tsx')
-rw-r--r--src/screens/Profile/KnownFollowers.tsx134
1 files changed, 134 insertions, 0 deletions
diff --git a/src/screens/Profile/KnownFollowers.tsx b/src/screens/Profile/KnownFollowers.tsx
new file mode 100644
index 000000000..5cb45a11e
--- /dev/null
+++ b/src/screens/Profile/KnownFollowers.tsx
@@ -0,0 +1,134 @@
+import React from 'react'
+import {View} from 'react-native'
+import {AppBskyActorDefs} from '@atproto/api'
+import {msg} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+import {useFocusEffect} from '@react-navigation/native'
+
+import {cleanError} from '#/lib/strings/errors'
+import {logger} from '#/logger'
+import {useProfileKnownFollowersQuery} from '#/state/queries/known-followers'
+import {useResolveDidQuery} from '#/state/queries/resolve-uri'
+import {useSetMinimalShellMode} from '#/state/shell'
+import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
+import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types'
+import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard'
+import {List} from '#/view/com/util/List'
+import {ViewHeader} from '#/view/com/util/ViewHeader'
+import {
+  ListFooter,
+  ListHeaderDesktop,
+  ListMaybePlaceholder,
+} from '#/components/Lists'
+
+function renderItem({item}: {item: AppBskyActorDefs.ProfileViewBasic}) {
+  return <ProfileCardWithFollowBtn key={item.did} profile={item} />
+}
+
+function keyExtractor(item: AppBskyActorDefs.ProfileViewBasic) {
+  return item.did
+}
+
+type Props = NativeStackScreenProps<
+  CommonNavigatorParams,
+  'ProfileKnownFollowers'
+>
+export const ProfileKnownFollowersScreen = ({route}: Props) => {
+  const {_} = useLingui()
+  const setMinimalShellMode = useSetMinimalShellMode()
+  const initialNumToRender = useInitialNumToRender()
+
+  const {name} = route.params
+
+  const [isPTRing, setIsPTRing] = React.useState(false)
+  const {
+    data: resolvedDid,
+    isLoading: isDidLoading,
+    error: resolveError,
+  } = useResolveDidQuery(route.params.name)
+  const {
+    data,
+    isLoading: isFollowersLoading,
+    isFetchingNextPage,
+    hasNextPage,
+    fetchNextPage,
+    error,
+    refetch,
+  } = useProfileKnownFollowersQuery(resolvedDid)
+
+  const onRefresh = React.useCallback(async () => {
+    setIsPTRing(true)
+    try {
+      await refetch()
+    } catch (err) {
+      logger.error('Failed to refresh followers', {message: err})
+    }
+    setIsPTRing(false)
+  }, [refetch, setIsPTRing])
+
+  const onEndReached = React.useCallback(async () => {
+    if (isFetchingNextPage || !hasNextPage || !!error) return
+    try {
+      await fetchNextPage()
+    } catch (err) {
+      logger.error('Failed to load more followers', {message: err})
+    }
+  }, [isFetchingNextPage, hasNextPage, error, fetchNextPage])
+
+  const followers = React.useMemo(() => {
+    if (data?.pages) {
+      return data.pages.flatMap(page => page.followers)
+    }
+    return []
+  }, [data])
+
+  const isError = Boolean(resolveError || error)
+
+  useFocusEffect(
+    React.useCallback(() => {
+      setMinimalShellMode(false)
+    }, [setMinimalShellMode]),
+  )
+
+  if (followers.length < 1) {
+    return (
+      <ListMaybePlaceholder
+        isLoading={isDidLoading || isFollowersLoading}
+        isError={isError}
+        emptyType="results"
+        emptyMessage={_(msg`You don't follow any users who follow @${name}.`)}
+        errorMessage={cleanError(resolveError || error)}
+        onRetry={isError ? refetch : undefined}
+      />
+    )
+  }
+
+  return (
+    <View style={{flex: 1}}>
+      <ViewHeader title={_(msg`Followers you know`)} />
+      <List
+        data={followers}
+        renderItem={renderItem}
+        keyExtractor={keyExtractor}
+        refreshing={isPTRing}
+        onRefresh={onRefresh}
+        onEndReached={onEndReached}
+        onEndReachedThreshold={4}
+        ListHeaderComponent={
+          <ListHeaderDesktop title={_(msg`Followers you know`)} />
+        }
+        ListFooterComponent={
+          <ListFooter
+            isFetchingNextPage={isFetchingNextPage}
+            error={cleanError(error)}
+            onRetry={fetchNextPage}
+          />
+        }
+        // @ts-ignore our .web version only -prf
+        desktopFixedHeight
+        initialNumToRender={initialNumToRender}
+        windowSize={11}
+      />
+    </View>
+  )
+}