mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-11-11 01:54:40 +08:00
TaskFactory now returns N tasks for performing standard actions, undo uses arrays
This commit is contained in:
parent
979c29e24f
commit
7458fdd4db
12 changed files with 137 additions and 103 deletions
|
@ -24,10 +24,10 @@ class ThreadArchiveButton extends React.Component
|
|||
|
||||
_onArchive: (e) =>
|
||||
return unless DOMUtils.nodeIsVisible(e.currentTarget)
|
||||
task = TaskFactory.taskForArchiving
|
||||
tasks = TaskFactory.tasksForArchiving
|
||||
threads: [@props.thread],
|
||||
fromPerspective: FocusedPerspectiveStore.current()
|
||||
Actions.queueTask(task)
|
||||
Actions.queueTasks(tasks)
|
||||
Actions.popSheet()
|
||||
e.stopPropagation()
|
||||
|
||||
|
|
|
@ -26,10 +26,10 @@ class ThreadTrashButton extends React.Component
|
|||
|
||||
_onRemove: (e) =>
|
||||
return unless DOMUtils.nodeIsVisible(e.currentTarget)
|
||||
task = TaskFactory.taskForMovingToTrash
|
||||
tasks = TaskFactory.tasksForMovingToTrash
|
||||
threads: [@props.thread],
|
||||
fromPerspective: FocusedPerspectiveStore.current()
|
||||
Actions.queueTask(task)
|
||||
Actions.queueTasks(tasks)
|
||||
Actions.popSheet()
|
||||
e.stopPropagation()
|
||||
|
||||
|
|
|
@ -28,10 +28,10 @@ class ThreadBulkArchiveButton extends React.Component
|
|||
</button>
|
||||
|
||||
_onArchive: =>
|
||||
task = TaskFactory.taskForArchiving
|
||||
tasks = TaskFactory.tasksForArchiving
|
||||
threads: @props.selection.items(),
|
||||
fromPerspective: FocusedPerspectiveStore.current()
|
||||
Actions.queueTask(task)
|
||||
Actions.queueTasks(tasks)
|
||||
|
||||
class ThreadBulkTrashButton extends React.Component
|
||||
@displayName: 'ThreadBulkTrashButton'
|
||||
|
@ -52,10 +52,10 @@ class ThreadBulkTrashButton extends React.Component
|
|||
</button>
|
||||
|
||||
_onRemove: =>
|
||||
task = TaskFactory.taskForMovingToTrash
|
||||
tasks = TaskFactory.tasksForMovingToTrash
|
||||
threads: @props.selection.items(),
|
||||
fromPerspective: FocusedPerspectiveStore.current()
|
||||
Actions.queueTask(task)
|
||||
Actions.queueTasks(tasks)
|
||||
|
||||
|
||||
class ThreadBulkStarButton extends React.Component
|
||||
|
|
|
@ -25,10 +25,10 @@ class ThreadArchiveQuickAction extends React.Component
|
|||
newProps.thread.id isnt @props?.thread.id
|
||||
|
||||
_onArchive: (event) =>
|
||||
task = TaskFactory.taskForArchiving
|
||||
tasks = TaskFactory.tasksForArchiving
|
||||
threads: [@props.thread]
|
||||
fromPerspective: FocusedPerspectiveStore.current()
|
||||
Actions.queueTask(task)
|
||||
Actions.queueTasks(tasks)
|
||||
|
||||
# Don't trigger the thread row click
|
||||
event.stopPropagation()
|
||||
|
@ -54,10 +54,10 @@ class ThreadTrashQuickAction extends React.Component
|
|||
newProps.thread.id isnt @props?.thread.id
|
||||
|
||||
_onRemove: (event) =>
|
||||
task = TaskFactory.taskForMovingToTrash
|
||||
tasks = TaskFactory.tasksForMovingToTrash
|
||||
threads: [@props.thread]
|
||||
fromPerspective: FocusedPerspectiveStore.current()
|
||||
Actions.queueTask(task)
|
||||
Actions.queueTasks(tasks)
|
||||
|
||||
# Don't trigger the thread row click
|
||||
event.stopPropagation()
|
||||
|
|
|
@ -176,19 +176,19 @@ class ThreadList extends React.Component
|
|||
if threads
|
||||
if backspaceDelete
|
||||
if FocusedPerspectiveStore.current().canTrashThreads()
|
||||
removeMethod = TaskFactory.taskForMovingToTrash
|
||||
removeMethod = TaskFactory.tasksForMovingToTrash
|
||||
else
|
||||
return
|
||||
else
|
||||
if FocusedPerspectiveStore.current().canArchiveThreads()
|
||||
removeMethod = TaskFactory.taskForArchiving
|
||||
removeMethod = TaskFactory.tasksForArchiving
|
||||
else
|
||||
removeMethod = TaskFactory.taskForMovingToTrash
|
||||
removeMethod = TaskFactory.tasksForMovingToTrash
|
||||
|
||||
task = removeMethod
|
||||
tasks = removeMethod
|
||||
threads: threads
|
||||
fromPerspective: FocusedPerspectiveStore.current()
|
||||
Actions.queueTask(task)
|
||||
Actions.queueTasks(tasks)
|
||||
|
||||
Actions.popSheet()
|
||||
|
||||
|
@ -196,20 +196,20 @@ class ThreadList extends React.Component
|
|||
return unless FocusedPerspectiveStore.current().canArchiveThreads()
|
||||
threads = @_threadsForKeyboardAction()
|
||||
if threads
|
||||
task = TaskFactory.taskForArchiving
|
||||
tasks = TaskFactory.tasksForArchiving
|
||||
threads: threads
|
||||
fromPerspective: FocusedPerspectiveStore.current()
|
||||
Actions.queueTask(task)
|
||||
Actions.queueTasks(tasks)
|
||||
Actions.popSheet()
|
||||
|
||||
_onDeleteItem: =>
|
||||
return unless FocusedPerspectiveStore.current().canTrashThreads()
|
||||
threads = @_threadsForKeyboardAction()
|
||||
if threads
|
||||
task = TaskFactory.taskForMovingToTrash
|
||||
tasks = TaskFactory.tasksForMovingToTrash
|
||||
threads: threads
|
||||
fromPerspective: FocusedPerspectiveStore.current()
|
||||
Actions.queueTask(task)
|
||||
Actions.queueTasks(tasks)
|
||||
Actions.popSheet()
|
||||
|
||||
_onSelectRead: =>
|
||||
|
|
|
@ -33,12 +33,12 @@ class UndoRedoComponent extends React.Component
|
|||
), 3000
|
||||
|
||||
_getStateFromStores: ->
|
||||
task = UndoRedoStore.getMostRecentTask()
|
||||
tasks = UndoRedoStore.getMostRecent()
|
||||
show = false
|
||||
if task
|
||||
if tasks
|
||||
show = true
|
||||
|
||||
return {show, task}
|
||||
return {show, tasks}
|
||||
|
||||
componentWillMount: ->
|
||||
@_unsubscribe = UndoRedoStore.listen(@_onChange)
|
||||
|
@ -65,7 +65,7 @@ class UndoRedoComponent extends React.Component
|
|||
if @state.show
|
||||
<div className="undo-redo" onMouseEnter={@_clearTimeout} onMouseLeave={@_setNewTimeout}>
|
||||
<div className="undo-redo-message-wrapper">
|
||||
{@state.task.description()}
|
||||
{@state.tasks.map((t) -> t.description()).join(', ')}
|
||||
</div>
|
||||
<div className="undo-redo-action-wrapper" onClick={@_onClick}>
|
||||
<RetinaImg name="undo-icon@2x.png"
|
||||
|
@ -81,6 +81,6 @@ class UndoRedoComponent extends React.Component
|
|||
@_hide()
|
||||
|
||||
_hide: =>
|
||||
@setState({show: false, task: null})
|
||||
@setState({show: false, tasks: null})
|
||||
|
||||
module.exports = UndoRedoComponent
|
||||
|
|
|
@ -90,6 +90,14 @@ class Actions
|
|||
###
|
||||
@queueTask: ActionScopeWorkWindow
|
||||
|
||||
###
|
||||
Public: Queue multiple {Task} objects to the {TaskQueue}, which should be
|
||||
undone as a single user action.
|
||||
|
||||
*Scope: Work Window*
|
||||
###
|
||||
@queueTasks: ActionScopeWorkWindow
|
||||
|
||||
@undoTaskId: ActionScopeWorkWindow
|
||||
|
||||
###
|
||||
|
|
|
@ -88,7 +88,9 @@ class QuerySubscription
|
|||
mustRefetchAllIds = true if @_itemSortOrderHasChanged(oldItem, item)
|
||||
|
||||
if impactCount > 0
|
||||
@_set = null if mustRefetchAllIds
|
||||
if mustRefetchAllIds
|
||||
@log("Clearing result set - mustRefetchAllIds")
|
||||
@_set = null
|
||||
@update()
|
||||
|
||||
_itemSortOrderHasChanged: (old, updated) ->
|
||||
|
@ -102,6 +104,9 @@ class QuerySubscription
|
|||
|
||||
return false
|
||||
|
||||
log: (msg) =>
|
||||
console.log(msg) if @_query._klass.name is 'Thread'
|
||||
|
||||
update: =>
|
||||
version = @_version += 1
|
||||
|
||||
|
@ -116,18 +121,23 @@ class QuerySubscription
|
|||
entireModels = not @_set or @_set.modelCacheCount() is 0
|
||||
|
||||
Promise.each ranges, (range) =>
|
||||
return unless version is @_version
|
||||
@_fetchRange(range, {entireModels})
|
||||
return @log("Update (#{version}) - Cancelled @ Step 0") unless version is @_version
|
||||
@log("Update (#{version}) - Fetching range #{range}")
|
||||
@_fetchRange(range, {entireModels, version})
|
||||
.then =>
|
||||
return unless version is @_version
|
||||
return @log("Update (#{version}) - Cancelled @ Step 1") unless version is @_version
|
||||
ids = @_set.ids().filter (id) => not @_set.modelWithId(id)
|
||||
return if ids.length is 0
|
||||
return DatabaseStore.findAll(@_query._klass, {id: ids}).then(@_appendToModelCache)
|
||||
return @log("Update (#{version}) - No missing Ids") if ids.length is 0
|
||||
@log("Update (#{version}) - Fetching missing Ids: #{ids}")
|
||||
return DatabaseStore.findAll(@_query._klass, {id: ids}).then (models) =>
|
||||
@log("Update (#{version}) - Fetched missing Ids")
|
||||
@_set.replaceModel(m) for m in models
|
||||
.then =>
|
||||
return unless version is @_version
|
||||
return @log("Update (#{version}) - Cancelled @ Step 2") unless version is @_version
|
||||
@log("Update (#{version}) - Triggering...")
|
||||
@_createResultAndTrigger()
|
||||
|
||||
_fetchRange: (range, {entireModels} = {}) ->
|
||||
_fetchRange: (range, {entireModels, version} = {}) ->
|
||||
rangeQuery = undefined
|
||||
|
||||
unless range.isInfinite()
|
||||
|
@ -141,7 +151,12 @@ class QuerySubscription
|
|||
rangeQuery ?= @_query
|
||||
|
||||
DatabaseStore.run(rangeQuery, {format: false}).then (results) =>
|
||||
@_set = null unless @_set?.range().isContiguousWith(range)
|
||||
if version and version isnt @_version
|
||||
return @log("Update (#{version}) - fetchRange Cancelled")
|
||||
|
||||
unless @_set?.range().isContiguousWith(range)
|
||||
@log("Clearing result set - #{range} isnt contiguous with #{@_set?.range()}")
|
||||
@_set = null
|
||||
@_set ?= new MutableQueryResultSet()
|
||||
|
||||
if entireModels
|
||||
|
|
|
@ -233,7 +233,7 @@ class ModelQuery
|
|||
|
||||
formatResultObjects: (objects) ->
|
||||
return objects[0] if @_returnOne
|
||||
return objects
|
||||
return [].concat(objects)
|
||||
|
||||
# Query SQL Building
|
||||
|
||||
|
|
|
@ -76,14 +76,14 @@ class TaskQueue
|
|||
|
||||
@_restoreQueue()
|
||||
|
||||
@listenTo(Actions.queueTask, @enqueue)
|
||||
@listenTo(Actions.undoTaskId, @enqueueUndoOfTaskId)
|
||||
@listenTo(Actions.dequeueTask, @dequeue)
|
||||
@listenTo(Actions.dequeueAllTasks, @dequeueAll)
|
||||
@listenTo(Actions.dequeueMatchingTask, @dequeueMatching)
|
||||
|
||||
@listenTo(Actions.clearDeveloperConsole, @clearCompleted)
|
||||
|
||||
@listenTo Actions.queueTask, @enqueue
|
||||
@listenTo Actions.queueTasks, (tasks) =>
|
||||
@enqueue(t) for t in tasks
|
||||
@listenTo Actions.undoTaskId, @enqueueUndoOfTaskId
|
||||
@listenTo Actions.dequeueTask, @dequeue
|
||||
@listenTo Actions.dequeueAllTasks, @dequeueAll
|
||||
@listenTo Actions.dequeueMatchingTask, @dequeueMatching
|
||||
@listenTo Actions.clearDeveloperConsole, @clearCompleted
|
||||
@listenTo Actions.longPollConnected, =>
|
||||
@_processQueue()
|
||||
|
||||
|
|
|
@ -16,32 +16,41 @@ class UndoRedoStore
|
|||
@_undo = []
|
||||
@_redo = []
|
||||
|
||||
@listenTo(Actions.queueTask, @_onTaskQueued)
|
||||
@listenTo(Actions.queueTask, @_onQueue)
|
||||
@listenTo(Actions.queueTasks, @_onQueue)
|
||||
|
||||
NylasEnv.commands.add('body', {'core:undo': => @undo() })
|
||||
NylasEnv.commands.add('body', {'core:redo': => @redo() })
|
||||
NylasEnv.commands.add('body', {'core:undo': @undo })
|
||||
NylasEnv.commands.add('body', {'core:redo': @redo })
|
||||
|
||||
_onTaskQueued: (task) =>
|
||||
if task.canBeUndone()
|
||||
_onQueue: (tasks) =>
|
||||
tasks = [tasks] unless tasks instanceof Array
|
||||
undoable = _.every tasks, (t) -> t.canBeUndone()
|
||||
|
||||
if undoable
|
||||
@_redo = []
|
||||
@_undo.push(task)
|
||||
@trigger() unless task._isReverting
|
||||
@_undo.push(tasks)
|
||||
@trigger()
|
||||
|
||||
undo: =>
|
||||
topTask = @_undo.pop()
|
||||
return unless topTask
|
||||
topTasks = @_undo.pop()
|
||||
return unless topTasks
|
||||
@trigger()
|
||||
Actions.undoTaskId(topTask.id)
|
||||
@_redo.push(topTask.createIdenticalTask())
|
||||
|
||||
for task in topTasks
|
||||
Actions.undoTaskId(task.id)
|
||||
|
||||
redoTasks = topTasks.map (t) -> t.createIdenticalTask()
|
||||
@_redo.push(redoTasks)
|
||||
|
||||
redo: =>
|
||||
redoTask = @_redo.pop()
|
||||
return unless redoTask
|
||||
Actions.queueTask(redoTask)
|
||||
redoTasks = @_redo.pop()
|
||||
return unless redoTasks
|
||||
Actions.queueTasks(redoTasks)
|
||||
|
||||
getMostRecentTask: =>
|
||||
getMostRecent: =>
|
||||
for idx in [@_undo.length-1...-1]
|
||||
return @_undo[idx] unless @_undo[idx]._isReverting
|
||||
allReverting = _.every @_undo[idx], (t) -> t._isReverting
|
||||
return @_undo[idx] unless allReverting
|
||||
|
||||
print: ->
|
||||
console.log("Undo Stack")
|
||||
|
|
|
@ -8,27 +8,43 @@ CategoryStore = require '../stores/category-store'
|
|||
|
||||
class TaskFactory
|
||||
|
||||
taskForApplyingCategory: ({threads, fromPerspective, category, exclusive}) =>
|
||||
# TODO Can not apply to threads across more than one account for now
|
||||
account = AccountStore.accountForItems(threads)
|
||||
return unless account?
|
||||
tasksForApplyingCategories: ({threads, categoriesToRemove, categoryToAdd}) =>
|
||||
byAccount = {}
|
||||
tasks = []
|
||||
|
||||
if account.usesFolders()
|
||||
return null unless category
|
||||
return new ChangeFolderTask
|
||||
folder: category
|
||||
threads: threads
|
||||
else
|
||||
labelsToRemove = []
|
||||
if exclusive
|
||||
currentLabel = fromPerspective?.category()
|
||||
currentLabel ?= CategoryStore.getStandardCategory(account, "inbox")
|
||||
labelsToRemove = [currentLabel]
|
||||
for thread in threads
|
||||
accountId = thread.accountId
|
||||
byAccount[accountId] ?=
|
||||
categoriesToRemove: categoriesToRemove?(accountId) ? []
|
||||
categoryToAdd: categoryToAdd(accountId)
|
||||
threads: []
|
||||
byAccount[accountId].threads.push(thread)
|
||||
|
||||
return new ChangeLabelsTask
|
||||
threads: threads
|
||||
labelsToRemove: labelsToRemove
|
||||
labelsToAdd: [category]
|
||||
for accountId, {categoryToAdd, categoriesToRemove, threads} of byAccount
|
||||
continue unless categoryToAdd and categoriesToRemove
|
||||
|
||||
account = AccountStore.accountForId(accountId)
|
||||
if account.usesFolders()
|
||||
tasks.push new ChangeFolderTask
|
||||
folder: categoryToAdd
|
||||
threads: threads
|
||||
else
|
||||
tasks.push new ChangeLabelsTask
|
||||
threads: threads
|
||||
labelsToRemove: categoriesToRemove
|
||||
labelsToAdd: [categoryToAdd]
|
||||
|
||||
return tasks
|
||||
|
||||
taskForApplyingCategory: ({threads, category}) =>
|
||||
tasks = @tasksForApplyingCategories
|
||||
threads: threads
|
||||
categoryToAdd: (accountId) -> category
|
||||
|
||||
if tasks.length > 1
|
||||
throw new Error("taskForApplyingCategory: Threads must be from the same account.")
|
||||
|
||||
return tasks[0]
|
||||
|
||||
taskForRemovingCategory: ({threads, fromPerspective, category, exclusive}) =>
|
||||
# TODO Can not apply to threads across more than one account for now
|
||||
|
@ -51,21 +67,17 @@ class TaskFactory
|
|||
labelsToRemove: [category]
|
||||
labelsToAdd: labelsToAdd
|
||||
|
||||
taskForArchiving: ({threads, fromPerspective}) =>
|
||||
category = @_getArchiveCategory(threads)
|
||||
@taskForApplyingCategory({threads, fromPerspective, category, exclusive: true})
|
||||
tasksForArchiving: ({threads, fromPerspective}) =>
|
||||
@tasksForApplyingCategories
|
||||
threads: threads,
|
||||
categoriesToRemove: (accountId) -> _.filter(fromPerspective.categories(), _.matcher({accountId}))
|
||||
categoryToAdd: (accountId) -> CategoryStore.getArchiveCategory(accountId)
|
||||
|
||||
taskForUnarchiving: ({threads, fromPerspective}) =>
|
||||
category = @_getArchiveCategory(threads)
|
||||
@taskForRemovingCategory({threads, fromPerspective, category, exclusive: true})
|
||||
|
||||
taskForMovingToTrash: ({threads, fromPerspective}) =>
|
||||
category = @_getTrashCategory(threads)
|
||||
@taskForApplyingCategory({threads, fromPerspective, category, exclusive: true})
|
||||
|
||||
taskForMovingFromTrash: ({threads, fromPerspective}) =>
|
||||
category = @_getTrashCategory(threads)
|
||||
@taskForRemovingCategory({threads, fromPerspective, category, exclusive: true})
|
||||
tasksForMovingToTrash: ({threads, fromPerspective}) =>
|
||||
@tasksForApplyingCategories
|
||||
threads: threads,
|
||||
categoriesToRemove: (accountId) -> _.filter(fromPerspective.categories(), _.matcher({accountId}))
|
||||
categoryToAdd: (accountId) -> CategoryStore.getTrashCategory(accountId)
|
||||
|
||||
taskForInvertingUnread: ({threads}) =>
|
||||
unread = _.every threads, (t) -> _.isMatch(t, {unread: false})
|
||||
|
@ -75,14 +87,4 @@ class TaskFactory
|
|||
starred = _.every threads, (t) -> _.isMatch(t, {starred: false})
|
||||
return new ChangeStarredTask({threads, starred})
|
||||
|
||||
_getArchiveCategory: (threads) =>
|
||||
account = AccountStore.accountForItems(threads)
|
||||
return unless account?
|
||||
CategoryStore.getArchiveCategory(account)
|
||||
|
||||
_getTrashCategory: (threads) =>
|
||||
account = AccountStore.accountForItems(threads)
|
||||
return unless account?
|
||||
CategoryStore.getTrashCategory(account)
|
||||
|
||||
module.exports = new TaskFactory
|
||||
|
|
Loading…
Reference in a new issue