import _ from 'underscore'; import classnames from 'classnames'; import React from 'react'; import { localized, Actions, Contact } from 'mailspring-exports'; const { Menu, MenuItem } = require('@electron/remote'); const MAX_COLLAPSED = 5; interface MessageParticipantsProps { to: Contact[]; cc: Contact[]; bcc: Contact[]; replyTo: Contact[]; from: Contact[]; onClick?: (e: React.MouseEvent) => void; isDetailed: boolean; } export default class MessageParticipants extends React.Component { static displayName = 'MessageParticipants'; static defaultProps = { to: [], cc: [], bcc: [], from: [], replyTo: [], }; // Helpers _allToParticipants() { return _.union(this.props.to, this.props.cc, this.props.bcc); } _shortNames(contacts = [], max = MAX_COLLAPSED) { let names = contacts.map((c, i) => ( {i > 0 && ', '} this._onContactContextMenu(c)}> {c.displayName({ includeAccountLabel: true, compact: !AppEnv.config.get('core.reading.detailedNames'), })} )); if (names.length > max) { const extra = names.length - max; names = names.slice(0, max); names.push(and ${extra} more); } return names; } _onSelectText = e => { e.preventDefault(); e.stopPropagation(); const textNode = e.currentTarget.childNodes[0]; const range = document.createRange(); range.setStart(textNode, 0); range.setEnd(textNode, textNode.length); const selection = document.getSelection(); selection.removeAllRanges(); selection.addRange(range); }; _onContactContextMenu = contact => { const menu = new Menu(); menu.append( window.getSelection()?.type == 'Range' ? new MenuItem({ role: 'copy' }) : new MenuItem({ label: `${localized(`Copy`)} "${contact.email}"`, click: () => navigator.clipboard.writeText(contact.email), }) ); menu.append( new MenuItem({ label: `${localized(`Email`)} ${contact.name ?? contact.email}`, click: () => Actions.composeNewDraftToRecipient(contact), }) ); menu.popup({}); }; _renderFullContacts(contacts = []) { return contacts.map((c, i) => { let comma = ','; if (contacts.length === 1 || i === contacts.length - 1) { comma = ''; } if (c.name && c.name.length > 0 && c.name !== c.email) { return (
this._onContactContextMenu(c)} > {c.fullName()}
{' <'} this._onContactContextMenu(c)} > {c.email} {`>${comma}`}
); } return (
this._onContactContextMenu(c)}> {c.email} {comma}
); }); } _renderExpandedField(name, label, field, { includeLabel = true } = {}) { return (
{includeLabel ? (
{label}: 
) : null}
{this._renderFullContacts(field)}
); } _renderExpanded() { const { from, replyTo, to, cc, bcc } = this.props; const expanded = []; if (from.length > 0) { expanded.push( this._renderExpandedField('from', localized('From'), from, { includeLabel: false }) ); } if (replyTo.length > 0) { expanded.push(this._renderExpandedField('reply-to', localized('Reply to'), replyTo)); } if (to.length > 0) { expanded.push(this._renderExpandedField('to', localized('To'), to)); } if (cc.length > 0) { expanded.push(this._renderExpandedField('cc', localized('Cc'), cc)); } if (bcc.length > 0) { expanded.push(this._renderExpandedField('bcc', localized('Bcc'), bcc)); } return
{expanded}
; } _renderCollapsed() { const childSpans = []; const toParticipants = this._allToParticipants(); if (this.props.from.length > 0) { childSpans.push( {this._shortNames(this.props.from)} ); } if (toParticipants.length > 0) { childSpans.push( {localized('To')}:  , {this._shortNames(toParticipants)} ); } return {childSpans}; } render() { const { isDetailed, from, onClick } = this.props; const classSet = classnames({ participants: true, 'message-participants': true, collapsed: !isDetailed, 'from-participants': from.length > 0, 'to-participants': this._allToParticipants().length > 0, }); return (
{isDetailed ? this._renderExpanded() : this._renderCollapsed()}
); } }