Move visual display logic out of PostThread generators (#2862)

* Split skeleton gen into replies and parents

Co-authored-by: Hailey <me@haileyok.com>

* Move REPLY_PROMPT out of the generator

* Move the rest of visual logic out of gen

---------

Co-authored-by: Hailey <me@haileyok.com>
This commit is contained in:
dan 2024-02-13 22:12:33 +00:00 committed by GitHub
parent 2f6d7606b3
commit 08525b52c3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -55,7 +55,15 @@ const CHILD_SPINNER = {_reactKey: '__child_spinner__'}
const LOAD_MORE = {_reactKey: '__load_more__'} const LOAD_MORE = {_reactKey: '__load_more__'}
const BOTTOM_COMPONENT = {_reactKey: '__bottom_component__'} const BOTTOM_COMPONENT = {_reactKey: '__bottom_component__'}
type YieldedItem = ThreadPost | typeof TOP_COMPONENT | typeof REPLY_PROMPT type YieldedItem = ThreadPost | ThreadBlocked | ThreadNotFound
type RowItem =
| YieldedItem
// TODO: TS doesn't actually enforce it's one of these, it only enforces matching shape.
| typeof TOP_COMPONENT
| typeof REPLY_PROMPT
| typeof CHILD_SPINNER
| typeof LOAD_MORE
| typeof BOTTOM_COMPONENT
export function PostThread({ export function PostThread({
uri, uri,
@ -160,19 +168,31 @@ function PostThreadLoaded({
// construct content // construct content
const posts = React.useMemo(() => { const posts = React.useMemo(() => {
let arr = Array.from( const root = sortThread(thread, threadViewPrefs)
flattenThreadSkeleton( let arr: RowItem[] = []
sortThread(thread, threadViewPrefs), if (root.type === 'post') {
hasSession, if (!root.ctx.isParentLoading) {
treeView, arr.push(TOP_COMPONENT)
), for (const parent of flattenThreadParents(root, hasSession)) {
) arr.push(parent)
}
}
arr.push(root)
if (!root.post.viewer?.replyDisabled) {
arr.push(REPLY_PROMPT)
}
if (root.ctx.isChildLoading) {
arr.push(CHILD_SPINNER)
} else {
for (const reply of flattenThreadReplies(root, hasSession, treeView)) {
arr.push(reply)
}
arr.push(BOTTOM_COMPONENT)
}
}
if (arr.length > maxVisible) { if (arr.length > maxVisible) {
arr = arr.slice(0, maxVisible).concat([LOAD_MORE]) arr = arr.slice(0, maxVisible).concat([LOAD_MORE])
} }
if (arr.indexOf(CHILD_SPINNER) === -1) {
arr.push(BOTTOM_COMPONENT)
}
return arr return arr
}, [thread, treeView, maxVisible, threadViewPrefs, hasSession]) }, [thread, treeView, maxVisible, threadViewPrefs, hasSession])
@ -239,7 +259,7 @@ function PostThreadLoaded({
}, [setIsPTRing, onRefresh]) }, [setIsPTRing, onRefresh])
const renderItem = React.useCallback( const renderItem = React.useCallback(
({item, index}: {item: YieldedItem; index: number}) => { ({item, index}: {item: RowItem; index: number}) => {
if (item === TOP_COMPONENT) { if (item === TOP_COMPONENT) {
return isTabletOrMobile ? ( return isTabletOrMobile ? (
<ViewHeader <ViewHeader
@ -489,36 +509,43 @@ function isThreadBlocked(v: unknown): v is ThreadBlocked {
return !!v && typeof v === 'object' && 'type' in v && v.type === 'blocked' return !!v && typeof v === 'object' && 'type' in v && v.type === 'blocked'
} }
function* flattenThreadSkeleton( function* flattenThreadParents(
node: ThreadNode,
hasSession: boolean,
): Generator<YieldedItem, void> {
if (node.type === 'post') {
if (node.parent) {
yield* flattenThreadParents(node.parent, hasSession)
}
if (!node.ctx.isHighlightedPost) {
yield node
}
} else if (node.type === 'not-found') {
yield node
} else if (node.type === 'blocked') {
yield node
}
}
function* flattenThreadReplies(
node: ThreadNode, node: ThreadNode,
hasSession: boolean, hasSession: boolean,
treeView: boolean, treeView: boolean,
isTraversingReplies: boolean = false,
): Generator<YieldedItem, void> { ): Generator<YieldedItem, void> {
if (node.type === 'post') { if (node.type === 'post') {
if (!node.ctx.isParentLoading) { if (!hasSession && hasPwiOptOut(node)) {
if (node.parent) {
yield* flattenThreadSkeleton(node.parent, hasSession, treeView, false)
} else if (!isTraversingReplies) {
yield TOP_COMPONENT
}
}
if (!hasSession && node.ctx.depth > 0 && hasPwiOptOut(node)) {
return return
} }
yield node if (!node.ctx.isHighlightedPost) {
if (node.ctx.isHighlightedPost && !node.post.viewer?.replyDisabled) { yield node
yield REPLY_PROMPT
} }
if (node.replies?.length) { if (node.replies?.length) {
for (const reply of node.replies) { for (const reply of node.replies) {
yield* flattenThreadSkeleton(reply, hasSession, treeView, true) yield* flattenThreadReplies(reply, hasSession, treeView)
if (!treeView && !node.ctx.isHighlightedPost) { if (!treeView && !node.ctx.isHighlightedPost) {
break break
} }
} }
} else if (node.ctx.isChildLoading) {
yield CHILD_SPINNER
} }
} else if (node.type === 'not-found') { } else if (node.type === 'not-found') {
yield node yield node