mediawiki-extensions-Echo/modules/api/mw.echo.api.APIHandler.js
Moriel Schottlender eea0dea7ce Only fetch 'unreadfirst' for the Popup, not Special:Notifications
We need the API to return the notifications for the special page
in timestamp order and not read/unread order, so that the 'continue'
value is still correct.

On top of that, if we have many unread notifications, they should
still be placed according to the dates, so the API must bring back
proper result.

In this fix, we add 'unreadFirst' to filters, and only use that
filter when needed (namely, in the popup)

Bug: T136885
Change-Id: I3018d09b009d735402d83074a5ffcd14ea1c242a
2016-07-07 16:29:40 -07:00

255 lines
7.9 KiB
JavaScript

( function ( mw, $ ) {
/**
* Abstract notification API handler
*
* @abstract
* @class
*
* @constructor
* @param {mw.Api} api
* @param {Object} [config] Configuration object
* @cfg {number} [limit=25] The limit on how many notifications to fetch
* @cfg {string} [userLang=mw.config.get( 'wgUserLanguage' )] User language. Defaults
* to the default user language configuration settings.
*/
mw.echo.api.APIHandler = function MwEchoApiAPIHandler( api, config ) {
config = config || {};
this.fetchNotificationsPromise = {};
this.apiErrorState = {};
this.limit = config.limit || 25;
this.userLang = config.userLang || mw.config.get( 'wgUserLanguage' );
this.api = api;
// Map the logical type to the type
// that the API recognizes
this.normalizedType = {
message: 'message',
alert: 'alert',
all: 'message|alert'
};
// Parameters that are sent through
// to the 'fetch notification' promise
// per type
this.typeParams = {
message: {},
alert: {},
all: {}
};
};
/* Setup */
OO.initClass( mw.echo.api.APIHandler );
/**
* Fetch notifications from the API.
*
* @param {string} type Notification type
* @param {Object} [overrideParams] An object defining parameters to override in the API
* fetching call.
* @return {jQuery.Promise} A promise that resolves with an object containing the
* notification items
*/
mw.echo.api.APIHandler.prototype.fetchNotifications = null;
/**
* Fetch all pages with unread notifications in them per wiki
*
* @param {string|string[]} [sources=*] Requested sources. If not given
* or if a '*' is given, all available sources will be queried
* @return {jQuery.Promise} Promise that is resolved with an object
* of pages with the number of unread notifications per wiki
*/
mw.echo.api.APIHandler.prototype.fetchUnreadNotificationPages = function ( sources ) {
var params = {
action: 'query',
meta: 'unreadnotificationpages'
};
if ( !sources || sources === '*' ) {
params.unpwikis = '*';
} else {
sources = Array.isArray( sources ) ? sources : [ sources ];
params.unpwikis = sources.join( '|' );
}
return this.api.get( params );
};
/**
* Check if the given source is local
*
* @param {string|string[]} sources Source names
* @return {boolean} Source is local
*/
mw.echo.api.APIHandler.prototype.isSourceLocal = function ( sources ) {
return Array.isArray( sources ) ?
(
sources.indexOf( 'local' ) !== -1 ||
sources.indexOf( mw.config.get( 'wgDBname' ) ) !== -1
) :
(
sources === 'local' ||
sources === mw.config.get( 'wgDBname' )
);
};
/**
* Create a new fetchNotifications promise that queries the API and overrides
* the cached promise.
*
* @param {string} type Notification type
* @param {string[]} [sources] An array of sources to query
* @param {Object} [overrideParams] An object defining parameters to override in the API
* fetching call.
* @return {jQuery.Promise} Promise that is resolved when notifications are
* fetched from the API.
*/
mw.echo.api.APIHandler.prototype.createNewFetchNotificationPromise = function ( type, sources, overrideParams ) {
var apiErrState, fetchNotifPromise,
fetchingSource = 'local',
me = this,
params = $.extend( {
action: 'query',
meta: 'notifications',
notsections: this.normalizedType[ type ],
notformat: 'model',
notlimit: this.limit,
notprop: 'list|count',
uselang: this.userLang
}, this.getTypeParams( type ) );
if ( !this.isSourceLocal( sources ) ) {
params.notwikis = sources.join( '|' );
params.notfilter = '!read';
fetchingSource = 'foreign';
}
// Initialize the nested value if it doesn't yet exist
this.fetchNotificationsPromise[ type ] = this.fetchNotificationsPromise[ type ] || {};
me.apiErrorState[ type ] = me.apiErrorState[ type ] || {};
// Reset cached values
apiErrState = false;
this.fetchNotificationsPromise[ type ][ fetchingSource ] = null;
this.apiErrorState[ type ][ fetchingSource ] = false;
// Create the fetch promise
fetchNotifPromise = this.api.get( $.extend( true, params, overrideParams ) );
// Only cache promises that don't have override params in them
if ( !overrideParams ) {
this.fetchNotificationsPromise[ type ][ fetchingSource ] = fetchNotifPromise;
}
return fetchNotifPromise
.fail( function () {
// Mark API error state
me.apiErrorState[ type ][ fetchingSource ] = true;
} );
};
/**
* Update the seen timestamp
*
* @param {string} [type] Notification type 'message', 'alert' or 'all'.
* @return {jQuery.Promise} A promise that resolves with the seen timestamp
*/
mw.echo.api.APIHandler.prototype.updateSeenTime = null;
/**
* Mark all notifications as read
*
* @param {string|string[]} type Notification type 'message', 'alert' or 'all'.
* @return {jQuery.Promise} A promise that resolves when all notifications
* are marked as read.
*/
mw.echo.api.APIHandler.prototype.markAllRead = null;
/**
* Mark multiple notification items as read using specific IDs
*
* @abstract
* @param {string[]} itemIdArray An array of notification item IDs
* @param {boolean} [isRead] Item's new read state; true for marking the item
* as read, false for marking the item as unread
* @return {jQuery.Promise} A promise that resolves when all given notifications
* are marked as read.
*/
mw.echo.api.APIHandler.prototype.markItemsRead = null;
/**
* Update the read status of a notification item in the API
*
* @param {string} itemId Item id
* @param {boolean} [isRead] Item's new read state; true for marking the item
* as read, false for marking the item as unread
* @return {jQuery.Promise} A promise that resolves when the notifications
* are marked as read.
*/
mw.echo.api.APIHandler.prototype.markItemRead = function ( itemId, isRead ) {
return this.markItemsRead( [ itemId ], isRead );
};
/**
* Query the API for unread count of the notifications in this model
*
* @param {string} type Notification type 'message', 'alert' or 'all'.
* @return {jQuery.Promise} jQuery promise that's resolved when the unread count is fetched
* and the badge label is updated.
*/
mw.echo.api.APIHandler.prototype.fetchUnreadCount = null;
/**
* Check whether the model has an API error state flagged
*
* @param {string} type Notification type, 'alert', 'message' or 'all'
* @return {boolean} The model is in API error state
*/
mw.echo.api.APIHandler.prototype.isFetchingErrorState = function ( type, sources ) {
var fetchingSource = 'local';
if ( !this.isSourceLocal( sources ) ) {
fetchingSource = 'foreign';
}
return !!( this.apiErrorState[ type ] && this.apiErrorState[ type ][ fetchingSource ] );
};
/**
* Return the fetch notifications promise
*
* @param {string} type Notification type, 'alert', 'message' or 'all'
* @param {string|string[]} [sources] A name of a source or an array of sources to query
* @param {Object} [overrideParams] An object defining parameters to override in the API
* fetching call.
* @return {jQuery.Promise} Promise that is resolved when notifications are
* fetched from the API.
*/
mw.echo.api.APIHandler.prototype.getFetchNotificationPromise = function ( type, sources, overrideParams ) {
var fetchingSource = 'local';
if ( !this.isSourceLocal( sources ) ) {
fetchingSource = 'foreign';
}
if ( overrideParams || !this.fetchNotificationsPromise[ type ] || !this.fetchNotificationsPromise[ type ][ fetchingSource ] ) {
this.createNewFetchNotificationPromise( type, sources, overrideParams );
}
return this.fetchNotificationsPromise[ type ][ fetchingSource ];
};
/**
* Get the extra parameters for fetching notifications for a given
* notification type.
*
* @param {string} type Notification type
* @return {Object} Extra API parameters for fetch notifications
*/
mw.echo.api.APIHandler.prototype.getTypeParams = function ( type ) {
return this.typeParams[ type ];
};
} )( mediaWiki, jQuery );