FIX: make composer full screen shortcut work when inputs have focus (#6907)

FIX: make composer full screen shortcut work when inputs have focus (#6907)

  • Uses a Mousetrap plugin for global shortcuts
  • Implemented for search ctrl+alt+f and composer fullscreen shift+f11 shortcuts
diff --git a/app/assets/javascripts/discourse/components/composer-editor.js.es6 b/app/assets/javascripts/discourse/components/composer-editor.js.es6
index db6cde6..d5e0c8f 100644
--- a/app/assets/javascripts/discourse/components/composer-editor.js.es6
+++ b/app/assets/javascripts/discourse/components/composer-editor.js.es6
@@ -108,6 +108,13 @@ export default Ember.Component.extend({
     this._resetUpload(true);
   },
 
+  @observes("focusTarget")
+  setFocus() {
+    if (this.get("focusTarget") === "editor") {
+      this.$("textarea").putCursorAtEnd();
+    }
+  },
+
   @computed
   markdownOptions() {
     return {
diff --git a/app/assets/javascripts/discourse/components/d-editor.js.es6 b/app/assets/javascripts/discourse/components/d-editor.js.es6
index fca09f4..73afe5e 100644
--- a/app/assets/javascripts/discourse/components/d-editor.js.es6
+++ b/app/assets/javascripts/discourse/components/d-editor.js.es6
@@ -256,15 +256,6 @@ export default Ember.Component.extend({
     const mouseTrap = Mousetrap(this.$(".d-editor-input")[0]);
     const shortcuts = this.get("toolbar.shortcuts");
 
-    // for some reason I am having trouble bubbling this so hack it in
-    mouseTrap.bind(["ctrl+alt+f"], event => {
-      this.appEvents.trigger("header:keyboard-trigger", {
-        type: "search",
-        event
-      });
-      return true;
-    });
-
     Object.keys(shortcuts).forEach(sc => {
       const button = shortcuts[sc];
       mouseTrap.bind(sc, () => {
@@ -323,7 +314,6 @@ export default Ember.Component.extend({
     Object.keys(this.get("toolbar.shortcuts")).forEach(sc =>
       mouseTrap.unbind(sc)
     );
-    mouseTrap.unbind("ctrl+/", "command+/");
     this.$(".d-editor-preview").off("click.preview");
   },
 
diff --git a/app/assets/javascripts/discourse/controllers/composer.js.es6 b/app/assets/javascripts/discourse/controllers/composer.js.es6
index aa077e7..4d299ef 100644
--- a/app/assets/javascripts/discourse/controllers/composer.js.es6
+++ b/app/assets/javascripts/discourse/controllers/composer.js.es6
@@ -133,9 +133,10 @@ export default Ember.Controller.extend({
   @computed(
     "model.replyingToTopic",
     "model.creatingPrivateMessage",
-    "model.targetUsernames"
+    "model.targetUsernames",
+    "model.composeState"
   )
-  focusTarget(replyingToTopic, creatingPM, usernames) {
+  focusTarget(replyingToTopic, creatingPM, usernames, composeState) {
     if (this.capabilities.isIOS) {
       return "none";
     }
@@ -153,6 +154,10 @@ export default Ember.Controller.extend({
       return "reply";
     }
 
+    if (composeState === Composer.FULLSCREEN) {
+      return "editor";
+    }
+
     return "title";
   },
 
diff --git a/app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6 b/app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6
index fb69080..2fbc5c4 100644
--- a/app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6
+++ b/app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6
@@ -7,7 +7,7 @@ const bindings = {
   "!": { postAction: "showFlags" },
   "#": { handler: "goToPost", anonymous: true },
   "/": { handler: "toggleSearch", anonymous: true },
-  "ctrl+alt+f": { handler: "toggleSearch", anonymous: true },
+  "ctrl+alt+f": { handler: "toggleSearch", anonymous: true, global: true },
   "=": { handler: "toggleHamburgerMenu", anonymous: true },
   "?": { handler: "showHelpModal", anonymous: true },
   ".": { click: ".alert.alert-info.clickable", anonymous: true }, // show incoming/updated topics
@@ -67,7 +67,7 @@ const bindings = {
   "shift+s": { click: "#topic-footer-buttons button.share", anonymous: true }, // share topic
   "shift+u": { handler: "goToUnreadPost" },
   "shift+z shift+z": { handler: "logout" },
-  "shift+f11": { handler: "fullscreenComposer" },
+  "shift+f11": { handler: "fullscreenComposer", global: true },
   t: { postAction: "replyAsNewTopic" },
   u: { handler: "goBack", anonymous: true },
   "x r": {
@@ -101,7 +101,12 @@ export default {
       if (binding.path) {
         this._bindToPath(binding.path, key);
       } else if (binding.handler) {
-        this._bindToFunction(binding.handler, key);
+        if (binding.global) {
+          // global shortcuts will trigger even while focusing on input/textarea
+          this._globalBindToFunction(binding.handler, key);
+        } else {
+          this._bindToFunction(binding.handler, key);
+        }
       } else if (binding.postAction) {
         this._bindToSelectedPost(binding.postAction, key);
       } else if (binding.click) {
@@ -399,6 +404,12 @@ export default {
     });
   },
 
+  _globalBindToFunction(func, binding) {
+    if (typeof this[func] === "function") {
+      this.keyTrapper.bindGlobal(binding, _.bind(this[func], this));
+    }
+  },
+
   _bindToFunction(func, binding) {
     if (typeof this[func] === "function") {
       this.keyTrapper.bind(binding, _.bind(this[func], this));
diff --git a/app/assets/javascripts/discourse/templates/composer.hbs b/app/assets/javascripts/discourse/templates/composer.hbs
index 4f31398..9f512ba 100644
--- a/app/assets/javascripts/discourse/templates/composer.hbs
+++ b/app/assets/javascripts/discourse/templates/composer.hbs
@@ -111,7 +111,8 @@
                             importQuote=(action "importQuote")
                             togglePreview=(action "togglePreview")
                             showToolbar=showToolbar
-                            afterRefresh=(action "afterRefresh")}}
+                            afterRefresh=(action "afterRefresh")
+                            focusTarget=focusTarget}}
 
           <div class='submit-panel'>
             {{plugin-outlet name="composer-fields-below" args=(hash model=model)}}
diff --git a/app/assets/javascripts/vendor.js b/app/assets/javascripts/vendor.js
index 76a27a3..5279b2c 100644
--- a/app/assets/javascripts/vendor.js
+++ b/app/assets/javascripts/vendor.js
@@ -22,6 +22,7 @@
 //= require jquery.sortable.js
 //= require lodash.js
 //= require mousetrap.js
+//= require mousetrap-global-bind.js
 //= require rsvp.js
 //= require show-html.js
 //= require break_string
diff --git a/lib/tasks/javascript.rake b/lib/tasks/javascript.rake
index 2a06412..c2a1004 100644
--- a/lib/tasks/javascript.rake
+++ b/lib/tasks/javascript.rake
@@ -74,6 +74,8 @@ task 'javascript:update' do
     }, {
       source: 'moment-timezone/builds/moment-timezone-with-data.js'
     }, {
+      source: 'mousetrap/plugins/global-bind/mousetrap-global-bind.js'
+    }, {
       source: 'resumablejs/resumable.js'
     }, {
       # TODO: drop when we eventually drop IE11, this will land in iOS in version 13
diff --git a/vendor/assets/javascripts/mousetrap-global-bind.js b/vendor/assets/javascripts/mousetrap-global-bind.js
new file mode 100644
index 0000000..1c4ac14
--- /dev/null
+++ b/vendor/assets/javascripts/mousetrap-global-bind.js
@@ -0,0 +1,43 @@
+/**
+ * adds a bindGlobal method to Mousetrap that allows you to
+ * bind specific keyboard shortcuts that will still work
+ * inside a text input field
+ *
+ * usage:
+ * Mousetrap.bindGlobal('ctrl+s', _saveChanges);
+ */
+/* global Mousetrap:true */
+(function(Mousetrap) {
+    var _globalCallbacks = {};
+    var _originalStopCallback = Mousetrap.prototype.stopCallback;
+
+    Mousetrap.prototype.stopCallback = function(e, element, combo, sequence) {
+        var self = this;
+
+        if (self.paused) {
+            return true;
+        }
+
+        if (_globalCallbacks[combo] || _globalCallbacks[sequence]) {
+            return false;
+        }
+
+        return _originalStopCallback.call(self, e, element, combo);
+    };
+
+    Mousetrap.prototype.bindGlobal = function(keys, callback, action) {
+        var self = this;
+        self.bind(keys, callback, action);
+
+        if (keys instanceof Array) {
+            for (var i = 0; i < keys.length; i++) {
+                _globalCallbacks[keys[i]] = true;
+            }
+            return;
+        }
+
+        _globalCallbacks[keys] = true;
+    };
+
+    Mousetrap.init();
+}) (Mousetrap);

GitHub sha: 090e9c84