From 10a4f8e45ee1e3b6a8f2b6c9348271724eb3371b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Tue, 20 Mar 2018 12:02:49 +0100 Subject: [PATCH] Confirm a share also by pressing enter on the input field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Besides confirming a share by clicking on the confirm button now it is possible to do it by pressing enter on the input field. Clicking on the confirm button implicitly hides the autocomplete dropdown. On the other hand, pressing enter on the input field does not, so the autocompletion must be disabled and closed when the confirmation begins and then enabled again once it finishes. Otherwise the autocomplete dropdown would be visible and it would be possible to interact with it while the share is being confirmed. The order in which the input field and the autompletion are disabled is important. Internally, the autocompletion sets a timeout when the input field is modified that requests the suggestions to the server and then shows them in the dropdown. That timeout is not cancelled when the autocompletion is disabled, but when the input field loses its focus and the autocompletion is not disabled. Therefore, the input field has to be disabled (which causes it to lose the focus) before the autocompletion is disabled. Otherwise it could happen that while a share is being confirmed the timeout ends, so an autocompletion request is sent and then, once the share is successfully confirmed and thus the autocompletion is enabled again, the request is received and the autocomplete dropdown is shown with the old suggestions. Strange, but possible nevertheless ;-) Signed-off-by: Daniel Calviño Sánchez --- core/js/sharedialogview.js | 36 ++++++++++++++++++++++ core/js/tests/specs/sharedialogviewSpec.js | 11 +++++++ 2 files changed, 47 insertions(+) diff --git a/core/js/sharedialogview.js b/core/js/sharedialogview.js index 89bd2e2dbfc..0f1d8f39550 100644 --- a/core/js/sharedialogview.js +++ b/core/js/sharedialogview.js @@ -415,6 +415,10 @@ _onSelectRecipient: function(e, s) { e.preventDefault(); + // Ensure that the keydown handler for the input field is not + // called; otherwise it would try to add the recipient again, which + // would fail. + e.stopImmediatePropagation(); $(e.target).attr('disabled', true) .val(s.item.label); var $loading = this.$el.find('.shareWithLoading'); @@ -451,6 +455,15 @@ $shareWithField.prop('disabled', true); + // Disabling the autocompletion does not clear its search timeout; + // removing the focus from the input field does, but only if the + // autocompletion is not disabled when the field loses the focus. + // Thus, the field has to be disabled before disabling the + // autocompletion to prevent an old pending search result from + // appearing once the field is enabled again. + $shareWithField.autocomplete('close'); + $shareWithField.autocomplete('disable'); + var perPage = 200; var onlyExactMatches = true; this._getSuggestions( @@ -467,6 +480,8 @@ $shareWithField.prop('disabled', false); $shareWithField.focus(); + $shareWithField.autocomplete('enable'); + // There is no need to show an error message here; it will // be automatically shown when the autocomplete is activated // again (due to the focus on the field) and it finds no @@ -483,6 +498,8 @@ $shareWithField.prop('disabled', false); $shareWithField.focus(); + $shareWithField.autocomplete('enable'); + return; } @@ -494,6 +511,8 @@ $shareWithField.val(''); $shareWithField.prop('disabled', false); $shareWithField.focus(); + + $shareWithField.autocomplete('enable'); }; var actionError = function(obj, msg) { @@ -504,6 +523,8 @@ $shareWithField.prop('disabled', false); $shareWithField.focus(); + $shareWithField.autocomplete('enable'); + OC.Notification.showTemporary(msg); }; @@ -519,6 +540,8 @@ $shareWithField.prop('disabled', false); $shareWithField.focus(); + $shareWithField.autocomplete('enable'); + // There is no need to show an error message here; it will be // automatically shown when the autocomplete is activated again // (due to the focus on the field) and getting the suggestions @@ -554,6 +577,7 @@ }, render: function() { + var self = this; var baseTemplate = this._getTemplate('base', TEMPLATE_BASE); this.$el.html(baseTemplate({ @@ -565,6 +589,16 @@ var $shareField = this.$el.find('.shareWithField'); if ($shareField.length) { + var shareFieldKeydownHandler = function(event) { + if (event.keyCode !== 13) { + return true; + } + + self._confirmShare(); + + return false; + }; + $shareField.autocomplete({ minLength: 1, delay: 750, @@ -574,6 +608,8 @@ source: this.autocompleteHandler, select: this._onSelectRecipient }).data('ui-autocomplete')._renderItem = this.autocompleteRenderItem; + + $shareField.on('keydown', null, shareFieldKeydownHandler); } this.resharerInfoView.$el = this.$el.find('.resharerInfoView'); diff --git a/core/js/tests/specs/sharedialogviewSpec.js b/core/js/tests/specs/sharedialogviewSpec.js index 76a214f6705..7ef38c022dd 100644 --- a/core/js/tests/specs/sharedialogviewSpec.js +++ b/core/js/tests/specs/sharedialogviewSpec.js @@ -1872,6 +1872,8 @@ describe('OC.Share.ShareDialogView', function() { expect(dialog.$el.find('.shareWithLoading').hasClass('hidden')).toEqual(false); expect(dialog.$el.find('.shareWithConfirm').hasClass('hidden')).toEqual(true); + expect(autocompleteStub.lastCall.args[0]).toEqual('disable'); + expect(autocompleteStub.calledWith('close')).toEqual(true); expect(dialog.$el.find('.shareWithField').prop('disabled')).toEqual(true); expect(dialog.$el.find('.shareWithField').val()).toEqual('bob'); }); @@ -1918,6 +1920,7 @@ describe('OC.Share.ShareDialogView', function() { // Ensure that the UI is not restored before adding the share expect(dialog.$el.find('.shareWithLoading').hasClass('hidden')).toEqual(false); expect(dialog.$el.find('.shareWithConfirm').hasClass('hidden')).toEqual(true); + expect(autocompleteStub.lastCall.args[0]).toEqual('disable'); expect(dialog.$el.find('.shareWithField').prop('disabled')).toEqual(true); expect(dialog.$el.find('.shareWithField').val()).toEqual('bob'); @@ -1933,6 +1936,7 @@ describe('OC.Share.ShareDialogView', function() { expect(dialog.$el.find('.shareWithLoading').hasClass('hidden')).toEqual(true); expect(dialog.$el.find('.shareWithConfirm').hasClass('hidden')).toEqual(false); + expect(autocompleteStub.lastCall.args[0]).toEqual('enable'); expect(dialog.$el.find('.shareWithField').prop('disabled')).toEqual(false); expect(dialog.$el.find('.shareWithField').val()).toEqual(''); }); @@ -1981,6 +1985,7 @@ describe('OC.Share.ShareDialogView', function() { // Ensure that the UI is not restored before adding the share expect(dialog.$el.find('.shareWithLoading').hasClass('hidden')).toEqual(false); expect(dialog.$el.find('.shareWithConfirm').hasClass('hidden')).toEqual(true); + expect(autocompleteStub.lastCall.args[0]).toEqual('disable'); expect(dialog.$el.find('.shareWithField').prop('disabled')).toEqual(true); expect(dialog.$el.find('.shareWithField').val()).toEqual('bob'); @@ -1996,6 +2001,7 @@ describe('OC.Share.ShareDialogView', function() { expect(dialog.$el.find('.shareWithLoading').hasClass('hidden')).toEqual(true); expect(dialog.$el.find('.shareWithConfirm').hasClass('hidden')).toEqual(false); + expect(autocompleteStub.lastCall.args[0]).toEqual('enable'); expect(dialog.$el.find('.shareWithField').prop('disabled')).toEqual(false); expect(dialog.$el.find('.shareWithField').val()).toEqual('bob'); @@ -2037,6 +2043,7 @@ describe('OC.Share.ShareDialogView', function() { expect(dialog.$el.find('.shareWithLoading').hasClass('hidden')).toEqual(true); expect(dialog.$el.find('.shareWithConfirm').hasClass('hidden')).toEqual(false); + expect(autocompleteStub.lastCall.args[0]).toEqual('enable'); expect(dialog.$el.find('.shareWithField').prop('disabled')).toEqual(false); expect(dialog.$el.find('.shareWithField').val()).toEqual('bob'); @@ -2088,6 +2095,7 @@ describe('OC.Share.ShareDialogView', function() { expect(dialog.$el.find('.shareWithLoading').hasClass('hidden')).toEqual(true); expect(dialog.$el.find('.shareWithConfirm').hasClass('hidden')).toEqual(false); + expect(autocompleteStub.lastCall.args[0]).toEqual('enable'); expect(dialog.$el.find('.shareWithField').prop('disabled')).toEqual(false); expect(dialog.$el.find('.shareWithField').val()).toEqual('bo'); }); @@ -2143,6 +2151,7 @@ describe('OC.Share.ShareDialogView', function() { expect(dialog.$el.find('.shareWithLoading').hasClass('hidden')).toEqual(true); expect(dialog.$el.find('.shareWithConfirm').hasClass('hidden')).toEqual(false); + expect(autocompleteStub.lastCall.args[0]).toEqual('enable'); expect(dialog.$el.find('.shareWithField').prop('disabled')).toEqual(false); expect(dialog.$el.find('.shareWithField').val()).toEqual('bob'); }); @@ -2171,6 +2180,7 @@ describe('OC.Share.ShareDialogView', function() { expect(dialog.$el.find('.shareWithLoading').hasClass('hidden')).toEqual(true); expect(dialog.$el.find('.shareWithConfirm').hasClass('hidden')).toEqual(false); + expect(autocompleteStub.lastCall.args[0]).toEqual('enable'); expect(dialog.$el.find('.shareWithField').prop('disabled')).toEqual(false); expect(dialog.$el.find('.shareWithField').val()).toEqual('bob'); @@ -2188,6 +2198,7 @@ describe('OC.Share.ShareDialogView', function() { expect(dialog.$el.find('.shareWithLoading').hasClass('hidden')).toEqual(true); expect(dialog.$el.find('.shareWithConfirm').hasClass('hidden')).toEqual(false); + expect(autocompleteStub.lastCall.args[0]).toEqual('enable'); expect(dialog.$el.find('.shareWithField').prop('disabled')).toEqual(false); expect(dialog.$el.find('.shareWithField').val()).toEqual('bob');