Migrate conversations to ESLint

This commit is contained in:
Chris Svenningsen 2020-09-14 12:51:27 -07:00 committed by Josh Perez
parent b4f0f3c685
commit 372aa44e49
90 changed files with 1261 additions and 1165 deletions

View file

@ -3,6 +3,7 @@ import ReactDOM, { createPortal } from 'react-dom';
import classNames from 'classnames';
import Measure from 'react-measure';
import { drop, groupBy, orderBy, take } from 'lodash';
import { ContextMenu, ContextMenuTrigger, MenuItem } from 'react-contextmenu';
import { Manager, Popper, Reference } from 'react-popper';
import moment, { Moment } from 'moment';
@ -24,6 +25,7 @@ import { Props as ReactionPickerProps, ReactionPicker } from './ReactionPicker';
import { Emoji } from '../emoji/Emoji';
import {
AttachmentType,
canDisplayImage,
getExtensionForDisplay,
getGridDimensions,
@ -34,8 +36,7 @@ import {
isImage,
isImageAttachment,
isVideo,
} from '../../../ts/types/Attachment';
import { AttachmentType } from '../../types/Attachment';
} from '../../types/Attachment';
import { ContactType } from '../../types/Contact';
import { getIncrement } from '../../util/timer';
@ -43,7 +44,6 @@ import { isFileDangerous } from '../../util/isFileDangerous';
import { BodyRangesType, LocalizerType } from '../../types/Util';
import { ColorType } from '../../types/Colors';
import { createRefMerger } from '../_util';
import { ContextMenu, ContextMenuTrigger, MenuItem } from 'react-contextmenu';
interface Trigger {
handleContextClick: (event: React.MouseEvent<HTMLDivElement>) => void;
@ -209,18 +209,24 @@ const EXPIRED_DELAY = 600;
export class Message extends React.PureComponent<Props, State> {
public menuTriggerRef: Trigger | undefined;
public audioRef: React.RefObject<HTMLAudioElement> = React.createRef();
public focusRef: React.RefObject<HTMLDivElement> = React.createRef();
public reactionsContainerRef: React.RefObject<
HTMLDivElement
> = React.createRef();
public reactionsContainerRefMerger = createRefMerger();
public wideMl: MediaQueryList;
public expirationCheckInterval: any;
public expiredTimeout: any;
public selectedTimeout: any;
public expirationCheckInterval: NodeJS.Timeout | undefined;
public expiredTimeout: NodeJS.Timeout | undefined;
public selectedTimeout: NodeJS.Timeout | undefined;
public constructor(props: Props) {
super(props);
@ -268,24 +274,23 @@ export class Message extends React.PureComponent<Props, State> {
return state;
}
public handleWideMlChange = (event: MediaQueryListEvent) => {
public handleWideMlChange = (event: MediaQueryListEvent): void => {
this.setState({ isWide: event.matches });
};
public captureMenuTrigger = (triggerRef: Trigger) => {
public captureMenuTrigger = (triggerRef: Trigger): void => {
this.menuTriggerRef = triggerRef;
};
public showMenu = (event: React.MouseEvent<HTMLDivElement>) => {
public showMenu = (event: React.MouseEvent<HTMLDivElement>): void => {
if (this.menuTriggerRef) {
this.menuTriggerRef.handleContextClick(event);
}
};
public handleImageError = () => {
public handleImageError = (): void => {
const { id } = this.props;
// tslint:disable-next-line no-console
console.log(
window.log.info(
`Message ${id}: Image failed to load; failing over to placeholder`
);
this.setState({
@ -293,7 +298,7 @@ export class Message extends React.PureComponent<Props, State> {
});
};
public handleFocus = () => {
public handleFocus = (): void => {
const { interactionMode } = this.props;
if (interactionMode === 'keyboard') {
@ -301,7 +306,7 @@ export class Message extends React.PureComponent<Props, State> {
}
};
public setSelected = () => {
public setSelected = (): void => {
const { id, conversationId, selectMessage } = this.props;
if (selectMessage) {
@ -309,7 +314,7 @@ export class Message extends React.PureComponent<Props, State> {
}
};
public setFocus = () => {
public setFocus = (): void => {
const container = this.focusRef.current;
if (container && !container.contains(document.activeElement)) {
@ -317,7 +322,7 @@ export class Message extends React.PureComponent<Props, State> {
}
};
public componentDidMount() {
public componentDidMount(): void {
this.startSelectedTimer();
const { isSelected } = this.props;
@ -340,7 +345,7 @@ export class Message extends React.PureComponent<Props, State> {
}, checkFrequency);
}
public componentWillUnmount() {
public componentWillUnmount(): void {
if (this.selectedTimeout) {
clearInterval(this.selectedTimeout);
}
@ -356,18 +361,20 @@ export class Message extends React.PureComponent<Props, State> {
this.wideMl.removeEventListener('change', this.handleWideMlChange);
}
public componentDidUpdate(prevProps: Props) {
public componentDidUpdate(prevProps: Props): void {
const { isSelected } = this.props;
this.startSelectedTimer();
if (!prevProps.isSelected && this.props.isSelected) {
if (!prevProps.isSelected && isSelected) {
this.setFocus();
}
this.checkExpired();
}
public startSelectedTimer() {
const { interactionMode } = this.props;
public startSelectedTimer(): void {
const { clearSelectedMessage, interactionMode } = this.props;
const { isSelected } = this.state;
if (interactionMode === 'keyboard' || !isSelected) {
@ -378,12 +385,12 @@ export class Message extends React.PureComponent<Props, State> {
this.selectedTimeout = setTimeout(() => {
this.selectedTimeout = undefined;
this.setState({ isSelected: false });
this.props.clearSelectedMessage();
clearSelectedMessage();
}, SELECTED_TIMEOUT);
}
}
public checkExpired() {
public checkExpired(): void {
const now = Date.now();
const { isExpired, expirationTimestamp, expirationLength } = this.props;
@ -408,7 +415,7 @@ export class Message extends React.PureComponent<Props, State> {
}
}
public renderTimestamp() {
public renderTimestamp(): JSX.Element {
const {
direction,
i18n,
@ -442,6 +449,7 @@ export class Message extends React.PureComponent<Props, State> {
i18n('sendFailed')
) : (
<button
type="button"
className="module-message__metadata__tapable"
onClick={(event: React.MouseEvent) => {
event.stopPropagation();
@ -463,7 +471,7 @@ export class Message extends React.PureComponent<Props, State> {
<Timestamp
i18n={i18n}
timestamp={timestamp}
extended={true}
extended
direction={metadataDirection}
withImageNoCaption={withImageNoCaption}
withSticker={isSticker}
@ -473,8 +481,7 @@ export class Message extends React.PureComponent<Props, State> {
);
}
// tslint:disable-next-line cyclomatic-complexity
public renderMetadata() {
public renderMetadata(): JSX.Element | null {
const {
collapseMetadata,
direction,
@ -548,7 +555,7 @@ export class Message extends React.PureComponent<Props, State> {
);
}
public renderAuthor() {
public renderAuthor(): JSX.Element | null {
const {
authorTitle,
authorName,
@ -564,7 +571,7 @@ export class Message extends React.PureComponent<Props, State> {
} = this.props;
if (collapseMetadata) {
return;
return null;
}
if (
@ -597,8 +604,7 @@ export class Message extends React.PureComponent<Props, State> {
);
}
// tslint:disable-next-line max-func-body-length cyclomatic-complexity
public renderAttachment() {
public renderAttachment(): JSX.Element | null {
const {
attachments,
collapseMetadata,
@ -667,11 +673,12 @@ export class Message extends React.PureComponent<Props, State> {
/>
</div>
);
} else if (!firstAttachment.pending && isAudio(attachments)) {
}
if (!firstAttachment.pending && isAudio(attachments)) {
return (
<audio
ref={this.audioRef}
controls={true}
controls
className={classNames(
'module-message__audio-attachment',
withContentBelow
@ -686,83 +693,82 @@ export class Message extends React.PureComponent<Props, State> {
<source src={firstAttachment.url} />
</audio>
);
} else {
const { pending, fileName, fileSize, contentType } = firstAttachment;
const extension = getExtensionForDisplay({ contentType, fileName });
const isDangerous = isFileDangerous(fileName || '');
}
const { pending, fileName, fileSize, contentType } = firstAttachment;
const extension = getExtensionForDisplay({ contentType, fileName });
const isDangerous = isFileDangerous(fileName || '');
return (
<button
className={classNames(
'module-message__generic-attachment',
withContentBelow
? 'module-message__generic-attachment--with-content-below'
: null,
withContentAbove
? 'module-message__generic-attachment--with-content-above'
: null,
!firstAttachment.url
? 'module-message__generic-attachment--not-active'
: null
)}
// There's only ever one of these, so we don't want users to tab into it
tabIndex={-1}
onClick={(event: React.MouseEvent) => {
event.stopPropagation();
event.preventDefault();
return (
<button
type="button"
className={classNames(
'module-message__generic-attachment',
withContentBelow
? 'module-message__generic-attachment--with-content-below'
: null,
withContentAbove
? 'module-message__generic-attachment--with-content-above'
: null,
!firstAttachment.url
? 'module-message__generic-attachment--not-active'
: null
)}
// There's only ever one of these, so we don't want users to tab into it
tabIndex={-1}
onClick={(event: React.MouseEvent) => {
event.stopPropagation();
event.preventDefault();
if (!firstAttachment.url) {
return;
}
if (!firstAttachment.url) {
return;
}
this.openGenericAttachment();
}}
>
{pending ? (
<div className="module-message__generic-attachment__spinner-container">
<Spinner svgSize="small" size="24px" direction={direction} />
</div>
) : (
<div className="module-message__generic-attachment__icon-container">
<div className="module-message__generic-attachment__icon">
{extension ? (
<div className="module-message__generic-attachment__icon__extension">
{extension}
</div>
) : null}
</div>
{isDangerous ? (
<div className="module-message__generic-attachment__icon-dangerous-container">
<div className="module-message__generic-attachment__icon-dangerous" />
this.openGenericAttachment();
}}
>
{pending ? (
<div className="module-message__generic-attachment__spinner-container">
<Spinner svgSize="small" size="24px" direction={direction} />
</div>
) : (
<div className="module-message__generic-attachment__icon-container">
<div className="module-message__generic-attachment__icon">
{extension ? (
<div className="module-message__generic-attachment__icon__extension">
{extension}
</div>
) : null}
</div>
)}
<div className="module-message__generic-attachment__text">
<div
className={classNames(
'module-message__generic-attachment__file-name',
`module-message__generic-attachment__file-name--${direction}`
)}
>
{fileName}
</div>
<div
className={classNames(
'module-message__generic-attachment__file-size',
`module-message__generic-attachment__file-size--${direction}`
)}
>
{fileSize}
</div>
{isDangerous ? (
<div className="module-message__generic-attachment__icon-dangerous-container">
<div className="module-message__generic-attachment__icon-dangerous" />
</div>
) : null}
</div>
</button>
);
}
)}
<div className="module-message__generic-attachment__text">
<div
className={classNames(
'module-message__generic-attachment__file-name',
`module-message__generic-attachment__file-name--${direction}`
)}
>
{fileName}
</div>
<div
className={classNames(
'module-message__generic-attachment__file-size',
`module-message__generic-attachment__file-size--${direction}`
)}
>
{fileSize}
</div>
</div>
</button>
);
}
// tslint:disable-next-line cyclomatic-complexity max-func-body-length
public renderPreview() {
public renderPreview(): JSX.Element | null {
const {
attachments,
conversationType,
@ -809,6 +815,7 @@ export class Message extends React.PureComponent<Props, State> {
return (
<button
type="button"
className={classNames(
'module-message__link-preview',
`module-message__link-preview--${direction}`,
@ -835,7 +842,7 @@ export class Message extends React.PureComponent<Props, State> {
<ImageGrid
attachments={[first.image]}
withContentAbove={withContentAbove}
withContentBelow={true}
withContentBelow
onError={this.handleImageError}
i18n={i18n}
/>
@ -852,9 +859,9 @@ export class Message extends React.PureComponent<Props, State> {
<div className="module-message__link-preview__icon_container">
<Image
smallCurveTopLeft={!withContentAbove}
noBorder={true}
noBackground={true}
softCorners={true}
noBorder
noBackground
softCorners
alt={i18n('previewThumbnail', [first.domain])}
height={72}
width={72}
@ -900,7 +907,7 @@ export class Message extends React.PureComponent<Props, State> {
);
}
public renderQuote() {
public renderQuote(): JSX.Element | null {
const {
conversationType,
authorColor,
@ -952,7 +959,7 @@ export class Message extends React.PureComponent<Props, State> {
);
}
public renderEmbeddedContact() {
public renderEmbeddedContact(): JSX.Element | null {
const {
collapseMetadata,
contact,
@ -989,7 +996,7 @@ export class Message extends React.PureComponent<Props, State> {
);
}
public renderSendMessageButton() {
public renderSendMessageButton(): JSX.Element | null {
const { contact, openConversation, i18n } = this.props;
if (!contact || !contact.signalAccount) {
return null;
@ -997,6 +1004,7 @@ export class Message extends React.PureComponent<Props, State> {
return (
<button
type="button"
onClick={() => {
if (contact.signalAccount) {
openConversation(contact.signalAccount);
@ -1009,7 +1017,7 @@ export class Message extends React.PureComponent<Props, State> {
);
}
public renderAvatar() {
public renderAvatar(): JSX.Element | undefined {
const {
authorAvatarPath,
authorName,
@ -1031,6 +1039,7 @@ export class Message extends React.PureComponent<Props, State> {
return;
}
// eslint-disable-next-line consistent-return
return (
<div className="module-message__author-avatar">
<Avatar
@ -1048,7 +1057,7 @@ export class Message extends React.PureComponent<Props, State> {
);
}
public renderText() {
public renderText(): JSX.Element | null {
const {
bodyRanges,
deletedForEveryone,
@ -1060,6 +1069,7 @@ export class Message extends React.PureComponent<Props, State> {
textPending,
} = this.props;
// eslint-disable-next-line no-nested-ternary
const contents = deletedForEveryone
? i18n('message--deletedForEveryone')
: direction === 'incoming' && status === 'error'
@ -1093,7 +1103,7 @@ export class Message extends React.PureComponent<Props, State> {
);
}
public renderError(isCorrectSide: boolean) {
public renderError(isCorrectSide: boolean): JSX.Element | null {
const { status, direction } = this.props;
if (!isCorrectSide || (status !== 'error' && status !== 'partial-sent')) {
@ -1112,10 +1122,12 @@ export class Message extends React.PureComponent<Props, State> {
);
}
public renderMenu(isCorrectSide: boolean, triggerId: string) {
public renderMenu(
isCorrectSide: boolean,
triggerId: string
): JSX.Element | null {
const {
attachments,
// tslint:disable-next-line max-func-body-length
canReply,
direction,
disableMenu,
@ -1123,8 +1135,10 @@ export class Message extends React.PureComponent<Props, State> {
id,
isSticker,
isTapToView,
reactToMessage,
renderEmojiPicker,
replyToMessage,
selectedReaction,
} = this.props;
if (!isCorrectSide || disableMenu) {
@ -1142,10 +1156,13 @@ export class Message extends React.PureComponent<Props, State> {
!isTapToView &&
firstAttachment &&
!firstAttachment.pending ? (
// This a menu meant for mouse use only
// eslint-disable-next-line max-len
// eslint-disable-next-line jsx-a11y/interactive-supports-focus, jsx-a11y/click-events-have-key-events
<div
onClick={this.openGenericAttachment}
// This a menu meant for mouse use only
role="button"
aria-label={i18n('downloadAttachment')}
className={classNames(
'module-message__buttons__download',
`module-message__buttons__download--${direction}`
@ -1161,6 +1178,9 @@ export class Message extends React.PureComponent<Props, State> {
const maybePopperRef = isWide ? popperRef : undefined;
return (
// This a menu meant for mouse use only
// eslint-disable-next-line max-len
// eslint-disable-next-line jsx-a11y/interactive-supports-focus, jsx-a11y/click-events-have-key-events
<div
ref={maybePopperRef}
onClick={(event: React.MouseEvent) => {
@ -1171,6 +1191,7 @@ export class Message extends React.PureComponent<Props, State> {
}}
role="button"
className="module-message__buttons__react"
aria-label={i18n('reactToMessage')}
/>
);
}}
@ -1178,6 +1199,9 @@ export class Message extends React.PureComponent<Props, State> {
);
const replyButton = (
// This a menu meant for mouse use only
// eslint-disable-next-line max-len
// eslint-disable-next-line jsx-a11y/interactive-supports-focus, jsx-a11y/click-events-have-key-events
<div
onClick={(event: React.MouseEvent) => {
event.stopPropagation();
@ -1187,6 +1211,7 @@ export class Message extends React.PureComponent<Props, State> {
}}
// This a menu meant for mouse use only
role="button"
aria-label={i18n('replyToMessage')}
className={classNames(
'module-message__buttons__reply',
`module-message__buttons__download--${direction}`
@ -1194,6 +1219,9 @@ export class Message extends React.PureComponent<Props, State> {
/>
);
// This a menu meant for mouse use only
/* eslint-disable jsx-a11y/interactive-supports-focus */
/* eslint-disable jsx-a11y/click-events-have-key-events */
const menuButton = (
<Reference>
{({ ref: popperRef }) => {
@ -1205,13 +1233,14 @@ export class Message extends React.PureComponent<Props, State> {
return (
<ContextMenuTrigger
id={triggerId}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
ref={this.captureMenuTrigger as any}
>
<div
// This a menu meant for mouse use only
ref={maybePopperRef}
role="button"
onClick={this.showMenu}
aria-label={i18n('messageContextMenuButton')}
className={classNames(
'module-message__buttons__menu',
`module-message__buttons__download--${direction}`
@ -1222,6 +1251,8 @@ export class Message extends React.PureComponent<Props, State> {
}}
</Reference>
);
/* eslint-enable jsx-a11y/interactive-supports-focus */
/* eslint-enable jsx-a11y/click-events-have-key-events */
return (
<Manager>
@ -1238,19 +1269,20 @@ export class Message extends React.PureComponent<Props, State> {
</div>
{reactionPickerRoot &&
createPortal(
// eslint-disable-next-line consistent-return
<Popper placement="top">
{({ ref, style }) => (
<ReactionPicker
i18n={i18n}
ref={ref}
style={style}
selected={this.props.selectedReaction}
selected={selectedReaction}
onClose={this.toggleReactionPicker}
onPick={emoji => {
this.toggleReactionPicker(true);
this.props.reactToMessage(id, {
reactToMessage(id, {
emoji,
remove: emoji === this.props.selectedReaction,
remove: emoji === selectedReaction,
});
}}
renderEmojiPicker={renderEmojiPicker}
@ -1263,8 +1295,7 @@ export class Message extends React.PureComponent<Props, State> {
);
}
// tslint:disable-next-line max-func-body-length
public renderContextMenu(triggerId: string) {
public renderContextMenu(triggerId: string): JSX.Element {
const {
attachments,
canReply,
@ -1396,7 +1427,7 @@ export class Message extends React.PureComponent<Props, State> {
const first = previews[0];
if (!first || !first.image) {
return;
return undefined;
}
const { width } = first.image;
@ -1414,9 +1445,11 @@ export class Message extends React.PureComponent<Props, State> {
}
}
return;
return undefined;
}
// Messy return here.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
public isShowingImage() {
const { isTapToView, attachments, previews } = this.props;
const { imageBroken } = this.state;
@ -1449,7 +1482,7 @@ export class Message extends React.PureComponent<Props, State> {
return false;
}
public isAttachmentPending() {
public isAttachmentPending(): boolean {
const { attachments } = this.props;
if (!attachments || attachments.length < 1) {
@ -1461,7 +1494,7 @@ export class Message extends React.PureComponent<Props, State> {
return Boolean(first.pending);
}
public renderTapToViewIcon() {
public renderTapToViewIcon(): JSX.Element {
const { direction, isTapToViewExpired } = this.props;
const isDownloadPending = this.isAttachmentPending();
@ -1482,7 +1515,7 @@ export class Message extends React.PureComponent<Props, State> {
);
}
public renderTapToViewText() {
public renderTapToViewText(): string | undefined {
const {
attachments,
direction,
@ -1505,6 +1538,7 @@ export class Message extends React.PureComponent<Props, State> {
return;
}
// eslint-disable-next-line consistent-return, no-nested-ternary
return isTapToViewError
? i18n('incomingError')
: direction === 'outgoing'
@ -1512,7 +1546,7 @@ export class Message extends React.PureComponent<Props, State> {
: incomingString;
}
public renderTapToView() {
public renderTapToView(): JSX.Element {
const {
collapseMetadata,
conversationType,
@ -1558,7 +1592,7 @@ export class Message extends React.PureComponent<Props, State> {
);
}
public toggleReactionViewer = (onlyRemove = false) => {
public toggleReactionViewer = (onlyRemove = false): void => {
this.setState(({ reactionViewerRoot }) => {
if (reactionViewerRoot) {
document.body.removeChild(reactionViewerRoot);
@ -1589,7 +1623,7 @@ export class Message extends React.PureComponent<Props, State> {
});
};
public toggleReactionPicker = (onlyRemove = false) => {
public toggleReactionPicker = (onlyRemove = false): void => {
this.setState(({ reactionPickerRoot }) => {
if (reactionPickerRoot) {
document.body.removeChild(reactionPickerRoot);
@ -1620,7 +1654,7 @@ export class Message extends React.PureComponent<Props, State> {
});
};
public handleClickOutsideReactionViewer = (e: MouseEvent) => {
public handleClickOutsideReactionViewer = (e: MouseEvent): void => {
const { reactionViewerRoot } = this.state;
const { current: reactionsContainer } = this.reactionsContainerRef;
if (reactionViewerRoot && reactionsContainer) {
@ -1633,7 +1667,7 @@ export class Message extends React.PureComponent<Props, State> {
}
};
public handleClickOutsideReactionPicker = (e: MouseEvent) => {
public handleClickOutsideReactionPicker = (e: MouseEvent): void => {
const { reactionPickerRoot } = this.state;
if (reactionPickerRoot) {
if (!reactionPickerRoot.contains(e.target as HTMLElement)) {
@ -1642,8 +1676,7 @@ export class Message extends React.PureComponent<Props, State> {
}
};
// tslint:disable-next-line max-func-body-length
public renderReactions(outgoing: boolean) {
public renderReactions(outgoing: boolean): JSX.Element | null {
const { reactions, i18n } = this.props;
if (!reactions || (reactions && reactions.length === 0)) {
@ -1726,6 +1759,8 @@ export class Message extends React.PureComponent<Props, State> {
return (
<button
type="button"
// eslint-disable-next-line react/no-array-index-key
key={`${re.emoji}-${i}`}
className={classNames(
'module-message__reactions__reaction',
@ -1764,7 +1799,7 @@ export class Message extends React.PureComponent<Props, State> {
+{maybeNotRenderedTotal}
</span>
) : (
<React.Fragment>
<>
<Emoji size={16} emoji={re.emoji} />
{re.count > 1 ? (
<span
@ -1778,7 +1813,7 @@ export class Message extends React.PureComponent<Props, State> {
{re.count}
</span>
) : null}
</React.Fragment>
</>
)}
</button>
);
@ -1808,7 +1843,7 @@ export class Message extends React.PureComponent<Props, State> {
);
}
public renderContents() {
public renderContents(): JSX.Element | null {
const { isTapToView, deletedForEveryone } = this.props;
if (deletedForEveryone) {
@ -1837,10 +1872,9 @@ export class Message extends React.PureComponent<Props, State> {
);
}
// tslint:disable-next-line cyclomatic-complexity max-func-body-length
public handleOpen = (
event: React.KeyboardEvent<HTMLDivElement> | React.MouseEvent
) => {
): void => {
const {
attachments,
contact,
@ -1923,10 +1957,8 @@ export class Message extends React.PureComponent<Props, State> {
event.stopPropagation();
if (this.audioRef.current.paused) {
// tslint:disable-next-line no-floating-promises
this.audioRef.current.play();
} else {
// tslint:disable-next-line no-floating-promises
this.audioRef.current.pause();
}
}
@ -1946,7 +1978,7 @@ export class Message extends React.PureComponent<Props, State> {
}
};
public openGenericAttachment = (event?: React.MouseEvent) => {
public openGenericAttachment = (event?: React.MouseEvent): void => {
const { attachments, downloadAttachment, timestamp } = this.props;
if (event) {
@ -1969,7 +2001,7 @@ export class Message extends React.PureComponent<Props, State> {
});
};
public handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
public handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>): void => {
// Do not allow reactions to error messages
const { canReply } = this.props;
@ -1989,7 +2021,7 @@ export class Message extends React.PureComponent<Props, State> {
this.handleOpen(event);
};
public handleClick = (event: React.MouseEvent) => {
public handleClick = (event: React.MouseEvent): void => {
// We don't want clicks on body text to result in the 'default action' for the message
const { text } = this.props;
if (text && text.length > 0) {
@ -2008,8 +2040,7 @@ export class Message extends React.PureComponent<Props, State> {
this.handleOpen(event);
};
// tslint:disable-next-line: cyclomatic-complexity
public renderContainer() {
public renderContainer(): JSX.Element {
const {
authorColor,
deletedForEveryone,
@ -2061,7 +2092,7 @@ export class Message extends React.PureComponent<Props, State> {
return (
<Measure
bounds={true}
bounds
onResize={({ bounds = { width: 0 } }) => {
this.setState({ containerWidth: bounds.width });
}}
@ -2081,8 +2112,7 @@ export class Message extends React.PureComponent<Props, State> {
);
}
// tslint:disable-next-line cyclomatic-complexity
public render() {
public render(): JSX.Element | null {
const {
authorPhoneNumber,
attachments,