Add more data to trending metrics (#11565)

This commit is contained in:
DS Boyce
2026-09-04 16:11:20 -07:00
committed by GitHub
parent 1556804f78
commit e595243b1f
6 changed files with 48 additions and 15 deletions
+2
View File
@@ -774,12 +774,14 @@ export type Events = {
} }
'trendingTopic:seen': { 'trendingTopic:seen': {
context: 'sidebar' | 'interstitial' | 'explore' context: 'sidebar' | 'interstitial' | 'explore'
feedUri?: string
recId?: string recId?: string
rank: number rank: number
feedSliceIndex?: number feedSliceIndex?: number
} }
'trendingTopic:click': { 'trendingTopic:click': {
context: 'sidebar' | 'interstitial' | 'explore' context: 'sidebar' | 'interstitial' | 'explore'
feedUri?: string
recId?: string recId?: string
rank: number rank: number
feedSliceIndex?: number feedSliceIndex?: number
+19 -2
View File
@@ -5,7 +5,7 @@ import {Trans, useLingui} from '@lingui/react/macro'
import {PressableScale} from '#/lib/custom-animations/PressableScale' import {PressableScale} from '#/lib/custom-animations/PressableScale'
import {useCallOnce} from '#/lib/once' import {useCallOnce} from '#/lib/once'
// import {makeProfileLink} from '#/lib/routes/links' // import {makeProfileLink} from '#/lib/routes/links'
// import {feedUriToHref} from '#/lib/strings/url-helpers' import {makeRecordUri} from '#/lib/strings/url-helpers'
import {atoms as a, native, useTheme} from '#/alf' import {atoms as a, native, useTheme} from '#/alf'
import {Link as InternalLink, type LinkProps} from '#/components/Link' import {Link as InternalLink, type LinkProps} from '#/components/Link'
import * as Prompt from '#/components/Prompt' import * as Prompt from '#/components/Prompt'
@@ -72,7 +72,8 @@ export function TrendingTopicLink({
recId?: string recId?: string
} & Omit<LinkProps, 'to' | 'label'>) { } & Omit<LinkProps, 'to' | 'label'>) {
const topic = useTopic(raw) const topic = useTopic(raw)
useTrendingTopicSeen(metricContext, rank, recId) const feedUri = getTrendingTopicFeedUri(raw)
useTrendingTopicSeen(metricContext, feedUri, rank, recId)
return ( return (
<InternalLink <InternalLink
@@ -87,6 +88,7 @@ export function TrendingTopicLink({
export function useTrendingTopicSeen( export function useTrendingTopicSeen(
context: Metrics['trendingTopic:seen']['context'], context: Metrics['trendingTopic:seen']['context'],
feedUri: string | undefined,
rank: number, rank: number,
recId?: string, recId?: string,
feedSliceIndex?: number, feedSliceIndex?: number,
@@ -95,6 +97,7 @@ export function useTrendingTopicSeen(
const trackSeen = useCallOnce(() => { const trackSeen = useCallOnce(() => {
ax.metric('trendingTopic:seen', { ax.metric('trendingTopic:seen', {
context, context,
feedUri,
rank, rank,
feedSliceIndex, feedSliceIndex,
recId, recId,
@@ -106,6 +109,20 @@ export function useTrendingTopicSeen(
}, [trackSeen]) }, [trackSeen])
} }
export function getTrendingTopicFeedUri(
topic: app.bsky.unspecced.defs.TrendView,
): string | undefined {
const match = topic.link.match(/^\/profile\/([^/]+)\/feed\/([^/?#]+)/)
if (!match) return undefined
return makeRecordUri(
decodeURIComponent(match[1]),
'app.bsky.feed.generator',
decodeURIComponent(match[2]),
)
}
type ParsedTrendingTopic = type ParsedTrendingTopic =
| { | {
type: 'topic' | 'tag' | 'starter-pack' | 'unknown' type: 'topic' | 'tag' | 'starter-pack' | 'unknown'
@@ -27,6 +27,7 @@ import {Link} from '#/components/Link'
import * as Prompt from '#/components/Prompt' import * as Prompt from '#/components/Prompt'
import {SubtleHover} from '#/components/SubtleHover' import {SubtleHover} from '#/components/SubtleHover'
import { import {
getTrendingTopicFeedUri,
TrendingTopicsPrompt, TrendingTopicsPrompt,
useTrendingTopicSeen, useTrendingTopicSeen,
} from '#/components/TrendingTopics' } from '#/components/TrendingTopics'
@@ -154,6 +155,7 @@ function Inner({feedSliceIndex}: {feedSliceIndex: number}) {
onPress={() => { onPress={() => {
ax.metric('trendingTopic:click', { ax.metric('trendingTopic:click', {
context: 'interstitial', context: 'interstitial',
feedUri: getTrendingTopicFeedUri(trend),
rank, rank,
feedSliceIndex, feedSliceIndex,
recId: trending.recId, recId: trending.recId,
@@ -195,7 +197,13 @@ function TrendRow({
const actors = useModerateTrendingActors(trend.actors) const actors = useModerateTrendingActors(trend.actors)
const formattedPostCount = formatCount(i18n, trend.postCount) const formattedPostCount = formatCount(i18n, trend.postCount)
useTrendingTopicSeen('interstitial', rank, recId, feedSliceIndex) useTrendingTopicSeen(
'interstitial',
getTrendingTopicFeedUri(trend),
rank,
recId,
feedSliceIndex,
)
return ( return (
<Link <Link
@@ -26,6 +26,7 @@ import * as Prompt from '#/components/Prompt'
import {RichText} from '#/components/RichText' import {RichText} from '#/components/RichText'
import {SubtleHover} from '#/components/SubtleHover' import {SubtleHover} from '#/components/SubtleHover'
import { import {
getTrendingTopicFeedUri,
TrendingTopicsPrompt, TrendingTopicsPrompt,
useTrendingTopicSeen, useTrendingTopicSeen,
} from '#/components/TrendingTopics' } from '#/components/TrendingTopics'
@@ -95,6 +96,7 @@ function Inner() {
onPress={() => { onPress={() => {
ax.metric('trendingTopic:click', { ax.metric('trendingTopic:click', {
context: 'explore', context: 'explore',
feedUri: getTrendingTopicFeedUri(trend),
rank, rank,
recId: trending.recId, recId: trending.recId,
}) })
@@ -134,7 +136,7 @@ export function TrendRow({
const actors = useModerateTrendingActors(trend.actors) const actors = useModerateTrendingActors(trend.actors)
const formattedPostCount = formatCount(i18n, trend.postCount) const formattedPostCount = formatCount(i18n, trend.postCount)
useTrendingTopicSeen('explore', rank, recId) useTrendingTopicSeen('explore', getTrendingTopicFeedUri(trend), rank, recId)
const description = useMemo(() => { const description = useMemo(() => {
if (!trend.description) return if (!trend.description) return
+13 -11
View File
@@ -10,12 +10,8 @@ import {AppState, type AppStateStatus} from 'react-native'
import {type AtUriString, type DidString} from '@atproto/syntax' import {type AtUriString, type DidString} from '@atproto/syntax'
import throttle from 'lodash.throttle' import throttle from 'lodash.throttle'
import {PROD_FEEDS, STAGING_FEEDS} from '#/lib/constants' import {PROD_FEEDS, STAGING_FEEDS, TRENDING_DID} from '#/lib/constants'
import { import {type FeedSourceInfo, isFeedSourceFeedInfo} from '#/state/queries/feed'
type FeedSourceFeedInfo,
type FeedSourceInfo,
isFeedSourceFeedInfo,
} from '#/state/queries/feed'
import { import {
type FeedDescriptor, type FeedDescriptor,
type FeedPostSliceItem, type FeedPostSliceItem,
@@ -144,7 +140,7 @@ export function useFeedFeedback(
const interactionsToSend = interactions.filter( const interactionsToSend = interactions.filter(
interaction => interaction =>
interaction.event && interaction.event &&
isInteractionAllowed(enabled, feed, interaction.event), isInteractionAllowed(enabled, feed?.feedDescriptor, interaction.event),
) )
if (interactionsToSend.length === 0) { if (interactionsToSend.length === 0) {
@@ -263,22 +259,28 @@ export function useFeedFeedbackContext() {
// TODO // TODO
// We will introduce a permissions framework for 3p feeds to // We will introduce a permissions framework for 3p feeds to
// take advantage of the feed feedback API. Until that's in // take advantage of the feed feedback API. Until that's in
// place, we're hardcoding it to the discover feed. // place, we're hardcoding it to the discover and trending feeds.
// -prf // -prf
export function isDiscoverFeed(feed?: FeedDescriptor) { export function isDiscoverFeed(feed?: FeedDescriptor) {
return !!feed && FEEDBACK_FEEDS.includes(feed) return !!feed && FEEDBACK_FEEDS.includes(feed)
} }
export function isTrendingFeed(feed?: FeedDescriptor) {
return !!feed && feed.startsWith(`feedgen|at://${TRENDING_DID}/`)
}
function isInteractionAllowed( function isInteractionAllowed(
enabled: boolean, enabled: boolean,
feed: FeedSourceFeedInfo | undefined, feed: FeedDescriptor | undefined,
interaction: app.bsky.feed.defs.Interaction['event'], interaction: app.bsky.feed.defs.Interaction['event'],
) { ) {
if (!enabled || !feed) { if (!enabled || !feed) {
return false return false
} }
const isDiscover = isDiscoverFeed(feed.feedDescriptor) if (isDiscoverFeed(feed) || isTrendingFeed(feed)) {
return isDiscover ? true : THIRD_PARTY_ALLOWED_INTERACTIONS.has(interaction) return true
}
return THIRD_PARTY_ALLOWED_INTERACTIONS.has(interaction)
} }
function toString(interaction: app.bsky.feed.defs.Interaction): string { function toString(interaction: app.bsky.feed.defs.Interaction): string {
@@ -17,6 +17,7 @@ import {Trending3_Stroke2_Corner1_Rounded as TrendingIcon} from '#/components/ic
import {Link} from '#/components/Link' import {Link} from '#/components/Link'
import * as Prompt from '#/components/Prompt' import * as Prompt from '#/components/Prompt'
import { import {
getTrendingTopicFeedUri,
TrendingTopicLink, TrendingTopicLink,
TrendingTopicsPrompt, TrendingTopicsPrompt,
} from '#/components/TrendingTopics' } from '#/components/TrendingTopics'
@@ -133,6 +134,7 @@ function Inner() {
onPress={() => { onPress={() => {
ax.metric('trendingTopic:click', { ax.metric('trendingTopic:click', {
context: 'sidebar', context: 'sidebar',
feedUri: getTrendingTopicFeedUri(topic),
rank, rank,
recId: trending.recId, recId: trending.recId,
}) })