compose: Change UI which toggles `enter_sends` setting.

Use a popover which displays both the options instead of long text.
We only use a small text indicating the current state which user
can click on to trigger the popover.
This commit is contained in:
Aman Agrawal 2021-12-10 08:07:42 +00:00 committed by Tim Abbott
parent 937d49e209
commit db09639f6c
10 changed files with 180 additions and 65 deletions

View File

@ -9,7 +9,6 @@ const {page_params, user_settings} = require("../zjsunit/zpage_params");
const noop = () => {}; const noop = () => {};
const channel = mock_esm("../../static/js/channel");
const compose = mock_esm("../../static/js/compose", { const compose = mock_esm("../../static/js/compose", {
finish: noop, finish: noop,
}); });
@ -1142,32 +1141,11 @@ test("initialize", ({override, mock_template}) => {
event.key = "a"; event.key = "a";
$("form#send_message_form").trigger(event); $("form#send_message_form").trigger(event);
// select_on_focus()
let channel_patch_called = false;
override(channel, "patch", (params) => {
assert.equal(params.url, "/json/settings");
assert.equal(params.idempotent, true);
assert.deepEqual(params.data, {enter_sends: user_settings.enter_sends});
channel_patch_called = true;
});
user_settings.enter_sends = false;
$(".enter_sends").trigger("click");
assert.equal(user_settings.enter_sends, true);
// Now we re-run both .initialize() and the click handler, this time
// with enter_sends: user_settings.enter_sends being true
user_settings.enter_sends = true;
$(".enter_sends").trigger("click");
assert.equal(user_settings.enter_sends, false);
$("#stream_message_recipient_stream").off("focus"); $("#stream_message_recipient_stream").off("focus");
$("#stream_message_recipient_topic").off("focus"); $("#stream_message_recipient_topic").off("focus");
$("#private_message_recipient").off("focus"); $("#private_message_recipient").off("focus");
$("form#send_message_form").off("keydown"); $("form#send_message_form").off("keydown");
$("form#send_message_form").off("keyup"); $("form#send_message_form").off("keyup");
$(".enter_sends").off("click");
$("#private_message_recipient").off("blur"); $("#private_message_recipient").off("blur");
ct.initialize(); ct.initialize();
@ -1176,7 +1154,6 @@ test("initialize", ({override, mock_template}) => {
assert.ok(stream_typeahead_called); assert.ok(stream_typeahead_called);
assert.ok(subject_typeahead_called); assert.ok(subject_typeahead_called);
assert.ok(pm_recipient_typeahead_called); assert.ok(pm_recipient_typeahead_called);
assert.ok(channel_patch_called);
assert.ok(compose_textarea_typeahead_called); assert.ok(compose_textarea_typeahead_called);
}); });

View File

