jenkins-bot has submitted this change and it was merged. (
https://gerrit.wikimedia.org/r/366861 )
Change subject: RCFilters: show new changes
......................................................................
RCFilters: show new changes
When "live update" is off and new changes are detected,
show a link to load and prepend the changes to the list.
Also adding a line between old and new changes
when grouping by pages is off.
Bug: T163426
Change-Id: I6a111d23956bdc04caa4c71e9deede056779aafa
---
M includes/changes/ChangesList.php
M includes/specials/SpecialRecentchanges.php
M languages/i18n/en.json
M languages/i18n/qqq.json
M resources/Resources.php
M resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.ChangesListViewModel.js
M resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
M resources/src/mediawiki.rcfilters/mw.rcfilters.init.js
M resources/src/mediawiki.rcfilters/styles/mw.rcfilters.less
M
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.ChangesListWrapperWidget.less
M
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesListWrapperWidget.js
M resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterWrapperWidget.js
M resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FormWrapperWidget.js
M resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.LiveUpdateButtonWidget.js
M tests/phpunit/includes/changes/EnhancedChangesListTest.php
M tests/phpunit/includes/changes/OldChangesListTest.php
16 files changed, 365 insertions(+), 72 deletions(-)
Approvals:
Catrope: Looks good to me, approved
jenkins-bot: Verified
diff --git a/includes/changes/ChangesList.php b/includes/changes/ChangesList.php
index 5aa693d..2182c6c 100644
--- a/includes/changes/ChangesList.php
+++ b/includes/changes/ChangesList.php
@@ -747,20 +747,22 @@
* @return string[] attribute name => value
*/
protected function getDataAttributes( RecentChange $rc ) {
+ $attrs = [];
+
$type = $rc->getAttribute( 'rc_source' );
switch ( $type ) {
case RecentChange::SRC_EDIT:
case RecentChange::SRC_NEW:
- return [
- 'data-mw-revid' =>
$rc->mAttribs['rc_this_oldid'],
- ];
+ $attrs[ 'data-mw-revid' ] =
$rc->mAttribs['rc_this_oldid'];
+ break;
case RecentChange::SRC_LOG:
- return [
- 'data-mw-logid' =>
$rc->mAttribs['rc_logid'],
- 'data-mw-logaction' =>
$rc->mAttribs['rc_log_type'] . '/' . $rc->mAttribs['rc_log_action'],
- ];
- default:
- return [];
+ $attrs[ 'data-mw-logid' ] =
$rc->mAttribs['rc_logid'];
+ $attrs[ 'data-mw-logaction' ] =
$rc->mAttribs['rc_log_type'] . '/' . $rc->mAttribs['rc_log_action'];
+ break;
}
+
+ $attrs[ 'data-mw-ts' ] = $rc->getAttribute( 'rc_timestamp' );
+
+ return $attrs;
}
}
diff --git a/includes/specials/SpecialRecentchanges.php
b/includes/specials/SpecialRecentchanges.php
index f9052ad..8d00e90 100644
--- a/includes/specials/SpecialRecentchanges.php
+++ b/includes/specials/SpecialRecentchanges.php
@@ -973,15 +973,20 @@
$resetLink = $this->makeOptionsLink( $this->msg(
'rclistfromreset' ),
[ 'from' => '' ], $nondefaults );
- $note .= $this->msg( 'rcnotefrom' )
+ $noteFromMsg = $this->msg( 'rcnotefrom' )
->numParams( $options['limit'] )
->params(
$lang->userTimeAndDate(
$options['from'], $user ),
$lang->userDate( $options['from'],
$user ),
$lang->userTime( $options['from'],
$user )
)
- ->numParams( $numRows )
- ->parse() . ' ' .
+ ->numParams( $numRows );
+ $note .= Html::rawElement(
+ 'span',
+ [ 'class' => 'rcnotefrom' ],
+ $noteFromMsg->parse()
+ ) .
+ ' ' .
Html::rawElement(
'span',
[ 'class' => 'rcoptions-listfromreset'
],
diff --git a/languages/i18n/en.json b/languages/i18n/en.json
index b2b4179..8c295b2 100644
--- a/languages/i18n/en.json
+++ b/languages/i18n/en.json
@@ -1375,6 +1375,8 @@
"rcfilters-savedqueries-add-new-title": "Save current filter settings",
"rcfilters-restore-default-filters": "Restore default filters",
"rcfilters-clear-all-filters": "Clear all filters",
+ "rcfilters-show-new-changes": "Show new changes",
+ "rcfilters-previous-changes-label": "Previously viewed changes",
"rcfilters-search-placeholder": "Filter recent changes (browse or start
typing)",
"rcfilters-invalid-filter": "Invalid filter",
"rcfilters-empty-filter": "No active filters. All contributions are
shown.",
diff --git a/languages/i18n/qqq.json b/languages/i18n/qqq.json
index f2755d1..89f6f41 100644
--- a/languages/i18n/qqq.json
+++ b/languages/i18n/qqq.json
@@ -1565,6 +1565,8 @@
"rcfilters-savedqueries-add-new-title": "Title for the popup to add new
quick link in [[Special:RecentChanges]]. This is for a small popup, please try
to use a short string.",
"rcfilters-restore-default-filters": "Label for the button that resets
filters to defaults",
"rcfilters-clear-all-filters": "Title for the button that clears all
filters",
+ "rcfilters-show-new-changes": "Label for the button to show new
changes.",
+ "rcfilters-previous-changes-label": "Label to indicate the changes
below have been previously viewed.",
"rcfilters-search-placeholder": "Placeholder for the filter search
input.",
"rcfilters-invalid-filter": "A label for an invalid filter.",
"rcfilters-empty-filter": "Placeholder for the filter list when no
filters were chosen.",
diff --git a/resources/Resources.php b/resources/Resources.php
index e3d8a1b..82f285e 100644
--- a/resources/Resources.php
+++ b/resources/Resources.php
@@ -1857,6 +1857,8 @@
'rcfilters-savedqueries-cancel-label',
'rcfilters-restore-default-filters',
'rcfilters-clear-all-filters',
+ 'rcfilters-show-new-changes',
+ 'rcfilters-previous-changes-label',
'rcfilters-search-placeholder',
'rcfilters-invalid-filter',
'rcfilters-empty-filter',
diff --git
a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.ChangesListViewModel.js
b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.ChangesListViewModel.js
index 49c0b82..34ed2eb 100644
---
a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.ChangesListViewModel.js
+++
b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.ChangesListViewModel.js
@@ -11,6 +11,9 @@
OO.EventEmitter.call( this );
this.valid = true;
+ this.newChangesExist = false;
+ this.nextFrom = null;
+ this.liveUpdate = false;
};
/* Initialization */
@@ -27,10 +30,26 @@
/**
* @event update
- * @param {jQuery|string} changesListContent
- * @param {jQuery} $fieldset
+ * @param {jQuery|string} $changesListContent List of changes
+ * @param {jQuery} $fieldset Server-generated form
+ * @param {boolean} isInitialDOM Whether the previous dom variables are
from the initial page load
+ * @param {boolean} fromLiveUpdate These are new changes fetched via
Live Update
*
- * The list of change is now up to date
+ * The list of changes has been updated
+ */
+
+ /**
+ * @event newChangesExist
+ * @param {boolean} newChangesExist
+ *
+ * The existence of changes newer than those currently displayed has
changed.
+ */
+
+ /**
+ * @event liveUpdateChange
+ * @param {boolean} enable
+ *
+ * The state of the 'live update' feature has changed.
*/
/* Methods */
@@ -53,10 +72,70 @@
* @param {jQuery|string} changesListContent
* @param {jQuery} $fieldset
* @param {boolean} [isInitialDOM] Using the initial (already attached)
DOM elements
+ * @param {boolean} [fromLiveUpdate] These are new changes fetched via
Live Update
+ * @fires update
*/
- mw.rcfilters.dm.ChangesListViewModel.prototype.update = function (
changesListContent, $fieldset, isInitialDOM ) {
+ mw.rcfilters.dm.ChangesListViewModel.prototype.update = function (
changesListContent, $fieldset, isInitialDOM, fromLiveUpdate ) {
+ var from = this.nextFrom;
this.valid = true;
- this.emit( 'update', changesListContent, $fieldset,
isInitialDOM );
+ this.extractNextFrom( $fieldset );
+ this.emit( 'update', changesListContent, $fieldset,
isInitialDOM, fromLiveUpdate ? from : null );
+ };
+
+ /**
+ * Specify whether new changes exist
+ *
+ * @param {boolean} newChangesExist
+ * @fires newChangesExist
+ */
+ mw.rcfilters.dm.ChangesListViewModel.prototype.setNewChangesExist =
function ( newChangesExist ) {
+ if ( newChangesExist !== this.newChangesExist ) {
+ this.newChangesExist = newChangesExist;
+ this.emit( 'newChangesExist', newChangesExist );
+ }
+ };
+
+ /**
+ * @return {boolean} Whether new changes exist
+ */
+ mw.rcfilters.dm.ChangesListViewModel.prototype.getNewChangesExist =
function () {
+ return this.newChangesExist;
+ };
+
+ /**
+ * Extract the value of the 'from' parameter from a link in the field
set
+ *
+ * @param {jQuery} $fieldset
+ */
+ mw.rcfilters.dm.ChangesListViewModel.prototype.extractNextFrom =
function ( $fieldset ) {
+ this.nextFrom = $fieldset.find( '.rclistfrom > a' ).data(
'params' ).from;
+ };
+
+ /**
+ * @return {string} The 'from' parameter that can be used to query new
changes
+ */
+ mw.rcfilters.dm.ChangesListViewModel.prototype.getNextFrom = function
() {
+ return this.nextFrom;
+ };
+
+ /**
+ * Toggle the 'live update' feature on/off
+ *
+ * @param {boolean} enable
+ */
+ mw.rcfilters.dm.ChangesListViewModel.prototype.toggleLiveUpdate =
function ( enable ) {
+ enable = enable === undefined ? !this.liveUpdate : enable;
+ if ( enable !== this.liveUpdate ) {
+ this.liveUpdate = enable;
+ this.emit( 'liveUpdateChange', this.liveUpdate );
+ }
+ };
+
+ /**
+ * @return {boolean} The 'live update' feature is enabled
+ */
+ mw.rcfilters.dm.ChangesListViewModel.prototype.getLiveUpdate = function
() {
+ return this.liveUpdate;
};
}( mediaWiki ) );
diff --git a/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
b/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
index 73ff165..a65a9c2 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;
@@ -223,6 +223,8 @@
this.initializing = false;
this.switchView( 'default' );
+
+ this._scheduleLiveUpdate();
};
/**
@@ -423,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();
}
};
@@ -436,7 +436,7 @@
* @private
*/
mw.rcfilters.Controller.prototype._scheduleLiveUpdate = function () {
- this.liveUpdateTimeout = setTimeout( this._doLiveUpdate.bind(
this ), 3000 );
+ setTimeout( this._doLiveUpdate.bind( this ), 3000 );
};
/**
@@ -444,14 +444,71 @@
* @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 );
};
/**
@@ -823,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 ) );
};
/**
@@ -935,16 +1003,29 @@
/**
* 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(),
stickyParams = this.filtersModel.getStickyParams(),
- requestId = ++this.requestCounter,
- latestRequest = function () {
- return requestId === this.requestCounter;
- }.bind( this );
+ 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
diff --git a/resources/src/mediawiki.rcfilters/mw.rcfilters.init.js
b/resources/src/mediawiki.rcfilters/mw.rcfilters.init.js
index 12a83cd..701e61d 100644
--- a/resources/src/mediawiki.rcfilters/mw.rcfilters.init.js
+++ b/resources/src/mediawiki.rcfilters/mw.rcfilters.init.js
@@ -19,13 +19,13 @@
$overlay = $( '<div>' )
.addClass( 'mw-rcfilters-ui-overlay' ),
filtersWidget = new
mw.rcfilters.ui.FilterWrapperWidget(
- controller, filtersModel,
savedQueriesModel, { $overlay: $overlay } );
+ controller, filtersModel,
savedQueriesModel, changesListModel, { $overlay: $overlay } );
// TODO: The changesListWrapperWidget should be able to
initialize
// after the model is ready.
// eslint-disable-next-line no-new
new mw.rcfilters.ui.ChangesListWrapperWidget(
- filtersModel, changesListModel, $(
'.mw-changeslist, .mw-changeslist-empty' ) );
+ filtersModel, changesListModel, controller, $(
'.mw-changeslist, .mw-changeslist-empty' ) );
controller.initialize(
mw.config.get( 'wgStructuredChangeFilters' ),
diff --git a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.less
b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.less
index 0d12b81..7f0d34e 100644
--- a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.less
+++ b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.less
@@ -2,11 +2,6 @@
.client-js {
.rcoptions {
border: 0;
- border-bottom: 1px solid #a2a9b1;
-
- legend {
- display: none;
- }
}
.mw-recentchanges-toplinks {
@@ -29,7 +24,7 @@
}
.rcfilters-head {
- min-height: 310px;
+ min-height: 220px;
margin-top: 1em;
&:not( .mw-rcfilters-ui-ready ) {
diff --git
a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.ChangesListWrapperWidget.less
b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.ChangesListWrapperWidget.less
index 89acdc0..d60e616 100644
---
a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.ChangesListWrapperWidget.less
+++
b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.ChangesListWrapperWidget.less
@@ -1,6 +1,26 @@
@import 'mw.rcfilters.mixins';
.mw-rcfilters-ui-changesListWrapperWidget {
+
+ &-newChanges {
+ min-height: 34px;
+ margin: 10px 0;
+ text-align: center;
+ }
+
+ &-previousChangesIndicator {
+ margin: 10px 0;
+ color: #36c;
+ border-top: 2px solid #36c;
+ text-align: center;
+
+ &:hover {
+ color: #72777d;
+ border-top-color: #72777d;
+ cursor: pointer;
+ }
+ }
+
&-results {
width: 35em;
margin: 5em auto;
diff --git
a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesListWrapperWidget.js
b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesListWrapperWidget.js
index c2533df..d571774 100644
---
a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesListWrapperWidget.js
+++
b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesListWrapperWidget.js
@@ -7,12 +7,14 @@
* @constructor
* @param {mw.rcfilters.dm.FiltersViewModel} filtersViewModel View model
* @param {mw.rcfilters.dm.ChangesListViewModel} changesListViewModel
View model
+ * @param {mw.rcfilters.Controller} controller
* @param {jQuery} $changesListRoot Root element of the changes list to
attach to
- * @param {Object} config Configuration object
+ * @param {Object} [config] Configuration object
*/
mw.rcfilters.ui.ChangesListWrapperWidget = function
MwRcfiltersUiChangesListWrapperWidget(
filtersViewModel,
changesListViewModel,
+ controller,
$changesListRoot,
config
) {
@@ -25,6 +27,7 @@
this.filtersViewModel = filtersViewModel;
this.changesListViewModel = changesListViewModel;
+ this.controller = controller;
// Events
this.filtersViewModel.connect( this, {
@@ -33,7 +36,8 @@
} );
this.changesListViewModel.connect( this, {
invalidate: 'onModelInvalidate',
- update: 'onModelUpdate'
+ update: 'onModelUpdate',
+ newChangesExist: 'onNewChangesExist'
} );
this.$element
@@ -43,6 +47,8 @@
// Set up highlight containers
this.setupHighlightContainers( this.$element );
+
+ this.setupNewChangesButtonContainer( this.$element );
};
/* Initialization */
@@ -86,16 +92,21 @@
* @param {jQuery|string} $changesListContent The content of the
updated changes list
* @param {jQuery} $fieldset The content of the updated fieldset
* @param {boolean} isInitialDOM Whether $changesListContent is the
existing (already attached) DOM
+ * @param {boolean} from Timestamp of the new changes
*/
- mw.rcfilters.ui.ChangesListWrapperWidget.prototype.onModelUpdate =
function ( $changesListContent, $fieldset, isInitialDOM ) {
+ mw.rcfilters.ui.ChangesListWrapperWidget.prototype.onModelUpdate =
function (
+ $changesListContent, $fieldset, isInitialDOM, from
+ ) {
var conflictItem,
$message = $( '<div>' )
.addClass(
'mw-rcfilters-ui-changesListWrapperWidget-results' ),
- isEmpty = $changesListContent === 'NO_RESULTS';
+ isEmpty = $changesListContent === 'NO_RESULTS',
+ $lastSeen,
+ $indicator,
+ $newChanges = $( [] );
this.$element.toggleClass( 'mw-changeslist', !isEmpty );
if ( isEmpty ) {
- this.$changesListContent = null;
this.$element.empty();
if ( this.filtersViewModel.hasConflict() ) {
@@ -121,10 +132,41 @@
this.$element.append( $message );
} else {
- this.$changesListContent = $changesListContent;
if ( !isInitialDOM ) {
- this.$element.empty().append(
this.$changesListContent );
+ this.$element.empty().append(
$changesListContent );
+
+ if ( from ) {
+ $lastSeen = null;
+ this.$element.find( 'li[data-mw-ts]'
).each( function () {
+ var $li = $( this ),
+ ts = $li.data( 'mw-ts'
);
+
+ if ( ts >= from ) {
+ $newChanges =
$newChanges.add( $li );
+ } else if ( $lastSeen === null
) {
+ $lastSeen = $li;
+ return false;
+ }
+ } );
+
+ if ( $lastSeen ) {
+ $indicator = $( '<div>' )
+ .addClass(
'mw-rcfilters-ui-changesListWrapperWidget-previousChangesIndicator' )
+ .text( mw.message(
'rcfilters-previous-changes-label' ).text() );
+
+ $indicator.on( 'click',
function () {
+ $indicator.detach();
+ } );
+
+ $lastSeen.before( $indicator );
+ }
+
+ $newChanges
+ .hide()
+ .fadeIn( 1000 );
+ }
}
+
// Set up highlight containers
this.setupHighlightContainers( this.$element );
@@ -142,14 +184,52 @@
};
/**
+ * Respond to changes list model newChangesExist
+ *
+ * @param {boolean} newChangesExist Whether new changes exist
+ */
+ mw.rcfilters.ui.ChangesListWrapperWidget.prototype.onNewChangesExist =
function ( newChangesExist ) {
+ this.showNewChangesLink.toggle( newChangesExist );
+ };
+
+ /**
+ * Respond to the user clicking the 'show new changes' button
+ */
+
mw.rcfilters.ui.ChangesListWrapperWidget.prototype.onShowNewChangesClick =
function () {
+ this.controller.showNewChanges();
+ };
+
+ /**
+ * Setup the container for the 'new changes' button.
+ *
+ * @param {jQuery} $content
+ */
+
mw.rcfilters.ui.ChangesListWrapperWidget.prototype.setupNewChangesButtonContainer
= function ( $content ) {
+ this.showNewChangesLink = new OO.ui.ButtonWidget( {
+ framed: false,
+ label: mw.message( 'rcfilters-show-new-changes'
).text(),
+ flags: [ 'progressive' ]
+ } );
+ this.showNewChangesLink.connect( this, { click:
'onShowNewChangesClick' } );
+ this.showNewChangesLink.toggle( false );
+
+ $content.before(
+ $( '<div>' )
+ .addClass(
'mw-rcfilters-ui-changesListWrapperWidget-newChanges' )
+ .append( this.showNewChangesLink.$element )
+ );
+ };
+
+ /**
* Set up the highlight containers with all color circle indicators.
*
* @param {jQuery|string} $content The content of the updated changes
list
*/
mw.rcfilters.ui.ChangesListWrapperWidget.prototype.setupHighlightContainers =
function ( $content ) {
var uri = new mw.Uri(),
+ highlightClass =
'mw-rcfilters-ui-changesListWrapperWidget-highlights',
$highlights = $( '<div>' )
- .addClass(
'mw-rcfilters-ui-changesListWrapperWidget-highlights' )
+ .addClass( highlightClass )
.append(
$( '<div>' )
.addClass(
'mw-rcfilters-ui-changesListWrapperWidget-highlights-color-none' )
diff --git
a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterWrapperWidget.js
b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterWrapperWidget.js
index a6b363d..d3cbc20 100644
---
a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterWrapperWidget.js
+++
b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterWrapperWidget.js
@@ -9,11 +9,14 @@
* @param {mw.rcfilters.Controller} controller Controller
* @param {mw.rcfilters.dm.FiltersViewModel} model View model
* @param {mw.rcfilters.dm.SavedQueriesModel} savedQueriesModel Saved
queries model
+ * @param {mw.rcfilters.dm.ChangesListViewModel} changesListModel
* @param {Object} [config] Configuration object
* @cfg {Object} [filters] A definition of the filter groups in this
list
* @cfg {jQuery} [$overlay] A jQuery object serving as overlay for
popups
*/
- mw.rcfilters.ui.FilterWrapperWidget = function
MwRcfiltersUiFilterWrapperWidget( controller, model, savedQueriesModel, config
) {
+ mw.rcfilters.ui.FilterWrapperWidget = function
MwRcfiltersUiFilterWrapperWidget(
+ controller, model, savedQueriesModel, changesListModel, config
+ ) {
var $top, $topRow, $bottom;
config = config || {};
@@ -35,7 +38,8 @@
);
this.liveUpdateButton = new
mw.rcfilters.ui.LiveUpdateButtonWidget(
- this.controller
+ this.controller,
+ changesListModel
);
this.numChangesWidget = new
mw.rcfilters.ui.ChangesLimitButtonWidget(
diff --git
a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FormWrapperWidget.js
b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FormWrapperWidget.js
index 50e3637..44f6da7 100644
--- a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FormWrapperWidget.js
+++ b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FormWrapperWidget.js
@@ -54,7 +54,7 @@
* @return {boolean} false
*/
mw.rcfilters.ui.FormWrapperWidget.prototype.onLinkClick = function ( e
) {
- this.controller.updateChangesList( $( e.target ).data( 'params'
) );
+ this.controller.updateChangesList( true, $( e.target ).data(
'params' ) );
return false;
};
@@ -78,7 +78,7 @@
data[ $( this ).prop( 'name' ) ] = value;
} );
- this.controller.updateChangesList( data );
+ this.controller.updateChangesList( true, data );
return false;
};
@@ -143,11 +143,20 @@
this.$element.find( '.mw-recentchanges-table'
).detach();
this.$element.find( 'hr' ).detach();
}
+
if ( !this.$element.find( '.rcshowhide' ).contents().length ) {
this.$element.find( '.rcshowhide' ).detach();
// If we're hiding rcshowhide, the '<br>'s are around
it,
// there's no need for them either.
this.$element.find( 'br' ).detach();
}
+
+ this.$element.find(
+ 'legend, .rclistfrom, .rcnotefrom,
.rcoptions-listfromreset'
+ ).detach();
+
+ if ( this.$element.text().trim() === '' ) {
+ this.$element.detach();
+ }
};
}( mediaWiki ) );
diff --git
a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.LiveUpdateButtonWidget.js
b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.LiveUpdateButtonWidget.js
index 90ee4d7..67c113d 100644
---
a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.LiveUpdateButtonWidget.js
+++
b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.LiveUpdateButtonWidget.js
@@ -6,9 +6,10 @@
*
* @constructor
* @param {mw.rcfilters.Controller} controller
- * @param {Object} config Configuration object
+ * @param {mw.rcfilters.dm.ChangesListViewModel} changesListModel
+ * @param {Object} [config] Configuration object
*/
- mw.rcfilters.ui.LiveUpdateButtonWidget = function
MwRcfiltersUiLiveUpdateButtonWidget( controller, config ) {
+ mw.rcfilters.ui.LiveUpdateButtonWidget = function
MwRcfiltersUiLiveUpdateButtonWidget( controller, changesListModel, config ) {
config = config || {};
// Parent
@@ -18,9 +19,11 @@
}, config ) );
this.controller = controller;
+ this.model = changesListModel;
// Events
- this.connect( this, { change: 'onChange' } );
+ this.connect( this, { click: 'onClick' } );
+ this.model.connect( this, { liveUpdateChange:
'onLiveUpdateChange' } );
this.$element.addClass(
'mw-rcfilters-ui-liveUpdateButtonWidget' );
};
@@ -32,11 +35,20 @@
/* Methods */
/**
- * Respond to the button being toggled.
- * @param {boolean} enable Whether the button is now pressed/enabled
+ * Respond to the button being clicked
*/
- mw.rcfilters.ui.LiveUpdateButtonWidget.prototype.onChange = function (
enable ) {
- this.controller.toggleLiveUpdate( enable );
+ mw.rcfilters.ui.LiveUpdateButtonWidget.prototype.onClick = function () {
+ this.controller.toggleLiveUpdate();
+ };
+
+ /**
+ * Respond to the 'live update' feature being turned on/off
+ *
+ * @param {boolean} enable Whether the 'live update' feature is now
on/off
+ */
+ mw.rcfilters.ui.LiveUpdateButtonWidget.prototype.onLiveUpdateChange =
function ( enable ) {
+ this.setValue( enable );
+ this.setIcon( enable ? 'stop' : 'play' );
};
}( mediaWiki ) );
diff --git a/tests/phpunit/includes/changes/EnhancedChangesListTest.php
b/tests/phpunit/includes/changes/EnhancedChangesListTest.php
index 28818d9..465bc22 100644
--- a/tests/phpunit/includes/changes/EnhancedChangesListTest.php
+++ b/tests/phpunit/includes/changes/EnhancedChangesListTest.php
@@ -99,7 +99,7 @@
$enhancedChangesList->recentChangesLine( $recentChange, false );
$html = $enhancedChangesList->endRecentChangesList();
- $this->assertRegExp( '/data-mw-revid="5"
class="[^"]*mw-enhanced-rc[^"]*"/', $html );
+ $this->assertRegExp( '/data-mw-revid="5"
data-mw-ts="20131103092153" class="[^"]*mw-enhanced-rc[^"]*"/', $html );
$recentChange2 = $this->getEditChange( '20131103092253' );
$enhancedChangesList->recentChangesLine( $recentChange2, false
);
@@ -133,7 +133,7 @@
private function getEditChange( $timestamp ) {
$user = $this->getMutableTestUser()->getUser();
$recentChange =
$this->testRecentChangesHelper->makeEditRecentChange(
- $user, 'Cat', $timestamp, 5, 191, 190, 0, 0
+ $user, 'Cat', 0, 5, 191, $timestamp, 0, 0
);
return $recentChange;
diff --git a/tests/phpunit/includes/changes/OldChangesListTest.php
b/tests/phpunit/includes/changes/OldChangesListTest.php
index f892eb7..90c60c8 100644
--- a/tests/phpunit/includes/changes/OldChangesListTest.php
+++ b/tests/phpunit/includes/changes/OldChangesListTest.php
@@ -126,9 +126,9 @@
$oldChangesList = $this->getOldChangesList();
$line = $oldChangesList->recentChangesLine( $recentChange,
false, 1 );
- $this->assertRegExp( '/<li data-mw-revid="\d+"
class="[\w\s-]*mw-tag-vandalism[\w\s-]*">/',
+ $this->assertRegExp( '/<li data-mw-revid="\d+" data-mw-ts="\d+"
class="[\w\s-]*mw-tag-vandalism[\w\s-]*">/',
$line );
- $this->assertRegExp( '/<li data-mw-revid="\d+"
class="[\w\s-]*mw-tag-newbie[\w\s-]*">/',
+ $this->assertRegExp( '/<li data-mw-revid="\d+" data-mw-ts="\d+"
class="[\w\s-]*mw-tag-newbie[\w\s-]*">/',
$line );
}
--
To view, visit https://gerrit.wikimedia.org/r/366861
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I6a111d23956bdc04caa4c71e9deede056779aafa
Gerrit-PatchSet: 12
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Sbisson <[email protected]>
Gerrit-Reviewer: Catrope <[email protected]>
Gerrit-Reviewer: Florianschmidtwelzow <[email protected]>
Gerrit-Reviewer: Jack Phoenix <[email protected]>
Gerrit-Reviewer: Sbisson <[email protected]>
Gerrit-Reviewer: Siebrand <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits