2 * A special widget that displays a message that a page is being watched/unwatched
3 * with a selection widget that can determine how long the page will be watched.
4 * If a page is being watched then a dropdown with expiry options is included.
6 * @exports mediawiki.watchstar.widgets
7 * @extends OO.ui.Widget
8 * @param {string} action One of 'watch', 'unwatch'
9 * @param {string} pageTitle Title of page that this widget will watch or unwatch
10 * @param {Function} updateWatchLink
11 * @param {Object} config Configuration object
13 function WatchlistExpiryWidget( action, pageTitle, updateWatchLink, config ) {
14 const dataExpiryOptions = require( './data.json' ).options,
18 config = config || {};
19 const $link = config.$link;
21 WatchlistExpiryWidget.super.call( this, config );
23 const messageLabel = new OO.ui.LabelWidget( {
28 .addClass( 'mw-watchstar-WatchlistExpiryWidget' )
29 .append( messageLabel.$element );
32 * Allows user to tab into the expiry dropdown from the watch link.
33 * Valid only for the initial keystroke after the popup appears, so as to
34 * avoid listening to every keystroke for the entire session.
36 function addTabKeyListener() {
37 $( window ).one( 'keydown.watchlistExpiry', ( e ) => {
38 if ( ( e.keyCode || e.which ) !== OO.ui.Keys.TAB ) {
42 // Here we look for focus on the watch link, going by the accessKey.
43 // This is because there is no CSS class or ID on the link itself,
44 // and skins could manipulate the position of the link. The accessKey
45 // however is always present on the link.
46 if ( document.activeElement.accessKey === mw.msg( 'accesskey-ca-watch' ) ) {
48 expiryDropdown.focus();
50 // Add another tab key listener so they can tab back to the watch link.
52 } else if ( $( e.target ).parents( '.mw-watchexpiry' ).length ) {
53 // Move focus to the watch link if they're tabbing from the dropdown.
55 $( '#ca-unwatch a' ).trigger( 'focus' );
60 if ( action === 'watch' ) {
63 for ( const key in dataExpiryOptions ) {
64 expiryOptions.push( { data: dataExpiryOptions[ key ], label: key } );
67 const dropdownLabel = new OO.ui.LabelWidget( {
68 label: mw.message( 'addedwatchexpiry-options-label' ).parseDom(),
69 classes: [ 'mw-WatchlistExpiryWidgetwatchlist-dropdown-label' ]
71 expiryDropdown = new OO.ui.DropdownInputWidget( {
72 options: expiryOptions,
73 classes: [ 'mw-watchexpiry' ]
75 const onDropdownChange = function ( value ) {
76 const notif = mw.notification,
77 optionSelectedLabel = expiryDropdown.dropdownWidget.label;
79 if ( typeof $link !== 'undefined' ) {
80 updateWatchLink( $link, 'watch', 'loading' );
83 // Pause the mw.notify so that we can wait for watch request to finish
85 const api = new mw.Api();
86 api.watch( pageTitle, value )
87 .done( ( watchResponse ) => {
89 const mwTitle = mw.Title.newFromText( pageTitle ),
90 isInfinity = mw.util.isInfinity( value );
91 if ( mwTitle.isTalkPage() ) {
92 message = isInfinity ? 'addedwatchindefinitelytext-talk' : 'addedwatchexpirytext-talk';
94 message = isInfinity ? 'addedwatchindefinitelytext' : 'addedwatchexpirytext';
97 // The following messages can be used here:
98 // * addedwatchindefinitelytext-talk
99 // * addedwatchindefinitelytext
100 // * addedwatchexpirytext-talk
101 // * addedwatchexpirytext
102 messageLabel.setLabel(
103 mw.message( message, mwTitle.getPrefixedText(), optionSelectedLabel ).parseDom()
105 // Resume the mw.notify once the label has been updated
108 updateWatchLink( mwTitle, 'unwatch', 'idle', watchResponse.expiry, value );
110 .fail( ( code, data ) => {
111 // Format error message
112 const $msg = api.getErrorMessage( data );
114 // Report to user about the error
119 // Resume the mw.notify once the error has been reported
124 expiryDropdown.on( 'change', onDropdownChange );
125 this.$element.append( dropdownLabel.$element, expiryDropdown.$element );
129 OO.inheritClass( WatchlistExpiryWidget, OO.ui.Widget );
131 module.exports = WatchlistExpiryWidget;