( function ( mw ) { /** * Notification widget for echo popup. * * @class * @extends OO.ui.Widget * * @constructor * @param {mw.echo.dm.NotificationsModel} model Notifications view model * @param {Object} [config] Configuration object * @cfg {boolean} [markReadWhenSeen=false] State whether the notifications are all * marked as read when they are seen. * @cfg {jQuery} [$overlay] A jQuery element functioning as an overlay * for popups. * @cfg {boolean} [bundle=false] This notification is part of a bundled notification * group. This affects the rendering of the items. */ mw.echo.ui.NotificationsWidget = function MwEchoUiNotificationsWidget( model, config ) { config = config || {}; // Parent constructor mw.echo.ui.NotificationsWidget.parent.call( this, config ); this.model = model; this.markReadWhenSeen = !!config.markReadWhenSeen; this.bundle = !!config.bundle; this.$overlay = config.$overlay || this.$element; // Dummy 'loading' option widget this.loadingOptionWidget = new mw.echo.ui.PlaceholderItemWidget(); this.addItems( [ this.loadingOptionWidget ] ); // Events this.model.connect( this, { add: 'onModelNotificationAdd', remove: 'onModelNotificationRemove', clear: 'onModelNotificationClear', done: 'onModelNotificationDone' } ); this.$element .addClass( 'mw-echo-ui-notificationsWidget' ) .toggleClass( 'mw-echo-ui-notificationsWidget-bundle', this.bundle ); }; /* Initialization */ OO.inheritClass( mw.echo.ui.NotificationsWidget, OO.ui.SelectWidget ); /* Methods */ /** * Handle done event from the model * * @param {boolean} isSuccess The operation was successful * @param {Object} result Result object from the API */ mw.echo.ui.NotificationsWidget.prototype.onModelNotificationDone = function ( isSuccess, result ) { if ( this.model.isEmpty() ) { this.resetLoadingOption( isSuccess ? mw.msg( 'echo-notification-placeholder' ) : mw.msg( 'echo-api-failure', result.errCode ) ); } if ( isSuccess ) { // Log impressions mw.echo.logger.logNotificationImpressions( this.type, result.ids, mw.echo.Logger.static.context.popup ); } }; /** * Respond to model add event * * @param {mw.echo.dm.NotificationItem} Added notification item * @param {number} index Index to add the item */ mw.echo.ui.NotificationsWidget.prototype.onModelNotificationAdd = function ( notificationItem, index ) { var widget; if ( notificationItem instanceof mw.echo.dm.NotificationGroupItem ) { widget = new mw.echo.ui.NotificationGroupItemWidget( notificationItem, { bundle: this.bundle, $overlay: this.$overlay } ); } else { widget = new mw.echo.ui.NotificationItemWidget( notificationItem, { $overlay: this.$overlay, bundle: this.bundle, markReadWhenSeen: this.markReadWhenSeen } ); } // Fire hook for gadgets to update the option list mw.hook( 'ext.echo.overlay.beforeShowingOverlay' ).fire( widget.$element ); // Remove dummy option this.removeItems( [ this.loadingOptionWidget ] ); this.addItems( [ widget ], index ); }; /** * Respond to model add event * * @param {mw.echo.dm.NotificationItem[]} Removed notification items */ mw.echo.ui.NotificationsWidget.prototype.onModelNotificationClear = function () { var i, len, items = this.getItems(); // Destroy all the widgets and their events for ( i = 0, len = items.length; i < len; i++ ) { if ( typeof items[ i ].destroy === 'function' ) { // Destroy if destroyable items[ i ].destroy(); } } this.clearItems(); // Add dummy option this.resetLoadingOption(); }; /** * Respond to model add event * * @param {mw.echo.dm.NotificationItem} notificationItem Removed notification items */ mw.echo.ui.NotificationsWidget.prototype.onModelNotificationRemove = function ( notificationItem ) { var widget, items; widget = this.getItemFromData( notificationItem.getId() ); if ( widget && typeof widget.destroy === 'function' ) { // Destroy all widgets that can be destroyed widget.destroy(); } this.removeItems( [ widget ] ); items = this.getItems(); if ( !items.length ) { this.resetLoadingOption(); } }; /** * Go over the items and remove all items with 'initiallyUnseen' class on them. * That class is given to the widgets so that the animation works. When we refresh * the notifications, they should no longer be animated, allowing any new notifications * that were fetched to be set as unseen. */ mw.echo.ui.NotificationsWidget.prototype.resetNotificationItems = function () { var i, len, items = this.getItems(); for ( i = 0, len = items.length; i < len; i++ ) { if ( items[ i ] && typeof items[ i ].reset === 'function' ) { items[ i ].reset(); } } }; /** * Reset the loading 'dummy' option widget * * @param {string} [label] Label for the option widget */ mw.echo.ui.NotificationsWidget.prototype.resetLoadingOption = function ( label ) { this.loadingOptionWidget.setLabel( label || '' ); this.addItems( [ this.loadingOptionWidget ] ); }; /** * Get the model associated with this widget * * @return {mw.echo.dm.NotificationsModel} Notifications model */ mw.echo.ui.NotificationsWidget.prototype.getModel = function () { return this.model; }; } )( mediaWiki );