diff --git a/client/js/keybinds.js b/client/js/keybinds.js index f702e341..0080d08b 100644 --- a/client/js/keybinds.js +++ b/client/js/keybinds.js @@ -107,6 +107,11 @@ const colorsHotkeys = { for (const hotkey in colorsHotkeys) { Mousetrap.bind("mod+" + hotkey, function(e) { + // Do not handle modifier hotkeys if input is not focused + if (document.activeElement !== input) { + return; + } + e.preventDefault(); const modifier = colorsHotkeys[e.key]; @@ -114,3 +119,66 @@ for (const hotkey in colorsHotkeys) { wrapCursor(input, modifier, input.selectionStart === input.selectionEnd ? "" : modifier); }); } + +// Ignored keys which should not automatically focus the input bar +const ignoredKeys = { + 8: true, // Backspace + 9: true, // Tab + 12: true, // Clear + 16: true, // Shift + 17: true, // Control + 18: true, // Alt + 19: true, // Pause + 20: true, // CapsLock + 27: true, // Escape + 33: true, // PageUp + 34: true, // PageDown + 35: true, // End + 36: true, // Home + 37: true, // ArrowLeft + 38: true, // ArrowUp + 39: true, // ArrowRight + 40: true, // ArrowDown + 45: true, // Insert + 46: true, // Delete + 112: true, // F1 + 113: true, // F2 + 114: true, // F3 + 115: true, // F4 + 116: true, // F5 + 117: true, // F6 + 118: true, // F7 + 119: true, // F8 + 120: true, // F9 + 121: true, // F10 + 122: true, // F11 + 123: true, // F12 + 144: true, // NumLock + 145: true, // ScrollLock + 224: true, // Meta +}; + +if (!("ontouchstart" in window || navigator.maxTouchPoints > 0)) { + $(document.body).on("keydown", (e) => { + // Ignore if target isn't body (e.g. focused into input) + // Ignore any key that uses alt modifier + // Ignore keys defined above + if (e.target !== document.body || e.altKey || ignoredKeys[e.which]) { + return; + } + + // Ignore all ctrl keys except for ctrl+v to allow pasting + if ((e.ctrlKey || e.metaKey) && e.which !== 86) { + return; + } + + // On enter, focus the input but do not propagate the event + // This way, a new line is not inserted + if (e.which === 13) { + input.trigger("focus"); + return false; + } + + input.trigger("focus"); + }); +} diff --git a/client/js/lounge.js b/client/js/lounge.js index 3f35d757..01048e4b 100644 --- a/client/js/lounge.js +++ b/client/js/lounge.js @@ -223,16 +223,6 @@ $(function() { input.trigger("focus"); } }; - - $(window).on("focus", focus); - - chat.on("click", ".chat", function() { - setTimeout(function() { - if (window.getSelection().isCollapsed) { - focus(); - } - }, 2); - }); } if (navigator.platform.match(/(Mac|iPhone|iPod|iPad)/i)) { @@ -241,7 +231,7 @@ $(function() { $("#form").on("submit", function(e) { e.preventDefault(); - utils.forceFocus(); + focus(); const target = chat.data("id"); const text = input.val(); diff --git a/client/js/renderPreview.js b/client/js/renderPreview.js index faccf26e..c7474d52 100644 --- a/client/js/renderPreview.js +++ b/client/js/renderPreview.js @@ -5,7 +5,6 @@ const options = require("./options"); const socket = require("./socket"); const templates = require("../views"); const chat = $("#chat"); -const input = $("#input"); const Mousetrap = require("mousetrap"); module.exports = renderPreview; @@ -207,8 +206,6 @@ function closeImageViewer({pushState = true} = {}) { imageViewer.empty(); }); - input.trigger("focus"); - // History management if (pushState) { const clickTarget = diff --git a/client/js/utils.js b/client/js/utils.js index 9e0bc37c..e972425e 100644 --- a/client/js/utils.js +++ b/client/js/utils.js @@ -2,7 +2,6 @@ const $ = require("jquery"); const escape = require("css.escape"); -const input = $("#input"); const viewport = $("#viewport"); var serverHash = -1; // eslint-disable-line no-var @@ -14,7 +13,6 @@ module.exports = { serverHash, lastMessageId, confirmExit, - forceFocus, scrollIntoViewNicely, hasRoleInChannel, move, @@ -54,12 +52,6 @@ function hasRoleInChannel(channel, roles) { return user.parent().is("." + roles.join(", .")); } -// Triggering click event opens the virtual keyboard on mobile -// This can only be called from another interactive event (e.g. button click) -function forceFocus() { - input.trigger("click").trigger("focus"); -} - // Reusable scrollIntoView parameters for channel list / user list function scrollIntoViewNicely(el) { // Ideally this would use behavior: "smooth", but that does not consistently work in e.g. Chrome