Checkpoint cleanup

This commit is contained in:
Eric Bailey
2025-05-28 16:23:50 -05:00
parent 2c80c1f288
commit a8dc0c9bb9
4 changed files with 37 additions and 105 deletions
+21 -41
View File
@@ -6,14 +6,12 @@ import {
import { import {
HiddenReplyKind, HiddenReplyKind,
type NTraversalMetadata,
type PostThreadParams, type PostThreadParams,
type Slice, type Slice,
type TraversalMetadata, type TraversalMetadata,
} from '#/state/queries/usePostThread/types' } from '#/state/queries/usePostThread/types'
import { import {
getPostRecord, getPostRecord,
getPostTraversalMetadata,
getThreadPostUI, getThreadPostUI,
getTraversalMetadata, getTraversalMetadata,
} from '#/state/queries/usePostThread/utils' } from '#/state/queries/usePostThread/utils'
@@ -205,15 +203,29 @@ export function sort(
const items: Slice[] = [] const items: Slice[] = []
const hidden: Slice[] = [] const hidden: Slice[] = []
const muted: Slice[] = [] const muted: Slice[] = []
const postDataMap = new Map<string, TraversalMetadata | undefined>() const metadatas = new Map<string, TraversalMetadata>()
const metadatas = new Map<string, NTraversalMetadata>()
// @ts-ignore // @ts-ignore
window.__data = metadatas // for debugging window.__data = metadatas // for debugging
traversal: for (let i = 0; i < thread.length; i++) { traversal: for (let i = 0; i < thread.length; i++) {
const item = thread[i] const item = thread[i]
let parentMetadata: TraversalMetadata | undefined
let metadata: TraversalMetadata | undefined
if (AppBskyUnspeccedGetPostThreadV2.isThreadItemPost(item.value)) {
parentMetadata = metadatas.get(
getPostRecord(item.value.post).reply?.parent?.uri || '',
)
metadata = getTraversalMetadata({
item,
parentMetadata,
prevItem: thread.at(i - 1),
nextItem: thread.at(i + 1),
})
metadatas.set(item.uri, metadata)
metadatas.set(metadata.text, metadata) // TODO debugging
}
if (item.depth < 0) { if (item.depth < 0) {
/* /*
@@ -240,17 +252,12 @@ export function sort(
uri: item.uri, uri: item.uri,
depth: item.depth, depth: item.depth,
value: item.value, value: item.value,
oneUp: thread[i - 1],
oneDown: thread[i + 1],
moderationOpts, moderationOpts,
}) })
postDataMap.set(item.uri, getPostTraversalMetadata(post))
items.push(post) items.push(post)
parentTraversal: for (let pi = i - 1; pi >= 0; pi--) { parentTraversal: for (let pi = i - 1; pi >= 0; pi--) {
const parentOneDown = thread[pi + 1]
const parent = thread[pi] const parent = thread[pi]
const parentOneUp = thread[pi - 1]
if ( if (
AppBskyUnspeccedGetPostThreadV2.isThreadItemNoUnauthenticated( AppBskyUnspeccedGetPostThreadV2.isThreadItemNoUnauthenticated(
@@ -277,8 +284,6 @@ export function sort(
uri: parent.uri, uri: parent.uri,
depth: parent.depth, depth: parent.depth,
value: parent.value, value: parent.value,
oneUp: parentOneUp,
oneDown: parentOneDown,
moderationOpts, moderationOpts,
}), }),
) )
@@ -304,37 +309,20 @@ export function sort(
i = branch.end i = branch.end
continue traversal continue traversal
} else if (AppBskyUnspeccedGetPostThreadV2.isThreadItemPost(item.value)) { } else if (AppBskyUnspeccedGetPostThreadV2.isThreadItemPost(item.value)) {
const parentMetadata = metadatas.get(
getPostRecord(item.value.post).reply?.parent?.uri || '',
)
const oneUp = thread.at(i - 1)
const oneDown = thread.at(i + 1)
const metadata = getTraversalMetadata({
item,
parentMetadata,
prevItem: oneUp,
nextItem: oneDown,
})
if (parentMetadata) { if (parentMetadata) {
parentMetadata.seenReplies += 1 parentMetadata.seenReplies += 1
if (metadata) {
metadata.isLastSibling = metadata.isLastSibling =
parentMetadata.replies === parentMetadata.seenReplies parentMetadata.replies === parentMetadata.seenReplies
} }
metadatas.set(item.uri, metadata) }
metadatas.set(metadata.text, metadata) // TODO debugging
const post = views.threadPost({ const post = views.threadPost({
uri: item.uri, uri: item.uri,
depth: item.depth, depth: item.depth,
value: item.value, value: item.value,
traversalMetadata: postDataMap.get(
getPostRecord(item.value.post)?.reply?.parent?.uri || '',
),
oneUp,
oneDown,
moderationOpts, moderationOpts,
}) })
postDataMap.set(item.uri, getPostTraversalMetadata(post))
const postMod = getModerationState(post.moderation) const postMod = getModerationState(post.moderation)
const postIsHiddenByThreadgate = threadgateHiddenReplies.has(item.uri) const postIsHiddenByThreadgate = threadgateHiddenReplies.has(item.uri)
const postIsModerated = const postIsModerated =
@@ -372,12 +360,10 @@ export function sort(
const childParentMetadata = metadatas.get( const childParentMetadata = metadatas.get(
getPostRecord(child.value.post).reply?.parent?.uri || '', getPostRecord(child.value.post).reply?.parent?.uri || '',
) )
const prevItem = thread[ci - 1]
const nextItem = thread[ci + 1]
const childMetadata = getTraversalMetadata({ const childMetadata = getTraversalMetadata({
item: child, item: child,
prevItem, prevItem: thread[ci - 1],
nextItem, nextItem: thread[ci + 1],
parentMetadata: childParentMetadata, parentMetadata: childParentMetadata,
}) })
if (childParentMetadata) { if (childParentMetadata) {
@@ -393,14 +379,8 @@ export function sort(
uri: child.uri, uri: child.uri,
depth: child.depth, depth: child.depth,
value: child.value, value: child.value,
traversalMetadata: postDataMap.get(
getPostRecord(child.value.post)?.reply?.parent?.uri || '',
),
oneUp: prevItem,
oneDown: nextItem,
moderationOpts, moderationOpts,
}) })
postDataMap.set(child.uri, getPostTraversalMetadata(childPost))
const childPostMod = getModerationState(childPost.moderation) const childPostMod = getModerationState(childPost.moderation)
const childPostIsHiddenByThreadgate = const childPostIsHiddenByThreadgate =
threadgateHiddenReplies.has(child.uri) threadgateHiddenReplies.has(child.uri)
+2 -8
View File
@@ -52,6 +52,7 @@ export type Slice =
indent: number indent: number
parentHasBranchingReplies: boolean parentHasBranchingReplies: boolean
isDeadEnd: boolean isDeadEnd: boolean
skippedIndents: Set<number>
/** /**
* Populated during the final traversal of the thread. Denotes whether * Populated during the final traversal of the thread. Denotes whether
* there is a "Read more" link for the parent immediately following * there is a "Read more" link for the parent immediately following
@@ -64,8 +65,6 @@ export type Slice =
* this item. * this item.
*/ */
precedesChildReadMore?: boolean precedesChildReadMore?: boolean
skippedIndents: Set<number>
[key: string]: any
} }
} }
| { | {
@@ -108,11 +107,6 @@ export type Slice =
} }
export type TraversalMetadata = { export type TraversalMetadata = {
indent: number
hasBranchingReplies: boolean
}
export type NTraversalMetadata = {
depth: number depth: number
indent: number indent: number
replies: number replies: number
@@ -120,7 +114,7 @@ export type NTraversalMetadata = {
seenReplies: number seenReplies: number
hasBranchingReplies: boolean hasBranchingReplies: boolean
isLastSibling: boolean isLastSibling: boolean
parentMetadata?: NTraversalMetadata parentMetadata?: TraversalMetadata
prevItemDepth?: number prevItemDepth?: number
nextItemDepth?: number nextItemDepth?: number
skippedIndents: Set<number> skippedIndents: Set<number>
+9 -22
View File
@@ -7,7 +7,6 @@ import {
} from '@atproto/api' } from '@atproto/api'
import { import {
type NTraversalMetadata,
type Slice, type Slice,
type TraversalMetadata, type TraversalMetadata,
} from '#/state/queries/usePostThread/types' } from '#/state/queries/usePostThread/types'
@@ -41,24 +40,6 @@ export function getPostRecord(post: AppBskyFeedDefs.PostView) {
return post.record as AppBskyFeedPost.Record return post.record as AppBskyFeedPost.Record
} }
export function getPostTraversalMetadata(
item: Extract<Slice, {type: 'threadPost'}>,
): TraversalMetadata | undefined {
if (!AppBskyUnspeccedGetPostThreadV2.isThreadItemPost(item.value)) return
const replyCount = item.value.post.replyCount || 0
const unhydratedReplies = item.value.moreReplies || 0
return {
indent: item.ui.indent,
/**
* If the post has more than a single reply, and the total reply count
* minus the number of replies not present in the response is greater than
* 1, then we must have more than a single branch of replies present in the
* response, which can affect how we render tree view.
*/
hasBranchingReplies: replyCount > 1 && replyCount - unhydratedReplies > 1,
}
}
export function getTraversalMetadata({ export function getTraversalMetadata({
item, item,
prevItem, prevItem,
@@ -68,13 +49,19 @@ export function getTraversalMetadata({
item: AppBskyUnspeccedGetPostThreadV2.ThreadItem item: AppBskyUnspeccedGetPostThreadV2.ThreadItem
prevItem?: AppBskyUnspeccedGetPostThreadV2.ThreadItem prevItem?: AppBskyUnspeccedGetPostThreadV2.ThreadItem
nextItem?: AppBskyUnspeccedGetPostThreadV2.ThreadItem nextItem?: AppBskyUnspeccedGetPostThreadV2.ThreadItem
parentMetadata?: NTraversalMetadata parentMetadata?: TraversalMetadata
}): NTraversalMetadata { }): TraversalMetadata {
if (!AppBskyUnspeccedGetPostThreadV2.isThreadItemPost(item.value)) { if (!AppBskyUnspeccedGetPostThreadV2.isThreadItemPost(item.value)) {
throw new Error(`Expected thread item to be a post`) throw new Error(`Expected thread item to be a post`)
} }
const replies = item.value.post.replyCount || 0 const replies = item.value.post.replyCount || 0
const unhydratedReplies = item.value.moreReplies || 0 const unhydratedReplies = item.value.moreReplies || 0
/**
* If the post has more than a single reply, and the total reply count
* minus the number of replies not present in the response is greater than
* 1, then we must have more than a single branch of replies present in the
* response, which can affect how we render tree view.
*/
const hasBranchingReplies = replies > 1 && replies - unhydratedReplies > 1 const hasBranchingReplies = replies > 1 && replies - unhydratedReplies > 1
return { return {
@@ -106,7 +93,7 @@ export function getThreadPostUI({
prevItemDepth, prevItemDepth,
nextItemDepth, nextItemDepth,
skippedIndents, skippedIndents,
}: NTraversalMetadata) { }: TraversalMetadata): Extract<Slice, {type: 'threadPost'}>['ui'] {
return { return {
isAnchor: depth === 0, isAnchor: depth === 0,
showParentReplyLine: showParentReplyLine:
+3 -32
View File
@@ -8,10 +8,7 @@ import {
type ModerationOpts, type ModerationOpts,
} from '@atproto/api' } from '@atproto/api'
import { import {type Slice} from '#/state/queries/usePostThread/types'
type Slice,
type TraversalMetadata,
} from '#/state/queries/usePostThread/types'
export function threadPostNoUnauthenticated({ export function threadPostNoUnauthenticated({
uri, uri,
@@ -68,28 +65,13 @@ export function threadPost({
uri, uri,
depth, depth,
value, value,
oneUp,
oneDown,
moderationOpts, moderationOpts,
traversalMetadata,
}: { }: {
uri: string uri: string
depth: number depth: number
value: $Typed<AppBskyUnspeccedGetPostThreadV2.ThreadItemPost> value: $Typed<AppBskyUnspeccedGetPostThreadV2.ThreadItemPost>
oneUp?: AppBskyUnspeccedGetPostThreadV2.OutputSchema['thread'][number]
oneDown?: AppBskyUnspeccedGetPostThreadV2.OutputSchema['thread'][number]
moderationOpts: ModerationOpts moderationOpts: ModerationOpts
traversalMetadata?: TraversalMetadata
}): Extract<Slice, {type: 'threadPost'}> { }): Extract<Slice, {type: 'threadPost'}> {
const parentHasBranchingReplies = !!traversalMetadata?.hasBranchingReplies
/*
* Indent differs from depth in cases where the tree has no branches, and so
* we can maintain the more shallow depth of the parent.
*/
const indent = parentHasBranchingReplies
? depth
: traversalMetadata?.indent || depth
return { return {
type: 'threadPost', type: 'threadPost',
key: uri, key: uri,
@@ -106,19 +88,8 @@ export function threadPost({
}, },
}, },
moderation: moderatePost(value.post, moderationOpts), moderation: moderatePost(value.post, moderationOpts),
ui: { // @ts-ignore populated by the traversal
isAnchor: depth === 0, ui: {},
showParentReplyLine: !!oneUp && oneUp.depth !== 0 && oneUp.depth < depth,
showChildReplyLine: (value.post.replyCount || 0) > 0,
indent,
parentHasBranchingReplies,
/*
* If there are no slices below this one, or the next slice is less
* indented than the computed indent for this post.
*/
isDeadEnd: !oneDown || oneDown?.depth < indent,
skippedIndents: new Set(),
},
} }
} }