diff options
author | Paul Frazee <pfrazee@gmail.com> | 2023-11-27 17:41:30 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-27 17:41:30 -0800 |
commit | f580d4daf0d2172fa285a5a87a1bec5100a70f63 (patch) | |
tree | 425c866f9d39a2c193d0b8097bcf01d6bbfe2064 /src/view/com/post-thread/PostThread.tsx | |
parent | d4714baf13561236a85d44fec144f7f27a149bfd (diff) | |
download | voidsky-f580d4daf0d2172fa285a5a87a1bec5100a70f63.tar.zst |
Restore post-thread caching behaviors (react-query refactor) (#2010)
* Rework resolve-did and resolve-uri queries to be smarter about cache reuse * Precache handle resolutions * Remove old unused code * Load placeholder threads from the post-feed and notifications-feed queries * Remove logs * Fix bad ref * Add loading spinners to the cache-loading thread view * Scroll replies into view when loading threads * Add caching within a thread * Fix: dont show bottom border when the child spinner is active
Diffstat (limited to 'src/view/com/post-thread/PostThread.tsx')
-rw-r--r-- | src/view/com/post-thread/PostThread.tsx | 164 |
1 files changed, 79 insertions, 85 deletions
diff --git a/src/view/com/post-thread/PostThread.tsx b/src/view/com/post-thread/PostThread.tsx index c19833948..edf02e9c5 100644 --- a/src/view/com/post-thread/PostThread.tsx +++ b/src/view/com/post-thread/PostThread.tsx @@ -39,8 +39,10 @@ import { usePreferencesQuery, } from '#/state/queries/preferences' import {useSession} from '#/state/session' +import {isNative} from '#/platform/detection' +import {logger} from '#/logger' -// const MAINTAIN_VISIBLE_CONTENT_POSITION = {minIndexForVisible: 2} TODO +const MAINTAIN_VISIBLE_CONTENT_POSITION = {minIndexForVisible: 2} const TOP_COMPONENT = {_reactKey: '__top_component__'} const PARENT_SPINNER = {_reactKey: '__parent_spinner__'} @@ -72,7 +74,6 @@ export function PostThread({ isError, error, refetch, - isRefetching, data: thread, } = usePostThreadQuery(uri) const {data: preferences} = usePreferencesQuery() @@ -110,7 +111,6 @@ export function PostThread({ return ( <PostThreadLoaded thread={thread} - isRefetching={isRefetching} threadViewPrefs={preferences.threadViewPrefs} onRefresh={refetch} onPressReply={onPressReply} @@ -120,13 +120,11 @@ export function PostThread({ function PostThreadLoaded({ thread, - isRefetching, threadViewPrefs, onRefresh, onPressReply, }: { thread: ThreadNode - isRefetching: boolean threadViewPrefs: UsePreferencesQueryResponse['threadViewPrefs'] onRefresh: () => void onPressReply: () => void @@ -136,29 +134,15 @@ function PostThreadLoaded({ const pal = usePalette('default') const {isTablet, isDesktop} = useWebMediaQueries() const ref = useRef<FlatList>(null) - // const hasScrolledIntoView = useRef<boolean>(false) TODO + const highlightedPostRef = useRef<View | null>(null) + const needsScrollAdjustment = useRef<boolean>( + !isNative || // web always uses scroll adjustment + (thread.type === 'post' && !thread.ctx.isParentLoading), // native only does it when not loading from placeholder + ) const [maxVisible, setMaxVisible] = React.useState(100) + const [isPTRing, setIsPTRing] = React.useState(false) - // TODO - // const posts = React.useMemo(() => { - // if (view.thread) { - // let arr = [TOP_COMPONENT].concat(Array.from(flattenThread(view.thread))) - // if (arr.length > maxVisible) { - // arr = arr.slice(0, maxVisible).concat([LOAD_MORE]) - // } - // if (view.isLoadingFromCache) { - // if (view.thread?.postRecord?.reply) { - // arr.unshift(PARENT_SPINNER) - // } - // arr.push(CHILD_SPINNER) - // } else { - // arr.push(BOTTOM_COMPONENT) - // } - // return arr - // } - // return [] - // }, [view.isLoadingFromCache, view.thread, maxVisible]) - // const highlightedPostIndex = posts.findIndex(post => post._isHighlightedPost) + // construct content const posts = React.useMemo(() => { let arr = [TOP_COMPONENT].concat( Array.from(flattenThreadSkeleton(sortThread(thread, threadViewPrefs))), @@ -166,54 +150,61 @@ function PostThreadLoaded({ if (arr.length > maxVisible) { arr = arr.slice(0, maxVisible).concat([LOAD_MORE]) } - arr.push(BOTTOM_COMPONENT) + if (arr.indexOf(CHILD_SPINNER) === -1) { + arr.push(BOTTOM_COMPONENT) + } return arr }, [thread, maxVisible, threadViewPrefs]) - // TODO - /*const onContentSizeChange = React.useCallback(() => { + /** + * NOTE + * Scroll positioning + * + * This callback is run if needsScrollAdjustment.current == true, which is... + * - On web: always + * - On native: when the placeholder cache is not being used + * + * It then only runs when viewing a reply, and the goal is to scroll the + * reply into view. + * + * On native, if the placeholder cache is being used then maintainVisibleContentPosition + * is a more effective solution, so we use that. Otherwise, typically we're loading from + * the react-query cache, so we just need to immediately scroll down to the post. + * + * On desktop, maintainVisibleContentPosition isn't supported so we just always use + * this technique. + * + * -prf + */ + const onContentSizeChange = React.useCallback(() => { // only run once - if (hasScrolledIntoView.current) { + if (!needsScrollAdjustment.current) { return } // wait for loading to finish - if ( - !view.hasContent || - (view.isFromCache && view.isLoadingFromCache) || - view.isLoading - ) { - return + if (thread.type === 'post' && !!thread.parent) { + highlightedPostRef.current?.measure( + (_x, _y, _width, _height, _pageX, pageY) => { + ref.current?.scrollToOffset({ + animated: false, + offset: pageY - (isDesktop ? 0 : 50), + }) + }, + ) + needsScrollAdjustment.current = false } + }, [thread, isDesktop]) - if (highlightedPostIndex !== -1) { - ref.current?.scrollToIndex({ - index: highlightedPostIndex, - animated: false, - viewPosition: 0, - }) - hasScrolledIntoView.current = true + const onPTR = React.useCallback(async () => { + setIsPTRing(true) + try { + await onRefresh() + } catch (err) { + logger.error('Failed to refresh posts thread', {error: err}) } - }, [ - highlightedPostIndex, - view.hasContent, - view.isFromCache, - view.isLoadingFromCache, - view.isLoading, - ])*/ - const onScrollToIndexFailed = React.useCallback( - (info: { - index: number - highestMeasuredFrameIndex: number - averageItemLength: number - }) => { - ref.current?.scrollToOffset({ - animated: false, - offset: info.averageItemLength * info.index, - }) - }, - [ref], - ) + setIsPTRing(false) + }, [setIsPTRing, onRefresh]) const renderItem = React.useCallback( ({item, index}: {item: YieldedItem; index: number}) => { @@ -290,18 +281,21 @@ function PostThreadLoaded({ ? (posts[index - 1] as ThreadPost) : undefined return ( - <PostThreadItem - post={item.post} - record={item.record} - treeView={threadViewPrefs.lab_treeViewEnabled || false} - depth={item.ctx.depth} - isHighlightedPost={item.ctx.isHighlightedPost} - hasMore={item.ctx.hasMore} - showChildReplyLine={item.ctx.showChildReplyLine} - showParentReplyLine={item.ctx.showParentReplyLine} - hasPrecedingItem={!!prev?.ctx.showChildReplyLine} - onPostReply={onRefresh} - /> + <View + ref={item.ctx.isHighlightedPost ? highlightedPostRef : undefined}> + <PostThreadItem + post={item.post} + record={item.record} + treeView={threadViewPrefs.lab_treeViewEnabled || false} + depth={item.ctx.depth} + isHighlightedPost={item.ctx.isHighlightedPost} + hasMore={item.ctx.hasMore} + showChildReplyLine={item.ctx.showChildReplyLine} + showParentReplyLine={item.ctx.showParentReplyLine} + hasPrecedingItem={!!prev?.ctx.showChildReplyLine} + onPostReply={onRefresh} + /> + </View> ) } return null @@ -330,25 +324,21 @@ function PostThreadLoaded({ data={posts} initialNumToRender={posts.length} maintainVisibleContentPosition={ - undefined // TODO - // isNative && view.isFromCache && view.isCachedPostAReply - // ? MAINTAIN_VISIBLE_CONTENT_POSITION - // : undefined + !needsScrollAdjustment.current + ? MAINTAIN_VISIBLE_CONTENT_POSITION + : undefined } keyExtractor={item => item._reactKey} renderItem={renderItem} refreshControl={ <RefreshControl - refreshing={isRefetching} - onRefresh={onRefresh} + refreshing={isPTRing} + onRefresh={onPTR} tintColor={pal.colors.text} titleColor={pal.colors.text} /> } - onContentSizeChange={ - undefined //TODOisNative && view.isFromCache ? undefined : onContentSizeChange - } - onScrollToIndexFailed={onScrollToIndexFailed} + onContentSizeChange={onContentSizeChange} style={s.hContentRegion} // @ts-ignore our .web version only -prf desktopFixedHeight @@ -465,6 +455,8 @@ function* flattenThreadSkeleton( if (node.type === 'post') { if (node.parent) { yield* flattenThreadSkeleton(node.parent) + } else if (node.ctx.isParentLoading) { + yield PARENT_SPINNER } yield node if (node.ctx.isHighlightedPost) { @@ -474,6 +466,8 @@ function* flattenThreadSkeleton( for (const reply of node.replies) { yield* flattenThreadSkeleton(reply) } + } else if (node.ctx.isChildLoading) { + yield CHILD_SPINNER } } else if (node.type === 'not-found') { yield DELETED |