diff --git a/assets/icons/bot_filled.svg b/assets/icons/bot_filled.svg
new file mode 100644
index 0000000000..207d13295b
--- /dev/null
+++ b/assets/icons/bot_filled.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/bot_stroke.svg b/assets/icons/bot_stroke.svg
new file mode 100644
index 0000000000..707d695595
--- /dev/null
+++ b/assets/icons/bot_stroke.svg
@@ -0,0 +1 @@
+
diff --git a/bskyembed/src/components/post.tsx b/bskyembed/src/components/post.tsx
index be3350485d..fb32250464 100644
--- a/bskyembed/src/components/post.tsx
+++ b/bskyembed/src/components/post.tsx
@@ -10,6 +10,7 @@ import logo from '../../assets/logo_full_name.svg'
import {Like as LikeIcon} from '../icons/Like'
import {Reply as ReplyIcon} from '../icons/Reply'
import {Repost as RepostIcon} from '../icons/Repost'
+import {Robot as RobotIcon} from '../icons/Robot'
import {CONTENT_LABELS} from '../labels'
import * as bsky from '../types/bsky'
import {niceDate} from '../util/nice-date'
@@ -43,6 +44,9 @@ export function Post({thread}: Props) {
}
const verification = getVerificationState({profile: post.author})
+ const isBot = post.author.labels?.some(
+ l => l.val === 'bot' && l.src === post.author.did,
+ )
const href = `/profile/${post.author.did}/post/${getRkey(post)}`
@@ -76,6 +80,12 @@ export function Post({thread}: Props) {
size={15}
/>
)}
+ {isBot && (
+
+ )}
(
+
+)
diff --git a/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go
index 1f9c0ed649..9c10827a8b 100644
--- a/bskyweb/cmd/bskyweb/server.go
+++ b/bskyweb/cmd/bskyweb/server.go
@@ -292,6 +292,7 @@ func serve(cctx *cli.Context) error {
e.GET("/settings/accessibility", server.WebGeneric)
e.GET("/settings/appearance", server.WebGeneric)
e.GET("/settings/account", server.WebGeneric)
+ e.GET("/settings/automation-label", server.WebGeneric)
e.GET("/settings/privacy-and-security", server.WebGeneric)
e.GET("/settings/privacy-and-security/activity", server.WebGeneric)
e.GET("/settings/content-and-media", server.WebGeneric)
diff --git a/src/Navigation.tsx b/src/Navigation.tsx
index 03ff43d739..7ef150e9e5 100644
--- a/src/Navigation.tsx
+++ b/src/Navigation.tsx
@@ -103,6 +103,7 @@ import {ActivityPrivacySettingsScreen} from '#/screens/Settings/ActivityPrivacyS
import {AppearanceSettingsScreen} from '#/screens/Settings/AppearanceSettings'
import {AppIconSettingsScreen} from '#/screens/Settings/AppIconSettings'
import {AppPasswordsScreen} from '#/screens/Settings/AppPasswords'
+import {AutomationLabelSettingsScreen} from '#/screens/Settings/AutomationLabelSettings'
import {ContentAndMediaSettingsScreen} from '#/screens/Settings/ContentAndMediaSettings'
import {ExternalMediaPreferencesScreen} from '#/screens/Settings/ExternalMediaPreferences'
import {FindContactsSettingsScreen} from '#/screens/Settings/FindContactsSettings'
@@ -403,6 +404,14 @@ function commonScreens(Stack: typeof Flat, unreadCountLabel?: string) {
requireAuth: true,
}}
/>
+ AutomationLabelSettingsScreen}
+ options={{
+ title: title(msg`Automation Label`),
+ requireAuth: true,
+ }}
+ />
PrivacyAndSecuritySettingsScreen}
diff --git a/src/analytics/metrics/types.ts b/src/analytics/metrics/types.ts
index 28c3150c26..d3d5d88f92 100644
--- a/src/analytics/metrics/types.ts
+++ b/src/analytics/metrics/types.ts
@@ -732,6 +732,9 @@ export type Events = {
'verification:settings:hideBadges': {}
'verification:settings:unHideBadges': {}
+ 'bot:label:toggle': {state: 'add' | 'remove'}
+ 'bot:badge:click': {}
+
'live:create': {duration: number}
'live:edit': {}
'live:remove': {}
diff --git a/src/components/AccountList.tsx b/src/components/AccountList.tsx
index ad0f44809d..4a43bf283c 100644
--- a/src/components/AccountList.tsx
+++ b/src/components/AccountList.tsx
@@ -16,9 +16,8 @@ import {Button} from '#/components/Button'
import {CheckThick_Stroke2_Corner0_Rounded as CheckIcon} from '#/components/icons/Check'
import {ChevronRight_Stroke2_Corner0_Rounded as ChevronIcon} from '#/components/icons/Chevron'
import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/Plus'
+import {ProfileBadges} from '#/components/ProfileBadges'
import {Text} from '#/components/Typography'
-import {useSimpleVerificationState} from '#/components/verification'
-import {VerificationCheck} from '#/components/verification/VerificationCheck'
import {useActorStatus} from '#/features/liveNow'
export function AccountList({
@@ -116,7 +115,6 @@ function AccountItem({
}) {
const t = useTheme()
const {_} = useLingui()
- const verification = useSimpleVerificationState({profile})
const {isActive: live} = useActorStatus(profile)
const onPress = useCallback(() => {
@@ -164,13 +162,12 @@ function AccountItem({
profile?.displayName || profile?.handle || account.handle,
)}
- {verification.showBadge && (
-
-
-
+ {profile && (
+
)}
+
+
+
+
+
+
+ {description}
+
+
+
+
+ {isSelf ? (
+
+ ) : null}
+
+
+
+ )
+}
diff --git a/src/components/BotBadge.tsx b/src/components/BotBadge.tsx
new file mode 100644
index 0000000000..83da49bc4e
--- /dev/null
+++ b/src/components/BotBadge.tsx
@@ -0,0 +1,92 @@
+import {View} from 'react-native'
+import {type ComAtprotoLabelDefs} from '@atproto/api'
+import {useLingui} from '@lingui/react/macro'
+
+import {atoms as a, useTheme} from '#/alf'
+import {BotAccountAlert} from '#/components/BotAccountAlert'
+import {Button} from '#/components/Button'
+import {useDialogControl} from '#/components/Dialog'
+import {Bot_Filled as RobotIcon} from '#/components/icons/Bot'
+import {useAnalytics} from '#/analytics'
+import type * as bsky from '#/types/bsky'
+
+export function isBotAccount(profile: {
+ did: string
+ labels?: ComAtprotoLabelDefs.Label[]
+}): boolean {
+ return (
+ profile.labels?.some(l => l.val === 'bot' && l.src === profile.did) ?? false
+ )
+}
+
+export function BotBadge({
+ profile,
+ alwaysShow = false,
+ width,
+}: {
+ profile: bsky.profile.AnyProfileView
+ alwaysShow?: boolean
+ width: number
+}) {
+ const t = useTheme()
+
+ if (!isBotAccount(profile) && !alwaysShow) {
+ return null
+ }
+
+ return (
+
+
+
+ )
+}
+
+export function BotBadgeButton({
+ profile,
+ width,
+}: {
+ profile: bsky.profile.AnyProfileView
+ width: number
+}) {
+ const t = useTheme()
+ const ax = useAnalytics()
+ const {t: l} = useLingui()
+ const control = useDialogControl()
+
+ if (!isBotAccount(profile)) {
+ return null
+ }
+
+ return (
+ <>
+
+
+ >
+ )
+}
diff --git a/src/components/PostControls/ShareMenu/RecentChats.tsx b/src/components/PostControls/ShareMenu/RecentChats.tsx
index 3ba2682239..178f8c0baa 100644
--- a/src/components/PostControls/ShareMenu/RecentChats.tsx
+++ b/src/components/PostControls/ShareMenu/RecentChats.tsx
@@ -17,9 +17,8 @@ import {UserAvatar} from '#/view/com/util/UserAvatar'
import {atoms as a, tokens, useTheme} from '#/alf'
import {Button} from '#/components/Button'
import {useDialogContext} from '#/components/Dialog'
+import {ProfileBadges} from '#/components/ProfileBadges'
import {Text} from '#/components/Typography'
-import {useSimpleVerificationState} from '#/components/verification'
-import {VerificationCheck} from '#/components/verification/VerificationCheck'
import {useAnalytics} from '#/analytics'
import type * as bsky from '#/types/bsky'
@@ -111,7 +110,6 @@ function RecentChatItem({
profile.displayName || sanitizeHandle(profile.handle),
moderation.ui('displayName'),
)
- const verification = useSimpleVerificationState({profile})
if (isBlockedOrBlocking(profile) || isMuted(profile)) {
return null
@@ -141,14 +139,7 @@ function RecentChatItem({
numberOfLines={1}>
{name}
- {verification.showBadge && (
-
-
-
- )}
+
)
diff --git a/src/components/ProfileBadges.tsx b/src/components/ProfileBadges.tsx
new file mode 100644
index 0000000000..dd0cedfe8f
--- /dev/null
+++ b/src/components/ProfileBadges.tsx
@@ -0,0 +1,76 @@
+import {View} from 'react-native'
+
+import {useProfileShadow} from '#/state/cache/profile-shadow'
+import {atoms as a, type ViewStyleProp} from '#/alf'
+import {BotBadge, BotBadgeButton, isBotAccount} from '#/components/BotBadge'
+import {useSimpleVerificationState} from '#/components/verification'
+import {VerificationCheck} from '#/components/verification/VerificationCheck'
+import {VerificationCheckButton} from '#/components/verification/VerificationCheckButton'
+import type * as bsky from '#/types/bsky'
+
+export type Size = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
+
+const verificationIconSizes: Record = {
+ xs: 10,
+ sm: 12,
+ md: 14,
+ lg: 18,
+ xl: 22,
+} as const
+
+const botIconSizes: Record = {
+ xs: 11,
+ sm: 13,
+ md: 15,
+ lg: 19,
+ xl: 23,
+} as const
+
+export function ProfileBadges({
+ profile,
+ interactive = false,
+ size,
+ style,
+}: ViewStyleProp & {
+ profile: bsky.profile.AnyProfileView
+ interactive?: boolean
+ size: Size
+}) {
+ const shadowed = useProfileShadow(profile)
+ const verification = useSimpleVerificationState({profile})
+
+ // if nothing to show, don't render the container at all
+ if (!verification.showBadge && !isBotAccount(shadowed)) return null
+
+ const isOnTheSmallSide = size === 'xs' || size === 'sm'
+
+ return (
+
+ {interactive ? (
+ <>
+
+
+ >
+ ) : (
+ <>
+ {verification.showBadge && (
+
+ )}
+
+ >
+ )}
+
+ )
+}
diff --git a/src/components/ProfileCard.tsx b/src/components/ProfileCard.tsx
index 0c8f26f200..ee9e657028 100644
--- a/src/components/ProfileCard.tsx
+++ b/src/components/ProfileCard.tsx
@@ -41,10 +41,9 @@ import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
import {Link as InternalLink, type LinkProps} from '#/components/Link'
import * as Pills from '#/components/Pills'
+import {ProfileBadges} from '#/components/ProfileBadges'
import {RichText} from '#/components/RichText'
import {Text} from '#/components/Typography'
-import {useSimpleVerificationState} from '#/components/verification'
-import {VerificationCheck} from '#/components/verification/VerificationCheck'
import {type Metrics} from '#/analytics'
import {useActorStatus} from '#/features/liveNow'
import type * as bsky from '#/types/bsky'
@@ -242,7 +241,6 @@ function InlineNameAndHandle({
moderationOpts: ModerationOpts
}) {
const t = useTheme()
- const verification = useSimpleVerificationState({profile})
const moderation = moderateProfile(profile, moderationOpts)
const name = sanitizeDisplayName(
profile.displayName || sanitizeHandle(profile.handle),
@@ -262,19 +260,15 @@ function InlineNameAndHandle({
numberOfLines={1}>
{forceLTR(name)}
- {verification.showBadge && (
-
-
-
- )}
+
{name}
- {verification.showBadge && (
-
-
-
- )}
+
)
}
diff --git a/src/components/ProfileHoverCard/index.web.tsx b/src/components/ProfileHoverCard/index.web.tsx
index 8586607e52..6ccdf89ae4 100644
--- a/src/components/ProfileHoverCard/index.web.tsx
+++ b/src/components/ProfileHoverCard/index.web.tsx
@@ -36,10 +36,9 @@ import {InlineLinkText, Link} from '#/components/Link'
import {Loader} from '#/components/Loader'
import * as Pills from '#/components/Pills'
import {Portal} from '#/components/Portal'
+import {ProfileBadges} from '#/components/ProfileBadges'
import {RichText} from '#/components/RichText'
import {Text} from '#/components/Typography'
-import {useSimpleVerificationState} from '#/components/verification'
-import {VerificationCheck} from '#/components/verification/VerificationCheck'
import {IS_WEB_TOUCH_DEVICE} from '#/env'
import {useActorStatus} from '#/features/liveNow'
import {LiveStatus} from '#/features/liveNow/components/LiveStatusDialog'
@@ -459,7 +458,6 @@ function Inner({
[currentAccount, profile],
)
const isLabeler = profile.associated?.labeler
- const verification = useSimpleVerificationState({profile})
return (
@@ -527,20 +525,16 @@ function Inner({
moderation.ui('displayName'),
)}
- {verification.showBadge && (
-
-
-
- )}
+
diff --git a/src/components/dms/MessagesListHeader.tsx b/src/components/dms/MessagesListHeader.tsx
index afdeee545e..3f7694a342 100644
--- a/src/components/dms/MessagesListHeader.tsx
+++ b/src/components/dms/MessagesListHeader.tsx
@@ -20,9 +20,8 @@ import {Bell2Off_Filled_Corner0_Rounded as BellStroke} from '#/components/icons/
import * as Layout from '#/components/Layout'
import {Link} from '#/components/Link'
import {PostAlerts} from '#/components/moderation/PostAlerts'
+import {ProfileBadges} from '#/components/ProfileBadges'
import {Text} from '#/components/Typography'
-import {useSimpleVerificationState} from '#/components/verification'
-import {VerificationCheck} from '#/components/verification/VerificationCheck'
import {IS_WEB} from '#/env'
const PFP_SIZE = IS_WEB ? 40 : Layout.HEADER_SLOT_SIZE
@@ -112,9 +111,6 @@ function HeaderReady({
const {_} = useLingui()
const t = useTheme()
const convoState = useConvo()
- const verification = useSimpleVerificationState({
- profile,
- })
const isDeletedAccount = profile?.handle === 'missing.invalid'
const displayName = isDeletedAccount
@@ -161,14 +157,7 @@ function HeaderReady({
numberOfLines={1}>
{displayName}
- {verification.showBadge && (
-
-
-
- )}
+
{!isDeletedAccount && (
!l.val.startsWith('!'))
+ labels = labels.filter(
+ l =>
+ !l.val.startsWith('!') &&
+ !(l.val === 'bot' && l.src === currentAccount.did),
+ )
if (!labels.length) {
return null
}
diff --git a/src/components/verification/VerificationCheckButton.tsx b/src/components/verification/VerificationCheckButton.tsx
index 40b12c2064..ceb9d24f2b 100644
--- a/src/components/verification/VerificationCheckButton.tsx
+++ b/src/components/verification/VerificationCheckButton.tsx
@@ -3,7 +3,7 @@ import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {type Shadow} from '#/state/cache/types'
-import {atoms as a, useBreakpoints, useTheme} from '#/alf'
+import {atoms as a, useTheme} from '#/alf'
import {Button} from '#/components/Button'
import {useDialogControl} from '#/components/Dialog'
import {useFullVerificationState} from '#/components/verification'
@@ -51,43 +51,36 @@ export function shouldShowVerificationCheckButton(
export function VerificationCheckButton({
profile,
- size,
+ width,
}: {
profile: Shadow
- size: 'lg' | 'md' | 'sm'
+ width: number
}) {
const state = useFullVerificationState({
profile,
})
if (shouldShowVerificationCheckButton(state)) {
- return
+ return
}
return null
}
-export function Badge({
+function Badge({
profile,
verificationState: state,
- size,
+ width,
}: {
profile: Shadow
verificationState: FullVerificationState
- size: 'lg' | 'md' | 'sm'
+ width: number
}) {
const t = useTheme()
const ax = useAnalytics()
const {_} = useLingui()
const verificationsDialogControl = useDialogControl()
const verifierDialogControl = useDialogControl()
- const {gtPhone} = useBreakpoints()
- let dimensions = 12
- if (size === 'lg') {
- dimensions = gtPhone ? 20 : 18
- } else if (size === 'md') {
- dimensions = 14
- }
const verifiedByHidden = !state.profile.showBadge && state.profile.isViewer
@@ -116,8 +109,8 @@ export function Badge({
a.align_end,
a.transition_transform,
{
- width: dimensions,
- height: dimensions,
+ width: width,
+ height: width,
transform: [
{
scale: hovered ? 1.1 : 1,
@@ -126,7 +119,7 @@ export function Badge({
},
]}>
({
AppearanceSettings: '/settings/appearance',
SavedFeeds: '/settings/saved-feeds',
AccountSettings: '/settings/account',
+ AutomationLabelSettings: '/settings/automation-label',
PrivacyAndSecuritySettings: '/settings/privacy-and-security',
ActivityPrivacySettings: '/settings/privacy-and-security/activity',
ContentAndMediaSettings: '/settings/content-and-media',
diff --git a/src/screens/Messages/components/ChatListItem.tsx b/src/screens/Messages/components/ChatListItem.tsx
index f09cf23007..82c5b76af6 100644
--- a/src/screens/Messages/components/ChatListItem.tsx
+++ b/src/screens/Messages/components/ChatListItem.tsx
@@ -41,9 +41,8 @@ import {Link} from '#/components/Link'
import {useMenuControl} from '#/components/Menu'
import {PostAlerts} from '#/components/moderation/PostAlerts'
import {createPortalGroup} from '#/components/Portal'
+import {ProfileBadges} from '#/components/ProfileBadges'
import {Text} from '#/components/Typography'
-import {useSimpleVerificationState} from '#/components/verification'
-import {VerificationCheck} from '#/components/verification/VerificationCheck'
import {useAnalytics} from '#/analytics'
import {IS_NATIVE} from '#/env'
import type * as bsky from '#/types/bsky'
@@ -112,9 +111,6 @@ function ChatListItemReady({
const playHaptic = useHaptics()
const queryClient = useQueryClient()
const isUnread = convo.unreadCount > 0
- const verification = useSimpleVerificationState({
- profile,
- })
const blockInfo = useMemo(() => {
const modui = moderation.ui('profileView')
@@ -414,14 +410,11 @@ function ChatListItemReady({
{displayName}
- {verification.showBadge && (
-
-
-
- )}
+
{lastMessageSentAt && (
diff --git a/src/screens/PostThread/components/ThreadItemAnchor.tsx b/src/screens/PostThread/components/ThreadItemAnchor.tsx
index 9133a313cd..f17a832461 100644
--- a/src/screens/PostThread/components/ThreadItemAnchor.tsx
+++ b/src/screens/PostThread/components/ThreadItemAnchor.tsx
@@ -47,12 +47,12 @@ import {Embed, PostEmbedViewContext} from '#/components/Post/Embed'
import {TranslatedPost} from '#/components/Post/Translated'
import {PostControls, PostControlsSkeleton} from '#/components/PostControls'
import {useFormatPostStatCount} from '#/components/PostControls/util'
+import {ProfileBadges} from '#/components/ProfileBadges'
import {ProfileHoverCard} from '#/components/ProfileHoverCard'
import * as Prompt from '#/components/Prompt'
import {RichText} from '#/components/RichText'
import * as Skele from '#/components/Skeleton'
import {Text} from '#/components/Typography'
-import {VerificationCheckButton} from '#/components/verification/VerificationCheckButton'
import {WhoCanReply} from '#/components/WhoCanReply'
import {useAnalytics} from '#/analytics'
import {useActorStatus} from '#/features/liveNow'
@@ -362,7 +362,11 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({
-
+
-
+
diff --git a/src/screens/Search/components/SearchHistory.tsx b/src/screens/Search/components/SearchHistory.tsx
index 5b463ae75d..f49fadf0d5 100644
--- a/src/screens/Search/components/SearchHistory.tsx
+++ b/src/screens/Search/components/SearchHistory.tsx
@@ -14,9 +14,8 @@ import {Button, ButtonIcon} from '#/components/Button'
import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times'
import * as Layout from '#/components/Layout'
import {Link} from '#/components/Link'
+import {ProfileBadges} from '#/components/ProfileBadges'
import {Text} from '#/components/Typography'
-import {useSimpleVerificationState} from '#/components/verification'
-import {VerificationCheck} from '#/components/verification/VerificationCheck'
import {useAnalytics} from '#/analytics'
import type * as bsky from '#/types/bsky'
@@ -139,7 +138,6 @@ function RecentProfileItem({
profile.displayName || sanitizeHandle(profile.handle),
moderation.ui('displayName'),
)
- const verification = useSimpleVerificationState({profile})
return (
@@ -165,14 +163,7 @@ function RecentProfileItem({
{name}
- {verification.showBadge && (
-
-
-
- )}
+
+
+
+
+ Automation label
+
+ {profile && (
+
+ {isBotAccount(profile) ? _(msg`On`) : _(msg`Off`)}
+
+ )}
+
+export function AutomationLabelSettingsScreen({}: Props) {
+ const t = useTheme()
+ const ax = useAnalytics()
+ const {t: l} = useLingui()
+ const queryClient = useQueryClient()
+ const {currentAccount} = useSession()
+ const {data: profile} = useProfileQuery({did: currentAccount?.did})
+ const updateProfile = useProfileUpdateMutation()
+ const verification = useSimpleVerificationState({profile})
+
+ const isBotLabeled =
+ profile?.labels?.some(l => l.val === 'bot' && l.src === profile.did) ??
+ false
+ const canToggle = profile && !updateProfile.isPending
+
+ const onToggle = () => {
+ if (!profile) {
+ return
+ }
+ let wasAdded = false
+ ax.metric('bot:label:toggle', {state: isBotLabeled ? 'remove' : 'add'})
+ updateProfile.mutate(
+ {
+ profile,
+ updates: existing => {
+ const labels: $Typed = bsky.validate(
+ existing.labels,
+ ComAtprotoLabelDefs.validateSelfLabels,
+ )
+ ? existing.labels
+ : {
+ $type: 'com.atproto.label.defs#selfLabels',
+ values: [],
+ }
+
+ const hasLabel = labels.values.some(l => l.val === 'bot')
+ if (hasLabel) {
+ wasAdded = false
+ labels.values = labels.values.filter(l => l.val !== 'bot')
+ } else {
+ wasAdded = true
+ labels.values.push({val: 'bot'})
+ }
+
+ if (labels.values.length === 0) {
+ delete existing.labels
+ } else {
+ existing.labels = labels
+ }
+
+ return existing
+ },
+ checkCommitted: res => {
+ const exists = !!res.data.labels?.some(l => l.val === 'bot')
+ return exists === wasAdded
+ },
+ },
+ {
+ onSuccess() {
+ queryClient.invalidateQueries({queryKey: [POST_FEED_RQKEY_ROOT]})
+ queryClient.invalidateQueries({queryKey: [postThreadQueryKeyRoot]})
+ },
+ },
+ )
+ }
+
+ return (
+
+
+
+
+
+ Automation Label
+
+
+
+
+
+
+ {profile && (
+
+
+
+
+
+
+ {profile.displayName || profile.handle}
+
+ {verification.isVerified && (
+
+ )}
+
+
+
+
+
+
+ @{profile.handle}
+
+
+
+ )}
+
+
+ Add automation label to account
+
+
+
+ This label lets the world know that this account is automated.
+ If turned on, this label appears next to the account's name on
+ their profile and posts. It can be turned on or off at any time.
+
+
+
+
+
+
+
+
+ Show automation label
+
+
+
+
+
+
+ )
+}
diff --git a/src/screens/Settings/Settings.tsx b/src/screens/Settings/Settings.tsx
index 575bd4cce1..c77c77a209 100644
--- a/src/screens/Settings/Settings.tsx
+++ b/src/screens/Settings/Settings.tsx
@@ -61,13 +61,9 @@ import * as Layout from '#/components/Layout'
import {Loader} from '#/components/Loader'
import * as Menu from '#/components/Menu'
import {ID as PolicyUpdate202508} from '#/components/PolicyUpdateOverlay/updates/202508/config'
+import {ProfileBadges} from '#/components/ProfileBadges'
import * as Prompt from '#/components/Prompt'
import {Text} from '#/components/Typography'
-import {useFullVerificationState} from '#/components/verification'
-import {
- shouldShowVerificationCheckButton,
- VerificationCheckButton,
-} from '#/components/verification/VerificationCheckButton'
import {useAnalytics} from '#/analytics'
import {IS_INTERNAL, IS_IOS, IS_NATIVE} from '#/env'
import {useActorStatus} from '#/features/liveNow'
@@ -321,9 +317,6 @@ function ProfilePreview({
const {gtMobile} = useBreakpoints()
const shadow = useProfileShadow(profile)
const moderationOpts = useModerationOpts()
- const verificationState = useFullVerificationState({
- profile: shadow,
- })
const {isActive: live} = useActorStatus(profile)
if (!moderationOpts) return null
@@ -364,16 +357,16 @@ function ProfilePreview({
]}>
{displayName}
- {shouldShowVerificationCheckButton(verificationState) && (
-
-
-
- )}
+
{sanitizeHandle(profile.handle, '@')}
diff --git a/src/view/com/composer/ComposerReplyTo.tsx b/src/view/com/composer/ComposerReplyTo.tsx
index 3757ed7c92..a6ef1a344c 100644
--- a/src/view/com/composer/ComposerReplyTo.tsx
+++ b/src/view/com/composer/ComposerReplyTo.tsx
@@ -16,9 +16,8 @@ import {type ComposerOptsPostRef} from '#/state/shell/composer'
import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar'
import {atoms as a, useTheme, web} from '#/alf'
import {QuoteEmbed} from '#/components/Post/Embed'
+import {ProfileBadges} from '#/components/ProfileBadges'
import {Text} from '#/components/Typography'
-import {useSimpleVerificationState} from '#/components/verification'
-import {VerificationCheck} from '#/components/verification/VerificationCheck'
import {parseEmbed} from '#/types/bsky/post'
export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) {
@@ -70,8 +69,6 @@ export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) {
}
}, [embed])
- const verification = useSimpleVerificationState({profile: replyTo.author})
-
return (
- {verification.showBadge && (
-
-
-
- )}
+
diff --git a/src/view/com/composer/text-input/mobile/Autocomplete.tsx b/src/view/com/composer/text-input/mobile/Autocomplete.tsx
index 1272e98909..9621ebd82c 100644
--- a/src/view/com/composer/text-input/mobile/Autocomplete.tsx
+++ b/src/view/com/composer/text-input/mobile/Autocomplete.tsx
@@ -9,9 +9,8 @@ import {sanitizeHandle} from '#/lib/strings/handles'
import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete'
import {UserAvatar} from '#/view/com/util/UserAvatar'
import {atoms as a, platform, useTheme} from '#/alf'
+import {ProfileBadges} from '#/components/ProfileBadges'
import {Text} from '#/components/Typography'
-import {useSimpleVerificationState} from '#/components/verification'
-import {VerificationCheck} from '#/components/verification/VerificationCheck'
export function Autocomplete({
prefix,
@@ -77,7 +76,6 @@ function AutocompleteProfileCard({
onPress: () => void
}) {
const t = useTheme()
- const state = useSimpleVerificationState({profile})
const displayName = sanitizeDisplayName(
profile.displayName || sanitizeHandle(profile.handle),
)
@@ -115,19 +113,15 @@ function AutocompleteProfileCard({
numberOfLines={1}>
{displayName}
- {state.isVerified && (
-
-
-
- )}
+
{forceLTR(firstAuthorName)}
- {firstAuthorVerification.showBadge && (
-
-
-
- )}
+
)
@@ -1017,9 +1010,6 @@ function ExpandedAuthorsList({
function ExpandedAuthorCard({author}: {author: Author}) {
const t = useTheme()
const {_} = useLingui()
- const verification = useSimpleVerificationState({
- profile: author.profile,
- })
return (
- {verification.showBadge && (
-
-
-
- )}
+
{
}, [queryClient, author])
const timestampLabel = niceDate(i18n, opts.timestamp)
- const verification = useSimpleVerificationState({profile: author})
const {isActive: live} = useActorStatus(author)
const MaybeLinkText = opts.linkDisabled ? Text : WebOnlyInlineLinkText
@@ -109,21 +107,11 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => {
),
)}
- {verification.showBadge && (
-
-
-
- )}
+
{profile?.displayName || account.handle}
- {verification.showBadge && (
-
-
-
- )}
+ {profile && }