about summary refs log tree commit diff
diff options
context:
space:
mode:
authordan <dan.abramov@gmail.com>2024-04-12 13:02:15 +0100
committerGitHub <noreply@github.com>2024-04-12 13:02:15 +0100
commitbedb0c3fbd65b6520c97f22c99f10bb535a177bc (patch)
tree7107f34544bc7ac66147a846475dfaf40517a44e
parente3e8f10538bba57f1cf298fd1205846f801557eb (diff)
downloadvoidsky-bedb0c3fbd65b6520c97f22c99f10bb535a177bc.tar.zst
Use getSuggestions endpoint behind the gate (#3499)
* Move suggested follows out of the component

* Add new suggestions implementation

* Put new endpoint behind the gate

* Make bottom less weird
-rw-r--r--src/lib/statsig/gates.ts1
-rw-r--r--src/lib/statsig/statsig.tsx5
-rw-r--r--src/view/screens/Search/Search.tsx65
3 files changed, 66 insertions, 5 deletions
diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts
index 5a9582383..acf0b2aff 100644
--- a/src/lib/statsig/gates.ts
+++ b/src/lib/statsig/gates.ts
@@ -7,3 +7,4 @@ export type Gate =
   | 'new_search'
   | 'show_follow_back_label'
   | 'start_session_with_following'
+  | 'use_new_suggestions_endpoint'
diff --git a/src/lib/statsig/statsig.tsx b/src/lib/statsig/statsig.tsx
index 159438647..7513b945c 100644
--- a/src/lib/statsig/statsig.tsx
+++ b/src/lib/statsig/statsig.tsx
@@ -82,7 +82,10 @@ export function useGate(gateName: Gate): boolean {
     // This should not happen because of waitForInitialization={true}.
     console.error('Did not expected isLoading to ever be true.')
   }
-  return value
+  // This shouldn't technically be necessary but let's get a strong
+  // guarantee that a gate value can never change while mounted.
+  const [initialValue] = React.useState(value)
+  return initialValue
 }
 
 function toStatsigUser(did: string | undefined) {
diff --git a/src/view/screens/Search/Search.tsx b/src/view/screens/Search/Search.tsx
index 3b06992fc..f5ebd155c 100644
--- a/src/view/screens/Search/Search.tsx
+++ b/src/view/screens/Search/Search.tsx
@@ -32,7 +32,10 @@ import {useActorAutocompleteFn} from '#/state/queries/actor-autocomplete'
 import {useActorSearch} from '#/state/queries/actor-search'
 import {useModerationOpts} from '#/state/queries/preferences'
 import {useSearchPostsQuery} from '#/state/queries/search-posts'
-import {useGetSuggestedFollowersByActor} from '#/state/queries/suggested-follows'
+import {
+  useGetSuggestedFollowersByActor,
+  useSuggestedFollowsQuery,
+} from '#/state/queries/suggested-follows'
 import {useSession} from '#/state/session'
 import {useSetDrawerOpen} from '#/state/shell'
 import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from '#/state/shell'
@@ -118,8 +121,10 @@ function EmptyState({message, error}: {message: string; error?: string}) {
   )
 }
 
-function SearchScreenSuggestedFollows() {
-  const pal = usePalette('default')
+function useSuggestedFollowsV1(): [
+  AppBskyActorDefs.ProfileViewBasic[],
+  () => void,
+] {
   const {currentAccount} = useSession()
   const [suggestions, setSuggestions] = React.useState<
     AppBskyActorDefs.ProfileViewBasic[]
@@ -162,6 +167,56 @@ function SearchScreenSuggestedFollows() {
     }
   }, [currentAccount, setSuggestions, getSuggestedFollowsByActor])
 
+  return [suggestions, () => {}]
+}
+
+function useSuggestedFollowsV2(): [
+  AppBskyActorDefs.ProfileViewBasic[],
+  () => void,
+] {
+  const {
+    data: suggestions,
+    hasNextPage,
+    isFetchingNextPage,
+    isError,
+    fetchNextPage,
+  } = useSuggestedFollowsQuery()
+
+  const onEndReached = React.useCallback(async () => {
+    if (isFetchingNextPage || !hasNextPage || isError) return
+    try {
+      await fetchNextPage()
+    } catch (err) {
+      logger.error('Failed to load more suggested follows', {message: err})
+    }
+  }, [isFetchingNextPage, hasNextPage, isError, fetchNextPage])
+
+  const items: AppBskyActorDefs.ProfileViewBasic[] = []
+  if (suggestions) {
+    // Currently the responses contain duplicate items.
+    // Needs to be fixed on backend, but let's dedupe to be safe.
+    let seen = new Set()
+    for (const page of suggestions.pages) {
+      for (const actor of page.actors) {
+        if (!seen.has(actor.did)) {
+          seen.add(actor.did)
+          items.push(actor)
+        }
+      }
+    }
+  }
+  return [items, onEndReached]
+}
+
+function SearchScreenSuggestedFollows() {
+  const pal = usePalette('default')
+  const useSuggestedFollows = useGate('use_new_suggestions_endpoint')
+    ? // Conditional hook call here is *only* OK because useGate()
+      // result won't change until a remount.
+      useSuggestedFollowsV2
+    : useSuggestedFollowsV1
+  const [suggestions, onEndReached] = useSuggestedFollows()
+
   return suggestions.length ? (
     <List
       data={suggestions}
@@ -169,9 +224,11 @@ function SearchScreenSuggestedFollows() {
       keyExtractor={item => item.did}
       // @ts-ignore web only -prf
       desktopFixedHeight
-      contentContainerStyle={{paddingBottom: 1200}}
+      contentContainerStyle={{paddingBottom: 200}}
       keyboardShouldPersistTaps="handled"
       keyboardDismissMode="on-drag"
+      onEndReached={onEndReached}
+      onEndReachedThreshold={2}
     />
   ) : (
     <CenteredView sideBorders style={[pal.border, s.hContentRegion]}>