mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
at main 282 lines 8.7 kB view raw
1import {View} from 'react-native' 2import {type AppBskyActorDefs} from '@atproto/api' 3import {msg, Trans} from '@lingui/macro' 4import {useLingui} from '@lingui/react' 5 6import {urls} from '#/lib/constants' 7import {getUserDisplayName} from '#/lib/getUserDisplayName' 8import {logger} from '#/logger' 9import {useModerationOpts} from '#/state/preferences/moderation-opts' 10import {useProfileQuery} from '#/state/queries/profile' 11import {useSession} from '#/state/session' 12import {atoms as a, useBreakpoints, useTheme} from '#/alf' 13import {Admonition} from '#/components/Admonition' 14import {Button, ButtonIcon, ButtonText} from '#/components/Button' 15import * as Dialog from '#/components/Dialog' 16import {useDialogControl} from '#/components/Dialog' 17import {Trash_Stroke2_Corner0_Rounded as TrashIcon} from '#/components/icons/Trash' 18import {Link} from '#/components/Link' 19import * as ProfileCard from '#/components/ProfileCard' 20import {Text} from '#/components/Typography' 21import {type FullVerificationState} from '#/components/verification' 22import {VerificationRemovePrompt} from '#/components/verification/VerificationRemovePrompt' 23import type * as bsky from '#/types/bsky' 24 25export {useDialogControl} from '#/components/Dialog' 26 27export function VerificationsDialog({ 28 control, 29 profile, 30 verificationState, 31}: { 32 control: Dialog.DialogControlProps 33 profile: bsky.profile.AnyProfileView 34 verificationState: FullVerificationState 35}) { 36 return ( 37 <Dialog.Outer control={control}> 38 <Dialog.Handle /> 39 <Inner 40 control={control} 41 profile={profile} 42 verificationState={verificationState} 43 /> 44 </Dialog.Outer> 45 ) 46} 47 48function Inner({ 49 profile, 50 control, 51 verificationState: state, 52}: { 53 control: Dialog.DialogControlProps 54 profile: bsky.profile.AnyProfileView 55 verificationState: FullVerificationState 56}) { 57 const t = useTheme() 58 const {_} = useLingui() 59 const {gtMobile} = useBreakpoints() 60 61 const userName = getUserDisplayName(profile) 62 const label = state.profile.isViewer 63 ? state.profile.isVerified 64 ? _(msg`You are verified`) 65 : _(msg`Your verifications`) 66 : state.profile.isVerified 67 ? _(msg`${userName} is verified`) 68 : _( 69 msg({ 70 message: `${userName}'s verifications`, 71 comment: `Possessive, meaning "the verifications of {userName}"`, 72 }), 73 ) 74 75 return ( 76 <Dialog.ScrollableInner 77 label={label} 78 style={[ 79 gtMobile ? {width: 'auto', maxWidth: 400, minWidth: 200} : a.w_full, 80 ]}> 81 <View style={[a.gap_sm, a.pb_lg]}> 82 <Text style={[a.text_2xl, a.font_semi_bold, a.pr_4xl, a.leading_tight]}> 83 {label} 84 </Text> 85 <Text style={[a.text_md, a.leading_snug]}> 86 {state.profile.isVerified ? ( 87 <Trans> 88 This account has a checkmark because it's been verified by trusted 89 sources. 90 </Trans> 91 ) : ( 92 <Trans> 93 This account has one or more attempted verifications, but it is 94 not currently verified. 95 </Trans> 96 )} 97 </Text> 98 </View> 99 100 {profile.verification ? ( 101 <View style={[a.pb_xl, a.gap_md]}> 102 <Text style={[a.text_sm, t.atoms.text_contrast_medium]}> 103 <Trans>Verified by:</Trans> 104 </Text> 105 106 <View style={[a.gap_lg]}> 107 {profile.verification.verifications.map(v => ( 108 <VerifierCard 109 key={v.uri} 110 verification={v} 111 subject={profile} 112 outerDialogControl={control} 113 /> 114 ))} 115 </View> 116 117 {profile.verification.verifications.some(v => !v.isValid) && 118 state.profile.isViewer && ( 119 <Admonition type="warning" style={[a.mt_xs]}> 120 <Trans>Some of your verifications are invalid.</Trans> 121 </Admonition> 122 )} 123 </View> 124 ) : null} 125 126 <View 127 style={[ 128 a.w_full, 129 a.gap_sm, 130 a.justify_end, 131 gtMobile 132 ? [a.flex_row, a.flex_row_reverse, a.justify_start] 133 : [a.flex_col], 134 ]}> 135 <Button 136 label={_(msg`Close dialog`)} 137 size="small" 138 variant="solid" 139 color="primary" 140 onPress={() => { 141 control.close() 142 }}> 143 <ButtonText> 144 <Trans>Close</Trans> 145 </ButtonText> 146 </Button> 147 <Link 148 overridePresentation 149 to={urls.website.blog.initialVerificationAnnouncement} 150 label={_( 151 msg({ 152 message: `Learn more about verification on Bluesky`, 153 context: `english-only-resource`, 154 }), 155 )} 156 size="small" 157 variant="solid" 158 color="secondary" 159 style={[a.justify_center]} 160 onPress={() => { 161 logger.metric( 162 'verification:learn-more', 163 { 164 location: 'verificationsDialog', 165 }, 166 {statsig: true}, 167 ) 168 }}> 169 <ButtonText> 170 <Trans context="english-only-resource">Learn more</Trans> 171 </ButtonText> 172 </Link> 173 </View> 174 175 <Dialog.Close /> 176 </Dialog.ScrollableInner> 177 ) 178} 179 180function VerifierCard({ 181 verification, 182 subject, 183 outerDialogControl, 184}: { 185 verification: AppBskyActorDefs.VerificationView 186 subject: bsky.profile.AnyProfileView 187 outerDialogControl: Dialog.DialogControlProps 188}) { 189 const t = useTheme() 190 const {_, i18n} = useLingui() 191 const {currentAccount} = useSession() 192 const moderationOpts = useModerationOpts() 193 const {data: profile, error} = useProfileQuery({did: verification.issuer}) 194 const verificationRemovePromptControl = useDialogControl() 195 const canAdminister = verification.issuer === currentAccount?.did 196 197 return ( 198 <View 199 style={{ 200 opacity: verification.isValid ? 1 : 0.5, 201 }}> 202 <ProfileCard.Outer> 203 <ProfileCard.Header> 204 {error ? ( 205 <> 206 <ProfileCard.AvatarPlaceholder /> 207 <View style={[a.flex_1]}> 208 <Text 209 style={[a.text_md, a.font_semi_bold, a.leading_snug]} 210 numberOfLines={1}> 211 <Trans>Unknown verifier</Trans> 212 </Text> 213 <Text 214 emoji 215 style={[a.leading_snug, t.atoms.text_contrast_medium]} 216 numberOfLines={1}> 217 {verification.issuer} 218 </Text> 219 </View> 220 </> 221 ) : profile && moderationOpts ? ( 222 <> 223 <ProfileCard.Link 224 profile={profile} 225 style={[a.flex_row, a.align_center, a.gap_sm, a.flex_1]} 226 onPress={() => { 227 outerDialogControl.close() 228 }}> 229 <ProfileCard.Avatar 230 profile={profile} 231 moderationOpts={moderationOpts} 232 disabledPreview 233 /> 234 <View style={[a.flex_1]}> 235 <ProfileCard.Name 236 profile={profile} 237 moderationOpts={moderationOpts} 238 /> 239 <Text 240 emoji 241 style={[a.leading_snug, t.atoms.text_contrast_medium]} 242 numberOfLines={1}> 243 {i18n.date(new Date(verification.createdAt), { 244 dateStyle: 'long', 245 })} 246 </Text> 247 </View> 248 </ProfileCard.Link> 249 {canAdminister && ( 250 <View> 251 <Button 252 label={_(msg`Remove verification`)} 253 size="small" 254 variant="outline" 255 color="negative" 256 shape="round" 257 onPress={() => { 258 verificationRemovePromptControl.open() 259 }}> 260 <ButtonIcon icon={TrashIcon} /> 261 </Button> 262 </View> 263 )} 264 </> 265 ) : ( 266 <> 267 <ProfileCard.AvatarPlaceholder /> 268 <ProfileCard.NameAndHandlePlaceholder /> 269 </> 270 )} 271 </ProfileCard.Header> 272 </ProfileCard.Outer> 273 274 <VerificationRemovePrompt 275 control={verificationRemovePromptControl} 276 profile={subject} 277 verifications={[verification]} 278 onConfirm={() => outerDialogControl.close()} 279 /> 280 </View> 281 ) 282}