Fix verified badge wrapping over the handle in the profile header
The profile header rendered `ProfileBadges` as an inline `View` inside the display name `Text`. On Android the space reserved for an inline view is converted with `PixelUtil.toPixelFromSP`, so the placeholder box is scaled by the device font scale on top of the scaling `ProfileBadges` already applies itself via `useNativeFontScale`. The reserved box therefore ends up larger than the badge it stands in for, which pushes the badge onto a line of its own and paints it below the name, over the start of the handle. The same conversion places the badge at `baseline - reservedHeight`, so it also floats above the text baseline. Render the badges as a sibling of the name in a baseline-aligned row instead, which is the pattern used everywhere else badges follow a name. Baseline alignment keeps the badge bottom on the name's baseline, matching how the inline view was positioned, and drops the iOS-only `marginTop` nudge that was compensating for the inline layout. Fixes #11238 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0184o34rCfWuqtNWCJ9NeTrH
This commit is contained in:
@@ -4,7 +4,7 @@ import {type AppBskyActorDefs, type ModerationDecision} from '@atproto/api'
|
||||
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||
import {type Shadow} from '#/state/cache/types'
|
||||
import {atoms as a, platform, useBreakpoints, useTheme} from '#/alf'
|
||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||
import {ProfileBadges} from '#/components/ProfileBadges'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
||||
@@ -18,15 +18,24 @@ export function ProfileHeaderDisplayName({
|
||||
const t = useTheme()
|
||||
const {gtMobile} = useBreakpoints()
|
||||
|
||||
/*
|
||||
* The badges are a sibling of the name rather than a child of it, because an
|
||||
* inline view inside a `Text` is measured in SP on Android: the space
|
||||
* reserved for it is multiplied by the device font scale a second time (once
|
||||
* by `ProfileBadges` itself, then again by the platform), so the badge
|
||||
* over-reserves, wraps onto a line of its own and gets painted over the
|
||||
* handle below. Baseline alignment keeps the badge sitting on the name's
|
||||
* baseline the way an inline view would.
|
||||
*/
|
||||
return (
|
||||
<View>
|
||||
<View style={[a.flex_row, a.align_baseline, a.gap_xs]}>
|
||||
<Text
|
||||
emoji
|
||||
testID="profileHeaderDisplayName"
|
||||
style={[
|
||||
t.atoms.text,
|
||||
gtMobile ? a.text_4xl : a.text_3xl,
|
||||
a.self_start,
|
||||
a.flex_shrink,
|
||||
a.font_bold,
|
||||
a.leading_tight,
|
||||
]}>
|
||||
@@ -34,10 +43,8 @@ export function ProfileHeaderDisplayName({
|
||||
profile.displayName || sanitizeHandle(profile.handle),
|
||||
moderation.ui('displayName'),
|
||||
)}
|
||||
<View style={[a.pl_xs, {marginTop: platform({ios: 2})}]}>
|
||||
<ProfileBadges profile={profile} size="lg" interactive />
|
||||
</View>
|
||||
</Text>
|
||||
<ProfileBadges profile={profile} size="lg" interactive />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user