New reply tree layout
This commit is contained in:
@@ -1,10 +1,10 @@
|
|||||||
import {View} from 'react-native'
|
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 {makeProfileLink} from '#/lib/routes/links'
|
||||||
import {type PostThreadParams,type Slice} from '#/state/queries/usePostThread'
|
import {type PostThreadParams, type Slice} from '#/state/queries/usePostThread'
|
||||||
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 {atoms as a, useTheme} from '#/alf'
|
||||||
import {CirclePlus_Stroke2_Corner0_Rounded as CirclePlus} from '#/components/icons/CirclePlus'
|
import {CirclePlus_Stroke2_Corner0_Rounded as CirclePlus} from '#/components/icons/CirclePlus'
|
||||||
import {Link} from '#/components/Link'
|
import {Link} from '#/components/Link'
|
||||||
@@ -30,7 +30,8 @@ export function ReadMore({
|
|||||||
t.atoms.border_contrast_low,
|
t.atoms.border_contrast_low,
|
||||||
{
|
{
|
||||||
borderRightWidth: 2,
|
borderRightWidth: 2,
|
||||||
width: TREE_INDENT,
|
width: TREE_INDENT + TREE_AVI_WIDTH / 2,
|
||||||
|
left: 1,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
@@ -51,7 +52,9 @@ export function ReadMore({
|
|||||||
style={[
|
style={[
|
||||||
t.atoms.border_contrast_low,
|
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,
|
borderLeftWidth: 2,
|
||||||
borderBottomWidth: 2,
|
borderBottomWidth: 2,
|
||||||
borderBottomLeftRadius: a.rounded_sm.borderRadius,
|
borderBottomLeftRadius: a.rounded_sm.borderRadius,
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import {TextLink} from '#/view/com/util/Link'
|
|||||||
import {PostCtrls} from '#/view/com/util/post-ctrls/PostCtrls'
|
import {PostCtrls} from '#/view/com/util/post-ctrls/PostCtrls'
|
||||||
import {PostEmbeds, PostEmbedViewContext} from '#/view/com/util/post-embeds'
|
import {PostEmbeds, PostEmbedViewContext} from '#/view/com/util/post-embeds'
|
||||||
import {PostMeta} from '#/view/com/util/PostMeta'
|
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 {atoms as a, useTheme} from '#/alf'
|
||||||
import {useInteractionState} from '#/components/hooks/useInteractionState'
|
import {useInteractionState} from '#/components/hooks/useInteractionState'
|
||||||
import {Trash_Stroke2_Corner0_Rounded as TrashIcon} from '#/components/icons/Trash'
|
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 {SubtleWebHover} from '#/components/SubtleWebHover'
|
||||||
import {Text} from '#/components/Typography'
|
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({
|
export function ThreadReply({
|
||||||
item,
|
item,
|
||||||
overrides,
|
overrides,
|
||||||
@@ -174,23 +179,24 @@ let PostThreadItemLoaded = ({
|
|||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
a.flex_row,
|
a.flex_row,
|
||||||
item.depth === 1 && [a.border_t, t.atoms.border_contrast_low],
|
item.ui.indent === 1 && [a.border_t, t.atoms.border_contrast_low],
|
||||||
]}>
|
]}>
|
||||||
{Array.from(Array(item.depth - 1)).map((_, n: number) => (
|
{Array.from(Array(item.ui.indent - 1)).map((_, n: number) => (
|
||||||
<View
|
<View
|
||||||
key={`${post.uri}-padding-${n}`}
|
key={`${post.uri}-padding-${n}`}
|
||||||
style={[
|
style={[
|
||||||
t.atoms.border_contrast_low,
|
t.atoms.border_contrast_low,
|
||||||
{
|
{
|
||||||
borderRightWidth: 2,
|
borderRightWidth: 2,
|
||||||
width: TREE_INDENT,
|
width: TREE_INDENT + TREE_AVI_WIDTH / 2,
|
||||||
|
left: 1,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<View style={a.flex_1}>
|
<View style={a.flex_1}>
|
||||||
<SubtleHover>
|
<SubtleHover>
|
||||||
<View style={[a.px_lg, a.py_sm, item.depth === 1 && [a.pt_lg]]}>
|
<View style={[a.px_lg, a.pt_sm, item.ui.indent === 1 && [a.pt_lg]]}>
|
||||||
<PostHider
|
<PostHider
|
||||||
testID={`postThreadItem-by-${post.author.handle}`}
|
testID={`postThreadItem-by-${post.author.handle}`}
|
||||||
href={postHref}
|
href={postHref}
|
||||||
@@ -207,53 +213,74 @@ let PostThreadItemLoaded = ({
|
|||||||
moderation={moderation}
|
moderation={moderation}
|
||||||
timestamp={post.indexedAt}
|
timestamp={post.indexedAt}
|
||||||
postHref={postHref}
|
postHref={postHref}
|
||||||
avatarSize={24}
|
avatarSize={TREE_AVI_WIDTH}
|
||||||
style={[a.pb_xs]}
|
style={[a.pb_xs]}
|
||||||
showAvatar
|
showAvatar
|
||||||
/>
|
/>
|
||||||
<LabelsOnMyPost post={post} style={[a.pb_xs]} />
|
<View style={[a.flex_row]}>
|
||||||
<PostAlerts
|
<View style={[a.relative, {width: TREE_AVI_PLUS_SPACE}]}>
|
||||||
modui={moderation.ui('contentList')}
|
{item.ui.showChildReplyLine && (
|
||||||
style={[a.pb_2xs]}
|
<View
|
||||||
additionalCauses={additionalPostAlerts}
|
style={[
|
||||||
/>
|
a.h_full,
|
||||||
{richText?.text ? (
|
t.atoms.border_contrast_low,
|
||||||
<View style={[a.pb_2xs, a.pr_sm]}>
|
{
|
||||||
<RichText
|
borderRightWidth: 2,
|
||||||
enableTags
|
width: '50%',
|
||||||
value={richText}
|
left: -1,
|
||||||
style={[a.flex_1, a.text_md]}
|
},
|
||||||
numberOfLines={limitLines ? MAX_POST_LINES : undefined}
|
]}
|
||||||
authorHandle={post.author.handle}
|
/>
|
||||||
shouldProxyLinks={true}
|
)}
|
||||||
|
</View>
|
||||||
|
<View style={[a.flex_1]}>
|
||||||
|
<LabelsOnMyPost post={post} style={[a.pb_xs]} />
|
||||||
|
<PostAlerts
|
||||||
|
modui={moderation.ui('contentList')}
|
||||||
|
style={[a.pb_2xs]}
|
||||||
|
additionalCauses={additionalPostAlerts}
|
||||||
|
/>
|
||||||
|
{richText?.text ? (
|
||||||
|
<View style={[a.pb_2xs, a.pr_sm]}>
|
||||||
|
<RichText
|
||||||
|
enableTags
|
||||||
|
value={richText}
|
||||||
|
style={[a.flex_1, a.text_md]}
|
||||||
|
numberOfLines={
|
||||||
|
limitLines ? MAX_POST_LINES : undefined
|
||||||
|
}
|
||||||
|
authorHandle={post.author.handle}
|
||||||
|
shouldProxyLinks={true}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
) : undefined}
|
||||||
|
{limitLines ? (
|
||||||
|
<TextLink
|
||||||
|
text={_(msg`Show More`)}
|
||||||
|
style={pal.link}
|
||||||
|
onPress={onPressShowMore}
|
||||||
|
href="#"
|
||||||
|
/>
|
||||||
|
) : undefined}
|
||||||
|
{post.embed && (
|
||||||
|
<View style={[a.pb_xs]}>
|
||||||
|
<PostEmbeds
|
||||||
|
embed={post.embed}
|
||||||
|
moderation={moderation}
|
||||||
|
viewContext={PostEmbedViewContext.Feed}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
<PostCtrls
|
||||||
|
post={postShadow}
|
||||||
|
record={record}
|
||||||
|
richText={richText}
|
||||||
|
onPressReply={onPressReply}
|
||||||
|
logContext="PostThreadItem"
|
||||||
|
threadgateRecord={threadgateRecord}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
) : undefined}
|
</View>
|
||||||
{limitLines ? (
|
|
||||||
<TextLink
|
|
||||||
text={_(msg`Show More`)}
|
|
||||||
style={pal.link}
|
|
||||||
onPress={onPressShowMore}
|
|
||||||
href="#"
|
|
||||||
/>
|
|
||||||
) : undefined}
|
|
||||||
{post.embed && (
|
|
||||||
<View style={[a.pb_xs]}>
|
|
||||||
<PostEmbeds
|
|
||||||
embed={post.embed}
|
|
||||||
moderation={moderation}
|
|
||||||
viewContext={PostEmbedViewContext.Feed}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
<PostCtrls
|
|
||||||
post={postShadow}
|
|
||||||
record={record}
|
|
||||||
richText={richText}
|
|
||||||
onPressReply={onPressReply}
|
|
||||||
logContext="PostThreadItem"
|
|
||||||
threadgateRecord={threadgateRecord}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</PostHider>
|
</PostHider>
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
export const TREE_INDENT = 16
|
export const TREE_INDENT = 16
|
||||||
|
export const TREE_AVI_WIDTH = 24
|
||||||
|
|||||||
@@ -114,8 +114,6 @@ export function flatten(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(flattened)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Insert hidden items and buttons to show them
|
* Insert hidden items and buttons to show them
|
||||||
*/
|
*/
|
||||||
@@ -269,6 +267,9 @@ export function sort(
|
|||||||
uri: item.uri,
|
uri: item.uri,
|
||||||
depth: item.depth,
|
depth: item.depth,
|
||||||
value: item.value,
|
value: item.value,
|
||||||
|
parent: thread.find(
|
||||||
|
p => p.uri === item.value.post.record.reply.parent.uri,
|
||||||
|
),
|
||||||
oneUp,
|
oneUp,
|
||||||
oneDown,
|
oneDown,
|
||||||
moderationOpts,
|
moderationOpts,
|
||||||
@@ -311,6 +312,9 @@ export function sort(
|
|||||||
uri: child.uri,
|
uri: child.uri,
|
||||||
depth: child.depth,
|
depth: child.depth,
|
||||||
value: child.value,
|
value: child.value,
|
||||||
|
parent: thread.find(
|
||||||
|
p => p.uri === child.value.post.record.reply.parent.uri,
|
||||||
|
),
|
||||||
oneUp: thread[ci - 1],
|
oneUp: thread[ci - 1],
|
||||||
oneDown: thread[ci + 1],
|
oneDown: thread[ci + 1],
|
||||||
moderationOpts,
|
moderationOpts,
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ export type Slice =
|
|||||||
isAnchor: boolean
|
isAnchor: boolean
|
||||||
showParentReplyLine: boolean
|
showParentReplyLine: boolean
|
||||||
showChildReplyLine: boolean
|
showChildReplyLine: boolean
|
||||||
|
indent: number
|
||||||
|
parentHasBranchingReplies: boolean
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
|
|||||||
@@ -65,17 +65,32 @@ export function threadPost({
|
|||||||
uri,
|
uri,
|
||||||
depth,
|
depth,
|
||||||
value,
|
value,
|
||||||
|
parent: up,
|
||||||
oneUp,
|
oneUp,
|
||||||
oneDown,
|
|
||||||
moderationOpts,
|
moderationOpts,
|
||||||
}: {
|
}: {
|
||||||
uri: string
|
uri: string
|
||||||
depth: number
|
depth: number
|
||||||
value: $Typed<AppBskyUnspeccedGetPostThreadV2.ThreadItemPost>
|
value: $Typed<AppBskyUnspeccedGetPostThreadV2.ThreadItemPost>
|
||||||
|
parent?: AppBskyUnspeccedGetPostThreadV2.OutputSchema['thread'][number]
|
||||||
oneUp?: AppBskyUnspeccedGetPostThreadV2.OutputSchema['thread'][number]
|
oneUp?: AppBskyUnspeccedGetPostThreadV2.OutputSchema['thread'][number]
|
||||||
oneDown?: AppBskyUnspeccedGetPostThreadV2.OutputSchema['thread'][number]
|
oneDown?: AppBskyUnspeccedGetPostThreadV2.OutputSchema['thread'][number]
|
||||||
moderationOpts: ModerationOpts
|
moderationOpts: ModerationOpts
|
||||||
}): Extract<Slice, {type: 'threadPost'}> {
|
}): Extract<Slice, {type: 'threadPost'}> {
|
||||||
|
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 {
|
return {
|
||||||
type: 'threadPost',
|
type: 'threadPost',
|
||||||
key: uri,
|
key: uri,
|
||||||
@@ -96,6 +111,8 @@ export function threadPost({
|
|||||||
isAnchor: depth === 0,
|
isAnchor: depth === 0,
|
||||||
showParentReplyLine: !!oneUp && oneUp.depth !== 0 && oneUp.depth < depth,
|
showParentReplyLine: !!oneUp && oneUp.depth !== 0 && oneUp.depth < depth,
|
||||||
showChildReplyLine: (value.post.replyCount || 0) > 0,
|
showChildReplyLine: (value.post.replyCount || 0) > 0,
|
||||||
|
indent: parentHasBranchingReplies ? depth : up?.depth || depth,
|
||||||
|
parentHasBranchingReplies,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,7 +125,9 @@ export function readMore({
|
|||||||
return {
|
return {
|
||||||
type: 'readMore' as const,
|
type: 'readMore' as const,
|
||||||
key: `readMore:${parent.uri}`,
|
key: `readMore:${parent.uri}`,
|
||||||
indent: parent.depth,
|
indent: parent.ui.parentHasBranchingReplies
|
||||||
|
? parent.depth
|
||||||
|
: parent.depth - 1,
|
||||||
replyCount: parent.value.moreReplies,
|
replyCount: parent.value.moreReplies,
|
||||||
nextAnchor: parent,
|
nextAnchor: parent,
|
||||||
nextAnchorUri: new AtUri(parent.uri),
|
nextAnchorUri: new AtUri(parent.uri),
|
||||||
|
|||||||
Reference in New Issue
Block a user