// Copyright 2018-2020 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; import classNames from 'classnames'; import moment from 'moment'; import { Avatar } from '../Avatar'; import { ContactName } from './ContactName'; import { Message, MessageStatusType, Props as MessageProps } from './Message'; import { LocalizerType } from '../../types/Util'; import { ColorType } from '../../types/Colors'; type Contact = { status: MessageStatusType; title: string; phoneNumber?: string; name?: string; profileName?: string; avatarPath?: string; color: ColorType; isOutgoingKeyError: boolean; isUnidentifiedDelivery: boolean; errors?: Array; onSendAnyway: () => void; onShowSafetyNumber: () => void; }; export type Props = { sentAt: number; receivedAt: number; message: MessageProps; errors: Array; contacts: Array; i18n: LocalizerType; }; const _keyForError = (error: Error): string => { return `${error.name}-${error.message}`; }; export class MessageDetail extends React.Component { private readonly focusRef = React.createRef(); public componentDidMount(): void { // When this component is created, it's initially not part of the DOM, and then it's // added off-screen and animated in. This ensures that the focus takes. setTimeout(() => { if (this.focusRef.current) { this.focusRef.current.focus(); } }); } public renderAvatar(contact: Contact): JSX.Element { const { i18n } = this.props; const { avatarPath, color, phoneNumber, name, profileName, title, } = contact; return ( ); } public renderDeleteButton(): JSX.Element { const { i18n, message } = this.props; return (
); } public renderContact(contact: Contact): JSX.Element { const { i18n } = this.props; const errors = contact.errors || []; const errorComponent = contact.isOutgoingKeyError ? (
) : null; const statusComponent = !contact.isOutgoingKeyError ? (
) : null; const unidentifiedDeliveryComponent = contact.isUnidentifiedDelivery ? (
) : null; return (
{this.renderAvatar(contact)}
{errors.map(error => (
{error.message}
))}
{errorComponent} {unidentifiedDeliveryComponent} {statusComponent}
); } public renderContacts(): JSX.Element | null { const { contacts } = this.props; if (!contacts || !contacts.length) { return null; } return (
{contacts.map(contact => this.renderContact(contact))}
); } public render(): JSX.Element { const { errors, message, receivedAt, sentAt, i18n } = this.props; return ( // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
{(errors || []).map(error => ( ))} {receivedAt ? ( ) : null}
{i18n('error')} {' '} {error.message}{' '}
{i18n('sent')} {moment(sentAt).format('LLLL')}{' '} ({sentAt})
{i18n('received')} {moment(receivedAt).format('LLLL')}{' '} ({receivedAt})
{message.direction === 'incoming' ? i18n('from') : i18n('to')}
{this.renderContacts()} {this.renderDeleteButton()}
); } }