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.} 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} */ 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} */ 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} */ 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} * @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;