Clean up
This commit is contained in:
@@ -227,7 +227,7 @@ export function Inner({uri}: {uri: string | undefined}) {
|
||||
item={item}
|
||||
threadgateRecord={thread.data.threadgate?.record ?? undefined}
|
||||
overrides={{
|
||||
moderation: thread.state.hiddenRepliesVisible && item.depth > 0,
|
||||
moderation: thread.state.hiddenItemsVisible && item.depth > 0,
|
||||
}}
|
||||
onPostSuccess={optimisticOnPostReply}
|
||||
/>
|
||||
@@ -238,7 +238,7 @@ export function Inner({uri}: {uri: string | undefined}) {
|
||||
item={item}
|
||||
threadgateRecord={thread.data.threadgate?.record ?? undefined}
|
||||
overrides={{
|
||||
moderation: thread.state.hiddenRepliesVisible && item.depth > 0,
|
||||
moderation: thread.state.hiddenItemsVisible && item.depth > 0,
|
||||
}}
|
||||
onPostSuccess={optimisticOnPostReply}
|
||||
/>
|
||||
|
||||
@@ -8,11 +8,12 @@ import {
|
||||
createCacheMutator,
|
||||
getThreadPlaceholder,
|
||||
} from '#/state/queries/usePostThread/queryCache'
|
||||
import {combine,traverse} from '#/state/queries/usePostThread/traversal'
|
||||
import {combine, traverse} from '#/state/queries/usePostThread/traversal'
|
||||
import {
|
||||
createPostThreadHiddenQueryKey,
|
||||
createPostThreadQueryKey,
|
||||
type ThreadItem,
|
||||
type UsePostThreadQueryResult,
|
||||
} from '#/state/queries/usePostThread/types'
|
||||
import {getThreadgateRecord} from '#/state/queries/usePostThread/utils'
|
||||
import * as views from '#/state/queries/usePostThread/views'
|
||||
@@ -35,6 +36,7 @@ export function usePostThread({anchor}: {anchor?: string}) {
|
||||
setView: baseSetView,
|
||||
prioritizeFollowedUsers,
|
||||
} = useThreadPreferences()
|
||||
|
||||
const postThreadQueryKey = createPostThreadQueryKey({
|
||||
anchor,
|
||||
sort,
|
||||
@@ -42,7 +44,7 @@ export function usePostThread({anchor}: {anchor?: string}) {
|
||||
prioritizeFollowedUsers,
|
||||
})
|
||||
|
||||
const query = useQuery({
|
||||
const query = useQuery<UsePostThreadQueryResult>({
|
||||
enabled: isThreadPreferencesLoaded && !!anchor && !!moderationOpts,
|
||||
queryKey: postThreadQueryKey,
|
||||
// gcTime: 0, // TODO faster if we let it cache
|
||||
@@ -73,10 +75,18 @@ export function usePostThread({anchor}: {anchor?: string}) {
|
||||
ctx.meta.hasHiddenReplies = true
|
||||
}
|
||||
|
||||
return {
|
||||
...data,
|
||||
const result = {
|
||||
thread: data.thread || [],
|
||||
threadgate: data.threadgate,
|
||||
hasHiddenReplies: !!ctx.meta.hasHiddenReplies,
|
||||
}
|
||||
|
||||
const record = getThreadgateRecord(result.threadgate)
|
||||
if (result.threadgate && record) {
|
||||
result.threadgate.record = record
|
||||
}
|
||||
|
||||
return result as UsePostThreadQueryResult
|
||||
},
|
||||
placeholderData() {
|
||||
if (!anchor) return
|
||||
@@ -90,39 +100,86 @@ export function usePostThread({anchor}: {anchor?: string}) {
|
||||
return {thread, threadgate: undefined, hasHiddenReplies: false}
|
||||
},
|
||||
select(data) {
|
||||
const threadgate = getThreadgateRecord(data.threadgate)
|
||||
return {
|
||||
...data,
|
||||
threadgate: {
|
||||
...data.threadgate,
|
||||
record: threadgate,
|
||||
},
|
||||
const record = getThreadgateRecord(data.threadgate)
|
||||
if (data.threadgate && record) {
|
||||
data.threadgate.record = record
|
||||
}
|
||||
return data
|
||||
},
|
||||
})
|
||||
|
||||
const hasServerHiddenReplies = !!query.data?.hasHiddenReplies
|
||||
const [hiddenRepliesVisible, setHiddenRepliesVisible] = useState(false)
|
||||
const thread = useMemo(() => query.data?.thread || [], [query.data?.thread])
|
||||
const threadgate = useMemo(
|
||||
() => query.data?.threadgate,
|
||||
[query.data?.threadgate],
|
||||
)
|
||||
const hasServerHiddenItems = useMemo(
|
||||
() => !!query.data?.hasHiddenReplies,
|
||||
[query.data?.hasHiddenReplies],
|
||||
)
|
||||
|
||||
const [hiddenItemsVisible, setHiddenItemsVisible] = useState(false)
|
||||
const [additionalHiddenItems, setAdditionalHiddenItems] = useState<
|
||||
ThreadItem[]
|
||||
>([])
|
||||
|
||||
/**
|
||||
* Sets the sort order for the thread and resets the hidden items
|
||||
*/
|
||||
const setSort: typeof baseSetSort = useCallback(
|
||||
nextSort => {
|
||||
setHiddenItemsVisible(false)
|
||||
setAdditionalHiddenItems([])
|
||||
baseSetSort(nextSort)
|
||||
},
|
||||
[baseSetSort, setAdditionalHiddenItems, setHiddenItemsVisible],
|
||||
)
|
||||
|
||||
/**
|
||||
* Sets the view variant for the thread and resets the hidden items
|
||||
*/
|
||||
const setView: typeof baseSetView = useCallback(
|
||||
nextView => {
|
||||
setHiddenItemsVisible(false)
|
||||
setAdditionalHiddenItems([])
|
||||
baseSetView(nextView)
|
||||
},
|
||||
[baseSetView, setAdditionalHiddenItems, setHiddenItemsVisible],
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates a mutator for the post thread cache. This is used to insert
|
||||
* replies into the thread cache after posting.
|
||||
*/
|
||||
const mutator = useMemo(
|
||||
() =>
|
||||
createCacheMutator({
|
||||
params: {
|
||||
sort,
|
||||
view,
|
||||
},
|
||||
queryKey: postThreadQueryKey,
|
||||
queryClient: qc,
|
||||
}),
|
||||
[qc, sort, view, postThreadQueryKey],
|
||||
)
|
||||
|
||||
/**
|
||||
* Loads hidden replies for this thread. Any replies that are moderated from
|
||||
* the initial visible response(s) are shown immediately. Remote data is
|
||||
* fetched and inserted when it's available.
|
||||
*/
|
||||
const loadHiddenReplies = useCallback(async () => {
|
||||
const loadServerHiddenItems = useCallback(async () => {
|
||||
/*
|
||||
* Show any moderated replies already in memory that were handled here on
|
||||
* the client. If there are server-hidden replies, we'll fetch those next.
|
||||
*/
|
||||
setHiddenRepliesVisible(true)
|
||||
setHiddenItemsVisible(true)
|
||||
|
||||
/*
|
||||
* If there are no server hidden replies, just stop here.
|
||||
*/
|
||||
if (!hasServerHiddenReplies) return
|
||||
if (!hasServerHiddenItems) return
|
||||
|
||||
setAdditionalHiddenItems(
|
||||
Array.from({length: 2}).map((_, i) =>
|
||||
@@ -153,9 +210,7 @@ export function usePostThread({anchor}: {anchor?: string}) {
|
||||
const {items} = traverse(data || [], {
|
||||
view,
|
||||
skipHiddenReplyHandling: true,
|
||||
threadgateHiddenReplies: mergeThreadgateHiddenReplies(
|
||||
query.data?.threadgate?.record,
|
||||
),
|
||||
threadgateHiddenReplies: mergeThreadgateHiddenReplies(threadgate?.record),
|
||||
moderationOpts: moderationOpts!,
|
||||
})
|
||||
|
||||
@@ -169,123 +224,118 @@ export function usePostThread({anchor}: {anchor?: string}) {
|
||||
prioritizeFollowedUsers,
|
||||
mergeThreadgateHiddenReplies,
|
||||
moderationOpts,
|
||||
query.data?.threadgate?.record,
|
||||
hasServerHiddenReplies,
|
||||
setHiddenRepliesVisible,
|
||||
threadgate?.record,
|
||||
hasServerHiddenItems,
|
||||
setHiddenItemsVisible,
|
||||
])
|
||||
|
||||
const combined = useMemo(() => {
|
||||
const traversal = traverse(query.data?.thread || [], {
|
||||
/**
|
||||
* Builds the full set of thread items, minus any server-hidden replies.
|
||||
*/
|
||||
const threadItems = useMemo(() => {
|
||||
const traversal = traverse(thread, {
|
||||
view: view,
|
||||
threadgateHiddenReplies: mergeThreadgateHiddenReplies(
|
||||
query.data?.threadgate?.record,
|
||||
),
|
||||
threadgateHiddenReplies: mergeThreadgateHiddenReplies(threadgate?.record),
|
||||
moderationOpts: moderationOpts!,
|
||||
})
|
||||
return combine(traversal, {
|
||||
hasSession,
|
||||
hasServerHiddenReplies,
|
||||
hiddenRepliesVisible,
|
||||
loadHiddenReplies,
|
||||
hasServerHiddenItems,
|
||||
hiddenItemsVisible,
|
||||
loadServerHiddenItems,
|
||||
})
|
||||
}, [
|
||||
query.data,
|
||||
thread,
|
||||
threadgate?.record,
|
||||
mergeThreadgateHiddenReplies,
|
||||
moderationOpts,
|
||||
hasSession,
|
||||
view,
|
||||
hasServerHiddenReplies,
|
||||
hiddenRepliesVisible,
|
||||
loadHiddenReplies,
|
||||
hasServerHiddenItems,
|
||||
hiddenItemsVisible,
|
||||
loadServerHiddenItems,
|
||||
])
|
||||
|
||||
/**
|
||||
* Computes the final thread items based on load state and the availability
|
||||
* of server-hidden replies.
|
||||
*/
|
||||
const items = useMemo(() => {
|
||||
return combined.concat(additionalHiddenItems)
|
||||
}, [combined, additionalHiddenItems])
|
||||
if (query.isPlaceholderData) {
|
||||
const anchorPost = threadItems.at(0)
|
||||
const skeletonReplies =
|
||||
anchorPost && anchorPost.type === 'threadPost'
|
||||
? anchorPost?.value.post.replyCount ?? 4
|
||||
: 4
|
||||
|
||||
if (query.isPlaceholderData) {
|
||||
const anchorPost = items.at(0)
|
||||
const skeletonReplies =
|
||||
anchorPost && anchorPost.type === 'threadPost'
|
||||
? anchorPost?.value.post.replyCount ?? 4
|
||||
: 4
|
||||
|
||||
if (!items.length) {
|
||||
items.push(
|
||||
views.skeleton({
|
||||
key: anchor!,
|
||||
item: 'anchor',
|
||||
}),
|
||||
)
|
||||
|
||||
if (hasSession) {
|
||||
items.push(
|
||||
if (!threadItems.length) {
|
||||
threadItems.push(
|
||||
views.skeleton({
|
||||
key: 'replyComposer',
|
||||
item: 'replyComposer',
|
||||
key: anchor!,
|
||||
item: 'anchor',
|
||||
}),
|
||||
)
|
||||
|
||||
if (hasSession) {
|
||||
threadItems.push(
|
||||
views.skeleton({
|
||||
key: 'replyComposer',
|
||||
item: 'replyComposer',
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < skeletonReplies; i++) {
|
||||
threadItems.push(
|
||||
views.skeleton({
|
||||
key: `${anchor!}-reply-${i}`,
|
||||
item: 'reply',
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
return threadItems
|
||||
} else {
|
||||
return threadItems.concat(additionalHiddenItems)
|
||||
}
|
||||
|
||||
for (let i = 0; i < skeletonReplies; i++) {
|
||||
items.push(
|
||||
views.skeleton({
|
||||
key: `${anchor!}-reply-${i}`,
|
||||
item: 'reply',
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const mutator = useMemo(
|
||||
() =>
|
||||
createCacheMutator({
|
||||
params: {
|
||||
sort,
|
||||
view,
|
||||
},
|
||||
queryKey: postThreadQueryKey,
|
||||
queryClient: qc,
|
||||
}),
|
||||
[qc, sort, view, postThreadQueryKey],
|
||||
)
|
||||
|
||||
const setSort: typeof baseSetSort = useCallback(
|
||||
nextSort => {
|
||||
setHiddenRepliesVisible(false)
|
||||
setAdditionalHiddenItems([])
|
||||
baseSetSort(nextSort)
|
||||
},
|
||||
[baseSetSort, setAdditionalHiddenItems, setHiddenRepliesVisible],
|
||||
)
|
||||
|
||||
const setView: typeof baseSetView = useCallback(
|
||||
nextView => {
|
||||
setHiddenRepliesVisible(false)
|
||||
setAdditionalHiddenItems([])
|
||||
baseSetView(nextView)
|
||||
},
|
||||
[baseSetView, setAdditionalHiddenItems, setHiddenRepliesVisible],
|
||||
)
|
||||
}, [
|
||||
query.isPlaceholderData,
|
||||
threadItems,
|
||||
additionalHiddenItems,
|
||||
anchor,
|
||||
hasSession,
|
||||
])
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
state: {
|
||||
/*
|
||||
* Copy in any query state that is useful
|
||||
*/
|
||||
isFetching: query.isFetching,
|
||||
isPlaceholderData: query.isPlaceholderData,
|
||||
error: query.error,
|
||||
hiddenRepliesVisible,
|
||||
/*
|
||||
* Other state
|
||||
*/
|
||||
sort,
|
||||
view,
|
||||
hiddenItemsVisible,
|
||||
},
|
||||
data: {
|
||||
items: items || [],
|
||||
threadgate: query.data?.threadgate,
|
||||
items,
|
||||
threadgate,
|
||||
},
|
||||
actions: {
|
||||
/*
|
||||
* Copy in any query actions that are useful
|
||||
*/
|
||||
insertReplies: mutator.insertReplies,
|
||||
refetch: query.refetch,
|
||||
/*
|
||||
* Other actions
|
||||
*/
|
||||
setSort,
|
||||
setView,
|
||||
},
|
||||
@@ -294,11 +344,12 @@ export function usePostThread({anchor}: {anchor?: string}) {
|
||||
query,
|
||||
items,
|
||||
mutator.insertReplies,
|
||||
hiddenRepliesVisible,
|
||||
hiddenItemsVisible,
|
||||
sort,
|
||||
view,
|
||||
setSort,
|
||||
setView,
|
||||
threadgate,
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import {
|
||||
AppBskyUnspeccedDefs,
|
||||
type ModerationDecision,
|
||||
type ModerationOpts,
|
||||
} from '@atproto/api'
|
||||
import {AppBskyUnspeccedDefs, type ModerationOpts} from '@atproto/api'
|
||||
|
||||
import {
|
||||
type ApiThreadItem,
|
||||
@@ -376,14 +372,14 @@ export function combine(
|
||||
{items, hidden}: {items: ThreadItem[]; hidden: ThreadItem[]},
|
||||
{
|
||||
hasSession,
|
||||
hiddenRepliesVisible,
|
||||
hasServerHiddenReplies,
|
||||
loadHiddenReplies,
|
||||
hiddenItemsVisible,
|
||||
hasServerHiddenItems,
|
||||
loadServerHiddenItems,
|
||||
}: {
|
||||
hasSession: boolean
|
||||
hiddenRepliesVisible: boolean
|
||||
hasServerHiddenReplies: boolean
|
||||
loadHiddenReplies: () => Promise<void>
|
||||
hiddenItemsVisible: boolean
|
||||
hasServerHiddenItems: boolean
|
||||
loadServerHiddenItems: () => Promise<void>
|
||||
},
|
||||
) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
@@ -402,14 +398,14 @@ export function combine(
|
||||
}
|
||||
}
|
||||
|
||||
if (hidden.length || hasServerHiddenReplies) {
|
||||
if (hiddenRepliesVisible) {
|
||||
if (hidden.length || hasServerHiddenItems) {
|
||||
if (hiddenItemsVisible) {
|
||||
return items.concat(hidden)
|
||||
} else {
|
||||
return items.concat({
|
||||
type: 'showHiddenReplies',
|
||||
key: 'showHiddenReplies',
|
||||
onLoad: loadHiddenReplies,
|
||||
onLoad: loadServerHiddenItems,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -456,14 +452,3 @@ export function getBranch(
|
||||
length: end - branchStartIndex,
|
||||
}
|
||||
}
|
||||
|
||||
export function getModerationState(moderation: ModerationDecision) {
|
||||
const modui = moderation.ui('contentList')
|
||||
const blurred = modui.blur || modui.filter
|
||||
const muted = (modui.blurs[0] || modui.filters[0])?.type === 'muted'
|
||||
return {
|
||||
blurred,
|
||||
muted,
|
||||
modui,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
type AppBskyFeedDefs,
|
||||
type AppBskyFeedPost,
|
||||
type AppBskyFeedThreadgate,
|
||||
type AppBskyUnspeccedDefs,
|
||||
type AppBskyUnspeccedGetPostThreadHiddenV2,
|
||||
type AppBskyUnspeccedGetPostThreadV2,
|
||||
@@ -29,6 +30,14 @@ export type PostThreadParams = Pick<
|
||||
view: 'tree' | 'linear'
|
||||
}
|
||||
|
||||
export type UsePostThreadQueryResult = {
|
||||
hasHiddenReplies: boolean
|
||||
thread: AppBskyUnspeccedGetPostThreadV2.ThreadItem[]
|
||||
threadgate?: Omit<AppBskyFeedDefs.ThreadgateView, 'record'> & {
|
||||
record: AppBskyFeedThreadgate.Record
|
||||
}
|
||||
}
|
||||
|
||||
export type ThreadItem =
|
||||
| {
|
||||
type: 'threadPost'
|
||||
|
||||
Reference in New Issue
Block a user