Merge "rdbms: make transaction rounds apply DBO_TRX to DB_REPLICA connections"
[mediawiki.git] / resources / src / mediawiki.watchstar.widgets / WatchlistExpiryWidget.js
blobe4a30f34b8ef4c3becfd5edcb9540fa3befdc77d
1 /**
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.
5  *
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
12  */
13 function WatchlistExpiryWidget( action, pageTitle, updateWatchLink, config ) {
14         const dataExpiryOptions = require( './data.json' ).options,
15                 expiryOptions = [];
16         let expiryDropdown;
18         config = config || {};
19         const $link = config.$link;
21         WatchlistExpiryWidget.super.call( this, config );
23         const messageLabel = new OO.ui.LabelWidget( {
24                 label: config.message
25         } );
27         this.$element
28                 .addClass( 'mw-watchstar-WatchlistExpiryWidget' )
29                 .append( messageLabel.$element );
31         /**
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.
35          */
36         function addTabKeyListener() {
37                 $( window ).one( 'keydown.watchlistExpiry', ( e ) => {
38                         if ( ( e.keyCode || e.which ) !== OO.ui.Keys.TAB ) {
39                                 return;
40                         }
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' ) ) {
47                                 e.preventDefault();
48                                 expiryDropdown.focus();
50                                 // Add another tab key listener so they can tab back to the watch link.
51                                 addTabKeyListener();
52                         } else if ( $( e.target ).parents( '.mw-watchexpiry' ).length ) {
53                                 // Move focus to the watch link if they're tabbing from the dropdown.
54                                 e.preventDefault();
55                                 $( '#ca-unwatch a' ).trigger( 'focus' );
56                         }
57                 } );
58         }
60         if ( action === 'watch' ) {
61                 addTabKeyListener();
63                 for ( const key in dataExpiryOptions ) {
64                         expiryOptions.push( { data: dataExpiryOptions[ key ], label: key } );
65                 }
67                 const dropdownLabel = new OO.ui.LabelWidget( {
68                         label: mw.message( 'addedwatchexpiry-options-label' ).parseDom(),
69                         classes: [ 'mw-WatchlistExpiryWidgetwatchlist-dropdown-label' ]
70                 } );
71                 expiryDropdown = new OO.ui.DropdownInputWidget( {
72                         options: expiryOptions,
73                         classes: [ 'mw-watchexpiry' ]
74                 } );
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' );
81                         }
83                         // Pause the mw.notify so that we can wait for watch request to finish
84                         notif.pause();
85                         const api = new mw.Api();
86                         api.watch( pageTitle, value )
87                                 .done( ( watchResponse ) => {
88                                         let message;
89                                         const mwTitle = mw.Title.newFromText( pageTitle ),
90                                                 isInfinity = mw.util.isInfinity( value );
91                                         if ( mwTitle.isTalkPage() ) {
92                                                 message = isInfinity ? 'addedwatchindefinitelytext-talk' : 'addedwatchexpirytext-talk';
93                                         } else {
94                                                 message = isInfinity ? 'addedwatchindefinitelytext' : 'addedwatchexpirytext';
95                                         }
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()
104                                         );
105                                         // Resume the mw.notify once the label has been updated
106                                         notif.resume();
108                                         updateWatchLink( mwTitle, 'unwatch', 'idle', watchResponse.expiry, value );
109                                 } )
110                                 .fail( ( code, data ) => {
111                                         // Format error message
112                                         const $msg = api.getErrorMessage( data );
114                                         // Report to user about the error
115                                         mw.notify( $msg, {
116                                                 tag: 'watch-self',
117                                                 type: 'error'
118                                         } );
119                                         // Resume the mw.notify once the error has been reported
120                                         notif.resume();
121                                 } );
122                 };
124                 expiryDropdown.on( 'change', onDropdownChange );
125                 this.$element.append( dropdownLabel.$element, expiryDropdown.$element );
126         }
129 OO.inheritClass( WatchlistExpiryWidget, OO.ui.Widget );
131 module.exports = WatchlistExpiryWidget;