fix: TSA-970 header layout shift on Follow Trading screens cp-8.6.0 - #34278
Conversation
These screens applied their top safe-area inset as native SafeAreaView padding. RNCSafeAreaView measures its own safeAreaInsets as the view is attached, and that measurement lands after the `slide_from_right` push completes — so the header painted at y=0 and then visibly dropped into place. Being a native measurement race, it reproduced intermittently and on both platforms. Turn the top edge off everywhere and apply the inset in JS off the already resolved provider, matching PerpsMarketDetailsView (pushed with the same navigation options, and the one screen of its kind that does not exhibit the jump). TopTradersView, SocialTradersTabsView and TraderProfileView take `includesTopInset` on HeaderStandardAnimated, which resolves to `marginTop: insets.top` in HeaderBase. TraderPositionView cannot use that prop: it swaps between three headers, and TraderPositionHeader is a plain Box while TraderPositionAnimatedHeader renders HeaderStandard without a prop spread. The inset goes on a single wrapper above the branch instead, so switching headers on load cannot introduce a second jump.
|
CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes. |
PR template — items to address before "Ready for review"Warnings — informational, address before merging:
See docs/readme/ready-for-review.md for the full Definition of Ready for Review. |
🔍 Smart E2E Test Selection
click to see 🤖 AI reasoning detailsE2E Test Selection: No existing E2E smoke tests cover the SocialLeaderboard feature (confirmed by searching for SocialLeaderboard, SocialTraders, TopTraders, TraderProfile, TraderPosition in spec files - no matches found). The changes don't affect any shared components (TabBar, navigation, modals, confirmations), controllers, Engine, or core infrastructure. The impact is entirely contained within the SocialLeaderboard views. No performance-sensitive code paths are affected. Performance Test Selection: |
|



Description
The Follow Trading screens showed a vertical layout shift on open: the header title and back button painted at the very top of the screen and then dropped to their natural position under the Dynamic Island once the push transition ended. It reproduced on both iOS and Android, intermittently.
Cause. Each screen applied its top inset as native
SafeAreaViewpadding.RNCSafeAreaViewmeasures its ownsafeAreaInsetsas the view is attached, and that measurement lands after the screen'sslide_from_rightpush completes — so the header renders aty=0for a few frames and then jumps down. Because it's a native measurement race rather than a stale JS value, it is intermittent and platform-independent.Fix. Turn the top edge off and apply the inset in JS off the already-resolved
SafeAreaProvider, so the value is correct on the first committed frame and there is nothing left to recalculate mid-transition. This matchesPerpsMarketDetailsView, which is pushed with the same navigation options (headerShown: false+slide_from_right) and is the one screen of its kind that does not exhibit the jump.Same root cause and fix as #34260 (What's Happening detail view, TSA-969), which is verified working on device.
TopTradersViewedges={['top']}includesTopInsetonHeaderStandardAnimatedSocialTradersTabsViewedges={['top']}includesTopInsetonHeaderStandardAnimatedTraderProfileViewedges={['top']}includesTopInsetonHeaderStandardAnimatedTraderPositionViewedges(all four)includesTopInsetreaches the right place on the animated header:HeaderStandardAnimatedspreads intoHeaderStandard, which spreads...headerBasePropsintoHeaderBase, where it becomesmarginTop: insets.top.Why
TraderPositionViewis differentIt can't use
includesTopInset. It swaps between three headers depending on state, and neither accepts the prop:TraderPositionHeader(loading and failed states) is a hand-rolledBox, andTraderPositionAnimatedHeaderrendersHeaderStandardwith explicit props and no{...rest}spread. Rather than plumb a new prop through two more components, the inset goes on a single wrapper above the branch. That also guarantees the three headers share one value, so switching from the loading header to the loaded one can't introduce a second jump.Both leaderboard entry points are covered
SocialTradersViewpicks betweenSocialTradersTabsViewand standaloneTopTradersViewdepending onaiSocialFeedEnabled, so both needed the fix.TopTradersViewinembeddedInTabsmode renders a bare list body with noSafeAreaViewof its own, so that path is covered by the tabs container.Related, not addressed here
app/shims/react-native-safe-area-context.tsxexists specifically to routeSafeAreaView's top inset through JS instead of native padding — added by #28622 ("fix: Fix UI issue related to SafeAreaView top inset recalculation"). The MetroresolveRequestalias that wired it was removed by the RN 0.81.5 upgrade (#29195), so the shim and its test are now dead code while everySafeAreaViewin the app is back on native top padding. Restoring that alias would address this class of bug app-wide, rather than screen by screen.Changelog
CHANGELOG entry: Fixed the header shifting down after the Top Traders, Trader Profile and Trader Position screens finished opening.
Related issues
Fixes: https://consensyssoftware.atlassian.net/browse/TSA-970
Refs: #34260
Manual testing steps
The jump was intermittent before the fix, so repeat each open several times to be confident.
Screenshots/Recordings
Before
After
no-shift.mp4
Pre-merge author checklist
Performance checks (if applicable)
trace()for usage andaddTokenfor an exampleFor performance guidelines and tooling, see the Performance Guide.
Pre-merge reviewer checklist
Note
Low Risk
Layout-only safe-area handling on four social leaderboard screens; no auth, data, or navigation logic changes.
Overview
Fixes TSA-970: headers on Follow Trading screens no longer jump down after a
slide_from_rightpush.Cause: Native
SafeAreaViewtop padding was re-measured after attach, so title/back briefly rendered aty=0then dropped under the notch.Change: Drop the top
SafeAreaViewedge on SocialTradersTabsView, TopTradersView (standalone), and TraderProfileView, and passincludesTopInsetonHeaderStandardAnimatedso top spacing comes from JSmarginTopvia the provider. TraderPositionView usesuseSafeAreaInsets()and a singlepaddingTopwrapper around loading/failed/animated headers so the back button stays fixed when state changes.Reviewed by Cursor Bugbot for commit aee6309. Bugbot is set up for automated code reviews on this repo. Configure here.