diff options
Diffstat (limited to 'src/screens/Search')
-rw-r--r-- | src/screens/Search/Explore.tsx | 106 | ||||
-rw-r--r-- | src/screens/Search/modules/ExploreSuggestedAccounts.tsx | 20 | ||||
-rw-r--r-- | src/screens/Search/util/useSuggestedUsers.ts | 56 |
3 files changed, 146 insertions, 36 deletions
diff --git a/src/screens/Search/Explore.tsx b/src/screens/Search/Explore.tsx index 1236005a3..8050d7f73 100644 --- a/src/screens/Search/Explore.tsx +++ b/src/screens/Search/Explore.tsx @@ -8,13 +8,16 @@ import { import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useQueryClient} from '@tanstack/react-query' +import * as bcp47Match from 'bcp-47-match' import {useGate} from '#/lib/statsig/statsig' import {cleanError} from '#/lib/strings/errors' import {sanitizeHandle} from '#/lib/strings/handles' import {logger} from '#/logger' import {type MetricEvents} from '#/logger/metrics' +import {useLanguagePrefs} from '#/state/preferences/languages' import {useModerationOpts} from '#/state/preferences/moderation-opts' +import {RQKEY_ROOT_PAGINATED as useActorSearchPaginatedQueryKeyRoot} from '#/state/queries/actor-search' import { type FeedPreviewItem, useFeedPreviews, @@ -26,10 +29,7 @@ import { createGetSuggestedFeedsQueryKey, useGetSuggestedFeedsQuery, } from '#/state/queries/trending/useGetSuggestedFeedsQuery' -import { - getSuggestedUsersQueryKeyRoot, - useGetSuggestedUsersQuery, -} from '#/state/queries/trending/useGetSuggestedUsersQuery' +import {getSuggestedUsersQueryKeyRoot} from '#/state/queries/trending/useGetSuggestedUsersQuery' import {createGetTrendsQueryKey} from '#/state/queries/trending/useGetTrendsQuery' import { createSuggestedStarterPacksQueryKey, @@ -43,6 +43,10 @@ import {List} from '#/view/com/util/List' import {FeedFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn' import { + popularInterests, + useInterestsDisplayNames, +} from '#/screens/Onboarding/state' +import { StarterPackCard, StarterPackCardSkeleton, } from '#/screens/Search/components/StarterPackCard' @@ -50,6 +54,7 @@ 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' @@ -64,6 +69,7 @@ import {Trending2_Stroke2_Corner2_Rounded as Graph} from '#/components/icons/Tre import {UserCircle_Stroke2_Corner0_Rounded as Person} from '#/components/icons/UserCircle' import {Loader} from '#/components/Loader' import * as ProfileCard from '#/components/ProfileCard' +import {boostInterests} from '#/components/ProgressGuide/FollowDialog' import {SubtleHover} from '#/components/SubtleHover' import {Text} from '#/components/Typography' import * as ModuleHeader from './components/ModuleHeader' @@ -135,6 +141,7 @@ type ExploreScreenItems = metricsTag: MetricEvents['explore:module:searchButtonPress']['module'] tab: 'user' | 'profile' | 'feed' } + hideDefaultTab?: boolean } | { type: 'trendingTopics' @@ -151,7 +158,7 @@ type ExploreScreenItems = | { type: 'profile' key: string - profile: AppBskyActorDefs.ProfileViewBasic + profile: AppBskyActorDefs.ProfileView recId?: number } | { @@ -212,14 +219,31 @@ export function Explore({ const gate = useGate() const guide = useProgressGuide('follow-10') const [selectedInterest, setSelectedInterest] = useState<string | null>(null) + + /* + * Begin special language handling + */ + const {contentLanguages} = useLanguagePrefs() + const useFullExperience = useMemo(() => { + if (contentLanguages.length === 0) return true + return bcp47Match.basicFilter('en', contentLanguages).length > 0 + }, [contentLanguages]) + const personalizedInterests = preferences?.interests?.tags + const interestsDisplayNames = useInterestsDisplayNames() + const interests = Object.keys(interestsDisplayNames) + .sort(boostInterests(popularInterests)) + .sort(boostInterests(personalizedInterests)) const { data: suggestedUsers, isLoading: suggestedUsersIsLoading, error: suggestedUsersError, isRefetching: suggestedUsersIsRefetching, - } = useGetSuggestedUsersQuery({ - category: selectedInterest, + } = useSuggestedUsers({ + category: selectedInterest || (useFullExperience ? null : interests[0]), + search: !useFullExperience, }) + /* End special language handling */ + const { data: feeds, hasNextPage: hasNextFeedsPage, @@ -227,7 +251,7 @@ export function Explore({ isFetchingNextPage: isFetchingNextFeedsPage, error: feedsError, fetchNextPage: fetchNextFeedsPage, - } = useGetPopularFeedsQuery({limit: 10}) + } = useGetPopularFeedsQuery({limit: 10, enabled: useFullExperience}) const interestsNux = useNux(Nux.ExploreInterestsCard) const showInterestsNux = interestsNux.status === 'ready' && !interestsNux.nux?.completed @@ -237,7 +261,7 @@ export function Explore({ isLoading: isLoadingSuggestedSPs, error: suggestedSPsError, isRefetching: isRefetchingSuggestedSPs, - } = useSuggestedStarterPacksQuery() + } = useSuggestedStarterPacksQuery({enabled: useFullExperience}) const isLoadingMoreFeeds = isFetchingNextFeedsPage && !isLoadingFeeds const [hasPressedLoadMoreFeeds, setHasPressedLoadMoreFeeds] = useState(false) @@ -260,7 +284,9 @@ export function Explore({ hasPressedLoadMoreFeeds, ]) - const {data: suggestedFeeds} = useGetSuggestedFeedsQuery() + const {data: suggestedFeeds} = useGetSuggestedFeedsQuery({ + enabled: useFullExperience, + }) const { data: feedPreviewSlices, query: { @@ -270,7 +296,7 @@ export function Explore({ hasNextPage: hasNextPageFeedPreviews, error: feedPreviewSlicesError, }, - } = useFeedPreviews(suggestedFeeds?.feeds ?? []) + } = useFeedPreviews(suggestedFeeds?.feeds ?? [], useFullExperience) const qc = useQueryClient() const [isPTR, setIsPTR] = useState(false) @@ -287,6 +313,9 @@ export function Explore({ queryKey: [getSuggestedUsersQueryKeyRoot], }), await qc.resetQueries({ + queryKey: [useActorSearchPaginatedQueryKeyRoot], + }), + await qc.resetQueries({ queryKey: createGetSuggestedFeedsQueryKey(), }), ]) @@ -334,6 +363,7 @@ export function Explore({ metricsTag: 'suggestedAccounts', tab: 'user', }, + hideDefaultTab: !useFullExperience, }) if (suggestedUsersIsLoading || suggestedUsersIsRefetching) { @@ -353,6 +383,7 @@ export function Explore({ let seen = new Set() const profileItems: ExploreScreenItems[] = [] for (const actor of suggestedUsers.actors) { + // checking for following still necessary if search data is used if (!seen.has(actor.did) && !actor.viewer?.following) { seen.add(actor.did) profileItems.push({ @@ -369,7 +400,7 @@ export function Explore({ key: 'profileEmpty', }) } else { - if (selectedInterest === null) { + if (selectedInterest === null && useFullExperience) { // First "For You" tab, only show 5 to keep screen short i.push(...profileItems.slice(0, 5)) } else { @@ -395,6 +426,7 @@ export function Explore({ suggestedUsersIsRefetching, suggestedUsersError, selectedInterest, + useFullExperience, ]) const suggestedFeedsModule = useMemo(() => { const i: ExploreScreenItems[] = [] @@ -565,26 +597,31 @@ export function Explore({ i.push(topBorder) i.push(...interestsNuxModule) - if (isNewUser) { - i.push(...suggestedFollowsModule) - i.push(...suggestedStarterPacksModule) - i.push({ - type: 'header', - key: 'trending-topics-header', - title: _(msg`Trending topics`), - icon: Graph, - bottomBorder: true, - }) - i.push(trendingTopicsModule) + + if (useFullExperience) { + if (isNewUser) { + i.push(...suggestedFollowsModule) + i.push(...suggestedStarterPacksModule) + i.push({ + type: 'header', + key: 'trending-topics-header', + title: _(msg`Trending topics`), + icon: Graph, + bottomBorder: true, + }) + i.push(trendingTopicsModule) + } else { + i.push(trendingTopicsModule) + i.push(...suggestedFollowsModule) + i.push(...suggestedStarterPacksModule) + } + if (gate('explore_show_suggested_feeds')) { + i.push(...suggestedFeedsModule) + } + i.push(...feedPreviewsModule) } else { - i.push(trendingTopicsModule) i.push(...suggestedFollowsModule) - i.push(...suggestedStarterPacksModule) } - if (gate('explore_show_suggested_feeds')) { - i.push(...suggestedFeedsModule) - } - i.push(...feedPreviewsModule) return i }, [ @@ -598,6 +635,7 @@ export function Explore({ feedPreviewsModule, interestsNuxModule, gate, + useFullExperience, ]) const renderItem = useCallback( @@ -641,6 +679,7 @@ export function Explore({ <SuggestedAccountsTabBar selectedInterest={selectedInterest} onSelectInterest={setSelectedInterest} + hideDefaultTab={item.hideDefaultTab} /> </View> ) @@ -672,7 +711,13 @@ export function Explore({ return ( <View style={[a.px_lg, a.pb_lg]}> <Admonition> - <Trans>No results for "{selectedInterest}".</Trans> + {selectedInterest ? ( + <Trans> + No results for "{interestsDisplayNames[selectedInterest]}". + </Trans> + ) : ( + <Trans>No results.</Trans> + )} </Admonition> </View> ) @@ -876,6 +921,7 @@ export function Explore({ focusSearchInput, moderationOpts, selectedInterest, + interestsDisplayNames, _, fetchNextPageFeedPreviews, ], diff --git a/src/screens/Search/modules/ExploreSuggestedAccounts.tsx b/src/screens/Search/modules/ExploreSuggestedAccounts.tsx index 8d66dfbc1..f91877143 100644 --- a/src/screens/Search/modules/ExploreSuggestedAccounts.tsx +++ b/src/screens/Search/modules/ExploreSuggestedAccounts.tsx @@ -58,9 +58,11 @@ export function useLoadEnoughProfiles({ export function SuggestedAccountsTabBar({ selectedInterest, onSelectInterest, + hideDefaultTab, }: { selectedInterest: string | null onSelectInterest: (interest: string | null) => void + hideDefaultTab?: boolean }) { const {_} = useLingui() const interestsDisplayNames = useInterestsDisplayNames() @@ -72,8 +74,10 @@ export function SuggestedAccountsTabBar({ return ( <BlockDrawerGesture> <Tabs - interests={['all', ...interests]} - selectedInterest={selectedInterest || 'all'} + interests={hideDefaultTab ? interests : ['all', ...interests]} + selectedInterest={ + selectedInterest || (hideDefaultTab ? interests[0] : 'all') + } onSelectTab={tab => { logger.metric( 'explore:suggestedAccounts:tabPressed', @@ -83,10 +87,14 @@ export function SuggestedAccountsTabBar({ onSelectInterest(tab === 'all' ? null : tab) }} hasSearchText={false} - interestsDisplayNames={{ - all: _(msg`For You`), - ...interestsDisplayNames, - }} + interestsDisplayNames={ + hideDefaultTab + ? interestsDisplayNames + : { + all: _(msg`For You`), + ...interestsDisplayNames, + } + } TabComponent={Tab} contentContainerStyle={[ { diff --git a/src/screens/Search/util/useSuggestedUsers.ts b/src/screens/Search/util/useSuggestedUsers.ts new file mode 100644 index 000000000..aa29dad8c --- /dev/null +++ b/src/screens/Search/util/useSuggestedUsers.ts @@ -0,0 +1,56 @@ +import {useMemo} from 'react' + +import {useActorSearchPaginated} from '#/state/queries/actor-search' +import {useGetSuggestedUsersQuery} from '#/state/queries/trending/useGetSuggestedUsersQuery' +import {useInterestsDisplayNames} from '#/screens/Onboarding/state' + +/** + * 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 = useActorSearchPaginated({ + 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) ?? [], + } + : undefined, + isLoading: searched.isLoading, + error: searched.error, + isRefetching: searched.isRefetching, + } + } else { + return { + data: curated.data, + isLoading: curated.isLoading, + error: curated.error, + isRefetching: curated.isRefetching, + } + } + }, [curated, searched, search]) +} |