diff --git a/src/screens/PostThread/index.tsx b/src/screens/PostThread/index.tsx index 959ab4d5de..9d440dc863 100644 --- a/src/screens/PostThread/index.tsx +++ b/src/screens/PostThread/index.tsx @@ -116,12 +116,19 @@ export function Inner({uri}: {uri: string | undefined}) { // distance from top of the list (screen) const anchorOffsetTop = anchorElement.getBoundingClientRect().top const headerHeight = headerElement.getBoundingClientRect().height - // don't scroll past 0 - const scrollPosition = Math.max(0, anchorOffsetTop - headerHeight) - listRef.current?.scrollToOffset({ - animated: false, - offset: scrollPosition, - }) + const scrollPosition = anchorOffsetTop - headerHeight + /* + * If scroll position is negative, it means the anchor post is above the + * top of the screen, meaning the user scrolled the list. In that case, + * we want to restore the previous scroll position by not scrolling here + * at all. + */ + if (scrollPosition >= 0) { + listRef.current?.scrollToOffset({ + animated: false, + offset: scrollPosition, + }) + } } }) @@ -137,15 +144,10 @@ export function Inner({uri}: {uri: string | undefined}) { * scroll in onContentSizeChange instead. */ const [deferParents, setDeferParents] = useState(isNative) - const items = useMemo(() => { - return (data?.items ?? []).filter(item => { - return !('depth' in item) || item.depth >= 0 || !deferParents - }) - }, [data, deferParents]) - const renderItem = ({item, index}: {item: Slice; index: number}) => { if (item.type === 'threadPost') { if (item.depth < 0) { + if (deferParents) return null return ( { + return traverse(query.data?.thread || [], { threadgateHiddenReplies: mergeThreadgateHiddenReplies( query.data?.threadgate?.record, ), moderationOpts: moderationOpts!, - }), - { hasSession, showMuted: state.shownHiddenReplyKinds.has(HiddenReplyKind.Muted), showHidden: state.shownHiddenReplyKinds.has(HiddenReplyKind.Hidden), - view: params.view, - }, - ) + }) + }, [ + query.data, + mergeThreadgateHiddenReplies, + moderationOpts, + hasSession, + state.shownHiddenReplyKinds, + ]) const mutator = createCacheMutator({ params, diff --git a/src/state/queries/usePostThread/traversal.ts b/src/state/queries/usePostThread/traversal.ts index 4b6331a2ad..8554910422 100644 --- a/src/state/queries/usePostThread/traversal.ts +++ b/src/state/queries/usePostThread/traversal.ts @@ -6,7 +6,6 @@ import { import { HiddenReplyKind, - type PostThreadParams, type Slice, type TraversalMetadata, } from '#/state/queries/usePostThread/types' @@ -18,88 +17,20 @@ import { } from '#/state/queries/usePostThread/utils' import * as views from '#/state/queries/usePostThread/views' -export function flatten( - sorted: ReturnType, - { - hasSession, - showMuted, - showHidden, - }: { - hasSession: boolean - showMuted: boolean - showHidden: boolean - view: PostThreadParams['view'] - }, -) { - const flattened: Slice[] = sorted.items - - for (let i = 0; i < flattened.length; i++) { - const item = flattened[i] - - if (item.type === 'threadPost') { - // TODO should not insert if not found post etc - if ( - item.ui.isAnchor && - hasSession && - !item.value.post.viewer?.replyDisabled - ) { - flattened.splice(i + 1, 0, { - type: 'replyComposer', - key: 'replyComposer', - }) - } - } - } - - /* - * Insert hidden items and buttons to show them - */ - - if (sorted.hidden.length) { - if (showHidden) { - flattened.push(...sorted.hidden) - - if (sorted.muted.length) { - if (showMuted) { - flattened.push(...sorted.muted) - } else { - flattened.push({ - type: 'showHiddenReplies', - key: 'showMutedReplies', - kind: HiddenReplyKind.Muted, - }) - } - } - } else { - flattened.push({ - type: 'showHiddenReplies', - key: 'showHiddenReplies', - kind: HiddenReplyKind.Hidden, - }) - } - } else if (sorted.muted.length) { - if (showMuted) { - flattened.push(...sorted.muted) - } else { - flattened.push({ - type: 'showHiddenReplies', - key: 'showMutedReplies', - kind: HiddenReplyKind.Muted, - }) - } - } - - return flattened -} - -export function sort( +export function traverse( thread: AppBskyUnspeccedGetPostThreadV2.OutputSchema['thread'], { threadgateHiddenReplies, moderationOpts, + hasSession, + showMuted, + showHidden, }: { threadgateHiddenReplies: Set moderationOpts: ModerationOpts + hasSession: boolean + showMuted: boolean + showHidden: boolean }, ) { const items: Slice[] = [] @@ -321,6 +252,18 @@ export function sort( const item = items[i] if (item.type === 'threadPost') { + if ( + item.depth === 0 && + !item.value.post.viewer?.replyDisabled && + hasSession + ) { + items.splice(i + 1, 0, { + type: 'replyComposer', + key: 'replyComposer', + }) + i++ // skip next iteration + } + const metadata = metadatas.get(item.uri) if (metadata) { @@ -429,11 +372,41 @@ export function sort( } } - return { - items, - hidden, - muted, + if (hidden.length) { + if (showHidden) { + items.push(...hidden) + + if (muted.length) { + if (showMuted) { + items.push(...muted) + } else { + items.push({ + type: 'showHiddenReplies', + key: 'showMutedReplies', + kind: HiddenReplyKind.Muted, + }) + } + } + } else { + items.push({ + type: 'showHiddenReplies', + key: 'showHiddenReplies', + kind: HiddenReplyKind.Hidden, + }) + } + } else if (muted.length) { + if (showMuted) { + items.push(...muted) + } else { + items.push({ + type: 'showHiddenReplies', + key: 'showMutedReplies', + kind: HiddenReplyKind.Muted, + }) + } } + + return items } /**