From ad175fafd7ae1418c24ba3303a76c3240ea672d0 Mon Sep 17 00:00:00 2001 From: Warrick Corfe-Tan Date: Mon, 21 Jun 2021 14:43:27 +1000 Subject: [PATCH] WIP refactoring message component. --- ts/components/conversation/Message.tsx | 1359 ++++++++++++------------ 1 file changed, 684 insertions(+), 675 deletions(-) diff --git a/ts/components/conversation/Message.tsx b/ts/components/conversation/Message.tsx index aa74447ec..8a40cb9d3 100644 --- a/ts/components/conversation/Message.tsx +++ b/ts/components/conversation/Message.tsx @@ -93,6 +93,7 @@ import { } from '../../interactions/messageInteractions'; import { updateUserDetailsModal } from '../../state/ducks/modalDialog'; import { MessageInteraction } from '../../interactions'; +import { useState } from 'react'; // Same as MIN_WIDTH in ImageGrid.tsx const MINIMUM_LINK_PREVIEW_IMAGE_WIDTH = 200; @@ -107,706 +108,746 @@ interface State { const EXPIRATION_CHECK_MINIMUM = 2000; const EXPIRED_DELAY = 600; -class MessageInner extends React.PureComponent { - public handleImageErrorBound: () => void; - - public expirationCheckInterval: any; - public expiredTimeout: any; - public ctxMenuID: string; - - public constructor(props: MessageRegularProps) { - super(props); - - this.handleImageErrorBound = this.handleImageError.bind(this); - this.onReplyPrivate = this.onReplyPrivate.bind(this); - this.handleContextMenu = this.handleContextMenu.bind(this); - this.onAddModerator = this.onAddModerator.bind(this); - this.onRemoveFromModerator = this.onRemoveFromModerator.bind(this); - this.updatePlaybackSpeed = this.updatePlaybackSpeed.bind(this); - - this.state = { - expiring: false, - expired: false, - imageBroken: false, - playbackSpeed: 1, - }; - this.ctxMenuID = `ctx-menu-message-${uuid()}`; - } +const MessageInner = (props: MessageRegularProps) => { + + let handleImageErrorBound: () => void; + + let expirationCheckInterval: any; + let expiredTimeout: any; + let ctxMenuID: string; + + // public constructor(props: MessageRegularProps) { + // super(props); + + // this.handleImageErrorBound = this.handleImageError.bind(this); + // this.onReplyPrivate = this.onReplyPrivate.bind(this); + // this.handleContextMenu = this.handleContextMenu.bind(this); + // this.onAddModerator = this.onAddModerator.bind(this); + // this.onRemoveFromModerator = this.onRemoveFromModerator.bind(this); + // this.updatePlaybackSpeed = this.updatePlaybackSpeed.bind(this); + + // this.state = { + // expiring: false, + // expired: false, + // imageBroken: false, + // playbackSpeed: 1, + // }; + // this.ctxMenuID = `ctx-menu-message-${uuid()}`; + // } - public componentDidMount() { - const { expirationLength } = this.props; - if (!expirationLength) { + const [expiring, setExpiring] = useState(false); + const [expired, setExpired] = useState(false); + const [image, setImageBroken] = useState(false); + const [playbackSpeed, setPlaybackSpeed] = useState(1); + + ctxMenuID = `ctx-menu-message-${uuid()}`; + + + useEffect(() => { + if (!props.expirationLength) { return; } + const { expirationLength } = props; + const increment = getIncrement(expirationLength); const checkFrequency = Math.max(EXPIRATION_CHECK_MINIMUM, increment); - this.checkExpired(); + checkExpired(); - this.expirationCheckInterval = setInterval(() => { - this.checkExpired(); + expirationCheckInterval = setInterval(() => { + checkExpired(); }, checkFrequency); - } - public componentWillUnmount() { - if (this.expirationCheckInterval) { - clearInterval(this.expirationCheckInterval); - } - if (this.expiredTimeout) { - clearTimeout(this.expiredTimeout); - } - } + }, []) - public componentDidUpdate() { - this.checkExpired(); - } - public checkExpired() { + // equivalent to componentWillUpdate + useEffect(() => { + checkExpired(); + + // return occurs on unmount equivalent to componentWillUnmount + return () => { + if (expirationCheckInterval) { + clearInterval(expirationCheckInterval); + } + if (expiredTimeout) { + clearTimeout(expiredTimeout); + } + } + }) + + const checkExpired = () => { const now = Date.now(); - const { isExpired, expirationTimestamp, expirationLength } = this.props; + const { isExpired, expirationTimestamp, expirationLength } = props; if (!expirationTimestamp || !expirationLength) { return; } - if (this.expiredTimeout) { + if (expiredTimeout) { return; } if (isExpired || now >= expirationTimestamp) { - this.setState({ - expiring: true, - }); + setExpiring(true); - const setExpired = () => { - this.setState({ - expired: true, - }); + const triggerSetExpired = () => { + setExpired(true) }; - this.expiredTimeout = setTimeout(setExpired, EXPIRED_DELAY); + expiredTimeout = setTimeout(triggerSetExpired(), EXPIRED_DELAY); } } - public handleImageError() { - this.setState({ - imageBroken: true, - }); - } + const handleImageError = () => { + setImageBroken(true); +} // tslint:disable-next-line max-func-body-length cyclomatic-complexity public renderAttachment() { - const { - id, - attachments, - text, - collapseMetadata, - conversationType, - direction, - quote, - onClickAttachment, - multiSelectMode, - onSelectMessage, - } = this.props; - const { imageBroken } = this.state; - - if (!attachments || !attachments[0]) { - return null; - } - const firstAttachment = attachments[0]; - - // For attachments which aren't full-frame - const withContentBelow = Boolean(text); - const withContentAbove = - Boolean(quote) || (conversationType === 'group' && direction === 'incoming'); - const displayImage = canDisplayImage(attachments); - - if ( - displayImage && - !imageBroken && - ((isImage(attachments) && hasImage(attachments)) || - (isVideo(attachments) && hasVideoScreenshot(attachments))) - ) { - return ( -
- { - if (multiSelectMode) { - onSelectMessage(id); - } else if (onClickAttachment) { - onClickAttachment(attachment); - } - }} - /> -
- ); - } else if (!firstAttachment.pending && isAudio(attachments)) { - return ( -
{ - e.stopPropagation(); + const { + id, + attachments, + text, + collapseMetadata, + conversationType, + direction, + quote, + onClickAttachment, + multiSelectMode, + onSelectMessage, + } = this.props; + const { imageBroken } = this.state; + + if (!attachments || !attachments[0]) { + return null; + } + const firstAttachment = attachments[0]; + + // For attachments which aren't full-frame + const withContentBelow = Boolean(text); + const withContentAbove = + Boolean(quote) || (conversationType === 'group' && direction === 'incoming'); + const displayImage = canDisplayImage(attachments); + + if ( + displayImage && + !imageBroken && + ((isImage(attachments) && hasImage(attachments)) || + (isVideo(attachments) && hasVideoScreenshot(attachments))) + ) { + return ( +
+ { + if (multiSelectMode) { + onSelectMessage(id); + } else if (onClickAttachment) { + onClickAttachment(attachment); + } }} - > - -
- ); - } else { - const { pending, fileName, fileSize, contentType } = firstAttachment; - const extension = getExtensionForDisplay({ contentType, fileName }); - const isDangerous = isFileDangerous(fileName || ''); + /> +
+ ); + } else if (!firstAttachment.pending && isAudio(attachments)) { + return ( +
{ + e.stopPropagation(); + }} + > + +
+ ); + } else { + const { pending, fileName, fileSize, contentType } = firstAttachment; + const extension = getExtensionForDisplay({ contentType, fileName }); + const isDangerous = isFileDangerous(fileName || ''); - return ( -
- {pending ? ( -
- -
- ) : ( -
-
{ - if (this.props?.onDownload) { - e.stopPropagation(); - this.props.onDownload(firstAttachment); - } - }} - > - {extension ? ( -
- {extension} -
- ) : null} -
- {isDangerous ? ( -
-
-
- ) : null} -
- )} -
-
- {fileName} -
+ return ( +
+ {pending ? ( +
+ +
+ ) : ( +
{ + if (this.props?.onDownload) { + e.stopPropagation(); + this.props.onDownload(firstAttachment); + } + }} > - {fileSize} + {extension ? ( +
+ {extension} +
+ ) : null}
+ {isDangerous ? ( +
+
+
+ ) : null} +
+ )} +
+
+ {fileName} +
+
+ {fileSize}
- ); - } +
+ ); } +} // tslint:disable-next-line cyclomatic-complexity public renderPreview() { - const { - attachments, - conversationType, - direction, - onClickLinkPreview, - previews, - quote, - } = this.props; - - // Attachments take precedence over Link Previews - if (attachments && attachments.length) { - return null; - } + const { + attachments, + conversationType, + direction, + onClickLinkPreview, + previews, + quote, + } = this.props; + + // Attachments take precedence over Link Previews + if (attachments && attachments.length) { + return null; + } - if (!previews || previews.length < 1) { - return null; - } + if (!previews || previews.length < 1) { + return null; + } - const first = previews[0]; - if (!first) { - return null; - } + const first = previews[0]; + if (!first) { + return null; + } - const withContentAbove = - Boolean(quote) || (conversationType === 'group' && direction === 'incoming'); + const withContentAbove = + Boolean(quote) || (conversationType === 'group' && direction === 'incoming'); - const previewHasImage = first.image && isImageAttachment(first.image); - const width = first.image && first.image.width; - const isFullSizeImage = width && width >= MINIMUM_LINK_PREVIEW_IMAGE_WIDTH; + const previewHasImage = first.image && isImageAttachment(first.image); + const width = first.image && first.image.width; + const isFullSizeImage = width && width >= MINIMUM_LINK_PREVIEW_IMAGE_WIDTH; - return ( + return ( +
{ + if (onClickLinkPreview) { + onClickLinkPreview(first.url); + } + }} + > + {first.image && previewHasImage && isFullSizeImage ? ( + + ) : null}
{ - if (onClickLinkPreview) { - onClickLinkPreview(first.url); - } - }} > - {first.image && previewHasImage && isFullSizeImage ? ( - + {first.image && previewHasImage && !isFullSizeImage ? ( +
+ {window.i18n('previewThumbnail', +
) : null}
- {first.image && previewHasImage && !isFullSizeImage ? ( -
- {window.i18n('previewThumbnail', -
- ) : null} -
-
{first.title}
-
{first.domain}
-
+
{first.title}
+
{first.domain}
- ); - } +
+ ); +} public renderQuote() { - const { - conversationType, - direction, - quote, - isPublic, - convoId, - id, - multiSelectMode, - } = this.props; - - if (!quote || !quote.authorPhoneNumber) { - return null; - } + const { + conversationType, + direction, + quote, + isPublic, + convoId, + id, + multiSelectMode, + } = this.props; + + if (!quote || !quote.authorPhoneNumber) { + return null; + } - const withContentAbove = conversationType === 'group' && direction === 'incoming'; + const withContentAbove = conversationType === 'group' && direction === 'incoming'; - const shortenedPubkey = PubKey.shorten(quote.authorPhoneNumber); + const shortenedPubkey = PubKey.shorten(quote.authorPhoneNumber); - const displayedPubkey = quote.authorProfileName ? shortenedPubkey : quote.authorPhoneNumber; + const displayedPubkey = quote.authorProfileName ? shortenedPubkey : quote.authorPhoneNumber; - return ( - { - e.preventDefault(); - e.stopPropagation(); - if (multiSelectMode && id) { - this.props.onSelectMessage(id); - return; - } - const { authorPhoneNumber, messageId: quoteId, referencedMessageNotFound } = quote; - quote?.onClick({ - quoteAuthor: authorPhoneNumber, - quoteId, - referencedMessageNotFound, - }); - }} - text={quote.text} - attachment={quote.attachment} - isIncoming={direction === 'incoming'} - conversationType={conversationType} - convoId={convoId} - isPublic={isPublic} - authorPhoneNumber={displayedPubkey} - authorProfileName={quote.authorProfileName} - authorName={quote.authorName} - referencedMessageNotFound={quote.referencedMessageNotFound} - isFromMe={quote.isFromMe} - withContentAbove={withContentAbove} - /> - ); - } + return ( + { + e.preventDefault(); + e.stopPropagation(); + if (multiSelectMode && id) { + this.props.onSelectMessage(id); + return; + } + const { authorPhoneNumber, messageId: quoteId, referencedMessageNotFound } = quote; + quote?.onClick({ + quoteAuthor: authorPhoneNumber, + quoteId, + referencedMessageNotFound, + }); + }} + text={quote.text} + attachment={quote.attachment} + isIncoming={direction === 'incoming'} + conversationType={conversationType} + convoId={convoId} + isPublic={isPublic} + authorPhoneNumber={displayedPubkey} + authorProfileName={quote.authorProfileName} + authorName={quote.authorName} + referencedMessageNotFound={quote.referencedMessageNotFound} + isFromMe={quote.isFromMe} + withContentAbove={withContentAbove} + /> + ); +} public renderAvatar() { - const { - authorAvatarPath, - authorName, - authorPhoneNumber, - authorProfileName, - collapseMetadata, - isAdmin, - conversationType, - direction, - isPublic, - firstMessageOfSeries, - } = this.props; - - if (collapseMetadata || conversationType !== 'group' || direction === 'outgoing') { - return; - } - const userName = authorName || authorProfileName || authorPhoneNumber; - - if (!firstMessageOfSeries) { - return
; - } + const { + authorAvatarPath, + authorName, + authorPhoneNumber, + authorProfileName, + collapseMetadata, + isAdmin, + conversationType, + direction, + isPublic, + firstMessageOfSeries, + } = this.props; + + if (collapseMetadata || conversationType !== 'group' || direction === 'outgoing') { + return; + } + const userName = authorName || authorProfileName || authorPhoneNumber; - return ( -
- { - window.inboxStore?.dispatch( - updateUserDetailsModal({ - conversationId: this.props.convoId, - userName, - authorAvatarPath, - }) - ); - }} - pubkey={authorPhoneNumber} - /> - {isPublic && isAdmin && ( -
-
-
- )} -
- ); + if (!firstMessageOfSeries) { + return
; } - public renderText() { - const { text, direction, status, conversationType, convoId, multiSelectMode } = this.props; + return ( +
+ { + window.inboxStore?.dispatch( + updateUserDetailsModal({ + conversationId: this.props.convoId, + userName, + authorAvatarPath, + }) + ); + }} + pubkey={authorPhoneNumber} + /> + {isPublic && isAdmin && ( +
+
+
+ )} +
+ ); +} - const contents = - direction === 'incoming' && status === 'error' ? window.i18n('incomingError') : text; + public renderText() { + const { text, direction, status, conversationType, convoId, multiSelectMode } = this.props; - if (!contents) { - return null; - } + const contents = + direction === 'incoming' && status === 'error' ? window.i18n('incomingError') : text; - return ( -
- -
- ); + if (!contents) { + return null; } - public renderError(isCorrectSide: boolean) { - const { status, direction } = this.props; + return ( +
+ +
+ ); +} - if (!isCorrectSide || status !== 'error') { - return null; - } + public renderError(isCorrectSide: boolean) { + const { status, direction } = this.props; - return ( -
-
-
- ); + if (!isCorrectSide || status !== 'error') { + return null; } - public renderContextMenu() { - const { - attachments, - authorPhoneNumber, - convoId, - onCopyText, - direction, - status, - isDeletable, - id, - onSelectMessage, - onDeleteMessage, - onDownload, - onRetrySend, - onShowDetail, - isPublic, - isOpenGroupV2, - weAreAdmin, - isAdmin, - } = this.props; - - const showRetry = status === 'error' && direction === 'outgoing'; - const multipleAttachments = attachments && attachments.length > 1; - - const onContextMenuShown = () => { - window.contextMenuShown = true; - }; - - const onContextMenuHidden = () => { - // This function will called before the click event - // on the message would trigger (and I was unable to - // prevent propagation in this case), so use a short timeout - setTimeout(() => { - window.contextMenuShown = false; - }, 100); - }; - - const selectMessageText = window.i18n('selectMessage'); - const deleteMessageText = window.i18n('deleteMessage'); + return ( +
+
+
+ ); +} - return ( - - {!multipleAttachments && attachments && attachments[0] ? ( - { - if (onDownload) { - onDownload(attachments[0]); - } - }} - > - {window.i18n('downloadAttachment')} - - ) : null} + public renderContextMenu() { + const { + attachments, + authorPhoneNumber, + convoId, + onCopyText, + direction, + status, + isDeletable, + id, + onSelectMessage, + onDeleteMessage, + onDownload, + onRetrySend, + onShowDetail, + isPublic, + isOpenGroupV2, + weAreAdmin, + isAdmin, + } = this.props; + + const showRetry = status === 'error' && direction === 'outgoing'; + const multipleAttachments = attachments && attachments.length > 1; + + const onContextMenuShown = () => { + window.contextMenuShown = true; + }; + + const onContextMenuHidden = () => { + // This function will called before the click event + // on the message would trigger (and I was unable to + // prevent propagation in this case), so use a short timeout + setTimeout(() => { + window.contextMenuShown = false; + }, 100); + }; + + const selectMessageText = window.i18n('selectMessage'); + const deleteMessageText = window.i18n('deleteMessage'); - {isAudio(attachments) ? ( - - {window.i18n('playAtCustomSpeed', this.state.playbackSpeed === 1 ? 2 : 1)} - - ) : null} - {window.i18n('copyMessage')} - {window.i18n('replyToMessage')} - {window.i18n('moreInformation')} - {showRetry ? {window.i18n('resend')} : null} - {isDeletable ? ( - <> - { - onSelectMessage(id); - }} - > - {selectMessageText} - - { - onDeleteMessage(id); - }} - > - {deleteMessageText} - - - ) : null} - {weAreAdmin && isPublic ? ( + return ( + + {!multipleAttachments && attachments && attachments[0] ? ( + { + if (onDownload) { + onDownload(attachments[0]); + } + }} + > + {window.i18n('downloadAttachment')} + + ) : null} + + {isAudio(attachments) ? ( + + {window.i18n('playAtCustomSpeed', this.state.playbackSpeed === 1 ? 2 : 1)} + + ) : null} + {window.i18n('copyMessage')} + {window.i18n('replyToMessage')} + {window.i18n('moreInformation')} + {showRetry ? {window.i18n('resend')} : null} + {isDeletable ? ( + <> { - MessageInteraction.banUser(authorPhoneNumber, convoId); + onSelectMessage(id); }} > - {window.i18n('banUser')} + {selectMessageText} - ) : null} - {weAreAdmin && isOpenGroupV2 ? ( { - MessageInteraction.unbanUser(authorPhoneNumber, convoId); + onDeleteMessage(id); }} > - {window.i18n('unbanUser')} + {deleteMessageText} - ) : null} - {weAreAdmin && isPublic && !isAdmin ? ( - {window.i18n('addAsModerator')} - ) : null} - {weAreAdmin && isPublic && isAdmin ? ( - {window.i18n('removeFromModerators')} - ) : null} - - ); - } + + ) : null} + {weAreAdmin && isPublic ? ( + { + MessageInteraction.banUser(authorPhoneNumber, convoId); + }} + > + {window.i18n('banUser')} + + ) : null} + {weAreAdmin && isOpenGroupV2 ? ( + { + MessageInteraction.unbanUser(authorPhoneNumber, convoId); + }} + > + {window.i18n('unbanUser')} + + ) : null} + {weAreAdmin && isPublic && !isAdmin ? ( + {window.i18n('addAsModerator')} + ) : null} + {weAreAdmin && isPublic && isAdmin ? ( + {window.i18n('removeFromModerators')} + ) : null} + + ); +} public getWidth(): number | undefined { - const { attachments, previews } = this.props; + const { attachments, previews } = this.props; - if (attachments && attachments.length) { - const dimensions = getGridDimensions(attachments); - if (dimensions) { - return dimensions.width; - } + if (attachments && attachments.length) { + const dimensions = getGridDimensions(attachments); + if (dimensions) { + return dimensions.width; } + } - if (previews && previews.length) { - const first = previews[0]; + if (previews && previews.length) { + const first = previews[0]; - if (!first || !first.image) { - return; - } - const { width } = first.image; + if (!first || !first.image) { + return; + } + const { width } = first.image; - if (isImageAttachment(first.image) && width && width >= MINIMUM_LINK_PREVIEW_IMAGE_WIDTH) { - const dimensions = getImageDimensions(first.image); - if (dimensions) { - return dimensions.width; - } + if (isImageAttachment(first.image) && width && width >= MINIMUM_LINK_PREVIEW_IMAGE_WIDTH) { + const dimensions = getImageDimensions(first.image); + if (dimensions) { + return dimensions.width; } } - - return; } - public isShowingImage(): boolean { - const { attachments, previews } = this.props; - const { imageBroken } = this.state; + return; +} - if (imageBroken) { - return false; - } + public isShowingImage(): boolean { + const { attachments, previews } = this.props; + const { imageBroken } = this.state; - if (attachments && attachments.length) { - const displayImage = canDisplayImage(attachments); + if (imageBroken) { + return false; + } - return Boolean( - displayImage && - ((isImage(attachments) && hasImage(attachments)) || - (isVideo(attachments) && hasVideoScreenshot(attachments))) - ); - } + if (attachments && attachments.length) { + const displayImage = canDisplayImage(attachments); - if (previews && previews.length) { - const first = previews[0]; - const { image } = first; + return Boolean( + displayImage && + ((isImage(attachments) && hasImage(attachments)) || + (isVideo(attachments) && hasVideoScreenshot(attachments))) + ); + } - if (!image) { - return false; - } + if (previews && previews.length) { + const first = previews[0]; + const { image } = first; - return isImageAttachment(image); + if (!image) { + return false; } - return false; + return isImageAttachment(image); } + return false; +} + // tslint:disable-next-line: cyclomatic-complexity public render() { - const { - direction, - id, - selected, - multiSelectMode, - conversationType, - isPublic, - text, - isUnread, - markRead, - } = this.props; - const { expired, expiring } = this.state; - - if (expired) { - return null; - } + const { + direction, + id, + selected, + multiSelectMode, + conversationType, + isPublic, + text, + isUnread, + markRead, + } = this.props; + const { expired, expiring } = this.state; + + if (expired) { + return null; + } - const width = this.getWidth(); - const isShowingImage = this.isShowingImage(); + const width = this.getWidth(); + const isShowingImage = this.isShowingImage(); - const isIncoming = direction === 'incoming'; - const shouldMarkReadWhenVisible = isIncoming && isUnread; - const divClasses = ['session-message-wrapper']; + const isIncoming = direction === 'incoming'; + const shouldMarkReadWhenVisible = isIncoming && isUnread; + const divClasses = ['session-message-wrapper']; - if (selected) { - divClasses.push('message-selected'); - } + if (selected) { + divClasses.push('message-selected'); + } - if (conversationType === 'group') { - divClasses.push('public-chat-message-wrapper'); - } + if (conversationType === 'group') { + divClasses.push('public-chat-message-wrapper'); + } - if (this.props.isQuotedMessageToAnimate) { - divClasses.push('flash-green-once'); + if (this.props.isQuotedMessageToAnimate) { + divClasses.push('flash-green-once'); + } + + const onVisible = (inView: boolean) => { + if (inView && shouldMarkReadWhenVisible) { + // mark the message as read. + // this will trigger the expire timer. + void markRead(Date.now()); } + }; - const onVisible = (inView: boolean) => { - if (inView && shouldMarkReadWhenVisible) { - // mark the message as read. - // this will trigger the expire timer. - void markRead(Date.now()); - } - }; + return ( + + {this.renderAvatar()} +
{ + const selection = window.getSelection(); + // Text is being selected + if (selection && selection.type === 'Range') { + return; + } - return ( - - {this.renderAvatar()} + {this.renderError(isIncoming)} +
{ const selection = window.getSelection(); @@ -817,10 +858,7 @@ class MessageInner extends React.PureComponent { // User clicked on message body const target = event.target as HTMLDivElement; - if ( - (!multiSelectMode && target.className === 'text-selectable') || - window.contextMenuShown - ) { + if (target.className === 'text-selectable' || window.contextMenuShown) { return; } @@ -829,122 +867,93 @@ class MessageInner extends React.PureComponent { } }} > - {this.renderError(isIncoming)} - -
{ - const selection = window.getSelection(); - // Text is being selected - if (selection && selection.type === 'Range') { - return; - } - - // User clicked on message body - const target = event.target as HTMLDivElement; - if (target.className === 'text-selectable' || window.contextMenuShown) { - return; - } - - if (id) { - this.props.onSelectMessage(id); - } - }} - > - {this.renderAuthor()} - {this.renderQuote()} - {this.renderAttachment()} - {this.renderPreview()} - {this.renderText()} - -
- {this.renderError(!isIncoming)} - {this.renderContextMenu()} + {this.renderAuthor()} + {this.renderQuote()} + {this.renderAttachment()} + {this.renderPreview()} + {this.renderText()} +
-
- ); - } + {this.renderError(!isIncoming)} + {this.renderContextMenu()} +
+
+ ); +} /** * Doubles / halves the playback speed based on the current playback speed. */ private updatePlaybackSpeed() { - this.setState({ - ...this.state, - playbackSpeed: this.state.playbackSpeed === 1 ? 2 : 1, - }); - } + this.setState({ + ...this.state, + playbackSpeed: this.state.playbackSpeed === 1 ? 2 : 1, + }); +} private handleContextMenu(e: any) { - e.preventDefault(); - e.stopPropagation(); - const { multiSelectMode, isKickedFromGroup } = this.props; - const enableContextMenu = !multiSelectMode && !isKickedFromGroup; - - if (enableContextMenu) { - // Don't forget to pass the id and the event and voila! - contextMenu.hideAll(); - contextMenu.show({ - id: this.ctxMenuID, - event: e, - }); - } + e.preventDefault(); + e.stopPropagation(); + const { multiSelectMode, isKickedFromGroup } = this.props; + const enableContextMenu = !multiSelectMode && !isKickedFromGroup; + + if (enableContextMenu) { + // Don't forget to pass the id and the event and voila! + contextMenu.hideAll(); + contextMenu.show({ + id: this.ctxMenuID, + event: e, + }); } +} private renderAuthor() { - const { - authorName, - authorPhoneNumber, - authorProfileName, - conversationType, - direction, - isPublic, - } = this.props; - - const title = authorName ? authorName : authorPhoneNumber; - - if (direction !== 'incoming' || conversationType !== 'group' || !title) { - return null; - } + const { + authorName, + authorPhoneNumber, + authorProfileName, + conversationType, + direction, + isPublic, + } = this.props; + + const title = authorName ? authorName : authorPhoneNumber; + + if (direction !== 'incoming' || conversationType !== 'group' || !title) { + return null; + } - const shortenedPubkey = PubKey.shorten(authorPhoneNumber); + const shortenedPubkey = PubKey.shorten(authorPhoneNumber); - const displayedPubkey = authorProfileName ? shortenedPubkey : authorPhoneNumber; + const displayedPubkey = authorProfileName ? shortenedPubkey : authorPhoneNumber; - return ( -
- -
- ); - } + return ( +
+ +
+ ); +} private onReplyPrivate(e: any) { - if (this.props && this.props.onReply) { - this.props.onReply(this.props.timestamp); - } + if (this.props && this.props.onReply) { + this.props.onReply(this.props.timestamp); } +} private async onAddModerator() { - await addSenderAsModerator(this.props.authorPhoneNumber, this.props.convoId); - } + await addSenderAsModerator(this.props.authorPhoneNumber, this.props.convoId); +} private async onRemoveFromModerator() { - await removeSenderFromModerator(this.props.authorPhoneNumber, this.props.convoId); - } + await removeSenderFromModerator(this.props.authorPhoneNumber, this.props.convoId); +} } export const Message = withTheme(MessageInner);