mediawiki-extensions-Echo/modules/ooui/mw.echo.ui.NotificationsWidget.js
Moriel Schottlender 4b7520af90 Display readable API error message
If an error has occurred while fetching from the API, the
placeholder item should display the API error information.

If the error is specifically a login issue, a specific
error message is displayed.

Also, adjusted the mw.echo.ui.PlaceholderItemWidget to
accept a clickable link; when valid (currently only with
login error) the link is applied so the user can click
the notification and be taken to the login page.
For general notices (like API error or a general 'no
notifications found' message) the link does nothing.

Bug: T121923
Change-Id: I89a43c7c0eb2cf8e63d03704536e0938ab57dd4d
2015-12-22 10:34:01 -08:00

213 lines
6.2 KiB
JavaScript

( 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
* @param {string} result.errCode The API error code
* @param {string} result.errInfo The API error info string
*/
mw.echo.ui.NotificationsWidget.prototype.onModelNotificationDone = function ( isSuccess, result ) {
var loginPageTitle = mw.Title.newFromText( 'Special:UserLogin' );
if ( this.model.isEmpty() ) {
if ( isSuccess ) {
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
// message in that case
if ( result.errCode === 'notlogin-required' ) {
// Login error
this.resetLoadingOption(
// This message has a link inside it, so it must be
// given to the OO.ui.LabelWidget as a jQuery object, otherwise
// the LabelWidget parses it as a raw string.
$( '<span>' ).text( mw.message( 'echo-notification-popup-loginrequired' ) ),
// Set the option link to the login page
loginPageTitle.getUrl()
);
} else {
// General error
this.resetLoadingOption( mw.msg( 'echo-api-failure', result.errInfo ) );
}
}
}
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
* @param {string} [link] Link for the option widget
*/
mw.echo.ui.NotificationsWidget.prototype.resetLoadingOption = function ( label, link ) {
this.loadingOptionWidget.setLabel( label || '' );
this.loadingOptionWidget.setLink( link || '' );
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, jQuery );