From 88e6dff484634b6d2ec9edb7caf6642cf4ebeb3d Mon Sep 17 00:00:00 2001
From: kindgracekind <155408223+kindgracekind@users.noreply.github.com>
Date: Tue, 26 Aug 2025 14:16:50 -0500
Subject: Enable show less / more buttons for third party feeds (#8672)

Co-authored-by: hailey <hailey@blueskyweb.xyz>
Co-authored-by: Hailey <me@haileyok.com>
---
 src/state/feed-feedback.tsx | 98 +++++++++++++++++++++++++++++++++++++++------
 1 file changed, 86 insertions(+), 12 deletions(-)

(limited to 'src/state/feed-feedback.tsx')

diff --git a/src/state/feed-feedback.tsx b/src/state/feed-feedback.tsx
index 8b235f492..3e9c2bafa 100644
--- a/src/state/feed-feedback.tsx
+++ b/src/state/feed-feedback.tsx
@@ -10,10 +10,15 @@ import {AppState, type AppStateStatus} from 'react-native'
 import {type AppBskyFeedDefs} from '@atproto/api'
 import throttle from 'lodash.throttle'
 
-import {FEEDBACK_FEEDS, STAGING_FEEDS} from '#/lib/constants'
+import {PROD_FEEDS, STAGING_FEEDS} from '#/lib/constants'
 import {isNetworkError} from '#/lib/hooks/useCleanError'
 import {logEvent} from '#/lib/statsig/statsig'
 import {Logger} from '#/logger'
+import {
+  type FeedSourceFeedInfo,
+  type FeedSourceInfo,
+  isFeedSourceFeedInfo,
+} from '#/state/queries/feed'
 import {
   type FeedDescriptor,
   type FeedPostSliceItem,
@@ -21,6 +26,42 @@ import {
 import {getItemsForFeedback} from '#/view/com/posts/PostFeed'
 import {useAgent} from './session'
 
+export const FEEDBACK_FEEDS = [...PROD_FEEDS, ...STAGING_FEEDS]
+
+export const PASSIVE_FEEDBACK_INTERACTIONS = [
+  'app.bsky.feed.defs#clickthroughItem',
+  'app.bsky.feed.defs#clickthroughAuthor',
+  'app.bsky.feed.defs#clickthroughReposter',
+  'app.bsky.feed.defs#clickthroughEmbed',
+  'app.bsky.feed.defs#interactionSeen',
+] as const
+
+export type PassiveFeedbackInteraction =
+  (typeof PASSIVE_FEEDBACK_INTERACTIONS)[number]
+
+export const DIRECT_FEEDBACK_INTERACTIONS = [
+  'app.bsky.feed.defs#requestLess',
+  'app.bsky.feed.defs#requestMore',
+] as const
+
+export type DirectFeedbackInteraction =
+  (typeof DIRECT_FEEDBACK_INTERACTIONS)[number]
+
+export const ALL_FEEDBACK_INTERACTIONS = [
+  ...PASSIVE_FEEDBACK_INTERACTIONS,
+  ...DIRECT_FEEDBACK_INTERACTIONS,
+] as const
+
+export type FeedbackInteraction = (typeof ALL_FEEDBACK_INTERACTIONS)[number]
+
+export function isFeedbackInteraction(
+  interactionEvent: string,
+): interactionEvent is FeedbackInteraction {
+  return ALL_FEEDBACK_INTERACTIONS.includes(
+    interactionEvent as FeedbackInteraction,
+  )
+}
+
 const logger = Logger.create(Logger.Context.FeedFeedback)
 
 export type StateContext = {
@@ -28,6 +69,7 @@ export type StateContext = {
   onItemSeen: (item: any) => void
   sendInteraction: (interaction: AppBskyFeedDefs.Interaction) => void
   feedDescriptor: FeedDescriptor | undefined
+  feedSourceInfo: FeedSourceInfo | undefined
 }
 
 const stateContext = createContext<StateContext>({
@@ -35,15 +77,27 @@ const stateContext = createContext<StateContext>({
   onItemSeen: (_item: any) => {},
   sendInteraction: (_interaction: AppBskyFeedDefs.Interaction) => {},
   feedDescriptor: undefined,
+  feedSourceInfo: undefined,
 })
 stateContext.displayName = 'FeedFeedbackContext'
 
 export function useFeedFeedback(
-  feed: FeedDescriptor | undefined,
+  feedSourceInfo: FeedSourceInfo | undefined,
   hasSession: boolean,
 ) {
   const agent = useAgent()
-  const enabled = isDiscoverFeed(feed) && hasSession
+
+  const feed =
+    !!feedSourceInfo && isFeedSourceFeedInfo(feedSourceInfo)
+      ? feedSourceInfo
+      : undefined
+
+  const isDiscover = isDiscoverFeed(feed?.feedDescriptor)
+  const acceptsInteractions = Boolean(isDiscover || feed?.acceptsInteractions)
+  const proxyDid = feed?.view?.did
+  const enabled =
+    Boolean(feed) && Boolean(proxyDid) && acceptsInteractions && hasSession
+  const enabledInteractions = getEnabledInteractions(enabled, feed, isDiscover)
 
   const queue = useRef<Set<string>>(new Set())
   const history = useRef<
@@ -66,19 +120,24 @@ export function useFeedFeedback(
     const interactions = Array.from(queue.current).map(toInteraction)
     queue.current.clear()
 
-    let proxyDid = 'did:web:discover.bsky.app'
-    if (STAGING_FEEDS.includes(feed ?? '')) {
-      proxyDid = 'did:web:algo.pop2.bsky.app'
+    const interactionsToSend = interactions.filter(
+      interaction =>
+        interaction.event &&
+        isFeedbackInteraction(interaction.event) &&
+        enabledInteractions.includes(interaction.event),
+    )
+
+    if (interactionsToSend.length === 0) {
+      return
     }
 
     // Send to the feed
     agent.app.bsky.feed
       .sendInteractions(
-        {interactions},
+        {interactions: interactionsToSend},
         {
           encoding: 'application/json',
           headers: {
-            // TODO when we start sending to other feeds, we need to grab their DID -prf
             'atproto-proxy': `${proxyDid}#bsky_fg`,
           },
         },
@@ -93,10 +152,13 @@ export function useFeedFeedback(
     if (aggregatedStats.current === null) {
       aggregatedStats.current = createAggregatedStats()
     }
-    sendOrAggregateInteractionsForStats(aggregatedStats.current, interactions)
+    sendOrAggregateInteractionsForStats(
+      aggregatedStats.current,
+      interactionsToSend,
+    )
     throttledFlushAggregatedStats()
     logger.debug('flushed')
-  }, [agent, throttledFlushAggregatedStats, feed])
+  }, [agent, throttledFlushAggregatedStats, proxyDid, enabledInteractions])
 
   const sendToFeed = useMemo(
     () =>
@@ -168,7 +230,8 @@ export function useFeedFeedback(
       // call on various events
       // queues the event to be sent with the throttled sendToFeed call
       sendInteraction,
-      feedDescriptor: feed,
+      feedDescriptor: feed?.feedDescriptor,
+      feedSourceInfo: typeof feed === 'object' ? feed : undefined,
     }
   }, [enabled, onItemSeen, sendInteraction, feed])
 }
@@ -184,10 +247,21 @@ export function useFeedFeedbackContext() {
 // take advantage of the feed feedback API. Until that's in
 // place, we're hardcoding it to the discover feed.
 // -prf
-function isDiscoverFeed(feed?: FeedDescriptor) {
+export function isDiscoverFeed(feed?: FeedDescriptor) {
   return !!feed && FEEDBACK_FEEDS.includes(feed)
 }
 
+function getEnabledInteractions(
+  enabled: boolean,
+  feed: FeedSourceFeedInfo | undefined,
+  isDiscover: boolean,
+): readonly FeedbackInteraction[] {
+  if (!enabled || !feed) {
+    return []
+  }
+  return isDiscover ? ALL_FEEDBACK_INTERACTIONS : DIRECT_FEEDBACK_INTERACTIONS
+}
+
 function toString(interaction: AppBskyFeedDefs.Interaction): string {
   return `${interaction.item}|${interaction.event}|${
     interaction.feedContext || ''
-- 
cgit 1.4.1

