Handle optimistic insert in hidden replies
This commit is contained in:
@@ -231,7 +231,8 @@ export function Inner({uri}: {uri: string | undefined}) {
|
|||||||
item={item}
|
item={item}
|
||||||
threadgateRecord={thread.data.threadgate?.record ?? undefined}
|
threadgateRecord={thread.data.threadgate?.record ?? undefined}
|
||||||
overrides={{
|
overrides={{
|
||||||
moderation: thread.state.hiddenItemsVisible && item.depth > 0,
|
moderation:
|
||||||
|
thread.state.hiddenThreadItemsVisible && item.depth > 0,
|
||||||
}}
|
}}
|
||||||
onPostSuccess={optimisticOnPostReply}
|
onPostSuccess={optimisticOnPostReply}
|
||||||
/>
|
/>
|
||||||
@@ -242,7 +243,8 @@ export function Inner({uri}: {uri: string | undefined}) {
|
|||||||
item={item}
|
item={item}
|
||||||
threadgateRecord={thread.data.threadgate?.record ?? undefined}
|
threadgateRecord={thread.data.threadgate?.record ?? undefined}
|
||||||
overrides={{
|
overrides={{
|
||||||
moderation: thread.state.hiddenItemsVisible && item.depth > 0,
|
moderation:
|
||||||
|
thread.state.hiddenThreadItemsVisible && item.depth > 0,
|
||||||
}}
|
}}
|
||||||
onPostSuccess={optimisticOnPostReply}
|
onPostSuccess={optimisticOnPostReply}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -47,11 +47,14 @@ export function usePostThread({anchor}: {anchor?: string}) {
|
|||||||
view,
|
view,
|
||||||
prioritizeFollowedUsers,
|
prioritizeFollowedUsers,
|
||||||
})
|
})
|
||||||
|
const postThreadHiddenQueryKey = createPostThreadHiddenQueryKey({
|
||||||
|
anchor,
|
||||||
|
prioritizeFollowedUsers,
|
||||||
|
})
|
||||||
|
|
||||||
const query = useQuery<UsePostThreadQueryResult>({
|
const query = useQuery<UsePostThreadQueryResult>({
|
||||||
enabled: isThreadPreferencesLoaded && !!anchor && !!moderationOpts,
|
enabled: isThreadPreferencesLoaded && !!anchor && !!moderationOpts,
|
||||||
queryKey: postThreadQueryKey,
|
queryKey: postThreadQueryKey,
|
||||||
// gcTime: 0, // TODO faster if we let it cache
|
|
||||||
async queryFn(ctx) {
|
async queryFn(ctx) {
|
||||||
const {data} = await wait(
|
const {data} = await wait(
|
||||||
400,
|
400,
|
||||||
@@ -117,39 +120,12 @@ export function usePostThread({anchor}: {anchor?: string}) {
|
|||||||
() => query.data?.threadgate,
|
() => query.data?.threadgate,
|
||||||
[query.data?.threadgate],
|
[query.data?.threadgate],
|
||||||
)
|
)
|
||||||
const hasServerHiddenItems = useMemo(
|
const hasServerHiddenThreadItems = useMemo(
|
||||||
() => !!query.data?.hasHiddenReplies,
|
() => !!query.data?.hasHiddenReplies,
|
||||||
[query.data?.hasHiddenReplies],
|
[query.data?.hasHiddenReplies],
|
||||||
)
|
)
|
||||||
|
const [hiddenThreadItemsVisible, setHiddenThreadItemsVisible] =
|
||||||
const [hiddenItemsVisible, setHiddenItemsVisible] = useState(false)
|
useState(false)
|
||||||
const [serverHiddenThreadItems, setServerHiddenThreadItems] = useState<
|
|
||||||
ThreadItem[]
|
|
||||||
>([])
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the sort order for the thread and resets the hidden items
|
|
||||||
*/
|
|
||||||
const setSort: typeof baseSetSort = useCallback(
|
|
||||||
nextSort => {
|
|
||||||
setHiddenItemsVisible(false)
|
|
||||||
setServerHiddenThreadItems([])
|
|
||||||
baseSetSort(nextSort)
|
|
||||||
},
|
|
||||||
[baseSetSort, setServerHiddenThreadItems, setHiddenItemsVisible],
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the view variant for the thread and resets the hidden items
|
|
||||||
*/
|
|
||||||
const setView: typeof baseSetView = useCallback(
|
|
||||||
nextView => {
|
|
||||||
setHiddenItemsVisible(false)
|
|
||||||
setServerHiddenThreadItems([])
|
|
||||||
baseSetView(nextView)
|
|
||||||
},
|
|
||||||
[baseSetView, setServerHiddenThreadItems, setHiddenItemsVisible],
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a mutator for the post thread cache. This is used to insert
|
* Creates a mutator for the post thread cache. This is used to insert
|
||||||
@@ -158,81 +134,93 @@ export function usePostThread({anchor}: {anchor?: string}) {
|
|||||||
const mutator = useMemo(
|
const mutator = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createCacheMutator({
|
createCacheMutator({
|
||||||
params: {
|
params: {view},
|
||||||
sort,
|
postThreadQueryKey,
|
||||||
view,
|
postThreadHiddenQueryKey,
|
||||||
},
|
|
||||||
queryKey: postThreadQueryKey,
|
|
||||||
queryClient: qc,
|
queryClient: qc,
|
||||||
}),
|
}),
|
||||||
[qc, sort, view, postThreadQueryKey],
|
[qc, view, postThreadQueryKey, postThreadHiddenQueryKey],
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads hidden replies for this thread. Any replies that are moderated from
|
* If we have server-hidden items and the user has chosen to view them,
|
||||||
* the initial visible response(s) are shown immediately. Remote data is
|
* start loading data
|
||||||
* fetched and inserted when it's available.
|
|
||||||
*/
|
*/
|
||||||
const loadServerHiddenThreadItems = useCallback(async () => {
|
const hiddenQueryEnabled =
|
||||||
/*
|
hasServerHiddenThreadItems && hiddenThreadItemsVisible
|
||||||
* Show any moderated replies already in memory that were handled here on
|
const hiddenQuery = useQuery({
|
||||||
* the client. If there are server-hidden replies, we'll fetch those next.
|
enabled: hiddenQueryEnabled,
|
||||||
*/
|
queryKey: postThreadHiddenQueryKey,
|
||||||
setHiddenItemsVisible(true)
|
async queryFn() {
|
||||||
|
const {data} = await wait(
|
||||||
/*
|
400,
|
||||||
* If there are no server hidden replies, just stop here.
|
agent.app.bsky.unspecced.getPostThreadHiddenV2({
|
||||||
*/
|
anchor: anchor!,
|
||||||
if (!hasServerHiddenItems) return
|
prioritizeFollowedUsers,
|
||||||
|
}),
|
||||||
setServerHiddenThreadItems(
|
)
|
||||||
Array.from({length: 2}).map((_, i) =>
|
return data
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const serverHiddenThreadItems: ThreadItem[] = useMemo(() => {
|
||||||
|
if (!hiddenQueryEnabled) return []
|
||||||
|
if (hiddenQuery.isLoading) {
|
||||||
|
return Array.from({length: 2}).map((_, i) =>
|
||||||
views.skeleton({
|
views.skeleton({
|
||||||
key: `${anchor!}-reply-${i}`,
|
key: `hidden-reply-${i}`,
|
||||||
item: 'reply',
|
item: 'reply',
|
||||||
}),
|
}),
|
||||||
),
|
)
|
||||||
)
|
} else if (hiddenQuery.isError) {
|
||||||
|
// TODO could insert error component
|
||||||
const params = {
|
return []
|
||||||
anchor: anchor!,
|
} else if (hiddenQuery.data?.thread) {
|
||||||
prioritizeFollowedUsers,
|
const {threadItems} = sortAndAnnotateThreadItems(
|
||||||
}
|
hiddenQuery.data.thread,
|
||||||
const data = await wait(
|
{
|
||||||
400,
|
view,
|
||||||
qc.fetchQuery({
|
skipHiddenReplyHandling: true,
|
||||||
queryKey: createPostThreadHiddenQueryKey(params),
|
threadgateHiddenReplies: mergeThreadgateHiddenReplies(
|
||||||
async queryFn() {
|
threadgate?.record,
|
||||||
const {data} = await agent.app.bsky.unspecced.getPostThreadHiddenV2(
|
),
|
||||||
params,
|
moderationOpts: moderationOpts!,
|
||||||
)
|
|
||||||
return data.thread || []
|
|
||||||
},
|
},
|
||||||
}),
|
)
|
||||||
)
|
return threadItems
|
||||||
|
} else {
|
||||||
const {threadItems} = sortAndAnnotateThreadItems(data || [], {
|
return []
|
||||||
view,
|
}
|
||||||
skipHiddenReplyHandling: true,
|
|
||||||
threadgateHiddenReplies: mergeThreadgateHiddenReplies(threadgate?.record),
|
|
||||||
moderationOpts: moderationOpts!,
|
|
||||||
})
|
|
||||||
|
|
||||||
// insert the hidden replies into the state
|
|
||||||
setServerHiddenThreadItems(threadItems)
|
|
||||||
}, [
|
}, [
|
||||||
qc,
|
|
||||||
agent,
|
|
||||||
view,
|
view,
|
||||||
anchor,
|
hiddenQueryEnabled,
|
||||||
prioritizeFollowedUsers,
|
hiddenQuery,
|
||||||
mergeThreadgateHiddenReplies,
|
mergeThreadgateHiddenReplies,
|
||||||
moderationOpts,
|
moderationOpts,
|
||||||
threadgate?.record,
|
threadgate?.record,
|
||||||
hasServerHiddenItems,
|
|
||||||
setHiddenItemsVisible,
|
|
||||||
])
|
])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the sort order for the thread and resets the hidden items
|
||||||
|
*/
|
||||||
|
const setSort: typeof baseSetSort = useCallback(
|
||||||
|
nextSort => {
|
||||||
|
setHiddenThreadItemsVisible(false)
|
||||||
|
baseSetSort(nextSort)
|
||||||
|
},
|
||||||
|
[baseSetSort, setHiddenThreadItemsVisible],
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the view variant for the thread and resets the hidden items
|
||||||
|
*/
|
||||||
|
const setView: typeof baseSetView = useCallback(
|
||||||
|
nextView => {
|
||||||
|
setHiddenThreadItemsVisible(false)
|
||||||
|
baseSetView(nextView)
|
||||||
|
},
|
||||||
|
[baseSetView, setHiddenThreadItemsVisible],
|
||||||
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is the main thread response, sorted into separate buckets based on
|
* This is the main thread response, sorted into separate buckets based on
|
||||||
* moderation, and annotated with all UI state needed for rendering.
|
* moderation, and annotated with all UI state needed for rendering.
|
||||||
@@ -263,9 +251,9 @@ export function usePostThread({anchor}: {anchor?: string}) {
|
|||||||
serverHiddenThreadItems,
|
serverHiddenThreadItems,
|
||||||
isLoading: query.isPlaceholderData,
|
isLoading: query.isPlaceholderData,
|
||||||
hasSession,
|
hasSession,
|
||||||
hasServerHiddenItems,
|
hasServerHiddenThreadItems,
|
||||||
hiddenItemsVisible,
|
hiddenThreadItemsVisible,
|
||||||
loadServerHiddenThreadItems,
|
showHiddenThreadItems: () => setHiddenThreadItemsVisible(true),
|
||||||
})
|
})
|
||||||
}, [
|
}, [
|
||||||
threadItems,
|
threadItems,
|
||||||
@@ -273,9 +261,9 @@ export function usePostThread({anchor}: {anchor?: string}) {
|
|||||||
serverHiddenThreadItems,
|
serverHiddenThreadItems,
|
||||||
query.isPlaceholderData,
|
query.isPlaceholderData,
|
||||||
hasSession,
|
hasSession,
|
||||||
hasServerHiddenItems,
|
hasServerHiddenThreadItems,
|
||||||
hiddenItemsVisible,
|
hiddenThreadItemsVisible,
|
||||||
loadServerHiddenThreadItems,
|
setHiddenThreadItemsVisible,
|
||||||
])
|
])
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
@@ -292,7 +280,7 @@ export function usePostThread({anchor}: {anchor?: string}) {
|
|||||||
*/
|
*/
|
||||||
sort,
|
sort,
|
||||||
view,
|
view,
|
||||||
hiddenItemsVisible,
|
hiddenThreadItemsVisible,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
items,
|
items,
|
||||||
@@ -314,7 +302,7 @@ export function usePostThread({anchor}: {anchor?: string}) {
|
|||||||
[
|
[
|
||||||
query,
|
query,
|
||||||
mutator.insertReplies,
|
mutator.insertReplies,
|
||||||
hiddenItemsVisible,
|
hiddenThreadItemsVisible,
|
||||||
sort,
|
sort,
|
||||||
view,
|
view,
|
||||||
setSort,
|
setSort,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import {
|
|||||||
type $Typed,
|
type $Typed,
|
||||||
type AppBskyFeedDefs,
|
type AppBskyFeedDefs,
|
||||||
AppBskyUnspeccedDefs,
|
AppBskyUnspeccedDefs,
|
||||||
|
type AppBskyUnspeccedGetPostThreadHiddenV2,
|
||||||
type AppBskyUnspeccedGetPostThreadV2,
|
type AppBskyUnspeccedGetPostThreadV2,
|
||||||
AtUri,
|
AtUri,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
@@ -15,6 +16,8 @@ import {findAllPostsInQueryData as findAllPostsInSearchQueryData} from '#/state/
|
|||||||
import {BELOW} from '#/state/queries/usePostThread/const'
|
import {BELOW} from '#/state/queries/usePostThread/const'
|
||||||
import {getBranch} from '#/state/queries/usePostThread/traversal'
|
import {getBranch} from '#/state/queries/usePostThread/traversal'
|
||||||
import {
|
import {
|
||||||
|
type ApiThreadItem,
|
||||||
|
type createPostThreadHiddenQueryKey,
|
||||||
type createPostThreadQueryKey,
|
type createPostThreadQueryKey,
|
||||||
type PostThreadParams,
|
type PostThreadParams,
|
||||||
postThreadQueryKeyRoot,
|
postThreadQueryKeyRoot,
|
||||||
@@ -26,96 +29,119 @@ import {embedViewRecordToPostView} from '#/state/queries/util'
|
|||||||
|
|
||||||
export function createCacheMutator({
|
export function createCacheMutator({
|
||||||
queryClient,
|
queryClient,
|
||||||
queryKey,
|
postThreadQueryKey,
|
||||||
|
postThreadHiddenQueryKey,
|
||||||
params,
|
params,
|
||||||
}: {
|
}: {
|
||||||
queryClient: QueryClient
|
queryClient: QueryClient
|
||||||
queryKey: ReturnType<typeof createPostThreadQueryKey>
|
postThreadQueryKey: ReturnType<typeof createPostThreadQueryKey>
|
||||||
// TODO could clean this up?
|
postThreadHiddenQueryKey: ReturnType<typeof createPostThreadHiddenQueryKey>
|
||||||
params: PostThreadParams
|
params: Pick<PostThreadParams, 'view'>
|
||||||
}) {
|
}) {
|
||||||
return {
|
return {
|
||||||
insertReplies(
|
insertReplies(
|
||||||
parentUri: string,
|
parentUri: string,
|
||||||
replies: AppBskyUnspeccedGetPostThreadV2.ThreadItem[],
|
replies: AppBskyUnspeccedGetPostThreadV2.ThreadItem[],
|
||||||
) {
|
) {
|
||||||
|
/*
|
||||||
|
* Main thread query mutator.
|
||||||
|
*/
|
||||||
queryClient.setQueryData<AppBskyUnspeccedGetPostThreadV2.OutputSchema>(
|
queryClient.setQueryData<AppBskyUnspeccedGetPostThreadV2.OutputSchema>(
|
||||||
queryKey,
|
postThreadQueryKey,
|
||||||
queryData => {
|
data => {
|
||||||
if (!queryData) return
|
if (!data) return
|
||||||
|
|
||||||
const thread = [...queryData.thread]
|
|
||||||
|
|
||||||
for (let i = 0; i < thread.length; i++) {
|
|
||||||
const existingParent = thread[i]
|
|
||||||
if (!AppBskyUnspeccedDefs.isThreadItemPost(existingParent.value))
|
|
||||||
continue
|
|
||||||
if (existingParent.uri !== parentUri) continue
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Update parent data
|
|
||||||
*/
|
|
||||||
existingParent.value.post = {
|
|
||||||
...existingParent.value.post,
|
|
||||||
replyCount: (existingParent.value.post.replyCount || 0) + 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
const opDid = getRootPostAtUri(existingParent.value.post)?.host
|
|
||||||
const nextItem = thread.at(i + 1)
|
|
||||||
const isReplyToRoot = existingParent.depth === 0
|
|
||||||
const isEndOfReplyChain =
|
|
||||||
!nextItem || nextItem.depth <= existingParent.depth
|
|
||||||
const firstReply = replies.at(0)
|
|
||||||
const opIsReplier = AppBskyUnspeccedDefs.isThreadItemPost(
|
|
||||||
firstReply?.value,
|
|
||||||
)
|
|
||||||
? opDid === firstReply.value.post.author.did
|
|
||||||
: false
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Always insert replies if the following conditions are met.
|
|
||||||
*/
|
|
||||||
const shouldAlwaysInsertReplies =
|
|
||||||
isReplyToRoot ||
|
|
||||||
params.view === 'tree' ||
|
|
||||||
(params.view === 'linear' && isEndOfReplyChain)
|
|
||||||
/*
|
|
||||||
* Maybe insert replies if the replier is the OP and certain conditions are met
|
|
||||||
*/
|
|
||||||
const shouldReplaceWithOPReplies =
|
|
||||||
!isReplyToRoot && params.view === 'linear' && opIsReplier
|
|
||||||
|
|
||||||
if (shouldAlwaysInsertReplies || shouldReplaceWithOPReplies) {
|
|
||||||
const branch = getBranch(thread, i, existingParent.depth)
|
|
||||||
/*
|
|
||||||
* OP insertions replace other replies _in linear view_.
|
|
||||||
*/
|
|
||||||
const itemsToRemove = shouldReplaceWithOPReplies
|
|
||||||
? branch.length
|
|
||||||
: 0
|
|
||||||
|
|
||||||
thread.splice(
|
|
||||||
i + 1,
|
|
||||||
itemsToRemove,
|
|
||||||
...replies
|
|
||||||
.map((r, ri) => {
|
|
||||||
r.depth = existingParent.depth + 1 + ri
|
|
||||||
return r
|
|
||||||
})
|
|
||||||
.filter(r => {
|
|
||||||
// Filter out replies that are too deep for our UI
|
|
||||||
return r.depth <= BELOW
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...queryData,
|
...data,
|
||||||
thread,
|
thread: mutator<AppBskyUnspeccedGetPostThreadV2.ThreadItem>([
|
||||||
|
...data.thread,
|
||||||
|
]),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hidden threads query mutator.
|
||||||
|
*/
|
||||||
|
queryClient.setQueryData<AppBskyUnspeccedGetPostThreadHiddenV2.OutputSchema>(
|
||||||
|
postThreadHiddenQueryKey,
|
||||||
|
data => {
|
||||||
|
if (!data) return
|
||||||
|
console.log(data)
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
thread:
|
||||||
|
mutator<AppBskyUnspeccedGetPostThreadHiddenV2.ThreadHiddenItem>([
|
||||||
|
...data.thread,
|
||||||
|
]),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
function mutator<T>(thread: ApiThreadItem[]): T[] {
|
||||||
|
for (let i = 0; i < thread.length; i++) {
|
||||||
|
const existingParent = thread[i]
|
||||||
|
if (!AppBskyUnspeccedDefs.isThreadItemPost(existingParent.value))
|
||||||
|
continue
|
||||||
|
if (existingParent.uri !== parentUri) continue
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update parent data
|
||||||
|
*/
|
||||||
|
existingParent.value.post = {
|
||||||
|
...existingParent.value.post,
|
||||||
|
replyCount: (existingParent.value.post.replyCount || 0) + 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
const opDid = getRootPostAtUri(existingParent.value.post)?.host
|
||||||
|
const nextItem = thread.at(i + 1)
|
||||||
|
const isReplyToRoot = existingParent.depth === 0
|
||||||
|
const isEndOfReplyChain =
|
||||||
|
!nextItem || nextItem.depth <= existingParent.depth
|
||||||
|
const firstReply = replies.at(0)
|
||||||
|
const opIsReplier = AppBskyUnspeccedDefs.isThreadItemPost(
|
||||||
|
firstReply?.value,
|
||||||
|
)
|
||||||
|
? opDid === firstReply.value.post.author.did
|
||||||
|
: false
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Always insert replies if the following conditions are met.
|
||||||
|
*/
|
||||||
|
const shouldAlwaysInsertReplies =
|
||||||
|
isReplyToRoot ||
|
||||||
|
params.view === 'tree' ||
|
||||||
|
(params.view === 'linear' && isEndOfReplyChain)
|
||||||
|
/*
|
||||||
|
* Maybe insert replies if the replier is the OP and certain conditions are met
|
||||||
|
*/
|
||||||
|
const shouldReplaceWithOPReplies =
|
||||||
|
!isReplyToRoot && params.view === 'linear' && opIsReplier
|
||||||
|
|
||||||
|
if (shouldAlwaysInsertReplies || shouldReplaceWithOPReplies) {
|
||||||
|
const branch = getBranch(thread, i, existingParent.depth)
|
||||||
|
/*
|
||||||
|
* OP insertions replace other replies _in linear view_.
|
||||||
|
*/
|
||||||
|
const itemsToRemove = shouldReplaceWithOPReplies ? branch.length : 0
|
||||||
|
|
||||||
|
thread.splice(
|
||||||
|
i + 1,
|
||||||
|
itemsToRemove,
|
||||||
|
...replies
|
||||||
|
.map((r, ri) => {
|
||||||
|
r.depth = existingParent.depth + 1 + ri
|
||||||
|
return r
|
||||||
|
})
|
||||||
|
.filter(r => {
|
||||||
|
// Filter out replies that are too deep for our UI
|
||||||
|
return r.depth <= BELOW
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return thread as T[]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Unused atm, post shadow does the trick, but it would be nice to clean up
|
* Unused atm, post shadow does the trick, but it would be nice to clean up
|
||||||
@@ -123,7 +149,7 @@ export function createCacheMutator({
|
|||||||
*/
|
*/
|
||||||
deletePost(post: AppBskyUnspeccedGetPostThreadV2.ThreadItem) {
|
deletePost(post: AppBskyUnspeccedGetPostThreadV2.ThreadItem) {
|
||||||
queryClient.setQueryData<AppBskyUnspeccedGetPostThreadV2.OutputSchema>(
|
queryClient.setQueryData<AppBskyUnspeccedGetPostThreadV2.OutputSchema>(
|
||||||
queryKey,
|
postThreadQueryKey,
|
||||||
queryData => {
|
queryData => {
|
||||||
if (!queryData) return
|
if (!queryData) return
|
||||||
|
|
||||||
|
|||||||
@@ -374,18 +374,18 @@ export function buildThread({
|
|||||||
serverHiddenThreadItems,
|
serverHiddenThreadItems,
|
||||||
isLoading,
|
isLoading,
|
||||||
hasSession,
|
hasSession,
|
||||||
hiddenItemsVisible,
|
hiddenThreadItemsVisible,
|
||||||
hasServerHiddenItems,
|
hasServerHiddenThreadItems,
|
||||||
loadServerHiddenThreadItems,
|
showHiddenThreadItems,
|
||||||
}: {
|
}: {
|
||||||
threadItems: ThreadItem[]
|
threadItems: ThreadItem[]
|
||||||
hiddenThreadItems: ThreadItem[]
|
hiddenThreadItems: ThreadItem[]
|
||||||
serverHiddenThreadItems: ThreadItem[]
|
serverHiddenThreadItems: ThreadItem[]
|
||||||
isLoading: boolean
|
isLoading: boolean
|
||||||
hasSession: boolean
|
hasSession: boolean
|
||||||
hiddenItemsVisible: boolean
|
hiddenThreadItemsVisible: boolean
|
||||||
hasServerHiddenItems: boolean
|
hasServerHiddenThreadItems: boolean
|
||||||
loadServerHiddenThreadItems: () => Promise<void>
|
showHiddenThreadItems: () => void
|
||||||
}) {
|
}) {
|
||||||
/**
|
/**
|
||||||
* `threadItems` is memoized here, so don't mutate it directly.
|
* `threadItems` is memoized here, so don't mutate it directly.
|
||||||
@@ -440,15 +440,15 @@ export function buildThread({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hiddenThreadItems.length || hasServerHiddenItems) {
|
if (hiddenThreadItems.length || hasServerHiddenThreadItems) {
|
||||||
if (hiddenItemsVisible) {
|
if (hiddenThreadItemsVisible) {
|
||||||
items.push(...hiddenThreadItems)
|
items.push(...hiddenThreadItems)
|
||||||
items.push(...serverHiddenThreadItems)
|
items.push(...serverHiddenThreadItems)
|
||||||
} else {
|
} else {
|
||||||
items.push({
|
items.push({
|
||||||
type: 'showHiddenReplies',
|
type: 'showHiddenReplies',
|
||||||
key: 'showHiddenReplies',
|
key: 'showHiddenReplies',
|
||||||
onPress: loadServerHiddenThreadItems,
|
onPress: showHiddenThreadItems,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ export const createPostThreadQueryKey = (props: PostThreadParams) =>
|
|||||||
[postThreadQueryKeyRoot, props] as const
|
[postThreadQueryKeyRoot, props] as const
|
||||||
|
|
||||||
export const createPostThreadHiddenQueryKey = (
|
export const createPostThreadHiddenQueryKey = (
|
||||||
props: AppBskyUnspeccedGetPostThreadHiddenV2.QueryParams,
|
props: Omit<AppBskyUnspeccedGetPostThreadHiddenV2.QueryParams, 'anchor'> & {
|
||||||
|
anchor?: string
|
||||||
|
},
|
||||||
) => [postThreadHiddenQueryKeyRoot, props] as const
|
) => [postThreadHiddenQueryKeyRoot, props] as const
|
||||||
|
|
||||||
export type PostThreadParams = Pick<
|
export type PostThreadParams = Pick<
|
||||||
@@ -89,7 +91,7 @@ export type ThreadItem =
|
|||||||
| {
|
| {
|
||||||
type: 'showHiddenReplies'
|
type: 'showHiddenReplies'
|
||||||
key: string
|
key: string
|
||||||
onPress: () => Promise<void>
|
onPress: () => void
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: 'readMore'
|
type: 'readMore'
|
||||||
|
|||||||
Reference in New Issue
Block a user