Enable show less / more buttons for third party feeds
This commit is contained in:
@@ -470,6 +470,7 @@ let PostMenuItems = ({
|
||||
<>
|
||||
<Menu.Divider />
|
||||
<Menu.Group>
|
||||
<Menu.LabelText>{_(msg`Tell feed provider:`)}</Menu.LabelText>
|
||||
<Menu.Item
|
||||
testID="postDropdownShowMoreBtn"
|
||||
label={_(msg`Show more like this`)}
|
||||
@@ -490,13 +491,16 @@ let PostMenuItems = ({
|
||||
)}
|
||||
|
||||
{isDiscoverDebugUser && (
|
||||
<Menu.Item
|
||||
testID="postDropdownReportMisclassificationBtn"
|
||||
label={_(msg`Assign topic for algo`)}
|
||||
onPress={onReportMisclassification}>
|
||||
<Menu.ItemText>{_(msg`Assign topic for algo`)}</Menu.ItemText>
|
||||
<Menu.ItemIcon icon={AtomIcon} position="right" />
|
||||
</Menu.Item>
|
||||
<>
|
||||
<Menu.Divider />
|
||||
<Menu.Item
|
||||
testID="postDropdownReportMisclassificationBtn"
|
||||
label={_(msg`Assign topic for algo`)}
|
||||
onPress={onReportMisclassification}>
|
||||
<Menu.ItemText>{_(msg`Assign topic for algo`)}</Menu.ItemText>
|
||||
<Menu.ItemIcon icon={AtomIcon} position="right" />
|
||||
</Menu.Item>
|
||||
</>
|
||||
)}
|
||||
|
||||
{hasSession && (
|
||||
|
||||
@@ -91,6 +91,26 @@ export const STAGING_FEEDS = [
|
||||
|
||||
export const FEEDBACK_FEEDS = [...PROD_FEEDS, ...STAGING_FEEDS]
|
||||
|
||||
export const PASSIVE_INTERACTIONS = [
|
||||
'app.bsky.feed.defs#requestLess',
|
||||
'app.bsky.feed.defs#requestMore',
|
||||
'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',
|
||||
]
|
||||
|
||||
export const DIRECT_INTERACTIONS = [
|
||||
'app.bsky.feed.defs#requestLess',
|
||||
'app.bsky.feed.defs#requestMore',
|
||||
]
|
||||
|
||||
export const ALL_INTERACTIONS = [
|
||||
...PASSIVE_INTERACTIONS,
|
||||
...DIRECT_INTERACTIONS,
|
||||
]
|
||||
|
||||
export const POST_IMG_MAX = {
|
||||
width: 2000,
|
||||
height: 2000,
|
||||
|
||||
@@ -178,7 +178,7 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({
|
||||
const {_, i18n} = useLingui()
|
||||
const {openComposer} = useOpenComposer()
|
||||
const {currentAccount, hasSession} = useSession()
|
||||
const feedFeedback = useFeedFeedback(postSource?.feed, hasSession)
|
||||
const feedFeedback = useFeedFeedback(postSource?.feedInfo, hasSession)
|
||||
|
||||
const post = postShadow
|
||||
const record = item.value.post.record
|
||||
|
||||
@@ -48,7 +48,10 @@ export function PostThread({uri}: {uri: string}) {
|
||||
const initialNumToRender = useInitialNumToRender()
|
||||
const {height: windowHeight} = useWindowDimensions()
|
||||
const anchorPostSource = useUnstablePostSource(uri)
|
||||
const feedFeedback = useFeedFeedback(anchorPostSource?.feed, hasSession)
|
||||
const feedFeedback = useFeedFeedback(
|
||||
anchorPostSource?.feedSourceInfo,
|
||||
hasSession,
|
||||
)
|
||||
|
||||
/*
|
||||
* One query to rule them all
|
||||
|
||||
@@ -169,7 +169,7 @@ export function ProfileFeedScreenInner({
|
||||
const [hasNew, setHasNew] = React.useState(false)
|
||||
const [isScrolledDown, setIsScrolledDown] = React.useState(false)
|
||||
const queryClient = useQueryClient()
|
||||
const feedFeedback = useFeedFeedback(feed, hasSession)
|
||||
const feedFeedback = useFeedFeedback(feedInfo, hasSession)
|
||||
const scrollElRef = useAnimatedRef() as ListRef
|
||||
|
||||
const onScrollToTop = useCallback(() => {
|
||||
|
||||
+88
-13
@@ -10,9 +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 {
|
||||
ALL_INTERACTIONS,
|
||||
DIRECT_INTERACTIONS,
|
||||
FEEDBACK_FEEDS,
|
||||
STAGING_FEEDS,
|
||||
} from '#/lib/constants'
|
||||
import {logEvent} from '#/lib/statsig/statsig'
|
||||
import {Logger} from '#/logger'
|
||||
import {type FeedSourceInfo} from '#/state/queries/feed'
|
||||
import {
|
||||
type FeedDescriptor,
|
||||
type FeedPostSliceItem,
|
||||
@@ -27,6 +33,7 @@ export type StateContext = {
|
||||
onItemSeen: (item: any) => void
|
||||
sendInteraction: (interaction: AppBskyFeedDefs.Interaction) => void
|
||||
feedDescriptor: FeedDescriptor | undefined
|
||||
feedSourceInfo: FeedSourceInfo | undefined
|
||||
}
|
||||
|
||||
const stateContext = createContext<StateContext>({
|
||||
@@ -34,14 +41,32 @@ const stateContext = createContext<StateContext>({
|
||||
onItemSeen: (_item: any) => {},
|
||||
sendInteraction: (_interaction: AppBskyFeedDefs.Interaction) => {},
|
||||
feedDescriptor: 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(
|
||||
feed: FeedDescriptor | undefined,
|
||||
feed: FeedSourceInfo | FeedDescriptor | undefined,
|
||||
hasSession: boolean,
|
||||
) {
|
||||
const agent = useAgent()
|
||||
const enabled = isDiscoverFeed(feed) && hasSession
|
||||
|
||||
const feedInfo = feed ? buildFeedInfo(feed) : null
|
||||
const enabled = feedInfo && feedInfo.acceptsInteractions && hasSession
|
||||
|
||||
const enabledInteractions = useMemo(() => {
|
||||
if (!enabled) {
|
||||
return []
|
||||
}
|
||||
return feedInfo.isDiscover ? ALL_INTERACTIONS : DIRECT_INTERACTIONS
|
||||
}, [enabled, feedInfo])
|
||||
|
||||
const queue = useRef<Set<string>>(new Set())
|
||||
const history = useRef<
|
||||
@@ -64,20 +89,22 @@ 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 =>
|
||||
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`,
|
||||
'atproto-proxy': `${feedInfo?.proxyDid}#bsky_fg`,
|
||||
},
|
||||
},
|
||||
)
|
||||
@@ -89,10 +116,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, feedInfo, enabledInteractions])
|
||||
|
||||
const sendToFeed = useMemo(
|
||||
() =>
|
||||
@@ -164,9 +194,10 @@ export function useFeedFeedback(
|
||||
// call on various events
|
||||
// queues the event to be sent with the throttled sendToFeed call
|
||||
sendInteraction,
|
||||
feedDescriptor: feed,
|
||||
feedDescriptor: feedInfo?.feedDescriptor,
|
||||
feedSourceInfo: typeof feed === 'object' ? feed : undefined,
|
||||
}
|
||||
}, [enabled, onItemSeen, sendInteraction, feed])
|
||||
}, [enabled, onItemSeen, sendInteraction, feedInfo, feed])
|
||||
}
|
||||
|
||||
export const FeedFeedbackProvider = stateContext.Provider
|
||||
@@ -184,6 +215,50 @@ 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 toString(interaction: AppBskyFeedDefs.Interaction): string {
|
||||
return `${interaction.item}|${interaction.event}|${
|
||||
interaction.feedContext || ''
|
||||
|
||||
@@ -48,6 +48,7 @@ export type FeedSourceFeedInfo = {
|
||||
creatorDid: string
|
||||
creatorHandle: string
|
||||
likeCount: number | undefined
|
||||
acceptsInteractions: boolean | undefined
|
||||
likeUri: string | undefined
|
||||
contentMode: AppBskyFeedDefs.GeneratorView['contentMode']
|
||||
}
|
||||
@@ -115,6 +116,7 @@ export function hydrateFeedGenerator(
|
||||
creatorDid: view.creator.did,
|
||||
creatorHandle: view.creator.handle,
|
||||
likeCount: view.likeCount,
|
||||
acceptsInteractions: view.acceptsInteractions,
|
||||
likeUri: view.viewer?.like,
|
||||
contentMode: view.contentMode,
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import {useEffect, useId, useState} from 'react'
|
||||
import {type AppBskyFeedDefs, AtUri} from '@atproto/api'
|
||||
|
||||
import {Logger} from '#/logger'
|
||||
import {type FeedDescriptor} from '#/state/queries/post-feed'
|
||||
import {type FeedSourceInfo} from '#/state/queries/feed'
|
||||
|
||||
/**
|
||||
* Separate logger for better debugging
|
||||
@@ -11,7 +11,7 @@ const logger = Logger.create(Logger.Context.PostSource)
|
||||
|
||||
export type PostSource = {
|
||||
post: AppBskyFeedDefs.FeedViewPost
|
||||
feed?: FeedDescriptor
|
||||
feedSourceInfo?: FeedSourceInfo
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,7 +17,7 @@ import {isNative} from '#/platform/detection'
|
||||
import {listenSoftReset} from '#/state/events'
|
||||
import {FeedFeedbackProvider, useFeedFeedback} from '#/state/feed-feedback'
|
||||
import {useSetHomeBadge} from '#/state/home-badge'
|
||||
import {type SavedFeedSourceInfo} from '#/state/queries/feed'
|
||||
import {type FeedSourceInfo} from '#/state/queries/feed'
|
||||
import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
|
||||
import {type FeedDescriptor, type FeedParams} from '#/state/queries/post-feed'
|
||||
import {truncateAndInvalidate} from '#/state/queries/util'
|
||||
@@ -51,7 +51,7 @@ export function FeedPage({
|
||||
renderEmptyState: () => JSX.Element
|
||||
renderEndOfFeed?: () => JSX.Element
|
||||
savedFeedConfig?: AppBskyActorDefs.SavedFeed
|
||||
feedInfo: SavedFeedSourceInfo
|
||||
feedInfo: FeedSourceInfo
|
||||
}) {
|
||||
const {hasSession} = useSession()
|
||||
const {_} = useLingui()
|
||||
@@ -61,7 +61,7 @@ export function FeedPage({
|
||||
const [isScrolledDown, setIsScrolledDown] = useState(false)
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const headerOffset = useHeaderOffset()
|
||||
const feedFeedback = useFeedFeedback(feed, hasSession)
|
||||
const feedFeedback = useFeedFeedback(feedInfo, hasSession)
|
||||
const scrollElRef = useRef<ListMethods>(null)
|
||||
const [hasNew, setHasNew] = useState(false)
|
||||
const setHomeBadge = useSetHomeBadge()
|
||||
|
||||
@@ -105,8 +105,10 @@ export function PostThread({uri}: {uri: string}) {
|
||||
)
|
||||
const headerRef = React.useRef<View | null>(null)
|
||||
const anchorPostSource = useUnstablePostSource(uri)
|
||||
const feedFeedback = useFeedFeedback(anchorPostSource?.feed, hasSession)
|
||||
|
||||
const feedFeedback = useFeedFeedback(
|
||||
anchorPostSource?.feedSourceInfo,
|
||||
hasSession,
|
||||
)
|
||||
const {data: preferences} = usePreferencesQuery()
|
||||
const {
|
||||
isFetching,
|
||||
|
||||
@@ -216,7 +216,7 @@ let PostThreadItemLoaded = ({
|
||||
anchorPostSource?: PostSource
|
||||
}): React.ReactNode => {
|
||||
const {currentAccount, hasSession} = useSession()
|
||||
const feedFeedback = useFeedFeedback(anchorPostSource?.feed, hasSession)
|
||||
const feedFeedback = useFeedFeedback(anchorPostSource?.feedInfo, hasSession)
|
||||
|
||||
const t = useTheme()
|
||||
const pal = usePalette('default')
|
||||
|
||||
@@ -180,7 +180,7 @@ let FeedItemInner = ({
|
||||
const urip = new AtUri(post.uri)
|
||||
return makeProfileLink(post.author, 'post', urip.rkey)
|
||||
}, [post.uri, post.author])
|
||||
const {sendInteraction, feedDescriptor} = useFeedFeedbackContext()
|
||||
const {sendInteraction, feedSourceInfo} = useFeedFeedbackContext()
|
||||
|
||||
const onPressReply = () => {
|
||||
sendInteraction({
|
||||
@@ -237,7 +237,7 @@ let FeedItemInner = ({
|
||||
})
|
||||
unstableCacheProfileView(queryClient, post.author)
|
||||
setUnstablePostSource(buildPostSourceKey(post.uri, post.author.handle), {
|
||||
feed: feedDescriptor,
|
||||
feedSourceInfo,
|
||||
post: {
|
||||
post,
|
||||
reason: AppBskyFeedDefs.isReasonRepost(reason) ? reason : undefined,
|
||||
|
||||
Reference in New Issue
Block a user