Dejank navigation between thread posts (#2625)

* Dejank parent thread spinner

* Fix bottom border/spinner jank

* Revert unnecessary change
zio/stable
dan 2024-01-25 21:32:17 +00:00 committed by GitHub
parent de6b380f4e
commit ef84f3a25e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 23 additions and 45 deletions

View File

@ -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,
},
})

View File

@ -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>