diff --git a/src/screens/Messages/Conversation/MessageListFirehoseError.tsx b/src/screens/Messages/Conversation/MessageListFirehoseError.tsx new file mode 100644 index 0000000000..63b06c413e --- /dev/null +++ b/src/screens/Messages/Conversation/MessageListFirehoseError.tsx @@ -0,0 +1,54 @@ +import React from 'react' +import {View} from 'react-native' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {ConvoItem} from '#/state/messages/convo/types' +import {atoms as a, useTheme} from '#/alf' +import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' +import {InlineLinkText} from '#/components/Link' +import {Text} from '#/components/Typography' + +export function MessageListFirehoseError({ + item, +}: { + item: ConvoItem & {type: 'firehose-error'} +}) { + const t = useTheme() + const {_} = useLingui() + + return ( + + + + + + {_(msg`This chat was disconnected`)} ·{' '} + {item.retry && ( + { + e.preventDefault() + item.retry?.() + return false + }}> + {_(msg`Reconnect`)} + + )} + + + + ) +} diff --git a/src/screens/Messages/Conversation/MessagesList.tsx b/src/screens/Messages/Conversation/MessagesList.tsx index bb65567a27..2fb848cc7b 100644 --- a/src/screens/Messages/Conversation/MessagesList.tsx +++ b/src/screens/Messages/Conversation/MessagesList.tsx @@ -15,6 +15,7 @@ import {isWeb} from 'platform/detection' import {List} from 'view/com/util/List' import {MessageInput} from '#/screens/Messages/Conversation/MessageInput' import {MessageListError} from '#/screens/Messages/Conversation/MessageListError' +import {MessageListFirehoseError} from '#/screens/Messages/Conversation/MessageListFirehoseError' import {atoms as a} from '#/alf' import {MessageItem} from '#/components/dms/MessageItem' import {Loader} from '#/components/Loader' @@ -41,6 +42,8 @@ function renderItem({item}: {item: ConvoItem}) { return Deleted message } else if (item.type === 'error') { return + } else if (item.type === 'firehose-error') { + return } return null diff --git a/src/state/messages/convo/agent.ts b/src/state/messages/convo/agent.ts index 679de8b700..a0ea7fc18b 100644 --- a/src/state/messages/convo/agent.ts +++ b/src/state/messages/convo/agent.ts @@ -21,6 +21,7 @@ import { import { ConvoDispatch, ConvoDispatchEvent, + ConvoError, ConvoErrorCode, ConvoEvent, ConvoItem, @@ -55,13 +56,7 @@ export class Convo { private senderUserDid: string private status: ConvoStatus = ConvoStatus.Uninitialized - private error: - | { - code: ConvoErrorCode - exception?: Error - retry: () => void - } - | undefined + private error: ConvoError | undefined private oldestRev: string | undefined | null = undefined private isFetchingHistory = false private latestRev: string | undefined = undefined @@ -79,7 +74,6 @@ export class Convo { {id: string; message: ChatBskyConvoSendMessage.InputSchema['message']} > = new Map() private deletedMessages: Set = new Set() - private footerItems: Map = new Map() private headerItems: Map = new Map() private isProcessingPendingMessages = false @@ -180,7 +174,7 @@ export class Convo { status: ConvoStatus.Error, items: [], convo: undefined, - error: this.error, + error: this.error!, sender: undefined, recipients: undefined, isFetchingHistory: false, @@ -288,7 +282,7 @@ export class Convo { if (this.convo) { this.status = ConvoStatus.Ready this.refreshConvo() - this.attemptCleanup() + this.maybeRecoverFromNetworkError() } else { this.status = ConvoStatus.Initializing this.setup() @@ -386,7 +380,6 @@ export class Convo { this.newMessages = new Map() this.pendingMessages = new Map() this.deletedMessages = new Set() - this.footerItems = new Map() this.headerItems = new Map() this.pendingMessageFailure = null @@ -394,10 +387,14 @@ export class Convo { this.dispatch({event: ConvoDispatchEvent.Init}) } - private attemptCleanup() { - // override what's here in case remote convo state has changed - this.pendingMessageFailure = 'recoverable' - this.batchRetryPendingMessages() + maybeRecoverFromNetworkError() { + if (this.firehoseError) { + this.firehoseError.retry() + this.firehoseError = undefined + this.commit() + } else { + this.batchRetryPendingMessages() + } } private async setup() { @@ -643,22 +640,16 @@ export class Convo { ) } + private firehoseError: MessagesEventBusError | undefined + onFirehoseConnect() { - this.footerItems.delete(ConvoItemError.FirehoseFailed) + this.firehoseError = undefined + this.batchRetryPendingMessages() this.commit() } onFirehoseError(error?: MessagesEventBusError) { - this.footerItems.set(ConvoItemError.FirehoseFailed, { - type: 'error', - key: ConvoItemError.FirehoseFailed, - code: ConvoItemError.FirehoseFailed, - retry: () => { - this.footerItems.delete(ConvoItemError.FirehoseFailed) - this.commit() - error?.retry() - }, - }) + this.firehoseError = error this.commit() } @@ -852,18 +843,14 @@ export class Convo { } async batchRetryPendingMessages() { - if (this.pendingMessageFailure === 'unrecoverable') return - - // reset if present, and recoverable - if (this.pendingMessageFailure === 'recoverable') { - this.pendingMessageFailure = null - this.commit() - } + if (this.pendingMessageFailure === null) return const messageArray = Array.from(this.pendingMessages.values()) - if (messageArray.length === 0) return + this.pendingMessageFailure = null + this.commit() + logger.debug( `Convo: batch retrying ${this.pendingMessages.size} pending messages`, {}, @@ -1012,15 +999,21 @@ export class Convo { retry: this.pendingMessageFailure === 'recoverable' ? () => { - this.batchRetryPendingMessages() + this.maybeRecoverFromNetworkError() } : undefined, }) }) - this.footerItems.forEach(item => { - items.push(item) - }) + if (this.firehoseError) { + items.push({ + type: 'firehose-error', + key: 'firehose-error', + retry: () => { + this.firehoseError?.retry() + }, + }) + } return items .filter(item => { diff --git a/src/state/messages/convo/types.ts b/src/state/messages/convo/types.ts index 2305c952f7..01b24a2a17 100644 --- a/src/state/messages/convo/types.ts +++ b/src/state/messages/convo/types.ts @@ -123,6 +123,14 @@ export type ConvoItem = */ retry?: () => void } + | { + type: 'firehose-error' + key: string + /** + * If present, error is recoverable. + */ + retry?: () => void + } type DeleteMessage = (messageId: string) => Promise type SendMessage = ( @@ -194,7 +202,7 @@ export type ConvoStateError = { status: ConvoStatus.Error items: [] convo: undefined - error: any + error: ConvoError sender: undefined recipients: undefined isFetchingHistory: false