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