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 weirdzio/stable
parent
e3e8f10538
commit
bedb0c3fbd
|
@ -7,3 +7,4 @@ export type Gate =
|
||||||
| 'new_search'
|
| 'new_search'
|
||||||
| 'show_follow_back_label'
|
| 'show_follow_back_label'
|
||||||
| 'start_session_with_following'
|
| 'start_session_with_following'
|
||||||
|
| 'use_new_suggestions_endpoint'
|
||||||
|
|
|
@ -82,7 +82,10 @@ export function useGate(gateName: Gate): boolean {
|
||||||
// This should not happen because of waitForInitialization={true}.
|
// This should not happen because of waitForInitialization={true}.
|
||||||
console.error('Did not expected isLoading to ever be 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) {
|
function toStatsigUser(did: string | undefined) {
|
||||||
|
|
|
@ -32,7 +32,10 @@ import {useActorAutocompleteFn} from '#/state/queries/actor-autocomplete'
|
||||||
import {useActorSearch} from '#/state/queries/actor-search'
|
import {useActorSearch} from '#/state/queries/actor-search'
|
||||||
import {useModerationOpts} from '#/state/queries/preferences'
|
import {useModerationOpts} from '#/state/queries/preferences'
|
||||||
import {useSearchPostsQuery} from '#/state/queries/search-posts'
|
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 {useSession} from '#/state/session'
|
||||||
import {useSetDrawerOpen} from '#/state/shell'
|
import {useSetDrawerOpen} from '#/state/shell'
|
||||||
import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from '#/state/shell'
|
import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from '#/state/shell'
|
||||||
|
@ -118,8 +121,10 @@ function EmptyState({message, error}: {message: string; error?: string}) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SearchScreenSuggestedFollows() {
|
function useSuggestedFollowsV1(): [
|
||||||
const pal = usePalette('default')
|
AppBskyActorDefs.ProfileViewBasic[],
|
||||||
|
() => void,
|
||||||
|
] {
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const [suggestions, setSuggestions] = React.useState<
|
const [suggestions, setSuggestions] = React.useState<
|
||||||
AppBskyActorDefs.ProfileViewBasic[]
|
AppBskyActorDefs.ProfileViewBasic[]
|
||||||
|
@ -162,6 +167,56 @@ function SearchScreenSuggestedFollows() {
|
||||||
}
|
}
|
||||||
}, [currentAccount, setSuggestions, getSuggestedFollowsByActor])
|
}, [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 ? (
|
return suggestions.length ? (
|
||||||
<List
|
<List
|
||||||
data={suggestions}
|
data={suggestions}
|
||||||
|
@ -169,9 +224,11 @@ function SearchScreenSuggestedFollows() {
|
||||||
keyExtractor={item => item.did}
|
keyExtractor={item => item.did}
|
||||||
// @ts-ignore web only -prf
|
// @ts-ignore web only -prf
|
||||||
desktopFixedHeight
|
desktopFixedHeight
|
||||||
contentContainerStyle={{paddingBottom: 1200}}
|
contentContainerStyle={{paddingBottom: 200}}
|
||||||
keyboardShouldPersistTaps="handled"
|
keyboardShouldPersistTaps="handled"
|
||||||
keyboardDismissMode="on-drag"
|
keyboardDismissMode="on-drag"
|
||||||
|
onEndReached={onEndReached}
|
||||||
|
onEndReachedThreshold={2}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<CenteredView sideBorders style={[pal.border, s.hContentRegion]}>
|
<CenteredView sideBorders style={[pal.border, s.hContentRegion]}>
|
||||||
|
|
Loading…
Reference in New Issue