Skip to content

Commit

Permalink
Appview: add grandparent author to reply ref, ensure no replies to bl…
Browse files Browse the repository at this point in the history
…ocked grandparent in feeds (#2461)

* lexicon: add parent reply author to #replyRef

* lexicon: tweak to naming in reply ref

* appview: hydrate and present grandparent author in feed items

* appview: ensure replies to blocked users don't appear in feeds via reply parents

* fix snaps

* fix snaps
  • Loading branch information
devinivy authored May 2, 2024
1 parent 173ef27 commit ec40af0
Show file tree
Hide file tree
Showing 26 changed files with 493 additions and 31 deletions.
5 changes: 5 additions & 0 deletions lexicons/app/bsky/feed/defs.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@
"parent": {
"type": "union",
"refs": ["#postView", "#notFoundPost", "#blockedPost"]
},
"grandparentAuthor": {
"type": "ref",
"ref": "app.bsky.actor.defs#profileViewBasic",
"description": "When parent is a reply to another post, this is the author of that post."
}
}
},
Expand Down
7 changes: 6 additions & 1 deletion packages/api/src/client/lexicons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4833,6 +4833,12 @@ export const schemaDict = {
'lex:app.bsky.feed.defs#blockedPost',
],
},
grandparentAuthor: {
type: 'ref',
ref: 'lex:app.bsky.actor.defs#profileViewBasic',
description:
'When parent is a reply to another post, this is the author of that post.',
},
},
},
reasonRepost: {
Expand Down Expand Up @@ -9164,7 +9170,6 @@ export const schemaDict = {
'lex:tools.ozone.moderation.defs#modEventMuteReporter',
'lex:tools.ozone.moderation.defs#modEventUnmuteReporter',
'lex:tools.ozone.moderation.defs#modEventReverseTakedown',
'lex:tools.ozone.moderation.defs#modEventUnmute',
'lex:tools.ozone.moderation.defs#modEventEmail',
'lex:tools.ozone.moderation.defs#modEventTag',
],
Expand Down
1 change: 1 addition & 0 deletions packages/api/src/client/types/app/bsky/feed/defs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export interface ReplyRef {
| NotFoundPost
| BlockedPost
| { $type: string; [k: string]: unknown }
grandparentAuthor?: AppBskyActorDefs.ProfileViewBasic
[k: string]: unknown
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ export interface InputSchema {
| ToolsOzoneModerationDefs.ModEventMuteReporter
| ToolsOzoneModerationDefs.ModEventUnmuteReporter
| ToolsOzoneModerationDefs.ModEventReverseTakedown
| ToolsOzoneModerationDefs.ModEventUnmute
| ToolsOzoneModerationDefs.ModEventEmail
| ToolsOzoneModerationDefs.ModEventTag
| { $type: string; [k: string]: unknown }
Expand Down
2 changes: 1 addition & 1 deletion packages/bsky/src/api/app/bsky/feed/getFeed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ const noBlocksOrMutes = (inputs: RulesFnInput<Context, Params, Skeleton>) => {
!bam.authorMuted &&
!bam.originatorBlocked &&
!bam.originatorMuted &&
!bam.parentAuthorBlocked
!bam.ancestorAuthorBlocked
)
})
return skeleton
Expand Down
2 changes: 1 addition & 1 deletion packages/bsky/src/api/app/bsky/feed/getListFeed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ const noBlocksOrMutes = (inputs: {
!bam.authorMuted &&
!bam.originatorBlocked &&
!bam.originatorMuted &&
!bam.parentAuthorBlocked
!bam.ancestorAuthorBlocked
)
})
return skeleton
Expand Down
2 changes: 1 addition & 1 deletion packages/bsky/src/api/app/bsky/feed/getTimeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ const noBlocksOrMutes = (inputs: {
!bam.authorMuted &&
!bam.originatorBlocked &&
!bam.originatorMuted &&
!bam.parentAuthorBlocked
!bam.ancestorAuthorBlocked
)
})
return skeleton
Expand Down
42 changes: 33 additions & 9 deletions packages/bsky/src/hydration/hydrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ export class Hydrator {
}

