This commit is contained in:
Dan Abramov
2023-12-23 01:40:01 +00:00
parent 90b0a69c0e
commit d83a9aceab
4 changed files with 53 additions and 33 deletions
+1 -1
View File
@@ -6,7 +6,6 @@ import {TabBar} from './TabBar'
import {usePalette} from '#/lib/hooks/usePalette'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {ListMethods} from '../util/List'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
export interface PagerWithHeaderChildParams {
headerHeight: number
@@ -108,6 +107,7 @@ let PagerTabBar = ({
renderHeader?: () => JSX.Element
onCurrentPageSelected?: (index: number) => void
onSelect?: (index: number) => void
tabBarAnchor?: JSX.Element
}): React.ReactNode => {
const pal = usePalette('default')
const {isMobile} = useWebMediaQueries()
+7 -3
View File
@@ -197,7 +197,7 @@ function PostThreadLoaded({
// wait for loading to finish
if (thread.type === 'post' && !!thread.parent) {
function onMeasure(pageY) {
function onMeasure(pageY: number) {
let spinnerHeight = 0
if (isDesktop) {
spinnerHeight = 40
@@ -216,8 +216,12 @@ function PostThreadLoaded({
},
)
} else {
const pageY = highlightedPostRef.current.getBoundingClientRect().top
onMeasure(pageY)
// Measure synchronously to avoid a layout jump.
const domNode = highlightedPostRef.current
if (domNode) {
const pageY = (domNode as any as Element).getBoundingClientRect().top
onMeasure(pageY)
}
}
needsScrollAdjustment.current = false
}
+1 -1
View File
@@ -86,9 +86,9 @@ function ListImpl<ItemT>(
return (
<FlatList_INTERNAL
{...props}
scrollIndicatorInsets={{right: 1}}
contentOffset={contentOffset}
refreshControl={refreshControl}
scrollIndicatorInsets={{right: 1}}
onScroll={scrollHandler}
scrollEventThrottle={1}
style={style}
+44 -28
View File
@@ -7,7 +7,7 @@ import {useScrollHandlers} from '#/lib/ScrollContext'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
import {batchedUpdates} from '#/lib/batchedUpdates'
export type ListMethods = FlatList_INTERNAL
export type ListMethods = any // TODO: Better types.
export type ListProps<ItemT> = Omit<
FlatListProps<ItemT>,
| 'onScroll' // Use ScrollContext instead.
@@ -18,8 +18,9 @@ export type ListProps<ItemT> = Omit<
headerOffset?: number
refreshing?: boolean
onRefresh?: () => void
desktopFixedHeight: any // TODO: Better types.
}
export type ListRef = React.MutableRefObject<FlatList_INTERNAL | null>
export type ListRef = React.MutableRefObject<any | null> // TODO: Better types.
function ListImpl<ItemT>(
{
@@ -27,7 +28,7 @@ function ListImpl<ItemT>(
ListFooterComponent,
contentContainerStyle,
data,
desktopFixedHeight, // TODO
desktopFixedHeight,
headerOffset,
keyExtractor,
refreshing: _unsupportedRefreshing,
@@ -53,7 +54,7 @@ function ListImpl<ItemT>(
)
}
let header: ListProps<ItemT>['ListHeaderComponent'] = null
let header: JSX.Element | null = null
if (ListHeaderComponent != null) {
if (typeof ListHeaderComponent === 'object') {
header = ListHeaderComponent
@@ -63,7 +64,7 @@ function ListImpl<ItemT>(
}
}
let footer: ListProps<ItemT>['ListHeaderComponent'] = null
let footer: JSX.Element | null = null
if (ListFooterComponent != null) {
if (typeof ListFooterComponent === 'object') {
footer = ListFooterComponent
@@ -100,11 +101,15 @@ function ListImpl<ItemT>(
behavior: animated ? 'smooth' : 'instant',
})
},
} as any), // TODO: Types.
} as any), // TODO: Better types.
[],
)
const [isVisible, setIsVisible] = React.useState(false)
// --- onContentSizeChange ---
const containerRef = useRef(null)
useResizeObserver(containerRef, onContentSizeChange)
// --- onScroll ---
const handleScroll = useNonReactiveCallback(() => {
contextScrollHandlers.onScroll?.(
{
@@ -112,21 +117,22 @@ function ListImpl<ItemT>(
x: window.scrollX,
y: window.scrollY,
},
// TODO
},
{
/* TODO */
},
} as any, // TODO: Better types.
null as any,
)
})
const [isParentTreeVisible, setIsParentTreeVisible] = React.useState(false)
React.useEffect(() => {
if (!isVisible) {
if (!isParentTreeVisible) {
// Prevents hidden tabs from firing scroll events.
// Only one list is expected to be firing these at a time.
return
}
window.addEventListener('scroll', handleScroll)
return () => window.removeEventListener('scroll', handleScroll)
}, [isVisible, handleScroll])
}, [isParentTreeVisible, handleScroll])
// --- onScrolledDownChange ---
const isScrolledDown = useRef(false)
function handleAboveTheFoldVisibleChange(isAboveTheFold: boolean) {
const didScrollDown = !isAboveTheFold
@@ -138,13 +144,21 @@ function ListImpl<ItemT>(
}
}
const containerRef = useRef(null)
useResizeObserver(containerRef, onContentSizeChange)
// --- onEndReached ---
const onTailVisibilityChange = useNonReactiveCallback(
(isTailVisible: boolean) => {
if (isTailVisible) {
onEndReached?.({
distanceFromEnd: onEndReachedThreshold || 0,
})
}
},
)
return (
<View {...props} style={style} ref={nativeRef}>
<Visibility
onVisibleChange={setIsVisible}
onVisibleChange={setIsParentTreeVisible}
style={styles.parentTreeVisibilityDetector}
/>
<View
@@ -172,13 +186,7 @@ function ListImpl<ItemT>(
{onEndReached && (
<Visibility
topMargin={(onEndReachedThreshold ?? 0) * 100 + '%'}
onVisibleChange={isVisible => {
if (isVisible) {
onEndReached?.({
distanceFromEnd: onEndReachedThreshold || 0,
})
}
}}
onVisibleChange={onTailVisibilityChange}
/>
)}
{footer}
@@ -187,17 +195,25 @@ function ListImpl<ItemT>(
)
}
function useResizeObserver(ref, onResize) {
function useResizeObserver(
ref: React.RefObject<Element>,
onResize: undefined | ((w: number, h: number) => void),
) {
const handleResize = useNonReactiveCallback(onResize ?? (() => {}))
const isActive = !!onResize
React.useEffect(() => {
if (!isActive) {
return
}
const resizeObserver = new ResizeObserver(() => {
handleResize()
const resizeObserver = new ResizeObserver(entries => {
batchedUpdates(() => {
for (let entry of entries) {
const rect = entry.contentRect
handleResize(rect.width, rect.height)
}
})
})
const node = ref.current
const node = ref.current!
resizeObserver.observe(node)
return () => {
resizeObserver.unobserve(node)