Fix bug where replying to grandparents re-orders thread (#8662)

* Fix bug where optimistic replies were inserted above the root post

* Rename variables for clarity
This commit is contained in:
Eric Bailey
2025-07-17 13:22:09 -05:00
committed by GitHub
parent d13a2e5f69
commit 00b017804b
2 changed files with 32 additions and 23 deletions
+26 -23
View File
@@ -77,53 +77,56 @@ export function createCacheMutator({
function mutator<T>(thread: ApiThreadItem[]): T[] {
for (let i = 0; i < thread.length; i++) {
const existingParent = thread[i]
if (!AppBskyUnspeccedDefs.isThreadItemPost(existingParent.value))
continue
if (existingParent.uri !== parentUri) continue
const parent = thread[i]
if (!AppBskyUnspeccedDefs.isThreadItemPost(parent.value)) continue
if (parent.uri !== parentUri) continue
/*
* Update parent data
*/
existingParent.value.post = {
...existingParent.value.post,
replyCount: (existingParent.value.post.replyCount || 0) + 1,
parent.value.post = {
...parent.value.post,
replyCount: (parent.value.post.replyCount || 0) + 1,
}
const opDid = getRootPostAtUri(existingParent.value.post)?.host
const nextItem = thread.at(i + 1)
const isReplyToRoot = existingParent.depth === 0
const opDid = getRootPostAtUri(parent.value.post)?.host
const nextPreexistingItem = thread.at(i + 1)
const isEndOfReplyChain =
!nextItem || nextItem.depth <= existingParent.depth
const firstReply = replies.at(0)
!nextPreexistingItem || nextPreexistingItem.depth <= parent.depth
const isParentRoot = parent.depth === 0
const isParentBelowRoot = parent.depth > 0
const optimisticReply = replies.at(0)
const opIsReplier = AppBskyUnspeccedDefs.isThreadItemPost(
firstReply?.value,
optimisticReply?.value,
)
? opDid === firstReply.value.post.author.did
? opDid === optimisticReply.value.post.author.did
: false
/*
* Always insert replies if the following conditions are met.
* Always insert replies if the following conditions are met. Max
* depth checks are handled below.
*/
const shouldAlwaysInsertReplies =
isReplyToRoot ||
params.view === 'tree' ||
const canAlwaysInsertReplies =
isParentRoot ||
(params.view === 'tree' && isParentBelowRoot) ||
(params.view === 'linear' && isEndOfReplyChain)
/*
* Maybe insert replies if the replier is the OP and certain conditions are met
* Maybe insert replies if we're in linear view, the replier is the
* OP, and certain conditions are met
*/
const shouldReplaceWithOPReplies =
!isReplyToRoot && params.view === 'linear' && opIsReplier
params.view === 'linear' && opIsReplier && isParentBelowRoot
if (shouldAlwaysInsertReplies || shouldReplaceWithOPReplies) {
const branch = getBranch(thread, i, existingParent.depth)
if (canAlwaysInsertReplies || shouldReplaceWithOPReplies) {
const branch = getBranch(thread, i, parent.depth)
/*
* OP insertions replace other replies _in linear view_.
*/
const itemsToRemove = shouldReplaceWithOPReplies ? branch.length : 0
const itemsToInsert = replies
.map((r, ri) => {
r.depth = existingParent.depth + 1 + ri
r.depth = parent.depth + 1 + ri
return r
})
.filter(r => {
+6
View File
@@ -33,6 +33,12 @@ export function getRootPostAtUri(post: AppBskyFeedDefs.PostView) {
AppBskyFeedPost.isRecord,
)
) {
/**
* If the record has no `reply` field, it is a root post.
*/
if (!post.record.reply) {
return new AtUri(post.uri)
}
if (post.record.reply?.root?.uri) {
return new AtUri(post.record.reply.root.uri)
}