Always show the header on post threads on native (#4254)
* always show header on native * ALF ALF ALF * rm offset for top border * wrap in a `CenteredView` * use `CenteredView`'s side borders * account for loading state on web * move `isTabletOrMobile` * hide top border on first post in list * show border if parents are loading * don't show top border for deleted or blocked posts * hide top border for hidden replies * Rm root post top border --------- Co-authored-by: Dan Abramov <dan.abramov@gmail.com>zio/stable
parent
9628070e52
commit
9edb487949
|
@ -1,5 +1,5 @@
|
||||||
import React, {useEffect, useRef} from 'react'
|
import React, {useEffect, useRef} from 'react'
|
||||||
import {StyleSheet, useWindowDimensions, View} from 'react-native'
|
import {useWindowDimensions, View} from 'react-native'
|
||||||
import {runOnJS} from 'react-native-reanimated'
|
import {runOnJS} from 'react-native-reanimated'
|
||||||
import {AppBskyFeedDefs} from '@atproto/api'
|
import {AppBskyFeedDefs} from '@atproto/api'
|
||||||
import {msg, Trans} from '@lingui/macro'
|
import {msg, Trans} from '@lingui/macro'
|
||||||
|
@ -22,15 +22,16 @@ import {
|
||||||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
|
||||||
import {useSetTitle} from 'lib/hooks/useSetTitle'
|
import {useSetTitle} from 'lib/hooks/useSetTitle'
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
import {sanitizeDisplayName} from 'lib/strings/display-names'
|
import {sanitizeDisplayName} from 'lib/strings/display-names'
|
||||||
import {cleanError} from 'lib/strings/errors'
|
import {cleanError} from 'lib/strings/errors'
|
||||||
|
import {CenteredView} from 'view/com/util/Views'
|
||||||
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
|
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
|
||||||
|
import {Text} from '#/components/Typography'
|
||||||
import {ComposePrompt} from '../composer/Prompt'
|
import {ComposePrompt} from '../composer/Prompt'
|
||||||
import {List, ListMethods} from '../util/List'
|
import {List, ListMethods} from '../util/List'
|
||||||
import {Text} from '../util/text/Text'
|
|
||||||
import {ViewHeader} from '../util/ViewHeader'
|
import {ViewHeader} from '../util/ViewHeader'
|
||||||
import {PostThreadItem} from './PostThreadItem'
|
import {PostThreadItem} from './PostThreadItem'
|
||||||
import {PostThreadShowHiddenReplies} from './PostThreadShowHiddenReplies'
|
import {PostThreadShowHiddenReplies} from './PostThreadShowHiddenReplies'
|
||||||
|
@ -45,7 +46,6 @@ const MAINTAIN_VISIBLE_CONTENT_POSITION = {
|
||||||
minIndexForVisible: 0,
|
minIndexForVisible: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
const TOP_COMPONENT = {_reactKey: '__top_component__'}
|
|
||||||
const REPLY_PROMPT = {_reactKey: '__reply__'}
|
const REPLY_PROMPT = {_reactKey: '__reply__'}
|
||||||
const LOAD_MORE = {_reactKey: '__load_more__'}
|
const LOAD_MORE = {_reactKey: '__load_more__'}
|
||||||
const SHOW_HIDDEN_REPLIES = {_reactKey: '__show_hidden_replies__'}
|
const SHOW_HIDDEN_REPLIES = {_reactKey: '__show_hidden_replies__'}
|
||||||
|
@ -66,7 +66,6 @@ type YieldedItem =
|
||||||
type RowItem =
|
type RowItem =
|
||||||
| YieldedItem
|
| YieldedItem
|
||||||
// TODO: TS doesn't actually enforce it's one of these, it only enforces matching shape.
|
// TODO: TS doesn't actually enforce it's one of these, it only enforces matching shape.
|
||||||
| typeof TOP_COMPONENT
|
|
||||||
| typeof REPLY_PROMPT
|
| typeof REPLY_PROMPT
|
||||||
| typeof LOAD_MORE
|
| typeof LOAD_MORE
|
||||||
|
|
||||||
|
@ -91,7 +90,7 @@ export function PostThread({
|
||||||
}) {
|
}) {
|
||||||
const {hasSession} = useSession()
|
const {hasSession} = useSession()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const pal = usePalette('default')
|
const t = useTheme()
|
||||||
const {isMobile, isTabletOrMobile} = useWebMediaQueries()
|
const {isMobile, isTabletOrMobile} = useWebMediaQueries()
|
||||||
const initialNumToRender = useInitialNumToRender()
|
const initialNumToRender = useInitialNumToRender()
|
||||||
const {height: windowHeight} = useWindowDimensions()
|
const {height: windowHeight} = useWindowDimensions()
|
||||||
|
@ -224,32 +223,21 @@ export function PostThread({
|
||||||
const {parents, highlightedPost, replies} = skeleton
|
const {parents, highlightedPost, replies} = skeleton
|
||||||
let arr: RowItem[] = []
|
let arr: RowItem[] = []
|
||||||
if (highlightedPost.type === 'post') {
|
if (highlightedPost.type === 'post') {
|
||||||
const isRoot =
|
// We want to wait for parents to load before rendering.
|
||||||
!highlightedPost.parent && !highlightedPost.ctx.isParentLoading
|
// If you add something here, you'll need to update both
|
||||||
if (isRoot) {
|
// maintainVisibleContentPosition and onContentSizeChange
|
||||||
// No parents to load.
|
// to "hold onto" the correct row instead of the first one.
|
||||||
arr.push(TOP_COMPONENT)
|
|
||||||
} else {
|
if (!highlightedPost.ctx.isParentLoading && !deferParents) {
|
||||||
if (highlightedPost.ctx.isParentLoading || deferParents) {
|
// When progressively revealing parents, rendering a placeholder
|
||||||
// We're loading parents of the highlighted post.
|
// here will cause scrolling jumps. Don't add it unless you test it.
|
||||||
// In this case, we don't render anything above the post.
|
// QT'ing this thread is a great way to test all the scrolling hacks:
|
||||||
// If you add something here, you'll need to update both
|
// https://bsky.app/profile/www.mozzius.dev/post/3kjqhblh6qk2o
|
||||||
// maintainVisibleContentPosition and onContentSizeChange
|
|
||||||
// to "hold onto" the correct row instead of the first one.
|
// Everything is loaded
|
||||||
} else {
|
let startIndex = Math.max(0, parents.length - maxParents)
|
||||||
// Everything is loaded
|
for (let i = startIndex; i < parents.length; i++) {
|
||||||
let startIndex = Math.max(0, parents.length - maxParents)
|
arr.push(parents[i])
|
||||||
if (startIndex === 0) {
|
|
||||||
arr.push(TOP_COMPONENT)
|
|
||||||
} else {
|
|
||||||
// When progressively revealing parents, rendering a placeholder
|
|
||||||
// here will cause scrolling jumps. Don't add it unless you test it.
|
|
||||||
// QT'ing this thread is a great way to test all the scrolling hacks:
|
|
||||||
// https://bsky.app/profile/www.mozzius.dev/post/3kjqhblh6qk2o
|
|
||||||
}
|
|
||||||
for (let i = startIndex; i < parents.length; i++) {
|
|
||||||
arr.push(parents[i])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
arr.push(highlightedPost)
|
arr.push(highlightedPost)
|
||||||
|
@ -323,117 +311,100 @@ export function PostThread({
|
||||||
setMaxReplies(prev => prev + 50)
|
setMaxReplies(prev => prev + 50)
|
||||||
}, [isFetching, maxReplies, posts.length])
|
}, [isFetching, maxReplies, posts.length])
|
||||||
|
|
||||||
const renderItem = React.useCallback(
|
const hasParents =
|
||||||
({item, index}: {item: RowItem; index: number}) => {
|
skeleton?.highlightedPost?.type === 'post' &&
|
||||||
if (item === TOP_COMPONENT) {
|
(skeleton.highlightedPost.ctx.isParentLoading ||
|
||||||
return isTabletOrMobile ? (
|
Boolean(skeleton?.parents && skeleton.parents.length > 0))
|
||||||
<ViewHeader
|
const showHeader =
|
||||||
title={_(msg({message: `Post`, context: 'description'}))}
|
isNative || (isTabletOrMobile && (!hasParents || !isFetching))
|
||||||
/>
|
|
||||||
) : null
|
const renderItem = ({item, index}: {item: RowItem; index: number}) => {
|
||||||
} else if (item === REPLY_PROMPT && hasSession) {
|
if (item === REPLY_PROMPT && hasSession) {
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
{!isMobile && <ComposePrompt onPressCompose={onPressReply} />}
|
{!isMobile && <ComposePrompt onPressCompose={onPressReply} />}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
} else if (item === SHOW_HIDDEN_REPLIES) {
|
} else if (item === SHOW_HIDDEN_REPLIES || item === SHOW_MUTED_REPLIES) {
|
||||||
return (
|
return (
|
||||||
<PostThreadShowHiddenReplies
|
<PostThreadShowHiddenReplies
|
||||||
type="hidden"
|
type={item === SHOW_HIDDEN_REPLIES ? 'hidden' : 'muted'}
|
||||||
onPress={() =>
|
onPress={() =>
|
||||||
setHiddenRepliesState(HiddenRepliesState.ShowAndOverridePostHider)
|
setHiddenRepliesState(HiddenRepliesState.ShowAndOverridePostHider)
|
||||||
|
}
|
||||||
|
hideTopBorder={index === 0}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
} else if (isThreadNotFound(item)) {
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.p_lg,
|
||||||
|
index !== 0 && a.border_t,
|
||||||
|
t.atoms.border_contrast_low,
|
||||||
|
t.atoms.bg_contrast_25,
|
||||||
|
]}>
|
||||||
|
<Text style={[a.font_bold, a.text_md, t.atoms.text_contrast_medium]}>
|
||||||
|
<Trans>Deleted post.</Trans>
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
} else if (isThreadBlocked(item)) {
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.p_lg,
|
||||||
|
index !== 0 && a.border_t,
|
||||||
|
t.atoms.border_contrast_low,
|
||||||
|
t.atoms.bg_contrast_25,
|
||||||
|
]}>
|
||||||
|
<Text style={[a.font_bold, a.text_md, t.atoms.text_contrast_medium]}>
|
||||||
|
<Trans>Blocked post.</Trans>
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
} else if (isThreadPost(item)) {
|
||||||
|
const prev = isThreadPost(posts[index - 1])
|
||||||
|
? (posts[index - 1] as ThreadPost)
|
||||||
|
: undefined
|
||||||
|
const next = isThreadPost(posts[index + 1])
|
||||||
|
? (posts[index + 1] as ThreadPost)
|
||||||
|
: undefined
|
||||||
|
const showChildReplyLine = (next?.ctx.depth || 0) > item.ctx.depth
|
||||||
|
const showParentReplyLine =
|
||||||
|
(item.ctx.depth < 0 && !!item.parent) || item.ctx.depth > 1
|
||||||
|
const hasUnrevealedParents =
|
||||||
|
index === 0 && skeleton?.parents && maxParents < skeleton.parents.length
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
ref={item.ctx.isHighlightedPost ? highlightedPostRef : undefined}
|
||||||
|
onLayout={deferParents ? () => setDeferParents(false) : undefined}>
|
||||||
|
<PostThreadItem
|
||||||
|
post={item.post}
|
||||||
|
record={item.record}
|
||||||
|
moderation={threadModerationCache.get(item)}
|
||||||
|
treeView={treeView}
|
||||||
|
depth={item.ctx.depth}
|
||||||
|
prevPost={prev}
|
||||||
|
nextPost={next}
|
||||||
|
isHighlightedPost={item.ctx.isHighlightedPost}
|
||||||
|
hasMore={item.ctx.hasMore}
|
||||||
|
showChildReplyLine={showChildReplyLine}
|
||||||
|
showParentReplyLine={showParentReplyLine}
|
||||||
|
hasPrecedingItem={showParentReplyLine || !!hasUnrevealedParents}
|
||||||
|
overrideBlur={
|
||||||
|
hiddenRepliesState ===
|
||||||
|
HiddenRepliesState.ShowAndOverridePostHider &&
|
||||||
|
item.ctx.depth > 0
|
||||||
}
|
}
|
||||||
|
onPostReply={refetch}
|
||||||
|
hideTopBorder={index === 0 && !item.ctx.isParentLoading}
|
||||||
/>
|
/>
|
||||||
)
|
</View>
|
||||||
} else if (item === SHOW_MUTED_REPLIES) {
|
)
|
||||||
return (
|
}
|
||||||
<PostThreadShowHiddenReplies
|
return null
|
||||||
type="muted"
|
}
|
||||||
onPress={() =>
|
|
||||||
setHiddenRepliesState(HiddenRepliesState.ShowAndOverridePostHider)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
} else if (isThreadNotFound(item)) {
|
|
||||||
return (
|
|
||||||
<View style={[pal.border, pal.viewLight, styles.itemContainer]}>
|
|
||||||
<Text type="lg-bold" style={pal.textLight}>
|
|
||||||
<Trans>Deleted post.</Trans>
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
} else if (isThreadBlocked(item)) {
|
|
||||||
return (
|
|
||||||
<View style={[pal.border, pal.viewLight, styles.itemContainer]}>
|
|
||||||
<Text type="lg-bold" style={pal.textLight}>
|
|
||||||
<Trans>Blocked post.</Trans>
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
} else if (isThreadPost(item)) {
|
|
||||||
const prev = isThreadPost(posts[index - 1])
|
|
||||||
? (posts[index - 1] as ThreadPost)
|
|
||||||
: undefined
|
|
||||||
const next = isThreadPost(posts[index + 1])
|
|
||||||
? (posts[index + 1] as ThreadPost)
|
|
||||||
: undefined
|
|
||||||
const showChildReplyLine = (next?.ctx.depth || 0) > item.ctx.depth
|
|
||||||
const showParentReplyLine =
|
|
||||||
(item.ctx.depth < 0 && !!item.parent) || item.ctx.depth > 1
|
|
||||||
const hasUnrevealedParents =
|
|
||||||
index === 0 &&
|
|
||||||
skeleton?.parents &&
|
|
||||||
maxParents < skeleton.parents.length
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
ref={item.ctx.isHighlightedPost ? highlightedPostRef : undefined}
|
|
||||||
onLayout={deferParents ? () => setDeferParents(false) : undefined}>
|
|
||||||
<PostThreadItem
|
|
||||||
post={item.post}
|
|
||||||
record={item.record}
|
|
||||||
moderation={threadModerationCache.get(item)}
|
|
||||||
treeView={treeView}
|
|
||||||
depth={item.ctx.depth}
|
|
||||||
prevPost={prev}
|
|
||||||
nextPost={next}
|
|
||||||
isHighlightedPost={item.ctx.isHighlightedPost}
|
|
||||||
hasMore={item.ctx.hasMore}
|
|
||||||
showChildReplyLine={showChildReplyLine}
|
|
||||||
showParentReplyLine={showParentReplyLine}
|
|
||||||
hasPrecedingItem={showParentReplyLine || !!hasUnrevealedParents}
|
|
||||||
overrideBlur={
|
|
||||||
hiddenRepliesState ===
|
|
||||||
HiddenRepliesState.ShowAndOverridePostHider &&
|
|
||||||
item.ctx.depth > 0
|
|
||||||
}
|
|
||||||
onPostReply={refetch}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
},
|
|
||||||
[
|
|
||||||
hasSession,
|
|
||||||
isTabletOrMobile,
|
|
||||||
_,
|
|
||||||
isMobile,
|
|
||||||
onPressReply,
|
|
||||||
pal.border,
|
|
||||||
pal.viewLight,
|
|
||||||
pal.textLight,
|
|
||||||
posts,
|
|
||||||
skeleton?.parents,
|
|
||||||
maxParents,
|
|
||||||
deferParents,
|
|
||||||
treeView,
|
|
||||||
refetch,
|
|
||||||
threadModerationCache,
|
|
||||||
hiddenRepliesState,
|
|
||||||
setHiddenRepliesState,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!thread || !preferences || error) {
|
if (!thread || !preferences || error) {
|
||||||
return (
|
return (
|
||||||
|
@ -449,39 +420,49 @@ export function PostThread({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollProvider onMomentumEnd={onMomentumEnd}>
|
<CenteredView style={[a.flex_1]} sideBorders={true}>
|
||||||
<List
|
{showHeader && (
|
||||||
ref={ref}
|
<ViewHeader
|
||||||
data={posts}
|
title={_(msg({message: `Post`, context: 'description'}))}
|
||||||
renderItem={renderItem}
|
showBorder
|
||||||
keyExtractor={keyExtractor}
|
/>
|
||||||
onContentSizeChange={isNative ? undefined : onContentSizeChangeWeb}
|
)}
|
||||||
onStartReached={onStartReached}
|
|
||||||
onEndReached={onEndReached}
|
<ScrollProvider onMomentumEnd={onMomentumEnd}>
|
||||||
onEndReachedThreshold={2}
|
<List
|
||||||
onScrollToTop={onScrollToTop}
|
ref={ref}
|
||||||
maintainVisibleContentPosition={
|
data={posts}
|
||||||
isNative ? MAINTAIN_VISIBLE_CONTENT_POSITION : undefined
|
renderItem={renderItem}
|
||||||
}
|
keyExtractor={keyExtractor}
|
||||||
// @ts-ignore our .web version only -prf
|
onContentSizeChange={isNative ? undefined : onContentSizeChangeWeb}
|
||||||
desktopFixedHeight
|
onStartReached={onStartReached}
|
||||||
removeClippedSubviews={isAndroid ? false : undefined}
|
onEndReached={onEndReached}
|
||||||
ListFooterComponent={
|
onEndReachedThreshold={2}
|
||||||
<ListFooter
|
onScrollToTop={onScrollToTop}
|
||||||
// Using `isFetching` over `isFetchingNextPage` is done on purpose here so we get the loader on
|
maintainVisibleContentPosition={
|
||||||
// initial render
|
isNative ? MAINTAIN_VISIBLE_CONTENT_POSITION : undefined
|
||||||
isFetchingNextPage={isFetching}
|
}
|
||||||
error={cleanError(threadError)}
|
// @ts-ignore our .web version only -prf
|
||||||
onRetry={refetch}
|
desktopFixedHeight
|
||||||
// 300 is based on the minimum height of a post. This is enough extra height for the `maintainVisPos` to
|
removeClippedSubviews={isAndroid ? false : undefined}
|
||||||
// work without causing weird jumps on web or glitches on native
|
ListFooterComponent={
|
||||||
height={windowHeight - 200}
|
<ListFooter
|
||||||
/>
|
// Using `isFetching` over `isFetchingNextPage` is done on purpose here so we get the loader on
|
||||||
}
|
// initial render
|
||||||
initialNumToRender={initialNumToRender}
|
isFetchingNextPage={isFetching}
|
||||||
windowSize={11}
|
error={cleanError(threadError)}
|
||||||
/>
|
onRetry={refetch}
|
||||||
</ScrollProvider>
|
// 300 is based on the minimum height of a post. This is enough extra height for the `maintainVisPos` to
|
||||||
|
// work without causing weird jumps on web or glitches on native
|
||||||
|
height={windowHeight - 200}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
initialNumToRender={initialNumToRender}
|
||||||
|
windowSize={11}
|
||||||
|
sideBorders={false}
|
||||||
|
/>
|
||||||
|
</ScrollProvider>
|
||||||
|
</CenteredView>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -630,11 +611,3 @@ function hasBranchingReplies(node?: ThreadNode) {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
itemContainer: {
|
|
||||||
borderTopWidth: 1,
|
|
||||||
paddingHorizontal: 18,
|
|
||||||
paddingVertical: 18,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
|
@ -65,6 +65,7 @@ export function PostThreadItem({
|
||||||
hasPrecedingItem,
|
hasPrecedingItem,
|
||||||
overrideBlur,
|
overrideBlur,
|
||||||
onPostReply,
|
onPostReply,
|
||||||
|
hideTopBorder,
|
||||||
}: {
|
}: {
|
||||||
post: AppBskyFeedDefs.PostView
|
post: AppBskyFeedDefs.PostView
|
||||||
record: AppBskyFeedPost.Record
|
record: AppBskyFeedPost.Record
|
||||||
|
@ -80,6 +81,7 @@ export function PostThreadItem({
|
||||||
hasPrecedingItem: boolean
|
hasPrecedingItem: boolean
|
||||||
overrideBlur: boolean
|
overrideBlur: boolean
|
||||||
onPostReply: () => void
|
onPostReply: () => void
|
||||||
|
hideTopBorder?: boolean
|
||||||
}) {
|
}) {
|
||||||
const postShadowed = usePostShadow(post)
|
const postShadowed = usePostShadow(post)
|
||||||
const richText = useMemo(
|
const richText = useMemo(
|
||||||
|
@ -91,7 +93,7 @@ export function PostThreadItem({
|
||||||
[record],
|
[record],
|
||||||
)
|
)
|
||||||
if (postShadowed === POST_TOMBSTONE) {
|
if (postShadowed === POST_TOMBSTONE) {
|
||||||
return <PostThreadItemDeleted />
|
return <PostThreadItemDeleted hideTopBorder={hideTopBorder} />
|
||||||
}
|
}
|
||||||
if (richText && moderation) {
|
if (richText && moderation) {
|
||||||
return (
|
return (
|
||||||
|
@ -113,16 +115,25 @@ export function PostThreadItem({
|
||||||
hasPrecedingItem={hasPrecedingItem}
|
hasPrecedingItem={hasPrecedingItem}
|
||||||
overrideBlur={overrideBlur}
|
overrideBlur={overrideBlur}
|
||||||
onPostReply={onPostReply}
|
onPostReply={onPostReply}
|
||||||
|
hideTopBorder={hideTopBorder}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
function PostThreadItemDeleted() {
|
function PostThreadItemDeleted({hideTopBorder}: {hideTopBorder?: boolean}) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
return (
|
return (
|
||||||
<View style={[styles.outer, pal.border, pal.view, s.p20, s.flexRow]}>
|
<View
|
||||||
|
style={[
|
||||||
|
styles.outer,
|
||||||
|
pal.border,
|
||||||
|
pal.view,
|
||||||
|
s.p20,
|
||||||
|
s.flexRow,
|
||||||
|
hideTopBorder && styles.noTopBorder,
|
||||||
|
]}>
|
||||||
<FontAwesomeIcon icon={['far', 'trash-can']} color={pal.colors.icon} />
|
<FontAwesomeIcon icon={['far', 'trash-can']} color={pal.colors.icon} />
|
||||||
<Text style={[pal.textLight, s.ml10]}>
|
<Text style={[pal.textLight, s.ml10]}>
|
||||||
<Trans>This post has been deleted.</Trans>
|
<Trans>This post has been deleted.</Trans>
|
||||||
|
@ -147,6 +158,7 @@ let PostThreadItemLoaded = ({
|
||||||
hasPrecedingItem,
|
hasPrecedingItem,
|
||||||
overrideBlur,
|
overrideBlur,
|
||||||
onPostReply,
|
onPostReply,
|
||||||
|
hideTopBorder,
|
||||||
}: {
|
}: {
|
||||||
post: Shadow<AppBskyFeedDefs.PostView>
|
post: Shadow<AppBskyFeedDefs.PostView>
|
||||||
record: AppBskyFeedPost.Record
|
record: AppBskyFeedPost.Record
|
||||||
|
@ -163,6 +175,7 @@ let PostThreadItemLoaded = ({
|
||||||
hasPrecedingItem: boolean
|
hasPrecedingItem: boolean
|
||||||
overrideBlur: boolean
|
overrideBlur: boolean
|
||||||
onPostReply: () => void
|
onPostReply: () => void
|
||||||
|
hideTopBorder?: boolean
|
||||||
}): React.ReactNode => {
|
}): React.ReactNode => {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
|
@ -237,7 +250,7 @@ let PostThreadItemLoaded = ({
|
||||||
styles.replyLine,
|
styles.replyLine,
|
||||||
{
|
{
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
backgroundColor: pal.colors.border,
|
backgroundColor: pal.colors.replyLine,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
@ -247,7 +260,14 @@ let PostThreadItemLoaded = ({
|
||||||
|
|
||||||
<View
|
<View
|
||||||
testID={`postThreadItem-by-${post.author.handle}`}
|
testID={`postThreadItem-by-${post.author.handle}`}
|
||||||
style={[styles.outer, styles.outerHighlighted, pal.border, pal.view]}
|
style={[
|
||||||
|
styles.outer,
|
||||||
|
styles.outerHighlighted,
|
||||||
|
pal.border,
|
||||||
|
pal.view,
|
||||||
|
rootUri === post.uri && styles.outerHighlightedRoot,
|
||||||
|
hideTopBorder && styles.noTopBorder,
|
||||||
|
]}
|
||||||
accessible={false}>
|
accessible={false}>
|
||||||
<View style={[styles.layout]}>
|
<View style={[styles.layout]}>
|
||||||
<View style={[styles.layoutAvi, {paddingBottom: 8}]}>
|
<View style={[styles.layoutAvi, {paddingBottom: 8}]}>
|
||||||
|
@ -395,7 +415,8 @@ let PostThreadItemLoaded = ({
|
||||||
depth={depth}
|
depth={depth}
|
||||||
showParentReplyLine={!!showParentReplyLine}
|
showParentReplyLine={!!showParentReplyLine}
|
||||||
treeView={treeView}
|
treeView={treeView}
|
||||||
hasPrecedingItem={hasPrecedingItem}>
|
hasPrecedingItem={hasPrecedingItem}
|
||||||
|
hideTopBorder={hideTopBorder}>
|
||||||
<PostHider
|
<PostHider
|
||||||
testID={`postThreadItem-by-${post.author.handle}`}
|
testID={`postThreadItem-by-${post.author.handle}`}
|
||||||
href={postHref}
|
href={postHref}
|
||||||
|
@ -574,6 +595,7 @@ function PostOuterWrapper({
|
||||||
depth,
|
depth,
|
||||||
showParentReplyLine,
|
showParentReplyLine,
|
||||||
hasPrecedingItem,
|
hasPrecedingItem,
|
||||||
|
hideTopBorder,
|
||||||
children,
|
children,
|
||||||
}: React.PropsWithChildren<{
|
}: React.PropsWithChildren<{
|
||||||
post: AppBskyFeedDefs.PostView
|
post: AppBskyFeedDefs.PostView
|
||||||
|
@ -581,6 +603,7 @@ function PostOuterWrapper({
|
||||||
depth: number
|
depth: number
|
||||||
showParentReplyLine: boolean
|
showParentReplyLine: boolean
|
||||||
hasPrecedingItem: boolean
|
hasPrecedingItem: boolean
|
||||||
|
hideTopBorder?: boolean
|
||||||
}>) {
|
}>) {
|
||||||
const {isMobile} = useWebMediaQueries()
|
const {isMobile} = useWebMediaQueries()
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
|
@ -617,6 +640,7 @@ function PostOuterWrapper({
|
||||||
styles.outer,
|
styles.outer,
|
||||||
pal.border,
|
pal.border,
|
||||||
showParentReplyLine && hasPrecedingItem && styles.noTopBorder,
|
showParentReplyLine && hasPrecedingItem && styles.noTopBorder,
|
||||||
|
hideTopBorder && styles.noTopBorder,
|
||||||
styles.cursor,
|
styles.cursor,
|
||||||
]}>
|
]}>
|
||||||
{children}
|
{children}
|
||||||
|
@ -677,10 +701,15 @@ const styles = StyleSheet.create({
|
||||||
paddingLeft: 8,
|
paddingLeft: 8,
|
||||||
},
|
},
|
||||||
outerHighlighted: {
|
outerHighlighted: {
|
||||||
paddingTop: 16,
|
borderTopWidth: 0,
|
||||||
|
paddingTop: 4,
|
||||||
paddingLeft: 8,
|
paddingLeft: 8,
|
||||||
paddingRight: 8,
|
paddingRight: 8,
|
||||||
},
|
},
|
||||||
|
outerHighlightedRoot: {
|
||||||
|
borderTopWidth: 1,
|
||||||
|
paddingTop: 16,
|
||||||
|
},
|
||||||
noTopBorder: {
|
noTopBorder: {
|
||||||
borderTopWidth: 0,
|
borderTopWidth: 0,
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,9 +11,11 @@ import {Text} from '#/components/Typography'
|
||||||
export function PostThreadShowHiddenReplies({
|
export function PostThreadShowHiddenReplies({
|
||||||
type,
|
type,
|
||||||
onPress,
|
onPress,
|
||||||
|
hideTopBorder,
|
||||||
}: {
|
}: {
|
||||||
type: 'hidden' | 'muted'
|
type: 'hidden' | 'muted'
|
||||||
onPress: () => void
|
onPress: () => void
|
||||||
|
hideTopBorder?: boolean
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
|
@ -31,7 +33,7 @@ export function PostThreadShowHiddenReplies({
|
||||||
a.gap_sm,
|
a.gap_sm,
|
||||||
a.py_lg,
|
a.py_lg,
|
||||||
a.px_xl,
|
a.px_xl,
|
||||||
a.border_t,
|
!hideTopBorder && a.border_t,
|
||||||
t.atoms.border_contrast_low,
|
t.atoms.border_contrast_low,
|
||||||
hovered || pressed ? t.atoms.bg_contrast_25 : t.atoms.bg,
|
hovered || pressed ? t.atoms.bg_contrast_25 : t.atoms.bg,
|
||||||
]}>
|
]}>
|
||||||
|
|
Loading…
Reference in New Issue