diff options
Diffstat (limited to 'src/components/dms/MessageItem.tsx')
-rw-r--r-- | src/components/dms/MessageItem.tsx | 136 |
1 files changed, 87 insertions, 49 deletions
diff --git a/src/components/dms/MessageItem.tsx b/src/components/dms/MessageItem.tsx index f8ab85188..cafd7ca5a 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 ( <View> - <ActionsWrapper isFromSelf={isFromSelf} message={item}> + <ActionsWrapper isFromSelf={isFromSelf} message={message}> <View style={[ a.py_sm, @@ -82,7 +86,7 @@ let MessageItem = ({ paddingLeft: 14, paddingRight: 14, backgroundColor: isFromSelf - ? pending + ? isPending ? pendingColor : t.palette.primary_500 : t.palette.contrast_50, @@ -98,18 +102,20 @@ let MessageItem = ({ a.text_md, a.leading_snug, isFromSelf && {color: t.palette.white}, - pending && t.name !== 'light' && {color: t.palette.primary_300}, + isPending && t.name !== 'light' && {color: t.palette.primary_300}, ]} interactiveStyle={a.underline} enableTags /> </View> </ActionsWrapper> - <MessageItemMetadata - message={item} - isLastInGroup={isLastInGroup} - style={isFromSelf ? a.text_right : a.text_left} - /> + + {isLastInGroup && ( + <MessageItemMetadata + item={item} + style={isFromSelf ? a.text_right : a.text_left} + /> + )} </View> ) } @@ -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<TextStyle> }): 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 timestamp={message.sentAt} timeToString={relativeTimestamp}> - {({timeElapsed}) => ( - <Text - style={[ - t.atoms.text_contrast_medium, - a.text_xs, - a.mt_2xs, - a.mb_lg, - style, - ]}> - {timeElapsed} - </Text> + <Text + style={[ + a.text_xs, + a.mt_2xs, + a.mb_lg, + t.atoms.text_contrast_medium, + style, + ]}> + <TimeElapsed timestamp={message.sentAt} timeToString={relativeTimestamp}> + {({timeElapsed}) => ( + <Text style={[a.text_xs, t.atoms.text_contrast_medium]}> + {timeElapsed} + </Text> + )} + </TimeElapsed> + + {item.type === 'pending-message' && item.retry && ( + <> + {' '} + ·{' '} + <Text + style={[ + a.text_xs, + { + color: t.palette.negative_400, + }, + ]}> + {_(msg`Failed to send`)} + </Text>{' '} + ·{' '} + <InlineLinkText + label={_(msg`Click to retry failed message`)} + to="#" + onPress={handleRetry} + style={[a.text_xs]}> + {_(msg`Retry`)} + </InlineLinkText> + </> )} - </TimeElapsed> + </Text> ) } |