diff --git a/src/components/ProfileBadges.tsx b/src/components/ProfileBadges.tsx
index 1c376ca57..b9f63084e 100644
--- a/src/components/ProfileBadges.tsx
+++ b/src/components/ProfileBadges.tsx
@@ -63,6 +63,8 @@ type ProfileBadgesProps = ViewStyleProp & {
profile: bsky.profile.AnyProfileView
interactive?: boolean
pdsInteractive?: boolean
+ /** The beta badge is reserved for the full profile header. */
+ showBetaBadge?: boolean
size: Size
allowFontScaling?: boolean
}
@@ -80,6 +82,7 @@ export function ProfileBadgesFromProfileShadow({
profile,
interactive = false,
pdsInteractive = true,
+ showBetaBadge = false,
size,
style,
allowFontScaling = true,
@@ -110,7 +113,7 @@ export function ProfileBadgesFromProfileShadow({
shouldResolvePds &&
(isPdsLoading || (!!pdsData && !(hideBskyPds && pdsData.isBsky)))
- const isBetaBadgeVisible = useIsBetaBadgeVisible(shadowed)
+ const isBetaBadgeVisible = useIsBetaBadgeVisible(shadowed) && showBetaBadge
const badgeVisibility = [
verification.showBadge,
isBetaBadgeVisible,
@@ -173,12 +176,14 @@ export function ProfileBadgesFromProfileShadow({
width={verificationIconWidth}
hitSlop={hitSlops[0]}
/>
-
+ {showBetaBadge ? (
+
+ ) : null}
) : null}
-
+ {showBetaBadge ? (
+
+ ) : null}
>
diff --git a/src/screens/Profile/Header/DisplayName.tsx b/src/screens/Profile/Header/DisplayName.tsx
index 513be7395..130a26363 100644
--- a/src/screens/Profile/Header/DisplayName.tsx
+++ b/src/screens/Profile/Header/DisplayName.tsx
@@ -54,7 +54,12 @@ export function ProfileHeaderDisplayName({
primaryName
)}
-
+
{/*
* TODO: Workaround for a rounding bug in Android RN.
diff --git a/src/view/com/notifications/NotificationFeedItem.tsx b/src/view/com/notifications/NotificationFeedItem.tsx
index bf1c8b6b2..55390eb66 100644
--- a/src/view/com/notifications/NotificationFeedItem.tsx
+++ b/src/view/com/notifications/NotificationFeedItem.tsx
@@ -272,23 +272,45 @@ let NotificationFeedItem = ({
)
}
- const firstAuthorLink = (
-
-
- {forceLTR(firstAuthorName)}
+ /*
+ * A View nested in native Text is laid out as an attachment and inflates the
+ * line box. Keep badges inline on web, but make them a sibling on native.
+ */
+ const firstAuthorNameLink = (
+
+ {forceLTR(firstAuthorName)}
+ {IS_WEB ? (
-
+ ) : null}
+
+ )
+ const firstAuthorLink = IS_WEB ? (
+
+ {firstAuthorNameLink}
+ ) : (
+
+
+ {firstAuthorNameLink}
+
+
+
)
const additionalAuthorsCount = authors.length - 1
const hasMultipleAuthors = additionalAuthorsCount > 0
@@ -380,7 +402,7 @@ let NotificationFeedItem = ({
/*
* Follow-backs are ungrouped, grouped follow-backs not supported atm,
* see `src/state/queries/notifications/util.ts`
- */
+ */
a11yLabel = starterPackName
? _(msg`${firstAuthorName} followed you back via starter pack ${starterPackName}`)
: _(msg`${firstAuthorName} followed you back`)
@@ -478,7 +500,7 @@ let NotificationFeedItem = ({
)
icon = (
-
+
)
} else if (item.type === 'verified') {
@@ -824,8 +846,14 @@ function FollowedViaStarterPack({
via starter pack{' '}
+ )
+
+ if (!IS_WEB) {
+ groupConjunctionWithAuthorCount(inlineChildren)
+
+ const lastChild = inlineChildren.at(-1)
+ if (
+ isValidElement<{children?: React.ReactNode}>(lastChild) &&
+ lastChild.type === Text
+ ) {
+ const lastContent =
+ typeof lastChild.props.children === 'string'
+ ? lastChild.props.children.trimStart()
+ : lastChild.props.children
+ inlineChildren[inlineChildren.length - 1] = cloneElement(
+ lastChild,
+ undefined,
+ lastContent,
+ timestampElement,
+ )
+ } else {
+ inlineChildren.push(timestampElement)
+ }
+ } else {
+ inlineChildren.push(timestampElement)
+ }
+
return (
- {renderInlineTransChildren(children, t)}
-
+ {inlineChildren}
)
}
+function groupConjunctionWithAuthorCount(children: React.ReactNode[]): void {
+ const authorCountIndex = children.findIndex(isSemiboldTextElement)
+ if (authorCountIndex < 1) return
+
+ const conjunction = children[authorCountIndex - 1]
+ const authorCount = children[authorCountIndex]
+ if (
+ !isValidElement<{children?: React.ReactNode}>(conjunction) ||
+ conjunction.type !== Text
+ ) {
+ return
+ }
+
+ children.splice(
+ authorCountIndex - 1,
+ 2,
+ cloneElement(
+ conjunction,
+ undefined,
+ typeof conjunction.props.children === 'string'
+ ? conjunction.props.children.trimStart()
+ : conjunction.props.children,
+ authorCount,
+ ' ',
+ ),
+ )
+}
+
+function isSemiboldTextElement(child: React.ReactNode): boolean {
+ if (
+ !isValidElement<{
+ style?: React.ComponentProps['style']
+ }>(child) ||
+ child.type !== Text
+ ) {
+ return false
+ }
+ const style = StyleSheet.flatten(child.props.style)
+ const semiboldStyle = StyleSheet.flatten(a.font_semi_bold)
+ return style?.fontWeight === semiboldStyle.fontWeight
+}
+
function renderInlineTransChildren(
children: React.ReactNode,
t: ReturnType,
): React.ReactNode {
return Children.map(children, (child, i) => {
if (typeof child === 'string') {
- if (!child) {
+ if (!child || (!IS_WEB && child.trim().length === 0)) {
return null
}
return (