Mailspring/internal_packages/message-list/lib/message-item-container.cjsx
Ben Gotow 9b54d9cf31 fix(MessageContainer): Fix state, prevent from getting different messages. #1175
Summary:
This fixes a serious issue where drafts could appear to be sending if
they were located in the same index of the message list as a draft which was
previously sending.

Under the hood this was due to two bad programming choices:

1) State based on props in MessageContainer requires correct implementation of
   componentWillReceiveProps. This is definitely an anti-pattern.

2) Using item index rather than clientId as the key for items in the MessageList
   caused containers to be given a different message prop when one was inserted,
   rather than just shifting the existing ones down and inserting a new one.

Test Plan: Not sure how to test this really...

Reviewers: drew, juan

Differential Revision: https://phab.nylas.com/D2673
2016-03-03 12:10:03 -08:00

90 lines
2.3 KiB
CoffeeScript

React = require 'react'
classNames = require 'classnames'
MessageItem = require './message-item'
{Utils,
DraftStore,
MessageStore} = require 'nylas-exports'
{InjectedComponent} = require 'nylas-component-kit'
class MessageItemContainer extends React.Component
@displayName = 'MessageItemContainer'
@propTypes =
thread: React.PropTypes.object.isRequired
message: React.PropTypes.object.isRequired
collapsed: React.PropTypes.bool
isLastMsg: React.PropTypes.bool
isBeforeReplyArea: React.PropTypes.bool
scrollTo: React.PropTypes.func
constructor: (@props) ->
@state = @_getStateFromStores()
componentWillReceiveProps: (newProps) ->
@setState(@_getStateFromStores(newProps))
componentDidMount: =>
if @props.message.draft
@_unlisten = DraftStore.listen @_onSendingStateChanged
shouldComponentUpdate: (nextProps, nextState) =>
not Utils.isEqualReact(nextProps, @props) or
not Utils.isEqualReact(nextState, @state)
componentWillUnmount: =>
@_unlisten() if @_unlisten
focus: =>
@refs.message.focus?()
render: =>
if @props.message.draft
if @state.isSending
@_renderMessage(pending: true)
else
@_renderComposer()
else
@_renderMessage(pending: false)
_renderMessage: ({pending}) =>
<MessageItem
ref="message"
pending={pending}
thread={@props.thread}
message={@props.message}
className={@_classNames()}
collapsed={@props.collapsed}
isLastMsg={@props.isLastMsg} />
_renderComposer: =>
exposedProps =
mode: "inline"
draftClientId: @props.message.clientId
threadId: @props.thread.id
scrollTo: @props.scrollTo
<InjectedComponent
ref="message"
matching={role: "Composer"}
className={@_classNames()}
exposedProps={exposedProps}/>
_classNames: => classNames
"draft": @props.message.draft
"unread": @props.message.unread
"collapsed": @props.collapsed
"message-item-wrap": true
"before-reply-area": @props.isBeforeReplyArea
_onSendingStateChanged: (draftClientId) =>
if draftClientId is @props.message.clientId
@setState(@_getStateFromStores())
_getStateFromStores: (props = @props) ->
isSending: DraftStore.isSendingDraft(props.message.clientId)
module.exports = MessageItemContainer