migrate the feed source and feed feedback calls to the appview client
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+22
-13
@@ -8,6 +8,7 @@ import {
|
|||||||
} from 'react'
|
} from 'react'
|
||||||
import {AppState, type AppStateStatus} from 'react-native'
|
import {AppState, type AppStateStatus} from 'react-native'
|
||||||
import {type AppBskyFeedDefs} from '@atproto/api'
|
import {type AppBskyFeedDefs} from '@atproto/api'
|
||||||
|
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} from '#/lib/constants'
|
||||||
@@ -22,7 +23,8 @@ import {
|
|||||||
} from '#/state/queries/post-feed'
|
} from '#/state/queries/post-feed'
|
||||||
import {getItemsForFeedback} from '#/view/com/posts/PostFeed'
|
import {getItemsForFeedback} from '#/view/com/posts/PostFeed'
|
||||||
import {useAnalytics} from '#/analytics'
|
import {useAnalytics} from '#/analytics'
|
||||||
import {useAgent} from './session'
|
import {app} from '#/lexicons'
|
||||||
|
import {useAppviewClient} from './session'
|
||||||
|
|
||||||
export const FEEDBACK_FEEDS = [...PROD_FEEDS, ...STAGING_FEEDS]
|
export const FEEDBACK_FEEDS = [...PROD_FEEDS, ...STAGING_FEEDS]
|
||||||
|
|
||||||
@@ -65,7 +67,7 @@ export function useFeedFeedback(
|
|||||||
) {
|
) {
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
const logger = ax.logger.useChild(ax.logger.Context.FeedFeedback)
|
const logger = ax.logger.useChild(ax.logger.Context.FeedFeedback)
|
||||||
const agent = useAgent()
|
const client = useAppviewClient()
|
||||||
|
|
||||||
const feed =
|
const feed =
|
||||||
!!feedSourceInfo && isFeedSourceFeedInfo(feedSourceInfo)
|
!!feedSourceInfo && isFeedSourceFeedInfo(feedSourceInfo)
|
||||||
@@ -150,16 +152,19 @@ export function useFeedFeedback(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send to the feed
|
/*
|
||||||
agent.app.bsky.feed
|
* Send to the feed. Interactions go to the feed generator rather than the
|
||||||
.sendInteractions(
|
* appview, which the agent did by setting `atproto-proxy` by hand; the
|
||||||
{interactions: interactionsToSend, feed: feed?.uri},
|
* client's per-call `service` option writes that same header.
|
||||||
|
*/
|
||||||
|
client
|
||||||
|
.call(
|
||||||
|
app.bsky.feed.sendInteractions,
|
||||||
{
|
{
|
||||||
encoding: 'application/json',
|
interactions: interactionsToSend,
|
||||||
headers: {
|
feed: feed?.uri as AtUriString | undefined,
|
||||||
'atproto-proxy': `${proxyDid}#bsky_fg`,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
{service: `${proxyDid as DidString}#bsky_fg`},
|
||||||
)
|
)
|
||||||
.catch(() => {}) // ignore upstream errors
|
.catch(() => {}) // ignore upstream errors
|
||||||
|
|
||||||
@@ -172,7 +177,7 @@ export function useFeedFeedback(
|
|||||||
)
|
)
|
||||||
throttledFlushAggregatedStats()
|
throttledFlushAggregatedStats()
|
||||||
logger.debug('flushed')
|
logger.debug('flushed')
|
||||||
}, [agent, throttledFlushAggregatedStats, proxyDid, enabled, feed])
|
}, [client, throttledFlushAggregatedStats, proxyDid, enabled, feed])
|
||||||
|
|
||||||
const sendToFeed = useMemo(
|
const sendToFeed = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@@ -283,9 +288,13 @@ function toString(interaction: AppBskyFeedDefs.Interaction): string {
|
|||||||
}|${interaction.reqId || ''}`
|
}|${interaction.reqId || ''}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function toInteraction(str: string): AppBskyFeedDefs.Interaction {
|
function toInteraction(str: string): app.bsky.feed.defs.Interaction {
|
||||||
const [item, event, feedContext, reqId] = str.split('|')
|
const [item, event, feedContext, reqId] = str.split('|')
|
||||||
return {item, event, feedContext, reqId}
|
/*
|
||||||
|
* The fields come from splitting an internally-built key, so neither the
|
||||||
|
* at-uri nor the event token is narrowed by the compiler here.
|
||||||
|
*/
|
||||||
|
return {item, event, feedContext, reqId} as app.bsky.feed.defs.Interaction
|
||||||
}
|
}
|
||||||
|
|
||||||
type AggregatedStats = {
|
type AggregatedStats = {
|
||||||
|
|||||||
+62
-50
@@ -3,11 +3,11 @@ import {
|
|||||||
type AppBskyActorDefs,
|
type AppBskyActorDefs,
|
||||||
type AppBskyFeedDefs,
|
type AppBskyFeedDefs,
|
||||||
type AppBskyGraphDefs,
|
type AppBskyGraphDefs,
|
||||||
type AppBskyUnspeccedGetPopularFeedGenerators,
|
|
||||||
AtUri,
|
AtUri,
|
||||||
moderateFeedGenerator,
|
moderateFeedGenerator,
|
||||||
RichText,
|
RichText,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
|
import {type AtUriString} from '@atproto/syntax'
|
||||||
import {t} from '@lingui/core/macro'
|
import {t} from '@lingui/core/macro'
|
||||||
import {
|
import {
|
||||||
type InfiniteData,
|
type InfiniteData,
|
||||||
@@ -26,7 +26,8 @@ import {GCTIME, STALE} from '#/state/queries'
|
|||||||
import {RQKEY as listQueryKey} from '#/state/queries/list'
|
import {RQKEY as listQueryKey} from '#/state/queries/list'
|
||||||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||||
import {createQueryKey} from '#/state/queries/util'
|
import {createQueryKey} from '#/state/queries/util'
|
||||||
import {useAgent, useSession} from '#/state/session'
|
import {useAppviewClient, useSession} from '#/state/session'
|
||||||
|
import {app} from '#/lexicons'
|
||||||
import {router} from '#/routes'
|
import {router} from '#/routes'
|
||||||
import {useModerationOpts} from '../preferences/moderation-opts'
|
import {useModerationOpts} from '../preferences/moderation-opts'
|
||||||
import {type FeedDescriptor} from './post-feed'
|
import {type FeedDescriptor} from './post-feed'
|
||||||
@@ -184,7 +185,7 @@ export function getAvatarTypeFromUri(uri: string) {
|
|||||||
|
|
||||||
export function useFeedSourceInfoQuery({uri}: {uri: string}) {
|
export function useFeedSourceInfoQuery({uri}: {uri: string}) {
|
||||||
const type = getFeedTypeFromUri(uri)
|
const type = getFeedTypeFromUri(uri)
|
||||||
const agent = useAgent()
|
const client = useAppviewClient()
|
||||||
|
|
||||||
return useQuery({
|
return useQuery({
|
||||||
staleTime: STALE.INFINITY,
|
staleTime: STALE.INFINITY,
|
||||||
@@ -193,14 +194,16 @@ export function useFeedSourceInfoQuery({uri}: {uri: string}) {
|
|||||||
let view: FeedSourceInfo
|
let view: FeedSourceInfo
|
||||||
|
|
||||||
if (type === 'feed') {
|
if (type === 'feed') {
|
||||||
const res = await agent.app.bsky.feed.getFeedGenerator({feed: uri})
|
const data = await client.call(app.bsky.feed.getFeedGenerator, {
|
||||||
view = hydrateFeedGenerator(res.data.view)
|
feed: uri as AtUriString,
|
||||||
|
})
|
||||||
|
view = hydrateFeedGenerator(data.view)
|
||||||
} else {
|
} else {
|
||||||
const res = await agent.app.bsky.graph.getList({
|
const data = await client.call(app.bsky.graph.getList, {
|
||||||
list: uri,
|
list: uri as AtUriString,
|
||||||
limit: 1,
|
limit: 1,
|
||||||
})
|
})
|
||||||
view = hydrateList(res.data.list)
|
view = hydrateList(data.list)
|
||||||
}
|
}
|
||||||
|
|
||||||
return view
|
return view
|
||||||
@@ -234,7 +237,7 @@ export function createGetPopularFeedsQueryKey(
|
|||||||
|
|
||||||
export function useGetPopularFeedsQuery(options?: GetPopularFeedsOptions) {
|
export function useGetPopularFeedsQuery(options?: GetPopularFeedsOptions) {
|
||||||
const {hasSession} = useSession()
|
const {hasSession} = useSession()
|
||||||
const agent = useAgent()
|
const client = useAppviewClient()
|
||||||
const limit = options?.limit || 10
|
const limit = options?.limit || 10
|
||||||
const {data: preferences} = usePreferencesQuery()
|
const {data: preferences} = usePreferencesQuery()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
@@ -255,24 +258,27 @@ export function useGetPopularFeedsQuery(options?: GetPopularFeedsOptions) {
|
|||||||
enabled: Boolean(moderationOpts) && options?.enabled !== false,
|
enabled: Boolean(moderationOpts) && options?.enabled !== false,
|
||||||
queryKey: createGetPopularFeedsQueryKey(options),
|
queryKey: createGetPopularFeedsQueryKey(options),
|
||||||
queryFn: async ({pageParam}) => {
|
queryFn: async ({pageParam}) => {
|
||||||
const res = await agent.app.bsky.unspecced.getPopularFeedGenerators({
|
const data = await client.call(
|
||||||
|
app.bsky.unspecced.getPopularFeedGenerators,
|
||||||
|
{
|
||||||
limit,
|
limit,
|
||||||
cursor: pageParam,
|
cursor: pageParam,
|
||||||
})
|
},
|
||||||
|
)
|
||||||
|
|
||||||
// precache feeds
|
// precache feeds
|
||||||
for (const feed of res.data.feeds) {
|
for (const feed of data.feeds) {
|
||||||
const hydratedFeed = hydrateFeedGenerator(feed)
|
const hydratedFeed = hydrateFeedGenerator(feed)
|
||||||
precacheFeed(queryClient, hydratedFeed)
|
precacheFeed(queryClient, hydratedFeed)
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.data
|
return data
|
||||||
},
|
},
|
||||||
initialPageParam: undefined as string | undefined,
|
initialPageParam: undefined as string | undefined,
|
||||||
getNextPageParam: lastPage => lastPage.cursor,
|
getNextPageParam: lastPage => lastPage.cursor,
|
||||||
select: useCallback(
|
select: useCallback(
|
||||||
(
|
(
|
||||||
data: InfiniteData<AppBskyUnspeccedGetPopularFeedGenerators.OutputSchema>,
|
data: InfiniteData<app.bsky.unspecced.getPopularFeedGenerators.$OutputBody>,
|
||||||
) => {
|
) => {
|
||||||
const {
|
const {
|
||||||
savedFeeds,
|
savedFeeds,
|
||||||
@@ -336,24 +342,27 @@ export function useGetPopularFeedsQuery(options?: GetPopularFeedsOptions) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useSearchPopularFeedsMutation() {
|
export function useSearchPopularFeedsMutation() {
|
||||||
const agent = useAgent()
|
const client = useAppviewClient()
|
||||||
const moderationOpts = useModerationOpts()
|
const moderationOpts = useModerationOpts()
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async (query: string) => {
|
mutationFn: async (query: string) => {
|
||||||
const res = await agent.app.bsky.unspecced.getPopularFeedGenerators({
|
const data = await client.call(
|
||||||
|
app.bsky.unspecced.getPopularFeedGenerators,
|
||||||
|
{
|
||||||
limit: 10,
|
limit: 10,
|
||||||
query: query,
|
query: query,
|
||||||
})
|
},
|
||||||
|
)
|
||||||
|
|
||||||
if (moderationOpts) {
|
if (moderationOpts) {
|
||||||
return res.data.feeds.filter(feed => {
|
return data.feeds.filter(feed => {
|
||||||
const decision = moderateFeedGenerator(feed, moderationOpts)
|
const decision = moderateFeedGenerator(feed, moderationOpts)
|
||||||
return !decision.ui('contentMedia').blur
|
return !decision.ui('contentMedia').blur
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.data.feeds
|
return data.feeds
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -371,7 +380,7 @@ export function usePopularFeedsSearch({
|
|||||||
query: string
|
query: string
|
||||||
enabled?: boolean
|
enabled?: boolean
|
||||||
}) {
|
}) {
|
||||||
const agent = useAgent()
|
const client = useAppviewClient()
|
||||||
const moderationOpts = useModerationOpts()
|
const moderationOpts = useModerationOpts()
|
||||||
const enabledInner = enabled ?? Boolean(moderationOpts)
|
const enabledInner = enabled ?? Boolean(moderationOpts)
|
||||||
|
|
||||||
@@ -379,12 +388,15 @@ export function usePopularFeedsSearch({
|
|||||||
enabled: enabledInner,
|
enabled: enabledInner,
|
||||||
queryKey: createPopularFeedsSearchQueryKey(query),
|
queryKey: createPopularFeedsSearchQueryKey(query),
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const res = await agent.app.bsky.unspecced.getPopularFeedGenerators({
|
const data = await client.call(
|
||||||
|
app.bsky.unspecced.getPopularFeedGenerators,
|
||||||
|
{
|
||||||
limit: 15,
|
limit: 15,
|
||||||
query: query,
|
query: query,
|
||||||
})
|
},
|
||||||
|
)
|
||||||
|
|
||||||
return res.data.feeds
|
return data.feeds
|
||||||
},
|
},
|
||||||
placeholderData: keepPreviousData,
|
placeholderData: keepPreviousData,
|
||||||
select(data) {
|
select(data) {
|
||||||
@@ -444,7 +456,7 @@ const createPinnedFeedInfosQueryKey = (
|
|||||||
|
|
||||||
export function usePinnedFeedsInfos() {
|
export function usePinnedFeedsInfos() {
|
||||||
const {hasSession} = useSession()
|
const {hasSession} = useSession()
|
||||||
const agent = useAgent()
|
const client = useAppviewClient()
|
||||||
const {data: preferences, isLoading: isLoadingPrefs} = usePreferencesQuery()
|
const {data: preferences, isLoading: isLoadingPrefs} = usePreferencesQuery()
|
||||||
const pinnedItems = preferences?.savedFeeds.filter(feed => feed.pinned) ?? []
|
const pinnedItems = preferences?.savedFeeds.filter(feed => feed.pinned) ?? []
|
||||||
|
|
||||||
@@ -467,13 +479,13 @@ export function usePinnedFeedsInfos() {
|
|||||||
const pinnedFeeds = pinnedItems.filter(feed => feed.type === 'feed')
|
const pinnedFeeds = pinnedItems.filter(feed => feed.type === 'feed')
|
||||||
let feedsPromise = Promise.resolve()
|
let feedsPromise = Promise.resolve()
|
||||||
if (pinnedFeeds.length > 0) {
|
if (pinnedFeeds.length > 0) {
|
||||||
feedsPromise = agent.app.bsky.feed
|
feedsPromise = client
|
||||||
.getFeedGenerators({
|
.call(app.bsky.feed.getFeedGenerators, {
|
||||||
feeds: pinnedFeeds.map(f => f.value),
|
feeds: pinnedFeeds.map(f => f.value as AtUriString),
|
||||||
})
|
})
|
||||||
.then(res => {
|
.then(data => {
|
||||||
for (let i = 0; i < res.data.feeds.length; i++) {
|
for (let i = 0; i < data.feeds.length; i++) {
|
||||||
const feedView = res.data.feeds[i]
|
const feedView = data.feeds[i]
|
||||||
resolved.set(feedView.uri, hydrateFeedGenerator(feedView))
|
resolved.set(feedView.uri, hydrateFeedGenerator(feedView))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -482,13 +494,13 @@ export function usePinnedFeedsInfos() {
|
|||||||
// Get all lists. This currently has to be done individually.
|
// Get all lists. This currently has to be done individually.
|
||||||
const pinnedLists = pinnedItems.filter(feed => feed.type === 'list')
|
const pinnedLists = pinnedItems.filter(feed => feed.type === 'list')
|
||||||
const listsPromises = pinnedLists.map(list =>
|
const listsPromises = pinnedLists.map(list =>
|
||||||
agent.app.bsky.graph
|
client
|
||||||
.getList({
|
.call(app.bsky.graph.getList, {
|
||||||
list: list.value,
|
list: list.value as AtUriString,
|
||||||
limit: 1,
|
limit: 1,
|
||||||
})
|
})
|
||||||
.then(res => {
|
.then(data => {
|
||||||
const listView = res.data.list
|
const listView = data.list
|
||||||
resolved.set(listView.uri, hydrateList(listView))
|
resolved.set(listView.uri, hydrateList(listView))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -551,7 +563,7 @@ export type SavedFeedItem =
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useSavedFeeds() {
|
export function useSavedFeeds() {
|
||||||
const agent = useAgent()
|
const client = useAppviewClient()
|
||||||
const {data: preferences, isLoading: isLoadingPrefs} = usePreferencesQuery()
|
const {data: preferences, isLoading: isLoadingPrefs} = usePreferencesQuery()
|
||||||
const savedItems = preferences?.savedFeeds ?? []
|
const savedItems = preferences?.savedFeeds ?? []
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
@@ -582,25 +594,25 @@ export function useSavedFeeds() {
|
|||||||
|
|
||||||
let feedsPromise = Promise.resolve()
|
let feedsPromise = Promise.resolve()
|
||||||
if (savedFeeds.length > 0) {
|
if (savedFeeds.length > 0) {
|
||||||
feedsPromise = agent.app.bsky.feed
|
feedsPromise = client
|
||||||
.getFeedGenerators({
|
.call(app.bsky.feed.getFeedGenerators, {
|
||||||
feeds: savedFeeds.map(f => f.value),
|
feeds: savedFeeds.map(f => f.value as AtUriString),
|
||||||
})
|
})
|
||||||
.then(res => {
|
.then(data => {
|
||||||
res.data.feeds.forEach(f => {
|
data.feeds.forEach(f => {
|
||||||
resolvedFeeds.set(f.uri, f)
|
resolvedFeeds.set(f.uri, f)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const listsPromises = savedLists.map(list =>
|
const listsPromises = savedLists.map(list =>
|
||||||
agent.app.bsky.graph
|
client
|
||||||
.getList({
|
.call(app.bsky.graph.getList, {
|
||||||
list: list.value,
|
list: list.value as AtUriString,
|
||||||
limit: 1,
|
limit: 1,
|
||||||
})
|
})
|
||||||
.then(res => {
|
.then(data => {
|
||||||
const listView = res.data.list
|
const listView = data.list
|
||||||
resolvedLists.set(listView.uri, listView)
|
resolvedLists.set(listView.uri, listView)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -656,7 +668,7 @@ export function useSavedFeeds() {
|
|||||||
const feedInfoQueryKeyRoot = 'feedInfo'
|
const feedInfoQueryKeyRoot = 'feedInfo'
|
||||||
|
|
||||||
export function useFeedInfo(feedUri: string | undefined) {
|
export function useFeedInfo(feedUri: string | undefined) {
|
||||||
const agent = useAgent()
|
const client = useAppviewClient()
|
||||||
|
|
||||||
return useQuery({
|
return useQuery({
|
||||||
staleTime: STALE.INFINITY,
|
staleTime: STALE.INFINITY,
|
||||||
@@ -666,11 +678,11 @@ export function useFeedInfo(feedUri: string | undefined) {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await agent.app.bsky.feed.getFeedGenerator({
|
const data = await client.call(app.bsky.feed.getFeedGenerator, {
|
||||||
feed: feedUri,
|
feed: feedUri as AtUriString,
|
||||||
})
|
})
|
||||||
|
|
||||||
const feedSourceInfo = hydrateFeedGenerator(res.data.view)
|
const feedSourceInfo = hydrateFeedGenerator(data.view)
|
||||||
return feedSourceInfo
|
return feedSourceInfo
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user