React = require 'react' classNames = require 'classnames' _ = require 'underscore' EmailFrame = require './email-frame' MessageParticipants = require "./message-participants" MessageTimestamp = require "./message-timestamp" MessageControls = require './message-controls' {Utils, Actions, MessageUtils, AccountStore, MessageStore, MessageBodyProcessor, QuotedHTMLParser, ComponentRegistry, FileDownloadStore} = require 'nylas-exports' {RetinaImg, InjectedComponentSet, InjectedComponent} = require 'nylas-component-kit' TransparentPixel = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGNikAQAACIAHF/uBd8AAAAASUVORK5CYII=" class MessageItem extends React.Component @displayName = 'MessageItem' @propTypes = thread: React.PropTypes.object.isRequired message: React.PropTypes.object.isRequired collapsed: React.PropTypes.bool constructor: (@props) -> @state = # Holds the downloadData (if any) for all of our files. It's a hash # keyed by a fileId. The value is the downloadData. downloads: FileDownloadStore.downloadDataForFiles(@props.message.fileIds()) showQuotedText: @_isForwardedMessage() detailedHeaders: false componentDidMount: => @_storeUnlisten = FileDownloadStore.listen(@_onDownloadStoreChange) componentWillUnmount: => @_storeUnlisten() if @_storeUnlisten shouldComponentUpdate: (nextProps, nextState) => not Utils.isEqualReact(nextProps, @props) or not Utils.isEqualReact(nextState, @state) render: => if @props.collapsed @_renderCollapsed() else @_renderFull() _renderCollapsed: => attachmentIcon = [] if @props.message.files.length > 0 attachmentIcon =
{@props.message.from?[0]?.displayFirstName()}
{@props.message.snippet}
{attachmentIcon}
_renderFull: =>
{@_renderHeader()} {@_renderQuotedTextControl()} {@_renderEvents()} {@_renderAttachments()}
_renderQuotedTextControl: -> if QuotedHTMLParser.hasQuotedHTML(@props.message.body) text = if @state.showQuotedText then "Hide" else "Show" •••{text} previous else return null _renderHeader: => classes = classNames "message-header": true "pending": @props.pending
{@_renderHeaderSideItems()}
{@_renderMessageControls()}
{@_renderFolder()} {@_renderHeaderDetailToggle()}
_renderMessageControls: -> _renderFolder: => return [] unless @state.detailedHeaders and @props.message.folder
Folder: 
{@props.message.folder.displayName}
_onClickParticipants: (e) => el = e.target while el isnt e.currentTarget if "collapsed-participants" in el.classList @setState(detailedHeaders: true) e.stopPropagation() return el = el.parentElement return _onClickHeader: (e) => return if @state.detailedHeaders el = e.target while el isnt e.currentTarget wl = ["message-header-right", "collapsed-participants", "header-toggle-control"] if "message-header-right" in el.classList then return if "collapsed-participants" in el.classList then return el = el.parentElement @_toggleCollapsed() _renderAttachments: => attachments = @_attachmentComponents() if attachments.length > 0
{attachments}
else
_renderEvents: => events = @_eventComponents() if events.length > 0 and not Utils.looksLikeGmailInvite(@props.message)
{events}
else
_renderHeaderSideItems: -> styles = position: "absolute" marginTop: -2
_renderHeaderDetailToggle: => return null if @props.pending if @state.detailedHeaders
@setState(detailedHeaders: false); e.stopPropagation()}>
else
@setState(detailedHeaders: true); e.stopPropagation()}>
# Eventually, _formatBody will run a series of registered body transformers. # For now, it just runs a few we've hardcoded here, which are all synchronous. _formatBody: => return "" unless @props.message and @props.message.body # Runs extensions, potentially asynchronous soon body = MessageBodyProcessor.process(@props.message) # Replace cid:// references with the paths to downloaded files for file in @props.message.files continue if @state.downloads[file.id] cidLink = "cid:#{file.contentId}" fileLink = "#{FileDownloadStore.pathForFile(file)}" body = body.replace(cidLink, fileLink) # Replace remaining cid:// references - we will not display them since they'll # throw "unknown ERR_UNKNOWN_URL_SCHEME". Show a transparent pixel so that there's # no "missing image" region shown, just a space. body = body.replace(MessageUtils.cidRegex, "src=\"#{TransparentPixel}\"") body _toggleQuotedText: => @setState showQuotedText: !@state.showQuotedText _toggleCollapsed: => return if @props.isLastMsg Actions.toggleMessageIdExpanded(@props.message.id) _formatContacts: (contacts=[]) => _attachmentComponents: => imageAttachments = [] otherAttachments = [] for file in (@props.message.files ? []) continue unless @_isRealFile(file) if Utils.looksLikeImage(file) imageAttachments.push(file) else otherAttachments.push(file) otherAttachments = otherAttachments.map (file) => imageAttachments = imageAttachments.map (file) => props = file: file download: @state.downloads[file.id] targetPath: FileDownloadStore.pathForFile(file) return otherAttachments.concat(imageAttachments) _eventComponents: => events = @props.message.events.map (e) => return events _isRealFile: (file) -> hasCIDInBody = file.contentId? and @props.message.body?.indexOf(file.contentId) > 0 return not hasCIDInBody _isForwardedMessage: => Utils.isForwardedMessage(@props.message) _onDownloadStoreChange: => @setState downloads: FileDownloadStore.downloadDataForFiles(@props.message.fileIds()) module.exports = MessageItem