diff --git a/internal_packages/thread-list/lib/thread-list-participants.cjsx b/internal_packages/thread-list/lib/thread-list-participants.cjsx index 0227bfd6c..93cad1f9c 100644 --- a/internal_packages/thread-list/lib/thread-list-participants.cjsx +++ b/internal_packages/thread-list/lib/thread-list-participants.cjsx @@ -16,20 +16,20 @@ ThreadListParticipants = React.createClass if @props.thread.messageMetadata and @props.thread.messageMetadata.length > 1 count = " (#{@props.thread.messageMetadata.length})" - chips = items.map (item, idx) -> - if item.spacer + chips = items.map ({spacer, contact, unread}, idx) -> + if spacer ... else - if item.name.length > 0 + if contact.name.length > 0 if items.length > 1 - short = item.displayFirstName() + short = contact.displayFirstName() else - short = item.displayName() + short = contact.displayName() else - short = item.email + short = contact.email if idx < items.length-1 and not items[idx+1].spacer short += ", " - {short} + {short}
{chips}{count} @@ -44,10 +44,11 @@ ThreadListParticipants = React.createClass last = null for msg in @props.thread.messageMetadata from = msg.from[0] - continue unless from - if from.email isnt last - from.unread = msg.unread - list.push(from) + if from and from.email isnt last + list.push({ + contact: msg.from[0] + unread: msg.unread + }) last = from.email else diff --git a/internal_packages/thread-list/lib/thread-list.cjsx b/internal_packages/thread-list/lib/thread-list.cjsx index 3335614a0..cf7749d2e 100644 --- a/internal_packages/thread-list/lib/thread-list.cjsx +++ b/internal_packages/thread-list/lib/thread-list.cjsx @@ -18,6 +18,7 @@ ThreadList = React.createClass @_getStateFromStores() componentDidMount: -> + @_prepareColumns() @thread_store_unsubscribe = ThreadStore.listen @_onChange @thread_unsubscriber = atom.commands.add '.thread-list', { 'thread-list:star-thread': => @_onStarThread() @@ -40,18 +41,29 @@ ThreadList = React.createClass @body_unsubscriber.dispose() render: -> + # IMPORTANT: DO NOT pass inline functions as props. _.isEqual thinks these + # are "different", and will re-render everything. Instead, declare them with ?=, + # pass a reference. (Alternatively, ignore these in children's shouldComponentUpdate.) + # + # BAD: onSelect={ (item) -> Actions.selectThreadId(item.id) } + # GOOD: onSelect={@_onSelectItem} + # classes = React.addons.classSet("thread-list": true, "ready": @state.ready) + + @_itemClassProvider ?= (item) -> if item.isUnread() then 'unread' else '' + @_itemOnSelect ?= (item) -> Actions.selectThreadId(item.id) +
if item.isUnread() then 'unread' else '' } + itemClassProvider={@_itemClassProvider} selectedId={@state.selectedId} - onSelect={ (item) -> Actions.selectThreadId(item.id) } /> + onSelect={@_itemOnSelect} />
- _computeColumns: -> + _prepareColumns: -> myEmail = NamespaceStore.current()?.emailAddress labelComponents = (thread) => @@ -101,7 +113,7 @@ ThreadList = React.createClass resolver: (thread) -> {timestamp(thread.lastMessageTimestamp)} - [c1, c2, c3, c4] + @_columns = [c1, c2, c3, c4] _onFocusSelectedIndex: -> Actions.selectThreadId(@state.selectedId) @@ -145,5 +157,4 @@ ThreadList = React.createClass _getStateFromStores: -> ready: not ThreadStore.itemsLoading() items: ThreadStore.items() - columns: @_computeColumns() selectedId: ThreadStore.selectedId() diff --git a/src/flux/models/thread.coffee b/src/flux/models/thread.coffee index 1133d02d4..0bfd3bd94 100644 --- a/src/flux/models/thread.coffee +++ b/src/flux/models/thread.coffee @@ -41,12 +41,12 @@ class Thread extends Model @naturalSortOrder: -> Thread.attributes.lastMessageTimestamp.descending() - fromJSON: (json) => + fromJSON: (json) -> super(json) @unread = @isUnread() @ - tagIds: => + tagIds: -> _.map @tags, (tag) -> tag.id hasTagId: (id) -> diff --git a/src/flux/models/utils.coffee b/src/flux/models/utils.coffee index 9f53b3b67..de8bf94e3 100644 --- a/src/flux/models/utils.coffee +++ b/src/flux/models/utils.coffee @@ -62,6 +62,13 @@ Utils = object.fromJSON(json) object + modelFreeze: (o) -> + Object.freeze(o) + for key, prop of o + if !o.hasOwnProperty(key) || typeof prop isnt 'object' || Object.isFrozen(prop) + continue + Utils.modelFreeze(prop) + modelReviver: (k, v) -> return v if k == "" v = Utils.modelFromJSON(v) if (v instanceof Object && v['object']) diff --git a/src/flux/stores/thread-store.coffee b/src/flux/stores/thread-store.coffee index d89a8b526..6865ad22c 100644 --- a/src/flux/stores/thread-store.coffee +++ b/src/flux/stores/thread-store.coffee @@ -5,6 +5,7 @@ WorkspaceStore = require './workspace-store' AddRemoveTagsTask = require '../tasks/add-remove-tags' MarkThreadReadTask = require '../tasks/mark-thread-read' Actions = require '../actions' +Utils = require '../models/utils' Thread = require '../models/thread' Message = require '../models/message' _ = require 'underscore-plus' @@ -60,6 +61,11 @@ ThreadStore = Reflux.createStore for item in items item.messageMetadata = results[item.id] + # Prevent anything from mutating these objects or their nested objects. + # Accidentally modifying items somewhere downstream (in a component) + # can trigger awful re-renders + Utils.modelFreeze(item) + @_items = items # Sometimes we can ask for a thread that's not in the current set @@ -90,6 +96,7 @@ ThreadStore = Reflux.createStore @_autoselectForLayoutMode() + console.log("Fetching data for thread list took #{Date.now() - start} msec") # If we've loaded threads, remove the loading indicator.