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,
} from '#/state/feed-feedback'
import {useFeedFeedback} from '#/state/feed-feedback'
import {useFeedInfo} from '#/state/queries/feed'
import {usePostLikeMutationQueue} from '#/state/queries/post'
import {
type AuthorFilter,
@@ -199,7 +200,9 @@ function Feed() {
throw new Error(`Invalid video feed params ${JSON.stringify(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} =
usePostFeedQuery(
feedDesc,
+24 -67
View File
@@ -16,11 +16,14 @@ import {
FEEDBACK_FEEDS,
type FeedbackInteraction,
isFeedbackInteraction,
STAGING_FEEDS,
} from '#/lib/constants'
import {logEvent} from '#/lib/statsig/statsig'
import {Logger} from '#/logger'
import {type FeedSourceInfo} from '#/state/queries/feed'
import {
type FeedSourceFeedInfo,
type FeedSourceInfo,
isFeedSourceFeedInfo,
} from '#/state/queries/feed'
import {
type FeedDescriptor,
type FeedPostSliceItem,
@@ -46,24 +49,21 @@ const stateContext = createContext<StateContext>({
feedSourceInfo: undefined,
})
// All info needed to send feedback to a feed
type FeedInfo = {
feedDescriptor: FeedDescriptor
acceptsInteractions: boolean
isDiscover: boolean
proxyDid: string
}
export function useFeedFeedback(
feed: FeedSourceInfo | FeedDescriptor | undefined,
feedSourceInfo: FeedSourceInfo | undefined,
hasSession: boolean,
) {
const agent = useAgent()
const feedInfo = feed ? buildFeedInfo(feed) : null
const enabled = !!feedInfo && feedInfo.acceptsInteractions && hasSession
const feed =
!!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 history = useRef<
@@ -97,6 +97,8 @@ export function useFeedFeedback(
return
}
const proxyDid = feed?.view?.did
// Send to the feed
agent.app.bsky.feed
.sendInteractions(
@@ -104,7 +106,7 @@ export function useFeedFeedback(
{
encoding: 'application/json',
headers: {
'atproto-proxy': `${feedInfo?.proxyDid}#bsky_fg`,
'atproto-proxy': `${proxyDid}#bsky_fg`,
},
},
)
@@ -122,7 +124,7 @@ export function useFeedFeedback(
)
throttledFlushAggregatedStats()
logger.debug('flushed')
}, [agent, throttledFlushAggregatedStats, feedInfo, enabledInteractions])
}, [agent, throttledFlushAggregatedStats, feed, enabledInteractions])
const sendToFeed = useMemo(
() =>
@@ -194,10 +196,10 @@ export function useFeedFeedback(
// call on various events
// queues the event to be sent with the throttled sendToFeed call
sendInteraction,
feedDescriptor: feedInfo?.feedDescriptor,
feedDescriptor: feed?.feedDescriptor,
feedSourceInfo: typeof feed === 'object' ? feed : undefined,
}
}, [enabled, onItemSeen, sendInteraction, feedInfo, feed])
}, [enabled, onItemSeen, sendInteraction, feed])
}
export const FeedFeedbackProvider = stateContext.Provider
@@ -215,60 +217,15 @@ function isDiscoverFeed(feed?: FeedDescriptor) {
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(
enabled: boolean,
feedInfo: FeedInfo | null,
feed: FeedSourceFeedInfo | undefined,
isDiscover: boolean,
): readonly FeedbackInteraction[] {
if (!enabled || !feedInfo) {
if (!enabled || !feed) {
return []
}
return feedInfo.isDiscover
? ALL_FEEDBACK_INTERACTIONS
: DIRECT_FEEDBACK_INTERACTIONS
return isDiscover ? ALL_FEEDBACK_INTERACTIONS : DIRECT_FEEDBACK_INTERACTIONS
}
function toString(interaction: AppBskyFeedDefs.Interaction): string {
+29
View File
@@ -74,6 +74,12 @@ export type FeedSourceListInfo = {
export type FeedSourceInfo = FeedSourceFeedInfo | FeedSourceListInfo
export function isFeedSourceFeedInfo(
feed: FeedSourceInfo,
): feed is FeedSourceFeedInfo {
return feed.type === 'feed'
}
const feedSourceInfoQueryKeyRoot = 'getFeedSourceInfo'
export const feedSourceInfoQueryKey = ({uri}: {uri: string}) => [
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) {
precacheResolvedUri(
queryClient,