Bluesky app fork with some witchin' additions 💫 witchsky.app
bluesky fork client

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

authored by

Eric Bailey and committed by
GitHub
27926f09 fe5a623b

+28 -28
+14 -14
src/state/queries/usePostThread/traversal.ts
··· 135 } else if (AppBskyUnspeccedDefs.isThreadItemPost(item.value)) { 136 if (parentMetadata) { 137 /* 138 - * Set this value before incrementing the parent's repliesSeenCounter 139 */ 140 - metadata!.replyIndex = parentMetadata.repliesIndexCounter 141 - // Increment the parent's repliesIndexCounter 142 - parentMetadata.repliesIndexCounter += 1 143 } 144 145 const post = views.threadPost({ ··· 193 storeTraversalMetadata(metadatas, childMetadata) 194 if (childParentMetadata) { 195 /* 196 - * Set this value before incrementing the parent's repliesIndexCounter 197 */ 198 childMetadata!.replyIndex = 199 - childParentMetadata.repliesIndexCounter 200 - childParentMetadata.repliesIndexCounter += 1 201 } 202 203 const childPost = views.threadPost({ ··· 264 if (nextItem?.type === 'threadPost') 265 metadata.nextItemDepth = nextItem?.depth 266 267 - /* 268 - * Item is the last "sibling" if we know for sure we're out of 269 - * replies on the parent (even though this item itself may have its 270 - * own reply branches). 271 */ 272 - const isLastSiblingByCounts = 273 metadata.replyIndex === 274 - metadata.parentMetadata.repliesIndexCounter - 1 275 276 /* 277 * Item can also be the last "sibling" if we know we don't have a ··· 287 * Ok now we can set the last sibling state. 288 */ 289 metadata.isLastSibling = 290 - isLastSiblingByCounts || isImplicitlyLastSibling 291 292 /* 293 * Item is the last "child" in a branch if there is no next item,
··· 135 } else if (AppBskyUnspeccedDefs.isThreadItemPost(item.value)) { 136 if (parentMetadata) { 137 /* 138 + * Set this value before incrementing the `repliesSeenCounter` later 139 + * on, since `repliesSeenCounter` is 1-indexed and `replyIndex` is 140 + * 0-indexed. 141 */ 142 + metadata!.replyIndex = parentMetadata.repliesSeenCounter 143 } 144 145 const post = views.threadPost({ ··· 193 storeTraversalMetadata(metadatas, childMetadata) 194 if (childParentMetadata) { 195 /* 196 + * Set this value before incrementing the 197 + * `repliesSeenCounter` later on, since `repliesSeenCounter` 198 + * is 1-indexed and `replyIndex` is 0-indexed. 199 */ 200 childMetadata!.replyIndex = 201 + childParentMetadata.repliesSeenCounter 202 } 203 204 const childPost = views.threadPost({ ··· 265 if (nextItem?.type === 'threadPost') 266 metadata.nextItemDepth = nextItem?.depth 267 268 + /** 269 + * Item is also the last "sibling" if its index matches the total 270 + * number of replies we're actually able to render to the page. 271 */ 272 + const isLastSiblingDueToMissingReplies = 273 metadata.replyIndex === 274 + metadata.parentMetadata.repliesSeenCounter - 1 275 276 /* 277 * Item can also be the last "sibling" if we know we don't have a ··· 287 * Ok now we can set the last sibling state. 288 */ 289 metadata.isLastSibling = 290 + isImplicitlyLastSibling || isLastSiblingDueToMissingReplies 291 292 /* 293 * Item is the last "child" in a branch if there is no next item,
+14 -13
src/state/queries/usePostThread/types.ts
··· 193 */ 194 repliesUnhydrated: number 195 /** 196 - * The number of replies that have been seen so far in the traversal. 197 - * Excludes replies that are moderated in some way, since those are not 198 - * "seen" on first load. Use `repliesIndexCounter` for the total number of 199 - * replies that were hydrated in the response. 200 * 201 - * After traversal, we can use this to calculate if we actually got all the 202 - * replies we expected, or if some were blocked, etc. 203 */ 204 repliesSeenCounter: number 205 /** 206 - * The total number of replies to this post hydrated in this response. Used 207 - * for populating the `replyIndex` of the post by referencing this value on 208 - * the parent. 209 - */ 210 - repliesIndexCounter: number 211 - /** 212 - * The index-0-based index of this reply in the parent post's replies. 213 */ 214 replyIndex: number 215 /**
··· 193 */ 194 repliesUnhydrated: number 195 /** 196 + * The number of replies that have been "seen" (actually able to be rendered) 197 + * so far in the traversal. Excludes replies that are moderated in some way, 198 + * since those are not "seen" on first load. 199 * 200 + * We use this to compute the `replyIndex` values of the children of this 201 + * parent. E.g. if a reply is not hydrated on the response, or is moderated 202 + * in some way (including by the user), this value is not incremented. So 203 + * this represents the _actual_ index of the reply in the rendered view. 204 + * 205 + * Note: this is a "counter", not an "index". Because this value is 206 + * incremented starting from 0, it is 1-indexed. So to when comparing to the 207 + * `replyIndex`, you'll need to subtract 1 from this value. 208 */ 209 repliesSeenCounter: number 210 /** 211 + * The index-0-based index of this reply in the parent post's replies. This 212 + * is computed from the `repliesSeenCounter` of the parent post, prior to it 213 + * being incremented for this reply. 214 */ 215 replyIndex: number 216 /**
-1
src/state/queries/usePostThread/utils.ts
··· 99 repliesCount, 100 repliesUnhydrated, 101 repliesSeenCounter: 0, 102 - repliesIndexCounter: 0, 103 replyIndex: 0, 104 skippedIndentIndices: new Set<number>(), 105 }
··· 99 repliesCount, 100 repliesUnhydrated, 101 repliesSeenCounter: 0, 102 replyIndex: 0, 103 skippedIndentIndices: new Set<number>(), 104 }