X-Git-Url: https://git.cyclocoop.org/%7B%24www_url%7Dadmin/compta/banques/?a=blobdiff_plain;f=resources%2Fsrc%2Fmediawiki.rcfilters%2Fmw.rcfilters.Controller.js;h=a65a9c21ad6632dd4cd003366c1c2043e2b22dbd;hb=e2bea6350c19c7133a94a50707b1b2a9599aec66;hp=256347944e72be564ebdea82efaa9e112af78364;hpb=fe067574483b15b500f06262aa37a0c4c57544ed;p=lhc%2Fweb%2Fwiklou.git diff --git a/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js b/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js index 256347944e..a65a9c21ad 100644 --- a/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js +++ b/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js @@ -13,7 +13,7 @@ this.filtersModel = filtersModel; this.changesListModel = changesListModel; this.savedQueriesModel = savedQueriesModel; - this.requestCounter = 0; + this.requestCounter = {}; this.baseFilterState = {}; this.uriProcessor = null; this.initializing = false; @@ -32,7 +32,7 @@ * @param {Object} [tagList] Tag definition */ mw.rcfilters.Controller.prototype.initialize = function ( filterStructure, namespaceStructure, tagList ) { - var parsedSavedQueries, + var parsedSavedQueries, limitDefault, controller = this, views = {}, items = [], @@ -88,6 +88,11 @@ }; } + // Convert the default from the old preference + // since the limit preference actually affects more + // than just the RecentChanges page + limitDefault = Number( mw.user.options.get( 'rcfilters-rclimit', mw.user.options.get( 'rclimit', '50' ) ) ); + // Add parameter range operations views.range = { groups: [ @@ -99,7 +104,8 @@ allowArbitrary: true, validate: $.isNumeric, sortFunc: function ( a, b ) { return Number( a.name ) - Number( b.name ); }, - 'default': mw.user.options.get( 'rclimit' ), + 'default': String( limitDefault ), + isSticky: true, filters: [ 50, 100, 250, 500 ].map( function ( num ) { return controller._createFilterDataFromNumber( num, num ); } ) @@ -117,7 +123,8 @@ ( Number( i ) * 24 ).toFixed( 2 ) : Number( i ); }, - 'default': mw.user.options.get( 'rcdays' ), + 'default': mw.user.options.get( 'rcdays', '30' ), + isSticky: true, filters: [ // Hours (1, 2, 6, 12) 0.04166, 0.0833, 0.25, 0.5, @@ -174,7 +181,9 @@ // can normalize them per each query item this.savedQueriesModel.initialize( parsedSavedQueries, - this._getBaseFilterState() + this._getBaseFilterState(), + // This is for backwards compatibility - delete all sticky filter states + Object.keys( this.filtersModel.getStickyFiltersState() ) ); // Check whether we need to load defaults. @@ -214,6 +223,8 @@ this.initializing = false; this.switchView( 'default' ); + + this._scheduleLiveUpdate(); }; /** @@ -292,6 +303,7 @@ */ mw.rcfilters.Controller.prototype.resetToDefaults = function () { this.uriProcessor.updateModelBasedOnQuery( this._getDefaultParams() ); + this.updateChangesList(); }; @@ -413,11 +425,9 @@ * @param {boolean} enable True to enable, false to disable */ mw.rcfilters.Controller.prototype.toggleLiveUpdate = function ( enable ) { - if ( enable && !this.liveUpdateTimeout ) { - this._scheduleLiveUpdate(); - } else if ( !enable && this.liveUpdateTimeout ) { - clearTimeout( this.liveUpdateTimeout ); - this.liveUpdateTimeout = null; + this.changesListModel.toggleLiveUpdate( enable ); + if ( this.changesListModel.getLiveUpdate() && this.changesListModel.getNewChangesExist() ) { + this.showNewChanges(); } }; @@ -426,7 +436,7 @@ * @private */ mw.rcfilters.Controller.prototype._scheduleLiveUpdate = function () { - this.liveUpdateTimeout = setTimeout( this._doLiveUpdate.bind( this ), 3000 ); + setTimeout( this._doLiveUpdate.bind( this ), 3000 ); }; /** @@ -434,24 +444,84 @@ * @private */ mw.rcfilters.Controller.prototype._doLiveUpdate = function () { - var controller = this; - this.updateChangesList( {}, true ) - .always( function () { - if ( controller.liveUpdateTimeout ) { - // Live update was not disabled in the meantime - controller._scheduleLiveUpdate(); + if ( !this._shouldCheckForNewChanges() ) { + // skip this turn and check back later + this._scheduleLiveUpdate(); + return; + } + + this._checkForNewChanges() + .then( function ( data ) { + if ( !this._shouldCheckForNewChanges() ) { + // by the time the response is received, + // it may not be appropriate anymore + return; } - } ); + + if ( data.changes !== 'NO_RESULTS' ) { + if ( this.changesListModel.getLiveUpdate() ) { + return this.updateChangesList( false, null, true, false ); + } else { + this.changesListModel.setNewChangesExist( true ); + } + } + }.bind( this ) ) + .always( this._scheduleLiveUpdate.bind( this ) ); + }; + + /** + * @return {boolean} It's appropriate to check for new changes now + * @private + */ + mw.rcfilters.Controller.prototype._shouldCheckForNewChanges = function () { + var liveUpdateFeatureFlag = mw.config.get( 'wgStructuredChangeFiltersEnableLiveUpdate' ) || + new mw.Uri().query.liveupdate; + + return !document.hidden && + !this.changesListModel.getNewChangesExist() && + !this.updatingChangesList && + liveUpdateFeatureFlag; + }; + + /** + * Check if new changes, newer than those currently shown, are available + * + * @return {jQuery.Promise} Promise object that resolves after trying + * to fetch 1 change newer than the last known 'from' parameter value + * + * @private + */ + mw.rcfilters.Controller.prototype._checkForNewChanges = function () { + return this._fetchChangesList( + 'liveUpdate', + { + limit: 1, + from: this.changesListModel.getNextFrom() + } + ); + }; + + /** + * Show the new changes + * + * @return {jQuery.Promise} Promise object that resolves after + * fetching and showing the new changes + */ + mw.rcfilters.Controller.prototype.showNewChanges = function () { + return this.updateChangesList( false, null, true, true ); }; /** * Save the current model state as a saved query * * @param {string} [label] Label of the saved query + * @param {boolean} [setAsDefault=false] This query should be set as the default */ - mw.rcfilters.Controller.prototype.saveCurrentQuery = function ( label ) { - var highlightedItems = {}, - highlightEnabled = this.filtersModel.isHighlightEnabled(); + mw.rcfilters.Controller.prototype.saveCurrentQuery = function ( label, setAsDefault ) { + var queryID, + highlightedItems = {}, + highlightEnabled = this.filtersModel.isHighlightEnabled(), + selectedState = this.filtersModel.getSelectedState(); // Prepare highlights this.filtersModel.getHighlightedItems().forEach( function ( item ) { @@ -461,16 +531,23 @@ // These are filter states; highlight is stored as boolean highlightedItems.highlight = this.filtersModel.isHighlightEnabled(); + // Delete all sticky filters + this._deleteStickyValuesFromFilterState( selectedState ); + // Add item - this.savedQueriesModel.addNewQuery( + queryID = this.savedQueriesModel.addNewQuery( label || mw.msg( 'rcfilters-savedqueries-defaultlabel' ), { - filters: this.filtersModel.getSelectedState(), + filters: selectedState, highlights: highlightedItems, invert: this.filtersModel.areNamespacesInverted() } ); + if ( setAsDefault ) { + this.savedQueriesModel.setDefault( queryID ); + } + // Save item this._saveSavedQueries(); }; @@ -529,7 +606,10 @@ highlights.highlight = highlights.highlights || highlights.highlight; // Update model state from filters - this.filtersModel.toggleFiltersSelected( data.filters ); + this.filtersModel.toggleFiltersSelected( + // Merge filters with sticky values + $.extend( true, {}, data.filters, this.filtersModel.getStickyFiltersState() ) + ); // Update namespace inverted property this.filtersModel.toggleInvertedNamespaces( !!Number( data.invert ) ); @@ -562,7 +642,8 @@ * @return {boolean} Query exists */ mw.rcfilters.Controller.prototype.findQueryMatchingCurrentState = function () { - var highlightedItems = {}; + var highlightedItems = {}, + selectedState = this.filtersModel.getSelectedState(); // Prepare highlights of the current query this.filtersModel.getItemsSupportingHighlights().forEach( function ( item ) { @@ -570,15 +651,30 @@ } ); highlightedItems.highlight = this.filtersModel.isHighlightEnabled(); + // Remove sticky filters + this._deleteStickyValuesFromFilterState( selectedState ); + return this.savedQueriesModel.findMatchingQuery( { - filters: this.filtersModel.getSelectedState(), + filters: selectedState, highlights: highlightedItems, invert: this.filtersModel.areNamespacesInverted() } ); }; + /** + * Delete sticky filters from given object + * + * @param {Object} filterState Filter state + */ + mw.rcfilters.Controller.prototype._deleteStickyValuesFromFilterState = function ( filterState ) { + // Remove sticky filters + $.each( this.filtersModel.getStickyFiltersState(), function ( filterName ) { + delete filterState[ filterName ]; + } ); + }; + /** * Get an object representing the base state of parameters * and highlights. @@ -701,6 +797,56 @@ mw.user.options.set( 'rcfilters-saved-queries', stringified ); }; + /** + * Update sticky preferences with current model state + */ + mw.rcfilters.Controller.prototype.updateStickyPreferences = function () { + // Update default sticky values with selected, whether they came from + // the initial defaults or from the URL value that is being normalized + this.updateDaysDefault( this.filtersModel.getGroup( 'days' ).getSelectedItems()[ 0 ].getParamName() ); + this.updateLimitDefault( this.filtersModel.getGroup( 'limit' ).getSelectedItems()[ 0 ].getParamName() ); + }; + + /** + * Update the limit default value + * + * @param {number} newValue New value + */ + mw.rcfilters.Controller.prototype.updateLimitDefault = function ( newValue ) { + if ( !$.isNumeric( newValue ) ) { + return; + } + + newValue = Number( newValue ); + + if ( mw.user.options.get( 'rcfilters-rclimit' ) !== newValue ) { + // Save the preference + new mw.Api().saveOption( 'rcfilters-rclimit', newValue ); + // Update the preference for this session + mw.user.options.set( 'rcfilters-rclimit', newValue ); + } + }; + + /** + * Update the days default value + * + * @param {number} newValue New value + */ + mw.rcfilters.Controller.prototype.updateDaysDefault = function ( newValue ) { + if ( !$.isNumeric( newValue ) ) { + return; + } + + newValue = Number( newValue ); + + if ( mw.user.options.get( 'rcdays' ) !== newValue ) { + // Save the preference + new mw.Api().saveOption( 'rcdays', newValue ); + // Update the preference for this session + mw.user.options.set( 'rcdays', newValue ); + } + }; + /** * Synchronize the URL with the current state of the filters * without adding an history entry. @@ -721,6 +867,10 @@ this.uriProcessor.updateModelBasedOnQuery( new mw.Uri().query ); + // Update the sticky preferences, in case we received a value + // from the URL + this.updateStickyPreferences(); + // Only update and fetch new results if it is requested if ( fetchChangesList ) { this.updateChangesList(); @@ -730,25 +880,36 @@ /** * Update the list of changes and notify the model * + * @param {boolean} [updateUrl=true] Whether the URL should be updated with the current state of the filters * @param {Object} [params] Extra parameters to add to the API call - * @param {boolean} [isLiveUpdate] Don't update the URL or invalidate the changes list + * @param {boolean} [isLiveUpdate=false] The purpose of this update is to show new results for the same filters + * @param {boolean} [invalidateCurrentChanges=true] Invalidate current changes by default (show spinner) * @return {jQuery.Promise} Promise that is resolved when the update is complete */ - mw.rcfilters.Controller.prototype.updateChangesList = function ( params, isLiveUpdate ) { - if ( !isLiveUpdate ) { + mw.rcfilters.Controller.prototype.updateChangesList = function ( updateUrl, params, isLiveUpdate, invalidateCurrentChanges ) { + updateUrl = updateUrl === undefined ? true : updateUrl; + invalidateCurrentChanges = invalidateCurrentChanges === undefined ? true : invalidateCurrentChanges; + if ( updateUrl ) { this._updateURL( params ); + } + if ( invalidateCurrentChanges ) { this.changesListModel.invalidate(); } + this.changesListModel.setNewChangesExist( false ); + this.updatingChangesList = true; return this._fetchChangesList() .then( // Success function ( pieces ) { var $changesListContent = pieces.changes, $fieldset = pieces.fieldset; - this.changesListModel.update( $changesListContent, $fieldset ); + this.changesListModel.update( $changesListContent, $fieldset, false, isLiveUpdate ); }.bind( this ) // Do nothing for failure - ); + ) + .always( function () { + this.updatingChangesList = false; + }.bind( this ) ); }; /** @@ -769,7 +930,10 @@ data = defaultSavedQueryItem.getData(); queryHighlights = data.highlights || {}; - savedParams = this.filtersModel.getParametersFromFilters( data.filters || {} ); + savedParams = this.filtersModel.getParametersFromFilters( + // Merge filters with sticky values + $.extend( true, {}, data.filters, this.filtersModel.getStickyFiltersState() ) + ); // Translate highlights to parameters savedHighlights.highlight = String( Number( queryHighlights.highlight ) ); @@ -839,15 +1003,37 @@ /** * Fetch the list of changes from the server for the current filters * + * @param {string} [counterId='updateChangesList'] Id for this request. To allow concurrent requests + * not to invalidate each other. + * @param {Object} [params={}] Parameters to add to the query + * * @return {jQuery.Promise} Promise object that will resolve with the changes list * or with a string denoting no results. */ - mw.rcfilters.Controller.prototype._fetchChangesList = function () { + mw.rcfilters.Controller.prototype._fetchChangesList = function ( counterId, params ) { var uri = this._getUpdatedUri(), - requestId = ++this.requestCounter, - latestRequest = function () { - return requestId === this.requestCounter; - }.bind( this ); + stickyParams = this.filtersModel.getStickyParams(), + requestId, + latestRequest; + + counterId = counterId || 'updateChangesList'; + params = params || {}; + + uri.extend( params ); + + this.requestCounter[ counterId ] = this.requestCounter[ counterId ] || 0; + requestId = ++this.requestCounter[ counterId ]; + latestRequest = function () { + return requestId === this.requestCounter[ counterId ]; + }.bind( this ); + + // Sticky parameters override the URL params + // this is to make sure that whether we represent + // the sticky params in the URL or not (they may + // be normalized out) the sticky parameters are + // always being sent to the server with their + // current/default values + uri.extend( stickyParams ); return $.ajax( uri.toString(), { contentType: 'html' } ) .then(