this repo has no description
1import {View} from 'react-native'
2import {type $Typed, ComAtprotoLabelDefs} from '@atproto/api'
3import {Trans, useLingui} from '@lingui/react/macro'
4import {type NativeStackScreenProps} from '@react-navigation/native-stack'
5import {useQueryClient} from '@tanstack/react-query'
6
7import {type CommonNavigatorParams} from '#/lib/routes/types'
8import {RQKEY_ROOT as POST_FEED_RQKEY_ROOT} from '#/state/queries/post-feed'
9import {
10 useProfileQuery,
11 useProfileUpdateMutation,
12} from '#/state/queries/profile'
13import {postThreadQueryKeyRoot} from '#/state/queries/usePostThread/types'
14import {useSession} from '#/state/session'
15import {UserAvatar} from '#/view/com/util/UserAvatar'
16import {atoms as a, platform, useTheme} from '#/alf'
17import {BotBadge} from '#/components/BotBadge'
18import * as Toggle from '#/components/forms/Toggle'
19import {Bot_Filled as RobotIcon} from '#/components/icons/Bot'
20import * as Layout from '#/components/Layout'
21import {Text} from '#/components/Typography'
22import {useSimpleVerificationState} from '#/components/verification'
23import {VerificationCheck} from '#/components/verification/VerificationCheck'
24import {useAnalytics} from '#/analytics'
25import * as bsky from '#/types/bsky'
26
27type Props = NativeStackScreenProps<
28 CommonNavigatorParams,
29 'AutomationLabelSettings'
30>
31export function AutomationLabelSettingsScreen({}: Props) {
32 const t = useTheme()
33 const ax = useAnalytics()
34 const {t: l} = useLingui()
35 const queryClient = useQueryClient()
36 const {currentAccount} = useSession()
37 const {data: profile} = useProfileQuery({did: currentAccount?.did})
38 const updateProfile = useProfileUpdateMutation()
39 const verification = useSimpleVerificationState({profile})
40
41 const isBotLabeled =
42 profile?.labels?.some(l => l.val === 'bot' && l.src === profile.did) ??
43 false
44 const canToggle = profile && !updateProfile.isPending
45
46 const onToggle = () => {
47 if (!profile) {
48 return
49 }
50 let wasAdded = false
51 ax.metric('bot:label:toggle', {state: isBotLabeled ? 'remove' : 'add'})
52 updateProfile.mutate(
53 {
54 profile,
55 updates: existing => {
56 const labels: $Typed<ComAtprotoLabelDefs.SelfLabels> = bsky.validate(
57 existing.labels,
58 ComAtprotoLabelDefs.validateSelfLabels,
59 )
60 ? existing.labels
61 : {
62 $type: 'com.atproto.label.defs#selfLabels',
63 values: [],
64 }
65
66 const hasLabel = labels.values.some(l => l.val === 'bot')
67 if (hasLabel) {
68 wasAdded = false
69 labels.values = labels.values.filter(l => l.val !== 'bot')
70 } else {
71 wasAdded = true
72 labels.values.push({val: 'bot'})
73 }
74
75 if (labels.values.length === 0) {
76 delete existing.labels
77 } else {
78 existing.labels = labels
79 }
80
81 return existing
82 },
83 checkCommitted: res => {
84 const exists = !!res.data.labels?.some(l => l.val === 'bot')
85 return exists === wasAdded
86 },
87 },
88 {
89 onSuccess() {
90 queryClient.invalidateQueries({queryKey: [POST_FEED_RQKEY_ROOT]})
91 queryClient.invalidateQueries({queryKey: [postThreadQueryKeyRoot]})
92 },
93 },
94 )
95 }
96
97 return (
98 <Layout.Screen>
99 <Layout.Header.Outer>
100 <Layout.Header.BackButton />
101 <Layout.Header.Content>
102 <Layout.Header.TitleText>
103 <Trans>Automation Label</Trans>
104 </Layout.Header.TitleText>
105 </Layout.Header.Content>
106 <Layout.Header.Slot />
107 </Layout.Header.Outer>
108 <Layout.Content>
109 <View style={[a.p_xl, a.gap_xl]}>
110 {profile && (
111 <View
112 style={[
113 a.flex_row,
114 a.justify_center,
115 a.align_center,
116 a.gap_sm,
117 a.rounded_lg,
118 a.border,
119 t.atoms.bg_contrast_50,
120 t.atoms.border_contrast_low,
121 {
122 height: 160,
123 paddingRight: 20, // helps visually center
124 },
125 ]}>
126 <UserAvatar size={42} avatar={profile.avatar} type="user" />
127 <View>
128 <View style={[a.flex_row, a.align_baseline]}>
129 <View style={[a.flex_row, a.align_center, a.gap_xs]}>
130 <Text
131 emoji
132 style={[
133 a.text_xl,
134 a.font_semi_bold,
135 a.flex_shrink,
136 a.leading_tight,
137 ]}
138 numberOfLines={1}>
139 {profile.displayName || profile.handle}
140 </Text>
141 {verification.isVerified && (
142 <VerificationCheck
143 verifier={verification.role === 'verifier'}
144 size="sm"
145 />
146 )}
147 <View style={{top: platform({ios: -1})}}>
148 <BotBadge profile={profile} alwaysShow width={17} />
149 </View>
150 </View>
151 </View>
152 <Text
153 style={[
154 a.text_md,
155 a.leading_snug,
156 t.atoms.text_contrast_medium,
157 ]}
158 numberOfLines={1}>
159 @{profile.handle}
160 </Text>
161 </View>
162 </View>
163 )}
164 <View style={[a.gap_sm]}>
165 <Text style={[a.text_2xl, a.font_bold]}>
166 <Trans>Add automation label to account</Trans>
167 </Text>
168 <Text style={[a.text_md, a.leading_snug]}>
169 <Trans>
170 This label lets the world know that this account is automated.
171 If turned on, this label appears next to the account's name on
172 their profile and posts. It can be turned on or off at any time.
173 </Trans>
174 </Text>
175 </View>
176 <Toggle.Item
177 name="automation_label"
178 disabled={!canToggle || updateProfile.isPending}
179 value={isBotLabeled}
180 onChange={onToggle}
181 label={l`Show automation label`}
182 style={[
183 a.w_full,
184 a.p_md,
185 a.rounded_lg,
186 a.border,
187 t.atoms.border_contrast_low,
188 t.atoms.bg_contrast_50,
189 ]}>
190 <View style={[a.pr_xs]}>
191 <RobotIcon width={24} fill={t.atoms.text_contrast_medium.color} />
192 </View>
193 <Toggle.LabelText style={[a.flex_1, a.text_md, a.font_medium]}>
194 <Trans>Show automation label</Trans>
195 </Toggle.LabelText>
196 <Toggle.Platform />
197 </Toggle.Item>
198 </View>
199 </Layout.Content>
200 </Layout.Screen>
201 )
202}