diff --git a/Echo.php b/Echo.php index d06ef04c4..dd24a9bcb 100644 --- a/Echo.php +++ b/Echo.php @@ -110,6 +110,9 @@ $wgHooks['LinksUpdateAfterInsert'][] = 'EchoHooks::onLinksUpdateAfterInsert'; // Beta features $wgHooks['GetBetaFeaturePreferences'][] = 'EchoHooks::getBetaFeaturePreferences'; +// Global config vars +$wgHooks['ResourceLoaderGetConfigVars'][] = 'EchoHooks::onResourceLoaderGetConfigVars'; + // Configuration // Whether to turn on email batch function diff --git a/Hooks.php b/Hooks.php index 87d572660..0b67aa126 100644 --- a/Hooks.php +++ b/Hooks.php @@ -1160,4 +1160,10 @@ class EchoHooks { return true; } + public static function onResourceLoaderGetConfigVars( &$vars ) { + global $wgEchoMaxNotificationCount; + $vars['wgEchoMaxNotificationCount'] = $wgEchoMaxNotificationCount; + return true; + } + } diff --git a/Resources.php b/Resources.php index b66f55877..4e88e2d86 100644 --- a/Resources.php +++ b/Resources.php @@ -87,6 +87,7 @@ $wgResourceModules += array( 'oojs-ui.styles.icons-content', ), 'messages' => array( + 'echo-notification-count', 'echo-overlay-link', 'echo-mark-all-as-read', 'echo-more-info', diff --git a/modules/ooui/mw.echo.ui.NotificationBadgeWidget.js b/modules/ooui/mw.echo.ui.NotificationBadgeWidget.js index 0c9ee6ef3..943edd3cc 100644 --- a/modules/ooui/mw.echo.ui.NotificationBadgeWidget.js +++ b/modules/ooui/mw.echo.ui.NotificationBadgeWidget.js @@ -208,7 +208,9 @@ mw.echo.ui.NotificationBadgeWidget.prototype.updateBadge = function () { var unseenCount = this.notificationsModel.getUnseenCount(), unreadCount = this.notificationsModel.getUnreadCount(), - nonBundledUnreadCount = this.notificationsModel.getNonbundledUnreadCount(); + nonBundledUnreadCount = this.notificationsModel.getNonbundledUnreadCount(), + wgEchoMaxNotificationCount, + badgeLabel; // Update numbers and seen/unseen state // If the popup is open, only allow a "demotion" of the badge @@ -225,7 +227,13 @@ // Update badge count if ( !this.markReadWhenSeen || !this.popup.isVisible() || unreadCount < this.currentUnreadCountInBadge ) { - this.badgeButton.setLabel( mw.language.convertNumber( unreadCount ) ); + wgEchoMaxNotificationCount = mw.config.get( 'wgEchoMaxNotificationCount' ); + if ( unreadCount > wgEchoMaxNotificationCount ) { + badgeLabel = mw.message( 'echo-notification-count', wgEchoMaxNotificationCount ).text(); + } else { + badgeLabel = mw.language.convertNumber( unreadCount ); + } + this.badgeButton.setLabel( badgeLabel ); } // Check if we need to display the 'mark all unread' button diff --git a/modules/ooui/mw.echo.ui.NotificationsWidget.js b/modules/ooui/mw.echo.ui.NotificationsWidget.js index 385f3a9c1..b34f13698 100644 --- a/modules/ooui/mw.echo.ui.NotificationsWidget.js +++ b/modules/ooui/mw.echo.ui.NotificationsWidget.js @@ -65,7 +65,7 @@ this.resetLoadingOption( mw.msg( 'echo-notification-placeholder' ) ); } else { // If failure, check if the failure is due to login - // so we can display a more complrehensive error + // so we can display a more comprehensive error // message in that case if ( result.errCode === 'notlogin-required' ) { // Login error diff --git a/modules/viewmodel/mw.echo.dm.NotificationGroupItem.js b/modules/viewmodel/mw.echo.dm.NotificationGroupItem.js index b8e617a6f..ce2acbd58 100644 --- a/modules/viewmodel/mw.echo.dm.NotificationGroupItem.js +++ b/modules/viewmodel/mw.echo.dm.NotificationGroupItem.js @@ -40,7 +40,8 @@ return b.getId() - a.getId(); } ); this.aggregate( { - empty: 'groupEmpty' + empty: 'groupEmpty', + itemRead: 'groupItemRead' } ); this.connect( this, { groupEmpty: 'onGroupEmpty' @@ -50,7 +51,7 @@ this.sources = sources; this.networkHandler = networkHandler; this.notifModels = {}; - this.count = config.count || 0; + this.anticipatedCount = config.count || 0; // Create notification models for each source for ( source in this.sources ) { @@ -88,12 +89,18 @@ * @event empty */ + /** + * The number of item read in a group changed + * + * @event groupItemRead + */ + /* Methods */ /** * Respond to notification model being empty * - * @param {mw.echo.dm.NotificationModel} notifModel Notification model + * @param {mw.echo.dm.NotificationsModel} notifModel Notifications model */ mw.echo.dm.NotificationGroupItem.prototype.onGroupEmpty = function ( notifModel ) { if ( this.removeReadNotifications ) { @@ -130,6 +137,9 @@ // Wait for all fetch processes to finish before we resolve this promise return mw.echo.dm.NetworkHandler.static.waitForAllPromises( fetchPromises ); + } ) + .then( function () { + model.anticipatedCount = null; } ); }; @@ -208,12 +218,20 @@ }; /** - * Get the anticipated count of items in this group item + * Get the anticipated count of items in this group item, + * or actual count if is has been loaded. * - * @return {number} Anticipated item count + * @return {number} count */ mw.echo.dm.NotificationGroupItem.prototype.getCount = function () { - return this.count; + if ( this.anticipatedCount !== null ) { + return this.anticipatedCount; + } + var sum = 0; + this.getItems().forEach( function ( notificationsModel ) { + sum += notificationsModel.getUnreadCount(); + } ); + return sum; }; /** diff --git a/modules/viewmodel/mw.echo.dm.NotificationItem.js b/modules/viewmodel/mw.echo.dm.NotificationItem.js index bf7c7800e..efac18063 100644 --- a/modules/viewmodel/mw.echo.dm.NotificationItem.js +++ b/modules/viewmodel/mw.echo.dm.NotificationItem.js @@ -264,4 +264,13 @@ mw.echo.dm.NotificationItem.prototype.getSource = function () { return this.source; }; + + /** + * Get the number of notifications represented by this object + * + * @return {number} Notification count + */ + mw.echo.dm.NotificationItem.prototype.getCount = function () { + return 1; + }; }( mediaWiki, jQuery ) ); diff --git a/modules/viewmodel/mw.echo.dm.NotificationList.js b/modules/viewmodel/mw.echo.dm.NotificationList.js index 7d4fbea34..435d50004 100644 --- a/modules/viewmodel/mw.echo.dm.NotificationList.js +++ b/modules/viewmodel/mw.echo.dm.NotificationList.js @@ -23,4 +23,21 @@ OO.initClass( mw.echo.dm.NotificationList ); OO.mixinClass( mw.echo.dm.NotificationList, OO.EventEmitter ); OO.mixinClass( mw.echo.dm.NotificationList, mw.echo.dm.List ); + + /* Methods */ + + /** + * Count the number of notifications by asking all contained objects + * how many notifications they each represent. Some are single, some + * are groups. + * @return {number} + */ + mw.echo.dm.NotificationList.prototype.getNotificationCount = function () { + var sum = 0; + this.getItems().forEach( function ( notificationItem ) { + sum += notificationItem.getCount(); + } ); + return sum; + }; + } )( mediaWiki ); diff --git a/modules/viewmodel/mw.echo.dm.NotificationsModel.js b/modules/viewmodel/mw.echo.dm.NotificationsModel.js index 79f3715ea..eb2e8a5b4 100644 --- a/modules/viewmodel/mw.echo.dm.NotificationsModel.js +++ b/modules/viewmodel/mw.echo.dm.NotificationsModel.js @@ -55,7 +55,8 @@ this.aggregate( { seen: 'itemSeen', read: 'itemRead', - empty: 'itemGroupEmpty' + empty: 'itemGroupEmpty', + groupItemRead: 'unreadChange' } ); this.connect( this, { @@ -221,6 +222,7 @@ // In this case, the notification is a "cross wiki" notification, which // goes away when it is empty this.removeItems( [ item ] ); + this.emit( 'unreadChange' ); }; /** @@ -257,7 +259,7 @@ * @return {number} Number of unseen notifications */ mw.echo.dm.NotificationsModel.prototype.getUnseenCount = function () { - return this.unseenNotifications.getItemCount(); + return this.unseenNotifications.getNotificationCount(); }; /** @@ -266,7 +268,7 @@ * @return {number} Number of unread notifications */ mw.echo.dm.NotificationsModel.prototype.getUnreadCount = function () { - return this.unreadNotifications.getItemCount(); + return this.unreadNotifications.getNotificationCount(); }; /**