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)
|
// distance from top of the list (screen)
|
||||||
const anchorOffsetTop = anchorElement.getBoundingClientRect().top
|
const anchorOffsetTop = anchorElement.getBoundingClientRect().top
|
||||||
const headerHeight = headerElement.getBoundingClientRect().height
|
const headerHeight = headerElement.getBoundingClientRect().height
|
||||||
// don't scroll past 0
|
const scrollPosition = anchorOffsetTop - headerHeight
|
||||||
const scrollPosition = Math.max(0, anchorOffsetTop - headerHeight)
|
/*
|
||||||
listRef.current?.scrollToOffset({
|
* If scroll position is negative, it means the anchor post is above the
|
||||||
animated: false,
|
* top of the screen, meaning the user scrolled the list. In that case,
|
||||||
offset: scrollPosition,
|
* 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.
|
* scroll in onContentSizeChange instead.
|
||||||
*/
|
*/
|
||||||
const [deferParents, setDeferParents] = useState(isNative)
|
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}) => {
|
const renderItem = ({item, index}: {item: Slice; index: number}) => {
|
||||||
if (item.type === 'threadPost') {
|
if (item.type === 'threadPost') {
|
||||||
if (item.depth < 0) {
|
if (item.depth < 0) {
|
||||||
|
if (deferParents) return null
|
||||||
return (
|
return (
|
||||||
<ThreadPost
|
<ThreadPost
|
||||||
item={item}
|
item={item}
|
||||||
@@ -275,7 +277,7 @@ export function Inner({uri}: {uri: string | undefined}) {
|
|||||||
>
|
>
|
||||||
<List
|
<List
|
||||||
ref={listRef}
|
ref={listRef}
|
||||||
data={items}
|
data={data?.items || []}
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
keyExtractor={keyExtractor}
|
keyExtractor={keyExtractor}
|
||||||
onContentSizeChange={onContentSizeChangeWebOnly}
|
onContentSizeChange={onContentSizeChangeWebOnly}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import {useMemo} from 'react'
|
||||||
import {useQuery, useQueryClient} from '@tanstack/react-query'
|
import {useQuery, useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||||
@@ -5,7 +6,7 @@ import {
|
|||||||
createCacheMutator,
|
createCacheMutator,
|
||||||
getThreadPlaceholder,
|
getThreadPlaceholder,
|
||||||
} from '#/state/queries/usePostThread/queryCache'
|
} from '#/state/queries/usePostThread/queryCache'
|
||||||
import {flatten, sort} from '#/state/queries/usePostThread/traversal'
|
import {traverse} from '#/state/queries/usePostThread/traversal'
|
||||||
import {
|
import {
|
||||||
createPostThreadQueryKey,
|
createPostThreadQueryKey,
|
||||||
HiddenReplyKind,
|
HiddenReplyKind,
|
||||||
@@ -69,20 +70,23 @@ export function usePostThread({
|
|||||||
|
|
||||||
// TODO map over pages, just like feeds
|
// TODO map over pages, just like feeds
|
||||||
|
|
||||||
const items = flatten(
|
const items = useMemo(() => {
|
||||||
sort(query.data?.thread || [], {
|
return traverse(query.data?.thread || [], {
|
||||||
threadgateHiddenReplies: mergeThreadgateHiddenReplies(
|
threadgateHiddenReplies: mergeThreadgateHiddenReplies(
|
||||||
query.data?.threadgate?.record,
|
query.data?.threadgate?.record,
|
||||||
),
|
),
|
||||||
moderationOpts: moderationOpts!,
|
moderationOpts: moderationOpts!,
|
||||||
}),
|
|
||||||
{
|
|
||||||
hasSession,
|
hasSession,
|
||||||
showMuted: state.shownHiddenReplyKinds.has(HiddenReplyKind.Muted),
|
showMuted: state.shownHiddenReplyKinds.has(HiddenReplyKind.Muted),
|
||||||
showHidden: state.shownHiddenReplyKinds.has(HiddenReplyKind.Hidden),
|
showHidden: state.shownHiddenReplyKinds.has(HiddenReplyKind.Hidden),
|
||||||
view: params.view,
|
})
|
||||||
},
|
}, [
|
||||||
)
|
query.data,
|
||||||
|
mergeThreadgateHiddenReplies,
|
||||||
|
moderationOpts,
|
||||||
|
hasSession,
|
||||||
|
state.shownHiddenReplyKinds,
|
||||||
|
])
|
||||||
|
|
||||||
const mutator = createCacheMutator({
|
const mutator = createCacheMutator({
|
||||||
params,
|
params,
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
HiddenReplyKind,
|
HiddenReplyKind,
|
||||||
type PostThreadParams,
|
|
||||||
type Slice,
|
type Slice,
|
||||||
type TraversalMetadata,
|
type TraversalMetadata,
|
||||||
} from '#/state/queries/usePostThread/types'
|
} from '#/state/queries/usePostThread/types'
|
||||||
@@ -18,88 +17,20 @@ import {
|
|||||||
} from '#/state/queries/usePostThread/utils'
|
} from '#/state/queries/usePostThread/utils'
|
||||||
import * as views from '#/state/queries/usePostThread/views'
|
import * as views from '#/state/queries/usePostThread/views'
|
||||||
|
|
||||||
export function flatten(
|
export function traverse(
|
||||||
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(
|
|
||||||
thread: AppBskyUnspeccedGetPostThreadV2.OutputSchema['thread'],
|
thread: AppBskyUnspeccedGetPostThreadV2.OutputSchema['thread'],
|
||||||
{
|
{
|
||||||
threadgateHiddenReplies,
|
threadgateHiddenReplies,
|
||||||
moderationOpts,
|
moderationOpts,
|
||||||
|
hasSession,
|
||||||
|
showMuted,
|
||||||
|
showHidden,
|
||||||
}: {
|
}: {
|
||||||
threadgateHiddenReplies: Set<string>
|
threadgateHiddenReplies: Set<string>
|
||||||
moderationOpts: ModerationOpts
|
moderationOpts: ModerationOpts
|
||||||
|
hasSession: boolean
|
||||||
|
showMuted: boolean
|
||||||
|
showHidden: boolean
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
const items: Slice[] = []
|
const items: Slice[] = []
|
||||||
@@ -321,6 +252,18 @@ export function sort(
|
|||||||
const item = items[i]
|
const item = items[i]
|
||||||
|
|
||||||
if (item.type === 'threadPost') {
|
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)
|
const metadata = metadatas.get(item.uri)
|
||||||
|
|
||||||
if (metadata) {
|
if (metadata) {
|
||||||
@@ -429,11 +372,41 @@ export function sort(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
if (hidden.length) {
|
||||||
items,
|
if (showHidden) {
|
||||||
hidden,
|
items.push(...hidden)
|
||||||
muted,
|
|
||||||
|
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