// app.bsky.feed.defs#feedViewPost
// - post (+ replies)
// - post (+ replies w/ reply parent author)
// - profile
// - list basic
// - list
Expand All @@ -413,22 +413,46 @@ export class Hydrator {
items: FeedItem[],
ctx: HydrateCtx,
): Promise<HydrationState> {
const postUris = items.map((item) => item.post.uri)
const repostUris = mapDefined(items, (item) => item.repost?.uri)
const [posts, reposts, repostProfileState] = await Promise.all([
this.feed.getPosts(postUris, ctx.includeTakedowns),
this.feed.getReposts(repostUris, ctx.includeTakedowns),
this.hydrateProfiles(repostUris.map(didFromUri), ctx),
])
// get posts, collect reply refs
const posts = await this.feed.getPosts(
items.map((item) => item.post.uri),
ctx.includeTakedowns,
)
const rootUris: string[] = []
const parentUris: string[] = []
const postAndReplyRefs: ItemRef[] = []
posts.forEach((post, uri) => {
if (!post) return
postAndReplyRefs.push({ uri, cid: post.cid })
if (post.record.reply) {
rootUris.push(post.record.reply.root.uri)
parentUris.push(post.record.reply.parent.uri)
postAndReplyRefs.push(post.record.reply.root, post.record.reply.parent)
}
})
const postState = await this.hydratePosts(postAndReplyRefs, ctx, { posts })
// get replies, collect reply parent authors
const replies = await this.feed.getPosts(
[...rootUris, ...parentUris],
ctx.includeTakedowns,
)
const replyParentAuthors: string[] = []
parentUris.forEach((uri) => {
const parent = replies.get(uri)
if (!parent?.record.reply) return
replyParentAuthors.push(didFromUri(parent.record.reply.parent.uri))
})
// hydrate state for all posts, reposts, authors of reposts + reply parent authors
const repostUris = mapDefined(items, (item) => item.repost?.uri)
const [postState, repostProfileState, reposts] = await Promise.all([
this.hydratePosts(postAndReplyRefs, ctx, {
posts: posts.merge(replies), // avoids refetches of posts
}),
this.hydrateProfiles(
[...repostUris.map(didFromUri), ...replyParentAuthors],
ctx,
),
this.feed.getReposts(repostUris, ctx.includeTakedowns),
])
return mergeManyStates(postState, repostProfileState, {
reposts,
ctx,
Expand Down
6 changes: 6 additions & 0 deletions packages/bsky/src/lexicon/lexicons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4833,6 +4833,12 @@ export const schemaDict = {
'lex:app.bsky.feed.defs#blockedPost',
],
},
grandparentAuthor: {
type: 'ref',
ref: 'lex:app.bsky.actor.defs#profileViewBasic',
description:
'When parent is a reply to another post, this is the author of that post.',
},
},
},
reasonRepost: {
Expand Down
1 change: 1 addition & 0 deletions packages/bsky/src/lexicon/types/app/bsky/feed/defs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export interface ReplyRef {
| NotFoundPost
| BlockedPost
| { $type: string; [k: string]: unknown }
grandparentAuthor?: AppBskyActorDefs.ProfileViewBasic
[k: string]: unknown
}

Expand Down
30 changes: 24 additions & 6 deletions packages/bsky/src/views/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ import {
NotFoundPost,
PostView,
ReasonRepost,
ReplyRef,
ThreadViewPost,
ThreadgateView,
isPostView,
} from '../lexicon/types/app/bsky/feed/defs'
import { isRecord as isPostRecord } from '../lexicon/types/app/bsky/feed/post'
import { ListView, ListViewBasic } from '../lexicon/types/app/bsky/graph/defs'
import { creatorFromUri, parseThreadGate, cidFromBlobJson } from './util'
import { isListRule } from '../lexicon/types/app/bsky/feed/threadgate'
Expand Down Expand Up @@ -334,7 +336,7 @@ export class Views {
originatorBlocked: boolean
authorMuted: boolean
authorBlocked: boolean
parentAuthorBlocked: boolean
ancestorAuthorBlocked: boolean
} {
const authorDid = creatorFromUri(item.post.uri)
const originatorDid = item.repost
Expand All @@ -343,14 +345,19 @@ export class Views {
const post = state.posts?.get(item.post.uri)
const parentUri = post?.record.reply?.parent.uri
const parentAuthorDid = parentUri && creatorFromUri(parentUri)
const parent = parentUri ? state.posts?.get(parentUri) : undefined
const grandparentUri = parent?.record.reply?.parent.uri
const grandparentAuthorDid =
grandparentUri && creatorFromUri(grandparentUri)
return {
originatorMuted: this.viewerMuteExists(originatorDid, state),
originatorBlocked: this.viewerBlockExists(originatorDid, state),
authorMuted: this.viewerMuteExists(authorDid, state),
authorBlocked: this.viewerBlockExists(authorDid, state),
parentAuthorBlocked: parentAuthorDid
? this.viewerBlockExists(parentAuthorDid, state)
: false,
ancestorAuthorBlocked:
(!!parentAuthorDid && this.viewerBlockExists(parentAuthorDid, state)) ||
(!!grandparentAuthorDid &&
this.viewerBlockExists(grandparentAuthorDid, state)),
}
}

Expand Down Expand Up @@ -477,7 +484,7 @@ export class Views {
}
}

replyRef(uri: string, state: HydrationState) {
replyRef(uri: string, state: HydrationState): ReplyRef | undefined {
const postRecord = state.posts?.get(uri.toString())?.record
if (!postRecord?.reply) return
let root = this.maybePost(postRecord.reply.root.uri, state)
Expand All @@ -489,7 +496,18 @@ export class Views {
root = parent
}
}
return root && parent ? { root, parent } : undefined
let grandparentAuthor: ProfileViewBasic | undefined
if (isPostRecord(parent.record) && parent.record.reply) {
grandparentAuthor = this.profileBasic(
creatorFromUri(parent.record.reply.parent.uri),
state,
)
}
return {
root,
parent,
grandparentAuthor,
}
}

maybePost(uri: string, state: HydrationState): MaybePostView {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,15 @@ Array [
"viewer": Object {},
},
"reply": Object {
"grandparentAuthor": Object {
"did": "user(0)",
"handle": "alice.test",
"labels": Array [],
"viewer": Object {
"blockedBy": false,
"muted": false,
},
},
"parent": Object {
"$type": "app.bsky.feed.defs#postView",
"author": Object {
Expand Down
81 changes: 81 additions & 0 deletions packages/bsky/tests/views/__snapshots__/author-feed.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,32 @@ Array [
"viewer": Object {},
},
"reply": Object {
"grandparentAuthor": Object {
"avatar": "https://bsky.public.url/img/avatar/plain/user(1)/cids(1)@jpeg",
"did": "user(0)",
"displayName": "ali",
"handle": "alice.test",
"labels": Array [
Object {
"cid": "cids(2)",
"cts": "1970-01-01T00:00:00.000Z",
"src": "user(0)",
"uri": "record(1)",
"val": "self-label-a",
},
Object {
"cid": "cids(2)",
"cts": "1970-01-01T00:00:00.000Z",
"src": "user(0)",
"uri": "record(1)",
"val": "self-label-b",
},
],
"viewer": Object {
"blockedBy": false,
"muted": false,
},
},
"parent": Object {
"$type": "app.bsky.feed.defs#postView",
"author": Object {
Expand Down Expand Up @@ -1194,6 +1220,33 @@ Array [
"indexedAt": "1970-01-01T00:00:00.000Z",
},
"reply": Object {
"grandparentAuthor": Object {
"avatar": "https://bsky.public.url/img/avatar/plain/user(1)/cids(1)@jpeg",
"did": "user(0)",
"displayName": "ali",
"handle": "alice.test",
"labels": Array [
Object {
"cid": "cids(2)",
"cts": "1970-01-01T00:00:00.000Z",
"src": "user(0)",
"uri": "record(2)",
"val": "self-label-a",
},
Object {
"cid": "cids(2)",
"cts": "1970-01-01T00:00:00.000Z",
"src": "user(0)",
"uri": "record(2)",
"val": "self-label-b",
},
],
"viewer": Object {
"blockedBy": false,
"followedBy": "record(1)",
"muted": false,
},
},
"parent": Object {
"$type": "app.bsky.feed.defs#postView",
"author": Object {
Expand Down Expand Up @@ -1630,6 +1683,34 @@ Array [
"viewer": Object {},
},
"reply": Object {
"grandparentAuthor": Object {
"avatar": "https://bsky.public.url/img/avatar/plain/user(1)/cids(1)@jpeg",
"did": "user(0)",
"displayName": "ali",
"handle": "alice.test",
"labels": Array [
Object {
"cid": "cids(2)",
"cts": "1970-01-01T00:00:00.000Z",
"src": "user(0)",
"uri": "record(3)",
"val": "self-label-a",
},
Object {
"cid": "cids(2)",
"cts": "1970-01-01T00:00:00.000Z",
"src": "user(0)",
"uri": "record(3)",
"val": "self-label-b",
},
],
"viewer": Object {
"blockedBy": false,
"followedBy": "record(2)",
"following": "record(1)",
"muted": false,
},
},
"parent": Object {
"$type": "app.bsky.feed.defs#postView",
"author": Object {
Expand Down
Loading

0 comments on commit ec40af0

Please sign in to comment.