From bedb0c3fbd65b6520c97f22c99f10bb535a177bc Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 12 Apr 2024 13:02:15 +0100 Subject: [PATCH] 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 --- src/lib/statsig/gates.ts | 1 + src/lib/statsig/statsig.tsx | 5 ++- src/view/screens/Search/Search.tsx | 65 ++++++++++++++++++++++++++++-- 3 files changed, 66 insertions(+), 5 deletions(-) diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts index 5a958238..acf0b2af 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 15943864..7513b945 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 3b06992f..f5ebd155 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 ? ( item.did} // @ts-ignore web only -prf desktopFixedHeight - contentContainerStyle={{paddingBottom: 1200}} + contentContainerStyle={{paddingBottom: 200}} keyboardShouldPersistTaps="handled" keyboardDismissMode="on-drag" + onEndReached={onEndReached} + onEndReachedThreshold={2} /> ) : (