[Clipclops] Moar error (#3837)

* Add history error

* Log error

* Add period
zio/stable
Eric Bailey 2024-05-03 09:39:40 -05:00 committed by GitHub
parent 1e484c6318
commit 051e897a2b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 132 additions and 34 deletions

View File

@ -0,0 +1,54 @@
import React from 'react'
import {View} from 'react-native'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {ConvoError, ConvoItem} from '#/state/messages/convo'
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 MessageListError({
item,
}: {
item: ConvoItem & {type: 'error-recoverable'}
}) {
const t = useTheme()
const {_} = useLingui()
const message = React.useMemo(() => {
return {
[ConvoError.HistoryFailed]: _(msg`Failed to load past messages.`),
}[item.code]
}, [_, item.code])
return (
<View style={[a.py_md, a.align_center]}>
<View
style={[
a.align_center,
a.pt_md,
a.pb_lg,
a.px_3xl,
a.rounded_md,
t.atoms.bg_contrast_25,
{maxWidth: 300},
]}>
<CircleInfo size="lg" fill={t.palette.negative_400} />
<Text style={[a.pt_sm, a.leading_snug]}>
{message}{' '}
<InlineLinkText
to="#"
label={_(msg`Press to retry`)}
onPress={e => {
e.preventDefault()
item.retry()
return false
}}>
{_(msg`Retry.`)}
</InlineLinkText>
</Text>
</View>
</View>
)
}

View File

@ -17,6 +17,7 @@ import {useChat} from '#/state/messages'
import {ConvoItem, ConvoStatus} from '#/state/messages/convo' import {ConvoItem, ConvoStatus} from '#/state/messages/convo'
import {useSetMinimalShellMode} from '#/state/shell' import {useSetMinimalShellMode} from '#/state/shell'
import {MessageInput} from '#/screens/Messages/Conversation/MessageInput' import {MessageInput} from '#/screens/Messages/Conversation/MessageInput'
import {MessageListError} from '#/screens/Messages/Conversation/MessageListError'
import {atoms as a} from '#/alf' import {atoms as a} from '#/alf'
import {Button, ButtonText} from '#/components/Button' import {Button, ButtonText} from '#/components/Button'
import {MessageItem} from '#/components/dms/MessageItem' import {MessageItem} from '#/components/dms/MessageItem'
@ -63,6 +64,8 @@ function renderItem({item}: {item: ConvoItem}) {
return <Text>Deleted message</Text> return <Text>Deleted message</Text>
} else if (item.type === 'pending-retry') { } else if (item.type === 'pending-retry') {
return <RetryButton onPress={item.retry} /> return <RetryButton onPress={item.retry} />
} else if (item.type === 'error-recoverable') {
return <MessageListError item={item} />
} }
return null return null

View File

@ -25,6 +25,10 @@ export enum ConvoStatus {
Suspended = 'suspended', Suspended = 'suspended',
} }
export enum ConvoError {
HistoryFailed = 'historyFailed',
}
export type ConvoItem = export type ConvoItem =
| { | {
type: 'message' | 'pending-message' type: 'message' | 'pending-message'
@ -49,6 +53,17 @@ export type ConvoItem =
key: string key: string
retry: () => void retry: () => void
} }
| {
type: 'error-recoverable'
key: string
code: ConvoError
retry: () => void
}
| {
type: 'error-fatal'
code: ConvoError
key: string
}
export type ConvoState = export type ConvoState =
| { | {
@ -169,6 +184,7 @@ export class Convo {
> = new Map() > = new Map()
private deletedMessages: Set<string> = new Set() private deletedMessages: Set<string> = new Set()
private footerItems: Map<string, ConvoItem> = new Map() private footerItems: Map<string, ConvoItem> = new Map()
private headerItems: Map<string, ConvoItem> = new Map()
private pendingEventIngestion: Promise<void> | undefined private pendingEventIngestion: Promise<void> | undefined
private isProcessingPendingMessages = false private isProcessingPendingMessages = false
@ -366,51 +382,72 @@ export class Convo {
*/ */
if (this.isFetchingHistory) return if (this.isFetchingHistory) return
this.isFetchingHistory = true
this.commit()
/* /*
* Delay if paginating while scrolled to prevent momentum scrolling from * If we've rendered a retry state for history fetching, exit. Upon retry,
* jerking the list around, plus makes it feel a little more human. * this will be removed and we'll try again.
*/ */
if (this.pastMessages.size > 0) { if (this.headerItems.has(ConvoError.HistoryFailed)) return
await new Promise(y => setTimeout(y, 500))
}
const response = await this.agent.api.chat.bsky.convo.getMessages( try {
{ this.isFetchingHistory = true
cursor: this.historyCursor, this.commit()
convoId: this.convoId,
limit: isNative ? 25 : 50, /*
}, * Delay if paginating while scrolled to prevent momentum scrolling from
{ * jerking the list around, plus makes it feel a little more human.
headers: { */
Authorization: this.__tempFromUserDid, if (this.pastMessages.size > 0) {
await new Promise(y => setTimeout(y, 500))
// throw new Error('UNCOMMENT TO TEST RETRY')
}
const response = await this.agent.api.chat.bsky.convo.getMessages(
{
cursor: this.historyCursor,
convoId: this.convoId,
limit: isNative ? 25 : 50,
}, },
}, {
) headers: {
const {cursor, messages} = response.data Authorization: this.__tempFromUserDid,
},
},
)
const {cursor, messages} = response.data
this.historyCursor = cursor || null this.historyCursor = cursor ?? null
for (const message of messages) { for (const message of messages) {
if (
ChatBskyConvoDefs.isMessageView(message) ||
ChatBskyConvoDefs.isDeletedMessageView(message)
) {
this.pastMessages.set(message.id, message)
// set to latest rev
if ( if (
message.rev > (this.eventsCursor = this.eventsCursor || message.rev) ChatBskyConvoDefs.isMessageView(message) ||
ChatBskyConvoDefs.isDeletedMessageView(message)
) { ) {
this.eventsCursor = message.rev this.pastMessages.set(message.id, message)
// set to latest rev
if (
message.rev > (this.eventsCursor = this.eventsCursor || message.rev)
) {
this.eventsCursor = message.rev
}
} }
} }
} } catch (e: any) {
logger.error('Convo: failed to fetch message history')
this.isFetchingHistory = false this.headerItems.set(ConvoError.HistoryFailed, {
this.commit() type: 'error-recoverable',
key: ConvoError.HistoryFailed,
code: ConvoError.HistoryFailed,
retry: () => {
this.headerItems.delete(ConvoError.HistoryFailed)
this.fetchMessageHistory()
},
})
} finally {
this.isFetchingHistory = false
this.commit()
}
} }
private async pollEvents() { private async pollEvents() {
@ -730,6 +767,10 @@ export class Convo {
} }
}) })
this.headerItems.forEach(item => {
items.push(item)
})
return items return items
.filter(item => { .filter(item => {
if (isConvoItemMessage(item)) { if (isConvoItemMessage(item)) {