include 'for you' in see more suggested users
This commit is contained in:
@@ -2,12 +2,17 @@ import {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'
|
|||||||
import {TextInput, View, type ViewToken} from 'react-native'
|
import {TextInput, View, type ViewToken} from 'react-native'
|
||||||
import {type ModerationOpts} from '@atproto/api'
|
import {type ModerationOpts} from '@atproto/api'
|
||||||
import {Trans, useLingui} from '@lingui/react/macro'
|
import {Trans, useLingui} from '@lingui/react/macro'
|
||||||
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||||
import {popularInterests, useInterestsDisplayNames} from '#/lib/interests'
|
import {popularInterests, useInterestsDisplayNames} from '#/lib/interests'
|
||||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||||
import {useActorSearch} from '#/state/queries/actor-search'
|
import {useActorSearch} from '#/state/queries/actor-search'
|
||||||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||||
|
import {
|
||||||
|
getAllDidsInDiscoverCache,
|
||||||
|
useGetSuggestedUsersForDiscoverQuery,
|
||||||
|
} from '#/state/queries/trending/useGetSuggestedUsersForDiscoverQuery'
|
||||||
import {useGetSuggestedUsersForSeeMoreQuery} from '#/state/queries/trending/useGetSuggestedUsersForSeeMoreQuery'
|
import {useGetSuggestedUsersForSeeMoreQuery} from '#/state/queries/trending/useGetSuggestedUsersForSeeMoreQuery'
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
import {type Follow10ProgressGuide} from '#/state/shell/progress-guide'
|
import {type Follow10ProgressGuide} from '#/state/shell/progress-guide'
|
||||||
@@ -109,21 +114,33 @@ export function FollowDialogWithoutGuide({
|
|||||||
let lastSelectedInterest = ''
|
let lastSelectedInterest = ''
|
||||||
let lastSearchText = ''
|
let lastSearchText = ''
|
||||||
|
|
||||||
|
const FOR_YOU_TAB = 'all'
|
||||||
|
|
||||||
function DialogInner({guide}: {guide?: Follow10ProgressGuide}) {
|
function DialogInner({guide}: {guide?: Follow10ProgressGuide}) {
|
||||||
const {t: l} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const interestsDisplayNames = useInterestsDisplayNames()
|
const queryClient = useQueryClient()
|
||||||
|
const rawInterestsDisplayNames = useInterestsDisplayNames()
|
||||||
const {data: preferences} = usePreferencesQuery()
|
const {data: preferences} = usePreferencesQuery()
|
||||||
const personalizedInterests = preferences?.interests?.tags
|
const personalizedInterests = preferences?.interests?.tags
|
||||||
const interests = Object.keys(interestsDisplayNames)
|
const interests = useMemo(
|
||||||
.sort(boostInterests(popularInterests))
|
() => [
|
||||||
.sort(boostInterests(personalizedInterests))
|
FOR_YOU_TAB,
|
||||||
|
...Object.keys(rawInterestsDisplayNames)
|
||||||
|
.sort(boostInterests(popularInterests))
|
||||||
|
.sort(boostInterests(personalizedInterests)),
|
||||||
|
],
|
||||||
|
[rawInterestsDisplayNames, personalizedInterests],
|
||||||
|
)
|
||||||
|
const interestsDisplayNames = useMemo(
|
||||||
|
() => ({
|
||||||
|
[FOR_YOU_TAB]: l`For You`,
|
||||||
|
...rawInterestsDisplayNames,
|
||||||
|
}),
|
||||||
|
[l, rawInterestsDisplayNames],
|
||||||
|
)
|
||||||
const [selectedInterest, setSelectedInterest] = useState(
|
const [selectedInterest, setSelectedInterest] = useState(
|
||||||
() =>
|
() => lastSelectedInterest || FOR_YOU_TAB,
|
||||||
lastSelectedInterest ||
|
|
||||||
(personalizedInterests && interests.includes(personalizedInterests[0])
|
|
||||||
? personalizedInterests[0]
|
|
||||||
: interests[0]),
|
|
||||||
)
|
)
|
||||||
const [searchText, setSearchText] = useState(lastSearchText)
|
const [searchText, setSearchText] = useState(lastSearchText)
|
||||||
const moderationOpts = useModerationOpts()
|
const moderationOpts = useModerationOpts()
|
||||||
@@ -137,14 +154,31 @@ function DialogInner({guide}: {guide?: Follow10ProgressGuide}) {
|
|||||||
lastSelectedInterest = selectedInterest
|
lastSelectedInterest = selectedInterest
|
||||||
}, [searchText, selectedInterest])
|
}, [searchText, selectedInterest])
|
||||||
|
|
||||||
const {
|
const isForYou = selectedInterest === FOR_YOU_TAB
|
||||||
data: suggestions,
|
|
||||||
isFetching: isFetchingSuggestions,
|
// Snapshot the DIDs already shown by the home-feed Discover interstitial at
|
||||||
error: suggestionsError,
|
// dialog open time. The endpoint has no cursor; we dedup client-side so the
|
||||||
} = useGetSuggestedUsersForSeeMoreQuery({
|
// For You tab doesn't re-show profiles the viewer just saw. Snapshotting
|
||||||
category: selectedInterest,
|
// once (lazy init) prevents our own `limit: 50` fetch from excluding itself
|
||||||
|
// after it lands in the shared cache.
|
||||||
|
const [alreadyShownDids] = useState(() =>
|
||||||
|
getAllDidsInDiscoverCache(queryClient),
|
||||||
|
)
|
||||||
|
|
||||||
|
const discoverQuery = useGetSuggestedUsersForDiscoverQuery({
|
||||||
limit: 50,
|
limit: 50,
|
||||||
|
enabled: isForYou,
|
||||||
})
|
})
|
||||||
|
const seeMoreQuery = useGetSuggestedUsersForSeeMoreQuery({
|
||||||
|
category: isForYou ? undefined : selectedInterest,
|
||||||
|
limit: 50,
|
||||||
|
enabled: !isForYou,
|
||||||
|
})
|
||||||
|
const suggestions = isForYou ? discoverQuery.data : seeMoreQuery.data
|
||||||
|
const isFetchingSuggestions = isForYou
|
||||||
|
? discoverQuery.isFetching
|
||||||
|
: seeMoreQuery.isFetching
|
||||||
|
const suggestionsError = isForYou ? discoverQuery.error : seeMoreQuery.error
|
||||||
const {
|
const {
|
||||||
data: searchResults,
|
data: searchResults,
|
||||||
isFetching: isFetchingSearchResults,
|
isFetching: isFetchingSearchResults,
|
||||||
@@ -188,6 +222,10 @@ function DialogInner({guide}: {guide?: Follow10ProgressGuide}) {
|
|||||||
if (seen.has(profile.did)) continue
|
if (seen.has(profile.did)) continue
|
||||||
if (profile.did === currentAccount?.did) continue
|
if (profile.did === currentAccount?.did) continue
|
||||||
if (profile.viewer?.following) continue
|
if (profile.viewer?.following) continue
|
||||||
|
// On the For You tab, skip profiles the viewer was already shown by
|
||||||
|
// the home-feed Discover interstitial.
|
||||||
|
if (isForYou && !hasSearchText && alreadyShownDids.has(profile.did))
|
||||||
|
continue
|
||||||
|
|
||||||
seen.add(profile.did)
|
seen.add(profile.did)
|
||||||
|
|
||||||
@@ -222,6 +260,8 @@ function DialogInner({guide}: {guide?: Follow10ProgressGuide}) {
|
|||||||
hasSearchText,
|
hasSearchText,
|
||||||
resultsKey,
|
resultsKey,
|
||||||
isSearchResultsError,
|
isSearchResultsError,
|
||||||
|
isForYou,
|
||||||
|
alreadyShownDids,
|
||||||
])
|
])
|
||||||
|
|
||||||
const isGuide = Boolean(guide)
|
const isGuide = Boolean(guide)
|
||||||
@@ -277,7 +317,10 @@ function DialogInner({guide}: {guide?: Follow10ProgressGuide}) {
|
|||||||
recId: recIdForLogging,
|
recId: recIdForLogging,
|
||||||
position: position !== -1 ? position : 0,
|
position: position !== -1 ? position : 0,
|
||||||
suggestedDid: item.profile.did,
|
suggestedDid: item.profile.did,
|
||||||
category: selectedInterestRef.current,
|
category:
|
||||||
|
selectedInterestRef.current === FOR_YOU_TAB
|
||||||
|
? null
|
||||||
|
: selectedInterestRef.current,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,21 +15,23 @@ import {useAgent} from '#/state/session'
|
|||||||
|
|
||||||
export type QueryProps = {
|
export type QueryProps = {
|
||||||
limit?: number
|
limit?: number
|
||||||
|
enabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getSuggestedUsersForDiscoverQueryKeyRoot =
|
export const getSuggestedUsersForDiscoverQueryKeyRoot =
|
||||||
'unspecced-suggested-users-for-explore'
|
'unspecced-suggested-users-for-explore'
|
||||||
export const createGetSuggestedUsersForDiscoverQueryKey = (
|
export const createGetSuggestedUsersForDiscoverQueryKey = (props: {
|
||||||
props: QueryProps,
|
limit?: number
|
||||||
) => [getSuggestedUsersForDiscoverQueryKeyRoot, props.limit]
|
}) => [getSuggestedUsersForDiscoverQueryKeyRoot, props.limit]
|
||||||
|
|
||||||
export function useGetSuggestedUsersForDiscoverQuery(props: QueryProps = {}) {
|
export function useGetSuggestedUsersForDiscoverQuery(props: QueryProps = {}) {
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
const {data: preferences} = usePreferencesQuery()
|
const {data: preferences} = usePreferencesQuery()
|
||||||
|
|
||||||
return useQuery({
|
return useQuery({
|
||||||
|
enabled: props.enabled ?? true,
|
||||||
staleTime: STALE.MINUTES.THREE,
|
staleTime: STALE.MINUTES.THREE,
|
||||||
queryKey: createGetSuggestedUsersForDiscoverQueryKey(props),
|
queryKey: createGetSuggestedUsersForDiscoverQueryKey({limit: props.limit}),
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const contentLangs = getContentLanguages().join(',')
|
const contentLangs = getContentLanguages().join(',')
|
||||||
const userInterests = aggregateUserInterests(preferences)
|
const userInterests = aggregateUserInterests(preferences)
|
||||||
@@ -73,3 +75,33 @@ export function* findAllProfilesInQueryData(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects every DID currently cached under any `getSuggestedUsersForDiscover`
|
||||||
|
* query. Used by the See More dialog's For You tab to filter out profiles the
|
||||||
|
* viewer has already been shown by the home-feed interstitial (the endpoint
|
||||||
|
* has no cursor/offset, so we dedup client-side).
|
||||||
|
*
|
||||||
|
* The query key root is shared with `getSuggestedUsersForSeeMore`, so we
|
||||||
|
* narrow by key length (Discover keys are `[root, limit]`, SeeMore keys are
|
||||||
|
* `[root, category, limit]`).
|
||||||
|
*/
|
||||||
|
export function getAllDidsInDiscoverCache(
|
||||||
|
queryClient: QueryClient,
|
||||||
|
): Set<string> {
|
||||||
|
const dids = new Set<string>()
|
||||||
|
const responses =
|
||||||
|
queryClient.getQueriesData<AppBskyUnspeccedGetSuggestedUsersForDiscover.OutputSchema>(
|
||||||
|
{
|
||||||
|
queryKey: [getSuggestedUsersForDiscoverQueryKeyRoot],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
for (const [key, response] of responses) {
|
||||||
|
if (!response) continue
|
||||||
|
if (key.length !== 2) continue
|
||||||
|
for (const actor of response.actors) {
|
||||||
|
dids.add(actor.did)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dids
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,21 +16,27 @@ import {useAgent} from '#/state/session'
|
|||||||
export type QueryProps = {
|
export type QueryProps = {
|
||||||
category?: string | null
|
category?: string | null
|
||||||
limit?: number
|
limit?: number
|
||||||
|
enabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getSuggestedUsersForSeeMoreQueryKeyRoot =
|
export const getSuggestedUsersForSeeMoreQueryKeyRoot =
|
||||||
'unspecced-suggested-users-for-explore'
|
'unspecced-suggested-users-for-explore'
|
||||||
export const createGetSuggestedUsersForSeeMoreQueryKey = (
|
export const createGetSuggestedUsersForSeeMoreQueryKey = (props: {
|
||||||
props: QueryProps,
|
category?: string | null
|
||||||
) => [getSuggestedUsersForSeeMoreQueryKeyRoot, props.category, props.limit]
|
limit?: number
|
||||||
|
}) => [getSuggestedUsersForSeeMoreQueryKeyRoot, props.category, props.limit]
|
||||||
|
|
||||||
export function useGetSuggestedUsersForSeeMoreQuery(props: QueryProps = {}) {
|
export function useGetSuggestedUsersForSeeMoreQuery(props: QueryProps = {}) {
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
const {data: preferences} = usePreferencesQuery()
|
const {data: preferences} = usePreferencesQuery()
|
||||||
|
|
||||||
return useQuery({
|
return useQuery({
|
||||||
|
enabled: props.enabled ?? true,
|
||||||
staleTime: STALE.MINUTES.THREE,
|
staleTime: STALE.MINUTES.THREE,
|
||||||
queryKey: createGetSuggestedUsersForSeeMoreQueryKey(props),
|
queryKey: createGetSuggestedUsersForSeeMoreQueryKey({
|
||||||
|
category: props.category,
|
||||||
|
limit: props.limit,
|
||||||
|
}),
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const contentLangs = getContentLanguages().join(',')
|
const contentLangs = getContentLanguages().join(',')
|
||||||
const userInterests = aggregateUserInterests(preferences)
|
const userInterests = aggregateUserInterests(preferences)
|
||||||
|
|||||||
Reference in New Issue
Block a user