Skip to content

Commit

Permalink
Send Bluesky feeds and suggested follows more data (#3695)
Browse files Browse the repository at this point in the history
* WIP

* Fix constructors

* Clean up

* Tweak

* Rm extra assignment

* Narrow down the argument

---------

Co-authored-by: Dan Abramov <[email protected]>
  • Loading branch information
estrattonbailey and gaearon authored Apr 29, 2024
1 parent d893fe0 commit a4e3453
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 10 deletions.
10 changes: 10 additions & 0 deletions src/lib/api/feed/custom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,25 @@ import {

import {getContentLanguages} from '#/state/preferences/languages'
import {FeedAPI, FeedAPIResponse} from './types'
import {createBskyTopicsHeader, isBlueskyOwnedFeed} from './utils'

export class CustomFeedAPI implements FeedAPI {
getAgent: () => BskyAgent
params: GetCustomFeed.QueryParams
userInterests?: string

constructor({
getAgent,
feedParams,
userInterests,
}: {
getAgent: () => BskyAgent
feedParams: GetCustomFeed.QueryParams
userInterests?: string
}) {
this.getAgent = getAgent
this.params = feedParams
this.userInterests = userInterests
}

async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> {
Expand All @@ -44,6 +49,8 @@ export class CustomFeedAPI implements FeedAPI {
}): Promise<FeedAPIResponse> {
const contentLangs = getContentLanguages().join(',')
const agent = this.getAgent()
const isBlueskyOwned = isBlueskyOwnedFeed(this.params.feed)

const res = agent.session
? await this.getAgent().app.bsky.feed.getFeed(
{
Expand All @@ -53,6 +60,9 @@ export class CustomFeedAPI implements FeedAPI {
},
{
headers: {
...(isBlueskyOwned
? createBskyTopicsHeader(this.userInterests)
: {}),
'Accept-Language': contentLangs,
},
},
Expand Down
11 changes: 10 additions & 1 deletion src/lib/api/feed/home.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,30 @@ export class HomeFeedAPI implements FeedAPI {
discover: CustomFeedAPI
usingDiscover = false
itemCursor = 0
userInterests?: string

constructor({getAgent}: {getAgent: () => BskyAgent}) {
constructor({
userInterests,
getAgent,
}: {
userInterests?: string
getAgent: () => BskyAgent
}) {
this.getAgent = getAgent
this.following = new FollowingFeedAPI({getAgent})
this.discover = new CustomFeedAPI({
getAgent,
feedParams: {feed: PROD_DEFAULT_FEED('whats-hot')},
})
this.userInterests = userInterests
}

reset() {
this.following = new FollowingFeedAPI({getAgent: this.getAgent})
this.discover = new CustomFeedAPI({
getAgent: this.getAgent,
feedParams: {feed: PROD_DEFAULT_FEED('whats-hot')},
userInterests: this.userInterests,
})
this.usingDiscover = false
this.itemCursor = 0
Expand Down
14 changes: 14 additions & 0 deletions src/lib/api/feed/merge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ import {feedUriToHref} from 'lib/strings/url-helpers'
import {FeedTuner} from '../feed-manip'
import {FeedTunerFn} from '../feed-manip'
import {FeedAPI, FeedAPIResponse, ReasonFeedSource} from './types'
import {createBskyTopicsHeader, isBlueskyOwnedFeed} from './utils'

const REQUEST_WAIT_MS = 500 // 500ms
const POST_AGE_CUTOFF = 60e3 * 60 * 24 // 24hours

export class MergeFeedAPI implements FeedAPI {
userInterests?: string
getAgent: () => BskyAgent
params: FeedParams
feedTuners: FeedTunerFn[]
Expand All @@ -27,14 +29,17 @@ export class MergeFeedAPI implements FeedAPI {
getAgent,
feedParams,
feedTuners,
userInterests,
}: {
getAgent: () => BskyAgent
feedParams: FeedParams
feedTuners: FeedTunerFn[]
userInterests?: string
}) {
this.getAgent = getAgent
this.params = feedParams
this.feedTuners = feedTuners
this.userInterests = userInterests
this.following = new MergeFeedSource_Following({
getAgent: this.getAgent,
feedTuners: this.feedTuners,
Expand All @@ -58,6 +63,7 @@ export class MergeFeedAPI implements FeedAPI {
getAgent: this.getAgent,
feedUri,
feedTuners: this.feedTuners,
userInterests: this.userInterests,
}),
),
)
Expand Down Expand Up @@ -254,22 +260,26 @@ class MergeFeedSource_Custom extends MergeFeedSource {
getAgent: () => BskyAgent
minDate: Date
feedUri: string
userInterests?: string

constructor({
getAgent,
feedUri,
feedTuners,
userInterests,
}: {
getAgent: () => BskyAgent
feedUri: string
feedTuners: FeedTunerFn[]
userInterests?: string
}) {
super({
getAgent,
feedTuners,
})
this.getAgent = getAgent
this.feedUri = feedUri
this.userInterests = userInterests
this.sourceInfo = {
$type: 'reasonFeedSource',
uri: feedUri,
Expand All @@ -284,6 +294,7 @@ class MergeFeedSource_Custom extends MergeFeedSource {
): Promise<AppBskyFeedGetTimeline.Response> {
try {
const contentLangs = getContentLanguages().join(',')
const isBlueskyOwned = isBlueskyOwnedFeed(this.feedUri)
const res = await this.getAgent().app.bsky.feed.getFeed(
{
cursor,
Expand All @@ -292,6 +303,9 @@ class MergeFeedSource_Custom extends MergeFeedSource {
},
{
headers: {
...(isBlueskyOwned
? createBskyTopicsHeader(this.userInterests)
: {}),
'Accept-Language': contentLangs,
},
},
Expand Down
21 changes: 21 additions & 0 deletions src/lib/api/feed/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {AtUri} from '@atproto/api'

import {BSKY_FEED_OWNER_DIDS} from '#/lib/constants'
import {UsePreferencesQueryResponse} from '#/state/queries/preferences'

export function createBskyTopicsHeader(userInterests?: string) {
return {
'X-Bsky-Topics': userInterests || '',
}
}

export function aggregateUserInterests(
preferences?: UsePreferencesQueryResponse,
) {
return preferences?.interests?.tags?.join(',') || ''
}

export function isBlueskyOwnedFeed(feedUri: string) {
const uri = new AtUri(feedUri)
return BSKY_FEED_OWNER_DIDS.includes(uri.host)
}
15 changes: 12 additions & 3 deletions src/state/queries/post-feed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
} from '@tanstack/react-query'

import {HomeFeedAPI} from '#/lib/api/feed/home'
import {aggregateUserInterests} from '#/lib/api/feed/utils'
import {moderatePost_wrapped as moderatePost} from '#/lib/moderatePost_wrapped'
import {logger} from '#/logger'
import {STALE} from '#/state/queries'
Expand All @@ -31,7 +32,7 @@ import {FeedTuner, FeedTunerFn, NoopFeedTuner} from 'lib/api/feed-manip'
import {BSKY_FEED_OWNER_DIDS} from 'lib/constants'
import {KnownError} from '#/view/com/posts/FeedErrorMessage'
import {useFeedTuners} from '../preferences/feed-tuners'
import {useModerationOpts} from './preferences'
import {useModerationOpts, usePreferencesQuery} from './preferences'
import {embedViewRecordToPostView, getEmbeddedPost} from './util'

type ActorDid = string
Expand Down Expand Up @@ -102,8 +103,11 @@ export function usePostFeedQuery(
) {
const feedTuners = useFeedTuners(feedDesc)
const moderationOpts = useModerationOpts()
const {data: preferences} = usePreferencesQuery()
const enabled =
opts?.enabled !== false && Boolean(moderationOpts) && Boolean(preferences)
const userInterests = aggregateUserInterests(preferences)
const {getAgent} = useAgent()
const enabled = opts?.enabled !== false && Boolean(moderationOpts)
const lastRun = useRef<{
data: InfiniteData<FeedPageUnselected>
args: typeof selectArgs
Expand Down Expand Up @@ -141,6 +145,7 @@ export function usePostFeedQuery(
feedDesc,
feedParams: params || {},
feedTuners,
userInterests, // Not in the query key because they don't change.
getAgent,
}),
cursor: undefined,
Expand Down Expand Up @@ -371,11 +376,13 @@ function createApi({
feedDesc,
feedParams,
feedTuners,
userInterests,
getAgent,
}: {
feedDesc: FeedDescriptor
feedParams: FeedParams
feedTuners: FeedTunerFn[]
userInterests?: string
getAgent: () => BskyAgent
}) {
if (feedDesc === 'home') {
Expand All @@ -384,9 +391,10 @@ function createApi({
getAgent,
feedParams,
feedTuners,
userInterests,
})
} else {
return new HomeFeedAPI({getAgent})
return new HomeFeedAPI({getAgent, userInterests})
}
} else if (feedDesc === 'following') {
return new FollowingFeedAPI({getAgent})
Expand All @@ -401,6 +409,7 @@ function createApi({
return new CustomFeedAPI({
getAgent,
feedParams: {feed},
userInterests,
})
} else if (feedDesc.startsWith('list')) {
const [_, list] = feedDesc.split('|')
Expand Down
30 changes: 24 additions & 6 deletions src/state/queries/suggested-follows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,16 @@ import {
useQuery,
} from '@tanstack/react-query'

import {
aggregateUserInterests,
createBskyTopicsHeader,
} from '#/lib/api/feed/utils'
import {getContentLanguages} from '#/state/preferences/languages'
import {STALE} from '#/state/queries'
import {useModerationOpts} from '#/state/queries/preferences'
import {
useModerationOpts,
usePreferencesQuery,
} from '#/state/queries/preferences'
import {useAgent, useSession} from '#/state/session'

const suggestedFollowsQueryKeyRoot = 'suggested-follows'
Expand All @@ -29,6 +37,7 @@ export function useSuggestedFollowsQuery() {
const {currentAccount} = useSession()
const {getAgent} = useAgent()
const moderationOpts = useModerationOpts()
const {data: preferences} = usePreferencesQuery()

return useInfiniteQuery<
AppBskyActorGetSuggestions.OutputSchema,
Expand All @@ -37,14 +46,23 @@ export function useSuggestedFollowsQuery() {
QueryKey,
string | undefined
>({
enabled: !!moderationOpts,
enabled: !!moderationOpts && !!preferences,
staleTime: STALE.HOURS.ONE,
queryKey: suggestedFollowsQueryKey,
queryFn: async ({pageParam}) => {
const res = await getAgent().app.bsky.actor.getSuggestions({
limit: 25,
cursor: pageParam,
})
const contentLangs = getContentLanguages().join(',')
const res = await getAgent().app.bsky.actor.getSuggestions(
{
limit: 25,
cursor: pageParam,
},
{
headers: {
...createBskyTopicsHeader(aggregateUserInterests(preferences)),
'Accept-Language': contentLangs,
},
},
)

res.data.actors = res.data.actors
.filter(
Expand Down

0 comments on commit a4e3453

Please sign in to comment.