diff --git a/chrome/content/zotero/xpcom/data/dataObject.js b/chrome/content/zotero/xpcom/data/dataObject.js index 4783d1d68..66beca393 100644 --- a/chrome/content/zotero/xpcom/data/dataObject.js +++ b/chrome/content/zotero/xpcom/data/dataObject.js @@ -1160,7 +1160,7 @@ Zotero.DataObject.prototype.updateSynced = Zotero.Promise.coroutine(function* (s */ Zotero.DataObject.prototype.erase = Zotero.Promise.coroutine(function* (options = {}) { if (!options || typeof options != 'object') { - throw new Error("'options' must be an object"); + throw new Error("'options' must be an object (" + typeof options + ")"); } var env = { diff --git a/chrome/content/zotero/xpcom/data/dataObjects.js b/chrome/content/zotero/xpcom/data/dataObjects.js index f07d477b8..548d8d6bd 100644 --- a/chrome/content/zotero/xpcom/data/dataObjects.js +++ b/chrome/content/zotero/xpcom/data/dataObjects.js @@ -909,6 +909,7 @@ Zotero.DataObjects.prototype.getPrimaryDataSQLPart = function (part) { * * @param {Integer|Integer[]} ids - Object ids * @param {Object} [options] - See Zotero.DataObject.prototype.erase + * @param {Function} [options.onProgress] - f(progress, progressMax) * @return {Promise} */ Zotero.DataObjects.prototype.erase = Zotero.Promise.coroutine(function* (ids, options = {}) { @@ -920,6 +921,9 @@ Zotero.DataObjects.prototype.erase = Zotero.Promise.coroutine(function* (ids, op continue; } yield obj.erase(options); + if (options.onProgress) { + options.onProgress(i + 1, ids.length); + } } this.unload(ids); }.bind(this)); diff --git a/chrome/content/zotero/xpcom/data/item.js b/chrome/content/zotero/xpcom/data/item.js index 35ad757b6..5c574bcf8 100644 --- a/chrome/content/zotero/xpcom/data/item.js +++ b/chrome/content/zotero/xpcom/data/item.js @@ -3972,13 +3972,13 @@ Zotero.Item.prototype._eraseData = Zotero.Promise.coroutine(function* (env) { ? (yield this.ObjectsClass.getByLibraryAndKeyAsync(this.libraryID, parentItem)) : null; - if (parentItem) { + if (parentItem && !env.options.skipParentRefresh) { Zotero.Notifier.queue('refresh', 'item', parentItem.id); } // // Delete associated attachment files if (this.isAttachment()) { - let linkMode = this.getAttachmentLinkMode(); + let linkMode = this.attachmentLinkMode; // If link only, nothing to delete if (linkMode != Zotero.Attachments.LINK_MODE_LINKED_URL) { try { @@ -4005,7 +4005,9 @@ Zotero.Item.prototype._eraseData = Zotero.Promise.coroutine(function* (env) { for (let i=0; i { + item.isTopLevelItem() ? toDelete.top.push(item.id) : toDelete.child.push(item.id) + }); + + // Show progress meter during deletions + let eraseOptions = options.onProgress + ? { + onProgress: function (progress, progressMax) { + options.onProgress(processed + progress, deleted.length); + } + } + : undefined; + for (let x of ['top', 'child']) { + await Zotero.Utilities.Internal.forEachChunkAsync( + toDelete[x], + 1000, + async function (chunk) { + await this.erase(chunk, eraseOptions); + processed += chunk.length; + }.bind(this) + ); + } + Zotero.debug("Emptied " + deleted.length + " item(s) from trash in " + (new Date() - t) + " ms"); } - if (deletedIDs.length) { - Zotero.debug("Emptied " + deletedIDs.length + " item(s) from trash in " + (new Date() - t) + " ms"); - } - - return deletedIDs.length; - }); + return deleted.length; + }; /** diff --git a/chrome/content/zotero/xpcom/itemTreeView.js b/chrome/content/zotero/xpcom/itemTreeView.js index afd33a043..707cf4313 100644 --- a/chrome/content/zotero/xpcom/itemTreeView.js +++ b/chrome/content/zotero/xpcom/itemTreeView.js @@ -702,7 +702,11 @@ Zotero.ItemTreeView.prototype.notify = Zotero.Promise.coroutine(function* (actio yield this.refresh(skipExpandMatchParents); refreshed = true; madeChanges = true; - sort = true; + // Don't bother re-sorting in trash, since it's probably just a modification of a parent + // item that's about to be deleted + if (!collectionTreeRow.isTrash()) { + sort = true; + } } else if (collectionTreeRow.isFeed()) { diff --git a/chrome/content/zotero/xpcom/sync/syncEventListeners.js b/chrome/content/zotero/xpcom/sync/syncEventListeners.js index 28e69ad8d..6a131c0c8 100644 --- a/chrome/content/zotero/xpcom/sync/syncEventListeners.js +++ b/chrome/content/zotero/xpcom/sync/syncEventListeners.js @@ -39,41 +39,30 @@ Zotero.Sync.EventListeners.ChangeListener = new function () { return; } - var syncSQL = "REPLACE INTO syncDeleteLog (syncObjectTypeID, libraryID, key) " - + "VALUES (?, ?, ?)"; - var storageSQL = "REPLACE INTO storageDeleteLog (libraryID, key) VALUES (?, ?)"; + var syncSQL = "REPLACE INTO syncDeleteLog (syncObjectTypeID, libraryID, key) VALUES "; + var storageSQL = "REPLACE INTO storageDeleteLog (libraryID, key) VALUES "; var storageForLibrary = {}; return Zotero.Utilities.Internal.forEachChunkAsync( ids, 100, - function (chunk) { - return Zotero.DB.executeTransaction(function* () { - for (let id of chunk) { - if (extraData[id] && extraData[id].skipDeleteLog) { - continue; - } - + async function (chunk) { + var syncSets = []; + var storageSets = []; + chunk + .filter(id => !extraData[id] || !extraData[id].skipDeleteLog) + .forEach(id => { if (type == 'setting') { var [libraryID, key] = id.split("/"); } else { var { libraryID, key } = extraData[id]; } - if (!key) { throw new Error("Key not provided in notifier object"); } - - yield Zotero.DB.queryAsync( - syncSQL, - [ - syncObjectTypeID, - libraryID, - key - ] - ); + syncSets.push(syncObjectTypeID, libraryID, key); if (type == 'item') { if (storageForLibrary[libraryID] === undefined) { @@ -81,17 +70,28 @@ Zotero.Sync.EventListeners.ChangeListener = new function () { Zotero.Sync.Storage.Local.getModeForLibrary(libraryID) == 'webdav'; } if (storageForLibrary[libraryID] && extraData[id].storageDeleteLog) { - yield Zotero.DB.queryAsync( - storageSQL, - [ - libraryID, - key - ] - ); + storageSets.push(libraryID, key); } } - } - }); + }); + + if (storageSets.length) { + return Zotero.DB.executeTransaction(function* () { + yield Zotero.DB.queryAsync( + syncSQL + Array(syncSets.length / 3).fill('(?, ?, ?)').join(', '), + syncSets + ); + yield Zotero.DB.queryAsync( + storageSQL + Array(storageSets.length / 3).fill('(?, ?)').join(', '), + storageSets + ); + }); + } + else if (syncSets.length) { + await Zotero.DB.queryAsync( + syncSQL + Array(syncSets.length / 3).fill('(?, ?, ?)').join(', '), syncSets + ); + } } ); }); diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js index 8c05035f8..d158b1260 100644 --- a/chrome/content/zotero/zoteroPane.js +++ b/chrome/content/zotero/zoteroPane.js @@ -2042,7 +2042,21 @@ var ZoteroPane = new function() + Zotero.getString('general.actionCannotBeUndone') ); if (result) { - let deleted = yield Zotero.Items.emptyTrash(libraryID); + Zotero.showZoteroPaneProgressMeter(null, true); + try { + let deleted = yield Zotero.Items.emptyTrash( + libraryID, + { + onProgress: (progress, progressMax) => { + var percentage = Math.round((progress / progressMax) * 100); + Zotero.updateZoteroPaneProgressMeter(percentage); + } + } + ); + } + finally { + Zotero.hideZoteroPaneOverlays(); + } yield Zotero.purgeDataObjects(); } });