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

Configure Feed

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

at post-text-option 221 lines 5.8 kB view raw
1import {Keyboard, View} from 'react-native' 2import { 3 type AppBskyActorDefs, 4 type AppBskyFeedDefs, 5 moderateFeedGenerator, 6 moderateProfile, 7 type ModerationOpts, 8 type ModerationUI, 9} from '@atproto/api' 10import {msg, Trans} from '@lingui/macro' 11import {useLingui} from '@lingui/react' 12 13import {DISCOVER_FEED_URI, STARTER_PACK_MAX_SIZE} from '#/lib/constants' 14import {sanitizeDisplayName} from '#/lib/strings/display-names' 15import {sanitizeHandle} from '#/lib/strings/handles' 16import {logger} from '#/logger' 17import {useSession} from '#/state/session' 18import {UserAvatar} from '#/view/com/util/UserAvatar' 19import { 20 type WizardAction, 21 type WizardState, 22} from '#/screens/StarterPack/Wizard/State' 23import {atoms as a, useTheme} from '#/alf' 24import {Button, ButtonText} from '#/components/Button' 25import * as Toggle from '#/components/forms/Toggle' 26import {Checkbox} from '#/components/forms/Toggle' 27import {Text} from '#/components/Typography' 28import type * as bsky from '#/types/bsky' 29 30function WizardListCard({ 31 type, 32 btnType, 33 displayName, 34 subtitle, 35 onPress, 36 avatar, 37 included, 38 disabled, 39 moderationUi, 40}: { 41 type: 'user' | 'algo' 42 btnType: 'checkbox' | 'remove' 43 profile?: AppBskyActorDefs.ProfileViewBasic 44 feed?: AppBskyFeedDefs.GeneratorView 45 displayName: string 46 subtitle: string 47 onPress: () => void 48 avatar?: string 49 included?: boolean 50 disabled?: boolean 51 moderationUi: ModerationUI 52}) { 53 const t = useTheme() 54 const {_} = useLingui() 55 56 return ( 57 <Toggle.Item 58 name={type === 'user' ? _(msg`Person toggle`) : _(msg`Feed toggle`)} 59 label={ 60 included 61 ? _(msg`Remove ${displayName} from starter pack`) 62 : _(msg`Add ${displayName} to starter pack`) 63 } 64 value={included} 65 disabled={btnType === 'remove' || disabled} 66 onChange={onPress} 67 style={[ 68 a.flex_row, 69 a.align_center, 70 a.px_lg, 71 a.py_md, 72 a.gap_md, 73 a.border_b, 74 t.atoms.border_contrast_low, 75 ]}> 76 <UserAvatar 77 size={45} 78 avatar={avatar} 79 moderation={moderationUi} 80 type={type} 81 /> 82 <View style={[a.flex_1, a.gap_2xs]}> 83 <Text 84 emoji 85 style={[ 86 a.flex_1, 87 a.font_semi_bold, 88 a.text_md, 89 a.leading_tight, 90 a.self_start, 91 ]} 92 numberOfLines={1}> 93 {displayName} 94 </Text> 95 <Text 96 style={[a.flex_1, a.leading_tight, t.atoms.text_contrast_medium]} 97 numberOfLines={1}> 98 {subtitle} 99 </Text> 100 </View> 101 {btnType === 'checkbox' ? ( 102 <Checkbox /> 103 ) : !disabled ? ( 104 <Button 105 label={_(msg`Remove`)} 106 variant="solid" 107 color="secondary" 108 size="small" 109 style={[a.self_center, {marginLeft: 'auto'}]} 110 onPress={onPress}> 111 <ButtonText> 112 <Trans>Remove</Trans> 113 </ButtonText> 114 </Button> 115 ) : null} 116 </Toggle.Item> 117 ) 118} 119 120export function WizardProfileCard({ 121 btnType, 122 state, 123 dispatch, 124 profile, 125 moderationOpts, 126}: { 127 btnType: 'checkbox' | 'remove' 128 state: WizardState 129 dispatch: (action: WizardAction) => void 130 profile: bsky.profile.AnyProfileView 131 moderationOpts: ModerationOpts 132}) { 133 const {currentAccount} = useSession() 134 135 // Determine the "main" profile for this starter pack - either targetDid or current account 136 const targetProfileDid = state.targetDid || currentAccount?.did 137 const isTarget = profile.did === targetProfileDid 138 const included = isTarget || state.profiles.some(p => p.did === profile.did) 139 const disabled = 140 isTarget || 141 (!included && state.profiles.length >= STARTER_PACK_MAX_SIZE - 1) 142 const moderationUi = moderateProfile(profile, moderationOpts).ui('avatar') 143 const displayName = profile.displayName 144 ? sanitizeDisplayName(profile.displayName) 145 : `@${sanitizeHandle(profile.handle)}` 146 147 const onPress = () => { 148 if (disabled) return 149 150 Keyboard.dismiss() 151 if (profile.did === targetProfileDid) return 152 153 if (!included) { 154 logger.metric('starterPack:addUser', {}) 155 dispatch({type: 'AddProfile', profile}) 156 } else { 157 logger.metric('starterPack:removeUser', {}) 158 dispatch({type: 'RemoveProfile', profileDid: profile.did}) 159 } 160 } 161 162 return ( 163 <WizardListCard 164 type="user" 165 btnType={btnType} 166 displayName={displayName} 167 subtitle={`@${sanitizeHandle(profile.handle)}`} 168 onPress={onPress} 169 avatar={profile.avatar} 170 included={included} 171 disabled={disabled} 172 moderationUi={moderationUi} 173 /> 174 ) 175} 176 177export function WizardFeedCard({ 178 btnType, 179 generator, 180 state, 181 dispatch, 182 moderationOpts, 183}: { 184 btnType: 'checkbox' | 'remove' 185 generator: AppBskyFeedDefs.GeneratorView 186 state: WizardState 187 dispatch: (action: WizardAction) => void 188 moderationOpts: ModerationOpts 189}) { 190 const isDiscover = generator.uri === DISCOVER_FEED_URI 191 const included = isDiscover || state.feeds.some(f => f.uri === generator.uri) 192 const disabled = isDiscover || (!included && state.feeds.length >= 3) 193 const moderationUi = moderateFeedGenerator(generator, moderationOpts).ui( 194 'avatar', 195 ) 196 197 const onPress = () => { 198 if (disabled) return 199 200 Keyboard.dismiss() 201 if (included) { 202 dispatch({type: 'RemoveFeed', feedUri: generator.uri}) 203 } else { 204 dispatch({type: 'AddFeed', feed: generator}) 205 } 206 } 207 208 return ( 209 <WizardListCard 210 type="algo" 211 btnType={btnType} 212 displayName={sanitizeDisplayName(generator.displayName)} 213 subtitle={`Feed by @${sanitizeHandle(generator.creator.handle)}`} 214 onPress={onPress} 215 avatar={generator.avatar} 216 included={included} 217 disabled={disabled} 218 moderationUi={moderationUi} 219 /> 220 ) 221}