Use new endpoints for suggested users (#10185)
This commit is contained in:
+1
-1
@@ -81,7 +81,7 @@
|
||||
"icons:optimize": "svgo -f ./assets/icons"
|
||||
},
|
||||
"dependencies": {
|
||||
"@atproto/api": "^0.19.5",
|
||||
"@atproto/api": "^0.19.6",
|
||||
"@bitdrift/react-native": "^0.6.8",
|
||||
"@braintree/sanitize-url": "^6.0.2",
|
||||
"@bsky.app/alf": "^0.1.7",
|
||||
|
||||
@@ -7,7 +7,7 @@ import Animated, {
|
||||
LayoutAnimationConfig,
|
||||
LinearTransition,
|
||||
} from 'react-native-reanimated'
|
||||
import {type AppBskyFeedDefs, AtUri} from '@atproto/api'
|
||||
import {type AppBskyFeedDefs} from '@atproto/api'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
|
||||
@@ -15,11 +15,9 @@ import {type NavigationProp} from '#/lib/routes/types'
|
||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||
import {useGetPopularFeedsQuery} from '#/state/queries/feed'
|
||||
import {type FeedDescriptor} from '#/state/queries/post-feed'
|
||||
import {useProfilesQuery} from '#/state/queries/profile'
|
||||
import {useSuggestedFollowsByActorWithDismiss} from '#/state/queries/suggested-follows'
|
||||
import {useGetSuggestedUsersForDiscoverQuery} from '#/state/queries/trending/useGetSuggestedUsersForDiscoverQuery'
|
||||
import {useSession} from '#/state/session'
|
||||
import * as userActionHistory from '#/state/userActionHistory'
|
||||
import {type SeenPost} from '#/state/userActionHistory'
|
||||
import {BlockDrawerGesture} from '#/view/shell/BlockDrawerGesture'
|
||||
import {
|
||||
atoms as a,
|
||||
@@ -37,12 +35,12 @@ import {Hashtag_Stroke2_Corner0_Rounded as Hashtag} from '#/components/icons/Has
|
||||
import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
|
||||
import {InlineLinkText} from '#/components/Link'
|
||||
import * as ProfileCard from '#/components/ProfileCard'
|
||||
import {ProgressGuideList} from '#/components/ProgressGuide/List'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {type Metrics, useAnalytics} from '#/analytics'
|
||||
import {IS_IOS} from '#/env'
|
||||
import type * as bsky from '#/types/bsky'
|
||||
import {FollowDialogWithoutGuide} from './ProgressGuide/FollowDialog'
|
||||
import {ProgressGuideList} from './ProgressGuide/List'
|
||||
|
||||
const DISMISS_ANIMATION_DURATION = 200
|
||||
|
||||
@@ -109,95 +107,6 @@ export function SuggestedFeedsCardPlaceholder() {
|
||||
)
|
||||
}
|
||||
|
||||
function getRank(seenPost: SeenPost): string {
|
||||
let tier: string
|
||||
if (seenPost.feedContext === 'popfriends') {
|
||||
tier = 'a'
|
||||
} else if (seenPost.feedContext?.startsWith('cluster')) {
|
||||
tier = 'b'
|
||||
} else if (seenPost.feedContext === 'popcluster') {
|
||||
tier = 'c'
|
||||
} else if (seenPost.feedContext?.startsWith('ntpc')) {
|
||||
tier = 'd'
|
||||
} else if (seenPost.feedContext?.startsWith('t-')) {
|
||||
tier = 'e'
|
||||
} else if (seenPost.feedContext === 'nettop') {
|
||||
tier = 'f'
|
||||
} else {
|
||||
tier = 'g'
|
||||
}
|
||||
let score = Math.round(
|
||||
Math.log(
|
||||
1 + seenPost.likeCount + seenPost.repostCount + seenPost.replyCount,
|
||||
),
|
||||
)
|
||||
if (seenPost.isFollowedBy || Math.random() > 0.9) {
|
||||
score *= 2
|
||||
}
|
||||
const rank = 100 - score
|
||||
return `${tier}-${rank}`
|
||||
}
|
||||
|
||||
function sortSeenPosts(postA: SeenPost, postB: SeenPost): 0 | 1 | -1 {
|
||||
const rankA = getRank(postA)
|
||||
const rankB = getRank(postB)
|
||||
// Yes, we're comparing strings here.
|
||||
// The "larger" string means a worse rank.
|
||||
if (rankA > rankB) {
|
||||
return 1
|
||||
} else if (rankA < rankB) {
|
||||
return -1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
function useExperimentalSuggestedUsersQuery() {
|
||||
const {currentAccount} = useSession()
|
||||
const userActionSnapshot = userActionHistory.useActionHistorySnapshot()
|
||||
const dids = useMemo(() => {
|
||||
const {likes, follows, followSuggestions, seen} = userActionSnapshot
|
||||
const likeDids = likes
|
||||
.map(l => new AtUri(l))
|
||||
.map(uri => uri.host)
|
||||
.filter(did => !follows.includes(did))
|
||||
let suggestedDids: string[] = []
|
||||
if (followSuggestions.length > 0) {
|
||||
suggestedDids = [
|
||||
// It's ok if these will pick the same item (weighed by its frequency)
|
||||
/* eslint-disable react-hooks/purity */
|
||||
followSuggestions[Math.floor(Math.random() * followSuggestions.length)],
|
||||
followSuggestions[Math.floor(Math.random() * followSuggestions.length)],
|
||||
followSuggestions[Math.floor(Math.random() * followSuggestions.length)],
|
||||
followSuggestions[Math.floor(Math.random() * followSuggestions.length)],
|
||||
/* eslint-enable react-hooks/purity */
|
||||
]
|
||||
}
|
||||
const seenDids = seen
|
||||
.sort(sortSeenPosts)
|
||||
.map(l => new AtUri(l.uri))
|
||||
.map(uri => uri.host)
|
||||
return [...new Set([...suggestedDids, ...likeDids, ...seenDids])].filter(
|
||||
did => did !== currentAccount?.did,
|
||||
)
|
||||
}, [userActionSnapshot, currentAccount])
|
||||
const {data, isLoading, error} = useProfilesQuery({
|
||||
handles: dids.slice(0, 16),
|
||||
})
|
||||
|
||||
const profiles = data
|
||||
? data.profiles.filter(profile => {
|
||||
return !profile.viewer?.following
|
||||
})
|
||||
: []
|
||||
|
||||
return {
|
||||
isLoading,
|
||||
error,
|
||||
profiles: profiles.slice(0, 6),
|
||||
}
|
||||
}
|
||||
|
||||
export function SuggestedFollows({feed}: {feed: FeedDescriptor}) {
|
||||
const {currentAccount} = useSession()
|
||||
const [feedType, feedUriOrDid] = feed.split('|')
|
||||
@@ -229,11 +138,9 @@ export function SuggestedFollowsProfile({did}: {did: string}) {
|
||||
}
|
||||
|
||||
export function SuggestedFollowsHome() {
|
||||
const {
|
||||
isLoading: isSuggestionsLoading,
|
||||
profiles: experimentalProfiles,
|
||||
error: experimentalError,
|
||||
} = useExperimentalSuggestedUsersQuery()
|
||||
const {isLoading, data, error} = useGetSuggestedUsersForDiscoverQuery()
|
||||
|
||||
const profiles = data?.actors
|
||||
|
||||
const [dismissedDids, setDismissedDids] = useState<Set<string>>(new Set())
|
||||
|
||||
@@ -247,12 +154,12 @@ export function SuggestedFollowsHome() {
|
||||
recId?: string
|
||||
}> = []
|
||||
|
||||
for (const profile of experimentalProfiles) {
|
||||
result.push({actor: profile, recId: undefined})
|
||||
for (const profile of profiles ?? []) {
|
||||
result.push({actor: profile, recId: data?.recId})
|
||||
}
|
||||
|
||||
return result
|
||||
}, [experimentalProfiles])
|
||||
}, [data?.recId, profiles])
|
||||
|
||||
const filteredProfiles = useMemo(() => {
|
||||
return allProfiles.filter(p => !dismissedDids.has(p.actor.did))
|
||||
@@ -260,10 +167,10 @@ export function SuggestedFollowsHome() {
|
||||
|
||||
return (
|
||||
<ProfileGrid
|
||||
isSuggestionsLoading={isSuggestionsLoading}
|
||||
isSuggestionsLoading={isLoading}
|
||||
profiles={filteredProfiles}
|
||||
totalProfileCount={allProfiles.length}
|
||||
error={experimentalError}
|
||||
error={error}
|
||||
viewContext="feed"
|
||||
onDismiss={onDismiss}
|
||||
/>
|
||||
|
||||
@@ -8,7 +8,7 @@ import {popularInterests, useInterestsDisplayNames} from '#/lib/interests'
|
||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||
import {useActorSearch} from '#/state/queries/actor-search'
|
||||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||
import {useGetSuggestedUsersQuery} from '#/state/queries/trending/useGetSuggestedUsersQuery'
|
||||
import {useGetSuggestedUsersForSeeMoreQuery} from '#/state/queries/trending/useGetSuggestedUsersForSeeMoreQuery'
|
||||
import {useSession} from '#/state/session'
|
||||
import {type Follow10ProgressGuide} from '#/state/shell/progress-guide'
|
||||
import {type ListMethods} from '#/view/com/util/List'
|
||||
@@ -141,7 +141,7 @@ function DialogInner({guide}: {guide?: Follow10ProgressGuide}) {
|
||||
data: suggestions,
|
||||
isFetching: isFetchingSuggestions,
|
||||
error: suggestionsError,
|
||||
} = useGetSuggestedUsersQuery({
|
||||
} = useGetSuggestedUsersForSeeMoreQuery({
|
||||
category: selectedInterest,
|
||||
limit: 50,
|
||||
})
|
||||
|
||||
@@ -28,7 +28,10 @@ import {
|
||||
createGetSuggestedFeedsQueryKey,
|
||||
useGetSuggestedFeedsQuery,
|
||||
} from '#/state/queries/trending/useGetSuggestedFeedsQuery'
|
||||
import {getSuggestedUsersQueryKeyRoot} from '#/state/queries/trending/useGetSuggestedUsersQuery'
|
||||
import {
|
||||
getSuggestedUsersForExploreQueryKeyRoot,
|
||||
useGetSuggestedUsersForExploreQuery,
|
||||
} from '#/state/queries/trending/useGetSuggestedUsersForExploreQuery'
|
||||
import {createGetTrendsQueryKey} from '#/state/queries/trending/useGetTrendsQuery'
|
||||
import {
|
||||
createSuggestedStarterPacksQueryKey,
|
||||
@@ -48,7 +51,6 @@ import {ExploreInterestsCard} from '#/screens/Search/modules/ExploreInterestsCar
|
||||
import {ExploreRecommendations} from '#/screens/Search/modules/ExploreRecommendations'
|
||||
import {ExploreTrendingTopics} from '#/screens/Search/modules/ExploreTrendingTopics'
|
||||
import {ExploreTrendingVideos} from '#/screens/Search/modules/ExploreTrendingVideos'
|
||||
import {useSuggestedUsers} from '#/screens/Search/util/useSuggestedUsers'
|
||||
import {atoms as a, native, platform, useTheme} from '#/alf'
|
||||
import {Admonition} from '#/components/Admonition'
|
||||
import {Button} from '#/components/Button'
|
||||
@@ -242,9 +244,8 @@ export function Explore({
|
||||
isLoading: suggestedUsersIsLoading,
|
||||
error: suggestedUsersError,
|
||||
isRefetching: suggestedUsersIsRefetching,
|
||||
} = useSuggestedUsers({
|
||||
} = useGetSuggestedUsersForExploreQuery({
|
||||
category: selectedInterest || (useFullExperience ? null : interests[0]),
|
||||
search: !useFullExperience,
|
||||
})
|
||||
/* End special language handling */
|
||||
|
||||
@@ -316,7 +317,7 @@ export function Explore({
|
||||
queryKey: createSuggestedStarterPacksQueryKey(),
|
||||
}),
|
||||
qc.resetQueries({
|
||||
queryKey: [getSuggestedUsersQueryKeyRoot],
|
||||
queryKey: [getSuggestedUsersForExploreQueryKeyRoot],
|
||||
}),
|
||||
qc.resetQueries({
|
||||
queryKey: [useActorSearchQueryKeyRoot],
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import {useMemo} from 'react'
|
||||
|
||||
import {useInterestsDisplayNames} from '#/lib/interests'
|
||||
import {useActorSearch} from '#/state/queries/actor-search'
|
||||
import {useGetSuggestedUsersQuery} from '#/state/queries/trending/useGetSuggestedUsersQuery'
|
||||
|
||||
/**
|
||||
* Conditional hook, used in case a user is a non-english speaker, in which
|
||||
* case we fall back to searching for users instead of our more curated set.
|
||||
*/
|
||||
export function useSuggestedUsers({
|
||||
category = null,
|
||||
search = false,
|
||||
}: {
|
||||
category?: string | null
|
||||
/**
|
||||
* If true, we'll search for users using the translated value of `category`,
|
||||
* based on the user's app language setting
|
||||
*/
|
||||
search?: boolean
|
||||
}) {
|
||||
const interestsDisplayNames = useInterestsDisplayNames()
|
||||
const curated = useGetSuggestedUsersQuery({
|
||||
enabled: !search,
|
||||
category,
|
||||
})
|
||||
const searched = useActorSearch({
|
||||
enabled: !!search,
|
||||
// use user's app language translation for this value
|
||||
query: category ? interestsDisplayNames[category] : '',
|
||||
limit: 10,
|
||||
})
|
||||
|
||||
return useMemo(() => {
|
||||
if (search) {
|
||||
return {
|
||||
// we're not paginating right now
|
||||
data: searched?.data
|
||||
? {
|
||||
actors: searched.data.pages.flatMap(p => p.actors) ?? [],
|
||||
recId: undefined,
|
||||
}
|
||||
: undefined,
|
||||
isLoading: searched.isLoading,
|
||||
error: searched.error,
|
||||
isRefetching: searched.isRefetching,
|
||||
refetch: searched.refetch,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
data: curated.data,
|
||||
isLoading: curated.isLoading,
|
||||
error: curated.error,
|
||||
isRefetching: curated.isRefetching,
|
||||
refetch: curated.refetch,
|
||||
}
|
||||
}
|
||||
}, [curated, searched, search])
|
||||
}
|
||||
@@ -19,7 +19,9 @@ import {
|
||||
} from '#/state/queries/preferences'
|
||||
import {type UsePreferencesQueryResponse} from '#/state/queries/preferences/types'
|
||||
import {createGetSuggestedFeedsQueryKey} from '#/state/queries/trending/useGetSuggestedFeedsQuery'
|
||||
import {createGetSuggestedUsersQueryKey} from '#/state/queries/trending/useGetSuggestedUsersQuery'
|
||||
import {createGetSuggestedUsersForDiscoverQueryKey} from '#/state/queries/trending/useGetSuggestedUsersForDiscoverQuery'
|
||||
import {createGetSuggestedUsersForExploreQueryKey} from '#/state/queries/trending/useGetSuggestedUsersForExploreQuery'
|
||||
import {createGetSuggestedUsersForSeeMoreQueryKey} from '#/state/queries/trending/useGetSuggestedUsersForSeeMoreQuery'
|
||||
import {createSuggestedStarterPacksQueryKey} from '#/state/queries/useSuggestedStarterPacksQuery'
|
||||
import {useAgent} from '#/state/session'
|
||||
import {atoms as a, useGutters, useTheme} from '#/alf'
|
||||
@@ -120,7 +122,15 @@ function Inner({
|
||||
await Promise.all([
|
||||
qc.resetQueries({queryKey: createSuggestedStarterPacksQueryKey()}),
|
||||
qc.resetQueries({queryKey: createGetSuggestedFeedsQueryKey()}),
|
||||
qc.resetQueries({queryKey: createGetSuggestedUsersQueryKey({})}),
|
||||
qc.resetQueries({
|
||||
queryKey: createGetSuggestedUsersForDiscoverQueryKey({}),
|
||||
}),
|
||||
qc.resetQueries({
|
||||
queryKey: createGetSuggestedUsersForExploreQueryKey({}),
|
||||
}),
|
||||
qc.resetQueries({
|
||||
queryKey: createGetSuggestedUsersForSeeMoreQueryKey({}),
|
||||
}),
|
||||
])
|
||||
|
||||
Toast.show(
|
||||
|
||||
Vendored
+6
-2
@@ -26,7 +26,9 @@ import {findAllProfilesInQueryData as findAllProfilesInProfileFollowersQueryData
|
||||
import {findAllProfilesInQueryData as findAllProfilesInProfileFollowsQueryData} from '#/state/queries/profile-follows'
|
||||
import {findAllProfilesInQueryData as findAllProfilesInSuggestedFollowsQueryData} from '#/state/queries/suggested-follows'
|
||||
import {findAllProfilesInQueryData as findAllProfilesInSuggestedOnboardingUsersQueryData} from '#/state/queries/trending/useGetSuggestedOnboardingUsersQuery'
|
||||
import {findAllProfilesInQueryData as findAllProfilesInSuggestedUsersQueryData} from '#/state/queries/trending/useGetSuggestedUsersQuery'
|
||||
import {findAllProfilesInQueryData as findAllProfilesInSuggestedUsersForDiscoverQueryData} from '#/state/queries/trending/useGetSuggestedUsersForDiscoverQuery'
|
||||
import {findAllProfilesInQueryData as findAllProfilesInSuggestedUsersForExploreQueryData} from '#/state/queries/trending/useGetSuggestedUsersForExploreQuery'
|
||||
import {findAllProfilesInQueryData as findAllProfilesInSuggestedUsersForSeeMoreQueryData} from '#/state/queries/trending/useGetSuggestedUsersForSeeMoreQuery'
|
||||
import {findAllProfilesInQueryData as findAllProfilesInPostThreadV2QueryData} from '#/state/queries/usePostThread/queryCache'
|
||||
import type * as bsky from '#/types/bsky'
|
||||
import {castAsShadow, type Shadow} from './types'
|
||||
@@ -249,7 +251,9 @@ function* findProfilesInCache(
|
||||
yield* findAllProfilesInProfileFollowersQueryData(queryClient, did)
|
||||
yield* findAllProfilesInProfileFollowsQueryData(queryClient, did)
|
||||
yield* findAllProfilesInSuggestedOnboardingUsersQueryData(queryClient, did)
|
||||
yield* findAllProfilesInSuggestedUsersQueryData(queryClient, did)
|
||||
yield* findAllProfilesInSuggestedUsersForDiscoverQueryData(queryClient, did)
|
||||
yield* findAllProfilesInSuggestedUsersForExploreQueryData(queryClient, did)
|
||||
yield* findAllProfilesInSuggestedUsersForSeeMoreQueryData(queryClient, did)
|
||||
yield* findAllProfilesInSuggestedFollowsQueryData(queryClient, did)
|
||||
yield* findAllProfilesInActorSearchQueryData(queryClient, did)
|
||||
yield* findAllProfilesInListConvosQueryData(queryClient, did)
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
import {type QueryClient, useQuery} from '@tanstack/react-query'
|
||||
|
||||
import {createBskyTopicsHeader} from '#/lib/api/feed/utils'
|
||||
import {logger} from '#/logger'
|
||||
import {getContentLanguages} from '#/state/preferences/languages'
|
||||
import {STALE} from '#/state/queries'
|
||||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||
@@ -54,27 +53,8 @@ export function useGetSuggestedOnboardingUsersQuery(props: QueryProps) {
|
||||
},
|
||||
},
|
||||
)
|
||||
// FALLBACK: if no results for 'all', try again with no interests specified
|
||||
if (!props.category && data.actors.length === 0) {
|
||||
logger.error(
|
||||
`Did not get any suggested onboarding users, falling back - interests: ${overrideInterests}`,
|
||||
)
|
||||
const {data: fallbackData} =
|
||||
await agent.app.bsky.unspecced.getSuggestedOnboardingUsers(
|
||||
{
|
||||
category: props.category ?? undefined,
|
||||
limit: props.limit || 10,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Accept-Language': contentLangs,
|
||||
},
|
||||
},
|
||||
)
|
||||
return fallbackData
|
||||
}
|
||||
|
||||
return data
|
||||
return {...data, recId: data.recIdStr}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import {
|
||||
type AppBskyActorDefs,
|
||||
type AppBskyUnspeccedGetSuggestedUsersForDiscover,
|
||||
} from '@atproto/api'
|
||||
import {type QueryClient, useQuery} from '@tanstack/react-query'
|
||||
|
||||
import {
|
||||
aggregateUserInterests,
|
||||
createBskyTopicsHeader,
|
||||
} from '#/lib/api/feed/utils'
|
||||
import {getContentLanguages} from '#/state/preferences/languages'
|
||||
import {STALE} from '#/state/queries'
|
||||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||
import {useAgent} from '#/state/session'
|
||||
|
||||
export type QueryProps = {
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export const getSuggestedUsersForDiscoverQueryKeyRoot =
|
||||
'unspecced-suggested-users-for-explore'
|
||||
export const createGetSuggestedUsersForDiscoverQueryKey = (
|
||||
props: QueryProps,
|
||||
) => [getSuggestedUsersForDiscoverQueryKeyRoot, props.limit]
|
||||
|
||||
export function useGetSuggestedUsersForDiscoverQuery(props: QueryProps = {}) {
|
||||
const agent = useAgent()
|
||||
const {data: preferences} = usePreferencesQuery()
|
||||
|
||||
return useQuery({
|
||||
staleTime: STALE.MINUTES.THREE,
|
||||
queryKey: createGetSuggestedUsersForDiscoverQueryKey(props),
|
||||
queryFn: async () => {
|
||||
const contentLangs = getContentLanguages().join(',')
|
||||
const userInterests = aggregateUserInterests(preferences)
|
||||
|
||||
const {data} =
|
||||
await agent.app.bsky.unspecced.getSuggestedUsersForDiscover(
|
||||
{
|
||||
limit: props.limit || 10,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
...createBskyTopicsHeader(userInterests),
|
||||
'Accept-Language': contentLangs,
|
||||
},
|
||||
},
|
||||
)
|
||||
return {...data, recId: data.recIdStr}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function* findAllProfilesInQueryData(
|
||||
queryClient: QueryClient,
|
||||
did: string,
|
||||
): Generator<AppBskyActorDefs.ProfileView, void> {
|
||||
const responses =
|
||||
queryClient.getQueriesData<AppBskyUnspeccedGetSuggestedUsersForDiscover.OutputSchema>(
|
||||
{
|
||||
queryKey: [getSuggestedUsersForDiscoverQueryKeyRoot],
|
||||
},
|
||||
)
|
||||
for (const [_key, response] of responses) {
|
||||
if (!response) {
|
||||
continue
|
||||
}
|
||||
|
||||
for (const actor of response.actors) {
|
||||
if (actor.did === did) {
|
||||
yield actor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
-36
@@ -1,6 +1,6 @@
|
||||
import {
|
||||
type AppBskyActorDefs,
|
||||
type AppBskyUnspeccedGetSuggestedUsers,
|
||||
type AppBskyUnspeccedGetSuggestedUsersForExplore,
|
||||
} from '@atproto/api'
|
||||
import {type QueryClient, useQuery} from '@tanstack/react-query'
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
aggregateUserInterests,
|
||||
createBskyTopicsHeader,
|
||||
} from '#/lib/api/feed/utils'
|
||||
import {logger} from '#/logger'
|
||||
import {getContentLanguages} from '#/state/preferences/languages'
|
||||
import {STALE} from '#/state/queries'
|
||||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||
@@ -17,29 +16,26 @@ import {useAgent} from '#/state/session'
|
||||
export type QueryProps = {
|
||||
category?: string | null
|
||||
limit?: number
|
||||
enabled?: boolean
|
||||
}
|
||||
|
||||
export const getSuggestedUsersQueryKeyRoot = 'unspecced-suggested-users'
|
||||
export const createGetSuggestedUsersQueryKey = (props: QueryProps) => [
|
||||
getSuggestedUsersQueryKeyRoot,
|
||||
props.category,
|
||||
props.limit,
|
||||
]
|
||||
export const getSuggestedUsersForExploreQueryKeyRoot =
|
||||
'unspecced-suggested-users-for-explore'
|
||||
export const createGetSuggestedUsersForExploreQueryKey = (
|
||||
props: QueryProps,
|
||||
) => [getSuggestedUsersForExploreQueryKeyRoot, props.category, props.limit]
|
||||
|
||||
export function useGetSuggestedUsersQuery(props: QueryProps) {
|
||||
export function useGetSuggestedUsersForExploreQuery(props: QueryProps = {}) {
|
||||
const agent = useAgent()
|
||||
const {data: preferences} = usePreferencesQuery()
|
||||
|
||||
return useQuery({
|
||||
enabled: !!preferences && props.enabled !== false,
|
||||
staleTime: STALE.MINUTES.THREE,
|
||||
queryKey: createGetSuggestedUsersQueryKey(props),
|
||||
queryKey: createGetSuggestedUsersForExploreQueryKey(props),
|
||||
queryFn: async () => {
|
||||
const contentLangs = getContentLanguages().join(',')
|
||||
const userInterests = aggregateUserInterests(preferences)
|
||||
|
||||
const {data} = await agent.app.bsky.unspecced.getSuggestedUsers(
|
||||
const {data} = await agent.app.bsky.unspecced.getSuggestedUsersForExplore(
|
||||
{
|
||||
category: props.category ?? undefined,
|
||||
limit: props.limit || 10,
|
||||
@@ -51,27 +47,8 @@ export function useGetSuggestedUsersQuery(props: QueryProps) {
|
||||
},
|
||||
},
|
||||
)
|
||||
// FALLBACK: if no results for 'all', try again with no interests specified
|
||||
if (!props.category && data.actors.length === 0) {
|
||||
logger.error(
|
||||
`Did not get any suggested users, falling back - interests: ${userInterests}`,
|
||||
)
|
||||
const {data: fallbackData} =
|
||||
await agent.app.bsky.unspecced.getSuggestedUsers(
|
||||
{
|
||||
category: props.category ?? undefined,
|
||||
limit: props.limit || 10,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Accept-Language': contentLangs,
|
||||
},
|
||||
},
|
||||
)
|
||||
return fallbackData
|
||||
}
|
||||
|
||||
return data
|
||||
return {...data, recId: data.recIdStr}
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -81,9 +58,11 @@ export function* findAllProfilesInQueryData(
|
||||
did: string,
|
||||
): Generator<AppBskyActorDefs.ProfileView, void> {
|
||||
const responses =
|
||||
queryClient.getQueriesData<AppBskyUnspeccedGetSuggestedUsers.OutputSchema>({
|
||||
queryKey: [getSuggestedUsersQueryKeyRoot],
|
||||
})
|
||||
queryClient.getQueriesData<AppBskyUnspeccedGetSuggestedUsersForExplore.OutputSchema>(
|
||||
{
|
||||
queryKey: [getSuggestedUsersForExploreQueryKeyRoot],
|
||||
},
|
||||
)
|
||||
for (const [_key, response] of responses) {
|
||||
if (!response) {
|
||||
continue
|
||||
@@ -0,0 +1,77 @@
|
||||
import {
|
||||
type AppBskyActorDefs,
|
||||
type AppBskyUnspeccedGetSuggestedUsersForSeeMore,
|
||||
} from '@atproto/api'
|
||||
import {type QueryClient, useQuery} from '@tanstack/react-query'
|
||||
|
||||
import {
|
||||
aggregateUserInterests,
|
||||
createBskyTopicsHeader,
|
||||
} from '#/lib/api/feed/utils'
|
||||
import {getContentLanguages} from '#/state/preferences/languages'
|
||||
import {STALE} from '#/state/queries'
|
||||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||
import {useAgent} from '#/state/session'
|
||||
|
||||
export type QueryProps = {
|
||||
category?: string | null
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export const getSuggestedUsersForSeeMoreQueryKeyRoot =
|
||||
'unspecced-suggested-users-for-explore'
|
||||
export const createGetSuggestedUsersForSeeMoreQueryKey = (
|
||||
props: QueryProps,
|
||||
) => [getSuggestedUsersForSeeMoreQueryKeyRoot, props.category, props.limit]
|
||||
|
||||
export function useGetSuggestedUsersForSeeMoreQuery(props: QueryProps = {}) {
|
||||
const agent = useAgent()
|
||||
const {data: preferences} = usePreferencesQuery()
|
||||
|
||||
return useQuery({
|
||||
staleTime: STALE.MINUTES.THREE,
|
||||
queryKey: createGetSuggestedUsersForSeeMoreQueryKey(props),
|
||||
queryFn: async () => {
|
||||
const contentLangs = getContentLanguages().join(',')
|
||||
const userInterests = aggregateUserInterests(preferences)
|
||||
|
||||
const {data} = await agent.app.bsky.unspecced.getSuggestedUsersForSeeMore(
|
||||
{
|
||||
category: props.category ?? undefined,
|
||||
limit: props.limit || 50,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
...createBskyTopicsHeader(userInterests),
|
||||
'Accept-Language': contentLangs,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
return {...data, recId: data.recIdStr}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function* findAllProfilesInQueryData(
|
||||
queryClient: QueryClient,
|
||||
did: string,
|
||||
): Generator<AppBskyActorDefs.ProfileView, void> {
|
||||
const responses =
|
||||
queryClient.getQueriesData<AppBskyUnspeccedGetSuggestedUsersForSeeMore.OutputSchema>(
|
||||
{
|
||||
queryKey: [getSuggestedUsersForSeeMoreQueryKeyRoot],
|
||||
},
|
||||
)
|
||||
for (const [_key, response] of responses) {
|
||||
if (!response) {
|
||||
continue
|
||||
}
|
||||
|
||||
for (const actor of response.actors) {
|
||||
if (actor.did === did) {
|
||||
yield actor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,14 +20,14 @@
|
||||
"@jridgewell/gen-mapping" "^0.3.0"
|
||||
"@jridgewell/trace-mapping" "^0.3.9"
|
||||
|
||||
"@atproto/api@^0.19.5":
|
||||
version "0.19.5"
|
||||
resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.19.5.tgz#6388e5d6d3a1693fe04b5f37c705682bac8601d3"
|
||||
integrity sha512-u6R5TecYJDO8l8QFN09AMuJASYnUkJ4HhYE5hg4/dha/z14a+OAil2/dli/208uM5AHPFLtlnB8kIK9XU5GgQQ==
|
||||
"@atproto/api@^0.19.6":
|
||||
version "0.19.6"
|
||||
resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.19.6.tgz#c8fae3d792fe429c900ac0ba2609d60b9a89e28b"
|
||||
integrity sha512-8L5dZvGaclB52b8msjtgDNx3uLWUY4PELA7KbFyAWBFVasCceE1txdrscCqDCLN+Fff9+Sm07OIjnjHYJXdETA==
|
||||
dependencies:
|
||||
"@atproto/common-web" "^0.4.19"
|
||||
"@atproto/lexicon" "^0.6.2"
|
||||
"@atproto/syntax" "^0.5.2"
|
||||
"@atproto/syntax" "^0.5.3"
|
||||
"@atproto/xrpc" "^0.7.7"
|
||||
await-lock "^2.2.2"
|
||||
multiformats "^9.9.0"
|
||||
@@ -73,13 +73,20 @@
|
||||
multiformats "^9.9.0"
|
||||
zod "^3.23.8"
|
||||
|
||||
"@atproto/syntax@^0.5.0", "@atproto/syntax@^0.5.1", "@atproto/syntax@^0.5.2":
|
||||
"@atproto/syntax@^0.5.0", "@atproto/syntax@^0.5.1":
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/@atproto/syntax/-/syntax-0.5.2.tgz#d4b32c9feb421ceeb5ade1fa80bc42764d51e52e"
|
||||
integrity sha512-W41szOnkppoHr0iCUrzL8gy3OD6qmDyp1UvUgmTx2oFQfgbudpz51T/gznesiCcqiUT5obfHdx4PJ+WdlEOE7Q==
|
||||
dependencies:
|
||||
tslib "^2.8.1"
|
||||
|
||||
"@atproto/syntax@^0.5.3":
|
||||
version "0.5.3"
|
||||
resolved "https://registry.yarnpkg.com/@atproto/syntax/-/syntax-0.5.3.tgz#4331d01f63fe56c374dcf95d4432a22b62271a17"
|
||||
integrity sha512-gzhlHOJHm5KXdCc17fXi1fXM81ccs5jJfNgCui84ay9JGvczxegpYHNqdMlv+iBuhtBzFIjgx6ChjRxN/kO8kQ==
|
||||
dependencies:
|
||||
tslib "^2.8.1"
|
||||
|
||||
"@atproto/xrpc@^0.7.7":
|
||||
version "0.7.7"
|
||||
resolved "https://registry.yarnpkg.com/@atproto/xrpc/-/xrpc-0.7.7.tgz#c0e3106c854cb9bc7d3129de2f31b8256eb0ed11"
|
||||
|
||||
Reference in New Issue
Block a user