diff --git a/web/e2e-tests/compose.test.ts b/web/e2e-tests/compose.test.ts index bb36cfbd80..613585eb84 100644 --- a/web/e2e-tests/compose.test.ts +++ b/web/e2e-tests/compose.test.ts @@ -106,9 +106,9 @@ async function test_open_close_compose_box(page: Page): Promise { await page.waitForSelector("#stream_message_recipient_topic", {hidden: true}); await page.keyboard.press("KeyX"); - await page.waitForSelector("#compose-private-recipient", {visible: true}); + await page.waitForSelector("#compose-direct-recipient", {visible: true}); await close_compose_box(page); - await page.waitForSelector("#compose-private-recipient", {hidden: true}); + await page.waitForSelector("#compose-direct-recipient", {hidden: true}); } async function test_narrow_to_private_messages_with_cordelia(page: Page): Promise { diff --git a/web/e2e-tests/drafts.test.ts b/web/e2e-tests/drafts.test.ts index 6546015ef5..c4f778e042 100644 --- a/web/e2e-tests/drafts.test.ts +++ b/web/e2e-tests/drafts.test.ts @@ -169,7 +169,7 @@ async function test_restore_private_message_draft_via_draft_overlay(page: Page): console.log("Restoring private message draft."); await page.click(".message_row.private-message .restore-draft"); await wait_for_drafts_to_disappear(page); - await page.waitForSelector("#compose-private-recipient", {visible: true}); + await page.waitForSelector("#compose-direct-recipient", {visible: true}); await common.check_compose_state(page, { content: "Test private message.", }); @@ -201,7 +201,7 @@ async function test_delete_draft(page: Page): Promise { async function test_save_draft_by_reloading(page: Page): Promise { console.log("Saving draft by reloading."); await page.keyboard.press("KeyX"); - await page.waitForSelector("#compose-private-recipient", {visible: true}); + await page.waitForSelector("#compose-direct-recipient", {visible: true}); await common.fill_form(page, "form#send_message_form", { content: "Test private message draft.", }); diff --git a/web/src/compose_actions.js b/web/src/compose_actions.js index 16a14219d8..75d5263222 100644 --- a/web/src/compose_actions.js +++ b/web/src/compose_actions.js @@ -41,7 +41,7 @@ function hide_box() { drafts.update_draft(); blur_compose_inputs(); $("#stream_message_recipient_topic").hide(); - $("#compose-private-recipient").hide(); + $("#compose-direct-recipient").hide(); $(".new_message_textarea").css("min-height", ""); compose_fade.clear_compose(); $(".message_comp").hide(); @@ -79,17 +79,24 @@ export function set_focus(msg_type, opts) { } } -function show_compose_box(msg_type, opts) { +export function show_compose_box(msg_type, opts) { if (msg_type === "stream") { - $("#compose-private-recipient").hide(); + $("#compose-direct-recipient").hide(); $("#stream_message_recipient_topic").show(); $("#stream_toggle").addClass("active"); $("#private_message_toggle").removeClass("active"); + $("#compose-recipient").removeClass("compose-recipient-direct-selected"); } else { - $("#compose-private-recipient").show(); + $("#compose-direct-recipient").show(); $("#stream_message_recipient_topic").hide(); $("#stream_toggle").removeClass("active"); $("#private_message_toggle").addClass("active"); + $("#compose-recipient").addClass("compose-recipient-direct-selected"); + // TODO: When "Direct message" is selected, we show "DM" on the dropdown + // button. It would be nice if the dropdown supported a way to attach + // the "DM" button display string so we wouldn't have to manually change + // it here. + $("#compose_select_recipient_name").text($t({defaultMessage: "DM"})); } compose_banner.clear_errors(); compose_banner.clear_warnings(); @@ -97,6 +104,9 @@ function show_compose_box(msg_type, opts) { // When changing this, edit the 42px in _maybe_autoscroll $(".new_message_textarea").css("min-height", "3em"); + if (opts.trigger === "toggle recipient type") { + update_placeholder_text(); + } set_focus(msg_type, opts); } diff --git a/web/src/compose_recipient.js b/web/src/compose_recipient.js index 54867dab3c..7f5ad60191 100644 --- a/web/src/compose_recipient.js +++ b/web/src/compose_recipient.js @@ -3,6 +3,8 @@ import $ from "jquery"; import _ from "lodash"; +// todo: fix circular import here +import * as compose_actions from "./compose_actions"; import * as compose_banner from "./compose_banner"; import * as compose_fade from "./compose_fade"; import * as compose_state from "./compose_state"; @@ -16,6 +18,8 @@ import * as util from "./util"; export let compose_recipient_widget; +const DIRECT_MESSAGE = "direct"; + function composing_to_current_topic_narrow() { return ( util.lower_same(compose_state.stream_name(), narrow_state.stream() || "") && @@ -121,15 +125,47 @@ export function check_stream_posting_policy_for_compose_box(stream_name) { } } -export function on_compose_select_recipient_update(new_value) { - const $stream_header_colorblock = $("#compose_recipient_selection_dropdown").find( - ".stream_header_colorblock", - ); - stream_bar.decorate(new_value, $stream_header_colorblock); - update_on_recipient_change(); - $("#stream_message_recipient_topic").trigger("focus").trigger("select"); +function switch_message_type(message_type) { + $("#compose-content .alert").hide(); - check_stream_posting_policy_for_compose_box(new_value); + compose_state.set_message_type(message_type); + + const opts = { + message_type, + trigger: "toggle recipient type", + stream: compose_state.stream_name(), + topic: compose_state.topic(), + private_message_recipient: compose_state.private_message_recipient(), + }; + compose_actions.show_compose_box(message_type, opts); +} + +export function on_compose_select_recipient_update(new_value) { + const message_type = compose_state.get_message_type(); + if (new_value === DIRECT_MESSAGE) { + // TODO: In theory, we could do something more lightweight in + // the case it's already that value, but doing nothing would + // display the wrong and fail to update focus properly. + switch_message_type("private"); + + if (compose_state.private_message_recipient().length === 0) { + $("#private_message_recipient").trigger("focus").trigger("select"); + } + } else { + const $stream_header_colorblock = $("#compose_recipient_selection_dropdown").find( + ".stream_header_colorblock", + ); + stream_bar.decorate(new_value, $stream_header_colorblock); + if (message_type === "private") { + switch_message_type("stream"); + } + // Always move focus to the topic input even if it's not empty, + // since it's likely the user will want to update the topic + // after updating the stream. + $("#stream_message_recipient_topic").trigger("focus").trigger("select"); + check_stream_posting_policy_for_compose_box(new_value); + } + update_on_recipient_change(); } export function update_stream_dropdown_options() { @@ -144,7 +180,7 @@ export function possibly_update_dropdown_selection(old_stream_name, new_stream_n } function get_options_for_recipient_widget() { - return stream_data + const options = stream_data .subscribed_subs() .map((stream) => ({ name: stream.name, @@ -160,6 +196,13 @@ function get_options_for_recipient_widget() { } return 0; }); + + const direct_messages_option = { + name: $t({defaultMessage: "Direct message"}), + value: DIRECT_MESSAGE, + }; + options.unshift(direct_messages_option); + return options; } export function initialize() { diff --git a/web/styles/compose.css b/web/styles/compose.css index 4a273cab98..0b7cb355ce 100644 --- a/web/styles/compose.css +++ b/web/styles/compose.css @@ -101,13 +101,22 @@ } } - .right_part, - #stream_message_recipient_topic { + #compose-recipient { padding: 0; display: flex; align-items: center; flex: 1; + &.compose-recipient-direct-selected { + #compose_recipient_selection_dropdown .dropdown-toggle { + border-radius: 4px !important; + } + + .stream_header_colorblock { + display: none; + } + } + .fa-angle-right { font-size: 0.9em; -webkit-text-stroke: 0.05em; @@ -132,17 +141,8 @@ } } - .pm_recipient { - margin-left: 5px; - display: flex; - align-items: center; - width: 100%; - } - - #compose-private-recipient .to_text { - vertical-align: middle; - - font-weight: 600; + #compose-direct-recipient { + flex-grow: 1; } .message_header { @@ -616,12 +616,9 @@ input.recipient_box { } } -#stream_message_recipient_topic, -#compose-private-recipient { +#compose-recipient { + min-width: 0; display: flex; -} - -#compose-private-recipient { align-items: center; width: 100%; } diff --git a/web/styles/dark_theme.css b/web/styles/dark_theme.css index 73d9c2ca98..f4469dd694 100644 --- a/web/styles/dark_theme.css +++ b/web/styles/dark_theme.css @@ -475,7 +475,7 @@ } #search_arrows .pill, - .pm_recipient .pill-container .pill { + #compose-direct-recipient.pill-container .pill { color: inherit; border: 1px solid hsl(0deg 0% 0% / 50%); background-color: hsl(0deg 0% 0% / 25%); @@ -483,7 +483,7 @@ } #search_arrows .pill:focus, - .pm_recipient .pill-container .pill:focus { + #compose-direct-recipient.pill-container .pill:focus { color: hsl(0deg 0% 100%); border: 1px solid hsl(176deg 78% 28% / 60%); background-color: hsl(176deg 49% 42% / 40%); diff --git a/web/styles/input_pill.css b/web/styles/input_pill.css index 8c4150dff1..be5daaba76 100644 --- a/web/styles/input_pill.css +++ b/web/styles/input_pill.css @@ -109,7 +109,7 @@ } } -.pm_recipient .pill-container { +#compose-direct-recipient.pill-container { padding: 0 2px; flex-grow: 1; align-content: center; diff --git a/web/templates/compose.hbs b/web/templates/compose.hbs index 412183e4ca..4d51b9365b 100644 --- a/web/templates/compose.hbs +++ b/web/templates/compose.hbs @@ -93,29 +93,18 @@ {{tooltip_hotkey_hints "Esc"}} -
- - +
+
{{> settings/dropdown_list_widget widget_name="compose_select_recipient" - list_placeholder=(t 'Filter streams')}} + list_placeholder=(t 'Filter')}}
-
-
-
- {{t 'To' }}: -
-
-
- -
-
-
-
+
+
diff --git a/web/tests/compose.test.js b/web/tests/compose.test.js index 698fa670a9..b7760c291d 100644 --- a/web/tests/compose.test.js +++ b/web/tests/compose.test.js @@ -766,8 +766,7 @@ test_ui("on_events", ({override}) => { test_ui("create_message_object", ({override, override_rewire}) => { mock_stream_header_colorblock(); mock_banners(); - override_rewire(stream_bar, "decorate", noop); - override_rewire(compose_recipient, "update_on_recipient_change", noop); + override_rewire(compose_recipient, "on_compose_select_recipient_update", noop); compose_state.set_stream_name("social"); $("#stream_message_recipient_topic").val("lunch"); diff --git a/web/tests/compose_actions.test.js b/web/tests/compose_actions.test.js index ba7970a10d..e40a3b34fa 100644 --- a/web/tests/compose_actions.test.js +++ b/web/tests/compose_actions.test.js @@ -148,7 +148,7 @@ test("start", ({override, override_rewire}) => { start("stream", opts); assert_visible("#stream_message_recipient_topic"); - assert_hidden("#compose-private-recipient"); + assert_hidden("#compose-direct-recipient"); assert.equal(compose_state.stream_name(), "stream1"); assert.equal(compose_state.topic(), "topic1"); @@ -209,7 +209,7 @@ test("start", ({override, override_rewire}) => { start("private", opts); assert_hidden("#stream_message_recipient_topic"); - assert_visible("#compose-private-recipient"); + assert_visible("#compose-direct-recipient"); assert.equal(compose_state.private_message_recipient(), "foo@example.com"); assert.equal($("#compose-textarea").val(), "hello"); @@ -245,7 +245,7 @@ test("start", ({override, override_rewire}) => { assert.ok(abort_xhr_called); assert.ok(pill_cleared); assert_visible("#compose_controls"); - assert_hidden("#compose-private-recipient"); + assert_hidden("#compose-direct-recipient"); assert.ok(!compose_state.composing()); }); @@ -254,7 +254,7 @@ test("respond_to_message", ({override, override_rewire}) => { override_rewire(compose_actions, "set_focus", () => {}); override_rewire(compose_actions, "complete_starting_tasks", () => {}); override_rewire(compose_actions, "clear_textarea", () => {}); - override_rewire(compose_recipient, "update_on_recipient_change", () => {}); + override_rewire(compose_recipient, "on_compose_select_recipient_update", noop); override_private_message_recipient({override}); mock_stream_header_colorblock(); diff --git a/web/tests/composebox_typeahead.test.js b/web/tests/composebox_typeahead.test.js index 68c311ee63..7f58063583 100644 --- a/web/tests/composebox_typeahead.test.js +++ b/web/tests/composebox_typeahead.test.js @@ -700,8 +700,7 @@ function sorted_names_from(subs) { test("initialize", ({override, override_rewire, mock_template}) => { mock_stream_header_colorblock(); mock_banners(); - override_rewire(compose_recipient, "update_on_recipient_change", noop); - override_rewire(stream_bar, "decorate", noop); + override_rewire(compose_recipient, "on_compose_select_recipient_update", noop); let pill_items = []; let cleared = false;