compose: Handle fading with general chat.

This commit also attempts to fix a bug, present in main, where a
draft restored directly to the compose box loses its original topic
upon switching to other topic narrows.

Co-authored-by: Prakhar Pratyush <prakhar@zulip.com>
This commit is contained in:
Karl Stolley
2025-02-11 14:03:33 -05:00
committed by Tim Abbott
parent 4723492e12
commit a3a43c2f8b
10 changed files with 74 additions and 13 deletions

View File

@@ -26,6 +26,7 @@ import * as reload_state from "./reload_state.ts";
import * as resize from "./resize.ts"; import * as resize from "./resize.ts";
import * as saved_snippets_ui from "./saved_snippets_ui.ts"; import * as saved_snippets_ui from "./saved_snippets_ui.ts";
import * as spectators from "./spectators.ts"; import * as spectators from "./spectators.ts";
import {realm} from "./state_data.ts";
import * as stream_data from "./stream_data.ts"; import * as stream_data from "./stream_data.ts";
// Opts sent to `compose_actions.start`. // Opts sent to `compose_actions.start`.
@@ -516,7 +517,8 @@ export let on_topic_narrow = (): void => {
} }
if ( if (
(compose_state.topic() && compose_state.has_novel_message_content()) || ((compose_state.topic() || !realm.realm_mandatory_topics) &&
compose_state.has_message_content()) ||
compose_state.is_recipient_edited_manually() compose_state.is_recipient_edited_manually()
) { ) {
// If the user has written something to a different topic or edited it, // If the user has written something to a different topic or edited it,

View File

@@ -105,7 +105,7 @@ function fade_messages(): void {
); );
} }
function do_update_all(): void { export function do_update_all(): void {
if (compose_fade_helper.want_normal_display()) { if (compose_fade_helper.want_normal_display()) {
if (!normal_display) { if (!normal_display) {
display_messages_normally(); display_messages_normally();

View File

@@ -1,4 +1,7 @@
import $ from "jquery";
import type {Message} from "./message_store.ts"; import type {Message} from "./message_store.ts";
import {realm} from "./state_data.ts";
import * as sub_store from "./sub_store.ts"; import * as sub_store from "./sub_store.ts";
import type {Recipient} from "./util.ts"; import type {Recipient} from "./util.ts";
import * as util from "./util.ts"; import * as util from "./util.ts";
@@ -31,11 +34,14 @@ export function want_normal_display(): boolean {
return true; return true;
} }
// This is kind of debatable. If the topic is empty, it could be that // If the topic is empty, we want a normal display in the following cases:
// the user simply hasn't started typing it yet, but disabling fading here // * realm requires topic
// means the feature doesn't help realms where topics aren't mandatory // * realm allows empty topic but the focus is in topic input box,
// (which is most realms as of this writing). // means user is still configuring topic.
if (focused_recipient.topic === "") { if (
focused_recipient.topic === "" &&
(realm.realm_mandatory_topics || $("input#stream_message_recipient_topic").is(":focus"))
) {
return true; return true;
} }
} }

View File

@@ -10,6 +10,7 @@ import * as compose_actions from "./compose_actions.ts";
import * as compose_banner from "./compose_banner.ts"; import * as compose_banner from "./compose_banner.ts";
import * as compose_call from "./compose_call.ts"; import * as compose_call from "./compose_call.ts";
import * as compose_call_ui from "./compose_call_ui.ts"; import * as compose_call_ui from "./compose_call_ui.ts";
import * as compose_fade from "./compose_fade.ts";
import * as compose_notifications from "./compose_notifications.ts"; import * as compose_notifications from "./compose_notifications.ts";
import * as compose_recipient from "./compose_recipient.ts"; import * as compose_recipient from "./compose_recipient.ts";
import * as compose_send_menu_popover from "./compose_send_menu_popover.js"; import * as compose_send_menu_popover from "./compose_send_menu_popover.js";
@@ -583,6 +584,7 @@ export function initialize() {
$("textarea#compose-textarea").on("focus", () => { $("textarea#compose-textarea").on("focus", () => {
compose_recipient.update_compose_area_placeholder_text(); compose_recipient.update_compose_area_placeholder_text();
compose_fade.do_update_all();
if (narrow_state.narrowed_by_reply()) { if (narrow_state.narrowed_by_reply()) {
compose_notifications.maybe_show_one_time_non_interleaved_view_messages_fading_banner(); compose_notifications.maybe_show_one_time_non_interleaved_view_messages_fading_banner();
} else { } else {

View File

@@ -2,6 +2,7 @@ import $ from "jquery";
import * as compose_pm_pill from "./compose_pm_pill.ts"; import * as compose_pm_pill from "./compose_pm_pill.ts";
import * as people from "./people.ts"; import * as people from "./people.ts";
import {realm} from "./state_data.ts";
import * as sub_store from "./sub_store.ts"; import * as sub_store from "./sub_store.ts";
let message_type: "stream" | "private" | undefined; let message_type: "stream" | "private" | undefined;
@@ -231,7 +232,8 @@ export function has_savable_message_content(): boolean {
export function has_full_recipient(): boolean { export function has_full_recipient(): boolean {
if (message_type === "stream") { if (message_type === "stream") {
return stream_id() !== undefined && topic() !== ""; const has_topic = topic() !== "" || !realm.realm_mandatory_topics;
return stream_id() !== undefined && has_topic;
} }
return private_message_recipient() !== ""; return private_message_recipient() !== "";
} }

View File

@@ -1378,7 +1378,7 @@ export function to_compose_target(): void {
// grey-out the message view instead of narrowing to an empty view. // grey-out the message view instead of narrowing to an empty view.
const terms = [{operator: "channel", operand: stream_id.toString()}]; const terms = [{operator: "channel", operand: stream_id.toString()}];
const topic = compose_state.topic(); const topic = compose_state.topic();
if (topic !== "") { if (topic !== "" || !realm.realm_mandatory_topics) {
terms.push({operator: "topic", operand: topic}); terms.push({operator: "topic", operand: topic});
} }
show(terms, opts); show(terms, opts);

View File

@@ -823,6 +823,7 @@ test_ui("on_events", ({override, override_rewire}) => {
fake_compose_box.show_message_preview(); fake_compose_box.show_message_preview();
override_rewire(compose_recipient, "update_compose_area_placeholder_text", noop); override_rewire(compose_recipient, "update_compose_area_placeholder_text", noop);
override(compose_fade, "do_update_all", noop);
override(narrow_state, "narrowed_by_reply", () => true); override(narrow_state, "narrowed_by_reply", () => true);
override( override(
compose_notifications, compose_notifications,

View File

@@ -12,6 +12,15 @@ mock_jquery((selector) => {
val() { val() {
return "lunch"; return "lunch";
}, },
is(arg) {
switch (arg) {
case ":focus":
return true;
/* istanbul ignore next */
default:
throw new Error(`Unknown arg ${arg}`);
}
},
}; };
/* istanbul ignore next */ /* istanbul ignore next */
default: default:
@@ -25,6 +34,10 @@ const people = zrequire("people");
const compose_fade = zrequire("compose_fade"); const compose_fade = zrequire("compose_fade");
const compose_fade_helper = zrequire("compose_fade_helper"); const compose_fade_helper = zrequire("compose_fade_helper");
const compose_state = zrequire("compose_state"); const compose_state = zrequire("compose_state");
const {set_realm} = zrequire("state_data");
const realm = {};
set_realm(realm);
const me = { const me = {
email: "me@example.com", email: "me@example.com",
@@ -77,7 +90,7 @@ run_test("set_focused_recipient", () => {
assert.ok(compose_fade_helper.should_fade_message(bad_msg)); assert.ok(compose_fade_helper.should_fade_message(bad_msg));
}); });
run_test("want_normal_display", () => { run_test("want_normal_display", ({override}) => {
const stream_id = 110; const stream_id = 110;
const sub = { const sub = {
stream_id, stream_id,
@@ -100,9 +113,16 @@ run_test("want_normal_display", () => {
assert.ok(compose_fade_helper.want_normal_display()); assert.ok(compose_fade_helper.want_normal_display());
// Focused recipient is a valid stream with no topic set // Focused recipient is a valid stream with no topic set
// when topics are mandatory
override(realm, "realm_mandatory_topics", true);
stream_data.add_sub(sub); stream_data.add_sub(sub);
assert.ok(compose_fade_helper.want_normal_display()); assert.ok(compose_fade_helper.want_normal_display());
// Focused recipient is a valid stream with no topic set
// when topics are not mandatory. Focused to input box.
override(realm, "realm_mandatory_topics", false);
assert.ok(compose_fade_helper.want_normal_display());
// If we're focused to a topic, then we do want to fade. // If we're focused to a topic, then we do want to fade.
compose_fade_helper.set_focused_recipient({ compose_fade_helper.set_focused_recipient({
type: "stream", type: "stream",

View File

@@ -10,6 +10,10 @@ const compose_pm_pill = mock_esm("../src/compose_pm_pill");
const compose_state = zrequire("compose_state"); const compose_state = zrequire("compose_state");
const stream_data = zrequire("stream_data"); const stream_data = zrequire("stream_data");
const {set_realm} = zrequire("state_data");
const realm = {};
set_realm(realm);
run_test("private_message_recipient", ({override}) => { run_test("private_message_recipient", ({override}) => {
let emails; let emails;

View File

@@ -769,7 +769,7 @@ run_test("narrow_to_compose_target errors", ({disallow_rewire}) => {
message_view.to_compose_target(); message_view.to_compose_target();
}); });
run_test("narrow_to_compose_target streams", ({override_rewire}) => { run_test("narrow_to_compose_target streams", ({override, override_rewire}) => {
const args = {called: false}; const args = {called: false};
override_rewire(message_view, "show", (terms, opts) => { override_rewire(message_view, "show", (terms, opts) => {
args.terms = terms; args.terms = terms;
@@ -803,19 +803,43 @@ run_test("narrow_to_compose_target streams", ({override_rewire}) => {
{operator: "topic", operand: "four"}, {operator: "topic", operand: "four"},
]); ]);
// Test with blank topic // Test with blank topic, with realm_mandatory_topics
override(realm, "realm_mandatory_topics", true);
compose_state.topic(""); compose_state.topic("");
args.called = false; args.called = false;
message_view.to_compose_target(); message_view.to_compose_target();
assert.equal(args.called, true); assert.equal(args.called, true);
assert.deepEqual(args.terms, [{operator: "channel", operand: rome_id.toString()}]); assert.deepEqual(args.terms, [{operator: "channel", operand: rome_id.toString()}]);
// Test with no topic // Test with blank topic, without realm_mandatory_topics
override(realm, "realm_mandatory_topics", false);
compose_state.topic("");
args.called = false;
message_view.to_compose_target();
assert.equal(args.called, true);
assert.deepEqual(args.terms, [
{operator: "channel", operand: rome_id.toString()},
{operator: "topic", operand: ""},
]);
// Test with no topic, with realm mandatory topics
override(realm, "realm_mandatory_topics", true);
compose_state.topic(undefined); compose_state.topic(undefined);
args.called = false; args.called = false;
message_view.to_compose_target(); message_view.to_compose_target();
assert.equal(args.called, true); assert.equal(args.called, true);
assert.deepEqual(args.terms, [{operator: "channel", operand: rome_id.toString()}]); assert.deepEqual(args.terms, [{operator: "channel", operand: rome_id.toString()}]);
// Test with no topic, without realm mandatory topics
override(realm, "realm_mandatory_topics", false);
compose_state.topic(undefined);
args.called = false;
message_view.to_compose_target();
assert.equal(args.called, true);
assert.deepEqual(args.terms, [
{operator: "channel", operand: rome_id.toString()},
{operator: "topic", operand: ""},
]);
}); });
run_test("narrow_to_compose_target direct messages", ({override, override_rewire}) => { run_test("narrow_to_compose_target direct messages", ({override, override_rewire}) => {