diff --git a/src/components/dms/MessageItem.tsx b/src/components/dms/MessageItem.tsx
index f8ab851884..cafd7ca5a5 100644
--- a/src/components/dms/MessageItem.tsx
+++ b/src/components/dms/MessageItem.tsx
@@ -1,40 +1,44 @@
import React, {useCallback, useMemo, useRef} from 'react'
-import {LayoutAnimation, StyleProp, TextStyle, View} from 'react-native'
+import {
+ GestureResponderEvent,
+ LayoutAnimation,
+ StyleProp,
+ TextStyle,
+ View,
+} from 'react-native'
import {ChatBskyConvoDefs, RichText as RichTextAPI} from '@atproto/api'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
+import {ConvoItem} from '#/state/messages/convo/types'
import {useSession} from '#/state/session'
import {TimeElapsed} from 'view/com/util/TimeElapsed'
import {atoms as a, useTheme} from '#/alf'
import {ActionsWrapper} from '#/components/dms/ActionsWrapper'
+import {InlineLinkText} from '#/components/Link'
import {Text} from '#/components/Typography'
import {RichText} from '../RichText'
let MessageItem = ({
item,
- next,
- pending,
}: {
- item: ChatBskyConvoDefs.MessageView
- next:
- | ChatBskyConvoDefs.MessageView
- | ChatBskyConvoDefs.DeletedMessageView
- | null
- pending?: boolean
+ item: ConvoItem & {type: 'message' | 'pending-message'}
}): React.ReactNode => {
const t = useTheme()
const {currentAccount} = useSession()
- const isFromSelf = item.sender?.did === currentAccount?.did
+ const {message, nextMessage} = item
+ const isPending = item.type === 'pending-message'
+
+ const isFromSelf = message.sender?.did === currentAccount?.did
const isNextFromSelf =
- ChatBskyConvoDefs.isMessageView(next) &&
- next.sender?.did === currentAccount?.did
+ ChatBskyConvoDefs.isMessageView(nextMessage) &&
+ nextMessage.sender?.did === currentAccount?.did
const isLastInGroup = useMemo(() => {
- // TODO this means it's a placeholder. Let's figure out the right way to do this though!
- if (item.id.length > 13) {
+ // if this message is pending, it means the next message is pending too
+ if (isPending && nextMessage) {
return false
}
@@ -44,9 +48,9 @@ let MessageItem = ({
}
// or, if there's a 3 minute gap between this message and the next
- if (ChatBskyConvoDefs.isMessageView(next)) {
- const thisDate = new Date(item.sentAt)
- const nextDate = new Date(next.sentAt)
+ if (ChatBskyConvoDefs.isMessageView(nextMessage)) {
+ const thisDate = new Date(message.sentAt)
+ const nextDate = new Date(nextMessage.sentAt)
const diff = nextDate.getTime() - thisDate.getTime()
@@ -55,7 +59,7 @@ let MessageItem = ({
}
return true
- }, [item, next, isFromSelf, isNextFromSelf])
+ }, [message, nextMessage, isFromSelf, isNextFromSelf, isPending])
const lastInGroupRef = useRef(isLastInGroup)
if (lastInGroupRef.current !== isLastInGroup) {
@@ -67,12 +71,12 @@ let MessageItem = ({
t.name === 'light' ? t.palette.primary_200 : t.palette.primary_800
const rt = useMemo(() => {
- return new RichTextAPI({text: item.text, facets: item.facets})
- }, [item.text, item.facets])
+ return new RichTextAPI({text: message.text, facets: message.facets})
+ }, [message.text, message.facets])
return (
-
+
-
+
+ {isLastInGroup && (
+
+ )}
)
}
@@ -117,16 +123,26 @@ MessageItem = React.memo(MessageItem)
export {MessageItem}
let MessageItemMetadata = ({
- message,
- isLastInGroup,
+ item,
style,
}: {
- message: ChatBskyConvoDefs.MessageView
- isLastInGroup: boolean
+ item: ConvoItem & {type: 'message' | 'pending-message'}
style: StyleProp
}): React.ReactNode => {
const t = useTheme()
const {_} = useLingui()
+ const {message} = item
+
+ const handleRetry = useCallback(
+ (e: GestureResponderEvent) => {
+ if (item.type === 'pending-message' && item.retry) {
+ e.preventDefault()
+ item.retry()
+ return false
+ }
+ },
+ [item],
+ )
const relativeTimestamp = useCallback(
(timestamp: string) => {
@@ -169,25 +185,47 @@ let MessageItemMetadata = ({
[_],
)
- if (!isLastInGroup) {
- return null
- }
-
return (
-
- {({timeElapsed}) => (
-
- {timeElapsed}
-
+
+
+ {({timeElapsed}) => (
+
+ {timeElapsed}
+
+ )}
+
+
+ {item.type === 'pending-message' && item.retry && (
+ <>
+ {' '}
+ ·{' '}
+
+ {_(msg`Failed to send`)}
+ {' '}
+ ·{' '}
+
+ {_(msg`Retry`)}
+
+ >
)}
-
+
)
}
diff --git a/src/components/dms/MessageReportDialog.tsx b/src/components/dms/MessageReportDialog.tsx
index 6071312e94..cc25732afb 100644
--- a/src/components/dms/MessageReportDialog.tsx
+++ b/src/components/dms/MessageReportDialog.tsx
@@ -245,8 +245,12 @@ function PreviewMessage({message}: {message: ChatBskyConvoDefs.MessageView}) {
/>
diff --git a/src/screens/Messages/Conversation/MessageListError.tsx b/src/screens/Messages/Conversation/MessageListError.tsx
index 38a63b0f12..c6e246a3fb 100644
--- a/src/screens/Messages/Conversation/MessageListError.tsx
+++ b/src/screens/Messages/Conversation/MessageListError.tsx
@@ -26,7 +26,6 @@ export function MessageListError({
msg`This chat was disconnected due to a network error.`,
),
[ConvoItemError.HistoryFailed]: _(msg`Failed to load past messages.`),
- [ConvoItemError.PendingFailed]: _(msg`Failed to send message(s).`),
}[item.code]
}, [_, item.code])
diff --git a/src/screens/Messages/Conversation/MessagesList.tsx b/src/screens/Messages/Conversation/MessagesList.tsx
index dac534cd41..0c1bce17dc 100644
--- a/src/screens/Messages/Conversation/MessagesList.tsx
+++ b/src/screens/Messages/Conversation/MessagesList.tsx
@@ -35,13 +35,7 @@ function MaybeLoader({isLoading}: {isLoading: boolean}) {
function renderItem({item}: {item: ConvoItem}) {
if (item.type === 'message' || item.type === 'pending-message') {
- return (
-
- )
+ return
} else if (item.type === 'deleted-message') {
return Deleted message
} else if (item.type === 'error-recoverable') {
diff --git a/src/state/messages/convo/agent.ts b/src/state/messages/convo/agent.ts
index 6d59e1369b..420eff3497 100644
--- a/src/state/messages/convo/agent.ts
+++ b/src/state/messages/convo/agent.ts
@@ -735,6 +735,8 @@ export class Convo {
}
}
+ private pendingFailed = false
+
async sendMessage(message: ChatBskyConvoSendMessage.InputSchema['message']) {
// Ignore empty messages for now since they have no other purpose atm
if (!message.text.trim()) return
@@ -747,11 +749,9 @@ export class Convo {
id: tempId,
message,
})
- // remove on each send, it might go through now without user having to click
- this.footerItems.delete(ConvoItemError.PendingFailed)
this.commit()
- if (!this.isProcessingPendingMessages) {
+ if (!this.isProcessingPendingMessages && !this.pendingFailed) {
this.processPendingMessages()
}
}
@@ -805,16 +805,7 @@ export class Convo {
this.commit()
} catch (e: any) {
logger.error(e, {context: `Convo: failed to send message`})
- this.footerItems.set(ConvoItemError.PendingFailed, {
- type: 'error-recoverable',
- key: ConvoItemError.PendingFailed,
- code: ConvoItemError.PendingFailed,
- retry: () => {
- this.footerItems.delete(ConvoItemError.PendingFailed)
- this.commit()
- this.batchRetryPendingMessages()
- },
- })
+ this.pendingFailed = true
this.commit()
} finally {
this.isProcessingPendingMessages = false
@@ -868,16 +859,7 @@ export class Convo {
)
} catch (e: any) {
logger.error(e, {context: `Convo: failed to batch retry messages`})
- this.footerItems.set(ConvoItemError.PendingFailed, {
- type: 'error-recoverable',
- key: ConvoItemError.PendingFailed,
- code: ConvoItemError.PendingFailed,
- retry: () => {
- this.footerItems.delete(ConvoItemError.PendingFailed)
- this.commit()
- this.batchRetryPendingMessages()
- },
- })
+ this.pendingFailed = true
this.commit()
}
}
@@ -958,6 +940,7 @@ export class Convo {
key: m.id,
message: {
...m.message,
+ $type: 'chat.bsky.convo.defs#messageView',
id: nanoid(),
rev: '__fake__',
sentAt: new Date().toISOString(),
@@ -968,6 +951,13 @@ export class Convo {
sender: this.sender!,
},
nextMessage: null,
+ retry: this.pendingFailed
+ ? () => {
+ this.pendingFailed = false
+ this.commit()
+ this.batchRetryPendingMessages()
+ }
+ : undefined,
})
})
diff --git a/src/state/messages/convo/types.ts b/src/state/messages/convo/types.ts
index 6ce4d40bd6..3fb0eb6ad3 100644
--- a/src/state/messages/convo/types.ts
+++ b/src/state/messages/convo/types.ts
@@ -35,10 +35,6 @@ export enum ConvoItemError {
* Error fetching past messages
*/
HistoryFailed = 'historyFailed',
- /**
- * Error sending new message
- */
- PendingFailed = 'pendingFailed',
}
export enum ConvoErrorCode {
@@ -83,7 +79,7 @@ export type ConvoDispatch =
export type ConvoItem =
| {
- type: 'message' | 'pending-message'
+ type: 'message'
key: string
message: ChatBskyConvoDefs.MessageView
nextMessage:
@@ -91,6 +87,19 @@ export type ConvoItem =
| ChatBskyConvoDefs.DeletedMessageView
| null
}
+ | {
+ type: 'pending-message'
+ key: string
+ message: ChatBskyConvoDefs.MessageView
+ nextMessage:
+ | ChatBskyConvoDefs.MessageView
+ | ChatBskyConvoDefs.DeletedMessageView
+ | null
+ /**
+ * Retry sending the message. If present, the message is in a failed state.
+ */
+ retry?: () => void
+ }
| {
type: 'deleted-message'
key: string