diff --git a/includes/formatters/EchoModelFormatter.php b/includes/formatters/EchoModelFormatter.php index 2ca6bcd9d..be617ec87 100644 --- a/includes/formatters/EchoModelFormatter.php +++ b/includes/formatters/EchoModelFormatter.php @@ -21,6 +21,11 @@ class EchoModelFormatter extends EchoEventFormatter { $link['url'] = wfExpandUrl( $link['url'] ); } + $bundledIds = $model->getBundledIds(); + if ( $bundledIds ) { + $data[ 'bundledIds' ] = $bundledIds; + } + return $data; } } diff --git a/includes/formatters/EventPresentationModel.php b/includes/formatters/EventPresentationModel.php index c748c98f2..691e013b8 100644 --- a/includes/formatters/EventPresentationModel.php +++ b/includes/formatters/EventPresentationModel.php @@ -172,6 +172,20 @@ abstract class EchoEventPresentationModel implements JsonSerializable { return $this->bundledEvents; } + /** + * Get the ids of the bundled notifications or false if it's not bundled + * + * @return int[]|bool + */ + public function getBundledIds() { + if ( $this->isBundled() ) { + return array_map( function ( EchoEvent $event ) { + return $event->getId(); + }, $this->getBundledEvents() ); + } + return false; + } + /** * This method returns true when there are bundled notifications, even if they are all * in the same group according to getBundleGrouping(). For presentation purposes, you may diff --git a/modules/controller/mw.echo.Controller.js b/modules/controller/mw.echo.Controller.js index bfd2081cb..eac7c92ea 100644 --- a/modules/controller/mw.echo.Controller.js +++ b/modules/controller/mw.echo.Controller.js @@ -267,7 +267,8 @@ iconURL: content.iconUrl, iconType: content.icon, primaryUrl: OO.getProp( content.links, 'primary', 'url' ), - secondaryUrls: OO.getProp( content.links, 'secondary' ) || [] + secondaryUrls: OO.getProp( content.links, 'secondary' ) || [], + bundledIds: content.bundledIds }; }; @@ -283,7 +284,7 @@ * were marked as read. */ mw.echo.Controller.prototype.markEntireListModelRead = function ( modelName ) { - var i, items, + var i, items, item, itemIds = [], model = this.manager.getNotificationModel( modelName || 'local' ); @@ -294,8 +295,9 @@ items = model.getItems(); for ( i = 0; i < items.length; i++ ) { - if ( !items[ i ].isRead() ) { - itemIds.push( items[ i ].getId() ); + item = items[ i ]; + if ( !item.isRead() ) { + itemIds = itemIds.concat( item.getAllIds() ); } } @@ -389,18 +391,20 @@ * for the set type of this controller, in the given source. */ mw.echo.Controller.prototype.markItemsRead = function ( itemIds, modelSource, isRead ) { + var allIds = []; itemIds = Array.isArray( itemIds ) ? itemIds : [ itemIds ]; // Default to true isRead = isRead === undefined ? true : isRead; this.manager.getNotificationModel( modelSource ).findByIds( itemIds ).forEach( function ( notification ) { + allIds = allIds.concat( notification.getAllIds() ); notification.toggleRead( isRead ); } ); - this.manager.getUnreadCounter().estimateChange( isRead ? -itemIds.length : itemIds.length ); + this.manager.getUnreadCounter().estimateChange( isRead ? -allIds.length : allIds.length ); - return this.api.markItemsRead( itemIds, modelSource, isRead ).then( this.refreshUnreadCount.bind( this ) ); + return this.api.markItemsRead( allIds, modelSource, isRead ).then( this.refreshUnreadCount.bind( this ) ); }; /** @@ -459,7 +463,7 @@ idArray = []; for ( i = 0; i < groupItems.length; i++ ) { - idArray.push( groupItems[ i ].id ); + idArray = idArray.concat( groupItems[ i ].id ).concat( groupItems[ i ][ '*' ].bundledIds || [] ); } itemCounter += idArray.length; @@ -467,8 +471,8 @@ promises.push( controller.markCrossWikiItemsRead( idArray, listModel.getSource() ) ); - } + // Synchronously remove this model from the widget controller.removeCrossWikiItem(); diff --git a/modules/model/mw.echo.dm.NotificationItem.js b/modules/model/mw.echo.dm.NotificationItem.js index 06ab66e03..a1b36210e 100644 --- a/modules/model/mw.echo.dm.NotificationItem.js +++ b/modules/model/mw.echo.dm.NotificationItem.js @@ -24,6 +24,7 @@ * @cfg {string} [primaryUrl] Notification primary link in raw url format * @cfg {boolean} [foreign=false] This notification is from a foreign source * @cfg {boolean} [bundled=false] This notification is part of a bundle + * @cfg {number[]} [bundledIds] IDs of notifications bundled with this one * @cfg {string} [source] The source this notification is coming from, if it is foreign * @cfg {Object[]} [secondaryUrls] An array of objects defining the secondary URLs * for this notification. The secondary URLs are expected to have this structure: @@ -70,6 +71,7 @@ this.timestamp = config.timestamp || fallbackMWDate; this.setPrimaryUrl( config.primaryUrl ); this.setSecondaryUrls( config.secondaryUrls ); + this.bundledIds = config.bundledIds; }; /* Initialization */ @@ -280,4 +282,14 @@ mw.echo.dm.NotificationItem.prototype.getSource = function () { return this.source; }; + + /** + * Get the all ids contained in this notification + * + * @return {number[]} + */ + mw.echo.dm.NotificationItem.prototype.getAllIds = function () { + return [ this.getId() ].concat( this.bundledIds || [] ); + }; + } )( mediaWiki, jQuery );