From 8d7a7369aa868babb112edcbb2ec6530f47d3462 Mon Sep 17 00:00:00 2001
From: DS Boyce <260543580+ds-boyce@users.noreply.github.com>
Date: Wed, 22 Jul 2026 15:06:08 -0700
Subject: [PATCH] Display beta badge when isBetaUser is true (#11160)
---
src/components/BetaBadge.tsx | 104 ++++++++++++++++++
src/components/BotBadge.tsx | 6 +-
src/components/ProfileBadges.tsx | 72 ++++++++++--
.../verification/VerificationCheckButton.tsx | 28 +++--
4 files changed, 187 insertions(+), 23 deletions(-)
create mode 100644 src/components/BetaBadge.tsx
diff --git a/src/components/BetaBadge.tsx b/src/components/BetaBadge.tsx
new file mode 100644
index 0000000000..115f8d0046
--- /dev/null
+++ b/src/components/BetaBadge.tsx
@@ -0,0 +1,104 @@
+import {useState} from 'react'
+import {type Insets, Pressable, View} from 'react-native'
+import {Trans, useLingui} from '@lingui/react/macro'
+
+import {usePreferencesQuery} from '#/state/queries/preferences'
+import {useSession} from '#/state/session'
+import {atoms as a, useTheme} from '#/alf'
+import {Beaker_Stroke2_Corner2_Rounded as BeakerIcon} from '#/components/icons/Beaker'
+import * as Tooltip from '#/components/Tooltip'
+import type * as bsky from '#/types/bsky'
+
+/**
+ * Whether to show the beta badge for a given profile. Only shown on the
+ * viewer's own profile, and only when the viewer has opted in to beta features.
+ */
+export function useIsBetaBadgeVisible(
+ profile: bsky.profile.AnyProfileView,
+): boolean {
+ const {currentAccount} = useSession()
+ const {data: preferences} = usePreferencesQuery()
+ const isBetaUser = preferences?.bskyAppState?.isBetaUser ?? false
+ const isSelf = currentAccount?.did === profile.did
+
+ return isSelf && isBetaUser
+}
+
+export function BetaBadge({
+ profile,
+ width,
+ padding,
+}: {
+ profile: bsky.profile.AnyProfileView
+ width: number
+ padding: number
+}) {
+ const t = useTheme()
+ const isVisible = useIsBetaBadgeVisible(profile)
+
+ if (!isVisible) return null
+
+ return (
+
+
+
+ )
+}
+
+export function BetaBadgeButton({
+ profile,
+ width,
+ padding,
+ hitSlop,
+}: {
+ profile: bsky.profile.AnyProfileView
+ width: number
+ padding: number
+ hitSlop: Insets
+}) {
+ const t = useTheme()
+ const {t: l} = useLingui()
+ const isVisible = useIsBetaBadgeVisible(profile)
+
+ const [tooltipVisible, setTooltipVisible] = useState(false)
+
+ if (!isVisible) return null
+
+ return (
+
+
+ [
+ a.rounded_full,
+ a.transition_transform,
+ {
+ backgroundColor: t.palette.primary_50,
+ padding,
+ transform: [
+ {
+ scale: hovered ? 1.1 : 1,
+ },
+ ],
+ },
+ ]}
+ onPress={() => setTooltipVisible(v => !v)}>
+
+
+
+
+ Beta features enabled
+
+
+ )
+}
diff --git a/src/components/BotBadge.tsx b/src/components/BotBadge.tsx
index 83da49bc4e..b5e6f867b6 100644
--- a/src/components/BotBadge.tsx
+++ b/src/components/BotBadge.tsx
@@ -1,4 +1,4 @@
-import {View} from 'react-native'
+import {type Insets, View} from 'react-native'
import {type ComAtprotoLabelDefs} from '@atproto/api'
import {useLingui} from '@lingui/react/macro'
@@ -44,9 +44,11 @@ export function BotBadge({
export function BotBadgeButton({
profile,
width,
+ hitSlop,
}: {
profile: bsky.profile.AnyProfileView
width: number
+ hitSlop: Insets
}) {
const t = useTheme()
const ax = useAnalytics()
@@ -61,7 +63,7 @@ export function BotBadgeButton({
<>
-
-