jerrification badge (pls no spoil)

This commit is contained in:
Samuel Newman
2026-03-30 14:11:00 +03:00
parent 0419066f45
commit 3d45346963
6 changed files with 167 additions and 1 deletions
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle cx="12" cy="12" r="12" fill="#006AFF"/><path fill="#fff" d="M15.117 7.116a1.25 1.25 0 0 1 1.767 1.768L13.767 12l3.2 3.2a1.25 1.25 0 0 1-1.767 1.768l-3.2-3.2-3.116 3.116a1.25 1.25 0 1 1-1.767-1.768L10.232 12 7.2 8.967A1.25 1.25 0 1 1 8.967 7.2L12 10.232l3.118-3.116Z"/></svg>

After

Width:  |  Height:  |  Size: 355 B

+2
View File
@@ -1043,4 +1043,6 @@ export type Events = {
'profile:associated:germ:click-self-info': {} 'profile:associated:germ:click-self-info': {}
'profile:associated:germ:self-disconnect': {} 'profile:associated:germ:self-disconnect': {}
'profile:associated:germ:self-reconnect': {} 'profile:associated:germ:self-reconnect': {}
'jerry:no': {}
} }
+16 -1
View File
@@ -7,6 +7,11 @@ import {useSimpleVerificationState} from '#/components/verification'
import {VerificationCheck} from '#/components/verification/VerificationCheck' import {VerificationCheck} from '#/components/verification/VerificationCheck'
import {VerificationCheckButton} from '#/components/verification/VerificationCheckButton' import {VerificationCheckButton} from '#/components/verification/VerificationCheckButton'
import type * as bsky from '#/types/bsky' import type * as bsky from '#/types/bsky'
import {
isJerrifiedAccount,
JerrifiedBadge,
} from './verification/aprilFools/JerrifiedBadge'
import {JerrifiedBadgeButton} from './verification/aprilFools/JerrifiedBadgeButton'
export type Size = 'xs' | 'sm' | 'md' | 'lg' | 'xl' export type Size = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
@@ -39,8 +44,11 @@ export function ProfileBadges({
const shadowed = useProfileShadow(profile) const shadowed = useProfileShadow(profile)
const verification = useSimpleVerificationState({profile}) const verification = useSimpleVerificationState({profile})
const isJerry = isJerrifiedAccount(profile)
// if nothing to show, don't render the container at all // if nothing to show, don't render the container at all
if (!verification.showBadge && !isBotAccount(shadowed)) return null if (!verification.showBadge && !isBotAccount(shadowed) && !isJerry)
return null
const isOnTheSmallSide = size === 'xs' || size === 'sm' const isOnTheSmallSide = size === 'xs' || size === 'sm'
@@ -54,6 +62,12 @@ export function ProfileBadges({
]}> ]}>
{interactive ? ( {interactive ? (
<> <>
{isJerry && (
<JerrifiedBadgeButton
profile={profile}
width={verificationIconSizes[size]}
/>
)}
<VerificationCheckButton <VerificationCheckButton
profile={shadowed} profile={shadowed}
width={verificationIconSizes[size]} width={verificationIconSizes[size]}
@@ -62,6 +76,7 @@ export function ProfileBadges({
</> </>
) : ( ) : (
<> <>
{isJerry && <JerrifiedBadge width={verificationIconSizes[size]} />}
{verification.showBadge && ( {verification.showBadge && (
<VerificationCheck <VerificationCheck
verifier={verification.role === 'verifier'} verifier={verification.role === 'verifier'}
+29
View File
@@ -0,0 +1,29 @@
import {forwardRef} from 'react'
import Svg, {Circle, Path} from 'react-native-svg'
import {type Props, useCommonSVGProps} from '#/components/icons/common'
export const UnverifiedX = forwardRef<Svg, Props>(
function LogoImpl(props, ref) {
const {fill, size, style, ...rest} = useCommonSVGProps(props)
return (
<Svg
fill="none"
{...rest}
ref={ref}
viewBox="0 0 24 24"
width={size}
height={size}
style={[style]}>
<Circle cx="12" cy="12" r="11.5" fill={fill} />
<Path
fill="#fff"
fillRule="evenodd"
clipRule="evenodd"
d="M15.117 7.116a1.25 1.25 0 0 1 1.767 1.768L13.767 12l3.2 3.2a1.25 1.25 0 0 1-1.767 1.768l-3.2-3.2-3.116 3.116a1.25 1.25 0 1 1-1.767-1.768L10.232 12 7.2 8.967A1.25 1.25 0 1 1 8.967 7.2L12 10.232l3.118-3.116Z"
/>
</Svg>
)
},
)
@@ -0,0 +1,12 @@
import {UnverifiedX} from '#/components/icons/UnverifiedX'
import type * as bsky from '#/types/bsky'
const JERRY = 'did:plc:vc7f4oafdgxsihk4cry2xpze'
export function isJerrifiedAccount(profile: bsky.profile.AnyProfileView) {
return profile.did === JERRY
}
export function JerrifiedBadge({width}: {width: number}) {
return <UnverifiedX width={width} />
}
@@ -0,0 +1,107 @@
import {View} from 'react-native'
import {Trans, useLingui} from '@lingui/react/macro'
import {UserAvatar} from '#/view/com/util/UserAvatar'
import {atoms as a, web} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {UnverifiedX} from '#/components/icons/UnverifiedX'
import {Text} from '#/components/Typography'
import {useAnalytics} from '#/analytics'
import type * as bsky from '#/types/bsky'
import {JerrifiedBadge} from './JerrifiedBadge'
export function JerrifiedBadgeButton({
profile,
width,
}: {
profile: bsky.profile.AnyProfileView
width: number
}) {
const {t: l} = useLingui()
const ax = useAnalytics()
const control = Dialog.useDialogControl()
return (
<>
<Button
label={l`Unverified account`}
hitSlop={20}
onPress={evt => {
evt.preventDefault()
control.open()
}}>
{({hovered}) => (
<View
style={[
a.justify_end,
a.align_end,
a.transition_transform,
{
width: width,
height: width,
transform: [{scale: hovered ? 1.1 : 1}],
},
]}>
<UnverifiedX width={width} />
</View>
)}
</Button>
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
<Dialog.Handle />
<Dialog.ScrollableInner
label={l`Unverified account`}
style={web({maxWidth: 350})}
contentContainerStyle={a.gap_sm}>
<View
style={[
a.w_full,
a.py_lg,
a.align_center,
a.flex_row,
a.justify_center,
]}>
<UserAvatar
avatar={profile.avatar}
type="user"
size={60}
noBorder
/>
<View style={[{opacity: 0.5}, a.absolute, a.z_10]}>
<JerrifiedBadge width={64} />
</View>
</View>
<Text style={[a.text_2xl, a.font_bold, a.leading_snug]}>
<Trans>Jerry is unverified</Trans>
</Text>
<Text style={[a.text_md, a.leading_snug]}>
<Trans>Do not trust this user under any circumstances.</Trans>
</Text>
<Button
size="small"
color="primary"
onPress={() => {
ax.metric('jerry:no', {})
control.close()
}}
label={l`Jerry, no!`}
style={[a.mt_sm]}>
<ButtonText>
<Trans>Jerry, no!</Trans>
</ButtonText>
</Button>
<Button
size="small"
color="secondary"
onPress={() => control.close()}
label={l`Close`}>
<ButtonText>
<Trans>Close</Trans>
</ButtonText>
</Button>
<Dialog.Close />
</Dialog.ScrollableInner>
</Dialog.Outer>
</>
)
}