mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-02-24 16:14:01 +08:00
142 lines
6 KiB
CoffeeScript
142 lines
6 KiB
CoffeeScript
_ = require 'underscore'
|
|
|
|
Task = require './flux/tasks/task'
|
|
Actions = require './flux/actions'
|
|
Category = require './flux/models/category'
|
|
Thread = require './flux/models/thread'
|
|
Message = require './flux/models/message'
|
|
AccountStore = require './flux/stores/account-store'
|
|
DatabaseStore = require './flux/stores/database-store'
|
|
TaskQueueStatusStore = require './flux/stores/task-queue-status-store'
|
|
|
|
MailRulesStore = require './flux/stores/mail-rules-store'
|
|
{ConditionMode, ConditionTemplates} = require './mail-rules-templates'
|
|
|
|
ChangeUnreadTask = require './flux/tasks/change-unread-task'
|
|
ChangeFolderTask = require './flux/tasks/change-folder-task'
|
|
ChangeStarredTask = require './flux/tasks/change-starred-task'
|
|
ChangeLabelsTask = require './flux/tasks/change-labels-task'
|
|
|
|
###
|
|
Note: At first glance, it seems like these task factory methods should use the
|
|
TaskFactory. Unfortunately, the TaskFactory uses the CategoryStore and other
|
|
information about the current view. Maybe after the unified inbox refactor...
|
|
###
|
|
MailRulesActions =
|
|
markAsImportant: (message, thread) ->
|
|
DatabaseStore.findBy(Category, {
|
|
name: 'important',
|
|
accountId: thread.accountId
|
|
}).then (important) ->
|
|
return Promise.reject(new Error("Could not find `important` label")) unless important
|
|
return new ChangeLabelsTask(labelsToAdd: [important], threads: [thread])
|
|
|
|
moveToTrash: (message, thread) ->
|
|
if AccountStore.accountForId(thread.accountId).usesLabels()
|
|
return MailRulesActions._applyStandardLabelRemovingInbox(message, thread, 'trash')
|
|
else
|
|
DatabaseStore.findBy(Category, { name: 'trash', accountId: thread.accountId }).then (folder) ->
|
|
return Promise.reject(new Error("The folder could not be found.")) unless folder
|
|
return new ChangeFolderTask(folder: folder, threads: [thread])
|
|
|
|
markAsRead: (message, thread) ->
|
|
new ChangeUnreadTask(unread: false, threads: [thread])
|
|
|
|
star: (message, thread) ->
|
|
new ChangeStarredTask(starred: true, threads: [thread])
|
|
|
|
changeFolder: (message, thread, value) ->
|
|
return Promise.reject(new Error("A folder is required.")) unless value
|
|
DatabaseStore.findBy(Category, { id: value, accountId: thread.accountId }).then (folder) ->
|
|
return Promise.reject(new Error("The folder could not be found.")) unless folder
|
|
return new ChangeFolderTask(folder: folder, threads: [thread])
|
|
|
|
applyLabel: (message, thread, value) ->
|
|
return Promise.reject(new Error("A label is required.")) unless value
|
|
DatabaseStore.findBy(Category, { id: value, accountId: thread.accountId }).then (label) ->
|
|
return Promise.reject(new Error("The label could not be found.")) unless label
|
|
return new ChangeLabelsTask(labelsToAdd: [label], threads: [thread])
|
|
|
|
applyLabelArchive: (message, thread) ->
|
|
return MailRulesActions._applyStandardLabelRemovingInbox(message, thread, 'all')
|
|
|
|
# Helpers for other actions
|
|
|
|
_applyStandardLabelRemovingInbox: (message, thread, value) ->
|
|
Promise.props(
|
|
inbox: DatabaseStore.findBy(Category, { name: 'inbox', accountId: thread.accountId })
|
|
newLabel: DatabaseStore.findBy(Category, { name: value, accountId: thread.accountId })
|
|
).then ({inbox, newLabel}) ->
|
|
return Promise.reject(new Error("Could not find `inbox` or `#{value}` label")) unless inbox and newLabel
|
|
return new ChangeLabelsTask
|
|
labelsToRemove: [inbox]
|
|
labelsToAdd: [newLabel]
|
|
threads: [thread]
|
|
|
|
|
|
class MailRulesProcessor
|
|
constructor: ->
|
|
|
|
processMessages: (messages) =>
|
|
return Promise.resolve() unless messages.length > 0
|
|
|
|
enabledRules = MailRulesStore.rules().filter (r) -> not r.disabled
|
|
|
|
# When messages arrive, we process all the messages in parallel, but one
|
|
# rule at a time. This is important, because users can order rules which
|
|
# may do and undo a change. Ie: "Star if from Ben, Unstar if subject is "Bla"
|
|
Promise.each enabledRules, (rule) =>
|
|
matching = messages.filter (message) =>
|
|
@_checkRuleForMessage(rule, message)
|
|
|
|
# Rules are declared at the message level, but actions are applied to
|
|
# threads. To ensure we don't apply the same action 50x on the same thread,
|
|
# just process one match per thread.
|
|
matching = _.uniq matching, false, (message) ->
|
|
message.threadId
|
|
|
|
Promise.map matching, (message) =>
|
|
# We always pull the thread from the database, even though it may be in
|
|
# `incoming.thread`, because rules may be modifying it as they run!
|
|
DatabaseStore.find(Thread, message.threadId).then (thread) =>
|
|
return console.warn("Cannot find thread #{message.threadId} to process mail rules.") unless thread
|
|
@_applyRuleToMessage(rule, message, thread)
|
|
|
|
_checkRuleForMessage: (rule, message) =>
|
|
if rule.conditionMode is ConditionMode.All
|
|
fn = _.every
|
|
else
|
|
fn = _.any
|
|
|
|
return false unless message.accountId is rule.accountId
|
|
|
|
fn rule.conditions, (condition) =>
|
|
template = _.findWhere(ConditionTemplates, {key: condition.templateKey})
|
|
value = template.valueForMessage(message)
|
|
template.evaluate(condition, value)
|
|
|
|
_applyRuleToMessage: (rule, message, thread) =>
|
|
actionPromises = rule.actions.map (action) =>
|
|
actionFunction = MailRulesActions[action.templateKey]
|
|
if not actionFunction
|
|
return Promise.reject(new Error("#{action.templateKey} is not a supported action."))
|
|
return actionFunction(message, thread, action.value)
|
|
|
|
Promise.all(actionPromises).then (actionResults) ->
|
|
performLocalPromises = []
|
|
|
|
actionTasks = actionResults.filter (r) -> r instanceof Task
|
|
actionTasks.forEach (task) ->
|
|
performLocalPromises.push TaskQueueStatusStore.waitForPerformLocal(task)
|
|
Actions.queueTask(task)
|
|
|
|
Promise.all(performLocalPromises)
|
|
|
|
.catch (err) ->
|
|
# Errors can occur if a mail rule specifies an invalid label or folder, etc.
|
|
# Disable the rule. Disable the mail rule so the failure is reflected in the
|
|
# interface.
|
|
Actions.disableMailRule(rule.id, err.toString())
|
|
return Promise.resolve()
|
|
|
|
module.exports = new MailRulesProcessor
|