diff --git a/assets/icons/arrowTopCircle_stroke2_corner0_rounded.svg b/assets/icons/arrowTopCircle_stroke2_corner0_rounded.svg
new file mode 100644
index 0000000000..e34b5eb38f
--- /dev/null
+++ b/assets/icons/arrowTopCircle_stroke2_corner0_rounded.svg
@@ -0,0 +1 @@
+
diff --git a/src/components/icons/ArrowTopCircle.tsx b/src/components/icons/ArrowTopCircle.tsx
new file mode 100644
index 0000000000..2d250367f3
--- /dev/null
+++ b/src/components/icons/ArrowTopCircle.tsx
@@ -0,0 +1,5 @@
+import {createSinglePathSVG} from './TEMPLATE'
+
+export const ArrowTopCircle_Stroke2_Corner0_Rounded = createSinglePathSVG({
+ path: 'M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2Zm0 2a8 8 0 1 0 0 16 8 8 0 0 0 0-16Zm-.63 3.225a1 1 0 0 1 1.337.068l3 3 .068.076a1 1 0 0 1-1.406 1.406l-.076-.068L13 10.414V16a1 1 0 1 1-2 0v-5.586l-1.293 1.293a1 1 0 1 1-1.414-1.414l3-3 .076-.068Z',
+})
diff --git a/src/screens/PostThread/components/ThreadItemReadMoreUp.tsx b/src/screens/PostThread/components/ThreadItemReadMoreUp.tsx
new file mode 100644
index 0000000000..eb8fcf2459
--- /dev/null
+++ b/src/screens/PostThread/components/ThreadItemReadMoreUp.tsx
@@ -0,0 +1,90 @@
+import {memo} from 'react'
+import {View} from 'react-native'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+
+import {type ThreadItem} from '#/state/queries/usePostThread'
+import {
+ LINEAR_AVI_WIDTH,
+ OUTER_SPACE,
+ REPLY_LINE_WIDTH,
+} from '#/screens/PostThread/const'
+import {atoms as a, useTheme} from '#/alf'
+import {ArrowTopCircle_Stroke2_Corner0_Rounded as UpIcon} from '#/components/icons/ArrowTopCircle'
+import {Link} from '#/components/Link'
+import {Text} from '#/components/Typography'
+
+export const ThreadItemReadMoreUp = memo(function ThreadItemReadMoreUp({
+ item,
+}: {
+ item: Extract
+}) {
+ const t = useTheme()
+ const {_} = useLingui()
+
+ return (
+
+ {({hovered, pressed}) => {
+ const interacted = hovered || pressed
+ return (
+
+
+
+
+
+
+ Continue thread...
+
+
+
+
+
+
+ )
+ }}
+
+ )
+})
diff --git a/src/screens/PostThread/index.tsx b/src/screens/PostThread/index.tsx
index c95f0c44b8..a4fcb31def 100644
--- a/src/screens/PostThread/index.tsx
+++ b/src/screens/PostThread/index.tsx
@@ -24,6 +24,7 @@ import {
} from '#/screens/PostThread/components/ThreadItemPost'
import {ThreadItemPostTombstone} from '#/screens/PostThread/components/ThreadItemPostTombstone'
import {ThreadItemReadMore} from '#/screens/PostThread/components/ThreadItemReadMore'
+import {ThreadItemReadMoreUp} from '#/screens/PostThread/components/ThreadItemReadMoreUp'
import {ThreadItemTreePost} from '#/screens/PostThread/components/ThreadItemTreePost'
import {useBreakpoints, web} from '#/alf'
import * as Layout from '#/components/Layout'
@@ -191,8 +192,8 @@ export function Inner({uri}: {uri: string | undefined}) {
}
}
} else {
- // ignore parents
- if (hasDepth && item.depth < 0) continue
+ // ignore any parent items
+ if (item.type === 'readMoreUp' || (hasDepth && item.depth < 0)) continue
// can exit early if we've reached the max children count
if (childrenCount > maxChildrenCount) break
@@ -271,6 +272,8 @@ export function Inner({uri}: {uri: string | undefined}) {
view={thread.state.view === 'tree' ? 'tree' : 'linear'}
/>
)
+ } else if (item.type === 'readMoreUp') {
+ return
} else if (item.type === 'threadPostBlocked') {
return
} else if (item.type === 'threadPostNotFound') {
diff --git a/src/state/queries/usePostThread/traversal.ts b/src/state/queries/usePostThread/traversal.ts
index ff43fceaed..c987f9156f 100644
--- a/src/state/queries/usePostThread/traversal.ts
+++ b/src/state/queries/usePostThread/traversal.ts
@@ -353,6 +353,16 @@ export function sortAndAnnotateThreadItems(
i++
}
+ /**
+ * Only occurs for the first item in the thread, which may have
+ * additional parents not included in this request.
+ */
+ if (item.value.moreParents) {
+ metadata.followsReadMoreUp = true
+ subset.splice(i, 0, views.readMoreUp(metadata))
+ i++
+ }
+
/*
* Calculate the final UI state for the thread item.
*/
@@ -362,6 +372,8 @@ export function sortAndAnnotateThreadItems(
}
}
+ console.log(threadItems)
+
return {
threadItems,
hiddenThreadItems,
diff --git a/src/state/queries/usePostThread/types.ts b/src/state/queries/usePostThread/types.ts
index d8f5a3dca4..8bfe3d96b5 100644
--- a/src/state/queries/usePostThread/types.ts
+++ b/src/state/queries/usePostThread/types.ts
@@ -100,6 +100,11 @@ export type ThreadItem =
moreReplies: number
skippedIndentIndices: Set
}
+ | {
+ type: 'readMoreUp'
+ key: string
+ href: string
+ }
| {
type: 'skeleton'
key: string
@@ -117,6 +122,11 @@ export type TraversalMetadata = {
* calculated on the server.
*/
depth: number
+ /**
+ * Indicates if this item is a "read more" link preceding this post that
+ * continues the thread upwards.
+ */
+ followsReadMoreUp: boolean
/**
* Indicates if the post is the last reply beneath its parent post.
*/
diff --git a/src/state/queries/usePostThread/utils.ts b/src/state/queries/usePostThread/utils.ts
index 3b2e6d8c14..f309be3e50 100644
--- a/src/state/queries/usePostThread/utils.ts
+++ b/src/state/queries/usePostThread/utils.ts
@@ -81,6 +81,10 @@ export function getTraversalMetadata({
* Unknown until after traversal
*/
precedesChildReadMore: false,
+ /*
+ * Unknown until after traversal
+ */
+ followsReadMoreUp: false,
postData: {
uri: item.uri,
authorHandle: item.value.post.author.handle,
@@ -124,6 +128,7 @@ export function getThreadPostUI({
repliesSeenCount,
repliesUnhydrated,
precedesChildReadMore,
+ followsReadMoreUp,
}: TraversalMetadata): Extract['ui'] {
// TODO might be able to simplify this
const isReplyAndHasReplies =
@@ -134,7 +139,8 @@ export function getThreadPostUI({
return {
isAnchor: depth === 0,
showParentReplyLine:
- !!prevItemDepth && prevItemDepth !== 0 && prevItemDepth < depth,
+ followsReadMoreUp ||
+ (!!prevItemDepth && prevItemDepth !== 0 && prevItemDepth < depth),
showChildReplyLine: depth < 0 || isReplyAndHasReplies,
indent: depth,
/*
diff --git a/src/state/queries/usePostThread/views.ts b/src/state/queries/usePostThread/views.ts
index a6f2f0f848..1028cd847e 100644
--- a/src/state/queries/usePostThread/views.ts
+++ b/src/state/queries/usePostThread/views.ts
@@ -124,6 +124,25 @@ export function readMore({
}
}
+export function readMoreUp({
+ postData,
+}: TraversalMetadata): Extract {
+ const urip = new AtUri(postData.uri)
+ const href = makeProfileLink(
+ {
+ did: urip.host,
+ handle: postData.authorHandle,
+ },
+ 'post',
+ urip.rkey,
+ )
+ return {
+ type: 'readMoreUp' as const,
+ key: `readMoreUp:${postData.uri}`,
+ href,
+ }
+}
+
export function skeleton({
key,
item,