Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix `IntersectionObserver` `rootMargin` in web `List` implementation, add `onStartReached` (#3866)

* add `onStartReached` to web list

* fix `rootMargin`

authored by

Hailey and committed by
GitHub
594b40c3 6b615f37

+30 -8
+30 -8
src/view/com/util/List.web.tsx
··· 1 - import React, {isValidElement, memo, useRef, startTransition} from 'react' 1 + import React, {isValidElement, memo, startTransition, useRef} from 'react' 2 2 import {FlatListProps, StyleSheet, View, ViewProps} from 'react-native' 3 - import {addStyle} from 'lib/styles' 3 + 4 + import {batchedUpdates} from '#/lib/batchedUpdates' 5 + import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' 6 + import {useScrollHandlers} from '#/lib/ScrollContext' 4 7 import {usePalette} from 'lib/hooks/usePalette' 5 8 import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' 6 - import {useScrollHandlers} from '#/lib/ScrollContext' 7 - import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' 8 - import {batchedUpdates} from '#/lib/batchedUpdates' 9 + import {addStyle} from 'lib/styles' 9 10 10 11 export type ListMethods = any // TODO: Better types. 11 12 export type ListProps<ItemT> = Omit< ··· 32 33 headerOffset, 33 34 keyExtractor, 34 35 refreshing: _unsupportedRefreshing, 36 + onStartReached, 37 + onStartReachedThreshold = 0, 35 38 onEndReached, 36 39 onEndReachedThreshold = 0, 37 40 onRefresh: _unsupportedOnRefresh, ··· 148 151 } 149 152 } 150 153 154 + // --- onStartReached --- 155 + const onHeadVisibilityChange = useNonReactiveCallback( 156 + (isHeadVisible: boolean) => { 157 + if (isHeadVisible) { 158 + onStartReached?.({ 159 + distanceFromStart: onStartReachedThreshold || 0, 160 + }) 161 + } 162 + }, 163 + ) 164 + 151 165 // --- onEndReached --- 152 166 const onTailVisibilityChange = useNonReactiveCallback( 153 167 (isTailVisible: boolean) => { ··· 181 195 onVisibleChange={handleAboveTheFoldVisibleChange} 182 196 style={[styles.aboveTheFoldDetector, {height: headerOffset}]} 183 197 /> 198 + {onStartReached && ( 199 + <Visibility 200 + onVisibleChange={onHeadVisibilityChange} 201 + topMargin={(onStartReachedThreshold ?? 0) * 100 + '%'} 202 + /> 203 + )} 184 204 {header} 185 205 {(data as Array<ItemT>).map((item, index) => ( 186 206 <Row<ItemT> ··· 193 213 ))} 194 214 {onEndReached && ( 195 215 <Visibility 196 - topMargin={(onEndReachedThreshold ?? 0) * 100 + '%'} 197 216 onVisibleChange={onTailVisibilityChange} 217 + bottomMargin={(onEndReachedThreshold ?? 0) * 100 + '%'} 198 218 /> 199 219 )} 200 220 {footer} ··· 256 276 257 277 let Visibility = ({ 258 278 topMargin = '0px', 279 + bottomMargin = '0px', 259 280 onVisibleChange, 260 281 style, 261 282 }: { 262 283 topMargin?: string 284 + bottomMargin?: string 263 285 onVisibleChange: (isVisible: boolean) => void 264 286 style?: ViewProps['style'] 265 287 }): React.ReactNode => { ··· 281 303 282 304 React.useEffect(() => { 283 305 const observer = new IntersectionObserver(handleIntersection, { 284 - rootMargin: `${topMargin} 0px 0px 0px`, 306 + rootMargin: `${topMargin} 0px ${bottomMargin} 0px`, 285 307 }) 286 308 const tail: Element | null = tailRef.current! 287 309 observer.observe(tail) 288 310 return () => { 289 311 observer.unobserve(tail) 290 312 } 291 - }, [handleIntersection, topMargin]) 313 + }, [bottomMargin, handleIntersection, topMargin]) 292 314 293 315 return ( 294 316 <View ref={tailRef} style={addStyle(styles.visibilityDetector, style)} />