parent
1e484c6318
commit
051e897a2b
|
@ -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>
|
||||||
|
)
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
Loading…
Reference in New Issue