Get feed source info for video feeds

This commit is contained in:
Grace Kind
2025-08-02 21:47:51 -05:00
parent 11c31c665b
commit bfdb2d7327
3 changed files with 57 additions and 68 deletions
+4 -1
View File
@@ -70,6 +70,7 @@ import {
useFeedFeedbackContext, useFeedFeedbackContext,
} from '#/state/feed-feedback' } from '#/state/feed-feedback'
import {useFeedFeedback} from '#/state/feed-feedback' import {useFeedFeedback} from '#/state/feed-feedback'
import {useFeedInfo} from '#/state/queries/feed'
import {usePostLikeMutationQueue} from '#/state/queries/post' import {usePostLikeMutationQueue} from '#/state/queries/post'
import { import {
type AuthorFilter, type AuthorFilter,
@@ -199,7 +200,9 @@ function Feed() {
throw new Error(`Invalid video feed params ${JSON.stringify(params)}`) throw new Error(`Invalid video feed params ${JSON.stringify(params)}`)
} }
}, [params]) }, [params])
const feedFeedback = useFeedFeedback(feedDesc, hasSession) const feedUri = params.type === 'feedgen' ? params.uri : undefined
const {data: feedInfo} = useFeedInfo(feedUri)
const feedFeedback = useFeedFeedback(feedInfo, hasSession)
const {data, error, hasNextPage, isFetchingNextPage, fetchNextPage} = const {data, error, hasNextPage, isFetchingNextPage, fetchNextPage} =
usePostFeedQuery( usePostFeedQuery(
feedDesc, feedDesc,
+24 -67
View File
@@ -16,11 +16,14 @@ import {
FEEDBACK_FEEDS, FEEDBACK_FEEDS,
type FeedbackInteraction, type FeedbackInteraction,
isFeedbackInteraction, isFeedbackInteraction,
STAGING_FEEDS,
} from '#/lib/constants' } from '#/lib/constants'
import {logEvent} from '#/lib/statsig/statsig' import {logEvent} from '#/lib/statsig/statsig'
import {Logger} from '#/logger' import {Logger} from '#/logger'
import {type FeedSourceInfo} from '#/state/queries/feed' import {
type FeedSourceFeedInfo,
type FeedSourceInfo,
isFeedSourceFeedInfo,
} from '#/state/queries/feed'
import { import {
type FeedDescriptor, type FeedDescriptor,
type FeedPostSliceItem, type FeedPostSliceItem,
@@ -46,24 +49,21 @@ const stateContext = createContext<StateContext>({
feedSourceInfo: undefined, feedSourceInfo: undefined,
}) })
// All info needed to send feedback to a feed
type FeedInfo = {
feedDescriptor: FeedDescriptor
acceptsInteractions: boolean
isDiscover: boolean
proxyDid: string
}
export function useFeedFeedback( export function useFeedFeedback(
feed: FeedSourceInfo | FeedDescriptor | undefined, feedSourceInfo: FeedSourceInfo | undefined,
hasSession: boolean, hasSession: boolean,
) { ) {
const agent = useAgent() const agent = useAgent()
const feedInfo = feed ? buildFeedInfo(feed) : null const feed =
const enabled = !!feedInfo && feedInfo.acceptsInteractions && hasSession !!feedSourceInfo && isFeedSourceFeedInfo(feedSourceInfo)
? feedSourceInfo
: undefined
const enabledInteractions = getEnabledInteractions(enabled, feedInfo) const isDiscover = isDiscoverFeed(feed?.feedDescriptor)
const acceptsInteractions = isDiscover || (feed?.acceptsInteractions ?? false)
const enabled = !!feed && acceptsInteractions && hasSession
const enabledInteractions = getEnabledInteractions(enabled, feed, isDiscover)
const queue = useRef<Set<string>>(new Set()) const queue = useRef<Set<string>>(new Set())
const history = useRef< const history = useRef<
@@ -97,6 +97,8 @@ export function useFeedFeedback(
return return
} }
const proxyDid = feed?.view?.did
// Send to the feed // Send to the feed
agent.app.bsky.feed agent.app.bsky.feed
.sendInteractions( .sendInteractions(
@@ -104,7 +106,7 @@ export function useFeedFeedback(
{ {
encoding: 'application/json', encoding: 'application/json',
headers: { headers: {
'atproto-proxy': `${feedInfo?.proxyDid}#bsky_fg`, 'atproto-proxy': `${proxyDid}#bsky_fg`,
}, },
}, },
) )
@@ -122,7 +124,7 @@ export function useFeedFeedback(
) )
throttledFlushAggregatedStats() throttledFlushAggregatedStats()
logger.debug('flushed') logger.debug('flushed')
}, [agent, throttledFlushAggregatedStats, feedInfo, enabledInteractions]) }, [agent, throttledFlushAggregatedStats, feed, enabledInteractions])
const sendToFeed = useMemo( const sendToFeed = useMemo(
() => () =>
@@ -194,10 +196,10 @@ export function useFeedFeedback(
// call on various events // call on various events
// queues the event to be sent with the throttled sendToFeed call // queues the event to be sent with the throttled sendToFeed call
sendInteraction, sendInteraction,
feedDescriptor: feedInfo?.feedDescriptor, feedDescriptor: feed?.feedDescriptor,
feedSourceInfo: typeof feed === 'object' ? feed : undefined, feedSourceInfo: typeof feed === 'object' ? feed : undefined,
} }
}, [enabled, onItemSeen, sendInteraction, feedInfo, feed]) }, [enabled, onItemSeen, sendInteraction, feed])
} }
export const FeedFeedbackProvider = stateContext.Provider export const FeedFeedbackProvider = stateContext.Provider
@@ -215,60 +217,15 @@ function isDiscoverFeed(feed?: FeedDescriptor) {
return !!feed && FEEDBACK_FEEDS.includes(feed) return !!feed && FEEDBACK_FEEDS.includes(feed)
} }
function buildFeedInfo(feed: FeedSourceInfo | FeedDescriptor): FeedInfo | null {
// Build FeedInfo object from either a feed source info object or a feed descriptor string
// Only discover feeds are supported for feed descriptor strings
if (typeof feed === 'object') {
if (feed.type !== 'feed') {
// Don't send feedback to non-feed sources
return null
}
const feedDescriptor = feed.feedDescriptor
const isDiscover = isDiscoverFeed(feed.feedDescriptor)
const proxyDid = feed.view?.did
if (!proxyDid) {
logger.warn(`No proxy did found for feed: ${feedDescriptor}.`)
return null
}
let acceptsInteractions = feed.acceptsInteractions ?? false
if (isDiscover) {
// Discover feed doesn't have acceptsInteractions: true, so hardcode this for now
acceptsInteractions = true
}
return {
feedDescriptor,
isDiscover,
proxyDid,
acceptsInteractions,
}
} else {
const feedDescriptor = feed
const isDiscover = isDiscoverFeed(feedDescriptor)
if (!isDiscover) {
return null
}
const proxyDid = STAGING_FEEDS.includes(feedDescriptor)
? 'did:web:algo.pop2.bsky.app'
: 'did:web:discover.bsky.app'
return {
feedDescriptor,
isDiscover,
proxyDid,
acceptsInteractions: true,
}
}
}
function getEnabledInteractions( function getEnabledInteractions(
enabled: boolean, enabled: boolean,
feedInfo: FeedInfo | null, feed: FeedSourceFeedInfo | undefined,
isDiscover: boolean,
): readonly FeedbackInteraction[] { ): readonly FeedbackInteraction[] {
if (!enabled || !feedInfo) { if (!enabled || !feed) {
return [] return []
} }
return feedInfo.isDiscover return isDiscover ? ALL_FEEDBACK_INTERACTIONS : DIRECT_FEEDBACK_INTERACTIONS
? ALL_FEEDBACK_INTERACTIONS
: DIRECT_FEEDBACK_INTERACTIONS
} }
function toString(interaction: AppBskyFeedDefs.Interaction): string { function toString(interaction: AppBskyFeedDefs.Interaction): string {
+29
View File
@@ -74,6 +74,12 @@ export type FeedSourceListInfo = {
export type FeedSourceInfo = FeedSourceFeedInfo | FeedSourceListInfo export type FeedSourceInfo = FeedSourceFeedInfo | FeedSourceListInfo
export function isFeedSourceFeedInfo(
feed: FeedSourceInfo,
): feed is FeedSourceFeedInfo {
return feed.type === 'feed'
}
const feedSourceInfoQueryKeyRoot = 'getFeedSourceInfo' const feedSourceInfoQueryKeyRoot = 'getFeedSourceInfo'
export const feedSourceInfoQueryKey = ({uri}: {uri: string}) => [ export const feedSourceInfoQueryKey = ({uri}: {uri: string}) => [
feedSourceInfoQueryKeyRoot, feedSourceInfoQueryKeyRoot,
@@ -621,6 +627,29 @@ export function useSavedFeeds() {
}) })
} }
const feedInfoQueryKeyRoot = 'feedInfo'
export function useFeedInfo(feedUri: string | undefined) {
const agent = useAgent()
return useQuery({
staleTime: STALE.INFINITY,
queryKey: [feedInfoQueryKeyRoot, feedUri],
queryFn: async () => {
if (!feedUri) {
return undefined
}
const res = await agent.app.bsky.feed.getFeedGenerator({
feed: feedUri,
})
const feedSourceInfo = hydrateFeedGenerator(res.data.view)
return feedSourceInfo
},
})
}
function precacheFeed(queryClient: QueryClient, hydratedFeed: FeedSourceInfo) { function precacheFeed(queryClient: QueryClient, hydratedFeed: FeedSourceInfo) {
precacheResolvedUri( precacheResolvedUri(
queryClient, queryClient,