Bluesky app fork with some witchin' additions 馃挮 witchsky.app
bluesky fork client
119
fork

Configure Feed

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

at a876aae44ea07494ebea9727350aa060b81f317b 287 lines 8.3 kB view raw
1import {useCallback, useMemo} from 'react' 2import {View} from 'react-native' 3import { 4 type AppBskyActorDefs, 5 AppBskyFeedGetAuthorFeed, 6 AtUri, 7} from '@atproto/api' 8import {msg as msgLingui} from '@lingui/core/macro' 9import {useLingui} from '@lingui/react' 10import {Trans} from '@lingui/react/macro' 11import {useNavigation} from '@react-navigation/native' 12 13import {usePalette} from '#/lib/hooks/usePalette' 14import {type NavigationProp} from '#/lib/routes/types' 15import {cleanError} from '#/lib/strings/errors' 16import {logger} from '#/logger' 17import {type FeedDescriptor} from '#/state/queries/post-feed' 18import {useRemoveFeedMutation} from '#/state/queries/preferences' 19import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning' 20import * as Prompt from '#/components/Prompt' 21import {EmptyState} from '../util/EmptyState' 22import {ErrorMessage} from '../util/error/ErrorMessage' 23import {Button} from '../util/forms/Button' 24import {Text} from '../util/text/Text' 25import * as Toast from '../util/Toast' 26 27export enum KnownError { 28 Block = 'Block', 29 FeedgenDoesNotExist = 'FeedgenDoesNotExist', 30 FeedgenMisconfigured = 'FeedgenMisconfigured', 31 FeedgenBadResponse = 'FeedgenBadResponse', 32 FeedgenOffline = 'FeedgenOffline', 33 FeedgenUnknown = 'FeedgenUnknown', 34 FeedSignedInOnly = 'FeedSignedInOnly', 35 FeedTooManyRequests = 'FeedTooManyRequests', 36 Unknown = 'Unknown', 37} 38 39export function PostFeedErrorMessage({ 40 feedDesc, 41 error, 42 onPressTryAgain, 43 savedFeedConfig, 44}: { 45 feedDesc: FeedDescriptor 46 error?: Error 47 onPressTryAgain: () => void 48 savedFeedConfig?: AppBskyActorDefs.SavedFeed 49}) { 50 const {_: _l} = useLingui() 51 const knownError = useMemo( 52 () => detectKnownError(feedDesc, error), 53 [feedDesc, error], 54 ) 55 56 if ( 57 typeof knownError !== 'undefined' && 58 knownError !== KnownError.Unknown && 59 feedDesc.startsWith('feedgen') 60 ) { 61 return ( 62 <FeedgenErrorMessage 63 feedDesc={feedDesc} 64 knownError={knownError} 65 rawError={error} 66 savedFeedConfig={savedFeedConfig} 67 /> 68 ) 69 } 70 71 if (knownError === KnownError.Block) { 72 return ( 73 <EmptyState 74 icon={WarningIcon} 75 iconSize="2xl" 76 message={_l(msgLingui`Posts hidden`)} 77 style={{paddingVertical: 40}} 78 /> 79 ) 80 } 81 82 return ( 83 <ErrorMessage 84 message={cleanError(error)} 85 onPressTryAgain={onPressTryAgain} 86 /> 87 ) 88} 89 90function FeedgenErrorMessage({ 91 feedDesc, 92 knownError, 93 rawError, 94 savedFeedConfig, 95}: { 96 feedDesc: FeedDescriptor 97 knownError: KnownError 98 rawError?: Error 99 savedFeedConfig?: AppBskyActorDefs.SavedFeed 100}) { 101 const pal = usePalette('default') 102 const {_: _l} = useLingui() 103 const navigation = useNavigation<NavigationProp>() 104 const msg = useMemo( 105 () => 106 ({ 107 [KnownError.Unknown]: '', 108 [KnownError.Block]: '', 109 [KnownError.FeedgenDoesNotExist]: _l( 110 msgLingui`Hmm, we're having trouble finding this feed. It may have been deleted.`, 111 ), 112 [KnownError.FeedgenMisconfigured]: _l( 113 msgLingui`Hmm, the feed server appears to be misconfigured. Please let the feed owner know about this issue.`, 114 ), 115 [KnownError.FeedgenBadResponse]: _l( 116 msgLingui`Hmm, the feed server gave a bad response. Please let the feed owner know about this issue.`, 117 ), 118 [KnownError.FeedgenOffline]: _l( 119 msgLingui`Hmm, the feed server appears to be offline. Please let the feed owner know about this issue.`, 120 ), 121 [KnownError.FeedSignedInOnly]: _l( 122 msgLingui`This content is not viewable without a Bluesky account.`, 123 ), 124 [KnownError.FeedgenUnknown]: _l( 125 msgLingui`Hmm, some kind of issue occurred when contacting the feed server. Please let the feed owner know about this issue.`, 126 ), 127 [KnownError.FeedTooManyRequests]: _l( 128 msgLingui`This feed is currently receiving high traffic and is temporarily unavailable. Please try again later.`, 129 ), 130 })[knownError], 131 [_l, knownError], 132 ) 133 const [__, uri] = feedDesc.split('|') 134 const [ownerDid] = safeParseFeedgenUri(uri) 135 const removePromptControl = Prompt.usePromptControl() 136 const {mutateAsync: removeFeed} = useRemoveFeedMutation() 137 138 const onViewProfile = useCallback(() => { 139 navigation.navigate('Profile', {name: ownerDid}) 140 }, [navigation, ownerDid]) 141 142 const onPressRemoveFeed = useCallback(() => { 143 removePromptControl.open() 144 }, [removePromptControl]) 145 146 const onRemoveFeed = useCallback(async () => { 147 try { 148 if (!savedFeedConfig) return 149 await removeFeed(savedFeedConfig) 150 } catch (err) { 151 Toast.show( 152 _l( 153 msgLingui`There was an issue removing this feed. Please check your internet connection and try again.`, 154 ), 155 'exclamation-circle', 156 ) 157 logger.error('Failed to remove feed', {message: err}) 158 } 159 }, [removeFeed, _l, savedFeedConfig]) 160 161 const cta = useMemo(() => { 162 switch (knownError) { 163 case KnownError.FeedSignedInOnly: { 164 return null 165 } 166 case KnownError.FeedgenDoesNotExist: 167 case KnownError.FeedgenMisconfigured: 168 case KnownError.FeedgenBadResponse: 169 case KnownError.FeedgenOffline: 170 case KnownError.FeedgenUnknown: { 171 return ( 172 <View style={{flexDirection: 'row', alignItems: 'center', gap: 10}}> 173 {knownError === KnownError.FeedgenDoesNotExist && 174 savedFeedConfig && ( 175 <Button 176 type="inverted" 177 label={_l(msgLingui`Remove feed`)} 178 onPress={onRemoveFeed} 179 /> 180 )} 181 <Button 182 type="default-light" 183 label={_l(msgLingui`View profile`)} 184 onPress={onViewProfile} 185 /> 186 </View> 187 ) 188 } 189 } 190 }, [knownError, onViewProfile, onRemoveFeed, _l, savedFeedConfig]) 191 192 return ( 193 <> 194 <View 195 style={[ 196 pal.border, 197 pal.viewLight, 198 { 199 borderTopWidth: 1, 200 paddingHorizontal: 20, 201 paddingVertical: 18, 202 gap: 12, 203 }, 204 ]}> 205 <Text style={pal.text}>{msg}</Text> 206 207 {rawError?.message && ( 208 <Text style={pal.textLight}> 209 <Trans>Message from server: {rawError.message}</Trans> 210 </Text> 211 )} 212 213 {cta} 214 </View> 215 216 <Prompt.Basic 217 control={removePromptControl} 218 title={_l(msgLingui`Remove feed?`)} 219 description={_l(msgLingui`Remove this feed from your saved feeds`)} 220 onConfirm={onPressRemoveFeed} 221 confirmButtonCta={_l(msgLingui`Remove`)} 222 confirmButtonColor="negative" 223 /> 224 </> 225 ) 226} 227 228function safeParseFeedgenUri(uri: string): [string, string] { 229 try { 230 const urip = new AtUri(uri) 231 return [urip.hostname, urip.rkey] 232 } catch { 233 return ['', ''] 234 } 235} 236 237function detectKnownError( 238 feedDesc: FeedDescriptor, 239 error: any, 240): KnownError | undefined { 241 if (!error) { 242 return undefined 243 } 244 if ( 245 error instanceof AppBskyFeedGetAuthorFeed.BlockedActorError || 246 error instanceof AppBskyFeedGetAuthorFeed.BlockedByActorError 247 ) { 248 return KnownError.Block 249 } 250 251 // check status codes 252 if (error?.status === 429) { 253 return KnownError.FeedTooManyRequests 254 } 255 256 // convert error to string and continue 257 if (typeof error !== 'string') { 258 error = error.toString() 259 } 260 if (error.includes(KnownError.FeedSignedInOnly)) { 261 return KnownError.FeedSignedInOnly 262 } 263 if (!feedDesc.startsWith('feedgen')) { 264 return KnownError.Unknown 265 } 266 if (error.includes('could not find feed')) { 267 return KnownError.FeedgenDoesNotExist 268 } 269 if (error.includes('feed unavailable')) { 270 return KnownError.FeedgenOffline 271 } 272 if (error.includes('invalid did document')) { 273 return KnownError.FeedgenMisconfigured 274 } 275 if (error.includes('could not resolve did document')) { 276 return KnownError.FeedgenMisconfigured 277 } 278 if ( 279 error.includes('invalid feed generator service details in did document') 280 ) { 281 return KnownError.FeedgenMisconfigured 282 } 283 if (error.includes('invalid response')) { 284 return KnownError.FeedgenBadResponse 285 } 286 return KnownError.FeedgenUnknown 287}