Simplify pager header measure system (#9990)
This commit is contained in:
@@ -277,35 +277,30 @@ let PagerTabBar = ({
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const headerRef = useRef<View>(null)
|
const pendingHeaderHeightForWhenSentinelReady = useRef<number | undefined>(
|
||||||
const fallbackHeaderOnlyHeight = useRef(0)
|
undefined,
|
||||||
|
)
|
||||||
|
const sentinelHasRenderedRef = useRef(false)
|
||||||
return (
|
return (
|
||||||
<Animated.View
|
<Animated.View
|
||||||
pointerEvents={IS_IOS ? 'auto' : 'box-none'}
|
pointerEvents={IS_IOS ? 'auto' : 'box-none'}
|
||||||
style={[styles.tabBarMobile, headerTransform, t.atoms.bg]}>
|
style={[styles.tabBarMobile, headerTransform, t.atoms.bg]}>
|
||||||
<View
|
<View
|
||||||
ref={headerRef}
|
|
||||||
pointerEvents={IS_IOS ? 'auto' : 'box-none'}
|
pointerEvents={IS_IOS ? 'auto' : 'box-none'}
|
||||||
collapsable={false}
|
collapsable={false}
|
||||||
onLayout={(e: LayoutChangeEvent) => {
|
onLayout={(e: LayoutChangeEvent) => {
|
||||||
|
// we want to measure this view's height to get the header height.
|
||||||
|
// however, we risk doing it too early if the header hasn't rendered yet.
|
||||||
|
// therefore, we use a sentinel view and wait for *that* to layout before
|
||||||
|
// we set the header height, using the last measured height from this
|
||||||
|
// onLayout. after the sentinel has rendered for the first time, we can
|
||||||
|
// just straightforwardly set the header height here directly -sfn
|
||||||
const height = e.nativeEvent.layout.height
|
const height = e.nativeEvent.layout.height
|
||||||
// Fallback measurement using onLayout directly on the header wrapper.
|
// note: sentinel only renders after `isHeaderReady` has turned `true`
|
||||||
// This is more reliable than .measure() on Android after certain
|
if (sentinelHasRenderedRef.current) {
|
||||||
// navigation transitions (e.g. returning from the logged-out view)
|
onHeaderOnlyLayout(height)
|
||||||
// where .measure() can fail to return a height. in general though,
|
} else {
|
||||||
// we should prefer using .measure() when possible as this can
|
pendingHeaderHeightForWhenSentinelReady.current = height
|
||||||
// fire too early and cause layout thrashing.
|
|
||||||
// ref: https://github.com/bluesky-social/social-app/pull/9964 -sfp
|
|
||||||
if (isHeaderReady) {
|
|
||||||
fallbackHeaderOnlyHeight.current = height
|
|
||||||
// Re-measure when the header content changes size (e.g.
|
|
||||||
// SuggestedFollows accordion expanding/collapsing). The sentinel
|
|
||||||
// view below only fires onLayout once on mount, so without this
|
|
||||||
// the headerOnlyHeight goes stale. - sfp
|
|
||||||
const rounded = Math.round(height * 2) / 2
|
|
||||||
if (rounded > 0 && rounded !== headerOnlyHeight) {
|
|
||||||
onHeaderOnlyLayout(height)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
{renderHeader?.({setMinimumHeight: setMinimumHeaderHeight})}
|
{renderHeader?.({setMinimumHeight: setMinimumHeaderHeight})}
|
||||||
@@ -315,22 +310,19 @@ let PagerTabBar = ({
|
|||||||
// Instead, we'll render a brand node conditionally and get fresh layout.
|
// Instead, we'll render a brand node conditionally and get fresh layout.
|
||||||
isHeaderReady && (
|
isHeaderReady && (
|
||||||
<View
|
<View
|
||||||
|
testID="layout-sentinel"
|
||||||
collapsable={false}
|
collapsable={false}
|
||||||
// It wouldn't be enough to do this in a `ref` of an effect because,
|
// It wouldn't be enough to do this in a `ref` of an effect because,
|
||||||
// even if `isHeaderReady` might have turned `true`, the associated
|
// even if `isHeaderReady` might have turned `true`, the associated
|
||||||
// layout might not have been performed yet on the native side.
|
// layout might not have been performed yet on the native side.
|
||||||
onLayout={() => {
|
onLayout={() => {
|
||||||
headerRef.current?.measure(
|
if (!sentinelHasRenderedRef.current) {
|
||||||
(_x: number, _y: number, _width: number, height: number) => {
|
sentinelHasRenderedRef.current = true
|
||||||
// sometimes height is `undefined` on Android, see above
|
const height = pendingHeaderHeightForWhenSentinelReady.current
|
||||||
if (height !== undefined) {
|
if (height !== undefined) {
|
||||||
onHeaderOnlyLayout(height)
|
onHeaderOnlyLayout(height)
|
||||||
} else {
|
}
|
||||||
// if measure fails, use the value we got from `onLayout`
|
}
|
||||||
onHeaderOnlyLayout(fallbackHeaderOnlyHeight.current)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user