Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 237 lines 6.1 kB view raw
1import {useWindowDimensions, View} from 'react-native' 2import {msg} from '@lingui/core/macro' 3import {useLingui} from '@lingui/react' 4 5import {useProfileShadow} from '#/state/cache/profile-shadow' 6import { 7 usePdsLabelEnabled, 8 usePdsLabelHideBskyPds, 9} from '#/state/preferences/pds-label' 10import {usePdsFaviconQuery, usePdsLabelQuery} from '#/state/queries/pds-label' 11import {atoms as a, useAlf, type ViewStyleProp} from '#/alf' 12import {BotBadge, BotBadgeButton, isBotAccount} from '#/components/BotBadge' 13import {Button} from '#/components/Button' 14import * as Dialog from '#/components/Dialog' 15import {PdsBadgeIcon, PdsDialog} from '#/components/PdsDialog' 16import {isPetAccount, PetBadge, PetBadgeButton} from '#/components/PetBadge' 17import {useSimpleVerificationState} from '#/components/verification' 18import {VerificationCheck} from '#/components/verification/VerificationCheck' 19import {VerificationCheckButton} from '#/components/verification/VerificationCheckButton' 20import {IS_WEB} from '#/env' 21import type * as bsky from '#/types/bsky' 22 23export type Size = 'xs' | 'sm' | 'md' | 'lg' | 'xl' 24 25const verificationIconSizes: Record<Size, number> = { 26 xs: 10, 27 sm: 12, 28 md: 14, 29 lg: 18, 30 xl: 22, 31} as const 32 33const botIconSizes: Record<Size, number> = { 34 xs: 11, 35 sm: 13, 36 md: 15, 37 lg: 19, 38 xl: 23, 39} as const 40 41export function ProfileBadges({ 42 profile, 43 interactive = false, 44 pdsInteractive = true, 45 size, 46 style, 47}: ViewStyleProp & { 48 profile: bsky.profile.AnyProfileView 49 interactive?: boolean 50 pdsInteractive?: boolean 51 size: Size 52}) { 53 const shadowed = useProfileShadow(profile) 54 const verification = useSimpleVerificationState({profile}) 55 const pdsLabelEnabled = usePdsLabelEnabled() 56 const hideBskyPds = usePdsLabelHideBskyPds() 57 const {data: pdsData, isLoading: isPdsLoading} = usePdsLabelQuery( 58 pdsLabelEnabled ? shadowed.did : undefined, 59 ) 60 const {data: pdsFaviconUrl} = usePdsFaviconQuery( 61 pdsData && !pdsData.isBsky && !pdsData.isBridged 62 ? pdsData.pdsUrl 63 : undefined, 64 ) 65 const {fontScale: nativeScaleMultiplier} = useWindowDimensions() 66 const { 67 fonts: {scaleMultiplier: alfScaleMultiplier}, 68 } = useAlf() 69 70 const isBskyHandle = 71 !!shadowed.handle && 72 (shadowed.handle.endsWith('.bsky.social') || 73 shadowed.handle === 'bsky.social') 74 75 const showPdsBadge = 76 pdsLabelEnabled && 77 (isPdsLoading || (!!pdsData && !(hideBskyPds && pdsData.isBsky))) 78 79 // if nothing to show, don't render the container at all 80 if ( 81 !showPdsBadge && 82 !verification.showBadge && 83 !isBotAccount(shadowed) && 84 !isPetAccount(shadowed) 85 ) 86 return null 87 88 const isOnTheSmallSide = size === 'xs' || size === 'sm' 89 90 const verificationIconWidth = 91 verificationIconSizes[size] * nativeScaleMultiplier * alfScaleMultiplier 92 const botIconWidth = 93 botIconSizes[size] * nativeScaleMultiplier * alfScaleMultiplier 94 95 return ( 96 <View 97 style={[ 98 a.flex_row, 99 a.align_center, 100 isOnTheSmallSide ? a.gap_2xs : a.gap_xs, 101 style, 102 ]}> 103 {showPdsBadge && ( 104 <PdsInlineIcon 105 size={size} 106 interactive={pdsInteractive} 107 isLoading={isPdsLoading} 108 isBsky={pdsData?.isBsky ?? isBskyHandle} 109 isBridged={pdsData?.isBridged ?? false} 110 pdsUrl={pdsData?.pdsUrl} 111 faviconUrl={pdsFaviconUrl} 112 /> 113 )} 114 {interactive ? ( 115 <> 116 <VerificationCheckButton 117 profile={shadowed} 118 width={verificationIconWidth} 119 /> 120 <BotBadgeButton profile={shadowed} width={botIconWidth} /> 121 <PetBadgeButton profile={shadowed} width={botIconWidth} /> 122 </> 123 ) : ( 124 <> 125 {verification.showBadge && ( 126 <VerificationCheck 127 verifier={verification.role === 'verifier'} 128 width={verificationIconWidth} 129 /> 130 )} 131 <BotBadge profile={shadowed} width={botIconWidth} /> 132 <PetBadge profile={shadowed} width={botIconWidth} /> 133 </> 134 )} 135 </View> 136 ) 137} 138 139function pdsIconDimensions(size: Size) { 140 switch (size) { 141 case 'md': 142 return 14 143 case 'lg': 144 return 20 145 case 'xl': 146 return 24 147 default: 148 return 12 149 } 150} 151 152function PdsInlineIcon({ 153 size, 154 interactive, 155 isLoading, 156 isBsky, 157 isBridged, 158 pdsUrl, 159 faviconUrl, 160}: { 161 size: Size 162 interactive: boolean 163 isLoading: boolean 164 isBsky: boolean 165 isBridged: boolean 166 pdsUrl?: string 167 faviconUrl?: string 168}) { 169 const {_} = useLingui() 170 const dialogControl = Dialog.useDialogControl() 171 const dimensions = pdsIconDimensions(size) 172 173 const icon = ( 174 <PdsBadgeIcon 175 faviconUrl={faviconUrl} 176 isBsky={isBsky} 177 isBridged={isBridged} 178 size={dimensions} 179 borderRadius={Math.round(dimensions * 0.25)} 180 /> 181 ) 182 183 if (isLoading || !pdsUrl || !interactive) { 184 return ( 185 <View 186 style={[ 187 a.justify_center, 188 a.align_center, 189 {width: dimensions, height: dimensions}, 190 ]}> 191 {icon} 192 </View> 193 ) 194 } 195 196 return ( 197 <> 198 <Button 199 label={_(msg`View PDS information`)} 200 hitSlop={20} 201 onPress={evt => { 202 evt.preventDefault() 203 dialogControl.open() 204 if (IS_WEB) { 205 ;(document.activeElement as HTMLElement | null)?.blur() 206 } 207 }}> 208 {({hovered}) => ( 209 <View style={{width: dimensions, height: dimensions}}> 210 <View 211 style={[ 212 a.justify_center, 213 a.align_center, 214 a.transition_transform, 215 { 216 position: 'absolute', 217 top: 0, 218 left: 0, 219 right: 0, 220 bottom: 0, 221 transform: [{scale: hovered ? 1.1 : 1}], 222 }, 223 ]}> 224 {icon} 225 </View> 226 </View> 227 )} 228 </Button> 229 230 <PdsDialog 231 control={dialogControl} 232 pdsUrl={pdsUrl} 233 faviconUrl={faviconUrl} 234 /> 235 </> 236 ) 237}