Mooeypoo has uploaded a new change for review.
https://gerrit.wikimedia.org/r/235383
Change subject: Add a confirmation dialog for canceling edits
......................................................................
Add a confirmation dialog for canceling edits
Pop up a confirmation dialog when the user tries to cancel an
edit with changes.
Bug: T111115
Change-Id: Ib2977892e413468262c2be76e89678aad4737ecd
---
M Resources.php
M i18n/en.json
M i18n/qqq.json
M modules/flow-initialize.js
A modules/flow/ui/mw.flow.ui.CancelConfirmDialog.js
A modules/flow/ui/mw.flow.ui.Overlay.js
M modules/flow/ui/mw.flow.ui.js
M modules/flow/ui/widgets/editor/editors/mw.flow.ui.AbstractEditorWidget.js
M modules/flow/ui/widgets/editor/editors/mw.flow.ui.VisualEditorWidget.js
M modules/flow/ui/widgets/editor/editors/mw.flow.ui.WikitextEditorWidget.js
M modules/flow/ui/widgets/editor/mw.flow.ui.EditorSwitcherWidget.js
M modules/flow/ui/widgets/editor/mw.flow.ui.EditorWidget.js
A modules/styles/flow/mw.flow.ui.Overlay.less
13 files changed, 168 insertions(+), 4 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Flow
refs/changes/83/235383/1
diff --git a/Resources.php b/Resources.php
index cb3930b..76f63ad 100644
--- a/Resources.php
+++ b/Resources.php
@@ -375,6 +375,8 @@
'ext.flow.ui' => $flowResourceTemplate + array(
'scripts' => array(
'flow/ui/mw.flow.ui.js',
+ 'flow/ui/mw.flow.ui.Overlay.js',
+ 'flow/ui/mw.flow.ui.CancelConfirmDialog.js',
'flow/ui/widgets/mw.flow.ui.TopicMenuSelectWidget.js',
'flow/ui/widgets/mw.flow.ui.ToCWidget.js',
'flow/ui/widgets/mw.flow.ui.ReorderTopicsWidget.js',
@@ -395,6 +397,7 @@
),
'styles' => array(
'styles/flow/mw.flow.ui.less',
+ 'styles/flow/mw.flow.ui.Overlay.less',
'styles/flow/widgets/mw.flow.ui.NavigationWidget.less',
'styles/flow/widgets/mw.flow.ui.TopicMenuSelectWidget.less',
'styles/flow/widgets/mw.flow.ui.ReorderTopicsWidget.less',
@@ -411,6 +414,10 @@
'messages' => array(
'flow-error-parsoid-failure',
'flow-error-default',
+ 'flow-dialog-cancelconfirm-title',
+ 'flow-dialog-cancelconfirm-message',
+ 'flow-dialog-cancelconfirm-keep',
+ 'flow-dialog-cancelconfirm-discard',
),
'dependencies' => array (
'oojs-ui',
diff --git a/i18n/en.json b/i18n/en.json
index 5af2b4b..31678cd 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -557,5 +557,9 @@
"flow-mark-revision-patrolled-link-title": "Mark this page as
patrolled",
"flow-mark-diff-patrolled-link-text": "Mark as patrolled",
"flow-mark-diff-patrolled-link-title": "Mark as patrolled",
+ "flow-dialog-cancelconfirm-title": "Are you sure?",
+ "flow-dialog-cancelconfirm-message": "Are you sure you want to cancel
without saving first?",
+ "flow-dialog-cancelconfirm-keep": "Continue editing",
+ "flow-dialog-cancelconfirm-discard": "Discard changes",
"flow-description-last-modified-at": "This description was last
modified on $1, at $2."
}
diff --git a/i18n/qqq.json b/i18n/qqq.json
index 6bcd486..106df87 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -564,5 +564,9 @@
"flow-mark-revision-patrolled-link-title": "Title of the link to mark a
revision as patrolled on a revision page.",
"flow-mark-diff-patrolled-link-text": "Text of the link to mark a
revision as patrolled on a diff page.\n{{Identical|Mark as patrolled}}",
"flow-mark-diff-patrolled-link-title": "Title of the link to mark a
revision as patrolled on a diff page.\n{{Identical|Mark as patrolled}}",
+ "flow-dialog-cancelconfirm-title": "Title of the dialog that appears
when the user cancels an edit and verifies whether the user wants to discard
their edits or continue editing.",
+ "flow-dialog-cancelconfirm-message": "Title of the dialog that appears
when the user cancels an edit, asking the user if they are sure they wants to
discard their edits or continue editing.",
+ "flow-dialog-cancelconfirm-keep": "Title for the keep option in the
dialog that appears when the user cancels an edit.",
+ "flow-dialog-cancelconfirm-discard": "Title for the discard option in
the dialog that appears when the user cancels an edit.",
"flow-description-last-modified-at": "Timestamp line stating when
description was modified. See also {{msg-mw|lastmodifiedat}}. Parameters:\n*
$1 - Date\n* $2 - Time"
}
diff --git a/modules/flow-initialize.js b/modules/flow-initialize.js
index 677403e..8d599b9 100644
--- a/modules/flow-initialize.js
+++ b/modules/flow-initialize.js
@@ -39,6 +39,10 @@
return;
}
+ // Set up window overlay
+ $( 'body' ).append( mw.flow.ui.windowOverlay.$element );
+ mw.flow.ui.windowOverlay.$element.append(
mw.flow.ui.windowManager.$element );
+
flowBoard = mw.flow.getPrototypeMethod( 'component',
'getInstanceByElement' )( $board );
if (
diff --git a/modules/flow/ui/mw.flow.ui.CancelConfirmDialog.js
b/modules/flow/ui/mw.flow.ui.CancelConfirmDialog.js
new file mode 100644
index 0000000..775df9b
--- /dev/null
+++ b/modules/flow/ui/mw.flow.ui.CancelConfirmDialog.js
@@ -0,0 +1,41 @@
+( function ( $ ) {
+ /**
+ * Dialog for confirming with the user if they reall want to cancel
+ * the edit.
+ *
+ * @class
+ * @extends OO.ui.MessageDialog
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+ mw.flow.ui.CancelConfirmDialog = function MwFlowUICancelConfirmDialog(
config ) {
+ // Parent constructor
+ mw.flow.ui.CancelConfirmDialog.super.call( this, config );
+ };
+
+ /* Inheritance */
+
+ OO.inheritClass( mw.flow.ui.CancelConfirmDialog, OO.ui.MessageDialog );
+
+ /* Static Properties */
+
+ mw.flow.ui.CancelConfirmDialog.static.name = 'cancelconfirm';
+
+ mw.flow.ui.CancelConfirmDialog.static.verbose = true;
+
+ mw.flow.ui.CancelConfirmDialog.static.size = 'small';
+
+ mw.flow.ui.CancelConfirmDialog.static.icon = 'help';
+
+ mw.flow.ui.CancelConfirmDialog.static.title =
+ mw.msg( 'flow-dialog-cancelconfirm-title' );
+
+ mw.flow.ui.CancelConfirmDialog.static.message =
+ mw.msg( 'flow-dialog-cancelconfirm-message' );
+
+ mw.flow.ui.CancelConfirmDialog.static.actions = [
+ { action: 'discard', label: mw.msg(
'flow-dialog-cancelconfirm-discard' ), flags: [ 'primary', 'destructive' ] },
+ { action: 'keep', label: mw.msg(
'flow-dialog-cancelconfirm-keep' ), flags: 'safe' }
+ ];
+}( jQuery ) );
diff --git a/modules/flow/ui/mw.flow.ui.Overlay.js
b/modules/flow/ui/mw.flow.ui.Overlay.js
new file mode 100644
index 0000000..afddc3d
--- /dev/null
+++ b/modules/flow/ui/mw.flow.ui.Overlay.js
@@ -0,0 +1,23 @@
+/**
+ * Container for content that should appear in front of everything else.
+ *
+ * @class
+ * @abstract
+ * @extends OO.ui.Element
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+mw.flow.ui.Overlay = function MwFlowUiOverlay( config ) {
+ // Parent constructor
+ OO.ui.Element.call( this, config );
+
+ // Initialization
+ this.$element.addClass( 'flow-ui-overlay' );
+};
+
+/* Inheritance */
+
+OO.inheritClass( mw.flow.ui.Overlay, OO.ui.Element );
+
+mw.flow.ui.windowOverlay = new mw.flow.ui.Overlay();
diff --git a/modules/flow/ui/mw.flow.ui.js b/modules/flow/ui/mw.flow.ui.js
index 947626b..a3370d0 100644
--- a/modules/flow/ui/mw.flow.ui.js
+++ b/modules/flow/ui/mw.flow.ui.js
@@ -1 +1,3 @@
-mw.flow.ui = {};
+mw.flow.ui = {
+ windowManager: new OO.ui.WindowManager()
+};
diff --git
a/modules/flow/ui/widgets/editor/editors/mw.flow.ui.AbstractEditorWidget.js
b/modules/flow/ui/widgets/editor/editors/mw.flow.ui.AbstractEditorWidget.js
index c25204a..42146d9 100644
--- a/modules/flow/ui/widgets/editor/editors/mw.flow.ui.AbstractEditorWidget.js
+++ b/modules/flow/ui/widgets/editor/editors/mw.flow.ui.AbstractEditorWidget.js
@@ -26,6 +26,8 @@
mw.flow.ui.AbstractEditorWidget = function
mwFlowUiAbstractEditorWidget( config ) {
config = config || {};
+ this.initialContent = null;
+
// Parent constructor
mw.flow.ui.AbstractEditorWidget.parent.call( this, config );
};
@@ -170,4 +172,13 @@
mw.flow.ui.AbstractEditorWidget.prototype.isEmpty = function () {
return !this.getContent();
};
+
+ /**
+ * Check if the information in the editor changed
+ * @return {boolean} Information has changed
+ */
+ mw.flow.ui.AbstractEditorWidget.prototype.hasBeenChanged = function () {
+ return this.initialContent !== this.getContent();
+ };
+
}( jQuery ) );
diff --git
a/modules/flow/ui/widgets/editor/editors/mw.flow.ui.VisualEditorWidget.js
b/modules/flow/ui/widgets/editor/editors/mw.flow.ui.VisualEditorWidget.js
index 0e28cfa..06df41e 100644
--- a/modules/flow/ui/widgets/editor/editors/mw.flow.ui.VisualEditorWidget.js
+++ b/modules/flow/ui/widgets/editor/editors/mw.flow.ui.VisualEditorWidget.js
@@ -185,6 +185,20 @@
return
!this.target.getSurface().getModel().getDocument().data.hasContent();
};
+ /**
+ * Check if there are any changes made to the data in the editor
+ *
+ * @return {boolean} The original content has changed
+ */
+ mw.flow.ui.VisualEditorWidget.prototype.hasBeenChanged = function () {
+ // If we haven't fully loaded yet, just return false
+ if ( !this.target ) {
+ return false;
+ }
+
+ return this.target.getSurface().getModel().hasBeenModified();
+ };
+
mw.flow.ui.VisualEditorWidget.prototype.setDisabled = function (
disabled ) {
// Parent method
mw.flow.ui.VisualEditorWidget.parent.prototype.setDisabled.call( this, disabled
);
diff --git
a/modules/flow/ui/widgets/editor/editors/mw.flow.ui.WikitextEditorWidget.js
b/modules/flow/ui/widgets/editor/editors/mw.flow.ui.WikitextEditorWidget.js
index e785abf..59292cc 100644
--- a/modules/flow/ui/widgets/editor/editors/mw.flow.ui.WikitextEditorWidget.js
+++ b/modules/flow/ui/widgets/editor/editors/mw.flow.ui.WikitextEditorWidget.js
@@ -136,6 +136,8 @@
*/
mw.flow.ui.WikitextEditorWidget.prototype.setContent = function (
content ) {
this.input.setValue( content );
+ // Cache content for comparison
+ this.initialContent = content;
};
mw.flow.ui.WikitextEditorWidget.prototype.setDisabled = function (
disabled ) {
@@ -151,7 +153,7 @@
* @inheritdoc
*/
mw.flow.ui.WikitextEditorWidget.prototype.setup = function ( content ) {
- this.input.setValue( content );
+ this.setContent( content );
return $.Deferred().resolve().promise();
};
diff --git a/modules/flow/ui/widgets/editor/mw.flow.ui.EditorSwitcherWidget.js
b/modules/flow/ui/widgets/editor/mw.flow.ui.EditorSwitcherWidget.js
index 3b9f929..e9844c8 100644
--- a/modules/flow/ui/widgets/editor/mw.flow.ui.EditorSwitcherWidget.js
+++ b/modules/flow/ui/widgets/editor/mw.flow.ui.EditorSwitcherWidget.js
@@ -563,6 +563,15 @@
};
/**
+ * Check if there are any changes made to the data in the editor
+ *
+ * @return {boolean} The original content has changed
+ */
+ mw.flow.ui.EditorSwitcherWidget.prototype.hasBeenChanged = function () {
+ var editor = this.getActiveEditor();
+ return editor && editor.hasBeenChanged();
+ };
+ /**
* Destroy the widget
*/
mw.flow.ui.EditorSwitcherWidget.prototype.destroy = function () {
diff --git a/modules/flow/ui/widgets/editor/mw.flow.ui.EditorWidget.js
b/modules/flow/ui/widgets/editor/mw.flow.ui.EditorWidget.js
index a9ac542..988532a 100644
--- a/modules/flow/ui/widgets/editor/mw.flow.ui.EditorWidget.js
+++ b/modules/flow/ui/widgets/editor/mw.flow.ui.EditorWidget.js
@@ -17,6 +17,8 @@
* @cfg {string} [cancelMsgKey='flow-cancel'] i18n message key for the
cancel button
* @cfg {boolean} [autoFocus=true] Automatically focus after switching
editors
* @cfg {boolean} [cancelOnEscape=true] Emit 'cancel' when Esc is
pressed
+ * @cfg {boolean} [confirmCancel=true] Pop up a confirmation dialog if
the user attempts
+ * to cancel when there are changes in the editor.
*/
mw.flow.ui.EditorWidget = function mwFlowUiEditorWidget( config ) {
var widget = this;
@@ -30,6 +32,7 @@
OO.ui.mixin.PendingElement.call( this, config );
this.initialEditor = config.editor;
+ this.confirmCancel = !!config.confirmCancel ||
config.cancelOnEscape === undefined;
this.editorControlsWidget = new
mw.flow.ui.EditorControlsWidget( {
termsMsgKey: config.termsMsgKey ||
'flow-terms-of-use-edit',
@@ -52,19 +55,23 @@
change: [ 'emit', 'change' ]
} );
this.editorControlsWidget.connect( this, {
- cancel: [ 'emit', 'cancel' ],
+ cancel: 'onEditorControlsWidgetCancel',
save: 'onEditorControlsWidgetSave'
} );
if ( config.cancelOnEscape || config.cancelOnEscape ===
undefined ) {
this.$element.on( 'keydown', function ( e ) {
if ( e.which === OO.ui.Keys.ESCAPE ) {
- widget.emit( 'cancel' );
+ widget.onEditorControlsWidgetCancel();
e.preventDefault();
e.stopPropagation();
}
} );
}
+
+ // Confirmation dialog
+ this.cancelConfirmDialog = new mw.flow.ui.CancelConfirmDialog();
+ mw.flow.ui.windowManager.addWindows( [ this.cancelConfirmDialog
] );
this.$element
.append(
@@ -112,6 +119,30 @@
// merge EditorSwitcherWidget into EditorWidget?
/**
+ * Respond to cancel event. Verify with the user that they want to
cancel if
+ * there is changed data in the editor.
+ * @fires cancel
+ */
+ mw.flow.ui.EditorWidget.prototype.onEditorControlsWidgetCancel =
function () {
+ var widget = this;
+
+ if ( this.confirmCancel &&
this.editorSwitcherWidget.hasBeenChanged() ) {
+ mw.flow.ui.windowManager.openWindow( 'cancelconfirm' )
+ .then( function ( opened ) {
+ opened.then( function ( closing, data )
{
+ if ( data && data.action ===
'discard' ) {
+ // Remove content
+ widget.setContent( '',
'wikitext' );
+ widget.emit( 'cancel' );
+ }
+ } );
+ } );
+ } else {
+ this.emit( 'cancel' );
+ }
+ };
+
+ /**
* Check whether an editor is currently active. This returns false
before the first editor
* is loaded, and true after that. It also returns true if and editor
switch is in progress.
* @return {boolean} Editor is active
diff --git a/modules/styles/flow/mw.flow.ui.Overlay.less
b/modules/styles/flow/mw.flow.ui.Overlay.less
new file mode 100644
index 0000000..060ccfe
--- /dev/null
+++ b/modules/styles/flow/mw.flow.ui.Overlay.less
@@ -0,0 +1,12 @@
+.flow-ui-overlay {
+ font-family: sans-serif;
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+ z-index: 1;
+}
+
+.flow-ui-overlay > * {
+ z-index: 1;
+}
--
To view, visit https://gerrit.wikimedia.org/r/235383
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ib2977892e413468262c2be76e89678aad4737ecd
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Flow
Gerrit-Branch: master
Gerrit-Owner: Mooeypoo <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits