mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-11-11 01:54:40 +08:00
[local-sync, iso-core] feat(send): add finishing touches to gmail multi-send
Delete the sent messages that gmail automatically creates and save our generic form of the draft as a sent message.
This commit is contained in:
parent
1a24840062
commit
e785d73bdc
8 changed files with 87 additions and 42 deletions
|
@ -1,4 +1,5 @@
|
|||
const _ = require('underscore');
|
||||
const PromiseUtils = require('./promise-utils')
|
||||
|
||||
const {
|
||||
IMAPConnectionNotReadyError,
|
||||
|
@ -173,6 +174,24 @@ class IMAPBox {
|
|||
return this._conn._imap.delLabelsAsync(range, labels)
|
||||
}
|
||||
|
||||
append(rawMime, options) {
|
||||
if (!this._conn._imap) {
|
||||
throw new IMAPConnectionNotReadyError(`IMAPBox::append`)
|
||||
}
|
||||
return PromiseUtils.promisify(this._conn._imap.append).call(
|
||||
this._conn._imap, rawMime, options
|
||||
);
|
||||
}
|
||||
|
||||
search(criteria) {
|
||||
if (!this._conn._imap) {
|
||||
throw new IMAPConnectionNotReadyError(`IMAPBox::search`)
|
||||
}
|
||||
return PromiseUtils.promisify(this._conn._imap.search).call(
|
||||
this._conn._imap, criteria
|
||||
);
|
||||
}
|
||||
|
||||
closeBox({expunge = true} = {}) {
|
||||
if (!this._conn._imap) {
|
||||
throw new IMAPConnectionNotReadyError(`IMAPBox::closeBox`)
|
||||
|
|
|
@ -168,10 +168,10 @@ module.exports = (server) => {
|
|||
// gmail creates sent messages for each one, go through and delete them
|
||||
if (account.provider === 'gmail') {
|
||||
try {
|
||||
// TODO: use type: "PermananentDeleteMessage" once it's fully implemented
|
||||
await db.SyncbackRequest.create({
|
||||
type: "DeleteMessage",
|
||||
props: { messageId: draft.id },
|
||||
accountId: account.id,
|
||||
type: "DeleteSentMessage",
|
||||
props: { messageId: `${draft.id}@nylas.com` },
|
||||
});
|
||||
} catch (err) {
|
||||
// Even if this fails, we need to finish the multi-send session,
|
||||
|
@ -185,7 +185,7 @@ module.exports = (server) => {
|
|||
await db.SyncbackRequest.create({
|
||||
accountId: account.id,
|
||||
type: "SaveSentMessage",
|
||||
props: {rawMime},
|
||||
props: {rawMime, messageId: `${draft.id}@nylas.com`},
|
||||
});
|
||||
|
||||
await (draft.isSent = true);
|
||||
|
|
|
@ -57,6 +57,7 @@ class SendmailClient {
|
|||
}
|
||||
msgData.subject = draft.subject;
|
||||
msgData.html = draft.body;
|
||||
msgData.messageId = `${draft.id}@nylas.com`;
|
||||
|
||||
// TODO: attachments
|
||||
|
||||
|
@ -69,18 +70,17 @@ class SendmailClient {
|
|||
msgData.headers = draft.headers;
|
||||
msgData.headers['User-Agent'] = `NylasMailer-K2`
|
||||
|
||||
// TODO: do we want to set messageId or date?
|
||||
|
||||
return msgData;
|
||||
}
|
||||
|
||||
async buildMime(draft) {
|
||||
const builder = mailcomposer(this._draftToMsgData(draft))
|
||||
return new Promise((resolve, reject) => {
|
||||
const mimeNode = await (new Promise((resolve, reject) => {
|
||||
builder.build((error, result) => {
|
||||
error ? reject(error) : resolve(result)
|
||||
})
|
||||
})
|
||||
}));
|
||||
return mimeNode.toString('ascii')
|
||||
}
|
||||
|
||||
async send(draft) {
|
||||
|
|
|
@ -56,6 +56,8 @@ class SyncbackTaskFactory {
|
|||
Task = require('./syncback_tasks/delete-message.imap'); break;
|
||||
case "SaveSentMessage":
|
||||
Task = require('./syncback_tasks/save-sent-message.imap'); break;
|
||||
case "DeleteSentMessage":
|
||||
Task = require('./syncback_tasks/delete-sent-message.gmail'); break;
|
||||
default:
|
||||
throw new Error(`Task type not defined in syncback-task-factory: ${syncbackRequest.type}`)
|
||||
}
|
||||
|
|
|
@ -6,13 +6,10 @@ class DeleteMessageIMAP extends SyncbackTask {
|
|||
return `DeleteMessage`;
|
||||
}
|
||||
|
||||
run(db, imap) {
|
||||
async run(db, imap) {
|
||||
const messageId = this.syncbackRequestObject().props.messageId
|
||||
|
||||
return TaskHelpers.openMessageBox({messageId, db, imap})
|
||||
.then(({box, message}) => {
|
||||
return box.addFlags(message.folderImapUID, 'DELETED')
|
||||
})
|
||||
const {box, message} = await TaskHelpers.openMessageBox({messageId, db, imap})
|
||||
return box.addFlags(message.folderImapUID, ['DELETED'])
|
||||
}
|
||||
}
|
||||
module.exports = DeleteMessageIMAP;
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
const SyncbackTask = require('./syncback-task')
|
||||
|
||||
class DeleteSentMessageGMAIL extends SyncbackTask {
|
||||
description() {
|
||||
return `DeleteSentMessage`;
|
||||
}
|
||||
|
||||
async run(db, imap) {
|
||||
const {messageId} = this.syncbackRequestObject().props
|
||||
|
||||
const trash = await db.Folder.find({where: {role: 'trash'}});
|
||||
if (!trash) { throw new Error(`Could not find folder with role 'trash'.`) }
|
||||
|
||||
const allMail = await db.Folder.find({where: {role: 'all'}});
|
||||
if (!allMail) { throw new Error(`Could not find folder with role 'all'.`) }
|
||||
|
||||
// Move the message from all mail to trash and then delete it from there
|
||||
const steps = [
|
||||
{folder: allMail, deleteFn: (box, uid) => box.moveFromBox(uid, trash.name)},
|
||||
{folder: trash, deleteFn: (box, uid) => box.addFlags(uid, 'DELETED')},
|
||||
]
|
||||
|
||||
for (const {folder, deleteFn} of steps) {
|
||||
const box = await imap.openBox(folder.name);
|
||||
const uids = await box.search([['HEADER', 'Message-ID', messageId]])
|
||||
for (const uid of uids) {
|
||||
await deleteFn(box, uid);
|
||||
}
|
||||
box.closeBox();
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = DeleteSentMessageGMAIL;
|
|
@ -1,24 +0,0 @@
|
|||
const SyncbackTask = require('./syncback-task')
|
||||
|
||||
class PermanentlyDeleteMessageIMAP extends SyncbackTask {
|
||||
description() {
|
||||
return `PermanentlyDeleteMessage`;
|
||||
}
|
||||
|
||||
async run(db, imap) {
|
||||
const messageId = this.syncbackRequestObject().props.messageId
|
||||
const message = await db.Message.findById(messageId);
|
||||
const folder = await db.Folder.findById(message.folderId);
|
||||
const box = await imap.openBox(folder.name);
|
||||
const result = await box.addFlags(message.folderImapUID, 'DELETED');
|
||||
return result;
|
||||
|
||||
// TODO: We need to also delete the message from the trash
|
||||
// if (folder.role === 'trash') { return result; }
|
||||
//
|
||||
// const trash = await db.Folder.find({where: {role: 'trash'}});
|
||||
// const trashBox = await imap.openBox(trash.name);
|
||||
// return await trashBox.addFlags(message.folderImapUID, 'DELETED');
|
||||
}
|
||||
}
|
||||
module.exports = PermanentlyDeleteMessageIMAP;
|
|
@ -6,10 +6,28 @@ class SaveSentMessageIMAP extends SyncbackTask {
|
|||
}
|
||||
|
||||
async run(db, imap) {
|
||||
// TODO: gmail doesn't have a sent folder
|
||||
const folder = await db.Folder.find({where: {role: 'sent'}});
|
||||
const box = await imap.openBox(folder.name);
|
||||
return box.append(this.syncbackRequestObject().props.rawMime);
|
||||
const {rawMime, messageId} = this.syncbackRequestObject().props;
|
||||
|
||||
// Non-gmail
|
||||
const sentFolder = await db.Folder.find({where: {role: 'sent'}});
|
||||
if (sentFolder) {
|
||||
const box = await imap.openBox(sentFolder.name);
|
||||
return box.append(rawMime);
|
||||
}
|
||||
|
||||
// Gmail, we need to add the message to all mail and add the sent label
|
||||
const sentLabel = await db.Label.find({where: {role: 'sent'}});
|
||||
const allMail = await db.Folder.find({where: {role: 'all'}});
|
||||
if (sentLabel && allMail) {
|
||||
let box = await imap.openBox(allMail.name);
|
||||
await box.append(rawMime, {flags: 'SEEN'})
|
||||
const uids = await box.search([['HEADER', 'Message-ID', messageId]])
|
||||
// There should only be one uid in the array
|
||||
return box.setLabels(uids[0], '\\Sent');
|
||||
}
|
||||
|
||||
throw new Error('Could not save message to sent folder.')
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SaveSentMessageIMAP;
|
||||
|
|
Loading…
Reference in a new issue