mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-01-01 13:14:16 +08:00
Run mail rules automatically as mail is received
This commit is contained in:
parent
aad8c70498
commit
1950f5b356
5 changed files with 53 additions and 34 deletions
|
@ -341,6 +341,7 @@ export default class MailsyncBridge {
|
|||
DatabaseStore.trigger(record);
|
||||
|
||||
// Run task success / error handlers if the task is now complete
|
||||
// Note: cannot use `record.objectClass` because of subclass names
|
||||
if (record.type === 'persist' && record.objects[0] instanceof Task) {
|
||||
for (const task of record.objects) {
|
||||
if (task.status !== 'complete') {
|
||||
|
|
|
@ -11,13 +11,25 @@ import MailRulesProcessor from '../../mail-rules-processor';
|
|||
import { ConditionMode, ConditionTemplates, ActionTemplates } from '../../mail-rules-templates';
|
||||
|
||||
const RulesJSONKey = 'MailRules-V2';
|
||||
const AutoSinceJSONKey = 'MailRules-Auto-Since';
|
||||
|
||||
class MailRulesStore extends MailspringStore {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
/* This is a bit strange - if the user has mail rules enabled, they only
|
||||
expect rules to be applied to "new" mail. Not "new" mail as in just created,
|
||||
since that includes old mail we're syncing for the first time. Just "new"
|
||||
mail that has arrived since they last ran Mailspring. So, we keep a date. */
|
||||
this._autoSince = (window.localStorage.getItem(AutoSinceJSONKey) || 0) / 1;
|
||||
if (this._autoSince === 0) {
|
||||
window.localStorage.setItem(AutoSinceJSONKey, Date.now());
|
||||
this._autoSince = Date.now();
|
||||
}
|
||||
|
||||
this._reprocessing = {};
|
||||
this._rules = [];
|
||||
|
||||
try {
|
||||
const txt = window.localStorage.getItem(RulesJSONKey);
|
||||
if (txt) {
|
||||
|
@ -34,6 +46,8 @@ class MailRulesStore extends MailspringStore {
|
|||
this.listenTo(Actions.disableMailRule, this._onDisableMailRule);
|
||||
this.listenTo(Actions.startReprocessingMailRules, this._onStartReprocessing);
|
||||
this.listenTo(Actions.stopReprocessingMailRules, this._onStopReprocessing);
|
||||
|
||||
this.listenTo(DatabaseStore, this._onDatabaseChanged);
|
||||
}
|
||||
|
||||
rules() {
|
||||
|
@ -52,6 +66,22 @@ class MailRulesStore extends MailspringStore {
|
|||
return this._reprocessing;
|
||||
}
|
||||
|
||||
_onDatabaseChanged = record => {
|
||||
// If the record contains new emails, process mail rules immediately.
|
||||
// This is necessary to avoid emails from bouncing through the inbox.
|
||||
if (record.type === 'persist' && record.objectClass === Message.name) {
|
||||
const newMessages = record.objects.filter(msg => {
|
||||
if (msg.version !== 1) return false;
|
||||
if (msg.draft) return false;
|
||||
if (!msg.date || msg.date.valueOf() < this._autoSince) return false;
|
||||
return true;
|
||||
});
|
||||
if (newMessages.length > 0) {
|
||||
MailRulesProcessor.processMessages(newMessages);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_onDeleteMailRule = id => {
|
||||
this._rules = this._rules.filter(f => f.id !== id);
|
||||
this._saveMailRules();
|
||||
|
|
|
@ -13,16 +13,6 @@ export default class ChangeStarredTask extends ChangeMailTask {
|
|||
}),
|
||||
});
|
||||
|
||||
constructor(data = {}) {
|
||||
if (data.threads) {
|
||||
data.threads = data.threads.filter(t => t.starred !== data.starred);
|
||||
}
|
||||
if (data.messages) {
|
||||
data.messages = data.messages.filter(m => m.starred !== data.starred);
|
||||
}
|
||||
super(data);
|
||||
}
|
||||
|
||||
label() {
|
||||
return this.starred ? 'Starring' : 'Unstarring';
|
||||
}
|
||||
|
|
|
@ -13,16 +13,6 @@ export default class ChangeUnreadTask extends ChangeMailTask {
|
|||
}),
|
||||
});
|
||||
|
||||
constructor(data = {}) {
|
||||
if (data.threads) {
|
||||
data.threads = data.threads.filter(t => t.unread !== data.unread);
|
||||
}
|
||||
if (data.messages) {
|
||||
data.messages = data.messages.filter(m => m.unread !== data.unread);
|
||||
}
|
||||
super(data);
|
||||
}
|
||||
|
||||
label() {
|
||||
return this.unread ? 'Marking as unread' : 'Marking as read';
|
||||
}
|
||||
|
|
|
@ -24,10 +24,7 @@ information about the current view. Maybe after the unified inbox refactor...
|
|||
*/
|
||||
const MailRulesActions = {
|
||||
markAsImportant: async (message, thread) => {
|
||||
const important = await DatabaseStore.findBy(Label, {
|
||||
role: 'important',
|
||||
accountId: thread.accountId,
|
||||
});
|
||||
const important = CategoryStore.getCategoryByRole(thread.accountId, 'important');
|
||||
if (!important) {
|
||||
throw new Error('Could not find `important` label');
|
||||
}
|
||||
|
@ -52,6 +49,9 @@ const MailRulesActions = {
|
|||
},
|
||||
|
||||
markAsRead: (message, thread) => {
|
||||
if (thread.unread === false) {
|
||||
return null;
|
||||
}
|
||||
return new ChangeUnreadTask({
|
||||
unread: false,
|
||||
threads: [thread],
|
||||
|
@ -60,6 +60,9 @@ const MailRulesActions = {
|
|||
},
|
||||
|
||||
star: (message, thread) => {
|
||||
if (thread.starred === true) {
|
||||
return null;
|
||||
}
|
||||
return new ChangeStarredTask({
|
||||
starred: true,
|
||||
threads: [thread],
|
||||
|
@ -71,8 +74,8 @@ const MailRulesActions = {
|
|||
if (!value) {
|
||||
throw new Error('A folder is required.');
|
||||
}
|
||||
const folder = await DatabaseStore.findBy(Folder, { id: value, accountId: thread.accountId });
|
||||
if (!folder) {
|
||||
const folder = CategoryStore.byId(thread.accountId, value);
|
||||
if (!folder || !(folder instanceof Folder)) {
|
||||
throw new Error('The folder could not be found.');
|
||||
}
|
||||
return new ChangeFolderTask({
|
||||
|
@ -86,8 +89,8 @@ const MailRulesActions = {
|
|||
if (!value) {
|
||||
throw new Error('A label is required.');
|
||||
}
|
||||
const label = await DatabaseStore.findBy(Label, { id: value, accountId: thread.accountId });
|
||||
if (!label) {
|
||||
const label = CategoryStore.byId(thread.accountId, value);
|
||||
if (!label || !(label instanceof Label)) {
|
||||
throw new Error('The label could not be found.');
|
||||
}
|
||||
return new ChangeLabelsTask({
|
||||
|
@ -112,12 +115,11 @@ const MailRulesActions = {
|
|||
throw new Error('A label is required.');
|
||||
}
|
||||
|
||||
const { withId, withRole } = await Promise.props({
|
||||
withId: DatabaseStore.findBy(Label, { id: roleOrId, accountId: thread.accountId }),
|
||||
withRole: DatabaseStore.findBy(Label, { role: roleOrId, accountId: thread.accountId }),
|
||||
});
|
||||
const label = withId || withRole;
|
||||
if (!label) {
|
||||
const label = CategoryStore.categories(thread.accountId).find(
|
||||
c => c.id === roleOrId || c.role === roleOrId
|
||||
);
|
||||
|
||||
if (!label || !(label instanceof Label)) {
|
||||
throw new Error('The label could not be found.');
|
||||
}
|
||||
return new ChangeLabelsTask({
|
||||
|
@ -189,6 +191,12 @@ class MailRulesProcessor {
|
|||
|
||||
const actionResults = await Promise.all(actionPromises);
|
||||
const actionTasks = actionResults.filter(r => r instanceof Task);
|
||||
|
||||
// mark that none of these tasks are undoable
|
||||
actionTasks.forEach(t => {
|
||||
t.canBeUndone = false;
|
||||
});
|
||||
|
||||
const performLocalPromises = actionTasks.map(t => TaskQueue.waitForPerformLocal(t));
|
||||
Actions.queueTasks(actionTasks);
|
||||
await performLocalPromises;
|
||||
|
|
Loading…
Reference in a new issue