2018-11-12 13:56:38 +00:00
|
|
|
( function () {
|
2016-11-18 21:16:43 +00:00
|
|
|
/* global moment:false */
|
2016-04-10 13:31:02 +00:00
|
|
|
/**
|
|
|
|
* Controller for Echo notifications
|
|
|
|
*
|
|
|
|
* @param {mw.echo.api.EchoApi} echoApi Echo API
|
|
|
|
* @param {mw.echo.dm.ModelManager} manager Model manager
|
|
|
|
*/
|
2016-11-18 21:16:43 +00:00
|
|
|
mw.echo.Controller = function MwEchoController( echoApi, manager ) {
|
2016-04-10 13:31:02 +00:00
|
|
|
this.api = echoApi;
|
|
|
|
this.manager = manager;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Initialization */
|
2016-11-18 21:16:43 +00:00
|
|
|
|
2016-04-10 13:31:02 +00:00
|
|
|
OO.initClass( mw.echo.Controller );
|
|
|
|
|
2016-05-20 04:22:41 +00:00
|
|
|
/**
|
2016-05-31 22:32:16 +00:00
|
|
|
* Update a filter value.
|
|
|
|
* The method accepts a filter name and as many arguments
|
|
|
|
* as needed.
|
2016-05-20 04:22:41 +00:00
|
|
|
*
|
|
|
|
* @param {string} filter Filter name
|
|
|
|
*/
|
2016-05-31 22:32:16 +00:00
|
|
|
mw.echo.Controller.prototype.setFilter = function ( filter ) {
|
2024-06-03 12:22:48 +00:00
|
|
|
const filtersModel = this.manager.getFiltersModel(),
|
2016-05-31 22:32:16 +00:00
|
|
|
values = Array.prototype.slice.call( arguments );
|
|
|
|
|
|
|
|
values.shift();
|
|
|
|
|
2016-05-20 04:22:41 +00:00
|
|
|
if ( filter === 'readState' ) {
|
2016-05-31 22:32:16 +00:00
|
|
|
filtersModel.setReadState( values[ 0 ] );
|
|
|
|
} else if ( filter === 'sourcePage' ) {
|
|
|
|
filtersModel.setCurrentSourcePage( values[ 0 ], values[ 1 ] );
|
2016-06-29 23:36:03 +00:00
|
|
|
this.manager.getLocalCounter().setSource( filtersModel.getSourcePagesModel().getCurrentSource() );
|
2016-05-20 04:22:41 +00:00
|
|
|
}
|
2016-05-31 22:32:16 +00:00
|
|
|
|
|
|
|
// Reset pagination
|
|
|
|
this.manager.getPaginationModel().reset();
|
2016-05-20 04:22:41 +00:00
|
|
|
};
|
|
|
|
|
2016-03-16 22:47:20 +00:00
|
|
|
/**
|
|
|
|
* Fetch the next page by date
|
|
|
|
*
|
|
|
|
* @return {jQuery.Promise} A promise that resolves with an object where the keys are
|
|
|
|
* days and the items are item IDs.
|
|
|
|
*/
|
|
|
|
mw.echo.Controller.prototype.fetchNextPageByDate = function () {
|
|
|
|
this.manager.getPaginationModel().forwards();
|
|
|
|
return this.fetchLocalNotificationsByDate();
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fetch the previous page by date
|
|
|
|
*
|
|
|
|
* @return {jQuery.Promise} A promise that resolves with an object where the keys are
|
|
|
|
* days and the items are item IDs.
|
|
|
|
*/
|
|
|
|
mw.echo.Controller.prototype.fetchPrevPageByDate = function () {
|
|
|
|
this.manager.getPaginationModel().backwards();
|
|
|
|
return this.fetchLocalNotificationsByDate();
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fetch the first page by date
|
|
|
|
*
|
|
|
|
* @return {jQuery.Promise} A promise that resolves with an object where the keys are
|
|
|
|
* days and the items are item IDs.
|
|
|
|
*/
|
|
|
|
mw.echo.Controller.prototype.fetchFirstPageByDate = function () {
|
|
|
|
this.manager.getPaginationModel().setCurrPageIndex( 0 );
|
|
|
|
return this.fetchLocalNotificationsByDate();
|
|
|
|
};
|
|
|
|
|
2016-05-31 22:32:16 +00:00
|
|
|
/**
|
|
|
|
* Fetch unread pages in all wikis and create foreign API sources
|
|
|
|
* as needed.
|
|
|
|
*
|
|
|
|
* @return {jQuery.Promise} A promise that resolves when the page filter
|
|
|
|
* model is updated with the unread notification count per page per wiki
|
|
|
|
*/
|
|
|
|
mw.echo.Controller.prototype.fetchUnreadPagesByWiki = function () {
|
2024-06-03 13:48:16 +00:00
|
|
|
const filterModel = this.manager.getFiltersModel(),
|
2016-05-31 22:32:16 +00:00
|
|
|
sourcePageModel = filterModel.getSourcePagesModel();
|
|
|
|
|
|
|
|
return this.api.fetchUnreadNotificationPages()
|
2024-06-03 12:26:18 +00:00
|
|
|
.then( ( data ) => {
|
2024-06-03 12:22:48 +00:00
|
|
|
const result = {},
|
2016-05-31 22:32:16 +00:00
|
|
|
foreignSources = {};
|
|
|
|
|
2024-06-03 12:22:48 +00:00
|
|
|
for ( const source in data ) {
|
2019-07-04 01:44:14 +00:00
|
|
|
if ( source !== mw.config.get( 'wgWikiID' ) ) {
|
2016-05-31 22:32:16 +00:00
|
|
|
// Collect sources for API
|
|
|
|
foreignSources[ source ] = data[ source ].source;
|
|
|
|
}
|
2019-07-04 01:44:14 +00:00
|
|
|
result[ source === mw.config.get( 'wgWikiID' ) ? 'local' : source ] = data[ source ];
|
2016-05-31 22:32:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Register the foreign sources in the API
|
2024-06-03 13:48:16 +00:00
|
|
|
this.api.registerForeignSources( foreignSources, false );
|
2016-05-31 22:32:16 +00:00
|
|
|
|
|
|
|
// Register pages
|
2016-07-07 19:20:39 +00:00
|
|
|
sourcePageModel.setAllSources( result );
|
2016-05-31 22:32:16 +00:00
|
|
|
} );
|
|
|
|
};
|
|
|
|
|
2016-03-16 22:47:20 +00:00
|
|
|
/**
|
|
|
|
* Fetch notifications from the local API and sort them by date.
|
|
|
|
* This method ignores cross-wiki notifications and bundles.
|
|
|
|
*
|
|
|
|
* @param {number} [page] Page number. If not given, it defaults to the current
|
|
|
|
* page.
|
|
|
|
* @return {jQuery.Promise} A promise that resolves with an object where the keys are
|
|
|
|
* days and the items are item IDs.
|
|
|
|
*/
|
|
|
|
mw.echo.Controller.prototype.fetchLocalNotificationsByDate = function ( page ) {
|
2024-06-03 13:48:16 +00:00
|
|
|
const pagination = this.manager.getPaginationModel(),
|
2016-05-20 04:22:41 +00:00
|
|
|
filters = this.manager.getFiltersModel(),
|
2016-05-31 22:32:16 +00:00
|
|
|
currentSource = filters.getSourcePagesModel().getCurrentSource(),
|
2016-03-16 22:47:20 +00:00
|
|
|
continueValue = pagination.getPageContinue( page || pagination.getCurrPageIndex() );
|
|
|
|
|
2016-05-31 00:12:31 +00:00
|
|
|
pagination.setItemsPerPage( this.api.getLimit() );
|
2016-05-31 22:32:16 +00:00
|
|
|
|
|
|
|
return this.api.fetchFilteredNotifications(
|
2016-03-16 22:47:20 +00:00
|
|
|
this.manager.getTypeString(),
|
2016-05-31 22:32:16 +00:00
|
|
|
currentSource,
|
|
|
|
{
|
2019-02-06 01:42:53 +00:00
|
|
|
continue: continueValue,
|
2016-05-31 22:32:16 +00:00
|
|
|
readState: filters.getReadState(),
|
2016-06-22 22:14:48 +00:00
|
|
|
titles: filters.getSourcePagesModel().getGroupedPagesForCurrentTitle()
|
2016-05-31 22:32:16 +00:00
|
|
|
}
|
2016-03-16 22:47:20 +00:00
|
|
|
)
|
2024-06-03 12:26:18 +00:00
|
|
|
.then( ( data ) => {
|
2024-06-03 12:22:48 +00:00
|
|
|
const dateItemIds = {},
|
2016-03-16 22:47:20 +00:00
|
|
|
dateItems = {},
|
|
|
|
models = {};
|
|
|
|
|
|
|
|
data = data || { list: [] };
|
|
|
|
|
|
|
|
// Go over the data
|
2024-06-03 12:22:48 +00:00
|
|
|
for ( let i = 0; i < data.list.length; i++ ) {
|
|
|
|
const notifData = data.list[ i ];
|
2016-03-16 22:47:20 +00:00
|
|
|
|
2016-07-25 23:03:29 +00:00
|
|
|
// Set source's seenTime
|
|
|
|
// TODO: This query brings up mixed alert and message notifications.
|
|
|
|
// Regularly, each of those will have a different seenTime that is
|
|
|
|
// calculated for each badge, but for this page, both are fetched.
|
|
|
|
// For the moment, we are picking the max seenTime from
|
|
|
|
// either alert or notice and updating both, since the page gives
|
|
|
|
// us a mixed view which will update both seenTime to be the same
|
|
|
|
// anyways.
|
2024-06-03 12:22:48 +00:00
|
|
|
const maxSeenTime = data.seenTime.alert < data.seenTime.notice ?
|
2016-07-25 23:03:29 +00:00
|
|
|
data.seenTime.notice : data.seenTime.alert;
|
2024-06-03 13:48:16 +00:00
|
|
|
this.manager.getSeenTimeModel().setSeenTime(
|
2016-07-25 23:03:29 +00:00
|
|
|
maxSeenTime
|
|
|
|
);
|
|
|
|
|
2016-03-16 22:47:20 +00:00
|
|
|
// Collect common data
|
2024-06-03 13:48:16 +00:00
|
|
|
const newNotifData = this.createNotificationData( notifData );
|
2016-03-16 22:47:20 +00:00
|
|
|
if ( notifData.type !== 'foreign' ) {
|
2024-06-03 12:22:48 +00:00
|
|
|
const localizedDate = moment.utc( newNotifData.timestamp ).local().format( 'YYYYMMDD' );
|
2016-07-22 18:59:10 +00:00
|
|
|
|
|
|
|
newNotifData.modelName = 'local_' + localizedDate;
|
2016-06-08 23:53:20 +00:00
|
|
|
newNotifData.source = currentSource;
|
2016-03-16 22:47:20 +00:00
|
|
|
|
|
|
|
// Single notifications
|
2024-06-03 12:22:48 +00:00
|
|
|
const itemModel = new mw.echo.dm.NotificationItem(
|
2016-03-16 22:47:20 +00:00
|
|
|
notifData.id,
|
|
|
|
newNotifData
|
|
|
|
);
|
|
|
|
|
2016-07-22 18:59:10 +00:00
|
|
|
dateItems[ localizedDate ] = dateItems[ localizedDate ] || [];
|
|
|
|
dateItems[ localizedDate ].push( itemModel );
|
2016-03-16 22:47:20 +00:00
|
|
|
|
2016-07-22 18:59:10 +00:00
|
|
|
dateItemIds[ localizedDate ] = dateItemIds[ localizedDate ] || [];
|
|
|
|
dateItemIds[ localizedDate ].push( notifData.id );
|
2016-03-16 22:47:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fill in the models
|
2024-06-03 12:22:48 +00:00
|
|
|
for ( const date in dateItems ) {
|
|
|
|
const symbolicName = 'local_' + date;
|
2016-03-16 22:47:20 +00:00
|
|
|
|
|
|
|
// Set up model
|
|
|
|
models[ symbolicName ] = new mw.echo.dm.NotificationsList( {
|
2024-06-03 13:48:16 +00:00
|
|
|
type: this.manager.getTypes(),
|
2016-06-08 23:53:20 +00:00
|
|
|
name: symbolicName,
|
|
|
|
source: currentSource,
|
2016-03-16 22:47:20 +00:00
|
|
|
title: date,
|
Fix fade-in/out animation in sorting
The fade in/out animation is asynchronous. This means that if we are
sorting multiple items one after the other, by the time the item faded
out, it will be reinserted back into the wrong position, breaking the
sorting.
This also broke the promise of OO.SortedEmitterList whereby all its items
are always in order.
The way to fix this was to force a better synchronization with the item
order while we hide and show the item in its new place. To do that,
a new widget is created as a fake clone of the old one, in the original
position of the old one. The original item is then reinserted (while hidden)
to the proper location -- preserving order. The fake clone is then faded
out, and the real item is then faded in.
For this to work properly, the cloned item had to preserve some of the
original item's information, like timestamp, foreigness and id. However,
since both the real item and the fake new clone have the same details,
the clone fakes its ID by adding a fraction to it - promising that the
fallback in case of equal timestamps (which happens on the real and
cloned items) will still resolve with some decision about the placement
of the items rather than (falsely but understandably) decide they are
both the same.
Since this whole animation is somewhat of a hack, the list now has a
configuration parameter to turn the animation on.
The animation is on in the popups, but off in the special page.
Bug: T141419
Change-Id: Ic7c35e5ddefc51bf7fde497eab36414b4dddcd9e
2016-07-29 23:35:29 +00:00
|
|
|
timestamp: date,
|
|
|
|
sortingCallback: function ( a, b ) {
|
|
|
|
// Reverse sorting. In the special page we want the
|
|
|
|
// items sorted only by timestamp, regardless of
|
|
|
|
// read/unread state
|
|
|
|
if ( b.getTimestamp() < a.getTimestamp() ) {
|
|
|
|
return -1;
|
|
|
|
} else if ( b.getTimestamp() > a.getTimestamp() ) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fallback on IDs
|
|
|
|
return b.getId() - a.getId();
|
|
|
|
}
|
2016-03-16 22:47:20 +00:00
|
|
|
} );
|
|
|
|
models[ symbolicName ].setItems( dateItems[ date ] );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Register local sources
|
2024-06-03 13:48:16 +00:00
|
|
|
this.api.registerLocalSources( Object.keys( models ) );
|
2016-03-16 22:47:20 +00:00
|
|
|
|
|
|
|
// Update the manager
|
2024-06-03 13:48:16 +00:00
|
|
|
this.manager.setNotificationModels( models );
|
2016-03-16 22:47:20 +00:00
|
|
|
|
|
|
|
// Update the pagination
|
2016-05-31 00:12:31 +00:00
|
|
|
pagination.setNextPageContinue( data.continue );
|
2016-03-16 22:47:20 +00:00
|
|
|
|
2016-06-29 23:36:03 +00:00
|
|
|
// Update the local counter
|
2024-06-03 13:48:16 +00:00
|
|
|
this.manager.getLocalCounter().update();
|
2016-06-29 23:36:03 +00:00
|
|
|
|
2016-03-16 22:47:20 +00:00
|
|
|
return dateItemIds;
|
2016-08-29 19:56:56 +00:00
|
|
|
} )
|
|
|
|
.then(
|
|
|
|
null,
|
2024-06-03 12:26:18 +00:00
|
|
|
( errCode, errObj ) => ( {
|
|
|
|
errCode: errCode,
|
|
|
|
errInfo: OO.getProp( errObj, 'error', 'info' )
|
|
|
|
} )
|
2016-08-29 19:56:56 +00:00
|
|
|
);
|
2016-03-16 22:47:20 +00:00
|
|
|
};
|
2016-04-10 13:31:02 +00:00
|
|
|
/**
|
|
|
|
* Fetch notifications from the local API and update the notifications list.
|
|
|
|
*
|
|
|
|
* @param {boolean} [isForced] Force a renewed fetching promise. If set to false, the
|
|
|
|
* model will request the stored/cached fetching promise from the API. A 'true' value
|
|
|
|
* will force the API to re-request that information from the server and update the
|
|
|
|
* notifications.
|
|
|
|
* @return {jQuery.Promise} A promise that resolves with an array of notification IDs
|
|
|
|
*/
|
|
|
|
mw.echo.Controller.prototype.fetchLocalNotifications = function ( isForced ) {
|
2024-06-03 13:48:16 +00:00
|
|
|
// Create a new local list model
|
|
|
|
const localListModel = new mw.echo.dm.NotificationsList( {
|
2016-04-10 13:31:02 +00:00
|
|
|
type: this.manager.getTypes()
|
|
|
|
} ),
|
|
|
|
localItems = [],
|
|
|
|
idArray = [];
|
|
|
|
|
|
|
|
this.manager.counter.update();
|
|
|
|
|
|
|
|
// Fetch the notifications from the database
|
|
|
|
// Initially, we're going to have to split the operation
|
|
|
|
// between local notifications and x-wiki notifications
|
|
|
|
// until the backend gives us the x-wiki notifications as
|
|
|
|
// part of the original response.
|
2016-08-04 14:42:11 +00:00
|
|
|
return this.api.fetchNotifications( this.manager.getTypeString(), 'local', !!isForced, { unreadFirst: true, bundle: true } /* filters */ )
|
2016-04-10 13:31:02 +00:00
|
|
|
.then(
|
|
|
|
// Success
|
2024-06-03 12:26:18 +00:00
|
|
|
( data ) => {
|
2024-06-03 12:22:48 +00:00
|
|
|
const allModels = { local: localListModel },
|
2024-06-13 14:59:19 +00:00
|
|
|
createBundledNotification = ( modelName, rawBundledNotifData ) => {
|
2024-06-03 13:48:16 +00:00
|
|
|
const bundleNotifData = this.createNotificationData( rawBundledNotifData );
|
2016-06-07 20:08:16 +00:00
|
|
|
bundleNotifData.bundled = true;
|
|
|
|
bundleNotifData.modelName = modelName;
|
|
|
|
return new mw.echo.dm.NotificationItem(
|
|
|
|
rawBundledNotifData.id,
|
|
|
|
bundleNotifData
|
|
|
|
);
|
|
|
|
};
|
2016-04-10 13:31:02 +00:00
|
|
|
|
|
|
|
data = data || { list: [] };
|
|
|
|
|
|
|
|
// Go over the data
|
2024-06-03 12:22:48 +00:00
|
|
|
for ( let i = 0; i < data.list.length; i++ ) {
|
|
|
|
const notifData = data.list[ i ];
|
2016-04-10 13:31:02 +00:00
|
|
|
|
2016-08-15 23:53:09 +00:00
|
|
|
// Set source's seenTime
|
2024-06-03 13:48:16 +00:00
|
|
|
this.manager.getSeenTimeModel().setSeenTime(
|
|
|
|
this.getTypes().length > 1 ?
|
2016-08-15 23:53:09 +00:00
|
|
|
(
|
|
|
|
data.seenTime.alert < data.seenTime.notice ?
|
|
|
|
data.seenTime.notice : data.seenTime.alert
|
|
|
|
) :
|
2024-06-03 13:48:16 +00:00
|
|
|
data.seenTime[ this.getTypeString() ]
|
2016-08-15 23:53:09 +00:00
|
|
|
);
|
|
|
|
|
2016-04-10 13:31:02 +00:00
|
|
|
// Collect common data
|
2024-06-03 13:48:16 +00:00
|
|
|
const newNotifData = this.createNotificationData( notifData );
|
2016-04-10 13:31:02 +00:00
|
|
|
if ( notifData.type === 'foreign' ) {
|
|
|
|
// x-wiki notification multi-group
|
|
|
|
// We need to request a new list model
|
2016-06-08 23:53:20 +00:00
|
|
|
newNotifData.name = 'xwiki';
|
2024-06-03 12:22:48 +00:00
|
|
|
const foreignListModel = allModels.xwiki = new mw.echo.dm.CrossWikiNotificationItem( notifData.id, newNotifData );
|
2016-04-10 13:31:02 +00:00
|
|
|
foreignListModel.setForeign( true );
|
|
|
|
|
|
|
|
// Register foreign sources
|
2024-06-03 13:48:16 +00:00
|
|
|
this.api.registerForeignSources( notifData.sources, true );
|
2016-04-10 13:31:02 +00:00
|
|
|
// Add the lists according to the sources
|
2024-06-03 12:22:48 +00:00
|
|
|
for ( const source in notifData.sources ) {
|
2016-04-10 13:31:02 +00:00
|
|
|
foreignListModel.getList().addGroup(
|
|
|
|
source,
|
|
|
|
notifData.sources[ source ]
|
|
|
|
);
|
|
|
|
}
|
2016-09-19 22:53:01 +00:00
|
|
|
|
|
|
|
} else if ( Array.isArray( newNotifData.bundledNotifications ) ) {
|
2016-06-07 20:08:16 +00:00
|
|
|
// local bundle
|
|
|
|
newNotifData.modelName = 'bundle_' + notifData.id;
|
2024-06-03 12:21:23 +00:00
|
|
|
const itemModel = new mw.echo.dm.BundleNotificationItem(
|
2016-06-07 20:08:16 +00:00
|
|
|
notifData.id,
|
|
|
|
newNotifData.bundledNotifications.map( createBundledNotification.bind( null, newNotifData.modelName ) ),
|
|
|
|
newNotifData
|
|
|
|
);
|
|
|
|
allModels[ newNotifData.modelName ] = itemModel;
|
2016-04-10 13:31:02 +00:00
|
|
|
} else {
|
|
|
|
// Local single notifications
|
2024-06-03 12:21:23 +00:00
|
|
|
const itemModel = new mw.echo.dm.NotificationItem(
|
2016-04-10 13:31:02 +00:00
|
|
|
notifData.id,
|
|
|
|
newNotifData
|
|
|
|
);
|
|
|
|
|
|
|
|
idArray.push( notifData.id );
|
|
|
|
localItems.push( itemModel );
|
2016-09-19 22:53:01 +00:00
|
|
|
|
|
|
|
if ( newNotifData.bundledNotifications ) {
|
|
|
|
// This means that bundledNotifications is truthy
|
|
|
|
// but is not an array. We should log this in the console
|
|
|
|
mw.log.warn(
|
|
|
|
'newNotifData.bundledNotifications is expected to be an array,' +
|
2019-02-06 01:42:53 +00:00
|
|
|
'but instead received "' + typeof newNotifData.bundledNotifications + '"'
|
2016-09-19 22:53:01 +00:00
|
|
|
);
|
|
|
|
}
|
2016-04-10 13:31:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Refresh local items
|
|
|
|
localListModel.addItems( localItems );
|
|
|
|
|
2024-06-03 13:48:16 +00:00
|
|
|
// Update the this
|
|
|
|
this.manager.setNotificationModels( allModels );
|
2016-04-10 13:31:02 +00:00
|
|
|
|
|
|
|
return idArray;
|
|
|
|
},
|
|
|
|
// Failure
|
2024-06-03 12:26:18 +00:00
|
|
|
( errCode, errObj ) => {
|
2024-06-03 13:48:16 +00:00
|
|
|
if ( !this.manager.getNotificationModel( 'local' ) ) {
|
|
|
|
// Update the this
|
|
|
|
this.manager.setNotificationModels( { local: localListModel } );
|
2016-04-10 13:31:02 +00:00
|
|
|
}
|
|
|
|
return {
|
|
|
|
errCode: errCode,
|
|
|
|
errInfo: OO.getProp( errObj, 'error', 'info' )
|
|
|
|
};
|
|
|
|
}
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create notification data config object for notification items from the
|
|
|
|
* given API data.
|
|
|
|
*
|
|
|
|
* @param {Object} apiData API data
|
|
|
|
* @return {Object} Notification config data object
|
|
|
|
*/
|
|
|
|
mw.echo.Controller.prototype.createNotificationData = function ( apiData ) {
|
2024-06-03 12:22:48 +00:00
|
|
|
const content = apiData[ '*' ] || {};
|
2016-07-22 18:59:10 +00:00
|
|
|
|
2024-06-03 12:22:48 +00:00
|
|
|
let utcTimestamp;
|
2016-07-22 18:59:10 +00:00
|
|
|
if ( apiData.timestamp.utciso8601 ) {
|
|
|
|
utcTimestamp = apiData.timestamp.utciso8601;
|
|
|
|
} else {
|
|
|
|
// Temporary until c05133283af0486e08c9a97a468bc075e238f2d2 rolls out to the
|
|
|
|
// whole WMF cluster
|
2024-06-03 12:22:48 +00:00
|
|
|
const utcIsoMoment = moment.utc( apiData.timestamp.utcunix * 1000 );
|
2016-07-22 18:59:10 +00:00
|
|
|
utcTimestamp = utcIsoMoment.format( 'YYYY-MM-DD[T]HH:mm:ss[Z]' );
|
|
|
|
}
|
2016-04-10 13:31:02 +00:00
|
|
|
|
|
|
|
return {
|
2016-07-07 00:15:47 +00:00
|
|
|
type: apiData.section,
|
2016-04-10 13:31:02 +00:00
|
|
|
foreign: false,
|
|
|
|
source: 'local',
|
|
|
|
count: apiData.count,
|
|
|
|
read: !!apiData.read,
|
2016-07-25 23:03:29 +00:00
|
|
|
seen: (
|
|
|
|
!!apiData.read ||
|
2016-09-13 23:18:29 +00:00
|
|
|
utcTimestamp <= this.manager.getSeenTime()
|
2016-07-25 23:03:29 +00:00
|
|
|
),
|
2016-07-22 18:59:10 +00:00
|
|
|
timestamp: utcTimestamp,
|
2016-04-10 13:31:02 +00:00
|
|
|
category: apiData.category,
|
|
|
|
content: {
|
|
|
|
header: content.header,
|
2016-06-07 20:08:16 +00:00
|
|
|
compactHeader: content.compactHeader,
|
2016-04-10 13:31:02 +00:00
|
|
|
body: content.body
|
|
|
|
},
|
2024-08-16 22:09:41 +00:00
|
|
|
iconUrl: content.iconUrl,
|
2016-04-10 13:31:02 +00:00
|
|
|
iconType: content.icon,
|
|
|
|
primaryUrl: OO.getProp( content.links, 'primary', 'url' ),
|
2016-06-02 14:00:48 +00:00
|
|
|
secondaryUrls: OO.getProp( content.links, 'secondary' ) || [],
|
2016-06-07 20:08:16 +00:00
|
|
|
bundledIds: apiData.bundledIds,
|
|
|
|
bundledNotifications: apiData.bundledNotifications
|
2016-04-10 13:31:02 +00:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Mark all items within a given list model as read.
|
|
|
|
*
|
|
|
|
* NOTE: This method is strictly for list models, and will not work for
|
|
|
|
* group list models. To mark items as read in the xwiki model, whether
|
|
|
|
* it is pre-populated or not, please see #markEntireCrossWikiItemAsRead
|
|
|
|
*
|
|
|
|
* @param {string} [modelName] Symbolic name for the model
|
2016-06-07 20:08:16 +00:00
|
|
|
* @param {boolean} [isRead=true]
|
2016-04-10 13:31:02 +00:00
|
|
|
* @return {jQuery.Promise} Promise that is resolved when all items
|
|
|
|
* were marked as read.
|
|
|
|
*/
|
2016-06-07 20:08:16 +00:00
|
|
|
mw.echo.Controller.prototype.markEntireListModelRead = function ( modelName, isRead ) {
|
2024-06-03 12:22:48 +00:00
|
|
|
const itemIds = [],
|
2016-03-16 22:47:20 +00:00
|
|
|
model = this.manager.getNotificationModel( modelName || 'local' );
|
2016-04-10 13:31:02 +00:00
|
|
|
|
|
|
|
if ( !model ) {
|
|
|
|
// Model doesn't exist
|
|
|
|
return $.Deferred().reject();
|
|
|
|
}
|
|
|
|
|
2016-06-07 20:08:16 +00:00
|
|
|
// Default to true
|
|
|
|
isRead = isRead === undefined ? true : isRead;
|
|
|
|
|
2024-06-03 12:22:48 +00:00
|
|
|
const items = model.getItems();
|
|
|
|
for ( let i = 0; i < items.length; i++ ) {
|
|
|
|
const item = items[ i ];
|
2016-06-07 20:08:16 +00:00
|
|
|
if ( item.isRead() !== isRead ) {
|
2016-06-21 12:27:39 +00:00
|
|
|
itemIds.push( item.getId() );
|
2016-04-10 13:31:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-07 20:08:16 +00:00
|
|
|
return this.markItemsRead( itemIds, model.getName(), isRead );
|
|
|
|
};
|
|
|
|
|
2016-06-29 23:36:03 +00:00
|
|
|
/**
|
|
|
|
* Mark all notifications of a certain source as read, even those that
|
|
|
|
* are not currently displayed.
|
|
|
|
*
|
|
|
|
* @param {string} [source] Notification source. If not given, the currently
|
|
|
|
* selected source is used.
|
|
|
|
* @return {jQuery.Promise} A promise that is resolved after
|
|
|
|
* all notifications for the given source were marked as read
|
|
|
|
*/
|
|
|
|
mw.echo.Controller.prototype.markAllRead = function ( source ) {
|
2024-06-03 13:48:16 +00:00
|
|
|
const itemIds = [],
|
2016-06-29 23:36:03 +00:00
|
|
|
readState = this.manager.getFiltersModel().getReadState(),
|
|
|
|
localCounter = this.manager.getLocalCounter();
|
|
|
|
|
|
|
|
source = source || this.manager.getFiltersModel().getSourcePagesModel().getCurrentSource();
|
|
|
|
|
2024-06-03 12:26:18 +00:00
|
|
|
this.manager.getNotificationsBySource( source ).forEach( ( notification ) => {
|
2016-06-29 23:36:03 +00:00
|
|
|
if ( !notification.isRead() ) {
|
2024-06-03 12:21:23 +00:00
|
|
|
itemIds.push( ...notification.getAllIds() );
|
2016-06-29 23:36:03 +00:00
|
|
|
notification.toggleRead( true );
|
|
|
|
|
|
|
|
if ( readState === 'unread' ) {
|
|
|
|
// Remove the items if we are in 'unread' filter state
|
2024-06-03 13:48:16 +00:00
|
|
|
const model = this.manager.getNotificationModel( notification.getModelName() );
|
2016-06-29 23:36:03 +00:00
|
|
|
model.discardItems( notification );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
|
|
|
|
// Update pagination count
|
|
|
|
this.manager.updateCurrentPageItemCount();
|
|
|
|
|
|
|
|
localCounter.estimateChange( -itemIds.length );
|
|
|
|
return this.api.markAllRead(
|
2017-10-17 12:53:08 +00:00
|
|
|
source,
|
|
|
|
this.getTypes()
|
|
|
|
).then(
|
|
|
|
this.refreshUnreadCount.bind( this )
|
|
|
|
).then(
|
|
|
|
localCounter.update.bind( localCounter, true )
|
|
|
|
);
|
2016-06-29 23:36:03 +00:00
|
|
|
};
|
|
|
|
|
2016-06-07 20:08:16 +00:00
|
|
|
/**
|
|
|
|
* Mark all local notifications as read
|
|
|
|
*
|
|
|
|
* @return {jQuery.Promise} Promise that is resolved when all
|
|
|
|
* local notifications have been marked as read.
|
|
|
|
*/
|
|
|
|
mw.echo.Controller.prototype.markLocalNotificationsRead = function () {
|
2024-06-03 12:22:48 +00:00
|
|
|
const readState = this.manager.getFiltersModel().getReadState(),
|
2016-06-29 23:36:03 +00:00
|
|
|
modelItems = {};
|
2016-06-07 20:08:16 +00:00
|
|
|
|
2024-06-03 12:26:18 +00:00
|
|
|
this.manager.getLocalNotifications().forEach( ( notification ) => {
|
2016-06-07 20:08:16 +00:00
|
|
|
if ( !notification.isRead() ) {
|
|
|
|
notification.toggleRead( true );
|
2016-06-29 23:36:03 +00:00
|
|
|
|
2024-06-03 12:22:48 +00:00
|
|
|
const modelName = notification.getModelName();
|
2016-06-29 23:36:03 +00:00
|
|
|
modelItems[ modelName ] = modelItems[ modelName ] || [];
|
|
|
|
modelItems[ modelName ].push( notification );
|
2016-06-07 20:08:16 +00:00
|
|
|
}
|
|
|
|
} );
|
|
|
|
|
2016-06-29 23:36:03 +00:00
|
|
|
// Remove the items if we are in 'unread' filter state
|
|
|
|
if ( readState === 'unread' ) {
|
2024-06-03 12:22:48 +00:00
|
|
|
for ( const name in modelItems ) {
|
|
|
|
const model = this.manager.getNotificationModel( name );
|
2024-03-05 22:39:09 +00:00
|
|
|
model.discardItems( modelItems[ name ] );
|
2016-06-29 23:36:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update pagination count
|
|
|
|
this.manager.updateCurrentPageItemCount();
|
|
|
|
|
2019-12-03 17:00:16 +00:00
|
|
|
this.manager.getLocalCounter().setCount( 0, false );
|
|
|
|
return this.api.markAllRead( 'local', this.getTypeString() ).then( this.refreshUnreadCount.bind( this ) );
|
2016-04-10 13:31:02 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fetch notifications from the cross-wiki sources.
|
|
|
|
*
|
|
|
|
* @return {jQuery.Promise} Promise that is resolved when all items
|
|
|
|
* from the cross-wiki sources are populated into the cross-wiki
|
|
|
|
* model.
|
|
|
|
*/
|
|
|
|
mw.echo.Controller.prototype.fetchCrossWikiNotifications = function () {
|
2024-06-03 13:48:16 +00:00
|
|
|
const xwikiModel = this.manager.getNotificationModel( 'xwiki' );
|
2016-04-10 13:31:02 +00:00
|
|
|
|
|
|
|
if ( !xwikiModel ) {
|
|
|
|
// There is no xwiki notifications model, so we can't
|
|
|
|
// fetch into it
|
|
|
|
return $.Deferred().reject().promise();
|
|
|
|
}
|
|
|
|
|
2016-08-04 14:42:11 +00:00
|
|
|
return this.api.fetchNotificationGroups( xwikiModel.getSourceNames(), this.manager.getTypeString(), true )
|
2016-04-10 13:31:02 +00:00
|
|
|
.then(
|
2024-06-03 12:26:18 +00:00
|
|
|
( groupList ) => {
|
2024-06-03 12:22:48 +00:00
|
|
|
for ( const group in groupList ) {
|
|
|
|
const listModel = xwikiModel.getItemBySource( group );
|
|
|
|
const groupItems = groupList[ group ];
|
2016-04-10 13:31:02 +00:00
|
|
|
|
2024-06-03 12:21:23 +00:00
|
|
|
const items = [];
|
2024-06-03 12:22:48 +00:00
|
|
|
for ( let i = 0; i < groupItems.length; i++ ) {
|
2024-06-03 13:48:16 +00:00
|
|
|
const notifData = this.createNotificationData( groupItems[ i ] );
|
2016-04-10 13:31:02 +00:00
|
|
|
items.push(
|
2024-06-13 14:34:07 +00:00
|
|
|
new mw.echo.dm.NotificationItem( groupItems[ i ].id, Object.assign( notifData, {
|
2016-05-31 22:32:16 +00:00
|
|
|
modelName: 'xwiki',
|
2016-04-10 13:31:02 +00:00
|
|
|
source: group,
|
2016-05-19 20:55:21 +00:00
|
|
|
bundled: true,
|
2016-04-10 13:31:02 +00:00
|
|
|
foreign: true
|
|
|
|
} ) )
|
|
|
|
);
|
|
|
|
}
|
|
|
|
// Add items
|
|
|
|
listModel.setItems( items );
|
|
|
|
}
|
|
|
|
},
|
2024-06-03 12:26:18 +00:00
|
|
|
( errCode, errObj ) => ( {
|
|
|
|
errCode: errCode,
|
|
|
|
errInfo: errCode === 'http' ?
|
|
|
|
mw.msg( 'echo-api-failure-cross-wiki' ) :
|
|
|
|
OO.getProp( errObj, 'error', 'info' )
|
|
|
|
} )
|
2016-04-10 13:31:02 +00:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Mark local items as read in the API.
|
|
|
|
*
|
|
|
|
* @param {string[]|string} itemIds An array of item IDs, or a single item ID, to mark as read
|
2016-06-08 23:53:20 +00:00
|
|
|
* @param {string} modelName The name of the model that these items belong to
|
2016-04-10 13:31:02 +00:00
|
|
|
* @param {boolean} [isRead=true] The read state of the item; true for marking the
|
|
|
|
* item as read, false for marking the item as unread
|
|
|
|
* @return {jQuery.Promise} A promise that is resolved when the operation
|
|
|
|
* is complete, with the number of unread notifications still remaining
|
|
|
|
* for the set type of this controller, in the given source.
|
|
|
|
*/
|
2016-06-08 23:53:20 +00:00
|
|
|
mw.echo.Controller.prototype.markItemsRead = function ( itemIds, modelName, isRead ) {
|
2024-06-03 12:21:23 +00:00
|
|
|
const model = this.manager.getNotificationModel( modelName ),
|
2016-07-14 00:03:57 +00:00
|
|
|
readState = this.manager.getFiltersModel().getReadState(),
|
2016-06-07 20:08:16 +00:00
|
|
|
allIds = [];
|
2016-06-08 23:53:20 +00:00
|
|
|
|
2016-04-10 13:31:02 +00:00
|
|
|
itemIds = Array.isArray( itemIds ) ? itemIds : [ itemIds ];
|
|
|
|
|
|
|
|
// Default to true
|
|
|
|
isRead = isRead === undefined ? true : isRead;
|
|
|
|
|
2024-06-03 12:22:48 +00:00
|
|
|
const items = model.findByIds( itemIds );
|
2016-07-14 00:03:57 +00:00
|
|
|
|
|
|
|
// If we are only looking at specific read state,
|
|
|
|
// then we need to make sure the items are removed
|
|
|
|
// from the visible list, because they no longer
|
|
|
|
// correspond with the chosen state filter
|
|
|
|
if ( readState === 'read' && !isRead ) {
|
|
|
|
model.discardItems( items );
|
|
|
|
} else if ( readState === 'unread' && isRead ) {
|
|
|
|
model.discardItems( items );
|
|
|
|
// TODO: We should also find a way to update the pagination
|
|
|
|
// here properly. Do we pull more items from the next page
|
|
|
|
// when items are cleared? Do we set some threshhold for
|
|
|
|
// removed items where if it is reached, we update the list
|
|
|
|
// to reflect the new pagination? etc.
|
|
|
|
}
|
|
|
|
|
2024-06-03 12:26:18 +00:00
|
|
|
items.forEach( ( notification ) => {
|
2024-06-03 12:21:23 +00:00
|
|
|
allIds.push( ...notification.getAllIds() );
|
2016-07-14 00:03:57 +00:00
|
|
|
if ( readState === 'all' ) {
|
|
|
|
notification.toggleRead( isRead );
|
|
|
|
}
|
2016-04-10 13:31:02 +00:00
|
|
|
} );
|
|
|
|
|
2016-07-14 00:03:57 +00:00
|
|
|
// Update pagination count
|
|
|
|
this.manager.updateCurrentPageItemCount();
|
|
|
|
|
2016-03-04 19:23:02 +00:00
|
|
|
this.manager.getUnreadCounter().estimateChange( isRead ? -allIds.length : allIds.length );
|
2016-06-29 23:36:03 +00:00
|
|
|
if ( modelName !== 'xwiki' ) {
|
|
|
|
// For the local counter, we should only estimate the change if the items
|
|
|
|
// are not cross-wiki
|
|
|
|
this.manager.getLocalCounter().estimateChange( isRead ? -allIds.length : allIds.length );
|
|
|
|
}
|
2016-04-10 13:31:02 +00:00
|
|
|
|
2016-06-08 23:53:20 +00:00
|
|
|
return this.api.markItemsRead( allIds, model.getSource(), isRead ).then( this.refreshUnreadCount.bind( this ) );
|
2016-04-10 13:31:02 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Mark cross-wiki items as read in the API.
|
|
|
|
*
|
|
|
|
* @param {string[]|string} itemIds An array of item IDs, or a single item ID, to mark as read
|
2016-07-04 14:00:59 +00:00
|
|
|
* @param {string} source The name for the source list that these items belong to
|
2016-04-10 13:31:02 +00:00
|
|
|
* @return {jQuery.Promise} A promise that is resolved when the operation
|
|
|
|
* is complete, with the number of unread notifications still remaining
|
|
|
|
* for the set type of this controller, in the given source.
|
|
|
|
*/
|
2016-07-04 14:00:59 +00:00
|
|
|
mw.echo.Controller.prototype.markCrossWikiItemsRead = function ( itemIds, source ) {
|
2024-06-03 12:21:23 +00:00
|
|
|
const allIds = [],
|
2016-03-16 22:47:20 +00:00
|
|
|
xwikiModel = this.manager.getNotificationModel( 'xwiki' );
|
2016-04-10 13:31:02 +00:00
|
|
|
|
|
|
|
if ( !xwikiModel ) {
|
|
|
|
return $.Deferred().reject().promise();
|
|
|
|
}
|
|
|
|
itemIds = Array.isArray( itemIds ) ? itemIds : [ itemIds ];
|
|
|
|
|
2024-06-03 12:22:48 +00:00
|
|
|
const sourceModel = xwikiModel.getList().getGroupByName( source );
|
|
|
|
const notifs = sourceModel.findByIds( itemIds );
|
2016-05-19 20:55:21 +00:00
|
|
|
sourceModel.discardItems( notifs );
|
2016-06-29 23:36:03 +00:00
|
|
|
// Update pagination count
|
|
|
|
this.manager.updateCurrentPageItemCount();
|
2016-04-10 13:31:02 +00:00
|
|
|
|
2024-06-03 12:26:18 +00:00
|
|
|
notifs.forEach( ( notif ) => {
|
2024-06-03 12:21:23 +00:00
|
|
|
allIds.push( ...notif.getAllIds() );
|
2016-08-03 20:33:48 +00:00
|
|
|
} );
|
2016-12-08 20:50:03 +00:00
|
|
|
this.manager.getUnreadCounter().estimateChange( -allIds.length );
|
2016-08-03 20:33:48 +00:00
|
|
|
return this.api.markItemsRead( allIds, source, true )
|
2016-04-10 13:31:02 +00:00
|
|
|
.then( this.refreshUnreadCount.bind( this ) );
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Mark all cross-wiki notifications from all sources as read
|
|
|
|
*
|
|
|
|
* @return {jQuery.Promise} Promise that is resolved when all notifications
|
|
|
|
* are marked as read
|
|
|
|
*/
|
|
|
|
mw.echo.Controller.prototype.markEntireCrossWikiItemAsRead = function () {
|
2024-06-03 13:48:16 +00:00
|
|
|
const xwikiModel = this.manager.getNotificationModel( 'xwiki' );
|
2016-04-10 13:31:02 +00:00
|
|
|
|
|
|
|
if ( !xwikiModel ) {
|
|
|
|
return $.Deferred().reject().promise();
|
|
|
|
}
|
|
|
|
|
Skip redundant bundle-id expansion when marking entire xwiki bundle read
When we mark an entire bundle as read, we don't care if it was opened
before or not - we call the API for the list of sources and their items
and build a list of IDs to mark as read in the remote source.
Then, we make sure that bundleIDs are expanded, added to that list.
Previously, we then sent those IDs to 'markCrossWikiItemsRead'
which marked the item IDs in the remote wikis as read.
markCrossWikiItemsRead is also expanding item bundle
IDs (correctly, because we also use it when we mark individual
xwiki items, and they should be expanded)
However, in cases where we mark individual xwiki items, the
model list is already filled, so markCrossWikiItemsRead trusts
the models to deliver the sub items (and then expand on their
IDs properly)
In the case of marking an entire xwiki bundle as read without
opening it first, however, that operation is not only redundant,
it produces a problem where the models were not yet filled
(because the xwiki bundle wasn't opened) and so we get empty
array of IDs, and the API has nothing to mark as read.
The solution is simple in this case - skip this method for working
with an entire bundle. It's both a redundant operation and the
wrong model to check. Send the information directly to the API
instead.
Bug: T142143
Change-Id: I4ed3bbc5c83290ed5791060b124840b1c3b12a75
2016-08-04 22:11:54 +00:00
|
|
|
this.manager.getUnreadCounter().estimateChange( -xwikiModel.getCount() );
|
|
|
|
|
2016-04-10 13:31:02 +00:00
|
|
|
return this.api.fetchNotificationGroups( xwikiModel.getSourceNames(), this.manager.getTypeString() )
|
2024-06-03 12:26:18 +00:00
|
|
|
.then( ( groupList ) => {
|
2024-06-03 12:21:23 +00:00
|
|
|
const promises = [];
|
2016-04-10 13:31:02 +00:00
|
|
|
|
2024-06-03 12:22:48 +00:00
|
|
|
for ( const group in groupList ) {
|
|
|
|
const listModel = xwikiModel.getItemBySource( group );
|
|
|
|
const groupItems = groupList[ group ];
|
2016-04-10 13:31:02 +00:00
|
|
|
|
2024-06-03 12:21:23 +00:00
|
|
|
const idArray = [];
|
2024-06-03 12:22:48 +00:00
|
|
|
for ( let i = 0; i < groupItems.length; i++ ) {
|
2024-07-24 15:14:46 +00:00
|
|
|
idArray.push( groupItems[ i ].id, ...( groupItems[ i ].bundledIds || [] ) );
|
2016-04-10 13:31:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Mark items as read in the API
|
|
|
|
promises.push(
|
2024-06-03 13:48:16 +00:00
|
|
|
this.api.markItemsRead( idArray, listModel.getName(), true )
|
2016-04-10 13:31:02 +00:00
|
|
|
);
|
|
|
|
}
|
2016-06-02 14:00:48 +00:00
|
|
|
|
2016-04-10 13:31:02 +00:00
|
|
|
// Synchronously remove this model from the widget
|
2024-06-03 13:48:16 +00:00
|
|
|
this.removeCrossWikiItem();
|
2016-04-10 13:31:02 +00:00
|
|
|
|
2017-10-17 12:53:08 +00:00
|
|
|
return mw.echo.api.NetworkHandler.static.waitForAllPromises( promises ).then(
|
2024-06-03 13:48:16 +00:00
|
|
|
this.refreshUnreadCount.bind( this )
|
2017-10-17 12:53:08 +00:00
|
|
|
);
|
2016-04-10 13:31:02 +00:00
|
|
|
} );
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove the entire cross-wiki model.
|
|
|
|
*/
|
|
|
|
mw.echo.Controller.prototype.removeCrossWikiItem = function () {
|
2016-03-16 22:47:20 +00:00
|
|
|
this.manager.removeNotificationModel( 'xwiki' );
|
2016-04-10 13:31:02 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Refresh the unread notifications counter
|
|
|
|
*
|
|
|
|
* @return {jQuery.Promise} A promise that is resolved when the counter
|
|
|
|
* is updated with the actual unread count from the server.
|
|
|
|
*/
|
|
|
|
mw.echo.Controller.prototype.refreshUnreadCount = function () {
|
|
|
|
return this.manager.getUnreadCounter().update();
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2016-09-13 23:18:29 +00:00
|
|
|
* Update global seenTime for all sources
|
2016-04-10 13:31:02 +00:00
|
|
|
*
|
|
|
|
* @return {jQuery.Promise} A promise that is resolved when the
|
2016-09-13 23:18:29 +00:00
|
|
|
* seenTime was updated for all the controller's types and sources.
|
2016-04-10 13:31:02 +00:00
|
|
|
*/
|
2016-09-13 23:18:29 +00:00
|
|
|
mw.echo.Controller.prototype.updateSeenTime = function () {
|
|
|
|
return this.api.updateSeenTime(
|
|
|
|
this.getTypes(),
|
|
|
|
// For consistency, use current source, though seenTime
|
|
|
|
// will be updated globally
|
|
|
|
this.manager.getFiltersModel().getSourcePagesModel().getCurrentSource()
|
|
|
|
)
|
2024-06-03 12:26:18 +00:00
|
|
|
.then( ( time ) => {
|
2024-06-03 13:48:16 +00:00
|
|
|
this.manager.getSeenTimeModel().setSeenTime( time );
|
2016-07-08 22:56:01 +00:00
|
|
|
} );
|
|
|
|
};
|
2016-04-10 13:31:02 +00:00
|
|
|
|
2016-08-05 21:44:55 +00:00
|
|
|
/**
|
|
|
|
* Perform a dynamic action
|
|
|
|
*
|
|
|
|
* @param {Object} data Action data for the network
|
|
|
|
* @param {string} [source] Requested source to query. Defaults to currently
|
|
|
|
* selected source.
|
|
|
|
* @return {jQuery.Promise} jQuery promise that resolves when the action is done
|
|
|
|
*/
|
|
|
|
mw.echo.Controller.prototype.performDynamicAction = function ( data, source ) {
|
|
|
|
source = source || this.manager.getFiltersModel().getSourcePagesModel().getCurrentSource();
|
|
|
|
return this.api.queryAPI( data, source );
|
|
|
|
};
|
|
|
|
|
2016-04-10 13:31:02 +00:00
|
|
|
/**
|
|
|
|
* Get the types associated with the controller and model
|
|
|
|
*
|
|
|
|
* @return {string[]} Notification types
|
|
|
|
*/
|
|
|
|
mw.echo.Controller.prototype.getTypes = function () {
|
|
|
|
return this.manager.getTypes();
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return a string representation of the notification type.
|
|
|
|
* It could be 'alert', 'message' or, if both are set, 'all'
|
|
|
|
*
|
|
|
|
* @return {string} String representation of notifications type
|
|
|
|
*/
|
|
|
|
mw.echo.Controller.prototype.getTypeString = function () {
|
|
|
|
return this.manager.getTypeString();
|
|
|
|
};
|
2018-11-12 13:56:38 +00:00
|
|
|
}() );
|