@ -265,12 +265,18 @@ class CommonUtils {
} }
async ensure_enter_does_not_send(page: Page): Promise<void> { async ensure_enter_does_not_send(page: Page): Promise<void> {
let enter_sends = false;
await page.$eval(".enter_sends_false", (el) => { await page.$eval(".enter_sends_false", (el) => {
if ((el as HTMLElement).style.display !== "none") { if ((el as HTMLElement).style.display !== "none") {
// Click events gets propagated to `.enter_sends` which toggles the value. enter_sends = true;
(el as HTMLElement).click();
} }
}); });
if (enter_sends) {
const enter_sends_false_selector = ".enter_sends_choice input[value='false']";
await page.waitForSelector(enter_sends_false_selector);
await page.click(enter_sends_false_selector);
}
} }
async assert_compose_box_content(page: Page, expected_value: string): Promise<void> { async assert_compose_box_content(page: Page, expected_value: string): Promise<void> {

View File

@ -660,6 +660,11 @@ export function initialize() {
return; return;
} }
if ($(".enter_sends").has(e.target).length) {
e.preventDefault();
return;
}
// Don't let clicks in the compose area count as // Don't let clicks in the compose area count as
// "unfocusing" our compose -- in other words, e.g. // "unfocusing" our compose -- in other words, e.g.
// clicking "Press Enter to send" should not // clicking "Press Enter to send" should not
@ -872,6 +877,7 @@ export function initialize() {
!$(e.target).closest(".micromodal").length && !$(e.target).closest(".micromodal").length &&
!$(e.target).closest("[data-tippy-root]").length && !$(e.target).closest("[data-tippy-root]").length &&
!$(e.target).closest(".modal-backdrop").length && !$(e.target).closest(".modal-backdrop").length &&
!$(e.target).closest(".enter_sends").length &&
$(e.target).closest("body").length $(e.target).closest("body").length
) { ) {
// Unfocus our compose area if we click out of it. Don't let exits out // Unfocus our compose area if we click out of it. Don't let exits out

View File

@ -7,7 +7,6 @@ import pygments_data from "../generated/pygments_data.json";
import * as emoji from "../shared/js/emoji"; import * as emoji from "../shared/js/emoji";
import * as typeahead from "../shared/js/typeahead"; import * as typeahead from "../shared/js/typeahead";
import * as channel from "./channel";
import * as compose from "./compose"; import * as compose from "./compose";
import * as compose_pm_pill from "./compose_pm_pill"; import * as compose_pm_pill from "./compose_pm_pill";
import * as compose_state from "./compose_state"; import * as compose_state from "./compose_state";
@ -1177,22 +1176,6 @@ export function initialize() {
$("form#send_message_form").on("keydown", handle_keydown); $("form#send_message_form").on("keydown", handle_keydown);
$("form#send_message_form").on("keyup", handle_keyup); $("form#send_message_form").on("keyup", handle_keyup);
$(".enter_sends").on("click", () => {
user_settings.enter_sends = !user_settings.enter_sends;
$(`.enter_sends_${!user_settings.enter_sends}`).hide();
$(`.enter_sends_${user_settings.enter_sends}`).show();
// Refocus in the content box so you can continue typing or
// press Enter to send.
$("#compose-textarea").trigger("focus");
return channel.patch({
url: "/json/settings",
idempotent: true,
data: {enter_sends: user_settings.enter_sends},
});
});
// limit number of items so the list doesn't fall off the screen // limit number of items so the list doesn't fall off the screen
$("#stream_message_recipient_stream").typeahead({ $("#stream_message_recipient_stream").typeahead({
source() { source() {

View File

@ -6,17 +6,22 @@ import $ from "jquery";
import {delegate} from "tippy.js"; import {delegate} from "tippy.js";
import render_compose_control_buttons_popover from "../templates/compose_control_buttons_popover.hbs"; import render_compose_control_buttons_popover from "../templates/compose_control_buttons_popover.hbs";
import render_compose_select_enter_behaviour_popover from "../templates/compose_select_enter_behaviour_popover.hbs";
import render_left_sidebar_stream_setting_popover from "../templates/left_sidebar_stream_setting_popover.hbs"; import render_left_sidebar_stream_setting_popover from "../templates/left_sidebar_stream_setting_popover.hbs";
import render_mobile_message_buttons_popover_content from "../templates/mobile_message_buttons_popover_content.hbs"; import render_mobile_message_buttons_popover_content from "../templates/mobile_message_buttons_popover_content.hbs";
import * as channel from "./channel";
import * as common from "./common";
import * as compose_actions from "./compose_actions"; import * as compose_actions from "./compose_actions";
import * as giphy from "./giphy"; import * as giphy from "./giphy";
import * as narrow_state from "./narrow_state"; import * as narrow_state from "./narrow_state";
import * as popovers from "./popovers"; import * as popovers from "./popovers";
import * as settings_data from "./settings_data"; import * as settings_data from "./settings_data";
import {user_settings} from "./user_settings";
let left_sidebar_stream_setting_popover_displayed = false; let left_sidebar_stream_setting_popover_displayed = false;
let compose_mobile_button_popover_displayed = false; let compose_mobile_button_popover_displayed = false;
export let compose_enter_sends_popover_displayed = false;
let compose_control_buttons_popover_instance; let compose_control_buttons_popover_instance;
export function get_compose_control_buttons_popover() { export function get_compose_control_buttons_popover() {
@ -41,7 +46,8 @@ export function any_active() {
return ( return (
left_sidebar_stream_setting_popover_displayed || left_sidebar_stream_setting_popover_displayed ||
compose_mobile_button_popover_displayed || compose_mobile_button_popover_displayed ||
compose_control_buttons_popover_instance compose_control_buttons_popover_instance ||
compose_enter_sends_popover_displayed
); );
} }
@ -125,4 +131,46 @@ export function initialize() {
compose_control_buttons_popover_instance = undefined; compose_control_buttons_popover_instance = undefined;
}, },
}); });
delegate("body", {
...default_popover_props,
target: ".enter_sends",
placement: "top",
onShow(instance) {
on_show_prep(instance);
instance.setContent(
render_compose_select_enter_behaviour_popover({
enter_sends_true: user_settings.enter_sends,
}),
);
compose_enter_sends_popover_displayed = true;
},
onMount(instance) {
common.adjust_mac_shortcuts(".enter_sends_choices kbd");
$(instance.popper).one("click", ".enter_sends_choice", (e) => {
let selected_behaviour = $(e.currentTarget)
.find("input[type='radio']")
.attr("value");
selected_behaviour = selected_behaviour === "true"; // Convert to bool
user_settings.enter_sends = selected_behaviour;
$(`.enter_sends_${!selected_behaviour}`).hide();
$(`.enter_sends_${selected_behaviour}`).show();
// Refocus in the content box so you can continue typing or
// press Enter to send.
$("#compose-textarea").trigger("focus");
return channel.patch({
url: "/json/settings",
idempotent: true,
data: {enter_sends: selected_behaviour},
});
});
},
onHidden(instance) {
instance.destroy();
compose_enter_sends_popover_displayed = false;
},
});
} }

View File

@ -1,7 +1,9 @@
import $ from "jquery"; import $ from "jquery";
import tippy, {delegate} from "tippy.js"; import tippy, {delegate} from "tippy.js";
import {$t} from "./i18n";
import * as message_lists from "./message_lists"; import * as message_lists from "./message_lists";
import * as popover_menus from "./popover_menus";
import * as reactions from "./reactions"; import * as reactions from "./reactions";
import * as rows from "./rows"; import * as rows from "./rows";
import * as timerender from "./timerender"; import * as timerender from "./timerender";
@ -185,4 +187,16 @@ export function initialize() {
instance.destroy(); instance.destroy();
}, },
}); });
delegate("body", {
target: [".enter_sends_true", ".enter_sends_false"],
content: $t({defaultMessage: "Change send shortcut"}),
onShow() {
// Don't show tooltip if the popover is displayed.
if (popover_menus.compose_enter_sends_popover_displayed) {
return false;
}
return true;
},
});
} }

View File

@ -441,12 +441,45 @@ input.recipient_box {
} }
} }
.enter_sends_choices {
.enter_sends_choice {
display: flex;
gap: 8px;
padding-top: 4px;
input[type="radio"] {
position: relative;
top: 5px;
}
&:first-child {
padding: 0 0 4px;
border-bottom: 1px solid hsla(0, 0%, 0%, 0.2);
}
}
.enter_sends_choice_text {
display: flex;
flex-direction: column;
}
.enter_sends_minor,
.enter_sends_minor kbd {
opacity: 0.9;
font-size: 11px;
color: hsl(0, 0%, 50%);
}
}
.enter_sends { .enter_sends {
font-size: 12px; font-size: 12px;
height: 14px; height: 14px;
padding-left: 4px; padding-left: 4px;
opacity: 0.7; opacity: 0.7;
margin-bottom: 5px; margin-bottom: 5px;
position: relative;
top: -2px;
cursor: pointer;
@media (width < $mm_min) { @media (width < $mm_min) {
font-size: 11px; font-size: 11px;
@ -454,6 +487,7 @@ input.recipient_box {
kbd { kbd {
height: 16px; height: 16px;
padding: 0 4px;
} }
&:hover { &:hover {
@ -465,8 +499,10 @@ input.recipient_box {
display: none; display: none;
} }
.fa-exchange { i {
margin: 0 4px; padding-left: 3px;
font-size: 12px;
font-weight: 600;
} }
} }

View File

@ -63,12 +63,21 @@ body.dark-theme {
background-color: hsl(211, 28%, 14%); background-color: hsl(211, 28%, 14%);
} }
.enter_sends kbd { .enter_sends,
background-color: hsl(211, 29%, 14%); .enter_sends_choices {
border-color: hsl(211, 29%, 14%);
box-shadow: inset 0 -1px 0 hsl(210, 5%, 34%, 0.2);
text-shadow: none;
color: hsl(236, 33%, 90%); color: hsl(236, 33%, 90%);
kbd {
background-color: hsl(211, 29%, 14%);
border-color: hsl(211, 29%, 14%);
box-shadow: inset 0 -1px 0 hsl(210, 5%, 34%, 0.2);
text-shadow: none;
color: hsl(236, 33%, 90%);
}
.enter_sends_minor {
color: hsl(0, 0%, 80%);
}
} }
table.table-striped thead.table-sticky-headers th { table.table-striped thead.table-sticky-headers th {

View File

@ -105,21 +105,21 @@
<div class="compose_bottom_bottom_container"> <div class="compose_bottom_bottom_container">
<span id="compose_limit_indicator"></span> <span id="compose_limit_indicator"></span>
<div class="enter_sends"> <div class="enter_sends">
<label id="enter-sends-label" class="compose_checkbox_label tippy-zulip-tooltip" data-tippy-content="Click to switch"> <span class="enter_sends_true">
<span class="enter_sends_true"> <kbd>Enter</kbd>
<kbd>Enter</kbd> {{t 'to send' }} {{#tr}}
</span> <kbd class="hide">Enter</kbd>
<span class="enter_sends_false"> <span>to send</span>
<kbd>Ctrl</kbd> + <kbd>Enter</kbd> {{t 'to send' }} {{/tr}}
</span> </span>
<i class="fa fa-exchange" aria-hidden="true"></i> <span class="enter_sends_false">
<span class="enter_sends_true"> <kbd>Ctrl</kbd> + <kbd>Enter</kbd>
<kbd>Ctrl</kbd> + <kbd>Enter</kbd> {{t 'to add a new line' }} {{#tr}}
</span> <kbd class="hide">Enter</kbd>
<span class="enter_sends_false"> <span>to send</span>
<kbd>Enter</kbd> {{t 'to add a new line' }} {{/tr}}
</span> </span>
</label> <i class="fa fa-caret-down" aria-hidden="true"></i>
</div> </div>
</div> </div>
</div> </div>

View File

@ -0,0 +1,36 @@
<div class="enter_sends_choices grey-box">
<label class="enter_sends_choice">
<input type="radio" class="prop-element" value="true"{{#if enter_sends_true }} checked{{/if}} />
<div class="enter_sends_choice_text">
<span>
{{#tr}}
<z-button><kbd>Enter</kbd></z-button>
<span>to send</span>
{{/tr}}
</span>
<span class="enter_sends_minor">
{{#tr}}
<z-button><kbd>Ctrl</kbd> + <kbd>Enter</kbd></z-button>
<span>to add a new line</span>
{{/tr}}
</span>
</div>
</label>
<label class="enter_sends_choice">
<input type="radio" class="prop-element" value="false"{{#unless enter_sends_true }} checked{{/unless}} />
<div class="enter_sends_choice_text">
<span>
{{#tr}}
<z-button><kbd>Ctrl</kbd> + <kbd>Enter</kbd></z-button>
<span>to send</span>
{{/tr}}
</span>
<span class="enter_sends_minor">
{{#tr}}
<z-button><kbd>Enter</kbd></z-button>
<span>to add a new line</span>
{{/tr}}
</span>
</div>
</label>
</div>