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

Reply via email to