Mailspring/sync/sync-worker.js

203 lines
5.1 KiB
JavaScript
Raw Normal View History

const IMAPConnection = require('./imap/connection');
2016-06-20 15:19:16 +08:00
const RefreshMailboxesOperation = require('./imap/refresh-mailboxes-operation')
const SyncMailboxOperation = require('./imap/sync-mailbox-operation')
2016-06-21 08:33:23 +08:00
//
// account.syncPolicy = {
// afterSync: 'idle',
// limit: {
// after: Date.now() - 7 * 24 * 60 * 60 * 1000,
// count: 10000,
// },
// folderRecentSync: {
// every: 60 * 1000,
// },
// folderDeepSync: {
// every: 5 * 60 * 1000,
// },
// expiration: Date.now() + 60 * 60 * 1000,
// }
2016-06-19 18:02:32 +08:00
2016-06-20 15:19:16 +08:00
class SyncWorker {
2016-06-21 08:33:23 +08:00
2016-06-20 15:19:16 +08:00
constructor(account, db) {
2016-06-21 08:33:23 +08:00
this._db = db;
this._conn = null;
this._account = account;
this._lastFolderRecentSync = null;
this._lastFolderDeepSync = null;
2016-06-19 18:02:32 +08:00
2016-06-21 08:33:23 +08:00
this._syncTimer = null;
this._expirationTimer = null;
2016-06-19 18:02:32 +08:00
2016-06-21 08:33:23 +08:00
this.syncNow();
this.scheduleExpiration();
}
2016-06-19 18:02:32 +08:00
2016-06-21 08:33:23 +08:00
// TODO: How does this get called?
onAccountChanged() {
this.syncNow();
this.scheduleExpiration();
}
onExpired() {
// Returning syncs to the unclaimed queue every so often is healthy.
// TODO: That.
}
onSyncDidComplete() {
const {afterSync} = this._account.syncPolicy;
if (afterSync === 'idle') {
this.getInboxCategory().then((inboxCategory) => {
this._conn.openBox(inboxCategory.name, true).then(() => {
console.log(" - Idling on inbox category");
});
2016-06-19 18:02:32 +08:00
});
2016-06-21 08:33:23 +08:00
} else if (afterSync === 'close') {
console.log(" - Closing connection");
this._conn.end();
this._conn = null;
} else {
throw new Error(`onSyncDidComplete: Unknown afterSync behavior: ${afterSync}`)
}
}
onConnectionIdleUpdate() {
this.getInboxCategory((inboxCategory) => {
this._conn.runOperation(new SyncMailboxOperation(inboxCategory, {
scanAllUIDs: false,
limit: this.account.syncPolicy.options,
}));
2016-06-19 18:02:32 +08:00
});
2016-06-21 08:33:23 +08:00
}
2016-06-19 18:02:32 +08:00
2016-06-21 08:33:23 +08:00
getInboxCategory() {
return this._db.Category.find({where: {role: 'inbox'}})
}
getCurrentFolderSyncOptionsForPolicy() {
const {folderRecentSync, folderDeepSync, limit} = this._account.syncPolicy;
if (Date.now() - this._lastFolderDeepSync > folderDeepSync.every) {
return {
mode: 'deep',
options: {
scanAllUIDs: true,
limit: limit,
},
};
}
if (Date.now() - this._lastFolderRecentSync > folderRecentSync.every) {
return {
mode: 'shallow',
options: {
scanAllUIDs: false,
limit: limit,
},
};
}
return {
mode: 'none',
};
2016-06-20 15:19:16 +08:00
}
2016-06-19 18:02:32 +08:00
2016-06-21 08:33:23 +08:00
ensureConnection() {
if (!this._conn) {
const conn = new IMAPConnection(this._db, {
user: 'inboxapptest1@fastmail.fm',
password: 'trar2e',
host: 'mail.messagingengine.com',
port: 993,
tls: true,
});
conn.on('mail', () => {
this.onConnectionIdleUpdate();
})
conn.on('update', () => {
this.onConnectionIdleUpdate();
})
conn.on('queue-empty', () => {
});
this._conn = conn;
}
return this._conn.connect();
}
queueOperationsForUpdates() {
// todo: syncback operations belong here!
return this._conn.runOperation(new RefreshMailboxesOperation())
}
queueOperationsForFolderSyncs() {
2016-06-20 15:19:16 +08:00
const {Category} = this._db;
2016-06-21 08:33:23 +08:00
const {mode, options} = this.getCurrentFolderSyncOptionsForPolicy();
if (mode === 'none') {
return Promise.resolve();
}
return Category.findAll().then((categories) => {
2016-06-20 15:19:16 +08:00
const priority = ['inbox', 'drafts', 'sent'];
2016-06-21 08:33:23 +08:00
const sorted = categories.sort((a, b) =>
priority.indexOf(b.role) - priority.indexOf(a.role)
)
return Promise.all(sorted.map((cat) =>
this._conn.runOperation(new SyncMailboxOperation(cat, options))
)).then(() => {
if (mode === 'deep') {
this._lastFolderDeepSync = Date.now();
this._lastFolderRecentSync = Date.now();
} else if (mode === 'shallow') {
this._lastFolderRecentSync = Date.now();
}
});
2016-06-20 15:19:16 +08:00
});
2016-06-19 18:02:32 +08:00
}
2016-06-21 08:33:23 +08:00
syncNow() {
clearTimeout(this._syncTimer);
this.ensureConnection().then(() =>
this.queueOperationsForUpdates().then(() =>
this.queueOperationsForFolderSyncs()
)
).catch((err) => {
// Sync has failed for some reason. What do we do?!
console.error(err);
}).finally(() => {
this.onSyncDidComplete();
this.scheduleNextSync();
});
}
scheduleExpiration() {
const {expiration} = this._account.syncPolicy;
clearTimeout(this._expirationTimer);
this._expirationTimer = setTimeout(() => this.onExpired(), expiration);
}
scheduleNextSync() {
const {folderRecentSync, folderDeepSync} = this._account.syncPolicy;
let target = Number.MAX_SAFE_INTEGER;
if (folderRecentSync) {
target = Math.min(target, this._lastFolderRecentSync + folderRecentSync.every);
}
if (folderDeepSync) {
target = Math.min(target, this._lastFolderDeepSync + folderDeepSync.every);
}
console.log(`Next sync scheduled for ${new Date(target).toLocaleString()}`);
this._syncTimer = setTimeout(() => {
this.syncNow();
}, target - Date.now());
}
2016-06-19 18:02:32 +08:00
}
module.exports = SyncWorker;