Make optimistic deletions of linear posts not look bad
This commit is contained in:
@@ -46,12 +46,7 @@ import * as Skele from '#/components/Skeleton'
|
|||||||
import {SubtleWebHover} from '#/components/SubtleWebHover'
|
import {SubtleWebHover} from '#/components/SubtleWebHover'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
|
|
||||||
export function ThreadPost({
|
export type ThreadItemPostProps = {
|
||||||
item,
|
|
||||||
overrides,
|
|
||||||
onPostSuccess,
|
|
||||||
threadgateRecord,
|
|
||||||
}: {
|
|
||||||
item: Extract<ThreadItem, {type: 'threadPost'}>
|
item: Extract<ThreadItem, {type: 'threadPost'}>
|
||||||
overrides?: {
|
overrides?: {
|
||||||
moderation?: boolean
|
moderation?: boolean
|
||||||
@@ -59,11 +54,18 @@ export function ThreadPost({
|
|||||||
}
|
}
|
||||||
onPostSuccess?: (data: OnPostSuccessData) => void
|
onPostSuccess?: (data: OnPostSuccessData) => void
|
||||||
threadgateRecord?: AppBskyFeedThreadgate.Record
|
threadgateRecord?: AppBskyFeedThreadgate.Record
|
||||||
}) {
|
}
|
||||||
|
|
||||||
|
export function ThreadPost({
|
||||||
|
item,
|
||||||
|
overrides,
|
||||||
|
onPostSuccess,
|
||||||
|
threadgateRecord,
|
||||||
|
}: ThreadItemPostProps) {
|
||||||
const postShadow = usePostShadow(item.value.post)
|
const postShadow = usePostShadow(item.value.post)
|
||||||
|
|
||||||
if (postShadow === POST_TOMBSTONE) {
|
if (postShadow === POST_TOMBSTONE) {
|
||||||
return <PostThreadItemDeleted />
|
return <PostThreadItemDeleted item={item} overrides={overrides} />
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -79,26 +81,104 @@ export function ThreadPost({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function PostThreadItemDeleted({hideTopBorder}: {hideTopBorder?: boolean}) {
|
function PostThreadItemDeleted({
|
||||||
|
item,
|
||||||
|
overrides,
|
||||||
|
}: Pick<ThreadItemPostProps, 'item' | 'overrides'>) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThreadItemPostOuterWrapper item={item} overrides={overrides}>
|
||||||
|
<ThreadItemPostParentReplyLine item={item} />
|
||||||
|
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_row,
|
||||||
|
a.align_center,
|
||||||
|
a.gap_sm,
|
||||||
|
a.py_md,
|
||||||
|
a.rounded_sm,
|
||||||
|
t.atoms.bg_contrast_25,
|
||||||
|
]}>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_row,
|
||||||
|
a.align_center,
|
||||||
|
a.justify_center,
|
||||||
|
{
|
||||||
|
width: LINEAR_AVI_WIDTH,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<TrashIcon style={[t.atoms.text]} />
|
||||||
|
</View>
|
||||||
|
<Text style={[t.atoms.text_contrast_medium, a.mt_2xs]}>
|
||||||
|
<Trans>This post has been deleted.</Trans>
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={[{height: 4}]} />
|
||||||
|
</ThreadItemPostOuterWrapper>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ThreadItemPostOuterWrapper = memo(function ThreadItemPostOuterWrapper({
|
||||||
|
item,
|
||||||
|
overrides,
|
||||||
|
children,
|
||||||
|
}: Pick<ThreadItemPostProps, 'item' | 'overrides'> & {
|
||||||
|
children: React.ReactNode
|
||||||
|
}) {
|
||||||
|
const t = useTheme()
|
||||||
|
const showTopBorder =
|
||||||
|
!item.ui.showParentReplyLine && overrides?.topBorder !== true
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
t.atoms.bg,
|
showTopBorder && [a.border_t, t.atoms.border_contrast_low],
|
||||||
t.atoms.border_contrast_low,
|
{
|
||||||
a.p_xl,
|
paddingHorizontal: OUTER_SPACE,
|
||||||
a.pl_lg,
|
},
|
||||||
a.flex_row,
|
// If there's no next child, add a little padding to bottom
|
||||||
a.gap_md,
|
!item.ui.showChildReplyLine &&
|
||||||
!hideTopBorder && a.border_t,
|
!item.ui.precedesChildReadMore && {
|
||||||
|
paddingBottom: OUTER_SPACE / 2,
|
||||||
|
},
|
||||||
]}>
|
]}>
|
||||||
<TrashIcon style={[t.atoms.text]} />
|
{children}
|
||||||
<Text style={[t.atoms.text_contrast_medium, a.mt_2xs]}>
|
|
||||||
<Trans>This post has been deleted.</Trans>
|
|
||||||
</Text>
|
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides some space between posts as well as contains the reply line
|
||||||
|
*/
|
||||||
|
const ThreadItemPostParentReplyLine = memo(
|
||||||
|
function ThreadItemPostParentReplyLine({
|
||||||
|
item,
|
||||||
|
}: Pick<ThreadItemPostProps, 'item'>) {
|
||||||
|
const t = useTheme()
|
||||||
|
return (
|
||||||
|
<View style={[a.flex_row, {height: 12}]}>
|
||||||
|
<View style={{width: LINEAR_AVI_WIDTH}}>
|
||||||
|
{item.ui.showParentReplyLine && (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.mx_auto,
|
||||||
|
a.flex_1,
|
||||||
|
a.mb_xs,
|
||||||
|
{
|
||||||
|
width: REPLY_LINE_WIDTH,
|
||||||
|
backgroundColor: t.atoms.border_contrast_low.borderColor,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
const ThreadPostInner = memo(function ThreadPostInner({
|
const ThreadPostInner = memo(function ThreadPostInner({
|
||||||
item,
|
item,
|
||||||
@@ -106,15 +186,8 @@ const ThreadPostInner = memo(function ThreadPostInner({
|
|||||||
overrides,
|
overrides,
|
||||||
onPostSuccess,
|
onPostSuccess,
|
||||||
threadgateRecord,
|
threadgateRecord,
|
||||||
}: {
|
}: ThreadItemPostProps & {
|
||||||
item: Extract<ThreadItem, {type: 'threadPost'}>
|
|
||||||
postShadow: Shadow<AppBskyFeedDefs.PostView>
|
postShadow: Shadow<AppBskyFeedDefs.PostView>
|
||||||
overrides?: {
|
|
||||||
moderation?: boolean
|
|
||||||
topBorder?: boolean
|
|
||||||
}
|
|
||||||
onPostSuccess?: (data: OnPostSuccessData) => void
|
|
||||||
threadgateRecord?: AppBskyFeedThreadgate.Record
|
|
||||||
}): React.ReactNode {
|
}): React.ReactNode {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
@@ -179,24 +252,9 @@ const ThreadPostInner = memo(function ThreadPostInner({
|
|||||||
|
|
||||||
const {isActive: live} = useActorStatus(post.author)
|
const {isActive: live} = useActorStatus(post.author)
|
||||||
|
|
||||||
const showTopBorder =
|
|
||||||
!item.ui.showParentReplyLine && overrides?.topBorder !== true
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SubtleHover>
|
<SubtleHover>
|
||||||
<View
|
<ThreadItemPostOuterWrapper item={item} overrides={overrides}>
|
||||||
style={[
|
|
||||||
a.pointer,
|
|
||||||
showTopBorder && [a.border_t, t.atoms.border_contrast_low],
|
|
||||||
{
|
|
||||||
paddingHorizontal: OUTER_SPACE,
|
|
||||||
},
|
|
||||||
// If there's no next child, add a little padding to bottom
|
|
||||||
!item.ui.showChildReplyLine &&
|
|
||||||
!item.ui.precedesChildReadMore && {
|
|
||||||
paddingBottom: OUTER_SPACE / 2,
|
|
||||||
},
|
|
||||||
]}>
|
|
||||||
<PostHider
|
<PostHider
|
||||||
testID={`postThreadItem-by-${post.author.handle}`}
|
testID={`postThreadItem-by-${post.author.handle}`}
|
||||||
href={postHref}
|
href={postHref}
|
||||||
@@ -206,24 +264,7 @@ const ThreadPostInner = memo(function ThreadPostInner({
|
|||||||
iconStyles={{marginLeft: 2, marginRight: 2}}
|
iconStyles={{marginLeft: 2, marginRight: 2}}
|
||||||
profile={post.author}
|
profile={post.author}
|
||||||
interpretFilterAsBlur>
|
interpretFilterAsBlur>
|
||||||
{/* Provides some space between posts as well as contains the reply line */}
|
<ThreadItemPostParentReplyLine item={item} />
|
||||||
<View style={[a.flex_row, {height: a.pt_md.paddingTop}]}>
|
|
||||||
<View style={{width: LINEAR_AVI_WIDTH}}>
|
|
||||||
{item.ui.showParentReplyLine && (
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
a.mx_auto,
|
|
||||||
a.flex_1,
|
|
||||||
a.mb_xs,
|
|
||||||
{
|
|
||||||
width: REPLY_LINE_WIDTH,
|
|
||||||
backgroundColor: t.atoms.border_contrast_low.borderColor,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View style={[a.flex_row, a.gap_md]}>
|
<View style={[a.flex_row, a.gap_md]}>
|
||||||
<View>
|
<View>
|
||||||
@@ -303,7 +344,7 @@ const ThreadPostInner = memo(function ThreadPostInner({
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</PostHider>
|
</PostHider>
|
||||||
</View>
|
</ThreadItemPostOuterWrapper>
|
||||||
</SubtleHover>
|
</SubtleHover>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -315,7 +356,10 @@ function SubtleHover({children}: {children: React.ReactNode}) {
|
|||||||
onOut: onHoverOut,
|
onOut: onHoverOut,
|
||||||
} = useInteractionState()
|
} = useInteractionState()
|
||||||
return (
|
return (
|
||||||
<View onPointerEnter={onHoverIn} onPointerLeave={onHoverOut}>
|
<View
|
||||||
|
onPointerEnter={onHoverIn}
|
||||||
|
onPointerLeave={onHoverOut}
|
||||||
|
style={a.pointer}>
|
||||||
<SubtleWebHover hover={hover} />
|
<SubtleWebHover hover={hover} />
|
||||||
{children}
|
{children}
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
Reference in New Issue
Block a user