( function ( mw, $ ) { /** * Notification group item widget for echo popup. * * @class * @extends mw.echo.ui.NotificationItemWidget * @mixins OO.ui.mixin.PendingElement * @mixins OO.ui.mixin.GroupElement * @mixins OO.ui.mixin.GroupWidget * * @constructor * @param {mw.echo.dm.NotificationGroupItem} model Item model * @param {Object} [config] Configuration object */ mw.echo.ui.NotificationGroupItemWidget = function MwEchoUiNotificationGroupItemWidget( model, config ) { config = config || {}; // Parent constructor mw.echo.ui.NotificationGroupItemWidget.parent.call( this, model, config ); // Mixin constructor OO.ui.mixin.GroupWidget.call( this, config ); OO.ui.mixin.PendingElement.call( this, config ); // Hide the [x] mark as read button for foreign notifications if ( this.getModel().isForeign() ) { this.toggleMarkAsReadButtons( false ); } this.setPendingElement( this.$group ); this.$group .addClass( 'mw-echo-ui-notificationGroupItemWidget-group' ) // We have to manually set the display to 'none' here because // otherwise the 'slideUp' and 'slideDown' jQuery effects don't // work .css( 'display', 'none' ); this.showTitles = false; this.expanded = false; // Add "expand" button this.toggleExpandButton = new OO.ui.ButtonOptionWidget( { icon: 'expand', framed: false, classes: [ 'mw-echo-ui-notificationItemWidget-content-actions-button' ] } ); this.updateExpandButton(); this.actionsButtonSelectWidget.addItems( [ this.toggleExpandButton ] ); // Events this.toggleExpandButton.connect( this, { click: 'expand' } ); this.$content.on( 'click', this.expand.bind( this ) ); this.model.connect( this, { add: 'onModelAddGroup', remove: 'onModelRemoveGroup', clear: 'onModelClearGroups' } ); // TODO: Handle cases where the group became empty or a case where the group only has 1 item left. // Note: Right now this code works primarily for cross-wiki notifications. These act differently // than local bundles. Cross-wiki notifications, when they "lose" their items for being read, they // vanish from the list. Unlike them, the plan for local bundles is that read sub-items go outside // the bundle and become their own items in the general notificationsWidget, and when the local bundle // has 1 notification left, the group will actually transform into that last notification item. // We don't listen to the empty event right now, because the entire item is deleted in cross-wiki // notifications. When we work on local bundles, we will have to add that event listener per item. // Initialization this.populateFromModel(); this.toggleExpanded( false ); this.$element .addClass( 'mw-echo-ui-notificationGroupItemWidget' ) .append( $( '
' ) .addClass( 'mw-echo-ui-notificationGroupItemWidget-separator' ), this.$group ); }; /* Initialization */ OO.inheritClass( mw.echo.ui.NotificationGroupItemWidget, mw.echo.ui.NotificationItemWidget ); OO.mixinClass( mw.echo.ui.NotificationGroupItemWidget, OO.ui.mixin.PendingElement ); // Need to mixin base class as well OO.mixinClass( mw.echo.ui.NotificationGroupItemWidget, OO.ui.mixin.GroupElement ); OO.mixinClass( mw.echo.ui.NotificationGroupItemWidget, OO.ui.mixin.GroupWidget ); /* Methods */ /** * Respond to model clearing the group */ mw.echo.ui.NotificationGroupItemWidget.prototype.onModelClearGroups = function () { this.clearItems(); this.handleEmptyGroups(); }; /** * Respond to model adding group * * @param {mw.echo.dm.NotificationsModel} notificationsModel Added group * @param {number} index Position the group was added */ mw.echo.ui.NotificationGroupItemWidget.prototype.onModelAddGroup = function ( notificationsModel, index ) { this.addItems( [ new mw.echo.ui.BundledNotificationGroupWidget( notificationsModel, { showTitle: this.showTitles, $overlay: this.$overlay } ) ], index ); this.checkShowTitles(); }; /** * Expand the notification group */ mw.echo.ui.NotificationGroupItemWidget.prototype.expand = function () { var widget = this; this.toggleExpanded( !this.expanded ); this.updateExpandButton(); if ( this.expanded ) { // Expand this.pushPending(); // Query all sources this.model.fetchAllNotificationsInGroups() .then( function ( /* Groups */ ) { var source, items, i, models = widget.model.getSubModels(); widget.popPending(); // Log impressions of all items from each group for ( source in models ) { items = models[source].getItems(); for ( i = 0; i < items.length; i++ ) { mw.echo.logger.logInteraction( mw.echo.Logger.static.actions.notificationImpression, mw.echo.Logger.static.context.popup, items[ i ].getId(), items[ i ].getCategory(), false, source ); } } } ); // Log the expand action mw.echo.logger.logInteraction( mw.echo.Logger.static.actions.notificationBundleExpand, mw.echo.Logger.static.context.popup, widget.getModel().getId(), widget.getModel().getCategory() ); } }; /** * Update the expand button label */ mw.echo.ui.NotificationGroupItemWidget.prototype.updateExpandButton = function () { var type = this.model.getType(); if ( $.isArray( type ) ) { type = 'all'; } this.toggleExpandButton.setLabel( this.expanded ? mw.msg( 'notification-link-text-collapse-all' ) : // Messages that appear here are: // notification-link-text-expand-alert-count // notification-link-text-expand-message-count mw.msg( 'notification-link-text-expand-' + this.model.getType() + '-count', this.model.getCount() ) ); this.toggleExpandButton.setIcon( this.expanded ? 'collapse' : 'expand' ); }; /** * Populate the items from the model */ mw.echo.ui.NotificationGroupItemWidget.prototype.populateFromModel = function () { var i, items = this.model.getItems(), widgets = []; for ( i = 0; i < items.length; i++ ) { widgets.push( new mw.echo.ui.BundledNotificationGroupWidget( items[ i ], { showTitle: this.showTitles, $overlay: this.$overlay } ) ); } this.addItems( widgets ); this.checkShowTitles(); }; /** * Respond to model removing a group * * @param {mw.echo.dm.NotificationsModel} notificationsModel Removed group */ mw.echo.ui.NotificationGroupItemWidget.prototype.onModelRemoveGroup = function ( notificationsModel ) { var widget = this.getItemFromData( notificationsModel.getId() ); if ( widget ) { this.removeItems( [ widget ] ); } }; /** * Toggle the expand/collapsed state of this group widget * * @param {boolean} show Show the widget expanded */ mw.echo.ui.NotificationGroupItemWidget.prototype.toggleExpanded = function ( show ) { this.expanded = show !== undefined ? !!show : !this.expanded; if ( show ) { this.$group.slideDown(); } else { this.$group.slideUp(); } }; /** * @inheritdoc */ mw.echo.ui.NotificationGroupItemWidget.prototype.toggleRead = function ( read ) { // Parent method mw.echo.ui.NotificationGroupItemWidget.parent.prototype.toggleRead.call( this, read ); if ( this.getModel().isForeign() ) { // Never show the [x] mark-all-unread button for foreign bundles this.toggleMarkAsReadButtons( false ); } }; /** * Check whether the titles should be shown and toggle them in the items. */ mw.echo.ui.NotificationGroupItemWidget.prototype.checkShowTitles = function () { var i, items = this.getItems(), numItems = items.length, showTitles = numItems > 1; if ( this.showTitles !== showTitles ) { this.showTitles = showTitles; for ( i = 0; i < numItems; i++ ) { items[ i ].toggleTitle( showTitles ); } } }; } )( mediaWiki, jQuery );