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