Mailspring/internal_packages/thread-list/lib/thread-list-store.coffee
Ben Gotow 4f34c8403f feat(trash): Trash for Gmail, and architectural changes for common tasks
Summary:
This diff centralizes logic for creating common tasks for things like moving to trash, archive, etc. TaskFactory exposes a set of convenience methods and hides the whole "and also remove the current label" business from the user.

This diff also formally separates the concept of "moving to trash" and "archiving" so that "remove" isn't used in an unclear way.

I also refactored where selection is managed. Previously you'd fire some action like archiveSelection and it'd clear the selection, but if you selected some items and used another method to archive a few, they were still selected. The selection is now bound to the ModelView as intended, so if items are removed from the modelView, they are removed from it's attached selection. This means that it shouldn't /technically/ be possible to have selected items which are not in view.

I haven't refactored the tests yet. They are likely broken...

Fix next/prev logic

Test Plan: Run tests

Reviewers: evan

Reviewed By: evan

Differential Revision: https://phab.nylas.com/D2157
2015-10-21 10:38:00 -07:00

134 lines
4.1 KiB
CoffeeScript

_ = require 'underscore'
NylasStore = require 'nylas-store'
{Thread,
Message,
Actions,
SearchView,
DatabaseView,
DatabaseStore,
AccountStore,
WorkspaceStore,
FocusedContentStore,
TaskQueueStatusStore,
FocusedMailViewStore} = require 'nylas-exports'
# Public: A mutable text container with undo/redo support and the ability
# to annotate logical regions in the text.
class ThreadListStore extends NylasStore
constructor: ->
@_resetInstanceVars()
@listenTo DatabaseStore, @_onDataChanged
@listenTo AccountStore, @_onAccountChanged
@listenTo FocusedMailViewStore, @_onMailViewChanged
# We can't create a @view on construction because the CategoryStore
# has hot yet been populated from the database with the list of
# categories and their corresponding ids. Once that is ready, the
# CategoryStore will trigger, which will update the
# FocusedMailViewStore, which will cause us to create a new
# @view.
_resetInstanceVars: ->
@_lastQuery = null
view: ->
@_view
setView: (view) ->
@_viewUnlisten() if @_viewUnlisten
@_view = view
@_viewUnlisten = view.listen ->
@trigger(@)
,@
@trigger(@)
createView: ->
mailViewFilter = FocusedMailViewStore.mailView()
account = AccountStore.current()
return unless account and mailViewFilter
if mailViewFilter.searchQuery
@setView(new SearchView(mailViewFilter.searchQuery, account.id))
else
matchers = []
matchers.push Thread.attributes.accountId.equal(account.id)
matchers = matchers.concat(mailViewFilter.matchers())
view = new DatabaseView Thread, {matchers}, (ids) =>
DatabaseStore.findAll(Message)
.where(Message.attributes.threadId.in(ids))
.where(Message.attributes.accountId.equal(account.id))
.then (messages) ->
messagesByThread = {}
for id in ids
messagesByThread[id] = []
for message in messages
messagesByThread[message.threadId].push message
messagesByThread
if WorkspaceStore.layoutMode() is 'split'
# Set up a one-time listener to focus an item in the new view
unlisten = view.listen ->
if view.loaded()
Actions.setFocus(collection: 'thread', item: view.get(0))
unlisten()
@setView(view)
Actions.setFocus(collection: 'thread', item: null)
# Inbound Events
_onMailViewChanged: ->
@createView()
_onAccountChanged: ->
accountId = AccountStore.current()?.id
accountMatcher = (m) ->
m.attribute() is Thread.attributes.accountId and m.value() is accountId
return if @_view and _.find(@_view.matchers, accountMatcher)
@createView()
_onDataChanged: (change) ->
return unless @_view
if change.objectClass is Thread.name
focusedId = FocusedContentStore.focusedId('thread')
keyboardId = FocusedContentStore.keyboardCursorId('thread')
viewModeAutofocuses = WorkspaceStore.layoutMode() is 'split' or WorkspaceStore.topSheet().root is true
focusedIndex = @_view.indexOfId(focusedId)
keyboardIndex = @_view.indexOfId(keyboardId)
shiftIndex = (i) =>
if i > 0 and (@_view.get(i - 1)?.unread or i >= @_view.count())
return i - 1
else
return i
@_view.invalidate({change: change, shallow: true})
focusedLost = focusedIndex >= 0 and @_view.indexOfId(focusedId) is -1
keyboardLost = keyboardIndex >= 0 and @_view.indexOfId(keyboardId) is -1
if viewModeAutofocuses and focusedLost
Actions.setFocus(collection: 'thread', item: @_view.get(shiftIndex(focusedIndex)))
if keyboardLost
Actions.setCursorPosition(collection: 'thread', item: @_view.get(shiftIndex(keyboardIndex)))
if change.objectClass is Message.name
# Important: Until we optimize this so that it detects the set change
# and avoids a query, this should be defered since it's very unimportant
_.defer =>
threadIds = _.uniq _.map change.objects, (m) -> m.threadId
@_view.invalidateMetadataFor(threadIds)
module.exports = new ThreadListStore()