jenkins-bot has submitted this change and it was merged.

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.

Also, make all editor instances discard data when discard is
chosen.

Bug: T111115
Bug: T109365
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, 174 insertions(+), 5 deletions(-)

Approvals:
  Catrope: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/Resources.php b/Resources.php
index 5fbb99a..e5d433a 100644
--- a/Resources.php
+++ b/Resources.php
@@ -374,6 +374,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',
@@ -394,6 +396,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',
@@ -410,6 +413,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 7f9662e..31fdb01 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -566,6 +566,10 @@
        "flow-notification-enabled-on-talkpage-email-batch-body": "Flow, the 
new wiki discussion system, has been enabled on your user talk page on 
{{SITENAME}}. You can get more information, provide feedback or disable the new 
system any time from the Beta features section in your preferences.",
        "flow-beta-feature-add-archive-template-edit-summary": "Adding archive 
template",
        "flow-beta-feature-remove-archive-template-edit-summary": "Removing 
archive template",
+       "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-guidedtour-optin-welcome": "{{GENDER:$1|Welcome}} to 
{{GENDER:$1|your}} new user talk page",
        "flow-guidedtour-optin-welcome-description": "{{GENDER:$1|Your}} talk 
page is the place where other editors can contact {{GENDER:$1|you}}. With Flow, 
it's easier to start new topics and keep track of conversations.",
        "flow-guidedtour-optin-find-old-conversations": "Find 
{{GENDER:$1|your}} old conversations",
diff --git a/i18n/qqq.json b/i18n/qqq.json
index 9abdae5..5e83ed9 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -573,6 +573,10 @@
        "flow-notification-enabled-on-talkpage-email-batch-body": "Email body 
for notification when Flow was enabled on your talk page.\n Parameters:\n* $1 - 
Title where Flow was enabled.",
        "flow-beta-feature-add-archive-template-edit-summary": "Edit summary 
message for the revision adding the archive template to an archived talk page.",
        "flow-beta-feature-remove-archive-template-edit-summary": "Edit summary 
message for the revision removing the archive template to an archived talk 
page.",
+       "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-guidedtour-optin-welcome": "Title of the 'welcome' step in the 
Flow opt-in guided tour. This message is the first to appear in the guided tour 
after a user chose to convert their talk page to a Flow board.",
        "flow-guidedtour-optin-welcome-description": "Description of the 
'welcome' step in the flow opt-in guided tour. This message is the first to 
appear in the guided tour after a user chose to convert their talk page to a 
Flow board.",
        "flow-guidedtour-optin-find-old-conversations": "Title of the 'find old 
conversations' step in the flow opt-in guided tour that launches after the user 
chose to convert their talk page to a Flow board. This message appears next to 
the board description, where a link is visible to the user's old talk page.",
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..42205d6
--- /dev/null
+++ b/modules/flow/ui/mw.flow.ui.CancelConfirmDialog.js
@@ -0,0 +1,46 @@
+( 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.parent.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' }
+       ];
+
+       /* Registration */
+
+       mw.flow.ui.windowFactory.register( mw.flow.ui.CancelConfirmDialog );
+
+}( 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..9b86b65
--- /dev/null
+++ b/modules/flow/ui/mw.flow.ui.Overlay.js
@@ -0,0 +1,22 @@
+/**
+ * Container for content that should appear in front of everything else.
+ *
+ * @class
+ * @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..6649dee 100644
--- a/modules/flow/ui/mw.flow.ui.js
+++ b/modules/flow/ui/mw.flow.ui.js
@@ -1 +1,5 @@
 mw.flow.ui = {};
+
+mw.flow.ui.windowFactory = new OO.Factory();
+mw.flow.ui.windowManager = new OO.ui.WindowManager( { factory: 
mw.flow.ui.windowFactory } );
+
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..b68da8f 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 );
        };
@@ -116,11 +118,13 @@
        /**
         * Change the content of the editor.
         *
-        * @abstract
         * @method
         * @param {string} content New content
         */
-       mw.flow.ui.AbstractEditorWidget.prototype.setContent = null;
+       mw.flow.ui.AbstractEditorWidget.prototype.setContent = function ( 
content ) {
+               // Cache content for comparison
+               this.initialContent = content;
+       };
 
        /**
         * Focus on the editor.
@@ -170,4 +174,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..a30a442 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
@@ -135,6 +135,9 @@
         * @inheritdoc
         */
        mw.flow.ui.WikitextEditorWidget.prototype.setContent = function ( 
content ) {
+               // Parent method
+               
mw.flow.ui.WikitextEditorWidget.parent.prototype.setContent.call( this, content 
);
+
                this.input.setValue( content );
        };
 
@@ -151,7 +154,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..00e6965 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,14 +55,14 @@
                        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();
                                }
@@ -112,6 +115,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: merged
Gerrit-Change-Id: Ib2977892e413468262c2be76e89678aad4737ecd
Gerrit-PatchSet: 7
Gerrit-Project: mediawiki/extensions/Flow
Gerrit-Branch: master
Gerrit-Owner: Mooeypoo <[email protected]>
Gerrit-Reviewer: Catrope <[email protected]>
Gerrit-Reviewer: Mooeypoo <[email protected]>
Gerrit-Reviewer: Siebrand <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to