From 886ea6c68c6352cc5ee155e67a8a6573d00ee4dc Mon Sep 17 00:00:00 2001 From: zadam Date: Sun, 24 Feb 2019 09:34:50 +0100 Subject: [PATCH] allow import of multiple files at the same time --- src/public/javascripts/dialogs/import.js | 57 +++++++++++++----------- src/routes/api/import.js | 2 +- src/services/import/enex.js | 7 +-- src/services/import/opml.js | 2 - src/services/import/single.js | 2 - src/services/import/tar.js | 2 - src/services/import_context.js | 20 +++++---- src/views/dialogs/import.ejs | 2 +- 8 files changed, 46 insertions(+), 48 deletions(-) diff --git a/src/public/javascripts/dialogs/import.js b/src/public/javascripts/dialogs/import.js index 95f19a940..7125e6ece 100644 --- a/src/public/javascripts/dialogs/import.js +++ b/src/public/javascripts/dialogs/import.js @@ -43,27 +43,45 @@ $form.submit(() => { return false; }); -function importIntoNote(importNoteId) { - const formData = new FormData(); - formData.append('upload', $fileUploadInput[0].files[0]); +async function importIntoNote(importNoteId) { + const files = Array.from($fileUploadInput[0].files); // shallow copy since we're resetting the upload button below // we generate it here (and not on opening) for the case when you try to import multiple times from the same // dialog (which shouldn't happen, but still ...) importId = utils.randomString(10); const safeImport = $safeImport.is(":checked") ? 1 : 0; + let noteId; - $.ajax({ - url: baseApiUrl + 'notes/' + importNoteId + '/import/' + importId + '/safe/' + safeImport, - headers: server.getHeaders(), - data: formData, - dataType: 'json', - type: 'POST', - contentType: false, // NEEDED, DON'T REMOVE THIS - processData: false, // NEEDED, DON'T REMOVE THIS - }) + for (const file of files) { + const formData = new FormData(); + formData.append('upload', file); + + noteId = await $.ajax({ + url: baseApiUrl + 'notes/' + importNoteId + '/import/' + importId + '/safe/' + safeImport, + headers: server.getHeaders(), + data: formData, + dataType: 'json', + type: 'POST', + timeout: 60 * 60 * 1000, + contentType: false, // NEEDED, DON'T REMOVE THIS + processData: false, // NEEDED, DON'T REMOVE THIS + }) // we actually ignore the error since it can be caused by HTTP timeout and use WS messages instead. - .fail((xhr, status, error) => {}); + .fail((xhr, status, error) => {}); + } + + $dialog.modal('hide'); + + infoService.showMessage("Import finished successfully."); + + await treeService.reload(); + + if (noteId) { + const node = await treeService.activateNote(noteId); + + node.setExpanded(true); + } } messagingService.subscribeToMessages(async message => { @@ -83,19 +101,6 @@ messagingService.subscribeToMessages(async message => { $importProgressCount.text(message.progressCount); } - else if (message.type === 'import-finished') { - $dialog.modal('hide'); - - infoService.showMessage("Import finished successfully."); - - await treeService.reload(); - - if (message.noteId) { - const node = await treeService.activateNote(message.noteId); - - node.setExpanded(true); - } - } }); $fileUploadInput.change(() => { diff --git a/src/routes/api/import.js b/src/routes/api/import.js index dbebf6aab..80f6a7462 100644 --- a/src/routes/api/import.js +++ b/src/routes/api/import.js @@ -36,7 +36,7 @@ async function importToBranch(req) { let note; // typically root of the import - client can show it after finishing the import - const importContext = new ImportContext(importId, safeImport); + const importContext = ImportContext.getInstance(importId, safeImport); try { if (extension === '.tar') { diff --git a/src/services/import/enex.js b/src/services/import/enex.js index fbfff05b1..0f49af32d 100644 --- a/src/services/import/enex.js +++ b/src/services/import/enex.js @@ -298,12 +298,7 @@ async function importEnex(importContext, file, parentNote) { return new Promise((resolve, reject) => { // resolve only when we parse the whole document AND saving of all notes have been finished - saxStream.on("end", () => { Promise.all(saveNotePromises).then(() => { - importContext.importFinished(rootNote.noteId); - - resolve(rootNote); - }); - }); + saxStream.on("end", () => { Promise.all(saveNotePromises).then(() => resolve(rootNote)) }); const bufferStream = new stream.PassThrough(); bufferStream.end(file.buffer); diff --git a/src/services/import/opml.js b/src/services/import/opml.js index d0d66aafc..af256e92e 100644 --- a/src/services/import/opml.js +++ b/src/services/import/opml.js @@ -64,8 +64,6 @@ async function importOpml(importContext, fileBuffer, parentNote) { returnNote = returnNote || note; } - importContext.importFinished(returnNote.noteId); - return returnNote; } diff --git a/src/services/import/single.js b/src/services/import/single.js index e874f00cf..487c8a2f3 100644 --- a/src/services/import/single.js +++ b/src/services/import/single.js @@ -21,7 +21,6 @@ async function importMarkdown(importContext, file, parentNote) { }); importContext.increaseProgressCount(); - importContext.importFinished(note.noteId); return note; } @@ -36,7 +35,6 @@ async function importHtml(importContext, file, parentNote) { }); importContext.increaseProgressCount(); - importContext.importFinished(note.noteId); return note; } diff --git a/src/services/import/tar.js b/src/services/import/tar.js index 2deff2974..807305b90 100644 --- a/src/services/import/tar.js +++ b/src/services/import/tar.js @@ -386,8 +386,6 @@ async function importTar(importContext, fileBuffer, importRootNote) { } } - importContext.importFinished(); - resolve(firstNote); }); diff --git a/src/services/import_context.js b/src/services/import_context.js index bca12895d..587e8d554 100644 --- a/src/services/import_context.js +++ b/src/services/import_context.js @@ -2,6 +2,9 @@ const messagingService = require('./messaging'); +// importId => ImportContext +const importContexts = {}; + class ImportContext { constructor(importId, safeImport) { // importId is to distinguish between different import events - it is possible (though not recommended) @@ -15,6 +18,15 @@ class ImportContext { this.lastSentCountTs = Date.now(); } + /** @return {ImportContext} */ + static getInstance(importId, safeImport) { + if (!importContexts[importId]) { + importContexts[importId] = new ImportContext(importId, safeImport); + } + + return importContexts[importId]; + } + async increaseProgressCount() { this.progressCount++; @@ -29,14 +41,6 @@ class ImportContext { } } - async importFinished(noteId) { - await messagingService.sendMessageToAllClients({ - importId: this.importId, - type: 'import-finished', - noteId: noteId - }); - } - // must remaing non-static async reportError(message) { await messagingService.sendMessageToAllClients({ diff --git a/src/views/dialogs/import.ejs b/src/views/dialogs/import.ejs index 786ca292d..ca9e19c93 100644 --- a/src/views/dialogs/import.ejs +++ b/src/views/dialogs/import.ejs @@ -16,7 +16,7 @@
- +

Content of the file will be imported as child note(s) into . Import file must be of supported type and have correct extension - one of .html, .md, .tar, .enex.