Skip to content

Commit

Permalink
reorg follow block rules
Browse files Browse the repository at this point in the history
  • Loading branch information
devinivy committed Dec 8, 2023
1 parent 5cd6fc7 commit eedc2ea
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 55 deletions.
31 changes: 16 additions & 15 deletions packages/bsky/src/api/app/bsky/graph/getFollowers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
createPipelineNew,
} from '../../../../pipeline'
import { didFromUri } from '../../../../hydration/util'
import { Hydrator } from '../../../../hydration/hydrator'
import { Hydrator, mergeStates } from '../../../../hydration/hydrator'
import { Views } from '../../../../views'

export default function (server: Server, ctx: AppContext) {
Expand Down Expand Up @@ -65,28 +65,29 @@ const hydration = async (
const { ctx, params, skeleton } = input
const { viewer } = params
const { followUris, subjectDid } = skeleton
// @TODO hydrate follows w/ block info
const follows = await ctx.hydrator.graph.getFollows(followUris, {
disallowBlock: true,
})
const followState = await ctx.hydrator.hydrateFollows(followUris)
const dids = [subjectDid]
for (const [uri, follow] of follows) {
if (follow) {
dids.push(didFromUri(uri))
if (followState.follows) {
for (const [uri, follow] of followState.follows) {
if (follow) {
dids.push(didFromUri(uri))
}
}
}
return ctx.hydrator.hydrateProfiles(dids, viewer)
const profileState = await ctx.hydrator.hydrateProfiles(dids, viewer)
return mergeStates(followState, profileState)
}

const noBlocks = (input: RulesFnInput<Context, Params, SkeletonState>) => {
const { skeleton, params, hydration, ctx } = input
const { viewer } = params
if (viewer) {
skeleton.followUris = skeleton.followUris.filter((followUri) => {
const followerDid = didFromUri(followUri)
return !ctx.views.viewerBlockExists(followerDid, hydration)
})
}
skeleton.followUris = skeleton.followUris.filter((followUri) => {
const followerDid = didFromUri(followUri)
return (
!hydration.followBlocks?.get(followUri) &&
(!viewer || !ctx.views.viewerBlockExists(followerDid, hydration))
)
})
return skeleton
}

Expand Down
33 changes: 17 additions & 16 deletions packages/bsky/src/api/app/bsky/graph/getFollows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
SkeletonFnInput,
createPipelineNew,
} from '../../../../pipeline'
import { Hydrator } from '../../../../hydration/hydrator'
import { Hydrator, mergeStates } from '../../../../hydration/hydrator'
import { Views } from '../../../../views'

export default function (server: Server, ctx: AppContext) {
Expand Down Expand Up @@ -64,30 +64,31 @@ const hydration = async (
const { ctx, params, skeleton } = input
const { viewer } = params
const { followUris, subjectDid } = skeleton
// @TODO hydrate follows w/ block info
const follows = await ctx.hydrator.graph.getFollows(followUris, {
disallowBlock: true,
})
const followState = await ctx.hydrator.hydrateFollows(followUris)
const dids = [subjectDid]
for (const follow of follows.values()) {
if (follow) {
dids.push(follow.record.subject)
if (followState.follows) {
for (const follow of followState.follows.values()) {
if (follow) {
dids.push(follow.record.subject)
}
}
}
const profileState = await ctx.hydrator.hydrateProfiles(dids, viewer)
return { ...profileState, follows }
return mergeStates(followState, profileState)
}

const noBlocks = (input: RulesFnInput<Context, Params, SkeletonState>) => {
const { skeleton, params, hydration, ctx } = input
const { viewer } = params
if (viewer) {
skeleton.followUris = skeleton.followUris.filter((followUri) => {
const follow = hydration.follows?.get(followUri)
if (!follow) return true
return !ctx.views.viewerBlockExists(follow.record.subject, hydration)
})
}
skeleton.followUris = skeleton.followUris.filter((followUri) => {
const follow = hydration.follows?.get(followUri)
if (!follow) return false
return (
!hydration.followBlocks?.get(followUri) &&
(!viewer ||
!ctx.views.viewerBlockExists(follow.record.subject, hydration))
)
})
return skeleton
}

Expand Down
27 changes: 3 additions & 24 deletions packages/bsky/src/hydration/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Record as FollowRecord } from '../lexicon/types/app/bsky/graph/follow'
import { Record as ListRecord } from '../lexicon/types/app/bsky/graph/list'
import { Record as ListItemRecord } from '../lexicon/types/app/bsky/graph/listitem'
import { DataPlaneClient } from '../data-plane/client'
import { HydrationMap, RecordInfo, didFromUri, parseRecord } from './util'
import { HydrationMap, RecordInfo, parseRecord } from './util'

export type List = RecordInfo<ListRecord>
export type Lists = HydrationMap<List>
Expand Down Expand Up @@ -132,32 +132,11 @@ export class GraphHydrator {
return blocks
}

async getFollows(
uris: string[],
opts?: { disallowBlock?: boolean },
): Promise<Follows> {
async getFollows(uris: string[]): Promise<Follows> {
const res = await this.dataplane.getFollowRecords({ uris })
const follows = uris.reduce((acc, uri, i) => {
return uris.reduce((acc, uri, i) => {
return acc.set(uri, parseRecord<FollowRecord>(res.records[i]) ?? null)
}, new HydrationMap<Follow>())
if (opts?.disallowBlock) {
const pairs: RelationshipPair[] = []
for (const [uri, follow] of follows) {
if (follow) {
pairs.push([didFromUri(uri), follow.record.subject])
}
}
const blocks = await this.getBidirectionalBlocks(pairs)
for (const [uri, follow] of follows) {
if (
follow &&
blocks.isBlocked(didFromUri(uri), follow.record.subject)
) {
follows.set(uri, null)
}
}
}
return follows
}

async getActorFollows(input: {
Expand Down
28 changes: 28 additions & 0 deletions packages/bsky/src/hydration/hydrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export type HydrationState = {
postBlocks?: PostBlocks
reposts?: Reposts
follows?: Follows
followBlocks?: FollowBlocks
threadgates?: Threadgates
lists?: Lists
listViewers?: ListViewerStates
Expand All @@ -60,6 +61,9 @@ export type PostBlock = { embed: boolean; reply: boolean }
export type PostBlocks = HydrationMap<PostBlock>
type PostBlockPairs = { embed?: RelationshipPair; reply?: RelationshipPair }

export type FollowBlock = boolean
export type FollowBlocks = HydrationMap<FollowBlock>

export class Hydrator {
actor: ActorHydrator
feed: FeedHydrator
Expand Down Expand Up @@ -407,6 +411,30 @@ export class Hydrator {
])
return mergeStates(profileState, { posts, likes, reposts, follows, labels })
}

// provides partial hydration state withing getFollows / getFollowers, mainly for applying rules
async hydrateFollows(uris: string[]): Promise<HydrationState> {
const follows = await this.graph.getFollows(uris)
const pairs: RelationshipPair[] = []
for (const [uri, follow] of follows) {
if (follow) {
pairs.push([didFromUri(uri), follow.record.subject])
}
}
const blocks = await this.graph.getBidirectionalBlocks(pairs)
const followBlocks = new HydrationMap<FollowBlock>()
for (const [uri, follow] of follows) {
if (follow) {
followBlocks.set(
uri,
blocks.isBlocked(didFromUri(uri), follow.record.subject),
)
} else {
followBlocks.set(uri, null)
}
}
return { follows, followBlocks }
}
}

const listUrisFromProfileViewer = (item: ProfileViewerState | null) => {
Expand Down

0 comments on commit eedc2ea

Please sign in to comment.