From af5b9a9b46f8e105e23f466521476777a05b8cd8 Mon Sep 17 00:00:00 2001 From: Ed Sanders Date: Sun, 12 Jun 2022 14:54:26 +0100 Subject: [PATCH] ReplyWidgetVisual: Pass in memory-wrapped store to VE We switched to our memory-wrapped store everywhere outside the VE surface, but not in the VE surface itself. Change the way we construct MemoryStorage objects so that they can be constructed from a specific mw.SafeStorage instance. Ensure MemoryStorage's cache is populated with initial storage object data, and that the cache is copied over when we create derivative objectes. Bug: T310438 Change-Id: I652428f114dc5169195887cb8ca719ae196bb76f --- extension.json | 2 +- modules/.eslintrc.json | 1 + modules/MemoryStorage.js | 66 ------------------------ modules/controller.js | 4 +- modules/createMemoryStorage.js | 83 ++++++++++++++++++++++++++++++ modules/dt.ui.ReplyWidgetVisual.js | 8 ++- 6 files changed, 94 insertions(+), 70 deletions(-) delete mode 100644 modules/MemoryStorage.js create mode 100644 modules/createMemoryStorage.js diff --git a/extension.json b/extension.json index c556b56bc..478132851 100644 --- a/extension.json +++ b/extension.json @@ -72,7 +72,7 @@ "CommentItem.js", "HeadingItem.js", "CommentDetails.js", - "MemoryStorage.js", + "createMemoryStorage.js", "lib/moment-timezone/moment-timezone-with-data-1970-2030.js", { "name": "parser/data.json", diff --git a/modules/.eslintrc.json b/modules/.eslintrc.json index fc24b49d7..f5d7568bd 100644 --- a/modules/.eslintrc.json +++ b/modules/.eslintrc.json @@ -24,6 +24,7 @@ "CommentItem": "CommentItem", "DmMWPingNode": "DmMWPingNode", "HeadingItem": "HeadingItem", + "MemoryStorage": "MemoryStorage", "moment": "moment", "ThreadItemSet": "ThreadItemSet", "ThreadItem": "ThreadItem" diff --git a/modules/MemoryStorage.js b/modules/MemoryStorage.js deleted file mode 100644 index dbc864f2e..000000000 --- a/modules/MemoryStorage.js +++ /dev/null @@ -1,66 +0,0 @@ -/** - * MemoryStorage creates a wrapper around mw.SafeStorage objects, duplicating - * their contents in memory, so that even if the underlying storage mechanism - * fails (e.g. quota exceeded), the storage can be relied on before the - * page has been reloaded. - * - * @example - * var sessionStorage = new MemoryStorage( mw.storage.session.store ); - * var localStorage = new MemoryStorage( mw.storage.store ); - * - * @class - * @extends mw.SafeStorage - * @param {Object} store - */ -function MemoryStorage() { - this.data = {}; - - // Parent constructor - MemoryStorage.super.apply( this, arguments ); -} - -// HACK: SafeStorage is not exposed as a public API, but we can -// access it as the constructor of mw.storage. -var SafeStorage = mw.storage.constructor; - -/* Inheritance */ - -OO.inheritClass( MemoryStorage, SafeStorage ); - -/* Methods */ - -/** - * @inheritdoc - */ -MemoryStorage.prototype.get = function ( key ) { - if ( Object.prototype.hasOwnProperty.call( this.data, key ) ) { - return this.data[ key ]; - } else { - // Parent method - return MemoryStorage.super.prototype.get.apply( this, arguments ); - } -}; - -/** - * @inheritdoc - */ -MemoryStorage.prototype.set = function ( key, value ) { - // Parent method - MemoryStorage.super.prototype.set.apply( this, arguments ); - - this.data[ key ] = value; - return true; -}; - -/** - * @inheritdoc - */ -MemoryStorage.prototype.remove = function ( key ) { - // Parent method - MemoryStorage.super.prototype.remove.apply( this, arguments ); - - delete this.data[ key ]; - return true; -}; - -module.exports = MemoryStorage; diff --git a/modules/controller.js b/modules/controller.js index bf0993bde..a94416f43 100644 --- a/modules/controller.js +++ b/modules/controller.js @@ -5,8 +5,8 @@ var pageThreads, lastControllerScrollOffset, featuresEnabled = mw.config.get( 'wgDiscussionToolsFeaturesEnabled' ) || {}, - MemoryStorage = require( './MemoryStorage.js' ), - storage = new MemoryStorage( mw.storage.session.store ), + createMemoryStorage = require( './createMemoryStorage.js' ), + storage = createMemoryStorage( mw.storage.session ), Parser = require( './Parser.js' ), ThreadItemSet = require( './ThreadItemSet.js' ), CommentDetails = require( './CommentDetails.js' ), diff --git a/modules/createMemoryStorage.js b/modules/createMemoryStorage.js new file mode 100644 index 000000000..bb5874dee --- /dev/null +++ b/modules/createMemoryStorage.js @@ -0,0 +1,83 @@ +/** + * createMemoryStorage creates a wrapper around mw.SafeStorage objects, duplicating + * their contents in memory, so that even if the underlying storage mechanism + * fails (e.g. quota exceeded), the storage can be relied on before the + * page has been reloaded. + * + * @example + * var sessionStorage = createMemoryStorage( mw.storage.session ); + * var localStorage = createMemoryStorage( mw.storage ); + * + * @param {mw.SafeStorage} storage + * @return {MemoryStorage} + */ +var createMemoryStorage = function ( storage ) { + + /** + * @class + * @extends mw.SafeStorage + * + * @constructor + * @param {Storage|undefined} store The Storage instance to wrap around + */ + function MemoryStorage( store ) { + this.data = {}; + + // Attempt to populate memory cache with existing data. + // Ignore any errors accessing native Storage object, as in mw.SafeStorage. + try { + for ( var i = 0, l = store.length; i < l; i++ ) { + var key = store.key( i ); + this.data[ key ] = store.getItem( key ); + } + } catch ( e ) {} + + // Parent constructor + MemoryStorage.super.apply( this, arguments ); + } + + /* Inheritance */ + + var ParentStorage = storage.constructor; + OO.inheritClass( MemoryStorage, ParentStorage ); + + /* Methods */ + + /** + * @inheritdoc + */ + MemoryStorage.prototype.get = function ( key ) { + if ( Object.prototype.hasOwnProperty.call( this.data, key ) ) { + return this.data[ key ]; + } else { + // Parent method + return MemoryStorage.super.prototype.get.apply( this, arguments ); + } + }; + + /** + * @inheritdoc + */ + MemoryStorage.prototype.set = function ( key, value ) { + // Parent method + MemoryStorage.super.prototype.set.apply( this, arguments ); + + this.data[ key ] = value; + return true; + }; + + /** + * @inheritdoc + */ + MemoryStorage.prototype.remove = function ( key ) { + // Parent method + MemoryStorage.super.prototype.remove.apply( this, arguments ); + + delete this.data[ key ]; + return true; + }; + + return new MemoryStorage( storage.store ); +}; + +module.exports = createMemoryStorage; diff --git a/modules/dt.ui.ReplyWidgetVisual.js b/modules/dt.ui.ReplyWidgetVisual.js index ca78052d7..33ba03036 100644 --- a/modules/dt.ui.ReplyWidgetVisual.js +++ b/modules/dt.ui.ReplyWidgetVisual.js @@ -115,9 +115,15 @@ ReplyWidgetVisual.prototype.setup = function ( data, suppressNotifications ) { focus: [ 'emit', 'bodyFocus' ] } ); + var listStorage = ve.init.platform.createListStorage( widget.storage ); + // widget.storage is a MemoryStorage object. Copy over the .data cache so + // that listStorage reads from/writes to the same in-memory cache. + listStorage.data = widget.storage.data; + target.initAutosave( { suppressNotifications: suppressNotifications, - docId: widget.storagePrefix + docId: widget.storagePrefix, + storage: listStorage } ); widget.afterSetup();