From e5cc0c0ed244893aabf9a09c11e76fb962881e7d Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Tue, 27 May 2025 17:52:41 -0500 Subject: [PATCH] New reply tree layout --- .../PostThread/components/ReadMore.tsx | 13 +- .../PostThread/components/ThreadReply.tsx | 121 +++++++++++------- src/screens/PostThread/const.ts | 1 + src/state/queries/usePostThread/traversal.ts | 8 +- src/state/queries/usePostThread/types.ts | 2 + src/state/queries/usePostThread/views.ts | 23 +++- 6 files changed, 112 insertions(+), 56 deletions(-) diff --git a/src/screens/PostThread/components/ReadMore.tsx b/src/screens/PostThread/components/ReadMore.tsx index ec844939ea..f575eb48cd 100644 --- a/src/screens/PostThread/components/ReadMore.tsx +++ b/src/screens/PostThread/components/ReadMore.tsx @@ -1,10 +1,10 @@ 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 {makeProfileLink} from '#/lib/routes/links' -import {type PostThreadParams,type Slice} from '#/state/queries/usePostThread' -import {TREE_INDENT} from '#/screens/PostThread/const' +import {type PostThreadParams, type Slice} from '#/state/queries/usePostThread' +import {TREE_AVI_WIDTH,TREE_INDENT} from '#/screens/PostThread/const' import {atoms as a, useTheme} from '#/alf' import {CirclePlus_Stroke2_Corner0_Rounded as CirclePlus} from '#/components/icons/CirclePlus' import {Link} from '#/components/Link' @@ -30,7 +30,8 @@ export function ReadMore({ t.atoms.border_contrast_low, { borderRightWidth: 2, - width: TREE_INDENT, + width: TREE_INDENT + TREE_AVI_WIDTH / 2, + left: 1, }, ]} /> @@ -51,7 +52,9 @@ export function ReadMore({ style={[ t.atoms.border_contrast_low, { - marginLeft: isTreeView ? TREE_INDENT - 2 : TREE_INDENT, + marginLeft: isTreeView + ? TREE_INDENT + TREE_AVI_WIDTH / 2 - 1 + : TREE_INDENT, borderLeftWidth: 2, borderBottomWidth: 2, borderBottomLeftRadius: a.rounded_sm.borderRadius, diff --git a/src/screens/PostThread/components/ThreadReply.tsx b/src/screens/PostThread/components/ThreadReply.tsx index d6565dab75..2238e2492b 100644 --- a/src/screens/PostThread/components/ThreadReply.tsx +++ b/src/screens/PostThread/components/ThreadReply.tsx @@ -27,7 +27,7 @@ import {TextLink} from '#/view/com/util/Link' import {PostCtrls} from '#/view/com/util/post-ctrls/PostCtrls' import {PostEmbeds, PostEmbedViewContext} from '#/view/com/util/post-embeds' import {PostMeta} from '#/view/com/util/PostMeta' -import {TREE_INDENT} from '#/screens/PostThread/const' +import {TREE_AVI_WIDTH,TREE_INDENT} from '#/screens/PostThread/const' import {atoms as a, useTheme} from '#/alf' import {useInteractionState} from '#/components/hooks/useInteractionState' import {Trash_Stroke2_Corner0_Rounded as TrashIcon} from '#/components/icons/Trash' @@ -39,6 +39,11 @@ import {RichText} from '#/components/RichText' import {SubtleWebHover} from '#/components/SubtleWebHover' import {Text} from '#/components/Typography' +/** + * Mimic the space in PostMeta + */ +const TREE_AVI_PLUS_SPACE = TREE_AVI_WIDTH + a.gap_xs.gap + export function ThreadReply({ item, overrides, @@ -174,23 +179,24 @@ let PostThreadItemLoaded = ({ - {Array.from(Array(item.depth - 1)).map((_, n: number) => ( + {Array.from(Array(item.ui.indent - 1)).map((_, n: number) => ( ))} - + - - - {richText?.text ? ( - - + + {item.ui.showChildReplyLine && ( + + )} + + + + + {richText?.text ? ( + + + + ) : undefined} + {limitLines ? ( + + ) : undefined} + {post.embed && ( + + + + )} + - ) : undefined} - {limitLines ? ( - - ) : undefined} - {post.embed && ( - - - - )} - + diff --git a/src/screens/PostThread/const.ts b/src/screens/PostThread/const.ts index 50e3ad3cad..ca90a30ecf 100644 --- a/src/screens/PostThread/const.ts +++ b/src/screens/PostThread/const.ts @@ -1 +1,2 @@ export const TREE_INDENT = 16 +export const TREE_AVI_WIDTH = 24 diff --git a/src/state/queries/usePostThread/traversal.ts b/src/state/queries/usePostThread/traversal.ts index e3a7f0fa27..deeaa16c7b 100644 --- a/src/state/queries/usePostThread/traversal.ts +++ b/src/state/queries/usePostThread/traversal.ts @@ -114,8 +114,6 @@ export function flatten( } } - console.log(flattened) - /* * Insert hidden items and buttons to show them */ @@ -269,6 +267,9 @@ export function sort( uri: item.uri, depth: item.depth, value: item.value, + parent: thread.find( + p => p.uri === item.value.post.record.reply.parent.uri, + ), oneUp, oneDown, moderationOpts, @@ -311,6 +312,9 @@ export function sort( uri: child.uri, depth: child.depth, value: child.value, + parent: thread.find( + p => p.uri === child.value.post.record.reply.parent.uri, + ), oneUp: thread[ci - 1], oneDown: thread[ci + 1], moderationOpts, diff --git a/src/state/queries/usePostThread/types.ts b/src/state/queries/usePostThread/types.ts index 11eab28249..20e9b64623 100644 --- a/src/state/queries/usePostThread/types.ts +++ b/src/state/queries/usePostThread/types.ts @@ -49,6 +49,8 @@ export type Slice = isAnchor: boolean showParentReplyLine: boolean showChildReplyLine: boolean + indent: number + parentHasBranchingReplies: boolean } } | { diff --git a/src/state/queries/usePostThread/views.ts b/src/state/queries/usePostThread/views.ts index d4e31c4028..9cab23943b 100644 --- a/src/state/queries/usePostThread/views.ts +++ b/src/state/queries/usePostThread/views.ts @@ -65,17 +65,32 @@ export function threadPost({ uri, depth, value, + parent: up, oneUp, - oneDown, moderationOpts, }: { uri: string depth: number value: $Typed + parent?: AppBskyUnspeccedGetPostThreadV2.OutputSchema['thread'][number] oneUp?: AppBskyUnspeccedGetPostThreadV2.OutputSchema['thread'][number] oneDown?: AppBskyUnspeccedGetPostThreadV2.OutputSchema['thread'][number] moderationOpts: ModerationOpts }): Extract { + const parent = (up && 'post' in up?.value + ? up.value + : undefined) as unknown as AppBskyUnspeccedGetPostThreadV2.ThreadItemPost + const parentReplyCount = parent?.post?.replyCount || 0 + const parentAdditionalReplies = parent?.moreReplies || 0 + const parentHasBranchingReplies = + parentReplyCount > 1 && parentReplyCount - parentAdditionalReplies > 1 + // if (!parentHasBranchingReplies) { + // console.log(value.post.record.text, { + // parentReplyCount, + // parentAdditionalReplies, + // }) + // } + return { type: 'threadPost', key: uri, @@ -96,6 +111,8 @@ export function threadPost({ isAnchor: depth === 0, showParentReplyLine: !!oneUp && oneUp.depth !== 0 && oneUp.depth < depth, showChildReplyLine: (value.post.replyCount || 0) > 0, + indent: parentHasBranchingReplies ? depth : up?.depth || depth, + parentHasBranchingReplies, }, } } @@ -108,7 +125,9 @@ export function readMore({ return { type: 'readMore' as const, key: `readMore:${parent.uri}`, - indent: parent.depth, + indent: parent.ui.parentHasBranchingReplies + ? parent.depth + : parent.depth - 1, replyCount: parent.value.moreReplies, nextAnchor: parent, nextAnchorUri: new AtUri(parent.uri),