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