WIP optimistic
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import {useCallback} from 'react'
|
import {useCallback, useMemo, useState} from 'react'
|
||||||
import {
|
import {
|
||||||
$Typed,
|
$Typed,
|
||||||
AtUri,
|
AtUri,
|
||||||
@@ -138,7 +138,7 @@ export function useGetPostThreadV2({
|
|||||||
// - sort down muted
|
// - sort down muted
|
||||||
// - sort down blurred?
|
// - sort down blurred?
|
||||||
|
|
||||||
const filtered = filterAndSort(query.data?.thread || [], {
|
const {items, insertReplies} = useThread(query.data?.thread || [], {
|
||||||
hasSession,
|
hasSession,
|
||||||
params,
|
params,
|
||||||
threadgateHiddenReplies: mergeThreadgateHiddenReplies(
|
threadgateHiddenReplies: mergeThreadgateHiddenReplies(
|
||||||
@@ -151,9 +151,10 @@ export function useGetPostThreadV2({
|
|||||||
return {
|
return {
|
||||||
...query,
|
...query,
|
||||||
data: {
|
data: {
|
||||||
slices: filtered,
|
slices: items,
|
||||||
threadgate: query.data?.threadgate,
|
threadgate: query.data?.threadgate,
|
||||||
},
|
},
|
||||||
|
insertReplies,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,7 +266,7 @@ function getBranch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function filterAndSort(
|
export function useThread(
|
||||||
thread: AppBskyFeedGetPostThreadV2.OutputSchema['thread'],
|
thread: AppBskyFeedGetPostThreadV2.OutputSchema['thread'],
|
||||||
{
|
{
|
||||||
hasSession,
|
hasSession,
|
||||||
@@ -280,6 +281,136 @@ export function filterAndSort(
|
|||||||
moderationOpts: ModerationOpts
|
moderationOpts: ModerationOpts
|
||||||
shownHiddenReplyKinds: Set<HiddenReplyKind>
|
shownHiddenReplyKinds: Set<HiddenReplyKind>
|
||||||
},
|
},
|
||||||
|
) {
|
||||||
|
const isTreeView = params.view === 'tree'
|
||||||
|
const [shadowSlices, setShadowSlices] = useState<
|
||||||
|
Map<string, AppBskyFeedDefs.ThreadItemPost[][]>
|
||||||
|
>(new Map())
|
||||||
|
const insertReplies = useCallback((belowUri: string, posts: AppBskyFeedDefs.ThreadItemPost[]) => {
|
||||||
|
setShadowSlices(prev => {
|
||||||
|
if (prev.has(belowUri)) {
|
||||||
|
prev.set(belowUri, [...prev.get(belowUri)!, posts])
|
||||||
|
} else {
|
||||||
|
prev.set(belowUri, [posts])
|
||||||
|
}
|
||||||
|
return prev
|
||||||
|
})
|
||||||
|
}, [setShadowSlices])
|
||||||
|
|
||||||
|
const sorted = useMemo(() => {
|
||||||
|
return sortThread(thread, {
|
||||||
|
threadgateHiddenReplies,
|
||||||
|
moderationOpts,
|
||||||
|
})
|
||||||
|
}, [thread, threadgateHiddenReplies, moderationOpts])
|
||||||
|
|
||||||
|
const flattened = useMemo(() => {
|
||||||
|
const showMuted = shownHiddenReplyKinds.has(HiddenReplyKind.Muted)
|
||||||
|
const showHidden = shownHiddenReplyKinds.has(HiddenReplyKind.Hidden)
|
||||||
|
|
||||||
|
if (sorted.hidden.length) {
|
||||||
|
if (showHidden) {
|
||||||
|
sorted.slices.push(...sorted.hidden)
|
||||||
|
|
||||||
|
if (sorted.muted.length) {
|
||||||
|
if (showMuted) {
|
||||||
|
sorted.slices.push(...sorted.muted)
|
||||||
|
} else {
|
||||||
|
sorted.slices.push({
|
||||||
|
type: 'showHiddenReplies',
|
||||||
|
key: 'showMutedReplies',
|
||||||
|
kind: HiddenReplyKind.Muted,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sorted.slices.push({
|
||||||
|
type: 'showHiddenReplies',
|
||||||
|
key: 'showHiddenReplies',
|
||||||
|
kind: HiddenReplyKind.Hidden,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (sorted.muted.length) {
|
||||||
|
if (showMuted) {
|
||||||
|
sorted.slices.push(...sorted.muted)
|
||||||
|
} else {
|
||||||
|
sorted.slices.push({
|
||||||
|
type: 'showHiddenReplies',
|
||||||
|
key: 'showMutedReplies',
|
||||||
|
kind: HiddenReplyKind.Muted,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sorted.slices
|
||||||
|
}, [sorted, shownHiddenReplyKinds])
|
||||||
|
|
||||||
|
const optimistic = useMemo(() => {
|
||||||
|
if (!shadowSlices.size) return flattened
|
||||||
|
|
||||||
|
for (let i = 0; i < flattened.length; i++) {
|
||||||
|
const item = flattened[i]
|
||||||
|
if (item.type !== 'threadSlice') continue
|
||||||
|
if (!shadowSlices.has(item.slice.uri)) continue
|
||||||
|
|
||||||
|
const replyThreads = shadowSlices.get(item.slice.uri)
|
||||||
|
|
||||||
|
// TODO linear view will work
|
||||||
|
if (!isTreeView) continue
|
||||||
|
|
||||||
|
for (const thread of replyThreads!) {
|
||||||
|
for (let ri = 0; ri < thread.length; ri++) {
|
||||||
|
const post = thread[ri]
|
||||||
|
post.depth = item.slice.depth + 1 + ri
|
||||||
|
flattened.splice(i + 1 + ri, 0, views.post({
|
||||||
|
item: post,
|
||||||
|
oneUp: undefined,
|
||||||
|
oneDown: undefined,
|
||||||
|
moderationOpts,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
i = i + thread.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return flattened
|
||||||
|
}, [flattened, shadowSlices, isTreeView])
|
||||||
|
|
||||||
|
const items = useMemo(() => {
|
||||||
|
if (hasSession) {
|
||||||
|
for (let i = 0; i < optimistic.length; i++) {
|
||||||
|
const slice = optimistic[i]
|
||||||
|
if ('slice' in slice && slice.slice.depth === 0) {
|
||||||
|
optimistic.splice(i + 1, 0, {
|
||||||
|
type: 'replyComposer',
|
||||||
|
key: 'replyComposer',
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return optimistic
|
||||||
|
}, [optimistic, hasSession])
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() => ({
|
||||||
|
items,
|
||||||
|
insertReplies,
|
||||||
|
}),
|
||||||
|
[items, insertReplies],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sortThread(
|
||||||
|
thread: AppBskyFeedGetPostThreadV2.OutputSchema['thread'],
|
||||||
|
{
|
||||||
|
threadgateHiddenReplies,
|
||||||
|
moderationOpts,
|
||||||
|
}: {
|
||||||
|
threadgateHiddenReplies: Set<string>
|
||||||
|
moderationOpts: ModerationOpts
|
||||||
|
},
|
||||||
) {
|
) {
|
||||||
const slices: Slice[] = []
|
const slices: Slice[] = []
|
||||||
const hidden: Slice[] = []
|
const hidden: Slice[] = []
|
||||||
@@ -313,13 +444,6 @@ export function filterAndSort(
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
if (hasSession) {
|
|
||||||
slices.push({
|
|
||||||
type: 'replyComposer',
|
|
||||||
key: 'replyComposer',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
parentTraversal: for (let pi = i - 1; pi >= 0; pi--) {
|
parentTraversal: for (let pi = i - 1; pi >= 0; pi--) {
|
||||||
const parentOneDown = thread[pi + 1]
|
const parentOneDown = thread[pi + 1]
|
||||||
const parent = thread[pi]
|
const parent = thread[pi]
|
||||||
@@ -436,44 +560,11 @@ export function filterAndSort(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const showMuted = shownHiddenReplyKinds.has(HiddenReplyKind.Muted)
|
return {
|
||||||
const showHidden = shownHiddenReplyKinds.has(HiddenReplyKind.Hidden)
|
slices,
|
||||||
|
hidden,
|
||||||
if (hidden.length) {
|
muted,
|
||||||
if (showHidden) {
|
|
||||||
slices.push(...hidden)
|
|
||||||
|
|
||||||
if (muted.length) {
|
|
||||||
if (showMuted) {
|
|
||||||
slices.push(...muted)
|
|
||||||
} else {
|
|
||||||
slices.push({
|
|
||||||
type: 'showHiddenReplies',
|
|
||||||
key: 'showMutedReplies',
|
|
||||||
kind: HiddenReplyKind.Muted,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
|
||||||
slices.push({
|
|
||||||
type: 'showHiddenReplies',
|
|
||||||
key: 'showHiddenReplies',
|
|
||||||
kind: HiddenReplyKind.Hidden,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else if (muted.length) {
|
|
||||||
if (showMuted) {
|
|
||||||
slices.push(...muted)
|
|
||||||
} else {
|
|
||||||
slices.push({
|
|
||||||
type: 'showHiddenReplies',
|
|
||||||
key: 'showMutedReplies',
|
|
||||||
kind: HiddenReplyKind.Muted,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return slices
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getModerationState(moderation: ModerationDecision) {
|
export function getModerationState(moderation: ModerationDecision) {
|
||||||
|
|||||||
@@ -127,10 +127,9 @@ export function Inner({uri}: {uri: string | undefined}) {
|
|||||||
setTreeViewEnabled,
|
setTreeViewEnabled,
|
||||||
} = useThreadPreferences()
|
} = useThreadPreferences()
|
||||||
|
|
||||||
const [showHiddenReplies, setShowHiddenReplies] = useState<HiddenReplyKind | null>(null)
|
|
||||||
const [shownHiddenReplyKinds, setShownHiddenReplyKinds] = useState<Set<HiddenReplyKind>>(new Set())
|
const [shownHiddenReplyKinds, setShownHiddenReplyKinds] = useState<Set<HiddenReplyKind>>(new Set())
|
||||||
|
|
||||||
const {isFetching, isPlaceholderData, error, data, refetch} = useGetPostThreadV2({
|
const {isFetching, isPlaceholderData, error, data, refetch, insertReplies} = useGetPostThreadV2({
|
||||||
uri,
|
uri,
|
||||||
enabled: isThreadPreferencesLoaded,
|
enabled: isThreadPreferencesLoaded,
|
||||||
params: {
|
params: {
|
||||||
@@ -146,36 +145,14 @@ export function Inner({uri}: {uri: string | undefined}) {
|
|||||||
const ref = useRef<ListMethods>(null)
|
const ref = useRef<ListMethods>(null)
|
||||||
const layoutHeaderRef = useRef<View | null>(null)
|
const layoutHeaderRef = useRef<View | null>(null)
|
||||||
const anchorPostRef = useRef<View | null>(null)
|
const anchorPostRef = useRef<View | null>(null)
|
||||||
// TODO
|
|
||||||
const [justPostedUris, setJustPostedUris] = useState(
|
|
||||||
() => new Set<string>(),
|
|
||||||
)
|
|
||||||
|
|
||||||
const onPostReply = useCallback(
|
|
||||||
(postUri: string | undefined) => {
|
|
||||||
refetch()
|
|
||||||
if (postUri) {
|
|
||||||
setJustPostedUris(set => {
|
|
||||||
const nextSet = new Set(set)
|
|
||||||
nextSet.add(postUri)
|
|
||||||
return nextSet
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[refetch],
|
|
||||||
)
|
|
||||||
|
|
||||||
const optimisticOnPostReply = ({
|
const optimisticOnPostReply = ({
|
||||||
post,
|
post,
|
||||||
}: {
|
}: {
|
||||||
post: AppBskyFeedDefs.ThreadItemPost,
|
post: AppBskyFeedDefs.ThreadItemPost,
|
||||||
}) => (_: any, posts: AppBskyFeedDefs.ThreadItemPost[]) => {
|
}) => (_: any, posts: AppBskyFeedDefs.ThreadItemPost[]) => {
|
||||||
const parentDepth = post.depth
|
|
||||||
if (posts.length) {
|
if (posts.length) {
|
||||||
for (let i = 0; i < posts.length; i++) {
|
insertReplies(post.uri, posts)
|
||||||
const p = posts[i]
|
|
||||||
p.depth = parentDepth + 1 + i
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,6 +172,7 @@ export function Inner({uri}: {uri: string | undefined}) {
|
|||||||
embed: post.embed,
|
embed: post.embed,
|
||||||
moderation: anchorPost.moderation,
|
moderation: anchorPost.moderation,
|
||||||
},
|
},
|
||||||
|
// @ts-expect-error TODO
|
||||||
onPost: optimisticOnPostReply({post:anchorPost.slice}),
|
onPost: optimisticOnPostReply({post:anchorPost.slice}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -223,6 +201,7 @@ export function Inner({uri}: {uri: string | undefined}) {
|
|||||||
overrideBlur={
|
overrideBlur={
|
||||||
shownHiddenReplyKinds.has(HiddenReplyKind.Muted) && item.slice.depth > 0
|
shownHiddenReplyKinds.has(HiddenReplyKind.Muted) && item.slice.depth > 0
|
||||||
}
|
}
|
||||||
|
// @ts-expect-error TODO
|
||||||
onPostReply={optimisticOnPostReply({post:item.slice})}
|
onPostReply={optimisticOnPostReply({post:item.slice})}
|
||||||
hideTopBorder={index === 0} // && !item.slice.isParentLoading} // TODO
|
hideTopBorder={index === 0} // && !item.slice.isParentLoading} // TODO
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user