Dejank navigation between thread posts (#2625)
* Dejank parent thread spinner * Fix bottom border/spinner jank * Revert unnecessary changezio/stable
parent
de6b380f4e
commit
ef84f3a25e
|
@ -45,10 +45,9 @@ import {isAndroid, isNative} from '#/platform/detection'
|
|||
import {logger} from '#/logger'
|
||||
import {moderatePost_wrapped as moderatePost} from '#/lib/moderatePost_wrapped'
|
||||
|
||||
const MAINTAIN_VISIBLE_CONTENT_POSITION = {minIndexForVisible: 2}
|
||||
const MAINTAIN_VISIBLE_CONTENT_POSITION = {minIndexForVisible: 1}
|
||||
|
||||
const TOP_COMPONENT = {_reactKey: '__top_component__'}
|
||||
const PARENT_SPINNER = {_reactKey: '__parent_spinner__'}
|
||||
const REPLY_PROMPT = {_reactKey: '__reply__'}
|
||||
const DELETED = {_reactKey: '__deleted__'}
|
||||
const BLOCKED = {_reactKey: '__blocked__'}
|
||||
|
@ -59,11 +58,9 @@ const BOTTOM_COMPONENT = {_reactKey: '__bottom_component__'}
|
|||
type YieldedItem =
|
||||
| ThreadPost
|
||||
| typeof TOP_COMPONENT
|
||||
| typeof PARENT_SPINNER
|
||||
| typeof REPLY_PROMPT
|
||||
| typeof DELETED
|
||||
| typeof BLOCKED
|
||||
| typeof PARENT_SPINNER
|
||||
|
||||
export function PostThread({
|
||||
uri,
|
||||
|
@ -152,7 +149,7 @@ function PostThreadLoaded({
|
|||
const {hasSession} = useSession()
|
||||
const {_} = useLingui()
|
||||
const pal = usePalette('default')
|
||||
const {isTablet, isMobile, isDesktop, isTabletOrMobile} = useWebMediaQueries()
|
||||
const {isMobile, isTabletOrMobile} = useWebMediaQueries()
|
||||
const ref = useRef<ListMethods>(null)
|
||||
const highlightedPostRef = useRef<View | null>(null)
|
||||
const needsScrollAdjustment = useRef<boolean>(
|
||||
|
@ -168,13 +165,11 @@ function PostThreadLoaded({
|
|||
|
||||
// construct content
|
||||
const posts = React.useMemo(() => {
|
||||
let arr = [TOP_COMPONENT].concat(
|
||||
Array.from(
|
||||
flattenThreadSkeleton(
|
||||
sortThread(thread, threadViewPrefs),
|
||||
hasSession,
|
||||
treeView,
|
||||
),
|
||||
let arr = Array.from(
|
||||
flattenThreadSkeleton(
|
||||
sortThread(thread, threadViewPrefs),
|
||||
hasSession,
|
||||
treeView,
|
||||
),
|
||||
)
|
||||
if (arr.length > maxVisible) {
|
||||
|
@ -215,15 +210,9 @@ function PostThreadLoaded({
|
|||
// wait for loading to finish
|
||||
if (thread.type === 'post' && !!thread.parent) {
|
||||
function onMeasure(pageY: number) {
|
||||
let spinnerHeight = 0
|
||||
if (isDesktop) {
|
||||
spinnerHeight = 40
|
||||
} else if (isTabletOrMobile) {
|
||||
spinnerHeight = 82
|
||||
}
|
||||
ref.current?.scrollToOffset({
|
||||
animated: false,
|
||||
offset: pageY - spinnerHeight,
|
||||
offset: pageY,
|
||||
})
|
||||
}
|
||||
if (isNative) {
|
||||
|
@ -242,7 +231,7 @@ function PostThreadLoaded({
|
|||
}
|
||||
needsScrollAdjustment.current = false
|
||||
}
|
||||
}, [thread, isDesktop, isTabletOrMobile])
|
||||
}, [thread])
|
||||
|
||||
const onPTR = React.useCallback(async () => {
|
||||
setIsPTRing(true)
|
||||
|
@ -257,17 +246,11 @@ function PostThreadLoaded({
|
|||
const renderItem = React.useCallback(
|
||||
({item, index}: {item: YieldedItem; index: number}) => {
|
||||
if (item === TOP_COMPONENT) {
|
||||
return isTablet ? (
|
||||
return isTabletOrMobile ? (
|
||||
<ViewHeader
|
||||
title={_(msg({message: `Post`, context: 'description'}))}
|
||||
/>
|
||||
) : null
|
||||
} else if (item === PARENT_SPINNER) {
|
||||
return (
|
||||
<View style={styles.parentSpinner}>
|
||||
<ActivityIndicator />
|
||||
</View>
|
||||
)
|
||||
} else if (item === REPLY_PROMPT && hasSession) {
|
||||
return (
|
||||
<View>
|
||||
|
@ -318,7 +301,7 @@ function PostThreadLoaded({
|
|||
// @ts-ignore web-only
|
||||
style={{
|
||||
// Leave enough space below that the scroll doesn't jump
|
||||
height: isNative ? 400 : '100vh',
|
||||
height: isNative ? 600 : '100vh',
|
||||
borderTopWidth: 1,
|
||||
borderColor: pal.colors.border,
|
||||
}}
|
||||
|
@ -326,7 +309,7 @@ function PostThreadLoaded({
|
|||
)
|
||||
} else if (item === CHILD_SPINNER) {
|
||||
return (
|
||||
<View style={styles.childSpinner}>
|
||||
<View style={[pal.border, styles.childSpinner]}>
|
||||
<ActivityIndicator />
|
||||
</View>
|
||||
)
|
||||
|
@ -361,7 +344,7 @@ function PostThreadLoaded({
|
|||
},
|
||||
[
|
||||
hasSession,
|
||||
isTablet,
|
||||
isTabletOrMobile,
|
||||
isMobile,
|
||||
onPressReply,
|
||||
pal.border,
|
||||
|
@ -507,12 +490,15 @@ function* flattenThreadSkeleton(
|
|||
node: ThreadNode,
|
||||
hasSession: boolean,
|
||||
treeView: boolean,
|
||||
isTraversingReplies: boolean = false,
|
||||
): Generator<YieldedItem, void> {
|
||||
if (node.type === 'post') {
|
||||
if (node.parent) {
|
||||
yield* flattenThreadSkeleton(node.parent, hasSession, treeView)
|
||||
} else if (node.ctx.isParentLoading) {
|
||||
yield PARENT_SPINNER
|
||||
if (!node.ctx.isParentLoading) {
|
||||
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
|
||||
|
@ -523,7 +509,7 @@ function* flattenThreadSkeleton(
|
|||
}
|
||||
if (node.replies?.length) {
|
||||
for (const reply of node.replies) {
|
||||
yield* flattenThreadSkeleton(reply, hasSession, treeView)
|
||||
yield* flattenThreadSkeleton(reply, hasSession, treeView, true)
|
||||
if (!treeView && !node.ctx.isHighlightedPost) {
|
||||
break
|
||||
}
|
||||
|
@ -567,10 +553,9 @@ const styles = StyleSheet.create({
|
|||
paddingHorizontal: 18,
|
||||
paddingVertical: 18,
|
||||
},
|
||||
parentSpinner: {
|
||||
paddingVertical: 10,
|
||||
},
|
||||
childSpinner: {
|
||||
borderTopWidth: 1,
|
||||
paddingTop: 40,
|
||||
paddingBottom: 200,
|
||||
},
|
||||
})
|
||||
|
|
|
@ -5,7 +5,6 @@ import {useFocusEffect} from '@react-navigation/native'
|
|||
import {useQueryClient} from '@tanstack/react-query'
|
||||
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
|
||||
import {makeRecordUri} from 'lib/strings/url-helpers'
|
||||
import {ViewHeader} from '../com/util/ViewHeader'
|
||||
import {PostThread as PostThreadComponent} from '../com/post-thread/PostThread'
|
||||
import {ComposePrompt} from 'view/com/composer/Prompt'
|
||||
import {s} from 'lib/styles'
|
||||
|
@ -18,8 +17,6 @@ import {clamp} from 'lodash'
|
|||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useResolveUriQuery} from '#/state/queries/resolve-uri'
|
||||
import {ErrorMessage} from '../com/util/error/ErrorMessage'
|
||||
import {CenteredView} from '../com/util/Views'
|
||||
|
@ -30,7 +27,6 @@ import {isWeb} from '#/platform/detection'
|
|||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostThread'>
|
||||
export function PostThreadScreen({route}: Props) {
|
||||
const queryClient = useQueryClient()
|
||||
const {_} = useLingui()
|
||||
const {hasSession} = useSession()
|
||||
const {fabMinimalShellTransform} = useMinimalShellMode()
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
|
@ -79,9 +75,6 @@ export function PostThreadScreen({route}: Props) {
|
|||
|
||||
return (
|
||||
<View style={s.hContentRegion}>
|
||||
{isMobile && (
|
||||
<ViewHeader title={_(msg({message: 'Post', context: 'description'}))} />
|
||||
)}
|
||||
<View style={s.flex1}>
|
||||
{uriError ? (
|
||||
<CenteredView>
|
||||
|
|
Loading…
Reference in New Issue