Checkpoint, need to fix blocked posts

This commit is contained in:
Eric Bailey
2025-05-29 11:10:45 -05:00
parent a8dc0c9bb9
commit 70bc131ea2
6 changed files with 100 additions and 144 deletions
+9 -14
View File
@@ -2,7 +2,6 @@ import {View} from 'react-native'
import {msg, Plural, Trans} from '@lingui/macro' import {msg, Plural, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {makeProfileLink} from '#/lib/routes/links'
import {type PostThreadParams, type Slice} from '#/state/queries/usePostThread' import {type PostThreadParams, type Slice} from '#/state/queries/usePostThread'
import { import {
LINEAR_AVI_WIDTH, LINEAR_AVI_WIDTH,
@@ -28,19 +27,22 @@ export function ReadMore({
const indent = Math.max(0, item.indent - 1) const indent = Math.max(0, item.indent - 1)
const spacers = isTreeView const spacers = isTreeView
? Array.from(Array(indent)).map((_, n: number) => ( ? Array.from(Array(indent)).map((_, n: number) => {
const isSkipped = item.skippedIndents.has(n)
return (
<View <View
key={`${item.key}-padding-${n}`} key={`${item.key}-padding-${n}`}
style={[ style={[
t.atoms.border_contrast_low, t.atoms.border_contrast_low,
{ {
borderRightWidth: REPLY_LINE_WIDTH, borderRightWidth: isSkipped ? 0 : REPLY_LINE_WIDTH,
width: TREE_INDENT + TREE_AVI_WIDTH / 2, width: TREE_INDENT + TREE_AVI_WIDTH / 2,
left: 1, left: 1,
}, },
]} ]}
/> />
)) )
})
: null : null
return ( return (
@@ -63,14 +65,7 @@ export function ReadMore({
/> />
<Link <Link
label={_(msg`Read more replies`)} label={_(msg`Read more replies`)}
to={makeProfileLink( to={item.href}
{
did: item.nextAnchorUri.host,
handle: item.nextAnchor.value.post.author.handle,
},
'post',
item.nextAnchorUri.rkey,
)}
style={[a.pt_sm, a.pb_md, a.gap_xs]}> style={[a.pt_sm, a.pb_md, a.gap_xs]}>
{({hovered, pressed}) => { {({hovered, pressed}) => {
return ( return (
@@ -83,8 +78,8 @@ export function ReadMore({
(hovered || pressed) && a.underline, (hovered || pressed) && a.underline,
]}> ]}>
<Trans> <Trans>
Read {item.replyCount} more{' '} Read {item.moreReplies} more{' '}
<Plural one="reply" other="replies" value={item.replyCount} /> <Plural one="reply" other="replies" value={item.moreReplies} />
</Trans> </Trans>
</Text> </Text>
</> </>
+1 -1
View File
@@ -41,7 +41,7 @@ export function usePostThread({
const {data} = await agent.app.bsky.unspecced.getPostThreadV2({ const {data} = await agent.app.bsky.unspecced.getPostThreadV2({
anchor: params.anchor!, anchor: params.anchor!,
branchingFactor: params.view === 'linear' ? 1 : 3, // 100 TODO branchingFactor: params.view === 'linear' ? 1 : 3, // 100 TODO
below: 6, below: 3,
sort: params.sort, sort: params.sort,
prioritizeFollowedUsers: params.prioritizeFollowedUsers, prioritizeFollowedUsers: params.prioritizeFollowedUsers,
}) })
+50 -105
View File
@@ -32,7 +32,6 @@ export function flatten(
}, },
) { ) {
const flattened: Slice[] = sorted.items const flattened: Slice[] = sorted.items
const parents = []
for (let i = 0; i < flattened.length; i++) { for (let i = 0; i < flattened.length; i++) {
const item = flattened[i] const item = flattened[i]
@@ -49,103 +48,6 @@ export function flatten(
key: 'replyComposer', key: 'replyComposer',
}) })
} }
const deepestParent = parents[parents.length - 1]
if (deepestParent) {
// next item is a sibling or an aunt/uncle
if (item.depth <= deepestParent.depth) {
for (let pi = parents.length - 1; pi >= 0; pi--) {
const parent = parents[pi]
if (item.depth <= parent.depth) {
/*
* Find the previous post item and set the read more flags
*/
for (let ui = i - 1; ui >= 0; ui--) {
let prev = flattened[ui]
if (prev.type === 'threadPost') {
prev.ui.precedesParentReadMore =
prev.ui.indent - 1 === parent.ui.indent // true
prev.ui.precedesChildReadMore =
prev.ui.indent === item.ui.indent
break
}
}
flattened.splice(
i + 1 + (pi - parents.length),
0,
views.readMore({
parent,
}),
)
parents.pop()
// skip next iteration
i++
if (view === 'linear') {
break
}
} else {
break
}
}
}
}
if (item.value.moreReplies > 0) {
parents.push(item)
}
const isLastIteration = i === flattened.length - 1
if (isLastIteration) {
const deepestParent = parents[parents.length - 1]
if (deepestParent) {
// next item is a sibling or an aunt/uncle
if (deepestParent.depth <= item.depth) {
for (let pi = parents.length - 1; pi >= 0; pi--) {
const parent = parents[pi]
if (parent.depth <= item.depth) {
/*
* Find the previous post item and set the read more flags
*/
for (let ui = i; ui >= 0; ui--) {
let prev = flattened[ui]
if (prev.type === 'threadPost') {
prev.ui.precedesParentReadMore =
prev.ui.indent - 1 === parent.ui.indent
prev.ui.precedesChildReadMore =
prev.ui.indent === item.ui.indent
break
}
}
flattened.splice(
i + 2 + (pi - parents.length),
0,
views.readMore({
parent,
}),
)
parents.pop()
// skip next iteration
i++
if (view === 'linear') {
break
}
} else {
break
}
}
}
}
}
} }
} }
@@ -310,10 +212,23 @@ export function sort(
continue traversal continue traversal
} else if (AppBskyUnspeccedGetPostThreadV2.isThreadItemPost(item.value)) { } else if (AppBskyUnspeccedGetPostThreadV2.isThreadItemPost(item.value)) {
if (parentMetadata) { if (parentMetadata) {
if (metadata) {
metadata.replyIndex = parentMetadata.seenReplies
}
parentMetadata.seenReplies += 1 parentMetadata.seenReplies += 1
if (metadata) { if (metadata) {
metadata.isLastSibling = metadata.isLastSibling =
parentMetadata.replies === parentMetadata.seenReplies parentMetadata.replies - parentMetadata.unhydratedReplies ===
parentMetadata.seenReplies
if (
parentMetadata.unhydratedReplies > 0 &&
metadata.isLastSibling
) {
metadata.upcomingParentReadMore = parentMetadata
}
} }
} }
@@ -369,8 +284,16 @@ export function sort(
if (childParentMetadata) { if (childParentMetadata) {
childParentMetadata.seenReplies += 1 childParentMetadata.seenReplies += 1
childMetadata.isLastSibling = childMetadata.isLastSibling =
childParentMetadata.replies === childParentMetadata.replies -
childParentMetadata.unhydratedReplies ===
childParentMetadata.seenReplies childParentMetadata.seenReplies
if (
childParentMetadata.unhydratedReplies > 0 &&
childMetadata.isLastSibling
) {
childMetadata.upcomingParentReadMore = childParentMetadata
}
} }
metadatas.set(item.uri, childMetadata) metadatas.set(item.uri, childMetadata)
metadatas.set(childMetadata.text, childMetadata) // TODO debugging metadatas.set(childMetadata.text, childMetadata) // TODO debugging
@@ -418,7 +341,9 @@ export function sort(
} }
} }
for (const item of items) { for (let i = 0; i < items.length; i++) {
const item = items[i]
if (item.type === 'threadPost') { if (item.type === 'threadPost') {
const metadata = metadatas.get(item.uri) const metadata = metadatas.get(item.uri)
if (metadata) { if (metadata) {
@@ -426,9 +351,31 @@ export function sort(
metadata.skippedIndents = new Set([ metadata.skippedIndents = new Set([
...metadata.parentMetadata.skippedIndents, ...metadata.parentMetadata.skippedIndents,
]) ])
if (
metadata.isLastSibling &&
metadata.parentMetadata.unhydratedReplies <= 0
) {
metadata.skippedIndents.add(item.depth - 2)
}
} }
if (metadata.isLastSibling) {
metadata.skippedIndents.add(item.depth - 2) if (
metadata.unhydratedReplies > 0 &&
(metadata.nextItemDepth === undefined ||
metadata.nextItemDepth <= item.depth)
) {
items.splice(i + 1, 0, views.readMore(metadata))
i++
}
if (metadata.upcomingParentReadMore && metadata.isDeadEnd) {
items.splice(
i + 1,
0,
views.readMore(metadata.upcomingParentReadMore),
)
i++
} }
item.ui = getThreadPostUI(metadata) item.ui = getThreadPostUI(metadata)
@@ -436,8 +383,6 @@ export function sort(
} }
} }
// console.log(metadatas)
return { return {
items, items,
hidden, hidden,
+3 -4
View File
@@ -101,14 +101,13 @@ export type Slice =
type: 'readMore' type: 'readMore'
key: string key: string
indent: number indent: number
replyCount: number href: string
nextAnchor: Extract<Slice, {type: 'threadPost'}> moreReplies: number
nextAnchorUri: AtUri skippedIndents: Set<number>
} }
export type TraversalMetadata = { export type TraversalMetadata = {
depth: number depth: number
indent: number
replies: number replies: number
unhydratedReplies: number unhydratedReplies: number
seenReplies: number seenReplies: number
+15 -8
View File
@@ -65,11 +65,9 @@ export function getTraversalMetadata({
const hasBranchingReplies = replies > 1 && replies - unhydratedReplies > 1 const hasBranchingReplies = replies > 1 && replies - unhydratedReplies > 1
return { return {
uri: item.uri,
depth: item.depth, depth: item.depth,
// TODO maybe not used authorHandle: item.value.post.author.handle,
indent: parentMetadata?.hasBranchingReplies
? item.depth
: parentMetadata?.indent || item.depth,
replies, replies,
unhydratedReplies, unhydratedReplies,
seenReplies: 0, seenReplies: 0,
@@ -79,6 +77,13 @@ export function getTraversalMetadata({
skippedIndents: new Set(), skippedIndents: new Set(),
prevItemDepth: prevItem?.depth, prevItemDepth: prevItem?.depth,
nextItemDepth: nextItem?.depth, nextItemDepth: nextItem?.depth,
/*
* If there are no slices below this one, or the next slice is less
* indented than the computed indent for this post.
*/
isDeadEnd: nextItem?.depth === undefined || nextItem?.depth < item.depth,
upcomingParentReadMore: parentMetadata?.upcomingParentReadMore || undefined,
// TODO non-spec // TODO non-spec
text: getPostRecord(item.value.post).text, text: getPostRecord(item.value.post).text,
@@ -87,25 +92,27 @@ export function getTraversalMetadata({
export function getThreadPostUI({ export function getThreadPostUI({
depth, depth,
indent,
replies, replies,
parentMetadata, parentMetadata,
prevItemDepth, prevItemDepth,
nextItemDepth, isDeadEnd,
skippedIndents, skippedIndents,
seenReplies,
unhydratedReplies,
}: TraversalMetadata): Extract<Slice, {type: 'threadPost'}>['ui'] { }: TraversalMetadata): Extract<Slice, {type: 'threadPost'}>['ui'] {
const isReplyAndHasReplies = depth > 0 && replies > 0 && ((replies - unhydratedReplies) === seenReplies || seenReplies > 0)
return { return {
isAnchor: depth === 0, isAnchor: depth === 0,
showParentReplyLine: showParentReplyLine:
!!prevItemDepth && prevItemDepth !== 0 && prevItemDepth < depth, !!prevItemDepth && prevItemDepth !== 0 && prevItemDepth < depth,
showChildReplyLine: replies > 0, showChildReplyLine: depth < 0 || isReplyAndHasReplies,
indent: depth, indent: depth,
parentHasBranchingReplies: !!parentMetadata?.hasBranchingReplies, parentHasBranchingReplies: !!parentMetadata?.hasBranchingReplies,
/* /*
* If there are no slices below this one, or the next slice is less * If there are no slices below this one, or the next slice is less
* indented than the computed indent for this post. * indented than the computed indent for this post.
*/ */
isDeadEnd: nextItemDepth === undefined || nextItemDepth < indent, isDeadEnd, //nextItemDepth === undefined || nextItemDepth < depth,
skippedIndents, skippedIndents,
} }
} }
+22 -12
View File
@@ -8,7 +8,8 @@ import {
type ModerationOpts, type ModerationOpts,
} from '@atproto/api' } from '@atproto/api'
import {type Slice} from '#/state/queries/usePostThread/types' import {makeProfileLink} from '#/lib/routes/links'
import {type Slice, type TraversalMetadata} from '#/state/queries/usePostThread/types'
export function threadPostNoUnauthenticated({ export function threadPostNoUnauthenticated({
uri, uri,
@@ -94,19 +95,28 @@ export function threadPost({
} }
export function readMore({ export function readMore({
parent, uri,
}: { authorHandle,
parent: Extract<Slice, {type: 'threadPost'}> unhydratedReplies: moreReplies,
}) { depth: indent,
skippedIndents,
}: TraversalMetadata): Extract<Slice, {type: 'readMore'}> {
const urip = new AtUri(uri)
const href = makeProfileLink(
{
did: urip.host,
handle: authorHandle,
},
'post',
urip.rkey,
)
return { return {
type: 'readMore' as const, type: 'readMore' as const,
key: `readMore:${parent.uri}`, key: `readMore:${uri}`,
indent: parent.ui.parentHasBranchingReplies href,
? parent.depth moreReplies,
: parent.ui.indent, indent,
replyCount: parent.value.moreReplies, skippedIndents,
nextAnchor: parent,
nextAnchorUri: new AtUri(parent.uri),
} }
} }