src/mobile.startup/watchstar/WatchstarGateway.js
const util = require( '../util' ),
actionParams = require( '../actionParams' );
/**
* @ignore
* @typedef {string|number} PageID Page ID. 0 / "0" is a special no-ID value.
* {@link https://www.mediawiki.org/wiki/Manual:Page_table#page_id Page ID}
*
* @ignore
* @typedef {string} PageTitle Canonical page title.
* {@link https://www.mediawiki.org/wiki/Manual:Title.php#Canonical_forms Canonical forms}
*
* @ignore
* @typedef {boolean} WatchStatus Page watch status; true if watched, false if
* unwatched.
* {@link https://www.mediawiki.org/wiki/API:Info API:Info} (see inprop.watched)
* {@link https://www.mediawiki.org/wiki/API:Watch API:Watch} (see unwatch)
*
* @ignore
* @typedef {Object.<PageTitle, WatchStatus>} WatchStatusMap
*/
/**
* API for retrieving and modifying page watch statuses. This module interacts
* with two endpoints, API:Info for GETs and API:Watch and for POSTs.
*
* @class WatchstarGateway
* @param {mw.Api} api
* @ignore
*/
function WatchstarGateway( api ) {
this.api = api;
}
WatchstarGateway.prototype = {
/**
* Issues zero to two asynchronous HTTP requests for the watch status of
* each page ID and title passed.
*
* Every watch entry has a title but not necessarily a page ID. Entries
* without IDs are missing pages, i.e., pages that do not exist. These
* entries are used to observe when a page with a given title is created.
* Although it is convenient to use titles because they're always present,
* IDs are preferred since they're far less likely to exceed the URL length
* limit.
*
* No request is issued when no IDs and no titles are passed. Given that the
* server state does not change between the two requests, overlapping title
* and ID members will behave as expected but there is no reason to issue
* such a request.
*
* @memberof WatchstarGateway
* @instance
* @param {PageID[]} ids
* @param {PageTitle[]} titles
* @return {jQuery.Deferred<WatchStatusMap>}
*/
getStatuses( ids, titles ) {
// Issue two requests and coalesce the results.
return util.Promise.all( [
this.getStatusesByID( ids ),
this.getStatusesByTitle( titles )
] ).then( function () {
return util.extend.apply( util, arguments );
} );
},
/**
* @memberof WatchstarGateway
* @instance
* @param {PageID[]} ids
* @return {jQuery.Deferred<WatchStatusMap>}
*/
getStatusesByID( ids ) {
const self = this;
if ( !ids.length ) {
return util.Deferred().resolve( {} );
}
return this.api.get( {
formatversion: 2,
action: 'query',
prop: 'info',
inprop: 'watched',
pageids: ids
} ).then( ( rsp ) => self._unmarshalGetResponse( rsp ) );
},
/**
* @memberof WatchstarGateway
* @instance
* @param {PageTitle[]} titles
* @return {jQuery.Deferred<WatchStatusMap>}
*/
getStatusesByTitle( titles ) {
const self = this;
if ( !titles.length ) {
return util.Deferred().resolve( {} );
}
return this.api.get( actionParams( {
prop: 'info',
inprop: 'watched',
titles
} ) ).then( ( rsp ) => self._unmarshalGetResponse( rsp ) );
},
/**
* @memberof WatchstarGateway
* @instance
* @param {PageTitle[]} titles
* @param {WatchStatus} watched
* @return {jQuery.Deferred}
*/
postStatusesByTitle( titles, watched ) {
const params = {
action: 'watch',
titles
};
if ( !watched ) {
params.unwatch = !watched;
}
return this.api.postWithToken( 'watch', params );
},
/**
* @memberof WatchstarGateway
* @instance
* @param {Object} rsp The API:Info response.
* @return {jQuery.Deferred<WatchStatusMap>}
* @see getStatusesByID
* @see getStatusesByTitle
* @ignore
*/
_unmarshalGetResponse( rsp ) {
const pages = rsp && rsp.query && rsp.query.pages || [];
return pages.reduce( ( statuses, page ) => {
statuses[page.title] = page.watched;
return statuses;
}, {} );
}
};
module.exports = WatchstarGateway;