From 27926f093c847b7f51d3749c0eab455b5fce29dc Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Wed, 15 Oct 2025 11:13:08 -0500 Subject: [PATCH] Fix computation of `isLastSibling`, `isLastChild`, and `replyIndex` (#9202) * Fix computation of isLastSibling and isLastChild to account for muted or otherwise hidden replies * Update comments * isLastSiblingByCounts isn't needed, should rely only on the count of replies seen * The counters serve the same purpose, we only need to know the count of the actual replies rendered to the view in order to calculate the replyIndex * Remove redundant check --- src/state/queries/usePostThread/traversal.ts | 28 ++++++++++---------- src/state/queries/usePostThread/types.ts | 27 ++++++++++--------- src/state/queries/usePostThread/utils.ts | 1 - 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/state/queries/usePostThread/traversal.ts b/src/state/queries/usePostThread/traversal.ts index 2e7693fab3..f9b06cf645 100644 --- a/src/state/queries/usePostThread/traversal.ts +++ b/src/state/queries/usePostThread/traversal.ts @@ -135,11 +135,11 @@ export function sortAndAnnotateThreadItems( } else if (AppBskyUnspeccedDefs.isThreadItemPost(item.value)) { if (parentMetadata) { /* - * Set this value before incrementing the parent's repliesSeenCounter + * Set this value before incrementing the `repliesSeenCounter` later + * on, since `repliesSeenCounter` is 1-indexed and `replyIndex` is + * 0-indexed. */ - metadata!.replyIndex = parentMetadata.repliesIndexCounter - // Increment the parent's repliesIndexCounter - parentMetadata.repliesIndexCounter += 1 + metadata!.replyIndex = parentMetadata.repliesSeenCounter } const post = views.threadPost({ @@ -193,11 +193,12 @@ export function sortAndAnnotateThreadItems( storeTraversalMetadata(metadatas, childMetadata) if (childParentMetadata) { /* - * Set this value before incrementing the parent's repliesIndexCounter + * Set this value before incrementing the + * `repliesSeenCounter` later on, since `repliesSeenCounter` + * is 1-indexed and `replyIndex` is 0-indexed. */ childMetadata!.replyIndex = - childParentMetadata.repliesIndexCounter - childParentMetadata.repliesIndexCounter += 1 + childParentMetadata.repliesSeenCounter } const childPost = views.threadPost({ @@ -264,14 +265,13 @@ export function sortAndAnnotateThreadItems( if (nextItem?.type === 'threadPost') metadata.nextItemDepth = nextItem?.depth - /* - * Item is the last "sibling" if we know for sure we're out of - * replies on the parent (even though this item itself may have its - * own reply branches). + /** + * Item is also the last "sibling" if its index matches the total + * number of replies we're actually able to render to the page. */ - const isLastSiblingByCounts = + const isLastSiblingDueToMissingReplies = metadata.replyIndex === - metadata.parentMetadata.repliesIndexCounter - 1 + metadata.parentMetadata.repliesSeenCounter - 1 /* * Item can also be the last "sibling" if we know we don't have a @@ -287,7 +287,7 @@ export function sortAndAnnotateThreadItems( * Ok now we can set the last sibling state. */ metadata.isLastSibling = - isLastSiblingByCounts || isImplicitlyLastSibling + isImplicitlyLastSibling || isLastSiblingDueToMissingReplies /* * Item is the last "child" in a branch if there is no next item, diff --git a/src/state/queries/usePostThread/types.ts b/src/state/queries/usePostThread/types.ts index 5df7c2e42e..3e84dbfc71 100644 --- a/src/state/queries/usePostThread/types.ts +++ b/src/state/queries/usePostThread/types.ts @@ -193,23 +193,24 @@ export type TraversalMetadata = { */ repliesUnhydrated: number /** - * The number of replies that have been seen so far in the traversal. - * Excludes replies that are moderated in some way, since those are not - * "seen" on first load. Use `repliesIndexCounter` for the total number of - * replies that were hydrated in the response. + * The number of replies that have been "seen" (actually able to be rendered) + * so far in the traversal. Excludes replies that are moderated in some way, + * since those are not "seen" on first load. * - * After traversal, we can use this to calculate if we actually got all the - * replies we expected, or if some were blocked, etc. + * We use this to compute the `replyIndex` values of the children of this + * parent. E.g. if a reply is not hydrated on the response, or is moderated + * in some way (including by the user), this value is not incremented. So + * this represents the _actual_ index of the reply in the rendered view. + * + * Note: this is a "counter", not an "index". Because this value is + * incremented starting from 0, it is 1-indexed. So to when comparing to the + * `replyIndex`, you'll need to subtract 1 from this value. */ repliesSeenCounter: number /** - * The total number of replies to this post hydrated in this response. Used - * for populating the `replyIndex` of the post by referencing this value on - * the parent. - */ - repliesIndexCounter: number - /** - * The index-0-based index of this reply in the parent post's replies. + * The index-0-based index of this reply in the parent post's replies. This + * is computed from the `repliesSeenCounter` of the parent post, prior to it + * being incremented for this reply. */ replyIndex: number /** diff --git a/src/state/queries/usePostThread/utils.ts b/src/state/queries/usePostThread/utils.ts index 265bf7f5fb..be3be36b85 100644 --- a/src/state/queries/usePostThread/utils.ts +++ b/src/state/queries/usePostThread/utils.ts @@ -99,7 +99,6 @@ export function getTraversalMetadata({ repliesCount, repliesUnhydrated, repliesSeenCounter: 0, - repliesIndexCounter: 0, replyIndex: 0, skippedIndentIndices: new Set(), }