mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Echo
synced 2024-12-03 20:06:59 +00:00
52b60075d1
Foreign notification sources can end up being empty for legitimate reasons (already marked as read on the foreign wiki) or errors (failed to query remote server). This change makes the front-end resilient by removing the sources if they end up being empty, and removing the foreign notifications bundle if all the sources end up being empty. Bug: T135252 Bug: T135250 Change-Id: I5901bb0007420a19d8c0727faa982eb00809c074
232 lines
6.1 KiB
JavaScript
232 lines
6.1 KiB
JavaScript
( function ( mw ) {
|
|
/**
|
|
* Echo notification group item model
|
|
*
|
|
* @class
|
|
* @extends mw.echo.dm.NotificationItem
|
|
* @mixins mw.echo.dm.SortedList
|
|
*
|
|
* @constructor
|
|
* @param {mw.echo.api.EchoApi} api Echo API
|
|
* @param {mw.echo.dm.UnreadNotificationCounter} unreadCounter Counter of unread notifications
|
|
* @param {Object[]} sources An array of objects defining the sources
|
|
* of its item's sub-items.
|
|
* @param {number} id Notification id,
|
|
* @param {Object} [config] Configuration object
|
|
* @cfg {boolean} [removeReadNotifications=false] Completely remove notifications that are
|
|
* marked as read.
|
|
* @cfg {number} [count=0] The number of items this group contains. This is used for both the
|
|
* 'expand' label and also to potentially update the badge counters for local bundles.
|
|
*/
|
|
mw.echo.dm.NotificationGroupItem = function mwEchoDmNotificationGroupItem( api, unreadCounter, sources, id, config ) {
|
|
var source, item,
|
|
items = [];
|
|
|
|
config = config || {};
|
|
|
|
// Parent
|
|
mw.echo.dm.NotificationGroupItem.parent.call( this, id, config );
|
|
|
|
// Mixin constructor
|
|
mw.echo.dm.SortedList.call( this );
|
|
this.setSortingCallback( function ( a, b ) {
|
|
var diff;
|
|
// Reverse sorting
|
|
diff = Number( b.getTimestamp() ) - Number( a.getTimestamp() );
|
|
if ( diff !== 0 ) {
|
|
return diff;
|
|
}
|
|
|
|
// Fallback on IDs
|
|
return b.getId() - a.getId();
|
|
} );
|
|
this.aggregate( {
|
|
empty: 'groupEmpty',
|
|
itemRead: 'groupItemRead'
|
|
} );
|
|
this.connect( this, {
|
|
groupEmpty: 'onGroupEmpty'
|
|
} );
|
|
this.removeReadNotifications = !!config.removeReadNotifications;
|
|
|
|
this.unreadCounter = unreadCounter;
|
|
this.sources = sources;
|
|
this.api = api;
|
|
this.notifModels = {};
|
|
this.count = config.count || 0;
|
|
|
|
// Create notification models for each source
|
|
for ( source in this.sources ) {
|
|
// Create a notifications model
|
|
item = new mw.echo.dm.NotificationsModel(
|
|
this.api,
|
|
this.unreadCounter,
|
|
{
|
|
type: this.getType(),
|
|
source: source,
|
|
foreign: this.isForeign(),
|
|
title: this.sources[ source ].title,
|
|
removeReadNotifications: this.removeReadNotifications,
|
|
timestamp: this.sources[ source ].ts
|
|
}
|
|
);
|
|
items.push( item );
|
|
this.notifModels[ source ] = item;
|
|
}
|
|
|
|
this.addItems( items );
|
|
};
|
|
|
|
/* Inheritance */
|
|
|
|
OO.inheritClass( mw.echo.dm.NotificationGroupItem, mw.echo.dm.NotificationItem );
|
|
OO.mixinClass( mw.echo.dm.NotificationGroupItem, mw.echo.dm.SortedList );
|
|
|
|
/* Events */
|
|
|
|
/**
|
|
* The group is empty
|
|
*
|
|
* @event empty
|
|
*/
|
|
|
|
/**
|
|
* The number of item read in a group changed
|
|
*
|
|
* @event groupItemRead
|
|
*/
|
|
|
|
/* Methods */
|
|
|
|
/**
|
|
* Respond to notification model being empty
|
|
*
|
|
* @param {mw.echo.dm.NotificationsModel} notifModel Notifications model
|
|
*/
|
|
mw.echo.dm.NotificationGroupItem.prototype.onGroupEmpty = function ( notifModel ) {
|
|
if ( this.removeReadNotifications ) {
|
|
// This means the model is now empty. We should remove it as a group completely
|
|
this.removeItems( [ notifModel ] );
|
|
}
|
|
|
|
if ( this.isEmpty() ) {
|
|
this.emit( 'empty' );
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Fetch items from each of the sources
|
|
*
|
|
* @return {jQuery.Promise} Promise that is resolved when all items are fetched
|
|
*/
|
|
mw.echo.dm.NotificationGroupItem.prototype.fetchAllNotificationsInGroups = function () {
|
|
var notifModel,
|
|
model = this,
|
|
sourceKeys = Object.keys( this.sources );
|
|
|
|
return this.api.fetchNotificationGroups( sourceKeys, this.getType() )
|
|
.then(
|
|
function ( itemsData ) {
|
|
var i, notifData, source,
|
|
modelItems = {};
|
|
|
|
for ( i = 0; i < itemsData.length; i++ ) {
|
|
notifData = itemsData[ i ];
|
|
|
|
// Split notifications into groups per source
|
|
modelItems[ notifData.wiki ] = modelItems[ notifData.wiki ] || [];
|
|
modelItems[ notifData.wiki ].push( notifData );
|
|
}
|
|
|
|
// Process all models
|
|
for ( source in modelItems ) {
|
|
notifModel = model.getItemById( source );
|
|
if ( notifModel ) {
|
|
notifModel.processAPIData( modelItems[ source ] );
|
|
}
|
|
}
|
|
|
|
// hack: after loading the foreign notifications, get rid of the empty models
|
|
model.getItems().forEach( function ( foreignModel ) {
|
|
if ( foreignModel.isEmpty() ) {
|
|
foreignModel.emit( 'empty' );
|
|
}
|
|
} );
|
|
},
|
|
// Failure
|
|
function ( errCode, errObj ) {
|
|
var i;
|
|
// This means something went wrong fetching the remote notifications
|
|
// from the local API. We need to trigger the error handler in all
|
|
// of the relevant models
|
|
for ( i = 0; i < sourceKeys.length; i++ ) {
|
|
model.getItemById( sourceKeys[ i ] ).handleApiFetchError( errCode, errObj );
|
|
}
|
|
}
|
|
);
|
|
};
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
mw.echo.dm.NotificationGroupItem.prototype.toggleRead = function ( read ) {
|
|
this.fetchAllNotificationsInGroups().then( function () {
|
|
var promises = this.getItems().map( function ( foreignSourceModel ) {
|
|
return foreignSourceModel.markAllRead();
|
|
} );
|
|
return mw.echo.api.NetworkHandler.static.waitForAllPromises( promises );
|
|
}.bind( this ) );
|
|
|
|
mw.echo.dm.NotificationGroupItem.parent.prototype.toggleRead.call( this, read );
|
|
};
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
mw.echo.dm.NotificationGroupItem.prototype.toggleSeen = function ( seen ) {
|
|
var i,
|
|
notifModels = this.getItems();
|
|
|
|
seen = seen !== undefined ? seen : !this.seen;
|
|
|
|
if ( this.seen !== seen ) {
|
|
// Mark sub items as seen
|
|
for ( i = 0; i < notifModels.length; i++ ) {
|
|
notifModels[ i ].updateSeenTime();
|
|
}
|
|
}
|
|
|
|
// Parent method
|
|
mw.echo.dm.NotificationGroupItem.parent.prototype.toggleSeen.call( this, seen );
|
|
};
|
|
|
|
/**
|
|
* Get the item count.
|
|
*
|
|
* @return {number} count
|
|
*/
|
|
mw.echo.dm.NotificationGroupItem.prototype.getCount = function () {
|
|
return this.count;
|
|
};
|
|
|
|
/**
|
|
* Get the array of sources for this group
|
|
*
|
|
* @return {string[]} Sources
|
|
*/
|
|
mw.echo.dm.NotificationGroupItem.prototype.getSources = function () {
|
|
return this.sources;
|
|
};
|
|
|
|
/**
|
|
* Get all the sub-notification models for this group
|
|
*
|
|
* @return {Object} A keyed object containing mw.echo.dm.NotificationModel
|
|
* objects keyed by their source name.
|
|
*/
|
|
mw.echo.dm.NotificationGroupItem.prototype.getSubModels = function () {
|
|
return this.notifModels;
|
|
};
|
|
|
|
} )( mediaWiki );
|