filter blocks in curate list ()

This commit is contained in:
Hailey 2024-08-21 14:55:30 -07:00 committed by GitHub
parent 1572058887
commit 47263e9f3d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 314 additions and 26 deletions
packages/bsky
src/api/app/bsky
tests/views

@ -7,12 +7,14 @@ import {
HydrateCtx,
HydrationState,
Hydrator,
mergeStates,
} from '../../../../hydration/hydrator'
import { Views } from '../../../../views'
import { DataPlaneClient } from '../../../../data-plane'
import { mapDefined } from '@atproto/common'
import { parseString } from '../../../../hydration/util'
import { FeedItem } from '../../../../hydration/feed'
import { uriToDid } from '../../../../util/uris'
export default function (server: Server, ctx: AppContext) {
const getListFeed = createPipeline(
@ -71,23 +73,34 @@ const hydration = async (inputs: {
skeleton: Skeleton
}): Promise<HydrationState> => {
const { ctx, params, skeleton } = inputs
return ctx.hydrator.hydrateFeedItems(skeleton.items, params.hydrateCtx)
const [feedItemsState, bidirectionalBlocks] = await Promise.all([
ctx.hydrator.hydrateFeedItems(skeleton.items, params.hydrateCtx),
getBlocks({ ctx, params, skeleton }),
])
return mergeStates(feedItemsState, {
bidirectionalBlocks,
})
}
const noBlocksOrMutes = (inputs: {
ctx: Context
params: Params
skeleton: Skeleton
hydration: HydrationState
}): Skeleton => {
const { ctx, skeleton, hydration } = inputs
const { ctx, params, skeleton, hydration } = inputs
skeleton.items = skeleton.items.filter((item) => {
const bam = ctx.views.feedItemBlocksAndMutes(item, hydration)
const creatorBlocks = hydration.bidirectionalBlocks?.get(
uriToDid(params.list),
)
return (
!bam.authorBlocked &&
!bam.authorMuted &&
!bam.originatorBlocked &&
!bam.originatorMuted &&
!bam.ancestorAuthorBlocked
!bam.ancestorAuthorBlocked &&
!creatorBlocks?.get(uriToDid(item.post.uri))
)
})
return skeleton
@ -105,6 +118,20 @@ const presentation = (inputs: {
return { feed, cursor: skeleton.cursor }
}
const getBlocks = async (input: {
ctx: Context
skeleton: Skeleton
params: Params
}) => {
const { ctx, skeleton, params } = input
const pairs: Map<string, string[]> = new Map()
pairs.set(
uriToDid(params.list),
skeleton.items.map((item) => uriToDid(item.post.uri)),
)
return await ctx.hydrator.hydrateBidirectionalBlocks(pairs)
}
type Context = {
hydrator: Hydrator
views: Views

@ -70,7 +70,7 @@ const hydration = async (
params.hydrateCtx,
),
])
const bidirectionalBlocks = await maybeGetBlocksForReferenceList({
const bidirectionalBlocks = await maybeGetBlocksForReferenceAndCurateList({
ctx,
params,
skeleton,
@ -106,7 +106,7 @@ const presentation = (
return { list, items, cursor }
}
const maybeGetBlocksForReferenceList = async (input: {
const maybeGetBlocksForReferenceAndCurateList = async (input: {
ctx: Context
listState: HydrationState
skeleton: SkeletonState
@ -118,8 +118,8 @@ const maybeGetBlocksForReferenceList = async (input: {
const listRecord = listState.lists?.get(list)
const creator = didFromUri(list)
if (
listRecord?.record.purpose !== 'app.bsky.graph.defs#referencelist' ||
params.hydrateCtx.viewer === creator
params.hydrateCtx.viewer === creator ||
listRecord?.record.purpose === 'app.bsky.graph.defs#modlist'
) {
return
}

@ -46,6 +46,52 @@ Array [
]
`;
exports[`bsky actor likes feed views does include users with creator block relationship in reference lists for creator 2`] = `
Array [
Object {
"subject": Object {
"did": "user(0)",
"handle": "frankie.test",
"labels": Array [],
"viewer": Object {
"blockedBy": true,
"muted": false,
},
},
"uri": "record(0)",
},
Object {
"subject": Object {
"avatar": "https://bsky.public.url/img/avatar/plain/user(2)/cids(0)@jpeg",
"createdAt": "1970-01-01T00:00:00.000Z",
"description": "hi im bob label_me",
"did": "user(1)",
"displayName": "bobby",
"handle": "bob.test",
"indexedAt": "1970-01-01T00:00:00.000Z",
"labels": Array [],
"viewer": Object {
"blockedBy": false,
"muted": false,
},
},
"uri": "record(1)",
},
Object {
"subject": Object {
"did": "user(3)",
"handle": "eve.test",
"labels": Array [],
"viewer": Object {
"blockedBy": false,
"muted": false,
},
},
"uri": "record(2)",
},
]
`;
exports[`bsky actor likes feed views does not include reference lists in getActorLists 1`] = `
Array [
Object {
@ -62,6 +108,72 @@ Array [
"purpose": "app.bsky.graph.defs#curatelist",
"uri": "record(0)",
},
Object {
"cid": "cids(1)",
"creator": Object {
"did": "user(0)",
"handle": "eve.test",
"labels": Array [],
},
"indexedAt": "1970-01-01T00:00:00.000Z",
"labels": Array [],
"listItemCount": 3,
"name": "blah curate list!",
"purpose": "app.bsky.graph.defs#curatelist",
"uri": "record(1)",
},
]
`;
exports[`bsky actor likes feed views does not include users with creator block relationship in reference and curate lists for signed-out viewers 1`] = `
Array [
Object {
"subject": Object {
"avatar": "https://bsky.public.url/img/avatar/plain/user(1)/cids(0)@jpeg",
"createdAt": "1970-01-01T00:00:00.000Z",
"description": "hi im bob label_me",
"did": "user(0)",
"displayName": "bobby",
"handle": "bob.test",
"indexedAt": "1970-01-01T00:00:00.000Z",
"labels": Array [],
},
"uri": "record(0)",
},
Object {
"subject": Object {
"did": "user(2)",
"handle": "eve.test",
"labels": Array [],
},
"uri": "record(1)",
},
]
`;
exports[`bsky actor likes feed views does not include users with creator block relationship in reference and curate lists for signed-out viewers 2`] = `
Array [
Object {
"subject": Object {
"avatar": "https://bsky.public.url/img/avatar/plain/user(1)/cids(0)@jpeg",
"createdAt": "1970-01-01T00:00:00.000Z",
"description": "hi im bob label_me",
"did": "user(0)",
"displayName": "bobby",
"handle": "bob.test",
"indexedAt": "1970-01-01T00:00:00.000Z",
"labels": Array [],
},
"uri": "record(0)",
},
Object {
"subject": Object {
"did": "user(2)",
"handle": "eve.test",
"labels": Array [],
},
"uri": "record(1)",
},
]
`;
@ -100,6 +212,41 @@ Array [
]
`;
exports[`bsky actor likes feed views does not include users with creator block relationship in reference lists for non-creator, in-list viewers 2`] = `
Array [
Object {
"subject": Object {
"avatar": "https://bsky.public.url/img/avatar/plain/user(1)/cids(0)@jpeg",
"createdAt": "1970-01-01T00:00:00.000Z",
"description": "hi im bob label_me",
"did": "user(0)",
"displayName": "bobby",
"handle": "bob.test",
"indexedAt": "1970-01-01T00:00:00.000Z",
"labels": Array [],
"viewer": Object {
"blockedBy": false,
"muted": false,
},
},
"uri": "record(0)",
},
Object {
"subject": Object {
"did": "user(2)",
"handle": "eve.test",
"labels": Array [],
"viewer": Object {
"blockedBy": false,
"blocking": "record(2)",
"muted": false,
},
},
"uri": "record(1)",
},
]
`;
exports[`bsky actor likes feed views does not include users with creator block relationship in reference lists for non-creator, not-in-list viewers 1`] = `
Array [
Object {
@ -134,7 +281,7 @@ Array [
]
`;
exports[`bsky actor likes feed views does not include users with creator block relationship in reference lists for signed-out viewers 1`] = `
exports[`bsky actor likes feed views does not include users with creator block relationship in reference lists for non-creator, not-in-list viewers 2`] = `
Array [
Object {
"subject": Object {
@ -146,6 +293,10 @@ Array [
"handle": "bob.test",
"indexedAt": "1970-01-01T00:00:00.000Z",
"labels": Array [],
"viewer": Object {
"blockedBy": false,
"muted": false,
},
},
"uri": "record(0)",
},
@ -154,6 +305,39 @@ Array [
"did": "user(2)",
"handle": "eve.test",
"labels": Array [],
"viewer": Object {
"blockedBy": false,
"muted": false,
},
},
"uri": "record(1)",
},
]
`;
exports[`bsky actor likes feed views does return all users regardless of creator block relationship in moderation lists 1`] = `
Array [
Object {
"subject": Object {
"did": "user(0)",
"handle": "greta.test",
"labels": Array [],
"viewer": Object {
"blockedBy": false,
"muted": false,
},
},
"uri": "record(0)",
},
Object {
"subject": Object {
"did": "user(1)",
"handle": "frankie.test",
"labels": Array [],
"viewer": Object {
"blockedBy": false,
"muted": false,
},
},
"uri": "record(1)",
},

@ -175,4 +175,16 @@ describe('list feed views', () => {
recordUri: postRef.uriStr,
})
})
it('does not return posts with creator blocks', async () => {
await sc.block(bob, alice)
await network.processAll()
const res = await agent.api.app.bsky.feed.getListFeed({
list: listRef.uriStr,
})
const hasBob = res.data.feed.some((item) => item.post.author.did === bob)
expect(hasBob).toBe(false)
})
})

@ -8,7 +8,9 @@ describe('bsky actor likes feed views', () => {
let agent: AtpAgent
let sc: SeedClient
let curateList: string
let referenceList: string
let alice: string
let eve: string
let frankie: string
let greta: string
@ -35,17 +37,32 @@ describe('bsky actor likes feed views', () => {
email: 'greta@greta.com',
password: 'hunter4real',
})
const newList = await sc.createList(
const newRefList = await sc.createList(
sc.dids.eve,
'blah starter pack list!',
'reference',
)
await sc.addToList(sc.dids.eve, sc.dids.eve, newList)
await sc.addToList(sc.dids.eve, sc.dids.bob, newList)
await sc.addToList(sc.dids.eve, sc.dids.frankie, newList)
const newCurrList = await sc.createList(
sc.dids.eve,
'blah curate list!',
'curate',
)
await sc.addToList(sc.dids.eve, sc.dids.eve, newRefList)
await sc.addToList(sc.dids.eve, sc.dids.bob, newRefList)
await sc.addToList(sc.dids.eve, sc.dids.frankie, newRefList)
await sc.addToList(sc.dids.eve, sc.dids.eve, newCurrList)
await sc.addToList(sc.dids.eve, sc.dids.bob, newCurrList)
await sc.addToList(sc.dids.eve, sc.dids.frankie, newCurrList)
await sc.block(sc.dids.frankie, sc.dids.eve)
await network.processAll()
referenceList = newList.uriStr
curateList = newCurrList.uriStr
referenceList = newRefList.uriStr
alice = sc.dids.alice
eve = sc.dids.eve
frankie = sc.dids.frankie
greta = sc.dids.greta
@ -61,44 +78,92 @@ describe('bsky actor likes feed views', () => {
const view = await agent.api.app.bsky.graph.getLists({
actor: eve,
})
expect(view.data.lists.length).toBe(1)
expect(view.data.lists.length).toBe(2)
expect(forSnapshot(view.data.lists)).toMatchSnapshot()
})
it('does not include users with creator block relationship in reference lists for non-creator, in-list viewers', async () => {
const view = await agent.api.app.bsky.graph.getList(
const curView = await agent.api.app.bsky.graph.getList(
{
list: curateList,
},
{
headers: await network.serviceHeaders(frankie, ids.AppBskyGraphGetList),
},
)
expect(curView.data.items.length).toBe(2)
expect(forSnapshot(curView.data.items)).toMatchSnapshot()
const refView = await agent.api.app.bsky.graph.getList(
{ list: referenceList },
{
headers: await network.serviceHeaders(frankie, ids.AppBskyGraphGetList),
},
)
expect(view.data.items.length).toBe(2)
expect(forSnapshot(view.data.items)).toMatchSnapshot()
expect(refView.data.items.length).toBe(2)
expect(forSnapshot(refView.data.items)).toMatchSnapshot()
})
it('does not include users with creator block relationship in reference lists for non-creator, not-in-list viewers', async () => {
const view = await agent.api.app.bsky.graph.getList(
const curView = await agent.api.app.bsky.graph.getList(
{
list: curateList,
},
{ headers: await network.serviceHeaders(greta, ids.AppBskyGraphGetList) },
)
expect(curView.data.items.length).toBe(2)
expect(forSnapshot(curView.data.items)).toMatchSnapshot()
const refView = await agent.api.app.bsky.graph.getList(
{ list: referenceList },
{ headers: await network.serviceHeaders(greta, ids.AppBskyGraphGetList) },
)
expect(view.data.items.length).toBe(2)
expect(forSnapshot(view.data.items)).toMatchSnapshot()
expect(refView.data.items.length).toBe(2)
expect(forSnapshot(refView.data.items)).toMatchSnapshot()
})
it('does not include users with creator block relationship in reference lists for signed-out viewers', async () => {
const view = await agent.api.app.bsky.graph.getList({
it('does not include users with creator block relationship in reference and curate lists for signed-out viewers', async () => {
const curView = await agent.api.app.bsky.graph.getList({
list: curateList,
})
expect(curView.data.items.length).toBe(2)
expect(forSnapshot(curView.data.items)).toMatchSnapshot()
const refView = await agent.api.app.bsky.graph.getList({
list: referenceList,
})
expect(view.data.items.length).toBe(2)
expect(forSnapshot(view.data.items)).toMatchSnapshot()
expect(refView.data.items.length).toBe(2)
expect(forSnapshot(refView.data.items)).toMatchSnapshot()
})
it('does include users with creator block relationship in reference lists for creator', async () => {
const view = await agent.api.app.bsky.graph.getList(
const curView = await agent.api.app.bsky.graph.getList(
{ list: curateList },
{ headers: await network.serviceHeaders(eve, ids.AppBskyGraphGetList) },
)
expect(curView.data.items.length).toBe(3)
expect(forSnapshot(curView.data.items)).toMatchSnapshot()
const refView = await agent.api.app.bsky.graph.getList(
{ list: referenceList },
{ headers: await network.serviceHeaders(eve, ids.AppBskyGraphGetList) },
)
expect(view.data.items.length).toBe(3)
expect(refView.data.items.length).toBe(3)
expect(forSnapshot(refView.data.items)).toMatchSnapshot()
})
it('does return all users regardless of creator block relationship in moderation lists', async () => {
const blockList = await sc.createList(eve, 'block list', 'mod')
await sc.addToList(eve, frankie, blockList)
await sc.addToList(eve, greta, blockList)
await sc.block(frankie, greta)
await network.processAll()
const view = await agent.api.app.bsky.graph.getList(
{ list: blockList.uriStr },
{ headers: await network.serviceHeaders(alice, ids.AppBskyGraphGetList) },
)
expect(view.data.items.length).toBe(2)
expect(forSnapshot(view.data.items)).toMatchSnapshot()
})
})