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:
parent
2f6d7606b3
commit
08525b52c3
1 changed files with 55 additions and 28 deletions
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue