jenkins-bot has submitted this change and it was merged. Change subject: Convert 'seenTime' to a global property ......................................................................
Convert 'seenTime' to a global property This transforms seenTime concept to a global property for all wikis and sources, and updates the global seen time on opening the popup. Bug: T134855 Change-Id: I67bcc4b346237317c7a9204dd43cd0e9ee02792f --- M includes/SeenTime.php M modules/api/mw.echo.api.EchoApi.js M modules/controller/mw.echo.Controller.js M modules/model/mw.echo.dm.BundleNotificationItem.js M modules/model/mw.echo.dm.CrossWikiNotificationItem.js M modules/model/mw.echo.dm.ModelManager.js M modules/model/mw.echo.dm.NotificationItem.js M modules/model/mw.echo.dm.NotificationsList.js M modules/model/mw.echo.dm.SeenTimeModel.js M modules/ui/mw.echo.ui.CrossWikiNotificationItemWidget.js M modules/ui/mw.echo.ui.NotificationBadgeWidget.js M modules/ui/mw.echo.ui.NotificationsInboxWidget.js 12 files changed, 115 insertions(+), 103 deletions(-) Approvals: Catrope: Looks good to me, approved jenkins-bot: Verified diff --git a/includes/SeenTime.php b/includes/SeenTime.php index 4a33720..687613c 100644 --- a/includes/SeenTime.php +++ b/includes/SeenTime.php @@ -41,12 +41,12 @@ private static function cache() { static $c = null; - // Use db-replicated for persistent storage, and + // Use main stash for persistent storage, and // wrap it with CachedBagOStuff for an in-process // cache. (T144534) if ( $c === null ) { $c = new CachedBagOStuff( - ObjectCache::getInstance( 'db-replicated' ) + ObjectCache::getMainStashInstance() ); } @@ -75,9 +75,8 @@ return false; } - $key = wfMemcKey( 'echo', 'seen', $type, 'time', $this->user->getId() ); $cas = 0; // Unused, but we have to pass something by reference - $data = self::cache()->get( $key, $cas, $flags ); + $data = self::cache()->get( $this->getMemcKey( $type ), $cas, $flags ); if ( $data === false ) { // Check if the user still has it set in their preferences @@ -108,9 +107,7 @@ } } else { if ( $this->validateType( $type ) ) { - $key = wfMemcKey( 'echo', 'seen', $type, 'time', $this->user->getId() ); - - return self::cache()->set( $key, $time ); + return self::cache()->set( $this->getMemcKey( $type ), $time ); } } } @@ -124,4 +121,28 @@ private function validateType( $type ) { return in_array( $type, self::$allowedTypes ); } + + /** + * Build a memcached key. + * + * @param string $type Given notification type + * @return string Memcached key + */ + protected function getMemcKey( $type = 'all' ) { + $localKey = wfMemcKey( 'echo', 'seen', $type, 'time', $this->user->getId() ); + + if ( !$this->user->getOption( 'echo-cross-wiki-notifications' ) ) { + return $localKey; + } + + $lookup = CentralIdLookup::factory(); + $globalId = $lookup->centralIdFromLocalUser( $this->user, CentralIdLookup::AUDIENCE_RAW ); + + if ( !$globalId ) { + return $localKey; + } + + return wfGlobalCacheKey( 'echo', 'seen', $type, 'time', $globalId ); + + } } diff --git a/modules/api/mw.echo.api.EchoApi.js b/modules/api/mw.echo.api.EchoApi.js index 3ea96b7..ebf4d2a 100644 --- a/modules/api/mw.echo.api.EchoApi.js +++ b/modules/api/mw.echo.api.EchoApi.js @@ -271,7 +271,10 @@ }; /** - * Update the seenTime property for the given type and source. + * Update the seenTime property for the given type. + * We only need to update this in a single source for the seenTime + * to be updated globally - but we will let the consumer of + * this method override the choice of which source to update. * * @param {string} [type='alert,message'] Notification type * @param {string} [source='local'] Notification source @@ -280,6 +283,7 @@ mw.echo.api.EchoApi.prototype.updateSeenTime = function ( type, source ) { source = source || 'local'; type = type || [ 'alert', 'message' ]; + return this.network.getApiHandler( source ).updateSeenTime( type ); }; diff --git a/modules/controller/mw.echo.Controller.js b/modules/controller/mw.echo.Controller.js index 59cb771..0945e64 100644 --- a/modules/controller/mw.echo.Controller.js +++ b/modules/controller/mw.echo.Controller.js @@ -158,8 +158,7 @@ // anyways. maxSeenTime = data.seenTime.alert < data.seenTime.notice ? data.seenTime.notice : data.seenTime.alert; - controller.manager.getSeenTimeModel().setSeenTimeForSource( - currentSource, + controller.manager.getSeenTimeModel().setSeenTime( maxSeenTime ); @@ -287,8 +286,7 @@ content = notifData[ '*' ] || {}; // Set source's seenTime - controller.manager.getSeenTimeModel().setSeenTimeForSource( - 'local', + controller.manager.getSeenTimeModel().setSeenTime( controller.getTypes().length > 1 ? ( data.seenTime.alert < data.seenTime.notice ? @@ -368,7 +366,6 @@ */ mw.echo.Controller.prototype.createNotificationData = function ( apiData ) { var utcTimestamp, utcIsoMoment, - source = this.manager.getFiltersModel().getSourcePagesModel().getCurrentSource(), content = apiData[ '*' ] || {}; if ( apiData.timestamp.utciso8601 ) { @@ -388,7 +385,7 @@ read: !!apiData.read, seen: ( !!apiData.read || - utcTimestamp <= this.manager.getSeenTime( source ) + utcTimestamp <= this.manager.getSeenTime() ), timestamp: utcTimestamp, category: apiData.category, @@ -737,62 +734,23 @@ }; /** - * Update seenTime for the given source + * Update global seenTime for all sources * * @return {jQuery.Promise} A promise that is resolved when the - * seenTime was updated for all the controller's types. + * seenTime was updated for all the controller's types and sources. */ - mw.echo.Controller.prototype.updateSeenTime = function ( source ) { + mw.echo.Controller.prototype.updateSeenTime = function () { var controller = this; - return this.api.updateSeenTime( this.getTypes(), source ) + return this.api.updateSeenTime( + this.getTypes(), + // For consistency, use current source, though seenTime + // will be updated globally + this.manager.getFiltersModel().getSourcePagesModel().getCurrentSource() + ) .then( function ( time ) { - controller.manager.getSeenTimeModel().setSeenTimeForSource( source, time ); + controller.manager.getSeenTimeModel().setSeenTime( time ); } ); - }; - - /** - * Update local seen time - * - * @return {jQuery.Promise} A promise that is resolved when the - * seenTime was updated for all given types. - */ - mw.echo.Controller.prototype.updateLocalSeenTime = function () { - return this.updateSeenTime( 'local' ); - }; - - /** - * Update seen time for all sources within a cross-wiki bundle. - * - * @return {jQuery.Promise} A promise that is resolved when the - * seenTime was updated for all available cross-wiki sources. - */ - mw.echo.Controller.prototype.updateSeenTimeForCrossWiki = function () { - var model = this.manager.getNotificationModel( 'xwiki' ), - controller = this, - promises = []; - - if ( !model ) { - // There is no xwiki notifications model - return $.Deferred().reject().promise(); - } - - model.getSourceNames().forEach( function ( source ) { - promises.push( controller.updateSeenTime( source ) ); - } ); - - return mw.echo.api.NetworkHandler.static.waitForAllPromises( promises ); - }; - /** - * Update seenTime for the currently selected source - * - * @return {jQuery.Promise} A promise that is resolved when the - * seenTime was updated for all given types. - */ - mw.echo.Controller.prototype.updateSeenTimeForCurrentSource = function () { - var currSource = this.manager.getFiltersModel().getSourcePagesModel().getCurrentSource(); - - return this.updateSeenTime( currSource ); }; /** diff --git a/modules/model/mw.echo.dm.BundleNotificationItem.js b/modules/model/mw.echo.dm.BundleNotificationItem.js index 62f7363..9a28f42 100644 --- a/modules/model/mw.echo.dm.BundleNotificationItem.js +++ b/modules/model/mw.echo.dm.BundleNotificationItem.js @@ -97,6 +97,19 @@ }; /** + * Set all notifications to seen + * + * @param {number} timestamp New seen timestamp + */ + mw.echo.dm.BundleNotificationItem.prototype.updateSeenState = function ( timestamp ) { + this.list.getItems().forEach( function ( notification ) { + notification.toggleSeen( + notification.isRead() || notification.getTimestamp() < timestamp + ); + } ); + }; + + /** * This item is a group. * This method is required for all models that are managed by the * mw.echo.dm.ModelManager. diff --git a/modules/model/mw.echo.dm.CrossWikiNotificationItem.js b/modules/model/mw.echo.dm.CrossWikiNotificationItem.js index e7f64d0..eddf484 100644 --- a/modules/model/mw.echo.dm.CrossWikiNotificationItem.js +++ b/modules/model/mw.echo.dm.CrossWikiNotificationItem.js @@ -118,6 +118,21 @@ }; /** + * Set all notifications in all groups to seen + * + * @param {number} timestamp New seen timestamp + */ + mw.echo.dm.CrossWikiNotificationItem.prototype.updateSeenState = function ( timestamp ) { + this.getList().getItems().forEach( function ( source ) { + source.getItems().forEach( function ( notification ) { + notification.toggleSeen( + notification.isRead() || notification.getTimestamp() < timestamp + ); + } ); + } ); + }; + + /** * Get all items in the cross wiki notification bundle * * @return {mw.echo.dm.NotificationItem[]} All items across all sources diff --git a/modules/model/mw.echo.dm.ModelManager.js b/modules/model/mw.echo.dm.ModelManager.js index 706413d..2cdb76a 100644 --- a/modules/model/mw.echo.dm.ModelManager.js +++ b/modules/model/mw.echo.dm.ModelManager.js @@ -107,16 +107,18 @@ /** * Respond to seen time change for a given source * - * @param {string} source Source where seen time has changed * @param {string} timestamp Seen time, as a full UTC ISO 8601 timestamp + * @fires seen */ - mw.echo.dm.ModelManager.prototype.onSeenTimeUpdate = function ( source, timestamp ) { - var notifs = this.getNotificationsBySource( source ); - notifs.forEach( function ( notification ) { - notification.toggleSeen( notification.isRead() || notification.getTimestamp() < timestamp ); - } ); + mw.echo.dm.ModelManager.prototype.onSeenTimeUpdate = function ( timestamp ) { + var modelId, + models = this.getAllNotificationModels(); - this.emit( 'seen', source, timestamp ); + for ( modelId in models ) { + models[ modelId ].updateSeenState( timestamp ); + } + + this.emit( 'seen', timestamp ); }; /** diff --git a/modules/model/mw.echo.dm.NotificationItem.js b/modules/model/mw.echo.dm.NotificationItem.js index 02609cc..fdd4d30 100644 --- a/modules/model/mw.echo.dm.NotificationItem.js +++ b/modules/model/mw.echo.dm.NotificationItem.js @@ -199,7 +199,12 @@ */ mw.echo.dm.NotificationItem.prototype.toggleSeen = function ( seen ) { seen = seen !== undefined ? seen : !this.seen; - if ( this.seen !== seen ) { + if ( + this.seen !== seen && + // Do not change the state of a read item, since its + // seen state (never 'unseen') never changes + !this.isRead() + ) { this.seen = seen; this.emit( 'update' ); } diff --git a/modules/model/mw.echo.dm.NotificationsList.js b/modules/model/mw.echo.dm.NotificationsList.js index 32e787c..67334f6 100644 --- a/modules/model/mw.echo.dm.NotificationsList.js +++ b/modules/model/mw.echo.dm.NotificationsList.js @@ -254,6 +254,19 @@ }; /** + * Set all notifications to seen + * + * @param {string} timestamp New seen timestamp + */ + mw.echo.dm.NotificationsList.prototype.updateSeenState = function ( timestamp ) { + this.getItems().forEach( function ( notification ) { + notification.toggleSeen( + notification.isRead() || notification.getTimestamp() < timestamp + ); + } ); + }; + + /** * @inheritdoc */ mw.echo.dm.NotificationsList.prototype.isGroup = function () { diff --git a/modules/model/mw.echo.dm.SeenTimeModel.js b/modules/model/mw.echo.dm.SeenTimeModel.js index f61a5bb..073adf4 100644 --- a/modules/model/mw.echo.dm.SeenTimeModel.js +++ b/modules/model/mw.echo.dm.SeenTimeModel.js @@ -7,8 +7,6 @@ * that this model handles */ mw.echo.dm.SeenTimeModel = function MwEchoSeenTimeModel( config ) { - var originalSeenTime; - config = config || {}; // Mixin constructor @@ -19,14 +17,7 @@ this.types = Array.isArray( config.types ) ? config.types : [ config.types ]; } - originalSeenTime = mw.config.get( 'wgEchoSeenTime' ) || {}; - - this.seenTime = { - local: { - alert: originalSeenTime.alert, - message: originalSeenTime.notice - } - }; + this.seenTime = mw.config.get( 'wgEchoSeenTime' ) || {}; }; /* Initialization */ @@ -38,7 +29,6 @@ /** * @event update - * @param {string} source The source that updated its seenTime * @param {string} time Seen time, as a full UTC ISO 8601 timestamp. * * Seen time has been updated for the given source @@ -47,42 +37,34 @@ /* Methods */ /** - * Get the seenTime value for the source + * Get the global seenTime value * - * @param {string} source Source name * @return {string} Seen time, as a full UTC ISO 8601 timestamp. */ - mw.echo.dm.SeenTimeModel.prototype.getSeenTime = function ( source ) { - source = source || 'local'; - - return ( this.seenTime[ source ] && this.seenTime[ source ][ this.getTypes()[ 0 ] ] ) || 0; + mw.echo.dm.SeenTimeModel.prototype.getSeenTime = function () { + return this.seenTime[ this.getTypes()[ 0 ] ] || 0; }; /** * Set the seen time value for the source * * @private - * @param {string} [source='local'] Given source * @param {string} time Seen time, as a full UTC ISO 8601 timestamp. * @fires update */ - mw.echo.dm.SeenTimeModel.prototype.setSeenTimeForSource = function ( source, time ) { + mw.echo.dm.SeenTimeModel.prototype.setSeenTime = function ( time ) { var model = this, hasChanged = false; - source = source || 'local'; - - this.seenTime[ source ] = this.seenTime[ source ] || {}; - this.getTypes().forEach( function ( type ) { - if ( model.seenTime[ source ][ type ] !== time ) { - model.seenTime[ source ][ type ] = time; + if ( model.seenTime[ type ] !== time ) { + model.seenTime[ type ] = time; hasChanged = true; } } ); if ( hasChanged ) { - this.emit( 'update', source, time ); + this.emit( 'update', time ); } }; diff --git a/modules/ui/mw.echo.ui.CrossWikiNotificationItemWidget.js b/modules/ui/mw.echo.ui.CrossWikiNotificationItemWidget.js index 42a95f6..a596b44 100644 --- a/modules/ui/mw.echo.ui.CrossWikiNotificationItemWidget.js +++ b/modules/ui/mw.echo.ui.CrossWikiNotificationItemWidget.js @@ -299,7 +299,6 @@ } } ) - .then( this.controller.updateSeenTimeForCrossWiki.bind( this.controller ) ) .always( this.popPending.bind( this ) ); // Only run the fetch notifications action once diff --git a/modules/ui/mw.echo.ui.NotificationBadgeWidget.js b/modules/ui/mw.echo.ui.NotificationBadgeWidget.js index 8d833b2..d7dcbdb 100644 --- a/modules/ui/mw.echo.ui.NotificationBadgeWidget.js +++ b/modules/ui/mw.echo.ui.NotificationBadgeWidget.js @@ -235,16 +235,16 @@ * Respond to SeenTime model update event */ mw.echo.ui.NotificationBadgeWidget.prototype.onSeenTimeModelUpdate = function () { - this.updateBadgeSeenState( this.manager.hasUnseenInSource( 'local' ) ); + this.updateBadgeSeenState( false ); }; /** * Update the badge style to match whether it contains unseen notifications. * - * @param {boolean} hasUnseen There are unseen notifications + * @param {boolean} [hasUnseen=false] There are unseen notifications */ mw.echo.ui.NotificationBadgeWidget.prototype.updateBadgeSeenState = function ( hasUnseen ) { - hasUnseen = hasUnseen === undefined ? this.manager.hasUnseenInSource( 'local' ) : !!hasUnseen; + hasUnseen = hasUnseen === undefined ? false : !!hasUnseen; this.badgeButton.setFlags( { unseen: !!hasUnseen } ); }; @@ -336,7 +336,7 @@ if ( widget.popup.isVisible() ) { widget.popup.clip(); // Update seen time - return widget.controller.updateLocalSeenTime(); + return widget.controller.updateSeenTime(); } }, // Failure diff --git a/modules/ui/mw.echo.ui.NotificationsInboxWidget.js b/modules/ui/mw.echo.ui.NotificationsInboxWidget.js index 4e1df77..d46f72d 100644 --- a/modules/ui/mw.echo.ui.NotificationsInboxWidget.js +++ b/modules/ui/mw.echo.ui.NotificationsInboxWidget.js @@ -245,7 +245,7 @@ function () { widget.popPending(); // Update seen time - widget.controller.updateSeenTimeForCurrentSource(); + widget.controller.updateSeenTime(); }, // Failure function ( errObj ) { -- To view, visit https://gerrit.wikimedia.org/r/310460 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I67bcc4b346237317c7a9204dd43cd0e9ee02792f Gerrit-PatchSet: 8 Gerrit-Project: mediawiki/extensions/Echo Gerrit-Branch: master Gerrit-Owner: Mooeypoo <mor...@gmail.com> Gerrit-Reviewer: Catrope <roan.katt...@gmail.com> Gerrit-Reviewer: Mooeypoo <mor...@gmail.com> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits