diff --git a/docs/overview/changelog.md b/docs/overview/changelog.md index bf16883afb..39cee69e52 100644 --- a/docs/overview/changelog.md +++ b/docs/overview/changelog.md @@ -52,6 +52,7 @@ mentioned, even if they are not subscribed. discoverable. - Added a fast local echo to emoji reactions. - Added new "basics" section to keyboard shortcuts documentation. +- Added a new keyboard shortcut for quote-and-reply. - Renamed "Home" to "All messages", to avoid users clicking on it too early in using Zulip. - Messages containing just a link to an image (or an uploaded image) diff --git a/frontend_tests/node_tests/hotkey.js b/frontend_tests/node_tests/hotkey.js index d997166831..84f20acaf4 100644 --- a/frontend_tests/node_tests/hotkey.js +++ b/frontend_tests/node_tests/hotkey.js @@ -236,7 +236,7 @@ function stubbing(func_name_to_stub, test_function) { assert_mapping('d', 'drafts.launch'); // Next, test keys that only work on a selected message. - var message_view_only_keys = '@*+RjJkKsSuvi:GM'; + var message_view_only_keys = '@*+>RjJkKsSuvi:GM'; // Check that they do nothing without a selected message global.current_msg_list.empty = return_true; @@ -246,7 +246,7 @@ function stubbing(func_name_to_stub, test_function) { // Check that they do nothing while in the settings overlay overlays.settings_open = return_true; - assert_unmapped('@*+-rRjJkKsSuvi:GM'); + assert_unmapped('@*+->rRjJkKsSuvi:GM'); overlays.settings_open = return_false; // TODO: Similar check for being in the subs page @@ -266,6 +266,7 @@ function stubbing(func_name_to_stub, test_function) { assert_mapping('u', 'popovers.show_sender_info'); assert_mapping('i', 'popovers.open_message_menu'); assert_mapping(':', 'reactions.open_reactions_popover', true); + assert_mapping('>', 'compose_actions.quote_and_reply'); overlays.is_active = return_true; overlays.lightbox_open = return_true; diff --git a/static/js/compose_actions.js b/static/js/compose_actions.js index 49ded76406..0abf5b62e7 100644 --- a/static/js/compose_actions.js +++ b/static/js/compose_actions.js @@ -365,6 +365,25 @@ exports.on_topic_narrow = function () { $('#compose-textarea').focus().select(); }; +exports.quote_and_reply = function (opts) { + var textarea = $("#compose-textarea"); + var message_id = current_msg_list.selected_id(); + + exports.respond_to_message(opts); + channel.get({ + url: '/json/messages/' + message_id, + idempotent: true, + success: function (data) { + if (textarea.val() === "") { + textarea.val("```quote\n" + data.raw_content +"\n```\n"); + } else { + textarea.val(textarea.val() + "\n```quote\n" + data.raw_content +"\n```\n"); + } + $("#compose-textarea").trigger("autosize.resize"); + }, + }); +}; + exports.on_narrow = function () { if (narrow_state.narrowed_by_topic_reply()) { exports.on_topic_narrow(); diff --git a/static/js/hotkey.js b/static/js/hotkey.js index 79bc766aa5..4a27416c12 100644 --- a/static/js/hotkey.js +++ b/static/js/hotkey.js @@ -66,6 +66,7 @@ var keypress_mappings = { 45: {name: 'toggle_message_collapse', message_view_only: true}, // '-' 47: {name: 'search', message_view_only: false}, // '/' 58: {name: 'toggle_reactions_popover', message_view_only: true}, // ':' + 62: {name: 'compose_quote_reply', message_view_only: true}, // '>' 63: {name: 'show_shortcuts', message_view_only: false}, // '?' 64: {name: 'compose_reply_with_mention', message_view_only: true}, // '@' 65: {name: 'stream_cycle_backward', message_view_only: true}, // 'A' @@ -703,6 +704,9 @@ exports.process_hotkey = function (e, hotkey) { case 'toggle_message_collapse': condense.toggle_collapse(msg); return true; + case 'compose_quote_reply': // > : respond to selected message with quote + compose_actions.quote_and_reply({trigger: 'hotkey'}); + return true; } return false; diff --git a/static/js/popovers.js b/static/js/popovers.js index 6f5637e875..ceefc64df7 100644 --- a/static/js/popovers.js +++ b/static/js/popovers.js @@ -577,22 +577,12 @@ exports.register_click_handlers = function () { }); $('body').on('click', '.respond_button', function (e) { - var textarea = $("#compose-textarea"); - var msgid = $(e.currentTarget).data("message-id"); - - compose_actions.respond_to_message({trigger: 'popover respond'}); - channel.get({ - url: '/json/messages/' + msgid, - idempotent: true, - success: function (data) { - if (textarea.val() === "") { - textarea.val("```quote\n" + data.raw_content +"\n```\n"); - } else { - textarea.val(textarea.val() + "\n```quote\n" + data.raw_content +"\n```\n"); - } - $("#compose-textarea").trigger("autosize.resize"); - }, - }); + // Arguably, we should fetch the message ID to respond to from + // e.target, but that should always be the current selected + // message in the current message list (and + // compose_actions.respond_to_message doesn't take a message + // argument). + compose_actions.quote_and_reply({trigger: 'popover respond'}); popovers.hide_actions_popover(); e.stopPropagation(); e.preventDefault(); diff --git a/static/styles/dark.css b/static/styles/dark.css index d185a1bfc7..4b7a38d9d6 100644 --- a/static/styles/dark.css +++ b/static/styles/dark.css @@ -159,7 +159,7 @@ body.dark-mode .popover { } body.dark-mode .dropdown-menu a { - color: inherit; + color: inherit; } body.dark-mode .dropdown .dropdown-menu li.divider, diff --git a/templates/zerver/help/keyboard-shortcuts.md b/templates/zerver/help/keyboard-shortcuts.md index 37a8de466e..d82c5db6f6 100644 --- a/templates/zerver/help/keyboard-shortcuts.md +++ b/templates/zerver/help/keyboard-shortcuts.md @@ -85,6 +85,8 @@ below, and add more to your repertoire as needed. * **Reply only to author**: `R` +* **Quote and reply to message**: `>` + * **New stream message**: `c` — For starting a new topic in a stream. * **New private message**: `C` diff --git a/templates/zerver/keyboard_shortcuts.html b/templates/zerver/keyboard_shortcuts.html index 98e2d64ac0..b3ea78659d 100644 --- a/templates/zerver/keyboard_shortcuts.html +++ b/templates/zerver/keyboard_shortcuts.html @@ -116,6 +116,10 @@