mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-01-09 01:35:52 +08:00
Initial support for multisend
This commit is contained in:
parent
da2dbe43c9
commit
28766b8804
9 changed files with 67 additions and 142 deletions
|
@ -1 +0,0 @@
|
|||
/Users/bengotow/Work/F376/Projects/Nylas2/nylas-mail/packages/isomorphic-core/spec
|
|
@ -13,7 +13,6 @@ _ = require 'underscore'
|
|||
Task,
|
||||
Utils,
|
||||
ChangeMailTask,
|
||||
EnsureMessageInSentFolderTask,
|
||||
} = require 'nylas-exports'
|
||||
|
||||
xdescribe "ChangeMailTask", ->
|
||||
|
|
|
@ -6,6 +6,7 @@ import Actions from './actions';
|
|||
import Utils from './models/utils';
|
||||
|
||||
let AccountStore = null;
|
||||
let Task = null;
|
||||
|
||||
export default class MailsyncBridge {
|
||||
constructor() {
|
||||
|
@ -21,7 +22,8 @@ export default class MailsyncBridge {
|
|||
|
||||
this.clients = {};
|
||||
|
||||
AccountStore = require('./stores/account-store').default;
|
||||
Task = require('./tasks/task').default; //eslint-disable-line
|
||||
AccountStore = require('./stores/account-store').default; //eslint-disable-line
|
||||
AccountStore.listen(this.ensureClients, this);
|
||||
this.ensureClients();
|
||||
|
||||
|
@ -102,6 +104,14 @@ export default class MailsyncBridge {
|
|||
DatabaseStore.triggeringFromActionBridge = true;
|
||||
DatabaseStore.trigger(new DatabaseChangeRecord({type, objectClass, objects: [object]}));
|
||||
DatabaseStore.triggeringFromActionBridge = false;
|
||||
|
||||
if (object instanceof Task && object.status === 'complete') {
|
||||
if (object.error != null) {
|
||||
object.onError(object.error);
|
||||
} else {
|
||||
object.onSuccess();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ class DraftFactory
|
|||
unread: false
|
||||
starred: false
|
||||
folderUID: 0
|
||||
headerMessageId: Utils.generateTempId()
|
||||
headerMessageId: Utils.generateTempId() + "@" + require('os').hostname()
|
||||
from: [account.defaultMe()]
|
||||
date: (new Date)
|
||||
draft: true
|
||||
|
|
|
@ -413,19 +413,15 @@ class DraftStore extends NylasStore {
|
|||
// We also need to delay because the old draft window needs to fully
|
||||
// close. It takes windows currently (June 2016) 100ms to close by
|
||||
setTimeout(() => {
|
||||
this._notifyUserOfError({headerMessageId, threadId, errorMessage, errorDetail});
|
||||
const focusedThread = FocusedContentStore.focused('thread');
|
||||
if (threadId && focusedThread && focusedThread.id === threadId) {
|
||||
NylasEnv.showErrorDialog(errorMessage, {detail: errorDetail});
|
||||
} else {
|
||||
Actions.composePopoutDraft(headerMessageId, {errorMessage, errorDetail});
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
_notifyUserOfError({headerMessageId, threadId, errorMessage, errorDetail}) {
|
||||
const focusedThread = FocusedContentStore.focused('thread');
|
||||
if (threadId && focusedThread && focusedThread.id === threadId) {
|
||||
NylasEnv.showErrorDialog(errorMessage, {detail: errorDetail});
|
||||
} else {
|
||||
Actions.composePopoutDraft(headerMessageId, {errorMessage, errorDetail});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new DraftStore();
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
import Task from './task';
|
||||
import SendDraftTask from './send-draft-task';
|
||||
|
||||
|
||||
export default class EnsureMessageInSentFolderTask extends Task {
|
||||
constructor(opts = {}) {
|
||||
super(opts);
|
||||
this.message = opts.message;
|
||||
this.customSentMessage = opts.customSentMessage;
|
||||
}
|
||||
|
||||
label() {
|
||||
return "Saving to sent folder";
|
||||
}
|
||||
|
||||
isDependentOnTask(other) {
|
||||
return (other instanceof SendDraftTask) && (other.message) && (other.message.id === this.message.id);
|
||||
}
|
||||
|
||||
performLocal() {
|
||||
if (!this.message) {
|
||||
const errMsg = `Attempt to call ${this.constructor.name}.performLocal without a message`;
|
||||
return Promise.reject(new Error(errMsg));
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
/* eslint global-require: 0 */
|
||||
import AccountStore from '../stores/account-store';
|
||||
import Task from './task';
|
||||
import Actions from '../actions';
|
||||
import SoundRegistry from '../../registries/sound-registry';
|
||||
|
||||
const OPEN_TRACKING_ID = NylasEnv.packages.pluginIdFor('open-tracking')
|
||||
const LINK_TRACKING_ID = NylasEnv.packages.pluginIdFor('link-tracking')
|
||||
|
@ -17,132 +19,69 @@ export default class SendDraftTask extends Task {
|
|||
this.emitError = emitError
|
||||
this.playSound = playSound
|
||||
this.allowMultiSend = allowMultiSend
|
||||
|
||||
if (draft) {
|
||||
// const pluginsAvailable = (OPEN_TRACKING_ID && LINK_TRACKING_ID);
|
||||
// const pluginsInUse = pluginsAvailable && (!!this.draft.metadataForPluginId(OPEN_TRACKING_ID) || !!this.draft.metadataForPluginId(LINK_TRACKING_ID));
|
||||
// if (pluginsInUse) {
|
||||
this.perRecipientBodies = {
|
||||
self: draft.body,
|
||||
};
|
||||
// perform transformations here
|
||||
const ps = draft.participants({includeFrom: false, includeBcc: true});
|
||||
ps.forEach((p) => {
|
||||
this.perRecipientBodies[p.email] = draft.body + p.email;
|
||||
})
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
label() {
|
||||
return "Sending message";
|
||||
}
|
||||
|
||||
assertDraftValidity = () => {
|
||||
if (!this.draft.from[0]) {
|
||||
return Promise.reject(new Error("SendDraftTask - you must populate `from` before sending."));
|
||||
}
|
||||
|
||||
validate() {
|
||||
const account = AccountStore.accountForEmail(this.draft.from[0].email);
|
||||
|
||||
if (!this.draft.from[0]) {
|
||||
throw new Error("SendDraftTask - you must populate `from` before sending.");
|
||||
}
|
||||
if (!account) {
|
||||
return Promise.reject(new Error("SendDraftTask - you can only send drafts from a configured account."));
|
||||
throw new Error("SendDraftTask - you can only send drafts from a configured account.");
|
||||
}
|
||||
if (this.draft.accountId !== account.id) {
|
||||
return Promise.reject(new Error("The from address has changed since you started sending this draft. Double-check the draft and click 'Send' again."));
|
||||
throw new Error("The from address has changed since you started sending this draft. Double-check the draft and click 'Send' again.");
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
_trackingPluginsInUse() {
|
||||
const pluginsAvailable = (OPEN_TRACKING_ID && LINK_TRACKING_ID);
|
||||
if (!pluginsAvailable) {
|
||||
return false;
|
||||
}
|
||||
return (!!this.draft.metadataForPluginId(OPEN_TRACKING_ID) || !!this.draft.metadataForPluginId(LINK_TRACKING_ID)) || false;
|
||||
}
|
||||
|
||||
_createMessageFromResponse = (responseJSON) => {
|
||||
const {failedRecipients, message} = responseJSON
|
||||
if (failedRecipients && failedRecipients.length > 0) {
|
||||
const errorMessage = `We had trouble sending this message to all recipients. ${failedRecipients} may not have received this email.`;
|
||||
NylasEnv.showErrorDialog(errorMessage, {showInMainWindow: true});
|
||||
}
|
||||
if (!message || !message.id || !message.account_id) {
|
||||
const errorMessage = `Your message successfully sent; however, we had trouble saving your message, "${message.subject}", to your Sent folder.`;
|
||||
if (!message) {
|
||||
throw new Error(`${errorMessage}\n\nError: Did not return message`)
|
||||
}
|
||||
if (!message.id) {
|
||||
throw new Error(`${errorMessage}\n\nError: Returned a message without id`)
|
||||
}
|
||||
if (!message.accountId) {
|
||||
throw new Error(`${errorMessage}\n\nError: Returned a message without accountId`)
|
||||
}
|
||||
}
|
||||
|
||||
this.message = new Message().fromJSON(message);
|
||||
this.message.id = this.draft.id;
|
||||
this.message.body = this.draft.body;
|
||||
this.message.draft = false;
|
||||
this.message.clonePluginMetadataFrom(this.draft);
|
||||
|
||||
return DatabaseStore.inTransaction((t) =>
|
||||
this.refreshDraftReference().then(() => {
|
||||
return t.persistModel(this.message);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
onSuccess = () => {
|
||||
onSuccess() {
|
||||
Actions.recordUserEvent("Draft Sent")
|
||||
Actions.draftDeliverySucceeded({message: this.message, messageId: this.message.id, headerMessageId: this.draft.headerMessageId});
|
||||
Actions.draftDeliverySucceeded({headerMessageId: this.draft.headerMessageId});
|
||||
|
||||
// Play the sending sound
|
||||
if (this.playSound && NylasEnv.config.get("core.sending.sounds")) {
|
||||
SoundRegistry.playSound('send');
|
||||
}
|
||||
return Promise.resolve(Task.Status.Success);
|
||||
}
|
||||
|
||||
onError = (err) => {
|
||||
let message = err.message;
|
||||
|
||||
// TODO Handle errors in a cleaner way
|
||||
if (err instanceof APIError) {
|
||||
const errorMessage = (err.body && err.body.message) || ''
|
||||
message = `Sorry, this message could not be sent, please try again.`;
|
||||
message += `\n\nReason: ${err.message}`
|
||||
if (errorMessage.includes('unable to reach your SMTP server')) {
|
||||
message = `Sorry, this message could not be sent. There was a network error, please make sure you are online.`
|
||||
}
|
||||
if (errorMessage.includes('Incorrect SMTP username or password') ||
|
||||
errorMessage.includes('SMTP protocol error') ||
|
||||
errorMessage.includes('unable to look up your SMTP host')) {
|
||||
Actions.updateAccount(this.draft.accountId, {syncState: Account.SYNC_STATE_AUTH_FAILED})
|
||||
message = `Sorry, this message could not be sent due to an authentication error. Please re-authenticate your account and try again.`
|
||||
}
|
||||
if (err.statusCode === 402) {
|
||||
if (errorMessage.includes('at least one recipient')) {
|
||||
message = `This message could not be delivered to at least one recipient. (Note: other recipients may have received this message - you should check Sent Mail before re-sending this message.)`;
|
||||
} else {
|
||||
message = `Sorry, this message could not be sent because it was rejected by your mail provider. (${errorMessage})`;
|
||||
if (err.body.server_error) {
|
||||
message += `\n\n${err.body.server_error}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
onError({key, debuginfo}) {
|
||||
let message = key;
|
||||
if (key === 'no-sent-folder') {
|
||||
message = "We couldn't find a Sent folder in your account.";
|
||||
}
|
||||
|
||||
if (this.emitError) {
|
||||
if (err instanceof RequestEnsureOnceError) {
|
||||
Actions.draftDeliveryFailed({
|
||||
threadId: this.draft.threadId,
|
||||
headerMessageId: this.draft.headerMessageId,
|
||||
errorMessage: `WARNING: Your message MIGHT have sent. We encountered a network problem while the send was in progress. Please wait a few minutes then check your sent folder and try again if necessary.`,
|
||||
errorDetail: `Please email support@nylas.com if you see this error message.`,
|
||||
});
|
||||
} else {
|
||||
Actions.draftDeliveryFailed({
|
||||
threadId: this.draft.threadId,
|
||||
headerMessageId: this.draft.headerMessageId,
|
||||
errorMessage: message,
|
||||
errorDetail: err.message + (err.error ? err.error.stack : '') + err.stack,
|
||||
});
|
||||
}
|
||||
Actions.draftDeliveryFailed({
|
||||
threadId: this.draft.threadId,
|
||||
headerMessageId: this.draft.headerMessageId,
|
||||
errorMessage: message,
|
||||
errorDetail: debuginfo,
|
||||
});
|
||||
}
|
||||
Actions.recordUserEvent("Draft Sending Errored", {
|
||||
error: err.message,
|
||||
errorClass: err.constructor.name,
|
||||
error: message,
|
||||
key: key,
|
||||
})
|
||||
err.message = `Send failed (client): ${err.message}`
|
||||
NylasEnv.reportError(err);
|
||||
|
||||
return Promise.resolve([Task.Status.Failed, err]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,6 +27,9 @@ export default class Task extends Model {
|
|||
queryable: true,
|
||||
modelKey: 'status',
|
||||
}),
|
||||
error: Attributes.Object({
|
||||
modelKey: 'error',
|
||||
}),
|
||||
});
|
||||
|
||||
// Public: Override the constructor to pass initial args to your Task and
|
||||
|
@ -116,4 +119,12 @@ export default class Task extends Model {
|
|||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
onError(err) {
|
||||
// noop
|
||||
}
|
||||
|
||||
onSuccess() {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,7 +100,6 @@ lazyLoad(`IMAPSearchQueryBackend`, 'services/search/search-query-backend-imap');
|
|||
lazyLoad(`TaskFactory`, 'flux/tasks/task-factory');
|
||||
lazyLoadAndRegisterTask(`Task`, 'task');
|
||||
lazyLoadAndRegisterTask(`EventRSVPTask`, 'event-rsvp-task');
|
||||
lazyLoadAndRegisterTask(`BaseDraftTask`, 'base-draft-task');
|
||||
lazyLoadAndRegisterTask(`SendDraftTask`, 'send-draft-task');
|
||||
lazyLoadAndRegisterTask(`ChangeMailTask`, 'change-mail-task');
|
||||
lazyLoadAndRegisterTask(`DestroyDraftTask`, 'destroy-draft-task');
|
||||
|
@ -117,7 +116,6 @@ lazyLoadAndRegisterTask(`SyncbackCategoryTask`, 'syncback-category-task');
|
|||
lazyLoadAndRegisterTask(`SyncbackMetadataTask`, 'syncback-metadata-task');
|
||||
lazyLoadAndRegisterTask(`ReprocessMailRulesTask`, 'reprocess-mail-rules-task');
|
||||
lazyLoadAndRegisterTask(`SendFeatureUsageEventTask`, 'send-feature-usage-event-task');
|
||||
lazyLoadAndRegisterTask(`EnsureMessageInSentFolderTask`, 'ensure-message-in-sent-folder-task');
|
||||
|
||||
// Stores
|
||||
// These need to be required immediately since some Stores are
|
||||
|
|
Loading…
Reference in a new issue