Split up traversal and avoid multiple passes
This commit is contained in:
@@ -8,7 +8,7 @@ import {
|
|||||||
createCacheMutator,
|
createCacheMutator,
|
||||||
getThreadPlaceholder,
|
getThreadPlaceholder,
|
||||||
} from '#/state/queries/usePostThread/queryCache'
|
} from '#/state/queries/usePostThread/queryCache'
|
||||||
import {traverse} from '#/state/queries/usePostThread/traversal'
|
import {combine,traverse} from '#/state/queries/usePostThread/traversal'
|
||||||
import {
|
import {
|
||||||
createPostThreadHiddenQueryKey,
|
createPostThreadHiddenQueryKey,
|
||||||
createPostThreadQueryKey,
|
createPostThreadQueryKey,
|
||||||
@@ -150,50 +150,44 @@ export function usePostThread({anchor}: {anchor?: string}) {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
const items = traverse(data || [], {
|
const {items} = traverse(data || [], {
|
||||||
|
view,
|
||||||
|
skipHiddenReplyHandling: true,
|
||||||
threadgateHiddenReplies: mergeThreadgateHiddenReplies(
|
threadgateHiddenReplies: mergeThreadgateHiddenReplies(
|
||||||
query.data?.threadgate?.record,
|
query.data?.threadgate?.record,
|
||||||
),
|
),
|
||||||
moderationOpts: moderationOpts!,
|
moderationOpts: moderationOpts!,
|
||||||
hasSession,
|
|
||||||
view,
|
|
||||||
hasServerHiddenReplies,
|
|
||||||
hiddenRepliesVisible,
|
|
||||||
skipHiddenReplyHandling: true,
|
|
||||||
loadHiddenReplies,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// insert the hidden replies into the state
|
// insert the hidden replies into the state
|
||||||
setAdditionalHiddenItems(items)
|
setAdditionalHiddenItems(items)
|
||||||
}, [
|
}, [
|
||||||
|
qc,
|
||||||
agent,
|
agent,
|
||||||
view,
|
view,
|
||||||
anchor,
|
anchor,
|
||||||
prioritizeFollowedUsers,
|
prioritizeFollowedUsers,
|
||||||
hasSession,
|
|
||||||
mergeThreadgateHiddenReplies,
|
mergeThreadgateHiddenReplies,
|
||||||
moderationOpts,
|
moderationOpts,
|
||||||
qc,
|
|
||||||
query.data?.threadgate?.record,
|
query.data?.threadgate?.record,
|
||||||
hasServerHiddenReplies,
|
hasServerHiddenReplies,
|
||||||
hiddenRepliesVisible,
|
|
||||||
setHiddenRepliesVisible,
|
setHiddenRepliesVisible,
|
||||||
])
|
])
|
||||||
|
|
||||||
const items = useMemo(() => {
|
const combined = useMemo(() => {
|
||||||
const results = traverse(query.data?.thread || [], {
|
const traversal = traverse(query.data?.thread || [], {
|
||||||
|
view: view,
|
||||||
threadgateHiddenReplies: mergeThreadgateHiddenReplies(
|
threadgateHiddenReplies: mergeThreadgateHiddenReplies(
|
||||||
query.data?.threadgate?.record,
|
query.data?.threadgate?.record,
|
||||||
),
|
),
|
||||||
moderationOpts: moderationOpts!,
|
moderationOpts: moderationOpts!,
|
||||||
|
})
|
||||||
|
return combine(traversal, {
|
||||||
hasSession,
|
hasSession,
|
||||||
view: view,
|
|
||||||
hasServerHiddenReplies,
|
hasServerHiddenReplies,
|
||||||
hiddenRepliesVisible,
|
hiddenRepliesVisible,
|
||||||
loadHiddenReplies,
|
loadHiddenReplies,
|
||||||
})
|
})
|
||||||
|
|
||||||
return results.concat(additionalHiddenItems)
|
|
||||||
}, [
|
}, [
|
||||||
query.data,
|
query.data,
|
||||||
mergeThreadgateHiddenReplies,
|
mergeThreadgateHiddenReplies,
|
||||||
@@ -203,9 +197,12 @@ export function usePostThread({anchor}: {anchor?: string}) {
|
|||||||
hasServerHiddenReplies,
|
hasServerHiddenReplies,
|
||||||
hiddenRepliesVisible,
|
hiddenRepliesVisible,
|
||||||
loadHiddenReplies,
|
loadHiddenReplies,
|
||||||
additionalHiddenItems,
|
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const items = useMemo(() => {
|
||||||
|
return combined.concat(additionalHiddenItems)
|
||||||
|
}, [combined, additionalHiddenItems])
|
||||||
|
|
||||||
if (query.isPlaceholderData) {
|
if (query.isPlaceholderData) {
|
||||||
const anchorPost = items.at(0)
|
const anchorPost = items.at(0)
|
||||||
const skeletonReplies =
|
const skeletonReplies =
|
||||||
|
|||||||
@@ -23,21 +23,20 @@ export function traverse(
|
|||||||
{
|
{
|
||||||
threadgateHiddenReplies,
|
threadgateHiddenReplies,
|
||||||
moderationOpts,
|
moderationOpts,
|
||||||
hasSession,
|
|
||||||
view,
|
view,
|
||||||
hasServerHiddenReplies,
|
|
||||||
hiddenRepliesVisible,
|
|
||||||
skipHiddenReplyHandling,
|
skipHiddenReplyHandling,
|
||||||
loadHiddenReplies,
|
|
||||||
}: {
|
}: {
|
||||||
threadgateHiddenReplies: Set<string>
|
threadgateHiddenReplies: Set<string>
|
||||||
moderationOpts: ModerationOpts
|
moderationOpts: ModerationOpts
|
||||||
hasSession: boolean
|
|
||||||
view: PostThreadParams['view']
|
view: PostThreadParams['view']
|
||||||
hasServerHiddenReplies: boolean
|
/**
|
||||||
hiddenRepliesVisible: boolean
|
* Set to `true` in cases where we already know the moderation state of the
|
||||||
|
* post e.g. when fetching server-hidden replies. This will prevent
|
||||||
|
* additional sorting or nested-branch truncation, and all replies,
|
||||||
|
* regardless of moderation state, will be included in the resulting
|
||||||
|
* `items` array.
|
||||||
|
*/
|
||||||
skipHiddenReplyHandling?: boolean
|
skipHiddenReplyHandling?: boolean
|
||||||
loadHiddenReplies: () => Promise<void>
|
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
const items: ThreadItem[] = []
|
const items: ThreadItem[] = []
|
||||||
@@ -80,6 +79,7 @@ export function traverse(
|
|||||||
depth: item.depth,
|
depth: item.depth,
|
||||||
value: item.value,
|
value: item.value,
|
||||||
moderationOpts,
|
moderationOpts,
|
||||||
|
threadgateHiddenReplies,
|
||||||
})
|
})
|
||||||
items.push(post)
|
items.push(post)
|
||||||
|
|
||||||
@@ -104,6 +104,7 @@ export function traverse(
|
|||||||
depth: parent.depth,
|
depth: parent.depth,
|
||||||
value: parent.value,
|
value: parent.value,
|
||||||
moderationOpts,
|
moderationOpts,
|
||||||
|
threadgateHiddenReplies,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -140,15 +141,12 @@ export function traverse(
|
|||||||
depth: item.depth,
|
depth: item.depth,
|
||||||
value: item.value,
|
value: item.value,
|
||||||
moderationOpts,
|
moderationOpts,
|
||||||
|
threadgateHiddenReplies,
|
||||||
})
|
})
|
||||||
const postMod = getModerationState(post.moderation)
|
|
||||||
const postIsHiddenByThreadgate = threadgateHiddenReplies.has(item.uri)
|
|
||||||
const postIsModerated =
|
|
||||||
postIsHiddenByThreadgate || postMod.blurred || postMod.muted
|
|
||||||
|
|
||||||
if (!postIsModerated || skipHiddenReplyHandling) {
|
if (!post.isBlurred || skipHiddenReplyHandling) {
|
||||||
/*
|
/*
|
||||||
* Not moderated, probably need to insert it
|
* Not moderated, need to insert it
|
||||||
*/
|
*/
|
||||||
items.push(post)
|
items.push(post)
|
||||||
|
|
||||||
@@ -201,21 +199,15 @@ export function traverse(
|
|||||||
depth: child.depth,
|
depth: child.depth,
|
||||||
value: child.value,
|
value: child.value,
|
||||||
moderationOpts,
|
moderationOpts,
|
||||||
|
threadgateHiddenReplies,
|
||||||
})
|
})
|
||||||
const childPostMod = getModerationState(childPost.moderation)
|
|
||||||
const childPostIsHiddenByThreadgate =
|
|
||||||
threadgateHiddenReplies.has(child.uri)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If a child is hidden in any way, drop it an its sub-branch
|
* If a child is hidden in any way, drop it an its sub-branch
|
||||||
* entirely. To reveal these, the user must navigate to the
|
* entirely. To reveal these, the user must navigate to the
|
||||||
* parent post directly.
|
* parent post directly.
|
||||||
*/
|
*/
|
||||||
if (
|
if (childPost.isBlurred) {
|
||||||
childPostMod.blurred ||
|
|
||||||
childPostMod.muted ||
|
|
||||||
childPostIsHiddenByThreadgate
|
|
||||||
) {
|
|
||||||
ci = getBranch(thread, ci, child.depth).end
|
ci = getBranch(thread, ci, child.depth).end
|
||||||
} else {
|
} else {
|
||||||
hidden.push(childPost)
|
hidden.push(childPost)
|
||||||
@@ -239,160 +231,189 @@ export function traverse(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!skipHiddenReplyHandling) {
|
/*
|
||||||
if (hidden.length || hasServerHiddenReplies) {
|
* Both `items` and `hidden` now need to be traversed again to fully compute
|
||||||
if (hiddenRepliesVisible) {
|
* UI state based on collected metadata. These arrays will be muted in situ.
|
||||||
items.push(...hidden)
|
*/
|
||||||
} else {
|
for (const subset of [items, hidden]) {
|
||||||
items.push({
|
for (let i = 0; i < subset.length; i++) {
|
||||||
type: 'showHiddenReplies',
|
const item = subset[i]
|
||||||
key: 'showHiddenReplies',
|
const prevItem = subset.at(i - 1)
|
||||||
onLoad: loadHiddenReplies,
|
const nextItem = subset.at(i + 1)
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < items.length; i++) {
|
if (item.type === 'threadPost') {
|
||||||
const item = items[i]
|
const metadata = metadatas.get(item.uri)
|
||||||
const prevItem = items.at(i - 1)
|
|
||||||
const nextItem = items.at(i + 1)
|
|
||||||
|
|
||||||
if (item.type === 'threadPost') {
|
if (metadata) {
|
||||||
if (
|
if (metadata.parentMetadata) {
|
||||||
item.depth === 0 &&
|
/*
|
||||||
!item.value.post.viewer?.replyDisabled &&
|
* Track what's before/after now that we've applied moderation
|
||||||
hasSession
|
*/
|
||||||
) {
|
if (prevItem?.type === 'threadPost')
|
||||||
items.splice(i + 1, 0, {
|
metadata.prevItemDepth = prevItem?.depth
|
||||||
type: 'replyComposer',
|
if (nextItem?.type === 'threadPost')
|
||||||
key: 'replyComposer',
|
metadata.nextItemDepth = nextItem?.depth
|
||||||
})
|
|
||||||
i++ // skip next iteration
|
|
||||||
}
|
|
||||||
|
|
||||||
const metadata = metadatas.get(item.uri)
|
/*
|
||||||
|
* We can now officially calculate `isLastSibling` and `isLastChild`
|
||||||
|
* based on the actual data that we've seen.
|
||||||
|
*/
|
||||||
|
metadata.isLastSibling =
|
||||||
|
metadata.replyIndex ===
|
||||||
|
metadata.parentMetadata.repliesSeenCount - 1
|
||||||
|
metadata.isLastChild =
|
||||||
|
metadata.nextItemDepth === undefined ||
|
||||||
|
metadata.nextItemDepth <= metadata.depth
|
||||||
|
|
||||||
if (metadata) {
|
/*
|
||||||
if (metadata.parentMetadata) {
|
* If this is the last sibling, it's implicitly part of the last
|
||||||
/*
|
* branch of this sub-tree.
|
||||||
* Track what's before/after now that we've applied moderation
|
*/
|
||||||
*/
|
if (metadata.isLastSibling) {
|
||||||
if (prevItem?.type === 'threadPost')
|
metadata.isPartOfLastBranchFromDepth = metadata.depth
|
||||||
metadata.prevItemDepth = prevItem?.depth
|
|
||||||
if (nextItem?.type === 'threadPost')
|
|
||||||
metadata.nextItemDepth = nextItem?.depth
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* We can now officially calculate `isLastSibling` and `isLastChild`
|
* If the parent is part of the last branch of the sub-tree, so is the child.
|
||||||
* based on the actual data that we've seen.
|
*/
|
||||||
*/
|
if (metadata.parentMetadata.isPartOfLastBranchFromDepth) {
|
||||||
metadata.isLastSibling =
|
metadata.isPartOfLastBranchFromDepth =
|
||||||
metadata.replyIndex === metadata.parentMetadata.repliesSeenCount - 1
|
metadata.parentMetadata.isPartOfLastBranchFromDepth
|
||||||
metadata.isLastChild =
|
}
|
||||||
metadata.nextItemDepth === undefined ||
|
}
|
||||||
metadata.nextItemDepth <= metadata.depth
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If this is the last sibling, it's implicitly part of the last
|
* If this is the last sibling, and the parent has unhydrated replies,
|
||||||
* branch of this sub-tree.
|
* at some point down the line we will need to show a "read more".
|
||||||
*/
|
*/
|
||||||
if (metadata.isLastSibling) {
|
if (
|
||||||
metadata.isPartOfLastBranchFromDepth = metadata.depth
|
metadata.parentMetadata.repliesUnhydrated > 0 &&
|
||||||
|
metadata.isLastSibling
|
||||||
|
) {
|
||||||
|
metadata.upcomingParentReadMore = metadata.parentMetadata
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy in the parent's upcoming read more, if it exists. Once we
|
||||||
|
* reach the bottom, we'll insert a "read more"
|
||||||
|
*/
|
||||||
|
if (metadata.parentMetadata.upcomingParentReadMore) {
|
||||||
|
metadata.upcomingParentReadMore =
|
||||||
|
metadata.parentMetadata.upcomingParentReadMore
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy in the parent's skipped indents
|
||||||
|
*/
|
||||||
|
metadata.skippedIndentIndices = new Set([
|
||||||
|
...metadata.parentMetadata.skippedIndentIndices,
|
||||||
|
])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the parent is part of the last branch of the sub-tree, so is the child.
|
* If this is the last sibling, and the parent has no unhydrated
|
||||||
|
* replies, then we know we can skip an indent line.
|
||||||
*/
|
*/
|
||||||
if (metadata.parentMetadata.isPartOfLastBranchFromDepth) {
|
if (
|
||||||
metadata.isPartOfLastBranchFromDepth =
|
metadata.parentMetadata.repliesUnhydrated <= 0 &&
|
||||||
metadata.parentMetadata.isPartOfLastBranchFromDepth
|
metadata.isLastSibling
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* Depth is 2 more than the 0-index of the indent calculation
|
||||||
|
* bc of how we render these. So instead of handling that in the
|
||||||
|
* component, we just adjust that back to 0-index here.
|
||||||
|
*/
|
||||||
|
metadata.skippedIndentIndices.add(item.depth - 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If this is the last sibling, and the parent has unhydrated replies,
|
* If this post has unhydrated replies, and it is the last child, then
|
||||||
* at some point down the line we will need to show a "read more".
|
* it itself needs a "read more"
|
||||||
*/
|
*/
|
||||||
if (
|
if (metadata.repliesUnhydrated > 0 && metadata.isLastChild) {
|
||||||
metadata.parentMetadata.repliesUnhydrated > 0 &&
|
metadata.precedesChildReadMore = true
|
||||||
metadata.isLastSibling
|
subset.splice(i + 1, 0, views.readMore(metadata))
|
||||||
) {
|
i++ // skip next iteration
|
||||||
metadata.upcomingParentReadMore = metadata.parentMetadata
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy in the parent's upcoming read more, if it exists. Once we
|
* Tree-view only.
|
||||||
* reach the bottom, we'll insert a "read more"
|
*
|
||||||
|
* If there's an upcoming parent read more, this branch is part of the
|
||||||
|
* last branch of the sub-tree, and the item itself is the last child,
|
||||||
|
* insert the parent "read more".
|
||||||
*/
|
*/
|
||||||
if (metadata.parentMetadata.upcomingParentReadMore) {
|
if (
|
||||||
metadata.upcomingParentReadMore =
|
view === 'tree' &&
|
||||||
metadata.parentMetadata.upcomingParentReadMore
|
metadata.upcomingParentReadMore &&
|
||||||
|
metadata.isPartOfLastBranchFromDepth ===
|
||||||
|
metadata.upcomingParentReadMore.depth &&
|
||||||
|
metadata.isLastChild
|
||||||
|
) {
|
||||||
|
subset.splice(
|
||||||
|
i + 1,
|
||||||
|
0,
|
||||||
|
views.readMore(metadata.upcomingParentReadMore),
|
||||||
|
)
|
||||||
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy in the parent's skipped indents
|
* Calculate the final UI state for the thread item.
|
||||||
*/
|
*/
|
||||||
metadata.skippedIndentIndices = new Set([
|
item.ui = getThreadPostUI(metadata)
|
||||||
...metadata.parentMetadata.skippedIndentIndices,
|
|
||||||
])
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If this is the last sibling, and the parent has no unhydrated
|
|
||||||
* replies, then we know we can skip an indent line.
|
|
||||||
*/
|
|
||||||
if (
|
|
||||||
metadata.parentMetadata.repliesUnhydrated <= 0 &&
|
|
||||||
metadata.isLastSibling
|
|
||||||
) {
|
|
||||||
/**
|
|
||||||
* Depth is 2 more than the 0-index of the indent calculation
|
|
||||||
* bc of how we render these. So instead of handling that in the
|
|
||||||
* component, we just adjust that back to 0-index here.
|
|
||||||
*/
|
|
||||||
metadata.skippedIndentIndices.add(item.depth - 2)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* If this post has unhydrated replies, and it is the last child, then
|
|
||||||
* it itself needs a "read more"
|
|
||||||
*/
|
|
||||||
if (metadata.repliesUnhydrated > 0 && metadata.isLastChild) {
|
|
||||||
metadata.precedesChildReadMore = true
|
|
||||||
items.splice(i + 1, 0, views.readMore(metadata))
|
|
||||||
i++ // skip next iteration
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Tree-view only.
|
|
||||||
*
|
|
||||||
* If there's an upcoming parent read more, this branch is part of the
|
|
||||||
* last branch of the sub-tree, and the item itself is the last child,
|
|
||||||
* insert the parent "read more".
|
|
||||||
*/
|
|
||||||
if (
|
|
||||||
view === 'tree' &&
|
|
||||||
metadata.upcomingParentReadMore &&
|
|
||||||
metadata.isPartOfLastBranchFromDepth ===
|
|
||||||
metadata.upcomingParentReadMore.depth &&
|
|
||||||
metadata.isLastChild
|
|
||||||
) {
|
|
||||||
items.splice(
|
|
||||||
i + 1,
|
|
||||||
0,
|
|
||||||
views.readMore(metadata.upcomingParentReadMore),
|
|
||||||
)
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Calculate the final UI state for the thread item.
|
|
||||||
*/
|
|
||||||
item.ui = getThreadPostUI(metadata)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
items,
|
||||||
|
hidden,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function combine(
|
||||||
|
{items, hidden}: {items: ThreadItem[]; hidden: ThreadItem[]},
|
||||||
|
{
|
||||||
|
hasSession,
|
||||||
|
hiddenRepliesVisible,
|
||||||
|
hasServerHiddenReplies,
|
||||||
|
loadHiddenReplies,
|
||||||
|
}: {
|
||||||
|
hasSession: boolean
|
||||||
|
hiddenRepliesVisible: boolean
|
||||||
|
hasServerHiddenReplies: boolean
|
||||||
|
loadHiddenReplies: () => Promise<void>
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
const item = items[i]
|
||||||
|
if (
|
||||||
|
item.type === 'threadPost' &&
|
||||||
|
item.depth === 0 &&
|
||||||
|
!item.value.post.viewer?.replyDisabled &&
|
||||||
|
hasSession
|
||||||
|
) {
|
||||||
|
items.splice(i + 1, 0, {
|
||||||
|
type: 'replyComposer',
|
||||||
|
key: 'replyComposer',
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hidden.length || hasServerHiddenReplies) {
|
||||||
|
if (hiddenRepliesVisible) {
|
||||||
|
return items.concat(hidden)
|
||||||
|
} else {
|
||||||
|
return items.concat({
|
||||||
|
type: 'showHiddenReplies',
|
||||||
|
key: 'showHiddenReplies',
|
||||||
|
onLoad: loadHiddenReplies,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export type ThreadItem =
|
|||||||
record: AppBskyFeedPost.Record
|
record: AppBskyFeedPost.Record
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
isBlurred: boolean
|
||||||
moderation: ModerationDecision
|
moderation: ModerationDecision
|
||||||
ui: {
|
ui: {
|
||||||
isAnchor: boolean
|
isAnchor: boolean
|
||||||
|
|||||||
@@ -63,12 +63,20 @@ export function threadPost({
|
|||||||
depth,
|
depth,
|
||||||
value,
|
value,
|
||||||
moderationOpts,
|
moderationOpts,
|
||||||
|
threadgateHiddenReplies,
|
||||||
}: {
|
}: {
|
||||||
uri: string
|
uri: string
|
||||||
depth: number
|
depth: number
|
||||||
value: $Typed<AppBskyUnspeccedDefs.ThreadItemPost>
|
value: $Typed<AppBskyUnspeccedDefs.ThreadItemPost>
|
||||||
moderationOpts: ModerationOpts
|
moderationOpts: ModerationOpts
|
||||||
|
threadgateHiddenReplies: Set<string>
|
||||||
}): Extract<ThreadItem, {type: 'threadPost'}> {
|
}): Extract<ThreadItem, {type: 'threadPost'}> {
|
||||||
|
const moderation = moderatePost(value.post, moderationOpts)
|
||||||
|
const modui = moderation.ui('contentList')
|
||||||
|
const blurred = modui.blur || modui.filter
|
||||||
|
const muted = (modui.blurs[0] || modui.filters[0])?.type === 'muted'
|
||||||
|
const hiddenByThreadgate = threadgateHiddenReplies.has(uri)
|
||||||
|
const isBlurred = hiddenByThreadgate || blurred || muted
|
||||||
return {
|
return {
|
||||||
type: 'threadPost',
|
type: 'threadPost',
|
||||||
key: uri,
|
key: uri,
|
||||||
@@ -84,7 +92,8 @@ export function threadPost({
|
|||||||
record: AppBskyFeedPost.Record
|
record: AppBskyFeedPost.Record
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
moderation: moderatePost(value.post, moderationOpts),
|
isBlurred,
|
||||||
|
moderation,
|
||||||
// @ts-ignore populated by the traversal
|
// @ts-ignore populated by the traversal
|
||||||
ui: {},
|
ui: {},
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user