Bluesky app fork with some witchin' additions ๐Ÿ’ซ
0
fork

Configure Feed

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

[๐Ÿด] Handle errors, improve styling (#3937)

* Handle errors, improve styling

* Remove old UI

authored by

Eric Bailey and committed by
GitHub
195c9f10 1821a992

+96 -78
+36 -22
src/screens/Messages/Conversation/MessageListError.tsx
··· 5 5 6 6 import {ConvoItem, ConvoItemError} from '#/state/messages/convo/types' 7 7 import {atoms as a, useTheme} from '#/alf' 8 + import {Button, ButtonIcon, ButtonText} from '#/components/Button' 9 + import {ArrowRotateCounterClockwise_Stroke2_Corner0_Rounded as Refresh} from '#/components/icons/ArrowRotateCounterClockwise' 8 10 import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' 9 - import {InlineLinkText} from '#/components/Link' 10 11 import {Text} from '#/components/Typography' 11 12 12 13 export function MessageListError({ ··· 21 22 [ConvoItemError.Network]: _( 22 23 msg`There was an issue connecting to the chat.`, 23 24 ), 24 - [ConvoItemError.HistoryFailed]: _(msg`Failed to load past messages.`), 25 - [ConvoItemError.PollFailed]: _( 25 + [ConvoItemError.FirehoseFailed]: _( 26 26 msg`This chat was disconnected due to a network error.`, 27 27 ), 28 + [ConvoItemError.HistoryFailed]: _(msg`Failed to load past messages.`), 29 + [ConvoItemError.PendingFailed]: _(msg`Failed to send message(s).`), 28 30 }[item.code] 29 31 }, [_, item.code]) 30 32 31 33 return ( 32 - <View style={[a.py_md, a.align_center]}> 34 + <View style={[a.py_lg, a.align_center]}> 33 35 <View 34 36 style={[ 37 + a.flex_row, 35 38 a.align_center, 36 - a.pt_md, 37 - a.pb_lg, 38 - a.px_3xl, 39 + a.justify_between, 40 + a.gap_lg, 41 + a.py_md, 42 + a.px_lg, 39 43 a.rounded_md, 40 44 t.atoms.bg_contrast_25, 41 - {maxWidth: 300}, 45 + {maxWidth: 400}, 42 46 ]}> 43 - <CircleInfo size="lg" fill={t.palette.negative_400} /> 44 - <Text style={[a.pt_sm, a.leading_snug]}> 45 - {message}{' '} 46 - <InlineLinkText 47 - to="#" 48 - label={_(msg`Press to retry`)} 49 - onPress={e => { 50 - e.preventDefault() 51 - item.retry() 52 - return false 53 - }}> 54 - {_(msg`Retry.`)} 55 - </InlineLinkText> 56 - </Text> 47 + <View style={[a.flex_row, a.align_start, a.justify_between, a.gap_sm]}> 48 + <CircleInfo 49 + size="sm" 50 + fill={t.palette.negative_400} 51 + style={[{top: 3}]} 52 + /> 53 + <View style={[a.flex_1, {maxWidth: 200}]}> 54 + <Text style={[a.leading_snug]}>{message}</Text> 55 + </View> 56 + </View> 57 + 58 + <Button 59 + label={_(msg`Press to retry`)} 60 + size="small" 61 + variant="ghost" 62 + color="secondary" 63 + onPress={e => { 64 + e.preventDefault() 65 + item.retry() 66 + return false 67 + }}> 68 + <ButtonText>{_(msg`Retry`)}</ButtonText> 69 + <ButtonIcon icon={Refresh} position="right" /> 70 + </Button> 57 71 </View> 58 72 </View> 59 73 )
-24
src/screens/Messages/Conversation/MessagesList.tsx
··· 8 8 import {ReanimatedScrollEvent} from 'react-native-reanimated/lib/typescript/reanimated2/hook/commonTypes' 9 9 import {useSafeAreaInsets} from 'react-native-safe-area-context' 10 10 import {AppBskyRichtextFacet, RichText} from '@atproto/api' 11 - import {msg, Trans} from '@lingui/macro' 12 - import {useLingui} from '@lingui/react' 13 11 14 12 import {shortenLinks} from '#/lib/strings/rich-text-manip' 15 13 import {isIOS} from '#/platform/detection' ··· 22 20 import {MessageInput} from '#/screens/Messages/Conversation/MessageInput' 23 21 import {MessageListError} from '#/screens/Messages/Conversation/MessageListError' 24 22 import {atoms as a, useBreakpoints} from '#/alf' 25 - import {Button, ButtonText} from '#/components/Button' 26 23 import {MessageItem} from '#/components/dms/MessageItem' 27 24 import {Loader} from '#/components/Loader' 28 25 import {Text} from '#/components/Typography' ··· 41 38 ) 42 39 } 43 40 44 - function RetryButton({onPress}: {onPress: () => unknown}) { 45 - const {_} = useLingui() 46 - 47 - return ( 48 - <View style={{alignItems: 'center'}}> 49 - <Button 50 - label={_(msg`Press to Retry`)} 51 - onPress={onPress} 52 - variant="ghost" 53 - color="negative" 54 - size="small"> 55 - <ButtonText> 56 - <Trans>Press to Retry</Trans> 57 - </ButtonText> 58 - </Button> 59 - </View> 60 - ) 61 - } 62 - 63 41 function renderItem({item}: {item: ConvoItem}) { 64 42 if (item.type === 'message' || item.type === 'pending-message') { 65 43 return ( ··· 71 49 ) 72 50 } else if (item.type === 'deleted-message') { 73 51 return <Text>Deleted message</Text> 74 - } else if (item.type === 'pending-retry') { 75 - return <RetryButton onPress={item.retry} /> 76 52 } else if (item.type === 'error-recoverable') { 77 53 return <MessageListError item={item} /> 78 54 }
+45 -25
src/state/messages/convo/agent.ts
··· 401 401 // throw new Error('UNCOMMENT TO TEST INIT FAILURE') 402 402 this.dispatch({event: ConvoDispatchEvent.Ready}) 403 403 } catch (e: any) { 404 - logger.error('Convo: setup() failed') 404 + logger.error(e, {context: 'Convo: setup failed'}) 405 405 406 406 this.dispatch({ 407 407 event: ConvoDispatchEvent.Error, ··· 413 413 }, 414 414 }, 415 415 }) 416 + this.commit() 416 417 } 417 418 } 418 419 ··· 500 501 this.sender = sender || this.sender 501 502 this.recipients = recipients || this.recipients 502 503 } catch (e: any) { 503 - logger.error(`Convo: failed to refresh convo`) 504 + logger.error(e, {context: `Convo: failed to refresh convo`}) 504 505 505 506 this.footerItems.set(ConvoItemError.Network, { 506 507 type: 'error-recoverable', ··· 601 602 } 602 603 603 604 onFirehoseConnect() { 604 - this.footerItems.delete(ConvoItemError.PollFailed) 605 + this.footerItems.delete(ConvoItemError.FirehoseFailed) 605 606 this.commit() 606 607 } 607 608 608 609 onFirehoseError(error?: MessagesEventBusError) { 609 - this.footerItems.set(ConvoItemError.PollFailed, { 610 + this.footerItems.set(ConvoItemError.FirehoseFailed, { 610 611 type: 'error-recoverable', 611 - key: ConvoItemError.PollFailed, 612 - code: ConvoItemError.PollFailed, 612 + key: ConvoItemError.FirehoseFailed, 613 + code: ConvoItemError.FirehoseFailed, 613 614 retry: () => { 614 - this.footerItems.delete(ConvoItemError.PollFailed) 615 + this.footerItems.delete(ConvoItemError.FirehoseFailed) 615 616 this.commit() 616 617 error?.retry() 617 618 }, ··· 772 773 await this.processPendingMessages() 773 774 774 775 this.commit() 775 - } catch (e) { 776 - this.footerItems.set('pending-retry', { 777 - type: 'pending-retry', 778 - key: 'pending-retry', 779 - retry: this.batchRetryPendingMessages.bind(this), 776 + } catch (e: any) { 777 + logger.error(e, {context: `Convo: failed to send message`}) 778 + this.footerItems.set(ConvoItemError.PendingFailed, { 779 + type: 'error-recoverable', 780 + key: ConvoItemError.PendingFailed, 781 + code: ConvoItemError.PendingFailed, 782 + retry: () => { 783 + this.footerItems.delete(ConvoItemError.PendingFailed) 784 + this.commit() 785 + this.batchRetryPendingMessages() 786 + }, 780 787 }) 781 788 this.commit() 789 + } finally { 790 + this.isProcessingPendingMessages = false 782 791 } 783 792 } 784 793 ··· 789 798 logger.DebugContext.convo, 790 799 ) 791 800 792 - this.footerItems.delete('pending-retry') 793 - this.commit() 794 - 795 801 try { 802 + // throw new Error('UNCOMMENT TO TEST RETRY') 796 803 const messageArray = Array.from(this.pendingMessages.values()) 797 804 const {data} = await networkRetry(2, () => { 798 805 return this.agent.api.chat.bsky.convo.sendMessageBatch( ··· 831 838 } 832 839 833 840 this.commit() 834 - } catch (e) { 835 - this.footerItems.set('pending-retry', { 836 - type: 'pending-retry', 837 - key: 'pending-retry', 838 - retry: this.batchRetryPendingMessages.bind(this), 841 + 842 + logger.debug( 843 + `Convo: sent ${this.pendingMessages.size} pending messages`, 844 + {}, 845 + logger.DebugContext.convo, 846 + ) 847 + } catch (e: any) { 848 + logger.error(e, {context: `Convo: failed to batch retry messages`}) 849 + this.footerItems.set(ConvoItemError.PendingFailed, { 850 + type: 'error-recoverable', 851 + key: ConvoItemError.PendingFailed, 852 + code: ConvoItemError.PendingFailed, 853 + retry: () => { 854 + this.footerItems.delete(ConvoItemError.PendingFailed) 855 + this.commit() 856 + this.batchRetryPendingMessages() 857 + }, 839 858 }) 840 859 this.commit() 841 860 } ··· 862 881 }, 863 882 ) 864 883 }) 865 - } catch (e) { 884 + } catch (e: any) { 885 + logger.error(e, {context: `Convo: failed to delete message`}) 866 886 this.deletedMessages.delete(messageId) 867 887 this.commit() 868 888 throw e ··· 875 895 getItems(): ConvoItem[] { 876 896 const items: ConvoItem[] = [] 877 897 878 - this.headerItems.forEach(item => { 879 - items.push(item) 880 - }) 881 - 882 898 this.pastMessages.forEach(m => { 883 899 if (ChatBskyConvoDefs.isMessageView(m)) { 884 900 items.unshift({ ··· 895 911 nextMessage: null, 896 912 }) 897 913 } 914 + }) 915 + 916 + this.headerItems.forEach(item => { 917 + items.unshift(item) 898 918 }) 899 919 900 920 this.newMessages.forEach(m => {
+15 -7
src/state/messages/convo/types.ts
··· 24 24 } 25 25 26 26 export enum ConvoItemError { 27 + /** 28 + * Generic error 29 + */ 30 + Network = 'network', 31 + /** 32 + * Error connecting to event firehose 33 + */ 34 + FirehoseFailed = 'firehoseFailed', 35 + /** 36 + * Error fetching past messages 37 + */ 27 38 HistoryFailed = 'historyFailed', 28 - PollFailed = 'pollFailed', 29 - Network = 'network', 39 + /** 40 + * Error sending new message 41 + */ 42 + PendingFailed = 'pendingFailed', 30 43 } 31 44 32 45 export enum ConvoErrorCode { ··· 87 100 | ChatBskyConvoDefs.MessageView 88 101 | ChatBskyConvoDefs.DeletedMessageView 89 102 | null 90 - } 91 - | { 92 - type: 'pending-retry' 93 - key: string 94 - retry: () => void 95 103 } 96 104 | { 97 105 type: 'error-recoverable'