about summary refs log tree commit diff
path: root/src/view/screens/Moderation.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/screens/Moderation.tsx')
-rw-r--r--src/view/screens/Moderation.tsx136
1 files changed, 134 insertions, 2 deletions
diff --git a/src/view/screens/Moderation.tsx b/src/view/screens/Moderation.tsx
index 4d8d8cad7..fe1e5a28c 100644
--- a/src/view/screens/Moderation.tsx
+++ b/src/view/screens/Moderation.tsx
@@ -1,15 +1,21 @@
 import React from 'react'
-import {StyleSheet, TouchableOpacity, View} from 'react-native'
+import {
+  ActivityIndicator,
+  StyleSheet,
+  TouchableOpacity,
+  View,
+} from 'react-native'
 import {useFocusEffect} from '@react-navigation/native'
 import {
   FontAwesomeIcon,
   FontAwesomeIconStyle,
 } from '@fortawesome/react-native-fontawesome'
+import {ComAtprotoLabelDefs} from '@atproto/api'
 import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
 import {s} from 'lib/styles'
 import {CenteredView} from '../com/util/Views'
 import {ViewHeader} from '../com/util/ViewHeader'
-import {Link} from '../com/util/Link'
+import {Link, TextLink} from '../com/util/Link'
 import {Text} from '../com/util/text/Text'
 import {usePalette} from 'lib/hooks/usePalette'
 import {useAnalytics} from 'lib/analytics/analytics'
@@ -18,6 +24,12 @@ import {useSetMinimalShellMode} from '#/state/shell'
 import {useModalControls} from '#/state/modals'
 import {Trans, msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
+import {ToggleButton} from '../com/util/forms/ToggleButton'
+import {useSession} from '#/state/session'
+import {
+  useProfileQuery,
+  useProfileUpdateMutation,
+} from '#/state/queries/profile'
 
 type Props = NativeStackScreenProps<CommonNavigatorParams, 'Moderation'>
 export function ModerationScreen({}: Props) {
@@ -109,10 +121,124 @@ export function ModerationScreen({}: Props) {
           <Trans>Blocked accounts</Trans>
         </Text>
       </Link>
+      <Text
+        type="xl-bold"
+        style={[
+          pal.text,
+          {
+            paddingHorizontal: 18,
+            paddingTop: 18,
+            paddingBottom: 6,
+          },
+        ]}>
+        <Trans>Logged-out users</Trans>
+      </Text>
+      <PwiOptOut />
     </CenteredView>
   )
 }
 
+function PwiOptOut() {
+  const pal = usePalette('default')
+  const {_} = useLingui()
+  const {currentAccount} = useSession()
+  const {data: profile} = useProfileQuery({did: currentAccount?.did})
+  const updateProfile = useProfileUpdateMutation()
+
+  const isOptedOut =
+    profile?.labels?.some(l => l.val === '!no-unauthenticated') || false
+  const canToggle = profile && !updateProfile.isPending
+
+  const onToggleOptOut = React.useCallback(() => {
+    if (!profile) {
+      return
+    }
+    let wasAdded = false
+    updateProfile.mutate({
+      profile,
+      updates: existing => {
+        // create labels attr if needed
+        existing.labels = ComAtprotoLabelDefs.isSelfLabels(existing.labels)
+          ? existing.labels
+          : {
+              $type: 'com.atproto.label.defs#selfLabels',
+              values: [],
+            }
+
+        // toggle the label
+        const hasLabel = existing.labels.values.some(
+          l => l.val === '!no-unauthenticated',
+        )
+        if (hasLabel) {
+          wasAdded = false
+          existing.labels.values = existing.labels.values.filter(
+            l => l.val !== '!no-unauthenticated',
+          )
+        } else {
+          wasAdded = true
+          existing.labels.values.push({val: '!no-unauthenticated'})
+        }
+
+        // delete if no longer needed
+        if (existing.labels.values.length === 0) {
+          delete existing.labels
+        }
+        return existing
+      },
+      checkCommitted: res => {
+        const exists = !!res.data.labels?.some(
+          l => l.val === '!no-unauthenticated',
+        )
+        return exists === wasAdded
+      },
+    })
+  }, [updateProfile, profile])
+
+  return (
+    <View style={[pal.view, styles.toggleCard]}>
+      <View
+        style={{flexDirection: 'row', alignItems: 'center', paddingRight: 14}}>
+        <ToggleButton
+          type="default-light"
+          label={_(msg`Limit the visibility of my account`)}
+          labelType="lg"
+          isSelected={isOptedOut}
+          onPress={canToggle ? onToggleOptOut : undefined}
+          style={[canToggle ? undefined : {opacity: 0.5}, {flex: 1}]}
+        />
+        {updateProfile.isPending && <ActivityIndicator />}
+      </View>
+      <View
+        style={{
+          flexDirection: 'column',
+          gap: 10,
+          paddingLeft: 66,
+          paddingRight: 12,
+          paddingBottom: 10,
+        }}>
+        <Text style={pal.textLight}>
+          <Trans>
+            Your profile and content will not be visible to anyone visiting the
+            Bluesky app without an account. Enabling this will not make your
+            profile private.
+          </Trans>
+        </Text>
+        <Text style={[pal.textLight, {fontWeight: '500'}]}>
+          <Trans>
+            Note: Third-party apps that display Bluesky content may not respect
+            this setting.
+          </Trans>
+        </Text>
+        <TextLink
+          style={pal.link}
+          href="https://blueskyweb.zendesk.com/hc/en-us/articles/15835264007693-Data-Privacy"
+          text={_(msg`Learn more about what is public on Bluesky.`)}
+        />
+      </View>
+    </View>
+  )
+}
+
 const styles = StyleSheet.create({
   desktopContainer: {
     borderLeftWidth: 1,
@@ -128,6 +254,12 @@ const styles = StyleSheet.create({
     paddingHorizontal: 18,
     marginBottom: 1,
   },
+  toggleCard: {
+    paddingVertical: 8,
+    paddingTop: 2,
+    paddingHorizontal: 6,
+    marginBottom: 1,
+  },
   iconContainer: {
     alignItems: 'center',
     justifyContent: 'center',