diff --git a/src/screens/PostThread/components/ReadMore.tsx b/src/screens/PostThread/components/ReadMore.tsx
index a6b6564db3..ee7a97706e 100644
--- a/src/screens/PostThread/components/ReadMore.tsx
+++ b/src/screens/PostThread/components/ReadMore.tsx
@@ -2,7 +2,6 @@ import {View} from 'react-native'
import {msg, Plural, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
-import {makeProfileLink} from '#/lib/routes/links'
import {type PostThreadParams, type Slice} from '#/state/queries/usePostThread'
import {
LINEAR_AVI_WIDTH,
@@ -28,19 +27,22 @@ export function ReadMore({
const indent = Math.max(0, item.indent - 1)
const spacers = isTreeView
- ? Array.from(Array(indent)).map((_, n: number) => (
+ ? Array.from(Array(indent)).map((_, n: number) => {
+ const isSkipped = item.skippedIndents.has(n)
+ return (
- ))
+ )
+ })
: null
return (
@@ -63,14 +65,7 @@ export function ReadMore({
/>
{({hovered, pressed}) => {
return (
@@ -83,8 +78,8 @@ export function ReadMore({
(hovered || pressed) && a.underline,
]}>
- Read {item.replyCount} more{' '}
-
+ Read {item.moreReplies} more{' '}
+
>
diff --git a/src/state/queries/usePostThread/index.ts b/src/state/queries/usePostThread/index.ts
index b553399dc5..8c4b509359 100644
--- a/src/state/queries/usePostThread/index.ts
+++ b/src/state/queries/usePostThread/index.ts
@@ -41,7 +41,7 @@ export function usePostThread({
const {data} = await agent.app.bsky.unspecced.getPostThreadV2({
anchor: params.anchor!,
branchingFactor: params.view === 'linear' ? 1 : 3, // 100 TODO
- below: 6,
+ below: 3,
sort: params.sort,
prioritizeFollowedUsers: params.prioritizeFollowedUsers,
})
diff --git a/src/state/queries/usePostThread/traversal.ts b/src/state/queries/usePostThread/traversal.ts
index 537cc2532e..6c7d9e331c 100644
--- a/src/state/queries/usePostThread/traversal.ts
+++ b/src/state/queries/usePostThread/traversal.ts
@@ -32,7 +32,6 @@ export function flatten(
},
) {
const flattened: Slice[] = sorted.items
- const parents = []
for (let i = 0; i < flattened.length; i++) {
const item = flattened[i]
@@ -49,103 +48,6 @@ export function flatten(
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
} else if (AppBskyUnspeccedGetPostThreadV2.isThreadItemPost(item.value)) {
if (parentMetadata) {
+ if (metadata) {
+ metadata.replyIndex = parentMetadata.seenReplies
+ }
+
parentMetadata.seenReplies += 1
+
if (metadata) {
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) {
childParentMetadata.seenReplies += 1
childMetadata.isLastSibling =
- childParentMetadata.replies ===
+ childParentMetadata.replies -
+ childParentMetadata.unhydratedReplies ===
childParentMetadata.seenReplies
+
+ if (
+ childParentMetadata.unhydratedReplies > 0 &&
+ childMetadata.isLastSibling
+ ) {
+ childMetadata.upcomingParentReadMore = childParentMetadata
+ }
}
metadatas.set(item.uri, childMetadata)
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') {
const metadata = metadatas.get(item.uri)
if (metadata) {
@@ -426,9 +351,31 @@ export function sort(
metadata.skippedIndents = new Set([
...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)
@@ -436,8 +383,6 @@ export function sort(
}
}
- // console.log(metadatas)
-
return {
items,
hidden,
diff --git a/src/state/queries/usePostThread/types.ts b/src/state/queries/usePostThread/types.ts
index 8d509ce43a..2b9a360f59 100644
--- a/src/state/queries/usePostThread/types.ts
+++ b/src/state/queries/usePostThread/types.ts
@@ -101,14 +101,13 @@ export type Slice =
type: 'readMore'
key: string
indent: number
- replyCount: number
- nextAnchor: Extract
- nextAnchorUri: AtUri
+ href: string
+ moreReplies: number
+ skippedIndents: Set
}
export type TraversalMetadata = {
depth: number
- indent: number
replies: number
unhydratedReplies: number
seenReplies: number
diff --git a/src/state/queries/usePostThread/utils.ts b/src/state/queries/usePostThread/utils.ts
index 509c340bf2..2800ace92b 100644
--- a/src/state/queries/usePostThread/utils.ts
+++ b/src/state/queries/usePostThread/utils.ts
@@ -65,11 +65,9 @@ export function getTraversalMetadata({
const hasBranchingReplies = replies > 1 && replies - unhydratedReplies > 1
return {
+ uri: item.uri,
depth: item.depth,
- // TODO maybe not used
- indent: parentMetadata?.hasBranchingReplies
- ? item.depth
- : parentMetadata?.indent || item.depth,
+ authorHandle: item.value.post.author.handle,
replies,
unhydratedReplies,
seenReplies: 0,
@@ -79,6 +77,13 @@ export function getTraversalMetadata({
skippedIndents: new Set(),
prevItemDepth: prevItem?.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
text: getPostRecord(item.value.post).text,
@@ -87,25 +92,27 @@ export function getTraversalMetadata({
export function getThreadPostUI({
depth,
- indent,
replies,
parentMetadata,
prevItemDepth,
- nextItemDepth,
+ isDeadEnd,
skippedIndents,
+ seenReplies,
+ unhydratedReplies,
}: TraversalMetadata): Extract['ui'] {
+ const isReplyAndHasReplies = depth > 0 && replies > 0 && ((replies - unhydratedReplies) === seenReplies || seenReplies > 0)
return {
isAnchor: depth === 0,
showParentReplyLine:
!!prevItemDepth && prevItemDepth !== 0 && prevItemDepth < depth,
- showChildReplyLine: replies > 0,
+ showChildReplyLine: depth < 0 || isReplyAndHasReplies,
indent: depth,
parentHasBranchingReplies: !!parentMetadata?.hasBranchingReplies,
/*
* If there are no slices below this one, or the next slice is less
* indented than the computed indent for this post.
*/
- isDeadEnd: nextItemDepth === undefined || nextItemDepth < indent,
+ isDeadEnd, //nextItemDepth === undefined || nextItemDepth < depth,
skippedIndents,
}
}
diff --git a/src/state/queries/usePostThread/views.ts b/src/state/queries/usePostThread/views.ts
index 8619332e24..945a57ca20 100644
--- a/src/state/queries/usePostThread/views.ts
+++ b/src/state/queries/usePostThread/views.ts
@@ -8,7 +8,8 @@ import {
type ModerationOpts,
} 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({
uri,
@@ -94,19 +95,28 @@ export function threadPost({
}
export function readMore({
- parent,
-}: {
- parent: Extract
-}) {
+ uri,
+ authorHandle,
+ unhydratedReplies: moreReplies,
+ depth: indent,
+ skippedIndents,
+}: TraversalMetadata): Extract {
+ const urip = new AtUri(uri)
+ const href = makeProfileLink(
+ {
+ did: urip.host,
+ handle: authorHandle,
+ },
+ 'post',
+ urip.rkey,
+ )
return {
type: 'readMore' as const,
- key: `readMore:${parent.uri}`,
- indent: parent.ui.parentHasBranchingReplies
- ? parent.depth
- : parent.ui.indent,
- replyCount: parent.value.moreReplies,
- nextAnchor: parent,
- nextAnchorUri: new AtUri(parent.uri),
+ key: `readMore:${uri}`,
+ href,
+ moreReplies,
+ indent,
+ skippedIndents,
}
}