diff --git a/src/screens/Settings/PrivacyAndSecuritySettings.tsx b/src/screens/Settings/PrivacyAndSecuritySettings.tsx
index 2979ace213..4ba11fdec9 100644
--- a/src/screens/Settings/PrivacyAndSecuritySettings.tsx
+++ b/src/screens/Settings/PrivacyAndSecuritySettings.tsx
@@ -14,9 +14,11 @@ import * as Admonition from '#/components/Admonition'
import {BellRinging_Stroke2_Corner0_Rounded as BellRingingIcon} from '#/components/icons/BellRinging'
import {EyeSlash_Stroke2_Corner0_Rounded as EyeSlashIcon} from '#/components/icons/EyeSlash'
import {Key_Stroke2_Corner2_Rounded as KeyIcon} from '#/components/icons/Key'
+import {MagnifyingGlass_Stroke2_Corner0_Rounded as MagnifyingGlassIcon} from '#/components/icons/MagnifyingGlass'
import {ShieldCheck_Stroke2_Corner0_Rounded as ShieldIcon} from '#/components/icons/Shield'
import * as Layout from '#/components/Layout'
import {InlineLinkText} from '#/components/Link'
+import {AlgoVisibilityOptOut} from './components/AlgoVisibilityOptOut'
import {Email2FAToggle} from './components/Email2FAToggle'
import {PwiOptOut} from './components/PwiOptOut'
import {ItemTextWithSubtitle} from './NotificationSettings/components/ItemTextWithSubtitle'
@@ -101,13 +103,14 @@ export function PrivacyAndSecuritySettingsScreen({}: Props) {
/>
-
+
+
+
+
+
-
- Logged-out visibility
-
-
+
@@ -115,11 +118,10 @@ export function PrivacyAndSecuritySettingsScreen({}: Props) {
- Note: Bluesky is an open and public network. This setting
- only limits the visibility of your content on the Bluesky
- app and website, and other apps may not respect this
- setting. Your content may still be shown to logged-out
- users by other apps and websites.
+ Note: Bluesky is part of the Atmosphere, an open public
+ network. These settings ask other apps and websites to
+ limit your visibility, but they can choose not to. Your
+ public content may still appear elsewhere.
diff --git a/src/screens/Settings/components/AlgoVisibilityOptOut.tsx b/src/screens/Settings/components/AlgoVisibilityOptOut.tsx
new file mode 100644
index 0000000000..e60f6da636
--- /dev/null
+++ b/src/screens/Settings/components/AlgoVisibilityOptOut.tsx
@@ -0,0 +1,106 @@
+import {useCallback} from 'react'
+import {View} from 'react-native'
+import {type $Typed, ComAtprotoLabelDefs} from '@atproto/api'
+import {msg} from '@lingui/core/macro'
+import {useLingui} from '@lingui/react'
+import {Trans} from '@lingui/react/macro'
+
+import {
+ useProfileQuery,
+ useProfileUpdateMutation,
+} from '#/state/queries/profile'
+import {useSession} from '#/state/session'
+import {atoms as a, useTheme} from '#/alf'
+import * as Toggle from '#/components/forms/Toggle'
+import {Text} from '#/components/Typography'
+import * as bsky from '#/types/bsky'
+
+const NO_PROMOTE_LABEL = '!no-promote'
+
+export function AlgoVisibilityOptOut() {
+ const t = useTheme()
+ const {_} = useLingui()
+ const {currentAccount} = useSession()
+ const {data: profile} = useProfileQuery({did: currentAccount?.did})
+ const updateProfile = useProfileUpdateMutation()
+
+ const isOptedOut =
+ profile?.labels?.some(label => label.val === NO_PROMOTE_LABEL) || false
+ const canToggle = profile && !updateProfile.isPending
+
+ const onToggleOptOut = useCallback(() => {
+ if (!profile) {
+ return
+ }
+ let wasAdded = false
+ 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(
+ label => label.val === NO_PROMOTE_LABEL,
+ )
+ if (hasLabel) {
+ labels.values = labels.values.filter(
+ label => label.val !== NO_PROMOTE_LABEL,
+ )
+ } else {
+ wasAdded = true
+ labels.values.push({val: NO_PROMOTE_LABEL})
+ }
+
+ if (labels.values.length === 0) {
+ delete existing.labels
+ } else {
+ existing.labels = labels
+ }
+
+ return existing
+ },
+ checkCommitted: response => {
+ const exists = !!response.data.labels?.some(
+ label => label.val === NO_PROMOTE_LABEL,
+ )
+ return exists === wasAdded
+ },
+ })
+ }, [updateProfile, profile])
+
+ return (
+
+
+
+
+ Ask apps to hide my posts from algorithmic recommendations
+
+
+
+
+
+
+
+ Bluesky will not show your posts in algorithmic recommendations and
+ will ask other apps to do the same. Your posts may still appear in the
+ Discover feed, but only to your followers.
+
+
+
+ )
+}
diff --git a/src/screens/Settings/components/PwiOptOut.tsx b/src/screens/Settings/components/PwiOptOut.tsx
index 193f26994e..9c32e48595 100644
--- a/src/screens/Settings/components/PwiOptOut.tsx
+++ b/src/screens/Settings/components/PwiOptOut.tsx
@@ -85,12 +85,12 @@ export function PwiOptOut() {
value={isOptedOut}
onChange={onToggleOptOut}
label={_(
- msg`Discourage apps from showing my account to logged-out users`,
+ msg`Ask apps and sites not to show my account to logged-out users`,
)}
style={[a.w_full]}>
- Discourage apps from showing my account to logged-out users
+ Ask apps and sites not to show my account to logged-out users
@@ -98,9 +98,9 @@ export function PwiOptOut() {
- Bluesky will not show your profile and posts to logged-out users.
- Other apps may not honor this request. This does not make your account
- private.
+ Bluesky will not show your account to logged-out users and will ask
+ other apps to do the same. Other apps may not honor this request. It
+ doesn't make your account private.