Mailspring/internal_packages/thread-list/lib/thread-list.cjsx
Ben Gotow dff5465931 fix(*) Small visual tweaks and fixes - see summary
Summary:
ThreadStore should be done loading as soon as threads are available

SearchSuggestionStore should use ContactsStore for contact results

Contact Store should not "filter all, take 10" it should only filter until it has 10. It should also check against "Ben Gotow" as well as "Ben" and "Gotow", so I can type "Ben Go"

Sometimes participants are "Ben Gotow <ben@g.com>", "ben@g.com". If we get zero contacts after removing Me, put "Me" back in...

Fix "Update Available" notification, broken reference to `atom.views.getView(atom.workspace)`

A bit more debugging around cursors. Need to handle this case soon.

Only use atomWorkspace if it exists.

Fix for dragging next to / around toolbar window controls

Consolidate the display of Contacts in menus into a single MenuItem subclass

Update Template Popover styling

fetchFromCache should only remove thread loading indicator *IF* it found results in the cache. Doh...

Give the thread list "Name" column a fixed width (mg)

Better styling of message list collapsed mode, rage against user selection and cursor: pointer

Occasionally admin.inboxapp.com returns bogus data

Sebaastian feedback on thread list

Test Plan: Run tests

Reviewers: evan

Reviewed By: evan

Differential Revision: https://review.inboxapp.com/D1350
2015-03-25 18:22:52 -07:00

150 lines
4.7 KiB
CoffeeScript

_ = require 'underscore-plus'
React = require 'react'
{ListTabular, Spinner} = require 'ui-components'
{timestamp, subject} = require './formatting-utils'
{Actions,
Utils,
ThreadStore,
WorkspaceStore,
NamespaceStore} = require 'inbox-exports'
ThreadListParticipants = require './thread-list-participants'
module.exports =
ThreadList = React.createClass
displayName: 'ThreadList'
getInitialState: ->
@_getStateFromStores()
componentDidMount: ->
@thread_store_unsubscribe = ThreadStore.listen @_onChange
@thread_unsubscriber = atom.commands.add '.thread-list', {
'thread-list:star-thread': => @_onStarThread()
}
@body_unsubscriber = atom.commands.add 'body', {
'application:previous-item': => @_onShiftSelectedIndex(-1)
'application:next-item': => @_onShiftSelectedIndex(1)
'application:focus-item': => @_onFocusSelectedIndex()
'application:remove-item': @_onArchiveCurrentThread
'application:remove-and-previous': -> Actions.archiveAndPrevious()
'application:remove-and-next': -> Actions.archiveAndNext()
'application:reply': @_onReply
'application:reply-all': @_onReplyAll
'application:forward': @_onForward
}
componentWillUnmount: ->
@thread_store_unsubscribe()
@thread_unsubscriber.dispose()
@body_unsubscriber.dispose()
render: ->
classes = React.addons.classSet("thread-list": true, "ready": @state.ready)
<div className={classes}>
<ListTabular
columns={@state.columns}
items={@state.items}
itemClassProvider={ (item) -> if item.isUnread() then 'unread' else '' }
selectedId={@state.selectedId}
onSelect={ (item) -> Actions.selectThreadId(item.id) } />
<Spinner visible={!@state.ready} />
</div>
_computeColumns: ->
myEmail = NamespaceStore.current()?.emailAddress
labelComponents = (thread) =>
for label in @state.threadLabelComponents
LabelComponent = label.view
<LabelComponent thread={thread} />
lastMessageType = (thread) ->
msgs = thread.messageMetadata
return 'unknown' unless msgs and msgs instanceof Array and msgs.length > 0
msg = msgs[msgs.length - 1]
if thread.unread
return 'unread'
else if msg.from[0].email isnt myEmail
return 'other'
else if Utils.isForwardedMessage(msg)
return 'forwarded'
else
return 'replied'
c1 = new ListTabular.Column
name: ""
resolver: (thread) ->
<div className="thread-icon thread-icon-#{lastMessageType(thread)}"></div>
c2 = new ListTabular.Column
name: "Name"
width: 200
resolver: (thread) ->
<ThreadListParticipants thread={thread} />
c3 = new ListTabular.Column
name: "Message"
flex: 4
resolver: (thread) ->
attachments = []
if thread.hasTagId('attachment')
attachments = <div className="thread-icon thread-icon-attachment"></div>
<span className="details">
<span className="subject">{subject(thread.subject)}</span>
<span className="snippet">{thread.snippet}</span>
{attachments}
</span>
c4 = new ListTabular.Column
name: "Date"
resolver: (thread) ->
<span className="timestamp">{timestamp(thread.lastMessageTimestamp)}</span>
[c1, c2, c3, c4]
_onFocusSelectedIndex: ->
Actions.selectThreadId(@state.selectedId)
_onShiftSelectedIndex: (delta) ->
item = _.find @state.items, (thread) => thread.id == @state.selectedId
index = if item then @state.items.indexOf(item) else -1
index = Math.max(0, Math.min(index + delta, @state.items.length-1))
Actions.selectThreadId(@state.items[index].id)
_onStarThread: ->
thread = ThreadStore.selectedThread()
thread.toggleStar() if thread
_onReply: ->
return unless @state.selectedId? and @_actionInVisualScope()
Actions.composeReply(threadId: @state.selectedId)
_onReplyAll: ->
return unless @state.selectedId? and @_actionInVisualScope()
Actions.composeReplyAll(threadId: @state.selectedId)
_onForward: ->
return unless @state.selectedId? and @_actionInVisualScope()
Actions.composeForward(threadId: @state.selectedId)
_actionInVisualScope: ->
if WorkspaceStore.selectedLayoutMode() is "list"
WorkspaceStore.sheet().type is "Thread"
else true
_onArchiveCurrentThread: ->
if WorkspaceStore.selectedLayoutMode() is "list"
Actions.archiveCurrentThread()
else if WorkspaceStore.selectedLayoutMode() is "split"
Actions.archiveAndNext()
_onChange: ->
@setState(@_getStateFromStores())
_getStateFromStores: ->
ready: not ThreadStore.itemsLoading()
items: ThreadStore.items()
columns: @_computeColumns()
selectedId: ThreadStore.selectedId()