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

Reply via email to