From 6563602fb39db8ae9a976784b62538cbcf3de108 Mon Sep 17 00:00:00 2001 From: Daniel Weipert Date: Thu, 1 Jul 2021 16:07:08 +0200 Subject: Adds progress log and ui enhancements --- app/imap.js | 66 +++++++++++++++++++++++++++++++++++++++----- app/src/App.vue | 16 +++++++++++ app/src/components/Panel.vue | 40 ++++++++++++++++++++------- app/src/pages/Home.vue | 66 +++++++++++++++++++++++++++++++------------- app/src/store.js | 2 ++ 5 files changed, 154 insertions(+), 36 deletions(-) (limited to 'app') diff --git a/app/imap.js b/app/imap.js index 181d9cf..4041089 100644 --- a/app/imap.js +++ b/app/imap.js @@ -5,6 +5,13 @@ ipcMain.on('imap:listTree:from', listTreeFrom); ipcMain.on('imap:listTree:to', listTreeTo); ipcMain.on('imap:migrate', migrate); +/** + * Connect to server + * + * @param {object} options + * + * @returns {Promise} + */ async function connect (options) { const client = new ImapFlow({ host: options.server, @@ -13,6 +20,7 @@ async function connect (options) { user: options.username, pass: options.password, }, + tls: options.tls, }); await client.connect(); @@ -20,6 +28,15 @@ async function connect (options) { return client; } +/** + * Turn fetch response into array + * + * @param {string} range + * @param {object} query + * @param {object} options + * + * @returns {Promise<*[]>} + */ ImapFlow.prototype.fetchArray = async function (range, query, options = {}) { const msgsGenerator = await this.fetch(range, query, options); const msgs = []; @@ -30,45 +47,80 @@ ImapFlow.prototype.fetchArray = async function (range, query, options = {}) { return msgs; }; +/** + * @param {IpcMainEvent} event + * @param {object} options + */ async function listTreeFrom (event, options) { - const client = await connect(options); + try { + const client = await connect(options); - event.reply('imap:listTree:from:reply', await client.listTree()); - await client.logout(); + event.reply('imap:listTree:from:reply', await client.listTree()); + await client.logout(); + } catch (e) { + event.reply('imap:from:error', e); + } } +/** + * @param {IpcMainEvent} event + * @param {object} options + */ async function listTreeTo (event, options) { - const client = await connect(options); + try { + const client = await connect(options); - event.reply('imap:listTree:to:reply', await client.listTree()); - await client.logout(); + event.reply('imap:listTree:to:reply', await client.listTree()); + await client.logout(); + } catch (e) { + event.reply('imap:to:error', e); + } } +/** + * @param {IpcMainEvent} event + * @param {object} from + * @param {object} to + */ async function migrate (event, { from, to }) { const fromClient = await connect(from); const toClient = await connect(to); + event.reply('imap:migrate:progress', 'Getting folders'); const fromFolders = (await fromClient.list()).filter((folder) => folder.subscribed); for (const fromFolder of fromFolders) { + event.reply('imap:migrate:progress', `Working on folder "${fromFolder.path}"`); + try { await toClient.mailboxCreate(fromFolder.path); } catch (e) {} + const appendCommands = []; const fromMailbox = await fromClient.getMailboxLock(fromFolder.path); const toMailbox = await toClient.getMailboxLock(fromFolder.path); try { + event.reply('imap:migrate:progress', '- Collecting messages from target for comparison'); const toMsgs = await toClient.fetchArray('1:*', { flags: true, envelope: true, source: true }); + + event.reply('imap:migrate:progress', '- Collecting messages from source'); const fromMsgs = await fromClient.fetchArray('1:*', { flags: true, envelope: true, source: true }); + event.reply('imap:migrate:progress', '- Comparing messages'); const msgs = fromMsgs.filter((fromMsg) => !toMsgs.some((toMsg) => Buffer.compare(toMsg.source, fromMsg.source) === 0)); + event.reply('imap:migrate:progress', `- Message status: Found ${fromMsgs.length} messages total and ${msgs.length} new messages to migrate`); + await toClient.noop(); for (const msg of msgs) { - toClient.append(fromFolder.path, msg.source, Array.from(msg.flags), msg.envelope.date); + appendCommands.push(toClient.append(fromFolder.path, msg.source, Array.from(msg.flags), msg.envelope.date)); } } finally { + await Promise.all(appendCommands); + event.reply('imap:migrate:progress', '- Done'); fromMailbox.release(); toMailbox.release(); } } + + event.reply('imap:migrate:progress', 'Done'); } diff --git a/app/src/App.vue b/app/src/App.vue index 98240ae..87365a2 100644 --- a/app/src/App.vue +++ b/app/src/App.vue @@ -1,3 +1,19 @@ + + + + diff --git a/app/src/components/Panel.vue b/app/src/components/Panel.vue index 3ae4000..527a8a9 100644 --- a/app/src/components/Panel.vue +++ b/app/src/components/Panel.vue @@ -1,21 +1,35 @@ diff --git a/app/src/pages/Home.vue b/app/src/pages/Home.vue index bfe92bf..8385a46 100644 --- a/app/src/pages/Home.vue +++ b/app/src/pages/Home.vue @@ -1,22 +1,38 @@ @@ -31,7 +47,10 @@ export default { }, data () { - return {}; + return { + progress: '', + log: [], + }; }, computed: { @@ -61,6 +80,18 @@ export default { this.$electron.ipcRenderer.on('imap:listTree:to:reply', (event, folders) => { this.to.folders = folders; }); + + this.$electron.ipcRenderer.on('imap:from:error', (event, error) => { + this.from.error = error; + }); + this.$electron.ipcRenderer.on('imap:to:error', (event, error) => { + this.to.error = error; + }); + + this.$electron.ipcRenderer.on('imap:migrate:progress', (event, progress) => { + this.progress = progress; + this.log.push(progress); + }); }, methods: { @@ -73,6 +104,9 @@ export default { }, async migrate () { + this.progress = ''; + this.log = []; + this.$electron.ipcRenderer.send('imap:migrate', { from: JSON.parse(JSON.stringify(this.from)), to: JSON.parse(JSON.stringify(this.to)), @@ -83,12 +117,6 @@ export default {