mediawiki-extensions-Echo/modules/ui/mw.echo.ui.NotificationsInboxWidget.js
Ed Sanders c8d24ebd17 build: Replace jscs/jshint with eslint
Change-Id: Iee1d1b20ed31e636bfb8fc8cf9b18ff328bf608c
2016-11-23 15:25:59 -08:00

348 lines
10 KiB
JavaScript

( function ( $, mw ) {
/**
* An inbox-type widget that encompases a dated notifications list with pagination
*
* @class
* @extends OO.ui.Widget
* @mixins OO.ui.mixin.PendingElement
*
* @constructor
* @param {mw.echo.Controller} controller Echo controller
* @param {mw.echo.dm.ModelManager} manager Model manager
* @param {Object} [config] Configuration object
* @cfg {number} [limit=25] Limit the number of notifications per page
* @cfg {jQuery} [$overlay] An overlay for the popup menus
*/
mw.echo.ui.NotificationsInboxWidget = function MwEchoUiNotificationsInboxWidget( controller, manager, config ) {
var $main, $sidebar;
config = config || {};
// Parent
mw.echo.ui.NotificationsInboxWidget.parent.call( this, config );
// Mixin constructors
OO.ui.mixin.PendingElement.call( this, config );
this.controller = controller;
this.manager = manager;
this.$overlay = config.$overlay || this.$element;
this.limit = config.limit || 25;
this.error = false;
// A notice or error message widget
this.noticeMessageWidget = new OO.ui.LabelWidget( {
classes: [ 'mw-echo-ui-notificationsInboxWidget-notice' ]
} );
// Notifications list
this.datedListWidget = new mw.echo.ui.DatedNotificationsWidget(
this.controller,
this.manager,
{
$overlay: this.$overlay,
animateSorting: false
}
);
this.setPendingElement( this.datedListWidget.$element );
// Pagination
this.topPaginationWidget = new mw.echo.ui.PaginationWidget(
this.manager.getPaginationModel(),
{
itemsPerPage: this.limit
}
);
this.bottomPaginationWidget = new mw.echo.ui.PaginationWidget(
this.manager.getPaginationModel(),
{
itemsPerPage: this.limit
}
);
// Settings menu
this.settingsMenu = new mw.echo.ui.SpecialHelpMenuWidget(
this.manager,
{
framed: true,
helpLink: config.helpLink,
prefLink: config.prefLink
}
);
// Filter by read state
this.readStateSelectWidget = new mw.echo.ui.ReadStateButtonSelectWidget();
// Sidebar filters
this.xwikiUnreadWidget = new mw.echo.ui.CrossWikiUnreadFilterWidget(
this.controller,
this.manager.getFiltersModel()
);
// Events
this.readStateSelectWidget.connect( this, { filter: 'onReadStateFilter' } );
this.xwikiUnreadWidget.connect( this, { filter: 'onSourcePageFilter' } );
this.manager.connect( this, {
modelItemUpdate: 'updatePaginationLabels',
localCountChange: 'updatePaginationLabels'
} );
this.manager.getFiltersModel().connect( this, { update: 'updateReadStateSelectWidget' } );
this.manager.getPaginationModel().connect( this, { update: 'updatePaginationLabels' } );
this.topPaginationWidget.connect( this, { change: 'populateNotifications' } );
this.bottomPaginationWidget.connect( this, { change: 'populateNotifications' } );
this.settingsMenu.connect( this, { markAllRead: 'onSettingsMarkAllRead' } );
$( window ).on( 'scroll resize', this.onWindowScroll.bind( this ) );
this.topPaginationWidget.setDisabled( true );
this.bottomPaginationWidget.setDisabled( true );
this.$topToolbar =
$( '<div>' )
.addClass( 'mw-echo-ui-notificationsInboxWidget-main-toolbar-top' )
.append(
$( '<div>' )
.addClass( 'mw-echo-ui-notificationsInboxWidget-row' )
.append(
$( '<div>' )
.addClass( 'mw-echo-ui-notificationsInboxWidget-main-toolbar-readState' )
.addClass( 'mw-echo-ui-notificationsInboxWidget-cell' )
.append( this.readStateSelectWidget.$element ),
$( '<div>' )
.addClass( 'mw-echo-ui-notificationsInboxWidget-cell-placeholder' ),
$( '<div>' )
.addClass( 'mw-echo-ui-notificationsInboxWidget-main-toolbar-pagination' )
.addClass( 'mw-echo-ui-notificationsInboxWidget-cell' )
.append( this.topPaginationWidget.$element ),
$( '<div>' )
.addClass( 'mw-echo-ui-notificationsInboxWidget-main-toolbar-settings' )
.addClass( 'mw-echo-ui-notificationsInboxWidget-cell' )
.append( this.settingsMenu.$element )
)
);
this.$toolbarWrapper =
$( '<div>' )
.addClass( 'mw-echo-ui-notificationsInboxWidget-toolbarWrapper' )
.append( this.$topToolbar );
$sidebar = $( '<div>' )
.addClass( 'mw-echo-ui-notificationsInboxWidget-sidebar' )
.append( this.xwikiUnreadWidget.$element );
$main = $( '<div>' )
.addClass( 'mw-echo-ui-notificationsInboxWidget-main' )
.append(
this.$toolbarWrapper,
this.noticeMessageWidget.$element,
this.datedListWidget.$element
);
this.$element
.addClass( 'mw-echo-ui-notificationsInboxWidget' )
.append(
$( '<div>' )
.addClass( 'mw-echo-ui-notificationsInboxWidget-row' )
.append(
$sidebar
.addClass( 'mw-echo-ui-notificationsInboxWidget-cell' ),
$main
.addClass( 'mw-echo-ui-notificationsInboxWidget-cell' )
)
);
this.updateReadStateSelectWidget();
this.xwikiUnreadWidget.populateSources();
this.populateNotifications();
};
/* Initialization */
OO.inheritClass( mw.echo.ui.NotificationsInboxWidget, OO.ui.Widget );
OO.mixinClass( mw.echo.ui.NotificationsInboxWidget, OO.ui.mixin.PendingElement );
/* Methods */
/**
* Respond to filters update
*/
mw.echo.ui.NotificationsInboxWidget.prototype.updateReadStateSelectWidget = function () {
this.readStateSelectWidget
.getItemFromData( this.manager.getFiltersModel().getReadState() )
.setSelected( true );
};
/**
* Update pagination messages
*/
mw.echo.ui.NotificationsInboxWidget.prototype.updatePaginationLabels = function () {
this.resetMessageLabel();
// Update the pagination label
this.topPaginationWidget.updateLabel();
this.bottomPaginationWidget.updateLabel();
};
/**
* Respond to mark all read for selected wiki
*/
mw.echo.ui.NotificationsInboxWidget.prototype.onSettingsMarkAllRead = function () {
this.pushPending();
this.controller.markAllRead()
.always( this.popPending.bind( this ) );
};
/**
* Respond to unread page filter
*
* @param {string} source Source symbolic name
* @param {string} page Page name
*/
mw.echo.ui.NotificationsInboxWidget.prototype.onSourcePageFilter = function ( source, page ) {
this.controller.setFilter( 'sourcePage', source, page );
this.populateNotifications();
};
/**
* Respond to read state filter event
*
* @param {string} readState Read state 'all', 'read' or 'unread'
*/
mw.echo.ui.NotificationsInboxWidget.prototype.onReadStateFilter = function ( readState ) {
this.controller.setFilter( 'readState', readState );
this.populateNotifications();
};
/**
* Populate the notifications list
*
* @param {string} [direction] Direction to fetch from. 'prev' for previous page
* or 'next' for the next page. If not given, the first page of results will be fetched.
* @return {jQuery.Promise} A promise that is resolved when the results
* have been fetched.
*/
mw.echo.ui.NotificationsInboxWidget.prototype.populateNotifications = function ( direction ) {
var fetchPromise,
widget = this;
if ( direction === 'prev' ) {
fetchPromise = this.controller.fetchPrevPageByDate();
} else if ( direction === 'next' ) {
fetchPromise = this.controller.fetchNextPageByDate();
} else {
fetchPromise = this.controller.fetchFirstPageByDate();
}
this.pushPending();
this.error = false;
return fetchPromise
.then(
// Success
function () {
// Fire initialization hook
mw.hook( 'ext.echo.special.onInitialize' ).fire( widget.controller.manager.getTypeString(), widget.controller );
widget.popPending();
// Update seen time
widget.controller.updateSeenTime();
},
// Failure
function ( errObj ) {
var msg;
if ( errObj.errCode === 'notlogin-required' ) {
// Login required message
msg = mw.msg( 'echo-notification-loginrequired' );
} else {
// Generic API failure message
msg = mw.msg( 'echo-api-failure' );
}
widget.error = true;
widget.noticeMessageWidget.setLabel( msg );
widget.displayMessage( true );
}
)
.always( this.popPending.bind( this ) );
};
/**
* Extend the pushPending method to disable UI elements
*/
mw.echo.ui.NotificationsInboxWidget.prototype.pushPending = function () {
this.noticeMessageWidget.toggle( false );
this.topPaginationWidget.setDisabled( true );
this.bottomPaginationWidget.setDisabled( true );
// Mixin method
OO.ui.mixin.PendingElement.prototype.pushPending.call( this );
};
/**
* Extend the popPending method to enable UI elements
*/
mw.echo.ui.NotificationsInboxWidget.prototype.popPending = function () {
if ( !this.error ) {
this.resetMessageLabel();
}
this.topPaginationWidget.setDisabled( false );
this.bottomPaginationWidget.setDisabled( false );
// Mixin method
OO.ui.mixin.PendingElement.prototype.popPending.call( this );
};
/**
* Reset the the text of the error message that displays in place of the list
* in case the list is empty.
*/
mw.echo.ui.NotificationsInboxWidget.prototype.resetMessageLabel = function () {
var label,
count = this.manager.getPaginationModel().getCurrentPageItemCount();
if ( count === 0 ) {
label = this.manager.getFiltersModel().getReadState() === 'all' ?
mw.msg( 'echo-notification-placeholder' ) :
mw.msg( 'echo-notification-placeholder-filters' );
this.noticeMessageWidget.setLabel( label );
}
this.displayMessage( count === 0 );
};
/**
* Display the error/notice message instead of the notifications list or vise versa.
*
* @private
* @param {boolean} displayMessage Display error message
*/
mw.echo.ui.NotificationsInboxWidget.prototype.displayMessage = function ( displayMessage ) {
this.noticeMessageWidget.toggle( displayMessage );
this.datedListWidget.toggle( !displayMessage );
};
/**
* Respond to window scroll
*/
mw.echo.ui.NotificationsInboxWidget.prototype.onWindowScroll = function () {
var scrollTop = $( window ).scrollTop(),
isScrolledDown = scrollTop >= this.$topToolbar.parent().offset().top;
// Fix the widget to the top when we scroll down below its original
// location
this.$topToolbar.toggleClass(
'mw-echo-ui-notificationsInboxWidget-main-toolbar-affixed',
isScrolledDown
);
if ( isScrolledDown ) {
// Copy width from parent, width: 100% doesn't do what we want when
// position: fixed; is set
this.$topToolbar.css( 'width', this.$topToolbar.parent().width() );
} else {
// Unset width when we no longer have position: fixed;
this.$topToolbar.css( 'width', '' );
}
};
}( jQuery, mediaWiki ) );