diff --git a/src/components/Pills.tsx b/src/components/Pills.tsx
index 1f3bb2b97..a0aef366b 100644
--- a/src/components/Pills.tsx
+++ b/src/components/Pills.tsx
@@ -1,6 +1,7 @@
import React from 'react'
import {View} from 'react-native'
import {BSKY_LABELER_DID, type ModerationCause} from '@atproto/api'
+import {Trans} from '@lingui/react/macro'
import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription'
import {useEnableSquareButtons} from '#/state/preferences/enable-square-buttons'
@@ -152,3 +153,30 @@ export function Label({
>
)
}
+
+export function FollowsYou({size = 'sm'}: CommonProps) {
+ const t = useTheme()
+
+ const variantStyles = React.useMemo(() => {
+ switch (size) {
+ case 'sm':
+ case 'lg':
+ default:
+ return [
+ {
+ paddingHorizontal: 6,
+ paddingVertical: 3,
+ borderRadius: 4,
+ },
+ ]
+ }
+ }, [size])
+
+ return (
+
+
+ Follows You
+
+
+ )
+}
diff --git a/src/components/ProfileCard.tsx b/src/components/ProfileCard.tsx
index 10369124c..9eb1a7bde 100644
--- a/src/components/ProfileCard.tsx
+++ b/src/components/ProfileCard.tsx
@@ -20,6 +20,7 @@ import {NON_BREAKING_SPACE} from '#/lib/strings/constants'
import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {sanitizeHandle} from '#/lib/strings/handles'
import {useProfileShadow} from '#/state/cache/profile-shadow'
+import {useShowFollowsYouBadge} from '#/state/preferences/show-follows-you-badge'
import {useProfileFollowMutationQueue} from '#/state/queries/profile'
import {useSession} from '#/state/session'
import * as Toast from '#/view/com/util/Toast'
@@ -647,13 +648,15 @@ export function Labels({
const moderation = moderateProfile(profile, moderationOpts)
const modui = moderation.ui('profileList')
const followedBy = profile.viewer?.followedBy
+ const showFollowsYouBadge = useShowFollowsYouBadge()
- if (!followedBy && !modui.inform && !modui.alert) {
+ if (!(followedBy && showFollowsYouBadge) && !modui.inform && !modui.alert) {
return null
}
return (
+ {followedBy && showFollowsYouBadge && }
{modui.alerts.map(alert => (
))}
diff --git a/src/screens/Profile/Header/Handle.tsx b/src/screens/Profile/Header/Handle.tsx
index 1d124e978..967327b5d 100644
--- a/src/screens/Profile/Header/Handle.tsx
+++ b/src/screens/Profile/Header/Handle.tsx
@@ -2,10 +2,12 @@ import {View} from 'react-native'
import {type AppBskyActorDefs} from '@atproto/api'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
+import {Trans} from '@lingui/react/macro'
import {isInvalidHandle, sanitizeHandle} from '#/lib/strings/handles'
import {sanitizePronouns} from '#/lib/strings/pronouns'
import {type Shadow} from '#/state/cache/types'
+import {useShowFollowsYouBadge} from '#/state/preferences/show-follows-you-badge'
import {useShowLinkInHandle} from '#/state/preferences/show-link-in-handle.tsx'
import {atoms as a, useTheme, web} from '#/alf'
import {InlineLinkText} from '#/components/Link.tsx'
@@ -24,7 +26,9 @@ export function ProfileHeaderHandle({
const {_} = useLingui()
const invalidHandle = isInvalidHandle(profile.handle)
const pronouns = profile.pronouns
+ const blockHide = profile.viewer?.blocking || profile.viewer?.blockedBy
const isBskySocialHandle = profile.handle.endsWith('.bsky.social')
+ const showFollowsYouBadge = useShowFollowsYouBadge()
const showProfileInHandle = useShowLinkInHandle()
const sanitized = sanitizeHandle(
profile.handle,
@@ -37,6 +41,13 @@ export function ProfileHeaderHandle({
style={[a.flex_row, a.gap_sm, a.align_center, {maxWidth: '100%'}]}
pointerEvents={disableTaps ? 'none' : IS_IOS ? 'auto' : 'box-none'}>
+ {showFollowsYouBadge && profile.viewer?.followedBy && !blockHide ? (
+
+
+ Follows you
+
+
+ ) : undefined}
+ setShowFollowsYouBadge(value)}
+ style={[a.w_full]}>
+
+ Show "Follows you" badge
+
+
+
+
setDisablePostsMetrics(value)}
style={[a.w_full]}>
- Disable posts metrics
+ Disable post counts metrics
diff --git a/src/screens/Signup/StepInfo/index.tsx b/src/screens/Signup/StepInfo/index.tsx
index 4324d7148..9d1bd8689 100644
--- a/src/screens/Signup/StepInfo/index.tsx
+++ b/src/screens/Signup/StepInfo/index.tsx
@@ -33,16 +33,12 @@ import {ScreenTransition} from '#/components/ScreenTransition'
import * as Toast from '#/components/Toast'
import {Text} from '#/components/Typography'
import {
- isUnderAge,
MIN_ACCESS_AGE,
useAgeAssuranceRegionConfigWithFallback,
} from '#/ageAssurance/util'
import {useAnalytics} from '#/analytics'
import {IS_NATIVE, IS_WEB} from '#/env'
-import {
- useDeviceGeolocationApi,
- useIsDeviceGeolocationGranted,
-} from '#/geolocation'
+import {useDeviceGeolocationApi} from '#/geolocation'
import {BackNextButtons} from '../BackNextButtons'
function sanitizeDate(date: Date): Date {
diff --git a/src/state/persisted/schema.ts b/src/state/persisted/schema.ts
index 7aee18bfb..b4a1e0c47 100644
--- a/src/state/persisted/schema.ts
+++ b/src/state/persisted/schema.ts
@@ -161,6 +161,7 @@ const schema = z.object({
disableFollowingMetrics: z.boolean().optional(),
disableFollowedByMetrics: z.boolean().optional(),
disablePostsMetrics: z.boolean().optional(),
+ showFollowsYouBadge: z.boolean().optional(),
hideSimilarAccountsRecomm: z.boolean().optional(),
discoverContextEnabled: z.boolean().optional(),
enableSquareAvatars: z.boolean().optional(),
@@ -278,6 +279,7 @@ export const defaults: Schema = {
disableFollowingMetrics: false,
disableFollowedByMetrics: false,
disablePostsMetrics: false,
+ showFollowsYouBadge: false,
hideSimilarAccountsRecomm: true,
discoverContextEnabled: false,
enableSquareAvatars: true,
diff --git a/src/state/preferences/index.tsx b/src/state/preferences/index.tsx
index 54e64fb77..3a4e3e8b1 100644
--- a/src/state/preferences/index.tsx
+++ b/src/state/preferences/index.tsx
@@ -41,6 +41,7 @@ import {Provider as OpenRouterProvider} from './openrouter'
import {Provider as PdsLabelProvider} from './pds-label'
import {Provider as PostNameReplacementProvider} from './post-name-replacement.tsx'
import {Provider as RepostCarouselProvider} from './repost-carousel-enabled'
+import {Provider as ShowFollowsYouBadgeProvider} from './show-follows-you-badge'
import {Provider as ShowLinkInHandleProvider} from './show-link-in-handle'
import {Provider as SubtitlesProvider} from './subtitles'
import {Provider as TranslationServicePreferenceProvider} from './translation-service-preference'
@@ -127,29 +128,31 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
-
-
-
-
-
-
-
-
-
-
- {
- children
- }
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+ {
+ children
+ }
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/state/preferences/show-follows-you-badge.tsx b/src/state/preferences/show-follows-you-badge.tsx
new file mode 100644
index 000000000..e1cea6c7c
--- /dev/null
+++ b/src/state/preferences/show-follows-you-badge.tsx
@@ -0,0 +1,50 @@
+import React from 'react'
+
+import * as persisted from '#/state/persisted'
+
+type StateContext = persisted.Schema['showFollowsYouBadge']
+type SetContext = (v: persisted.Schema['showFollowsYouBadge']) => void
+
+const stateContext = React.createContext(
+ persisted.defaults.showFollowsYouBadge,
+)
+const setContext = React.createContext(
+ (_: persisted.Schema['showFollowsYouBadge']) => {},
+)
+
+export function Provider({children}: React.PropsWithChildren<{}>) {
+ const [state, setState] = React.useState(persisted.get('showFollowsYouBadge'))
+
+ const setStateWrapped = React.useCallback(
+ (showFollowsYouBadge: persisted.Schema['showFollowsYouBadge']) => {
+ setState(showFollowsYouBadge)
+ persisted.write('showFollowsYouBadge', showFollowsYouBadge)
+ },
+ [setState],
+ )
+
+ React.useEffect(() => {
+ return persisted.onUpdate(
+ 'showFollowsYouBadge',
+ nextShowFollowsYouBadge => {
+ setState(nextShowFollowsYouBadge)
+ },
+ )
+ }, [setStateWrapped])
+
+ return (
+
+
+ {children}
+
+
+ )
+}
+
+export function useShowFollowsYouBadge() {
+ return React.useContext(stateContext)
+}
+
+export function useSetShowFollowsYouBadge() {
+ return React.useContext(setContext)
+}
diff --git a/src/view/com/posts/PostFeedItemCarousel.tsx b/src/view/com/posts/PostFeedItemCarousel.tsx
index a6ba100ba..0cf2cd0a7 100644
--- a/src/view/com/posts/PostFeedItemCarousel.tsx
+++ b/src/view/com/posts/PostFeedItemCarousel.tsx
@@ -1,7 +1,8 @@
import React from 'react'
import {Dimensions, ScrollView, View} from 'react-native'
-import {msg, Plural} from '@lingui/macro'
+import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
+import {Plural} from '@lingui/react/macro'
import {useEnableSquareButtons} from '#/state/preferences/enable-square-buttons'
import {type FeedPostSlice} from '#/state/queries/post-feed'
diff --git a/src/view/screens/PostThread.tsx b/src/view/screens/PostThread.tsx
index d2ed42bf3..d03d0ac96 100644
--- a/src/view/screens/PostThread.tsx
+++ b/src/view/screens/PostThread.tsx
@@ -1,5 +1,5 @@
import {useCallback} from 'react'
-import {msg} from '@lingui/macro'
+import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {useFocusEffect} from '@react-navigation/native'