my fork of the bluesky client
at main 158 lines 4.7 kB view raw
1import React, {useCallback} from 'react' 2import {View} from 'react-native' 3import {AppBskyActorDefs} from '@atproto/api' 4import {msg, Trans} from '@lingui/macro' 5import {useLingui} from '@lingui/react' 6 7import {sanitizeDisplayName} from '#/lib/strings/display-names' 8import {sanitizeHandle} from '#/lib/strings/handles' 9import {useProfilesQuery} from '#/state/queries/profile' 10import {type SessionAccount, useSession} from '#/state/session' 11import {UserAvatar} from '#/view/com/util/UserAvatar' 12import {atoms as a, useTheme} from '#/alf' 13import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' 14import {ChevronRight_Stroke2_Corner0_Rounded as Chevron} from '#/components/icons/Chevron' 15import {Button} from './Button' 16import {Text} from './Typography' 17 18export function AccountList({ 19 onSelectAccount, 20 onSelectOther, 21 otherLabel, 22 pendingDid, 23}: { 24 onSelectAccount: (account: SessionAccount) => void 25 onSelectOther: () => void 26 otherLabel?: string 27 pendingDid: string | null 28}) { 29 const {currentAccount, accounts} = useSession() 30 const t = useTheme() 31 const {_} = useLingui() 32 const {data: profiles} = useProfilesQuery({ 33 handles: accounts.map(acc => acc.did), 34 }) 35 36 const onPressAddAccount = useCallback(() => { 37 onSelectOther() 38 }, [onSelectOther]) 39 40 return ( 41 <View 42 pointerEvents={pendingDid ? 'none' : 'auto'} 43 style={[ 44 a.rounded_md, 45 a.overflow_hidden, 46 {borderWidth: 1}, 47 t.atoms.border_contrast_low, 48 ]}> 49 {accounts.map(account => ( 50 <React.Fragment key={account.did}> 51 <AccountItem 52 profile={profiles?.profiles.find(p => p.did === account.did)} 53 account={account} 54 onSelect={onSelectAccount} 55 isCurrentAccount={account.did === currentAccount?.did} 56 isPendingAccount={account.did === pendingDid} 57 /> 58 <View style={[{borderBottomWidth: 1}, t.atoms.border_contrast_low]} /> 59 </React.Fragment> 60 ))} 61 <Button 62 testID="chooseAddAccountBtn" 63 style={[a.flex_1]} 64 onPress={pendingDid ? undefined : onPressAddAccount} 65 label={_(msg`Login to account that is not listed`)}> 66 {({hovered, pressed}) => ( 67 <View 68 style={[ 69 a.flex_1, 70 a.flex_row, 71 a.align_center, 72 {height: 48}, 73 (hovered || pressed) && t.atoms.bg_contrast_25, 74 ]}> 75 <Text 76 style={[ 77 a.align_baseline, 78 a.flex_1, 79 a.flex_row, 80 a.py_sm, 81 {paddingLeft: 48}, 82 ]}> 83 {otherLabel ?? <Trans>Other account</Trans>} 84 </Text> 85 <Chevron size="sm" style={[t.atoms.text, a.mr_md]} /> 86 </View> 87 )} 88 </Button> 89 </View> 90 ) 91} 92 93function AccountItem({ 94 profile, 95 account, 96 onSelect, 97 isCurrentAccount, 98 isPendingAccount, 99}: { 100 profile?: AppBskyActorDefs.ProfileViewDetailed 101 account: SessionAccount 102 onSelect: (account: SessionAccount) => void 103 isCurrentAccount: boolean 104 isPendingAccount: boolean 105}) { 106 const t = useTheme() 107 const {_} = useLingui() 108 109 const onPress = useCallback(() => { 110 onSelect(account) 111 }, [account, onSelect]) 112 113 return ( 114 <Button 115 testID={`chooseAccountBtn-${account.handle}`} 116 key={account.did} 117 style={[a.flex_1]} 118 onPress={onPress} 119 label={ 120 isCurrentAccount 121 ? _(msg`Continue as ${account.handle} (currently signed in)`) 122 : _(msg`Sign in as ${account.handle}`) 123 }> 124 {({hovered, pressed}) => ( 125 <View 126 style={[ 127 a.flex_1, 128 a.flex_row, 129 a.align_center, 130 {height: 48}, 131 (hovered || pressed || isPendingAccount) && t.atoms.bg_contrast_25, 132 ]}> 133 <View style={a.p_md}> 134 <UserAvatar avatar={profile?.avatar} size={24} /> 135 </View> 136 <Text style={[a.align_baseline, a.flex_1, a.flex_row, a.py_sm]}> 137 <Text emoji style={[a.font_bold]}> 138 {sanitizeDisplayName( 139 profile?.displayName || profile?.handle || account.handle, 140 )} 141 </Text>{' '} 142 <Text emoji style={[t.atoms.text_contrast_medium]}> 143 {sanitizeHandle(account.handle)} 144 </Text> 145 </Text> 146 {isCurrentAccount ? ( 147 <Check 148 size="sm" 149 style={[{color: t.palette.positive_600}, a.mr_md]} 150 /> 151 ) : ( 152 <Chevron size="sm" style={[t.atoms.text, a.mr_md]} /> 153 )} 154 </View> 155 )} 156 </Button> 157 ) 158}