diff options
-rw-r--r-- | src/state/queries/post-thread.ts | 53 | ||||
-rw-r--r-- | src/view/com/post-thread/PostThread.tsx | 4 | ||||
-rw-r--r-- | src/view/com/post-thread/PostThreadLoadMore.tsx | 57 |
3 files changed, 112 insertions, 2 deletions
diff --git a/src/state/queries/post-thread.ts b/src/state/queries/post-thread.ts index f7d21a427..727eff253 100644 --- a/src/state/queries/post-thread.ts +++ b/src/state/queries/post-thread.ts @@ -41,6 +41,8 @@ export interface ThreadCtx { hasMore?: boolean isParentLoading?: boolean isChildLoading?: boolean + isSelfThread?: boolean + hasMoreSelfThread?: boolean } export type ThreadPost = { @@ -88,9 +90,12 @@ export function usePostThreadQuery(uri: string | undefined) { gcTime: 0, queryKey: RQKEY(uri || ''), async queryFn() { - const res = await agent.getPostThread({uri: uri!}) + const res = await agent.getPostThread({uri: uri!, depth: 10}) if (res.success) { - return responseToThreadNodes(res.data.thread) + const thread = responseToThreadNodes(res.data.thread) + annotateSelfThread(thread) + console.log(thread) + return thread } return {type: 'unknown', uri: uri!} }, @@ -234,6 +239,8 @@ function responseToThreadNodes( isHighlightedPost: depth === 0, hasMore: direction === 'down' && !node.replies?.length && !!node.replyCount, + isSelfThread: false, // populated `annotateSelfThread` + hasMoreSelfThread: false, // populated in `annotateSelfThread` }, } } else if (AppBskyFeedDefs.isBlockedPost(node)) { @@ -245,6 +252,48 @@ function responseToThreadNodes( } } +function annotateSelfThread(thread: ThreadNode) { + if (thread.type !== 'post') { + return + } + const selfThreadNodes: ThreadPost[] = [thread] + + let parent: ThreadNode | undefined = thread.parent + while (parent) { + if ( + parent.type !== 'post' || + parent.post.author.did !== thread.post.author.did + ) { + // not a self-thread + return + } + selfThreadNodes.push(parent) + parent = parent.parent + } + + let node = thread + for (let i = 0; i < 10; i++) { + const reply = node.replies?.find( + r => r.type === 'post' && r.post.author.did === thread.post.author.did, + ) + if (reply?.type !== 'post') { + break + } + selfThreadNodes.push(reply) + node = reply + } + + if (selfThreadNodes.length > 1) { + for (const selfThreadNode of selfThreadNodes) { + selfThreadNode.ctx.isSelfThread = true + } + const last = selfThreadNodes.at(-1) + if (last && last.post.replyCount && !last.replies?.length) { + last.ctx.hasMoreSelfThread = true + } + } +} + function findPostInQueryData( queryClient: QueryClient, uri: string, diff --git a/src/view/com/post-thread/PostThread.tsx b/src/view/com/post-thread/PostThread.tsx index 35028334c..8061eb11c 100644 --- a/src/view/com/post-thread/PostThread.tsx +++ b/src/view/com/post-thread/PostThread.tsx @@ -34,6 +34,7 @@ import {ComposePrompt} from '../composer/Prompt' import {List, ListMethods} from '../util/List' import {ViewHeader} from '../util/ViewHeader' import {PostThreadItem} from './PostThreadItem' +import {PostThreadLoadMore} from './PostThreadLoadMore' import {PostThreadShowHiddenReplies} from './PostThreadShowHiddenReplies' // FlatList maintainVisibleContentPosition breaks if too many items @@ -364,6 +365,9 @@ export function PostThread({ </View> ) } else if (isThreadPost(item)) { + if (!treeView && item.ctx.hasMoreSelfThread) { + return <PostThreadLoadMore post={item.post} /> + } const prev = isThreadPost(posts[index - 1]) ? (posts[index - 1] as ThreadPost) : undefined diff --git a/src/view/com/post-thread/PostThreadLoadMore.tsx b/src/view/com/post-thread/PostThreadLoadMore.tsx new file mode 100644 index 000000000..780ea7728 --- /dev/null +++ b/src/view/com/post-thread/PostThreadLoadMore.tsx @@ -0,0 +1,57 @@ +import * as React from 'react' +import {View} from 'react-native' +import {AppBskyFeedDefs, AtUri} from '@atproto/api' +import {Trans} from '@lingui/macro' + +import {makeProfileLink} from '#/lib/routes/links' +import {atoms as a, useTheme} from '#/alf' +import {Text} from '#/components/Typography' +import {Link} from '../util/Link' +import {UserAvatar} from '../util/UserAvatar' + +export function PostThreadLoadMore({post}: {post: AppBskyFeedDefs.PostView}) { + const t = useTheme() + + const postHref = React.useMemo(() => { + const urip = new AtUri(post.uri) + return makeProfileLink(post.author, 'post', urip.rkey) + }, [post.uri, post.author]) + + return ( + <Link + href={postHref} + style={[a.flex_row, a.align_center, a.py_md, {paddingHorizontal: 14}]} + hoverStyle={[t.atoms.bg_contrast_25]}> + <View style={[a.flex_row]}> + <View + style={{ + alignItems: 'center', + justifyContent: 'center', + width: 34, + height: 34, + borderRadius: 18, + backgroundColor: t.atoms.bg.backgroundColor, + marginRight: -20, + }}> + <UserAvatar avatar={post.author.avatar} size={30} /> + </View> + <View + style={{ + alignItems: 'center', + justifyContent: 'center', + width: 34, + height: 34, + borderRadius: 18, + backgroundColor: t.atoms.bg.backgroundColor, + }}> + <UserAvatar avatar={post.author.avatar} size={30} /> + </View> + </View> + <View style={[a.px_sm]}> + <Text style={[{color: t.palette.primary_500}, a.text_md]}> + <Trans>Continue thread...</Trans> + </Text> + </View> + </Link> + ) +} |