Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Send Bluesky feeds and suggested follows more data #3695

Merged
merged 8 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
}
20 changes: 17 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,11 @@ 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,
UsePreferencesQueryResponse,
} from './preferences'
import {embedViewRecordToPostView, getEmbeddedPost} from './util'

type ActorDid = string
Expand Down Expand Up @@ -102,8 +107,10 @@ export function usePostFeedQuery(
) {
const feedTuners = useFeedTuners(feedDesc)
const moderationOpts = useModerationOpts()
const {data: preferences} = usePreferencesQuery()
const enabled =
opts?.enabled !== false && Boolean(moderationOpts) && Boolean(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 +148,7 @@ export function usePostFeedQuery(
feedDesc,
feedParams: params || {},
feedTuners,
preferences,
getAgent,
}),
cursor: undefined,
Expand Down Expand Up @@ -371,22 +379,27 @@ function createApi({
feedDesc,
feedParams,
feedTuners,
preferences,
getAgent,
}: {
feedDesc: FeedDescriptor
feedParams: FeedParams
feedTuners: FeedTunerFn[]
preferences?: UsePreferencesQueryResponse
getAgent: () => BskyAgent
}) {
const userInterests = aggregateUserInterests(preferences)

if (feedDesc === 'home') {
if (feedParams.mergeFeedEnabled) {
return new MergeFeedAPI({
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 +414,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
Loading