From 03b7ae0a305928550fed673df98860b19ad56d42 Mon Sep 17 00:00:00 2001 From: JC Brand Date: Tue, 17 Dec 2019 13:39:32 +0100 Subject: [PATCH] Improvements to how things are stored. - Use the bare JID as indexedDB dataStore - Collapse localStorage and indexedDB stores into one `persistent` store. - When testing, only clear test data --- dev.html | 1 + spec/login.js | 6 +-- src/converse-controlbox.js | 4 +- src/converse-minimize.js | 19 ++++----- src/converse-muc-views.js | 2 +- src/headless/converse-bookmarks.js | 3 +- src/headless/converse-core.js | 68 ++++++++++++++---------------- src/headless/converse-disco.js | 3 +- src/headless/converse-roster.js | 7 ++- tests/mock.js | 30 +++++++++++-- 10 files changed, 81 insertions(+), 62 deletions(-) diff --git a/dev.html b/dev.html index d5d0e59bd..94511dedc 100644 --- a/dev.html +++ b/dev.html @@ -41,6 +41,7 @@ 'discuss@conference.conversejs.org' ], enable_smacks: true, + persistent_store: 'IndexedDB', muc_respect_autojoin: true, message_archiving: 'always', loglevel: 'debug' diff --git a/spec/login.js b/spec/login.js index ca8350700..7cc47fd31 100644 --- a/spec/login.js +++ b/spec/login.js @@ -29,9 +29,9 @@ spyOn(cbview.loginpanel, 'connect'); cbview.delegateEvents(); - expect(_converse.config.get('storage')).toBe('local'); + expect(_converse.config.get('storage')).toBe('persistent'); cbview.el.querySelector('input[type="submit"]').click(); - expect(_converse.config.get('storage')).toBe('local'); + expect(_converse.config.get('storage')).toBe('persistent'); expect(cbview.loginpanel.connect).toHaveBeenCalled(); checkbox.click(); @@ -72,7 +72,7 @@ checkbox.click(); cbview.el.querySelector('input[type="submit"]').click(); - expect(_converse.config.get('storage')).toBe('local'); + expect(_converse.config.get('storage')).toBe('persistent'); done(); }); })); diff --git a/src/converse-controlbox.js b/src/converse-controlbox.js index 990d3028a..b63c11516 100644 --- a/src/converse-controlbox.js +++ b/src/converse-controlbox.js @@ -442,12 +442,12 @@ converse.plugins.add('converse-controlbox', { if (_converse.trusted === 'on' || _converse.trusted === 'off') { _converse.config.save({ 'trusted': _converse.trusted === 'on', - 'storage': _converse.trusted === 'on' ? 'local' : 'session' + 'storage': _converse.trusted === 'on' ? 'persistent' : 'session' }); } else { _converse.config.save({ 'trusted': form_data.get('trusted') && true || false, - 'storage': form_data.get('trusted') ? 'local' : 'session' + 'storage': form_data.get('trusted') ? 'persistent' : 'session' }); } diff --git a/src/converse-minimize.js b/src/converse-minimize.js index 215ec4a18..28345798b 100644 --- a/src/converse-minimize.js +++ b/src/converse-minimize.js @@ -471,12 +471,11 @@ converse.plugins.add('converse-minimize', { }, initToggle () { - const storage = _converse.config.get('storage'), - id = `converse.minchatstoggle-${_converse.bare_jid}`; + const id = `converse.minchatstoggle-${_converse.bare_jid}`; this.toggleview = new _converse.MinimizedChatsToggleView({ 'model': new _converse.MinimizedChatsToggle({'id': id}) }); - this.toggleview.model.browserStorage = _converse.createStore(id, storage); + this.toggleview.model.browserStorage = _converse.createStore(id); this.toggleview.model.fetch(); }, @@ -568,22 +567,22 @@ converse.plugins.add('converse-minimize', { } }); - /************************ BEGIN Event Handlers ************************/ - _converse.api.waitUntil('chatBoxViewsInitialized').then(() => { - _converse.minimized_chats = new _converse.MinimizedChats({ - model: _converse.chatboxes - }); + function initMinimizedChats () { + _converse.minimized_chats = new _converse.MinimizedChats({model: _converse.chatboxes}); /** * Triggered once the _converse.MinimizedChats instance has been * initialized * @event _converse#minimizedChatsInitialized * @example _converse.api.listen.on('minimizedChatsInitialized', () => { ... }); */ _converse.api.trigger('minimizedChatsInitialized'); - }).catch(e => log.fatal(e)); + } - const debouncedTrimChats = _.debounce(() => _converse.chatboxviews.trimChats(), 250); + /************************ BEGIN Event Handlers ************************/ + _converse.api.listen.on('userSessionInitialized', () => initMinimizedChats()); _converse.api.listen.on('chatBoxInsertedIntoDOM', view => _converse.chatboxviews.trimChats(view)); _converse.api.listen.on('controlBoxOpened', view => _converse.chatboxviews.trimChats(view)); + + const debouncedTrimChats = _.debounce(() => _converse.chatboxviews.trimChats(), 250); _converse.api.listen.on('registeredGlobalEventHandlers', () => window.addEventListener("resize", debouncedTrimChats)); _converse.api.listen.on('unregisteredGlobalEventHandlers', () => window.removeEventListener("resize", debouncedTrimChats)); /************************ END Event Handlers ************************/ diff --git a/src/converse-muc-views.js b/src/converse-muc-views.js index af000fd3b..3304909ba 100644 --- a/src/converse-muc-views.js +++ b/src/converse-muc-views.js @@ -131,7 +131,7 @@ converse.plugins.add('converse-muc-views', { this.roomspanel = new _converse.RoomsPanel({ 'model': new (_converse.RoomsPanelModel.extend({ id, - 'browserStorage': _converse.createStore(id, _converse.config.get('storage')) + 'browserStorage': _converse.createStore(id) }))() }); this.roomspanel.model.fetch(); diff --git a/src/headless/converse-bookmarks.js b/src/headless/converse-bookmarks.js index ba0677550..9d33ec73a 100644 --- a/src/headless/converse-bookmarks.js +++ b/src/headless/converse-bookmarks.js @@ -112,10 +112,9 @@ converse.plugins.add('converse-bookmarks', { this.on('remove', this.markRoomAsUnbookmarked, this); this.on('remove', this.sendBookmarkStanza, this); - const storage = _converse.config.get('storage'); const cache_key = `converse.room-bookmarks${_converse.bare_jid}`; this.fetched_flag = cache_key+'fetched'; - this.browserStorage = _converse.createStore(cache_key, storage); + this.browserStorage = _converse.createStore(cache_key); }, async openBookmarkedRoom (bookmark) { diff --git a/src/headless/converse-core.js b/src/headless/converse-core.js index 89131d14f..574f35d11 100644 --- a/src/headless/converse-core.js +++ b/src/headless/converse-core.js @@ -359,33 +359,34 @@ _converse.isUniView = function () { }; -async function initStorage () { +async function initSessionStorage () { await BrowserStorage.sessionStorageInitialized; - - // Sets up Backbone.BrowserStorage and localForage for the 3 different stores. - _converse.localStorage = BrowserStorage.localForage.createInstance({ - 'name': 'local', - 'description': 'localStorage instance', - 'driver': [BrowserStorage.localForage.LOCALSTORAGE] - }); - - _converse.indexedDB = BrowserStorage.localForage.createInstance({ - 'name': 'indexed', - 'description': 'indexedDB instance', - 'driver': [BrowserStorage.localForage.INDEXEDDB] - }); - - _converse.sessionStorage = BrowserStorage.localForage.createInstance({ - 'name': 'session', - 'description': 'sessionStorage instance', - 'driver': ['sessionStorageWrapper'] - }); - _converse.storage = { - 'session': _converse.sessionStorage, - 'local': _converse.localStorage, - 'indexed': _converse.indexedDB + 'session': BrowserStorage.localForage.createInstance({ + 'name': _converse.isTestEnv() ? 'converse-test-session' : 'converse-session', + 'description': 'sessionStorage instance', + 'driver': ['sessionStorageWrapper'] + }) + }; +} + + +function initPersistentStorage () { + if (_converse.config.get('storage') !== 'persistent') { + return; } + const config = { + 'name': _converse.isTestEnv() ? 'converse-test-persistent' : 'converse-persistent', + 'storeName': _converse.bare_jid + } + if (_converse.persistent_store === 'localStorage') { + config['description'] = 'localStorage instance'; + config['driver'] = [BrowserStorage.localForage.LOCALSTORAGE]; + } else if (_converse.persistent_store === 'IndexedDB') { + config['description'] = 'indexedDB instance'; + config['driver'] = [BrowserStorage.localForage.INDEXEDDB]; + } + _converse.storage['persistent'] = BrowserStorage.localForage.createInstance(config); } @@ -447,11 +448,10 @@ function initClientConfig () { * user sessions. */ const id = 'converse.client-config'; - const store_map = { 'localStorage': 'local', 'IndexedDB': 'indexed' }; _converse.config = new Backbone.Model({ 'id': id, 'trusted': _converse.trusted && true || false, - 'storage': _converse.trusted ? store_map[_converse.persistent_store] : 'session' + 'storage': _converse.trusted ? 'persistent' : 'session' }); _converse.config.browserStorage = _converse.createStore(id, "session"); _converse.config.fetch(); @@ -684,6 +684,7 @@ async function initSession (jid) { _converse.session.save({id}); } saveJIDtoSession(jid); + initPersistentStorage(); /** * Triggered once the user's session has been initialized. The session is a * cache which stores information about the user's current session. @@ -831,7 +832,7 @@ function setUpXMLLogging () { async function finishInitialization () { - await initStorage(); + await initSessionStorage(); initClientConfig(); initPlugins(); registerGlobalEventHandlers(); @@ -976,15 +977,10 @@ function unregisterGlobalEventHandlers () { _converse.api.trigger('unregisteredGlobalEventHandlers'); } -async function cleanup () { + +function cleanup () { // Make sure everything is reset in case this is a subsequent call to // convesre.initialize (happens during tests). - if (_converse.localStorage) { - await Promise.all([ - BrowserStorage.localForage.dropInstance({'name': 'local'}), - BrowserStorage.localForage.dropInstance({'name': 'indexed'}), - BrowserStorage.localForage.dropInstance({'name': 'session'})]); - } Backbone.history.stop(); unregisterGlobalEventHandlers(); delete _converse.controlboxtoggle; @@ -1000,7 +996,7 @@ async function cleanup () { _converse.initialize = async function (settings, callback) { - await cleanup(); + cleanup(); settings = settings !== undefined ? settings : {}; PROMISES.forEach(addPromise); @@ -1802,7 +1798,7 @@ Object.assign(window.converse, { * @property {function} converse.env.sizzle - [Sizzle](https://sizzlejs.com) CSS selector engine. * @property {object} converse.env.utils - Module containing common utility methods used by Converse. */ - 'env': { $build, $iq, $msg, $pres, Backbone, Promise, Strophe, _, dayjs, log, sizzle, stanza_utils, u, 'utils': u } + 'env': { $build, $iq, $msg, $pres, Backbone, BrowserStorage, Promise, Strophe, _, dayjs, log, sizzle, stanza_utils, u, 'utils': u } }); /** diff --git a/src/headless/converse-disco.js b/src/headless/converse-disco.js index 8057acee7..c5cb8f7f6 100644 --- a/src/headless/converse-disco.js +++ b/src/headless/converse-disco.js @@ -236,7 +236,8 @@ converse.plugins.add('converse-disco', { this.fetch({ add: true, success: resolve, - error () { + error (m, e) { + log.error(e); reject (new Error("Could not fetch disco entities")); } }); diff --git a/src/headless/converse-roster.js b/src/headless/converse-roster.js index d3dca0562..87f061b3b 100644 --- a/src/headless/converse-roster.js +++ b/src/headless/converse-roster.js @@ -970,20 +970,19 @@ converse.plugins.add('converse-roster', { // Initialize the Bakcbone collections that represent the contats // roster and the roster groups. await _converse.api.waitUntil('VCardsInitialized'); - const storage = _converse.config.get('storage'); _converse.roster = new _converse.RosterContacts(); let id = `converse.contacts-${_converse.bare_jid}`; - _converse.roster.browserStorage = _converse.createStore(id, storage); + _converse.roster.browserStorage = _converse.createStore(id); _converse.roster.data = new Backbone.Model(); id = `converse-roster-model-${_converse.bare_jid}`; _converse.roster.data.id = id; - _converse.roster.data.browserStorage = _converse.createStore(id, storage); + _converse.roster.data.browserStorage = _converse.createStore(id); _converse.roster.data.fetch(); id = `converse.roster.groups${_converse.bare_jid}`; _converse.rostergroups = new _converse.RosterGroups(); - _converse.rostergroups.browserStorage = _converse.createStore(id, storage); + _converse.rostergroups.browserStorage = _converse.createStore(id); /** * Triggered once the `_converse.RosterContacts` and `_converse.RosterGroups` have * been created, but not yet populated with data. diff --git a/tests/mock.js b/tests/mock.js index 47328961a..b9bd1af56 100644 --- a/tests/mock.js +++ b/tests/mock.js @@ -5,6 +5,7 @@ converse.load(); const _ = converse.env._; + const u = converse.env.utils; const Promise = converse.env.Promise; const Strophe = converse.env.Strophe; const dayjs = converse.env.dayjs; @@ -220,9 +221,30 @@ Strophe.Connection = MockConnection; + function clearIndexedDB () { + const promise = u.getResolveablePromise(); + const DBOpenRequest = window.indexedDB.open("converse-test-persistent"); + DBOpenRequest.onsuccess = function () { + const db = DBOpenRequest.result; + const bare_jid = "romeo@montague.lit"; + const objectStore = db.transaction([bare_jid], "readwrite").objectStore(bare_jid); + const objectStoreRequest = objectStore.clear(); + objectStoreRequest.onsuccess = promise.resolve(); + objectStoreRequest.onerror = promise.resolve(); + }; + return promise; + } + + function clearStores () { + [localStorage, sessionStorage].forEach( + s => Object.keys(s).forEach(k => k.match(/^converse-test-/) && s.removeItem(k)) + ); + } + + async function initConverse (settings) { - window.localStorage.clear(); - window.sessionStorage.clear(); + clearStores(); + await clearIndexedDB(); const _converse = await converse.initialize(Object.assign({ 'animate': false, @@ -230,6 +252,7 @@ 'bosh_service_url': 'montague.lit/http-bind', 'enable_smacks': false, 'i18n': 'en', + // 'persistent_store': 'IndexedDB', 'loglevel': 'warn', 'no_trimming': true, 'play_sounds': false, @@ -282,6 +305,7 @@ promise_names = [] settings = null; } + return async done => { const _converse = await initConverse(settings); async function _done () { @@ -301,7 +325,7 @@ } catch(e) { console.error(e); fail(e); - _done(); + await _done(); } } };