diff --git a/src/analytics/metrics/types.ts b/src/analytics/metrics/types.ts
index aa525e6495..ea68ea01e6 100644
--- a/src/analytics/metrics/types.ts
+++ b/src/analytics/metrics/types.ts
@@ -510,7 +510,7 @@ export type Events = {
| 'ProgressGuide'
location: 'Card' | 'Profile' | 'FollowAll'
recSource?: 'Search'
- recId?: number | string
+ recId?: string
position: number
suggestedDid: string
category: string | null
@@ -523,7 +523,7 @@ export type Events = {
| 'ProfileHeader'
| 'Onboarding'
| 'SeeMoreSuggestedUsers'
- recId?: number | string
+ recId?: string
position: number
suggestedDid: string
category: string | null
@@ -538,7 +538,7 @@ export type Events = {
| 'SeeMoreSuggestedUsers'
| 'ProgressGuide'
recSource?: 'Search'
- recId?: number | string
+ recId?: string
position: number
suggestedDid: string
category: string | null
@@ -550,11 +550,11 @@ export type Events = {
| 'ProfileInterstitial'
| 'ProfileHeader'
| 'Onboarding'
- recId?: number | string
+ recId?: string
}
'suggestedUser:dismiss': {
logContext: 'DiscoverInterstitial' | 'ProfileInterstitial' | 'ProfileHeader'
- recId?: number | string
+ recId?: string
position: number
suggestedDid: string
}
@@ -743,9 +743,7 @@ export type Events = {
}
'trendingTopic:click': {
context: 'sidebar' | 'interstitial' | 'explore'
- }
- 'recommendedTopic:click': {
- context: 'explore'
+ recId?: string
}
'trendingVideos:show': {
context: 'settings'
diff --git a/src/components/TrendingTopics.tsx b/src/components/TrendingTopics.tsx
index 8d35e126d4..4b045c6e3e 100644
--- a/src/components/TrendingTopics.tsx
+++ b/src/components/TrendingTopics.tsx
@@ -1,143 +1,20 @@
import {useMemo} from 'react'
-import {View} from 'react-native'
-import {type AtUri} from '@atproto/api'
+import {type AppBskyUnspeccedDefs, type AtUri} from '@atproto/api'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {PressableScale} from '#/lib/custom-animations/PressableScale'
// import {makeProfileLink} from '#/lib/routes/links'
// import {feedUriToHref} from '#/lib/strings/url-helpers'
-// import {Hashtag_Stroke2_Corner0_Rounded as Hashtag} from '#/components/icons/Hashtag'
-// import {CloseQuote_Filled_Stroke2_Corner0_Rounded as Quote} from '#/components/icons/Quote'
-// import {UserAvatar} from '#/view/com/util/UserAvatar'
-import {type TrendingTopic} from '#/state/queries/trending/useTrendingTopics'
-import {atoms as a, native, useTheme, type ViewStyleProp} from '#/alf'
-import {StarterPack as StarterPackIcon} from '#/components/icons/StarterPack'
+import {native} from '#/alf'
import {Link as InternalLink, type LinkProps} from '#/components/Link'
-import {Text} from '#/components/Typography'
-
-export function TrendingTopic({
- topic: raw,
- size,
- style,
- hovered,
-}: {
- topic: TrendingTopic
- size?: 'large' | 'small'
- hovered?: boolean
-} & ViewStyleProp) {
- const topic = useTopic(raw)
-
- const isSmall = size === 'small'
- const hasIcon = topic.type === 'starter-pack' && !isSmall
- const iconSize = 20
-
- return (
-
- {hasIcon && topic.type === 'starter-pack' && (
-
- )}
-
- {/*
-
- {topic.type === 'tag' ? (
-
- ) : topic.type === 'topic' ? (
-
- ) : topic.type === 'feed' ? (
-
- ) : (
-
- )}
-
- */}
-
-
- {topic.displayName}
-
-
- )
-}
-
-export function TrendingTopicSkeleton({
- size = 'large',
- index = 0,
-}: {
- size?: 'large' | 'small'
- index?: number
-}) {
- const t = useTheme()
- const isSmall = size === 'small'
- return (
-
- )
-}
export function TrendingTopicLink({
topic: raw,
children,
...rest
}: {
- topic: TrendingTopic
+ topic: AppBskyUnspeccedDefs.TrendView
} & Omit) {
const topic = useTopic(raw)
@@ -168,7 +45,9 @@ type ParsedTrendingTopic =
uri: AtUri
}
-export function useTopic(raw: TrendingTopic): ParsedTrendingTopic {
+export function useTopic(
+ raw: AppBskyUnspeccedDefs.TrendView,
+): ParsedTrendingTopic {
const {_} = useLingui()
return useMemo(() => {
const {topic: displayName, link} = raw
diff --git a/src/components/interstitials/Trending.tsx b/src/components/interstitials/Trending.tsx
index 98e8f77b2a..94c4b37dd6 100644
--- a/src/components/interstitials/Trending.tsx
+++ b/src/components/interstitials/Trending.tsx
@@ -7,7 +7,7 @@ import {
useTrendingSettings,
useTrendingSettingsApi,
} from '#/state/preferences/trending'
-import {useTrendingTopics} from '#/state/queries/trending/useTrendingTopics'
+import {useGetTrendsQuery} from '#/state/queries/trending/useGetTrendsQuery'
import {useTrendingConfig} from '#/state/service-config'
import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
import {BlockDrawerGesture} from '#/view/shell/BlockDrawerGesture'
@@ -20,6 +20,8 @@ import {TrendingTopicLink} from '#/components/TrendingTopics'
import {Text} from '#/components/Typography'
import {useAnalytics} from '#/analytics'
+const TRENDING_LIMIT = 14
+
export function TrendingInterstitial() {
const {enabled} = useTrendingConfig()
const {trendingDisabled} = useTrendingSettings()
@@ -33,8 +35,15 @@ export function Inner() {
const gutters = useGutters([0, 'base', 0, 'base'])
const trendingPrompt = Prompt.usePromptControl()
const {setTrendingDisabled} = useTrendingSettingsApi()
- const {data: trending, error, isLoading} = useTrendingTopics()
- const noTopics = !isLoading && !error && !trending?.topics?.length
+ const {
+ data: trending,
+ error,
+ isLoading,
+ } = useGetTrendsQuery({
+ limit: TRENDING_LIMIT,
+ refetchOnWindowFocus: true,
+ })
+ const noTopics = !isLoading && !error && !trending?.trends?.length
const onConfirmHide = useCallback(() => {
ax.metric('trendingTopics:hide', {context: 'interstitial'})
@@ -88,15 +97,16 @@ export function Inner() {
{' '}
- ) : !trending?.topics ? null : (
+ ) : !trending?.trends ? null : (
<>
- {trending.topics.map(topic => (
+ {trending.trends.map(topic => (
{
ax.metric('trendingTopic:click', {
context: 'interstitial',
+ recId: trending.recId,
})
}}>
diff --git a/src/screens/Onboarding/StepSuggestedAccounts/index.tsx b/src/screens/Onboarding/StepSuggestedAccounts/index.tsx
index eaa68d9254..514e892895 100644
--- a/src/screens/Onboarding/StepSuggestedAccounts/index.tsx
+++ b/src/screens/Onboarding/StepSuggestedAccounts/index.tsx
@@ -370,7 +370,7 @@ function SuggestedProfileCard({
category: string | null
onSeen: (did: string, position: number) => void
recSource?: 'Search'
- recId?: number | string
+ recId?: string
}) {
const t = useTheme()
const ax = useAnalytics()
diff --git a/src/screens/Search/Explore.tsx b/src/screens/Search/Explore.tsx
index 95f58645ad..fc6f99bd55 100644
--- a/src/screens/Search/Explore.tsx
+++ b/src/screens/Search/Explore.tsx
@@ -48,7 +48,6 @@ import {
StarterPackCardSkeleton,
} from '#/screens/Search/components/StarterPackCard'
import {ExploreInterestsCard} from '#/screens/Search/modules/ExploreInterestsCard'
-import {ExploreRecommendations} from '#/screens/Search/modules/ExploreRecommendations'
import {ExploreTrendingTopics} from '#/screens/Search/modules/ExploreTrendingTopics'
import {ExploreTrendingVideos} from '#/screens/Search/modules/ExploreTrendingVideos'
import {atoms as a, native, platform, useTheme} from '#/alf'
@@ -154,10 +153,6 @@ type ExploreScreenItems =
type: 'trendingVideos'
key: string
}
- | {
- type: 'recommendations'
- key: string
- }
| {
type: 'profile'
key: string
@@ -807,9 +802,6 @@ export function Explore({
case 'trendingVideos': {
return
}
- case 'recommendations': {
- return
- }
case 'profile': {
return (
: null
-}
-
-function Inner() {
- const t = useTheme()
- const ax = useAnalytics()
- const gutters = useGutters([0, 'compact'])
- const {data: trending, error, isLoading} = useTrendingTopics()
- const noRecs = !isLoading && !error && !trending?.suggested?.length
- const allFeeds = trending?.suggested && isAllFeeds(trending.suggested)
-
- return error || noRecs ? null : (
- <>
-
-
-
-
-
- Recommended
-
-
- {!allFeeds ? (
-
-
- Content from across the network we think you might like.
-
-
- ) : (
-
- Feeds we think you might like.
-
- )}
-
-
-
-
-
- {isLoading ? (
- Array(RECOMMENDATIONS_COUNT)
- .fill(0)
- .map((_, i) => )
- ) : !trending?.suggested ? null : (
- <>
- {trending.suggested.map(topic => (
- {
- ax.metric('recommendedTopic:click', {context: 'explore'})
- }}>
- {({hovered}) => (
-
- )}
-
- ))}
- >
- )}
-
-
- >
- )
-}
-
-function isAllFeeds(topics: AppBskyUnspeccedDefs.TrendingTopic[]) {
- return topics.every(topic => {
- const segments = topic.link.split('/').slice(1)
- return segments[0] === 'profile' && segments[2] === 'feed'
- })
-}
diff --git a/src/screens/Search/modules/ExploreTrendingTopics.tsx b/src/screens/Search/modules/ExploreTrendingTopics.tsx
index 6ae7132f18..5a3f4ab7f4 100644
--- a/src/screens/Search/modules/ExploreTrendingTopics.tsx
+++ b/src/screens/Search/modules/ExploreTrendingTopics.tsx
@@ -45,7 +45,10 @@ function Inner() {
trend={trend}
rank={index + 1}
onPress={() => {
- ax.metric('trendingTopic:click', {context: 'explore'})
+ ax.metric('trendingTopic:click', {
+ context: 'explore',
+ recId: trending.recId,
+ })
}}
/>
))}
diff --git a/src/state/queries/trending/useGetTrendsQuery.ts b/src/state/queries/trending/useGetTrendsQuery.ts
index c670802aa7..21fa738f28 100644
--- a/src/state/queries/trending/useGetTrendsQuery.ts
+++ b/src/state/queries/trending/useGetTrendsQuery.ts
@@ -6,6 +6,7 @@ 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'
@@ -13,24 +14,43 @@ import {useAgent} from '#/state/session'
export const DEFAULT_LIMIT = 5
-export const createGetTrendsQueryKey = () => ['trends']
+type QueryProps = {
+ limit?: number
+ refetchOnWindowFocus?: boolean
+}
-export function useGetTrendsQuery() {
+function dedupe(trends: T[]): T[] {
+ const seen = new Set()
+ return trends.filter(trend => {
+ if (seen.has(trend.link)) return false
+ seen.add(trend.link)
+ return true
+ })
+}
+
+export const createGetTrendsQueryKey = (props: QueryProps = {}) => [
+ 'trends',
+ props.limit ?? DEFAULT_LIMIT,
+]
+
+export function useGetTrendsQuery(props: QueryProps = {}) {
const agent = useAgent()
const {data: preferences} = usePreferencesQuery()
+ const limit = props.limit ?? DEFAULT_LIMIT
const mutedWords = useMemo(() => {
return preferences?.moderationPrefs?.mutedWords || []
}, [preferences?.moderationPrefs])
return useQuery({
enabled: !!preferences,
+ refetchOnWindowFocus: props.refetchOnWindowFocus,
staleTime: STALE.MINUTES.THREE,
- queryKey: createGetTrendsQueryKey(),
+ queryKey: createGetTrendsQueryKey({limit}),
queryFn: async () => {
const contentLangs = getContentLanguages().join(',')
const {data} = await agent.app.bsky.unspecced.getTrends(
{
- limit: DEFAULT_LIMIT,
+ limit,
},
{
headers: {
@@ -39,17 +59,23 @@ export function useGetTrendsQuery() {
},
},
)
+ if (!data.recIdStr) {
+ logger.debug('useGetTrendsQuery response missing recIdStr')
+ }
return data
},
select: useCallback(
(data: AppBskyUnspeccedGetTrends.OutputSchema) => {
return {
- trends: (data.trends ?? []).filter(t => {
- return !hasMutedWord({
- mutedWords,
- text: t.topic + ' ' + t.displayName + ' ' + t.category,
- })
- }),
+ recId: data.recIdStr,
+ trends: dedupe(
+ (data.trends ?? []).filter(t => {
+ return !hasMutedWord({
+ mutedWords,
+ text: `${t.topic} ${t.displayName} ${t.category}`,
+ })
+ }),
+ ),
}
},
[mutedWords],
diff --git a/src/state/queries/trending/useTrendingTopics.ts b/src/state/queries/trending/useTrendingTopics.ts
deleted file mode 100644
index 81b25e5f94..0000000000
--- a/src/state/queries/trending/useTrendingTopics.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import {useCallback, useMemo} from 'react'
-import {type AppBskyUnspeccedDefs, hasMutedWord} from '@atproto/api'
-import {useQuery} from '@tanstack/react-query'
-
-import {STALE} from '#/state/queries'
-import {usePreferencesQuery} from '#/state/queries/preferences'
-import {useAgent} from '#/state/session'
-
-export type TrendingTopic = AppBskyUnspeccedDefs.TrendingTopic
-
-type Response = {
- topics: TrendingTopic[]
- suggested: TrendingTopic[]
-}
-
-export const DEFAULT_LIMIT = 14
-
-function dedup(topics: TrendingTopic[]): TrendingTopic[] {
- const seen = new Set()
- return topics.filter(t => {
- if (seen.has(t.link)) return false
- seen.add(t.link)
- return true
- })
-}
-
-export const trendingTopicsQueryKey = ['trending-topics']
-
-export function useTrendingTopics() {
- const agent = useAgent()
- const {data: preferences} = usePreferencesQuery()
- const mutedWords = useMemo(
- () => preferences?.moderationPrefs?.mutedWords ?? [],
- [preferences?.moderationPrefs?.mutedWords],
- )
-
- return useQuery({
- refetchOnWindowFocus: true,
- staleTime: STALE.MINUTES.THREE,
- queryKey: trendingTopicsQueryKey,
- async queryFn() {
- const {data} = await agent.app.bsky.unspecced.getTrendingTopics({
- limit: DEFAULT_LIMIT,
- })
- return {
- topics: data.topics ?? [],
- suggested: data.suggested ?? [],
- }
- },
- select: useCallback(
- (data: Response) => {
- return {
- topics: dedup(
- data.topics.filter(t => {
- return !hasMutedWord({
- mutedWords,
- text: `${t.topic} ${t.displayName ?? ''} ${t.description ?? ''}`,
- })
- }),
- ),
- suggested: dedup(
- data.suggested.filter(t => {
- return !hasMutedWord({
- mutedWords,
- text: `${t.topic} ${t.displayName ?? ''} ${t.description ?? ''}`,
- })
- }),
- ),
- }
- },
- [mutedWords],
- ),
- })
-}
diff --git a/src/view/shell/desktop/SidebarTrendingTopics.tsx b/src/view/shell/desktop/SidebarTrendingTopics.tsx
index f337edb86a..aa7bf4026c 100644
--- a/src/view/shell/desktop/SidebarTrendingTopics.tsx
+++ b/src/view/shell/desktop/SidebarTrendingTopics.tsx
@@ -5,7 +5,7 @@ import {
useTrendingSettings,
useTrendingSettingsApi,
} from '#/state/preferences/trending'
-import {useTrendingTopics} from '#/state/queries/trending/useTrendingTopics'
+import {useGetTrendsQuery} from '#/state/queries/trending/useGetTrendsQuery'
import {useTrendingConfig} from '#/state/service-config'
import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonIcon} from '#/components/Button'
@@ -30,8 +30,14 @@ function Inner() {
const ax = useAnalytics()
const trendingPrompt = Prompt.usePromptControl()
const {setTrendingDisabled} = useTrendingSettingsApi()
- const {data: trending, error, isLoading} = useTrendingTopics()
- const noTopics = !isLoading && !error && !trending?.topics?.length
+ const {
+ data: trending,
+ error,
+ isLoading,
+ } = useGetTrendsQuery({
+ refetchOnWindowFocus: true,
+ })
+ const noTopics = !isLoading && !error && !trending?.trends?.length
const onConfirmHide = () => {
ax.metric('trendingTopics:hide', {context: 'sidebar'})
@@ -82,14 +88,17 @@ function Inner() {
/>
))
- ) : !trending?.topics ? null : (
+ ) : !trending?.trends ? null : (
<>
- {trending.topics.slice(0, TRENDING_LIMIT).map((topic, i) => (
+ {trending.trends.slice(0, TRENDING_LIMIT).map((topic, i) => (
{
- ax.metric('trendingTopic:click', {context: 'sidebar'})
+ ax.metric('trendingTopic:click', {
+ context: 'sidebar',
+ recId: trending.recId,
+ })
}}>
{({hovered}) => (