Two passes only
This commit is contained in:
@@ -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 (
|
||||
<ThreadPost
|
||||
item={item}
|
||||
@@ -275,7 +277,7 @@ export function Inner({uri}: {uri: string | undefined}) {
|
||||
>
|
||||
<List
|
||||
ref={listRef}
|
||||
data={items}
|
||||
data={data?.items || []}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={keyExtractor}
|
||||
onContentSizeChange={onContentSizeChangeWebOnly}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import {useMemo} from 'react'
|
||||
import {useQuery, useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||
@@ -5,7 +6,7 @@ import {
|
||||
createCacheMutator,
|
||||
getThreadPlaceholder,
|
||||
} from '#/state/queries/usePostThread/queryCache'
|
||||
import {flatten, sort} from '#/state/queries/usePostThread/traversal'
|
||||
import {traverse} from '#/state/queries/usePostThread/traversal'
|
||||
import {
|
||||
createPostThreadQueryKey,
|
||||
HiddenReplyKind,
|
||||
@@ -69,20 +70,23 @@ export function usePostThread({
|
||||
|
||||
// TODO map over pages, just like feeds
|
||||
|
||||
const items = flatten(
|
||||
sort(query.data?.thread || [], {
|
||||
const items = useMemo(() => {
|
||||
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,
|
||||
|
||||
@@ -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<typeof sort>,
|
||||
{
|
||||
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<string>
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user