' );
- const selections = [];
- checks.forEach( ( check ) => {
- $checks.append( new OO.ui.MessageWidget( {
- type: 'warning',
- label: check.message,
- framed: false
- } ).$element );
+ // We only regenerate the checks on-change during the edit. If we're in
+ // the proofreading step, no new checks should appear based on changes:
+ if ( this.listener === 'onDocumentChange' || !this.currentChecks ) {
+ this.currentChecks = mw.editcheck.editCheckFactory.createAllByListener( this.listener, this.surface.getModel() );
+ }
+ if ( this.listener === 'onBeforeSave' && this.currentChecks.length === 0 ) {
+ return this.close( 'complete' );
+ }
+ const checks = this.currentChecks;
+ const newOffset = Math.min( this.currentOffset, checks.length - 1 );
+ this.$checks.empty();
+ this.$highlights.empty();
+
+ checks.forEach( ( check, index ) => {
+ const widget = check.render( index !== newOffset, this.listener === 'onBeforeSave', this.surface );
+ widget.on( 'togglecollapse', this.onToggleCollapse, [ check, index ], this );
+ widget.on( 'act', this.onAct, [ widget ], this );
+ this.$checks.append( widget.$element );
+ check.widget = widget;
+ } );
+
+ if ( this.reviewMode ) {
+ // Review mode grays out everything that's not highlighted:
+ const highlightNodes = [];
+ checks.forEach( ( check ) => {
+ check.getHighlightSelections().forEach( ( selection ) => {
+ highlightNodes.push.apply( highlightNodes, surfaceView.getDocument().selectNodes( selection.getCoveringRange(), 'branches' ).map( ( spec ) => spec.node ) );
+ } );
+ } );
+ surfaceView.setReviewMode( true, highlightNodes );
+ }
+
+ this.setCurrentOffset( newOffset );
+};
+
+ve.ui.EditCheckDialog.prototype.position = function () {
+ this.drawHighlights();
+ this.scrollCurrentCheckIntoView();
+};
+
+ve.ui.EditCheckDialog.prototype.drawHighlights = function () {
+ const surfaceView = this.surface.getView();
+ this.$highlights.empty();
+
+ this.currentChecks.forEach( ( check, index ) => {
check.getHighlightSelections().forEach( ( selection ) => {
- selections.push( ve.ce.Selection.static.newFromModel( selection, surfaceView ) );
+ const selectionView = ve.ce.Selection.static.newFromModel( selection, surfaceView );
+ const rect = selectionView.getSelectionBoundingRect();
+ // The following classes are used here:
+ // * ve-ui-editCheck-gutter-highlight-error
+ // * ve-ui-editCheck-gutter-highlight-warning
+ // * ve-ui-editCheck-gutter-highlight-notice
+ // * ve-ui-editCheck-gutter-highlight-success
+ // * ve-ui-editCheck-gutter-highlight-active
+ // * ve-ui-editCheck-gutter-highlight-inactive
+ this.$highlights.append( $( '
' )
+ .addClass( 've-ui-editCheck-gutter-highlight' )
+ .addClass( 've-ui-editCheck-gutter-highlight-' + check.getType() )
+ .addClass( 've-ui-editCheck-gutter-highlight-' + ( index === this.currentOffset ? 'active' : 'inactive' ) )
+ .css( {
+ top: rect.top - 2,
+ height: rect.height + 4
+ } )
+ );
} );
} );
- surfaceView.getSelectionManager().drawSelections( 'editCheckWarning', selections );
- this.$body.empty().append( $checks );
+
+ surfaceView.appendHighlights( this.$highlights, false );
+};
+
+/**
+ * Set the offset of the current check, within the list of all checks
+ *
+ * @param {number} offset
+ */
+ve.ui.EditCheckDialog.prototype.setCurrentOffset = function ( offset ) {
+ // TODO: work out how to tell the window to recalculate height here
+ this.currentOffset = Math.max( 0, offset );
+
+ this.$body.find( '.ve-ui-editCheckActionWidget' ).each( ( i, el ) => {
+ $( el ).toggleClass( 've-ui-editCheckActionWidget-collapsed', i !== this.currentOffset );
+ } );
+
+ this.footerLabel.setLabel(
+ ve.msg( 'visualeditor-find-and-replace-results',
+ ve.init.platform.formatNumber( this.currentOffset + 1 ),
+ ve.init.platform.formatNumber( this.currentChecks.length )
+ )
+ );
+ this.nextButton.setDisabled( this.currentOffset >= this.currentChecks.length - 1 );
+ this.previousButton.setDisabled( this.currentOffset <= 0 );
+
+ this.updateSize();
+
+ const surfaceView = this.surface.getView();
+ if ( this.currentChecks.length > 0 ) {
+ // The currently-focused check gets a selection:
+ // TODO: clicking the selection should activate the sidebar-action
+ surfaceView.getSelectionManager().drawSelections(
+ 'editCheckWarning',
+ this.currentChecks[ this.currentOffset ].getHighlightSelections().map(
+ ( selection ) => ve.ce.Selection.static.newFromModel( selection, surfaceView )
+ )
+ );
+
+ this.scrollCurrentCheckIntoView();
+ } else {
+ surfaceView.getSelectionManager().drawSelections( 'editCheckWarning', [] );
+ }
+
+ this.drawHighlights();
+};
+
+ve.ui.EditCheckDialog.prototype.scrollCurrentCheckIntoView = function () {
+ const currentCheck = this.currentChecks[ this.currentOffset ];
+ if ( currentCheck ) {
+ // scrollSelectionIntoView scrolls to the focus of a selection, but we
+ // want the very beginning to be in view, so collapse it:
+ const selection = currentCheck.getHighlightSelections()[ 0 ].collapseToStart();
+ this.surface.scrollSelectionIntoView( selection, {
+ animate: true,
+ padding: {
+ top: ( OO.ui.isMobile() ? 80 : currentCheck.widget.$element[ 0 ].getBoundingClientRect().top ),
+ bottom: ( OO.ui.isMobile() ? this.getContentHeight() : 0 ) + 20
+ },
+ alignToTop: true
+ } );
+ }
};
/**
@@ -74,8 +237,24 @@ ve.ui.EditCheckDialog.prototype.update = function () {
ve.ui.EditCheckDialog.prototype.getSetupProcess = function ( data ) {
return ve.ui.EditCheckDialog.super.prototype.getSetupProcess.call( this, data )
.first( () => {
+ this.currentOffset = 0;
+ this.listener = data.listener || 'onDocumentChange';
+ this.reviewMode = data.reviewMode;
this.surface = data.surface;
+
this.surface.getModel().on( 'undoStackChange', this.updateDebounced );
+ this.surface.getView().on( 'position', this.positionDebounced );
+
+ this.closeButton.toggle( OO.ui.isMobile() );
+ this.footer.toggle(
+ this.listener === 'onBeforeSave' &&
+ !mw.config.get( 'wgVisualEditorConfig' ).editCheckSingle
+ );
+
+ this.$element.toggleClass( 've-ui-editCheckDialog-singleAction', this.listener === 'onBeforeSave' );
+
+ this.surface.context.hide();
+
this.update();
}, this );
};
@@ -86,6 +265,12 @@ ve.ui.EditCheckDialog.prototype.getSetupProcess = function ( data ) {
ve.ui.EditCheckDialog.prototype.getReadyProcess = function ( data ) {
return ve.ui.EditCheckDialog.super.prototype.getReadyProcess.call( this, data )
.next( () => {
+ // The end of the ready process triggers a reflow after an
+ // animation, so we need to get past that to avoid the content
+ // being immediately scrolled away
+ setTimeout( () => {
+ this.scrollCurrentCheckIntoView();
+ }, 500 );
}, this );
};
@@ -95,17 +280,105 @@ ve.ui.EditCheckDialog.prototype.getReadyProcess = function ( data ) {
ve.ui.EditCheckDialog.prototype.getTeardownProcess = function ( data ) {
return ve.ui.EditCheckDialog.super.prototype.getTeardownProcess.call( this, data )
.next( () => {
+ this.surface.getView().setReviewMode( false );
+ this.surface.getView().getSelectionManager().drawSelections( 'editCheckWarning', [] );
+ this.surface.getView().off( 'position', this.positionDebounced );
this.surface.getModel().off( 'undoStackChange', this.updateDebounced );
+ this.$highlights.remove().empty();
+ this.$checks.empty();
}, this );
};
+/**
+ * Handle 'act' events from the mw.widget.EditCheckActionWidget
+ *
+ * @param {mw.editcheck.EditCheckActionWidget} widget
+ * @param {Object} choice Choice object (with 'reason', 'object', 'label')
+ * @param {string} actionChosen Choice action
+ * @param {jQuery.Promise} promise Promise which resolves when the action is complete
+ */
+ve.ui.EditCheckDialog.prototype.onAct = function ( widget, choice, actionChosen, promise ) {
+ widget.setDisabled( true );
+ this.nextButton.setDisabled( true );
+ this.previousButton.setDisabled( true );
+ promise.then( ( data ) => {
+ widget.setDisabled( false );
+ this.nextButton.setDisabled( false );
+ this.previousButton.setDisabled( false );
+ this.surface.getModel().setNullSelection();
+ if ( OO.ui.isMobile() ) {
+ // Delay on mobile means we need to rehide this
+ setTimeout( () => this.surface.getModel().setNullSelection(), 300 );
+ }
+
+ if ( !data ) {
+ // Nothing happened, just fall back and leave the check
+ return;
+ }
+
+ if ( this.listener === 'onBeforeSave' ) {
+ // We must have been acting on the currentOffset
+ setTimeout( () => {
+ // We want to linger for a brief moment before moving away
+ this.currentChecks.splice( this.currentOffset, 1 );
+ this.currentOffset = Math.max( 0, this.currentOffset - 1 );
+ this.update();
+ }, 500 );
+ }
+ } );
+};
+
+/**
+ * Handle 'togglecollapse' events from the mw.widget.EditCheckActionWidget
+ *
+ * @param {mw.editcheck.EditCheckAction} check
+ * @param {number} index
+ * @param {boolean} collapsed
+ */
+ve.ui.EditCheckDialog.prototype.onToggleCollapse = function ( check, index, collapsed ) {
+ if ( !collapsed ) {
+ // expanded one
+ this.setCurrentOffset( this.currentChecks.indexOf( check ) );
+ }
+};
+
+/**
+ * Handle click events from the close button
+ */
+ve.ui.EditCheckDialog.prototype.onCloseButtonClick = function () {
+ // eslint-disable-next-line no-jquery/no-class-state
+ const collapse = !this.$element.hasClass( 've-ui-editCheckDialog-collapsed' );
+ this.$element.toggleClass( 've-ui-editCheckDialog-collapsed', collapse );
+ this.closeButton.setIcon( collapse ? 'collapse' : 'expand' );
+};
+
+/**
+ * Handle click events from the next button
+ */
+ve.ui.EditCheckDialog.prototype.onNextButtonClick = function () {
+ this.setCurrentOffset( this.currentOffset + 1 );
+};
+
+/**
+ * Handle click events from the previous button
+ */
+ve.ui.EditCheckDialog.prototype.onPreviousButtonClick = function () {
+ this.setCurrentOffset( this.currentOffset - 1 );
+};
+
/* Registration */
ve.ui.windowFactory.register( ve.ui.EditCheckDialog );
ve.ui.commandRegistry.register(
new ve.ui.Command(
- 'editCheckDialog', 'window', 'toggle', { args: [ 'editCheckDialog' ] }
+ 'editCheckDialogInProcess', 'window', 'toggle', { args: [ 'editCheckDialog', { listener: 'onDocumentChange' } ] }
+ )
+);
+
+ve.ui.commandRegistry.register(
+ new ve.ui.Command(
+ 'editCheckDialogBeforeSave', 'window', 'toggle', { args: [ 'editCheckDialog', { listener: 'onBeforeSave', reviewMode: true } ] }
)
);
@@ -125,7 +398,8 @@ ve.ui.EditCheckDialogTool.static.group = 'notices';
ve.ui.EditCheckDialogTool.static.icon = 'robot';
ve.ui.EditCheckDialogTool.static.title = 'Edit check'; // OO.ui.deferMsg( 'visualeditor-dialog-command-help-title' );
ve.ui.EditCheckDialogTool.static.autoAddToCatchall = false;
-ve.ui.EditCheckDialogTool.static.commandName = 'editCheckDialog';
+ve.ui.EditCheckDialogTool.static.commandName = 'editCheckDialogInProcess';
+// ve.ui.EditCheckDialogTool.static.commandName = 'editCheckDialogBeforeSave';
// Demo button for opening edit check sidebar
// ve.ui.toolFactory.register( ve.ui.EditCheckDialogTool );
diff --git a/editcheck/modules/EditCheckFactory.js b/editcheck/modules/EditCheckFactory.js
index 0487515075..b514d937a6 100644
--- a/editcheck/modules/EditCheckFactory.js
+++ b/editcheck/modules/EditCheckFactory.js
@@ -66,7 +66,7 @@ mw.editcheck.EditCheckFactory.prototype.getNamesByListener = function ( listener
* @return {mw.editcheck.EditCheckActions[]} Actions, sorted by range
*/
mw.editcheck.EditCheckFactory.prototype.createAllByListener = function ( listener, surfaceModel ) {
- const newChecks = [];
+ let newChecks = [];
this.getNamesByListener( listener ).forEach( ( checkName ) => {
const check = this.create( checkName, mw.editcheck.config[ checkName ] );
if ( !check.canBeShown() ) {
@@ -80,6 +80,10 @@ mw.editcheck.EditCheckFactory.prototype.createAllByListener = function ( listene
newChecks.sort(
( a, b ) => a.getHighlightSelections()[ 0 ].getCoveringRange().start - b.getHighlightSelections()[ 0 ].getCoveringRange().start
);
+ if ( mw.config.get( 'wgVisualEditorConfig' ).editCheckSingle && listener === 'onBeforeSave' ) {
+ newChecks = newChecks.filter( ( action ) => action.getName() === 'addReference' );
+ newChecks.splice( 1 );
+ }
return newChecks;
};
diff --git a/editcheck/modules/EditCheckInspector.js b/editcheck/modules/EditCheckInspector.js
index 8cb23dc3d7..0021dc1fd6 100644
--- a/editcheck/modules/EditCheckInspector.js
+++ b/editcheck/modules/EditCheckInspector.js
@@ -119,7 +119,6 @@ ve.ui.EditCheckInspector.prototype.getSetupProcess = function ( data ) {
return ve.ui.EditCheckInspector.super.prototype.getSetupProcess.call( this, data )
.first( function () {
this.surface = data.surface;
- this.saveProcessDeferred = data.saveProcessDeferred;
this.answerRadioSelect.selectItem( null );
}, this );
};
diff --git a/editcheck/modules/editchecks/AddReferenceEditCheck.js b/editcheck/modules/editchecks/AddReferenceEditCheck.js
index 4f2d40470a..c66f7da5e2 100644
--- a/editcheck/modules/editchecks/AddReferenceEditCheck.js
+++ b/editcheck/modules/editchecks/AddReferenceEditCheck.js
@@ -7,6 +7,8 @@ OO.inheritClass( mw.editcheck.AddReferenceEditCheck, mw.editcheck.BaseEditCheck
mw.editcheck.AddReferenceEditCheck.static.name = 'addReference';
+mw.editcheck.AddReferenceEditCheck.static.title = ve.msg( 'editcheck-dialog-addref-title' );
+
mw.editcheck.AddReferenceEditCheck.static.description = ve.msg( 'editcheck-dialog-addref-description' );
mw.editcheck.AddReferenceEditCheck.static.defaultConfig = ve.extendObject( {}, mw.editcheck.BaseEditCheck.static.defaultConfig, {
@@ -20,9 +22,11 @@ mw.editcheck.AddReferenceEditCheck.prototype.onBeforeSave = function ( surfaceMo
return new mw.editcheck.EditCheckAction( {
fragments: [ fragment ],
check: this
+ // icon: 'quotes',
} );
} );
};
+mw.editcheck.AddReferenceEditCheck.prototype.onDocumentChange = mw.editcheck.AddReferenceEditCheck.prototype.onBeforeSave;
/**
* Find content ranges which have been inserted
@@ -53,9 +57,9 @@ mw.editcheck.AddReferenceEditCheck.prototype.findAddedContent = function ( docum
return ranges;
};
-mw.editcheck.AddReferenceEditCheck.prototype.act = function ( choice, action, contextItem ) {
+mw.editcheck.AddReferenceEditCheck.prototype.act = function ( choice, action, surface ) {
// The complex citoid workflow means that we can't just count on a single "windowAction" here...
- const windowAction = ve.ui.actionFactory.create( 'window', contextItem.context.getSurface(), 'check' );
+ const windowAction = ve.ui.actionFactory.create( 'window', surface, 'check' );
switch ( choice ) {
case 'accept':
ve.track( 'activity.editCheckReferences', { action: 'edit-check-confirm' } );
@@ -67,7 +71,7 @@ mw.editcheck.AddReferenceEditCheck.prototype.act = function ( choice, action, co
if ( citoidData && citoidData.action === 'manual-choose' ) {
// The plain reference dialog has been launched. Wait for the data from
// the basic Cite closing promise instead.
- contextItem.context.getSurface().getDialogs().once( 'closing', ( win, closed, citeData ) => {
+ surface.getDialogs().once( 'closing', ( win, closed, citeData ) => {
citoidOrCiteDataDeferred.resolve( citeData );
} );
} else {
@@ -75,18 +79,11 @@ mw.editcheck.AddReferenceEditCheck.prototype.act = function ( choice, action, co
// use the data form the Citoid closing promise.
citoidOrCiteDataDeferred.resolve( citoidData );
}
- citoidOrCiteDataDeferred.promise().then( ( data ) => {
- if ( !data ) {
- // Reference was not inserted - re-open this context
- setTimeout( () => {
- // Deactivate again for mobile after teardown has modified selections
- contextItem.context.getSurface().getView().deactivate();
- contextItem.context.afterContextChange();
- }, 500 );
- } else {
+ return citoidOrCiteDataDeferred.promise().done( ( data ) => {
+ if ( data ) {
// Edit check inspector is already closed by this point, but
// we need to end the workflow.
- contextItem.close( citoidData );
+ mw.notify( ve.msg( 'editcheck-dialog-addref-success-notify' ), { type: 'success' } );
}
} );
} );
@@ -95,20 +92,14 @@ mw.editcheck.AddReferenceEditCheck.prototype.act = function ( choice, action, co
return windowAction.open(
'editCheckReferencesInspector',
{
- fragment: action.fragments[ 0 ],
- callback: contextItem.data.callback,
- saveProcessDeferred: contextItem.data.saveProcessDeferred
+ fragment: action.fragments[ 0 ]
}
// eslint-disable-next-line arrow-body-style
).then( ( instance ) => {
- // contextItem.openingCitoid = false;
return instance.closing;
- } ).then( ( data ) => {
- if ( !data ) {
- // Form was closed, re-open this context
- contextItem.context.afterContextChange();
- } else {
- contextItem.close( data );
+ } ).done( ( data ) => {
+ if ( data && data.action === 'reject' && data.reason ) {
+ mw.editcheck.rejections.push( data.reason );
}
} );
}
diff --git a/editcheck/modules/editchecks/TextMatchEditCheck.js b/editcheck/modules/editchecks/TextMatchEditCheck.js
index 913eb34ba0..4da3fd3bae 100644
--- a/editcheck/modules/editchecks/TextMatchEditCheck.js
+++ b/editcheck/modules/editchecks/TextMatchEditCheck.js
@@ -7,10 +7,13 @@ OO.inheritClass( mw.editcheck.TextMatchEditCheck, mw.editcheck.BaseEditCheck );
mw.editcheck.TextMatchEditCheck.static.name = 'textMatch';
+mw.editcheck.TextMatchEditCheck.static.choices = [];
+
mw.editcheck.TextMatchEditCheck.static.replacers = [
// TODO: Load text replacement rules from community config
{
query: 'unfortunately',
+ title: 'Adverb usage',
message: new OO.ui.HtmlSnippet( 'Use of adverbs such as "unfortunately" should usually be avoided so as to maintain an impartial tone.
Read more.' )
}
];
@@ -23,6 +26,7 @@ mw.editcheck.TextMatchEditCheck.prototype.onDocumentChange = function ( surfaceM
actions.push(
new mw.editcheck.EditCheckAction( {
fragments: [ fragment ],
+ title: replacer.title,
message: replacer.message,
check: this
} )
@@ -32,4 +36,6 @@ mw.editcheck.TextMatchEditCheck.prototype.onDocumentChange = function ( surfaceM
return actions;
};
+// mw.editcheck.TextMatchEditCheck.prototype.onBeforeSave = mw.editcheck.TextMatchEditCheck.prototype.onDocumentChange;
+
mw.editcheck.editCheckFactory.register( mw.editcheck.TextMatchEditCheck );
diff --git a/editcheck/modules/init.js b/editcheck/modules/init.js
index df26a8847d..e21e7cc605 100644
--- a/editcheck/modules/init.js
+++ b/editcheck/modules/init.js
@@ -83,8 +83,12 @@ if ( mw.config.get( 'wgVisualEditorConfig' ).editCheckTagging ) {
}
if ( mw.config.get( 'wgVisualEditorConfig' ).editCheck || mw.editcheck.ecenable ) {
- let saveProcessDeferred;
-
+ mw.hook( 've.activationStart' ).add( () => {
+ document.documentElement.classList.add( 've-editcheck-available' );
+ } );
+ mw.hook( 've.deactivationComplete' ).add( () => {
+ document.documentElement.classList.remove( 've-editcheck-available' );
+ } );
mw.hook( 've.preSaveProcess' ).add( ( saveProcess, target ) => {
const surface = target.getSurface();
@@ -101,12 +105,11 @@ if ( mw.config.get( 'wgVisualEditorConfig' ).editCheck || mw.editcheck.ecenable
// clear rejection-reasons between runs of the save process, so only the last one counts
mw.editcheck.rejections.length = 0;
- let checks = mw.editcheck.editCheckFactory.createAllByListener( 'onBeforeSave', surface.getModel() );
+ const checks = mw.editcheck.editCheckFactory.createAllByListener( 'onBeforeSave', surface.getModel() );
if ( checks.length ) {
ve.track( 'counter.editcheck.preSaveChecksShown' );
mw.editcheck.refCheckShown = true;
- const surfaceView = surface.getView();
const toolbar = target.getToolbar();
const reviewToolbar = new ve.ui.PositionedTargetToolbar( target, target.toolbarConfig );
reviewToolbar.setup( [
@@ -142,122 +145,51 @@ if ( mw.config.get( 'wgVisualEditorConfig' ).editCheck || mw.editcheck.ecenable
target.toolbar.$element.before( reviewToolbar.$element );
target.toolbar = reviewToolbar;
- saveProcessDeferred = ve.createDeferred();
- const context = surface.getContext();
-
- // TODO: Allow multiple checks to be shown when multicheck is enabled
- checks = checks.slice( 0, 1 );
-
- // eslint-disable-next-line no-shadow
- const drawSelections = ( checks ) => {
- const highlightNodes = [];
- const selections = [];
- checks.forEach( ( check ) => {
- check.getHighlightSelections().forEach( ( selection ) => {
- highlightNodes.push.apply( highlightNodes, surfaceView.getDocument().selectNodes( selection.getCoveringRange(), 'branches' ).map( ( spec ) => spec.node ) );
- const selectionView = ve.ce.Selection.static.newFromModel( selection, surfaceView );
- selections.push( selectionView );
- } );
- } );
- // TODO: Make selections clickable when multicheck is enabled
- surfaceView.getSelectionManager().drawSelections(
- 'editCheck',
- selections
- );
- surfaceView.setReviewMode( true, highlightNodes );
- };
-
- const contextDone = ( responseData, contextData ) => {
- if ( !responseData ) {
- // this is the back button
- return saveProcessDeferred.resolve();
- }
- const selectionIndex = checks.indexOf( contextData.action );
-
- if ( responseData.action !== 'reject' ) {
- mw.notify( ve.msg( 'editcheck-dialog-addref-success-notify' ), { type: 'success' } );
- } else if ( responseData.reason ) {
- mw.editcheck.rejections.push( responseData.reason );
- }
- // TODO: Move on to the next issue, when multicheck is enabled
- // checks = mw.editcheck.editCheckFactory.createAllByListener( 'onBeforeSave', surface.getModel() );
- checks = [];
-
- if ( checks.length ) {
- context.removePersistentSource( 'editCheckReferences' );
- setTimeout( () => {
- // timeout needed to wait out the newly added content being focused
- surface.getModel().setNullSelection();
- drawSelections( checks );
- setTimeout( () => {
- // timeout needed to allow the context to reposition
- showCheckContext( checks[ Math.min( selectionIndex, checks.length - 1 ) ] );
- } );
- }, 500 );
- } else {
- saveProcessDeferred.resolve( true );
- }
- };
-
- // eslint-disable-next-line no-inner-declarations
- function showCheckContext( check ) {
- const fragment = check.fragments[ 0 ];
-
- // Select the found content to correctly position the context on desktop
- fragment.select();
-
- context.addPersistentSource( {
- embeddable: false,
- data: {
- action: check,
- fragment: fragment,
- callback: contextDone,
- saveProcessDeferred: saveProcessDeferred
- },
- name: 'editCheckReferences'
- } );
-
- // Deactivate to prevent selection suppressing mobile context
- surface.getView().deactivate();
-
- // Once the context is positioned, clear the selection
- setTimeout( () => {
- surface.getModel().setNullSelection();
- } );
+ let $contextContainer, contextPadding;
+ if ( surface.context.popup ) {
+ contextPadding = surface.context.popup.containerPadding;
+ $contextContainer = surface.context.popup.$container;
+ surface.context.popup.$container = surface.$element;
+ surface.context.popup.containerPadding = 20;
}
- drawSelections( checks );
- toolbar.toggle( false );
- target.onContainerScroll();
-
saveProcess.next( () => {
- showCheckContext( checks[ 0 ] );
+ toolbar.toggle( false );
+ target.onContainerScroll();
+ // surface.executeCommand( 'editCheckDialogBeforeSave' );
+ const windowAction = ve.ui.actionFactory.create( 'window', surface, 'check' );
+ return windowAction.open( 'editCheckDialog', { listener: 'onBeforeSave', reviewMode: true } )
+ .then( ( instance ) => instance.closing )
+ .then( ( data ) => {
+ reviewToolbar.$element.remove();
+ toolbar.toggle( true );
+ target.toolbar = toolbar;
+ if ( $contextContainer ) {
+ surface.context.popup.$container = $contextContainer;
+ surface.context.popup.containerPadding = contextPadding;
+ }
+ // Creating a new PositionedTargetToolbar stole the
+ // toolbar windowmanagers, so we need to make the
+ // original toolbar reclaim them:
+ toolbar.disconnect( target );
+ target.setupToolbar( surface );
+ target.onContainerScroll();
- return saveProcessDeferred.promise().then( ( data ) => {
- context.removePersistentSource( 'editCheckReferences' );
-
- surfaceView.getSelectionManager().drawSelections( 'editCheck', [] );
- surfaceView.setReviewMode( false );
-
- reviewToolbar.$element.remove();
- toolbar.toggle( true );
- target.toolbar = toolbar;
- target.onContainerScroll();
-
- // Check the user inserted a citation
- if ( data ) {
- const delay = ve.createDeferred();
- // If they inserted, wait 2 seconds on desktop before showing save dialog
- setTimeout( () => {
- ve.track( 'counter.editcheck.preSaveChecksCompleted' );
- delay.resolve();
- }, !OO.ui.isMobile() && data.action !== 'reject' ? 2000 : 0 );
- return delay.promise();
- } else {
- ve.track( 'counter.editcheck.preSaveChecksAbandoned' );
- return ve.createDeferred().reject().promise();
- }
- } );
+ if ( data ) {
+ const delay = ve.createDeferred();
+ // If they inserted, wait 2 seconds on desktop
+ // before showing save dialog to make sure insertions are finialized
+ setTimeout( () => {
+ ve.track( 'counter.editcheck.preSaveChecksCompleted' );
+ delay.resolve();
+ }, !OO.ui.isMobile() && data.action !== 'reject' ? 2000 : 0 );
+ return delay.promise();
+ } else {
+ // closed via "back" or otherwise
+ ve.track( 'counter.editcheck.preSaveChecksAbandoned' );
+ return ve.createDeferred().reject().promise();
+ }
+ } );
} );
} else {
// Counterpart to earlier preSaveChecksShown, for use in tracking
@@ -265,11 +197,6 @@ if ( mw.config.get( 'wgVisualEditorConfig' ).editCheck || mw.editcheck.ecenable
ve.track( 'counter.editcheck.preSaveChecksNotShown' );
}
} );
- mw.hook( 've.deactivationComplete' ).add( () => {
- if ( saveProcessDeferred ) {
- saveProcessDeferred.reject();
- }
- } );
}
ve.ui.EditCheckBack = function VeUiEditCheckBack() {
@@ -286,12 +213,9 @@ ve.ui.EditCheckBack.static.autoAddToGroup = false;
ve.ui.EditCheckBack.static.title =
OO.ui.deferMsg( 'visualeditor-backbutton-tooltip' );
ve.ui.EditCheckBack.prototype.onSelect = function () {
- const context = this.toolbar.getSurface().getContext();
- if ( context.inspector ) {
- context.inspector.close();
- } else {
- context.items[ 0 ].close();
- }
+ const surface = this.toolbar.getSurface();
+ surface.getContext().hide();
+ surface.execute( 'window', 'close', 'editCheckDialog' );
this.setActive( false );
};
ve.ui.EditCheckBack.prototype.onUpdateState = function () {
diff --git a/extension.json b/extension.json
index 83c1dc617b..3d0e942c9a 100644
--- a/extension.json
+++ b/extension.json
@@ -136,6 +136,10 @@
"value": false,
"description": "Enable experimental Edit Check feature. Can also be enabled using ?ecenable=1."
},
+ "VisualEditorEditCheckSingleCheckMode": {
+ "value": true,
+ "description": "Only allow a single edit check to be surfaced"
+ },
"VisualEditorEditCheckABTest": {
"value": false,
"description": "A/B test Edit Check for all users. A/B bucket status will override VisualEditorEditCheck."
@@ -647,7 +651,10 @@
"editcheck-dialog-addref-success-notify",
"editcheck-dialog-addref-title",
"editcheck-dialog-title",
- "visualeditor-backbutton-tooltip"
+ "editcheck-review-title",
+ "visualeditor-backbutton-tooltip",
+ "next",
+ "last"
]
},
"ext.visualEditor.core.utils": {
diff --git a/includes/Hooks.php b/includes/Hooks.php
index f592a0dff5..f335cf7325 100644
--- a/includes/Hooks.php
+++ b/includes/Hooks.php
@@ -1159,6 +1159,7 @@ class Hooks implements
'useChangeTagging' => $veConfig->get( 'VisualEditorUseChangeTagging' ),
'editCheckTagging' => $veConfig->get( 'VisualEditorEditCheckTagging' ),
'editCheck' => $veConfig->get( 'VisualEditorEditCheck' ),
+ 'editCheckSingle' => $veConfig->get( 'VisualEditorEditCheckSingleCheckMode' ),
'editCheckABTest' => $veConfig->get( 'VisualEditorEditCheckABTest' ),
'editCheckReliabilityAvailable' => ApiEditCheckReferenceUrl::isAvailable(),
'namespacesWithSubpages' => $namespacesWithSubpagesEnabled,