From 0469d6506a644ce2ac54ba4a077f6c25ab8d76d6 Mon Sep 17 00:00:00 2001 From: Dan Stillman Date: Sun, 10 Apr 2016 18:58:01 -0400 Subject: [PATCH] Show toolbar icon and collections pane while items are loading Items in a library are now loaded only when a library is clicked on and at sync time. There might be some other areas where they need to be loaded or where this causes problems (e.g., drag and drop, word processor integration). --- chrome/content/zotero/overlay.js | 3 +- .../content/zotero/xpcom/data/dataObjects.js | 10 +++-- chrome/content/zotero/xpcom/data/library.js | 42 +++++++++++++++++++ .../content/zotero/xpcom/sync/syncRunner.js | 12 ++++++ chrome/content/zotero/xpcom/zotero.js | 25 ++++++----- chrome/content/zotero/zoteroPane.js | 11 ++++- test/content/support.js | 5 --- 7 files changed, 87 insertions(+), 21 deletions(-) diff --git a/chrome/content/zotero/overlay.js b/chrome/content/zotero/overlay.js index 166e866d6..1f946da9d 100644 --- a/chrome/content/zotero/overlay.js +++ b/chrome/content/zotero/overlay.js @@ -59,7 +59,6 @@ var ZoteroOverlay = new function() } ZoteroPane_Overlay = ZoteroPane; - ZoteroPane.init(); // Open Zotero app tab, if in Fx 4 and requested by pref showInPref = Components.classes["@mozilla.org/preferences-service;1"] @@ -106,6 +105,8 @@ var ZoteroOverlay = new function() Zotero.logError(e); } + ZoteroPane.init(); + // TODO: Add only when progress window is open document.getElementById('appcontent').addEventListener('mousemove', Zotero.ProgressWindowSet.updateTimers, false); diff --git a/chrome/content/zotero/xpcom/data/dataObjects.js b/chrome/content/zotero/xpcom/data/dataObjects.js index 57f587156..2c33be246 100644 --- a/chrome/content/zotero/xpcom/data/dataObjects.js +++ b/chrome/content/zotero/xpcom/data/dataObjects.js @@ -416,19 +416,23 @@ Zotero.DataObjects.prototype._loadDataType = Zotero.Promise.coroutine(function* Zotero.DataObjects.prototype.loadAll = Zotero.Promise.coroutine(function* (libraryID, ids) { var t = new Date(); - var libraryName = Zotero.Libraries.get(libraryID).name; + var library = Zotero.Libraries.get(libraryID) Zotero.debug("Loading " + (ids ? ids.length : "all") + " " + (ids && ids.length == 1 ? this._ZDO_object : this._ZDO_objects) - + " in " + libraryName); + + " in " + library.name); + + library.setDataLoading(this._ZDO_object); let dataTypes = this.ObjectClass.prototype._dataTypes; for (let i = 0; i < dataTypes.length; i++) { yield this._loadDataType(dataTypes[i], libraryID, ids); } - Zotero.debug(`Loaded all data in ${libraryName} in ${new Date() - t} ms`); + Zotero.debug(`Loaded all ${this._ZDO_objects} in ${library.name} in ${new Date() - t} ms`); + + library.setDataLoaded(this._ZDO_object); }); diff --git a/chrome/content/zotero/xpcom/data/library.js b/chrome/content/zotero/xpcom/data/library.js index 1f2cf7f3b..cf5662a34 100644 --- a/chrome/content/zotero/xpcom/data/library.js +++ b/chrome/content/zotero/xpcom/data/library.js @@ -31,6 +31,9 @@ Zotero.Library = function(params = {}) { this._changed = {}; + this._dataLoaded = {}; + this._dataLoadedDeferreds = {}; + this._hasCollections = null; this._hasSearches = null; this._storageDownloadNeeded = false; @@ -344,6 +347,45 @@ Zotero.Library.prototype.loadAllDataTypes = Zotero.Promise.coroutine(function* ( yield Zotero.Items.loadAll(this.libraryID); }); +// +// Methods to handle promises that are resolved when object data is loaded for the library +// +Zotero.Library.prototype.getDataLoaded = function (objectType) { + return this._dataLoaded[objectType] || null; +}; + +Zotero.Library.prototype.setDataLoading = function (objectType) { + if (this._dataLoadedDeferreds[objectType]) { + throw new Error("Items already loading for library " + this.libraryID); + } + this._dataLoadedDeferreds[objectType] = Zotero.Promise.defer(); +}; + +Zotero.Library.prototype.getDataLoadedPromise = function (objectType) { + return this._dataLoadedDeferreds[objectType] + ? this._dataLoadedDeferreds[objectType].promise : null; +}; + +Zotero.Library.prototype.setDataLoaded = function (objectType) { + this._dataLoaded[objectType] = true; + this._dataLoadedDeferreds[objectType].resolve(); +}; + +Zotero.Library.prototype.waitForDataLoad = Zotero.Promise.coroutine(function* (objectType) { + if (this.getDataLoaded(objectType)) return; + + let promise = this.getDataLoadedPromise(objectType); + // If items are already being loaded, wait for them + if (promise) { + yield promise; + } + // Otherwise load them now + else { + let objectsClass = Zotero.DataObjectUtilities.getObjectsClassForObjectType(objectType); + yield objectsClass.loadAll(this.libraryID); + } +}); + Zotero.Library.prototype.isChildObjectAllowed = function(type) { return this._childObjectTypes.indexOf(type) != -1; }; diff --git a/chrome/content/zotero/xpcom/sync/syncRunner.js b/chrome/content/zotero/xpcom/sync/syncRunner.js index 17f2ceead..8434f6885 100644 --- a/chrome/content/zotero/xpcom/sync/syncRunner.js +++ b/chrome/content/zotero/xpcom/sync/syncRunner.js @@ -174,6 +174,15 @@ Zotero.Sync.Runner_Module = function (options = {}) { keyInfo, options.libraries ? Array.from(options.libraries) : [] ); + + // If items not yet loaded for libraries we need, load them now + for (let libraryID of librariesToSync) { + let library = Zotero.Libraries.get(libraryID); + if (!library.getDataLoaded('item')) { + yield library.waitForDataLoad('item'); + } + } + // Sync data and files, and then repeat if necessary let attempt = 1; let nextLibraries = librariesToSync.concat(); @@ -283,6 +292,9 @@ Zotero.Sync.Runner_Module = function (options = {}) { }); + /** + * @return {Promise - IDs of libraries to sync + */ this.checkLibraries = Zotero.Promise.coroutine(function* (client, options, keyInfo, libraries = []) { var access = keyInfo.access; diff --git a/chrome/content/zotero/xpcom/zotero.js b/chrome/content/zotero/xpcom/zotero.js index a65b20662..f3362f1f5 100644 --- a/chrome/content/zotero/xpcom/zotero.js +++ b/chrome/content/zotero/xpcom/zotero.js @@ -630,11 +630,15 @@ Components.utils.import("resource://gre/modules/osfile.jsm"); yield Zotero.Relations.init(); yield Zotero.Feeds.init(); - let libraryIDs = Zotero.Libraries.getAll().map(x => x.libraryID); - for (let libraryID of libraryIDs) { - let library = Zotero.Libraries.get(libraryID); - yield library.loadAllDataTypes(); - } + // Load all library data except for items + yield Zotero.Promise.each( + Zotero.Libraries.getAll(), + library => Zotero.Promise.coroutine(function* () { + yield Zotero.SyncedSettings.loadAll(library.libraryID); + yield Zotero.Collections.loadAll(library.libraryID); + yield Zotero.Searches.loadAll(library.libraryID); + })() + ); yield Zotero.QuickCopy.init(); @@ -2120,22 +2124,21 @@ Components.utils.import("resource://gre/modules/osfile.jsm"); /* * Clear entries that no longer exist from various tables */ - this.purgeDataObjects = Zotero.Promise.coroutine(function* (skipStoragePurge) { + this.purgeDataObjects = Zotero.Promise.coroutine(function* () { yield Zotero.DB.executeTransaction(function* () { return Zotero.Creators.purge(); }); yield Zotero.DB.executeTransaction(function* () { return Zotero.Tags.purge(); }); - // TEMP: Disabled until we have async DB (and maybe SQLite FTS) - //Zotero.Fulltext.purgeUnusedWords(); + Zotero.Fulltext.purgeUnusedWords(); yield Zotero.DB.executeTransaction(function* () { return Zotero.Items.purge(); }); // DEBUG: this might not need to be permanent - yield Zotero.DB.executeTransaction(function* () { - return Zotero.Relations.purge(); - }); + //yield Zotero.DB.executeTransaction(function* () { + // return Zotero.Relations.purge(); + //}); }); diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js index 82ba1f09a..cc7f66b43 100644 --- a/chrome/content/zotero/zoteroPane.js +++ b/chrome/content/zotero/zoteroPane.js @@ -1214,6 +1214,15 @@ var ZoteroPane = new function() ZoteroPane_Local.displayErrorMessage(); }; this.itemsView.addEventListener('load', this.setTagScope); + + // If item data not yet loaded for library, load it now. + // Other data types are loaded at startup + var library = Zotero.Libraries.get(collectionTreeRow.ref.libraryID); + if (!library.getDataLoaded('item')) { + Zotero.debug("Waiting for items to load for library " + library.libraryID); + yield library.waitForDataLoad('item'); + } + document.getElementById('zotero-items-tree').view = this.itemsView; // Add events to treecolpicker to update menu before showing/hiding @@ -1920,7 +1929,7 @@ var ZoteroPane = new function() ); if (result) { let deleted = yield Zotero.Items.emptyTrash(libraryID); - yield Zotero.purgeDataObjects(true); + yield Zotero.purgeDataObjects(); } }); diff --git a/test/content/support.js b/test/content/support.js index 1b76ae036..ee253ecc4 100644 --- a/test/content/support.js +++ b/test/content/support.js @@ -53,11 +53,6 @@ var loadZoteroPane = Zotero.Promise.coroutine(function* (win) { Zotero.Prefs.clear('lastViewedFolder'); win.ZoteroOverlay.toggleDisplay(true); - // Hack to wait for pane load to finish. This is the same hack - // we use in ZoteroPane.js, so either it's not good enough - // there or it should be good enough here. - yield Zotero.Promise.delay(52); - yield waitForItemsLoad(win, 0); return win;