mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-12-18 17:21:25 +00:00
EditCheck: move checks to a sidebar
Bug: T341308 Bug: T379443 Change-Id: I66147d95fc23d0f72960ff93a76b3e5ba65ce44e
This commit is contained in:
parent
b0c8e92155
commit
37627ad9ae
|
@ -18,6 +18,7 @@
|
|||
"editcheck-dialog-addref-success-notify": "Thank you for adding a citation!",
|
||||
"editcheck-dialog-addref-title": "Add a citation",
|
||||
"editcheck-dialog-title": "Before publishing",
|
||||
"editcheck-review-title": "Review changes",
|
||||
"tag-editcheck-reference-decline-common-knowledge": "Edit Check (references) declined (common knowledge)",
|
||||
"tag-editcheck-reference-decline-common-knowledge-description": "EditCheck reference was declined as common knowledge",
|
||||
"tag-editcheck-reference-decline-irrelevant": "Edit Check (references) declined (irrelevant)",
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
"editcheck-dialog-addref-success-notify": "Notification messages shown after a citation is added successfully.",
|
||||
"editcheck-dialog-addref-title": "Title for the edit check context asking user to add a citation.",
|
||||
"editcheck-dialog-title": "Title shown in the toolbar while the user is in the add a citation workflow.",
|
||||
"editcheck-review-title": "Title shown in the sidebar / drawer while checks are displayed",
|
||||
"tag-editcheck-reference-decline-common-knowledge": "Short description of the editcheck-reference-decline-common-knowledge tag.\n\nTag added when a user declines to add a suggested reference and selects the \"common knowledge\" reason.\n\nSee also:\n* {{msg-mw|editcheck-dialog-addref-reject-common-knowledge}}",
|
||||
"tag-editcheck-reference-decline-common-knowledge-description": "Long description of the editcheck-reference-decline-common-knowledge tag.\n\nTag added when a user declines to add a suggested reference and selects the \"common knowledge\" reason.\n\nSee also:\n* {{msg-mw|editcheck-dialog-addref-reject-common-knowledge}}",
|
||||
"tag-editcheck-reference-decline-irrelevant": "Short description of the editcheck-reference-decline-irrelevant tag.\n\nTag added when a user declines to add a suggested reference and selects the \"irrelevant\" reason.\n\nSee also:\n* {{msg-mw|editcheck-dialog-addref-reject-irrelevant}}",
|
||||
|
|
|
@ -26,8 +26,19 @@ mw.editcheck.BaseEditCheck.static.defaultConfig = {
|
|||
ignoreLeadSection: false
|
||||
};
|
||||
|
||||
mw.editcheck.BaseEditCheck.static.title = ve.msg( 'editcheck-review-title' );
|
||||
|
||||
mw.editcheck.BaseEditCheck.static.description = ve.msg( 'editcheck-dialog-addref-description' );
|
||||
|
||||
/**
|
||||
* Get the name of the check type
|
||||
*
|
||||
* @return {string} Check type name
|
||||
*/
|
||||
mw.editcheck.BaseEditCheck.prototype.getName = function () {
|
||||
return this.constructor.static.name;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {ve.dm.Surface} surfaceModel
|
||||
* @return {mw.editcheck.EditCheckAction[]}
|
||||
|
@ -43,7 +54,8 @@ mw.editcheck.BaseEditCheck.prototype.onDocumentChange = null;
|
|||
/**
|
||||
* @param {string} choice `action` key from static.choices
|
||||
* @param {mw.editcheck.EditCheckAction} action
|
||||
* @param {ve.ui.EditCheckContextItem} contextItem
|
||||
* @param {ve.ui.Surface} surface
|
||||
* @return {jQuery.Promise} Promise which resolves when action is complete
|
||||
*/
|
||||
mw.editcheck.BaseEditCheck.prototype.act = null;
|
||||
|
||||
|
@ -55,6 +67,16 @@ mw.editcheck.BaseEditCheck.prototype.getChoices = function () {
|
|||
return this.constructor.static.choices;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the title of the check
|
||||
*
|
||||
* @param {mw.editcheck.EditCheckAction} action
|
||||
* @return {jQuery|string|Function|OO.ui.HtmlSnippet}
|
||||
*/
|
||||
mw.editcheck.BaseEditCheck.prototype.getTitle = function () {
|
||||
return this.constructor.static.title;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {mw.editcheck.EditCheckAction} action
|
||||
* @return {string}
|
||||
|
|
|
@ -1,8 +1,29 @@
|
|||
@import '../../lib/codex-design-tokens/theme-wikimedia-ui.less';
|
||||
|
||||
@media ( max-width: 1492px ) {
|
||||
/* Hides the Vector sidebar while an editcheck-enabled editing session is occurring. See T379443. */
|
||||
/* stylelint-disable selector-class-pattern */
|
||||
.ve-editcheck-available {
|
||||
.mw-body .vector-column-end,
|
||||
.vector-pinnable-header-pin-button {
|
||||
display: none !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
}
|
||||
/* stylelint-enable selector-class-pattern */
|
||||
}
|
||||
|
||||
/* Toolbar */
|
||||
|
||||
.ve-ui-editCheck-toolbar {
|
||||
font-size: 0.875rem; // ignore content scaling
|
||||
|
||||
.mw-mf & {
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.oo-ui-toolbar-bar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.oo-ui-toolbar-tools {
|
||||
|
@ -14,6 +35,12 @@
|
|||
flex: 0;
|
||||
}
|
||||
|
||||
.oo-ui-toolbar-actions + div {
|
||||
// There's a clear div that we need to enhance slightly
|
||||
flex-basis: 100%;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
&.ve-init-mw-mobileArticleTarget-toolbar .oo-ui-toolbar-tools.oo-ui-toolbar-after {
|
||||
display: none;
|
||||
}
|
||||
|
@ -44,6 +71,162 @@
|
|||
border-bottom-color: #fce7fe;
|
||||
}
|
||||
|
||||
/* Actions */
|
||||
|
||||
.ve-ui-editCheckDialog {
|
||||
font-size: 0.875rem; // ignore content scaling
|
||||
|
||||
.oo-ui-window-body {
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
&-title {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
padding: @spacing-50 0;
|
||||
margin-bottom: @spacing-50;
|
||||
border-bottom: 1px solid @border-color-base;
|
||||
}
|
||||
|
||||
&-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
> .oo-ui-labelWidget {
|
||||
align-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.ve-ui-editCheckActionWidget {
|
||||
box-sizing: border-box;
|
||||
border: @border-base;
|
||||
margin: @spacing-100 0;
|
||||
white-space: normal; // Minerva needs this
|
||||
|
||||
&-head {
|
||||
position: relative;
|
||||
padding: @spacing-50 @spacing-75;
|
||||
|
||||
> .oo-ui-labelElement-label {
|
||||
display: block;
|
||||
margin-left: 2em;
|
||||
font-weight: @font-weight-semi-bold;
|
||||
}
|
||||
}
|
||||
|
||||
&-body {
|
||||
margin: @spacing-50 @spacing-75 @spacing-100;
|
||||
}
|
||||
|
||||
&-actions {
|
||||
margin-top: @spacing-100;
|
||||
}
|
||||
|
||||
&.ve-ui-editCheckActionWidget-collapsed {
|
||||
filter: grayscale( 1 );
|
||||
|
||||
> .ve-ui-editCheckActionWidget-body {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.oo-ui-flaggedElement-warning {
|
||||
border-color: @border-color-warning;
|
||||
|
||||
> .ve-ui-editCheckActionWidget-head {
|
||||
background-color: @background-color-warning-subtle;
|
||||
}
|
||||
}
|
||||
|
||||
&.oo-ui-flaggedElement-error {
|
||||
border-color: @border-color-error;
|
||||
|
||||
> .ve-ui-editCheckActionWidget-head {
|
||||
background-color: @background-color-error-subtle;
|
||||
}
|
||||
}
|
||||
|
||||
&.oo-ui-flaggedElement-success {
|
||||
border-color: @border-color-success;
|
||||
|
||||
> .ve-ui-editCheckActionWidget-head {
|
||||
background-color: @background-color-success-subtle;
|
||||
}
|
||||
}
|
||||
|
||||
&.oo-ui-flaggedElement-notice {
|
||||
border-color: @border-color-notice;
|
||||
|
||||
> .ve-ui-editCheckActionWidget-head {
|
||||
background-color: @background-color-notice-subtle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.ve-ui-editCheckDialog-singleAction .ve-ui-editCheckActionWidget-collapsed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mw-mf & {
|
||||
top: auto;
|
||||
// See: .ve-ui-mobileContext, which this is closely mimicking
|
||||
background-color: @background-color-interactive-subtle;
|
||||
/* Match toolbar border & shadow */
|
||||
border-top: @border-subtle;
|
||||
box-shadow: 0 -1px 1px 0 rgba( 0, 0, 0, 0.1 );
|
||||
/* Transition out faster, as keyboard may be coming up */
|
||||
transition: transform 100ms;
|
||||
transform: translateY( 0% );
|
||||
max-width: 995px;
|
||||
margin: 0 auto;
|
||||
|
||||
&-title {
|
||||
padding: @spacing-75 @spacing-100;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&-close {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
&-footer {
|
||||
margin: 0 @spacing-100 @spacing-50;
|
||||
}
|
||||
|
||||
&.ve-ui-editCheckDialog-collapsed {
|
||||
display: block;
|
||||
transition: transform 250ms;
|
||||
transform: translateY( calc( 100% - 2.5em ) );
|
||||
}
|
||||
|
||||
.oo-ui-window-body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ve-ui-editCheckActionWidget {
|
||||
margin: 0;
|
||||
border-width: 0;
|
||||
|
||||
&-head {
|
||||
background-color: transparent !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
&-body {
|
||||
padding-left: 2em;
|
||||
margin-bottom: @spacing-75;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mw-mf .ve-ce-surface-reviewMode.ve-ce-surface-deactivated {
|
||||
// Otherwise the content will be covered by the mobile context at the end
|
||||
// of the document. (Upstream this?)
|
||||
margin-bottom: 100%;
|
||||
}
|
||||
|
||||
/* Selections */
|
||||
|
||||
.ve-ce-surface-reviewMode + .ve-ui-overlay .ve-ce-surface-selections-editCheck .ve-ce-surface-selection {
|
||||
|
@ -68,9 +251,41 @@
|
|||
mix-blend-mode: darken;
|
||||
// Adjust target colours to account for 50% opacity
|
||||
background: ( #fef6e7 - 0.8 * ( #fff ) ) / 0.2;
|
||||
border: 1px solid ( ( #a66200 - 0.8 * ( #fff ) ) / 0.2 );
|
||||
// border: 1px solid ( ( #a66200 - 0.8 * ( #fff ) ) / 0.2 );
|
||||
border-radius: 2px;
|
||||
padding: 2px;
|
||||
margin: -2px 0 0 -2px;
|
||||
}
|
||||
}
|
||||
|
||||
.ve-ui-editCheck-gutter-highlight {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 2px;
|
||||
overflow: hidden;
|
||||
background-color: @color-base;
|
||||
|
||||
&-error {
|
||||
background-color: @color-error;
|
||||
}
|
||||
|
||||
&-warning {
|
||||
background-color: @color-warning;
|
||||
}
|
||||
|
||||
&-notice {
|
||||
background-color: @color-notice;
|
||||
}
|
||||
|
||||
&-success {
|
||||
background-color: @color-success;
|
||||
}
|
||||
|
||||
&-inactive {
|
||||
background-color: @border-color-base;
|
||||
}
|
||||
|
||||
.mw-mf & {
|
||||
left: -10px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,15 +5,28 @@
|
|||
* @param {mw.editcheck.BaseEditCheck} check
|
||||
* @param {ve.dm.SurfaceFragment[]} fragments Affected fragments
|
||||
* @param {jQuery|string|Function|OO.ui.HtmlSnippet} message Check message body
|
||||
* @param {jQuery|string|Function|OO.ui.HtmlSnippet} title Check title
|
||||
*/
|
||||
mw.editcheck.EditCheckAction = function MWEditCheckAction( config ) {
|
||||
this.check = config.check;
|
||||
this.fragments = config.fragments;
|
||||
this.message = config.message;
|
||||
this.title = config.title;
|
||||
this.icon = config.icon;
|
||||
this.type = config.type || 'warning';
|
||||
};
|
||||
|
||||
OO.initClass( mw.editcheck.EditCheckAction );
|
||||
|
||||
/**
|
||||
* Get the action's title
|
||||
*
|
||||
* @return {jQuery|string|Function|OO.ui.HtmlSnippet}
|
||||
*/
|
||||
mw.editcheck.EditCheckAction.prototype.getTitle = function () {
|
||||
return this.title || this.check.getTitle( this );
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the available choices
|
||||
*
|
||||
|
@ -38,5 +51,137 @@ mw.editcheck.EditCheckAction.prototype.getHighlightSelections = function () {
|
|||
* @return {string}
|
||||
*/
|
||||
mw.editcheck.EditCheckAction.prototype.getDescription = function () {
|
||||
return this.check.getDescription( this );
|
||||
return this.message || this.check.getDescription( this );
|
||||
};
|
||||
|
||||
mw.editcheck.EditCheckAction.prototype.getType = function () {
|
||||
return this.type;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the name of the check type
|
||||
*
|
||||
* @return {string} Check type name
|
||||
*/
|
||||
mw.editcheck.EditCheckAction.prototype.getName = function () {
|
||||
return this.check.getName();
|
||||
};
|
||||
|
||||
mw.editcheck.EditCheckAction.prototype.render = function ( collapsed, singleAction, surface ) {
|
||||
const widget = new mw.editcheck.EditCheckActionWidget( {
|
||||
type: this.type,
|
||||
icon: this.icon,
|
||||
label: this.getTitle(),
|
||||
message: this.getDescription(),
|
||||
classes: collapsed ? [ 've-ui-editCheckActionWidget-collapsed' ] : '',
|
||||
singleAction: singleAction
|
||||
} );
|
||||
this.getChoices().forEach( ( choice ) => {
|
||||
const button = new OO.ui.ButtonWidget( choice );
|
||||
button.connect( this, {
|
||||
click: () => {
|
||||
const promise = this.check.act( choice.action, this, surface ) || ve.createDeferred().resolve().promise();
|
||||
widget.emit( 'act', choice, choice.action, promise );
|
||||
}
|
||||
} );
|
||||
widget.addAction( button );
|
||||
} );
|
||||
|
||||
return widget;
|
||||
};
|
||||
|
||||
mw.editcheck.EditCheckActionWidget = function MWEditCheckActionWidget( config ) {
|
||||
// Configuration initialization
|
||||
config = config || {};
|
||||
|
||||
this.singleAction = config.singleAction;
|
||||
|
||||
this.actions = [];
|
||||
|
||||
// Parent constructor
|
||||
mw.editcheck.EditCheckActionWidget.super.call( this, config );
|
||||
|
||||
// Mixin constructors
|
||||
OO.ui.mixin.IconElement.call( this, config );
|
||||
OO.ui.mixin.LabelElement.call( this, config );
|
||||
OO.ui.mixin.TitledElement.call( this, config );
|
||||
OO.ui.mixin.FlaggedElement.call( this, config );
|
||||
|
||||
this.setType( config.type );
|
||||
|
||||
if ( config.icon ) {
|
||||
this.setIcon( config.icon );
|
||||
}
|
||||
|
||||
this.message = new OO.ui.LabelWidget( { label: config.message } );
|
||||
this.$actions = $( '<div>' ).addClass( 've-ui-editCheckActionWidget-actions oo-ui-element-hidden' );
|
||||
|
||||
this.$head = $( '<div>' )
|
||||
.append( this.$icon, this.$label )
|
||||
.addClass( 've-ui-editCheckActionWidget-head' )
|
||||
.on( 'click', this.onHeadClick.bind( this ) );
|
||||
this.$body = $( '<div>' )
|
||||
.append( this.message.$element, this.$actions )
|
||||
.addClass( 've-ui-editCheckActionWidget-body' );
|
||||
|
||||
this.$element
|
||||
.append( this.$head, this.$body )
|
||||
// .append( this.$icon, this.$label, this.closeButton && this.closeButton.$element )
|
||||
.addClass( 've-ui-editCheckActionWidget' );
|
||||
};
|
||||
|
||||
OO.inheritClass( mw.editcheck.EditCheckActionWidget, OO.ui.Widget );
|
||||
OO.mixinClass( mw.editcheck.EditCheckActionWidget, OO.ui.mixin.IconElement );
|
||||
OO.mixinClass( mw.editcheck.EditCheckActionWidget, OO.ui.mixin.LabelElement );
|
||||
OO.mixinClass( mw.editcheck.EditCheckActionWidget, OO.ui.mixin.TitledElement );
|
||||
OO.mixinClass( mw.editcheck.EditCheckActionWidget, OO.ui.mixin.FlaggedElement );
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
mw.editcheck.EditCheckActionWidget.prototype.setDisabled = function ( disabled ) {
|
||||
OO.ui.Widget.prototype.setDisabled.call( this, disabled );
|
||||
this.actions.forEach( ( action ) => {
|
||||
action.setDisabled( disabled );
|
||||
} );
|
||||
};
|
||||
|
||||
mw.editcheck.EditCheckActionWidget.static.iconMap = {
|
||||
notice: 'infoFilled',
|
||||
error: 'error',
|
||||
warning: 'alert'
|
||||
};
|
||||
|
||||
mw.editcheck.EditCheckActionWidget.prototype.setType = function ( type ) {
|
||||
if ( !this.constructor.static.iconMap[ type ] ) {
|
||||
type = 'notice';
|
||||
}
|
||||
if ( type !== this.type ) {
|
||||
this.clearFlags();
|
||||
this.setFlags( type );
|
||||
|
||||
this.setIcon( this.constructor.static.iconMap[ type ] );
|
||||
}
|
||||
this.type = type;
|
||||
};
|
||||
|
||||
mw.editcheck.EditCheckActionWidget.prototype.getType = function () {
|
||||
return this.type;
|
||||
};
|
||||
|
||||
mw.editcheck.EditCheckActionWidget.prototype.addAction = function ( action ) {
|
||||
this.actions.push( action );
|
||||
this.$actions.append( action.$element ).removeClass( 'oo-ui-element-hidden' );
|
||||
};
|
||||
|
||||
mw.editcheck.EditCheckActionWidget.prototype.onHeadClick = function ( e ) {
|
||||
if ( this.singleAction ) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
// eslint-disable-next-line no-jquery/no-class-state
|
||||
this.$element.toggleClass( 've-ui-editCheckActionWidget-collapsed' );
|
||||
// eslint-disable-next-line no-jquery/no-class-state
|
||||
this.emit( 'togglecollapse', this.$element.hasClass( 've-ui-editCheckActionWidget-collapsed' ) );
|
||||
};
|
||||
|
|
|
@ -27,15 +27,14 @@ OO.inheritClass( ve.ui.EditCheckDialog, ve.ui.ToolbarDialog );
|
|||
|
||||
ve.ui.EditCheckDialog.static.name = 'editCheckDialog';
|
||||
|
||||
ve.ui.EditCheckDialog.static.position = 'side';
|
||||
ve.ui.EditCheckDialog.static.position = OO.ui.isMobile() ? 'below' : 'side';
|
||||
|
||||
ve.ui.EditCheckDialog.static.size = 'medium';
|
||||
ve.ui.EditCheckDialog.static.size = OO.ui.isMobile() ? 'full' : 'medium';
|
||||
|
||||
ve.ui.EditCheckDialog.static.framed = false;
|
||||
|
||||
// // Invisible title for accessibility
|
||||
// ve.ui.EditCheckDialog.static.title =
|
||||
// OO.ui.deferMsg( 'visualeditor-find-and-replace-title' );
|
||||
// Invisible title for accessibility
|
||||
ve.ui.EditCheckDialog.static.title = OO.ui.deferMsg( 'editcheck-review-title' );
|
||||
|
||||
/* Methods */
|
||||
|
||||
|
@ -46,26 +45,190 @@ ve.ui.EditCheckDialog.prototype.initialize = function () {
|
|||
// Parent method
|
||||
ve.ui.EditCheckDialog.super.prototype.initialize.call( this );
|
||||
|
||||
this.title = new OO.ui.LabelWidget( {
|
||||
label: this.constructor.static.title,
|
||||
classes: [ 've-ui-editCheckDialog-title' ]
|
||||
} );
|
||||
|
||||
// FIXME: click handlers are getting unbound when the window is closed
|
||||
|
||||
this.closeButton = new OO.ui.ButtonWidget( {
|
||||
classes: [ 've-ui-editCheckDialog-close' ],
|
||||
framed: false,
|
||||
label: ve.msg( 'visualeditor-contextitemwidget-label-close' ),
|
||||
invisibleLabel: true,
|
||||
icon: 'expand'
|
||||
} ).connect( this, {
|
||||
click: 'onCloseButtonClick'
|
||||
} );
|
||||
|
||||
this.currentOffset = 0;
|
||||
|
||||
this.footerLabel = new OO.ui.LabelWidget();
|
||||
this.previousButton = new OO.ui.ButtonWidget( {
|
||||
icon: 'previous',
|
||||
title: ve.msg( 'last' ),
|
||||
invisibleLabel: true,
|
||||
framed: false
|
||||
} ).connect( this, {
|
||||
click: 'onPreviousButtonClick'
|
||||
} );
|
||||
this.nextButton = new OO.ui.ButtonWidget( {
|
||||
icon: 'next',
|
||||
title: ve.msg( 'next' ),
|
||||
invisibleLabel: true,
|
||||
framed: false
|
||||
} ).connect( this, {
|
||||
click: 'onNextButtonClick'
|
||||
} );
|
||||
this.footer = new OO.ui.HorizontalLayout( {
|
||||
classes: [ 've-ui-editCheckDialog-footer' ],
|
||||
items: [
|
||||
this.footerLabel,
|
||||
this.previousButton,
|
||||
this.nextButton
|
||||
]
|
||||
} );
|
||||
|
||||
this.$checks = $( '<div>' );
|
||||
this.$body.append( this.title.$element, this.closeButton.$element, this.$checks, this.footer.$element );
|
||||
|
||||
this.$highlights = $( '<div>' );
|
||||
|
||||
this.updateDebounced = ve.debounce( this.update.bind( this ), 100 );
|
||||
this.positionDebounced = ve.debounce( this.position.bind( this ), 100 );
|
||||
};
|
||||
|
||||
ve.ui.EditCheckDialog.prototype.update = function () {
|
||||
const surfaceView = this.surface.getView();
|
||||
const checks = mw.editcheck.editCheckFactory.createAllByListener( 'onDocumentChange', this.surface.getModel() );
|
||||
const $checks = $( '<div>' );
|
||||
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( $( '<div>' )
|
||||
.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 );
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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 );
|
||||
};
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
|
|
@ -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. <a href="#">Read more</a>.' )
|
||||
}
|
||||
];
|
||||
|
@ -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 );
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue