compose: Add banner for unmuting the muted topic a message is sent to.

We add a new banner informing the user if and when they send a message
to a muted topic / stream. It also has a button to unmute the topic.

Fixes: #24246.
This commit is contained in:
N-Shar-ma 2023-04-13 22:10:17 +05:30 committed by Tim Abbott
parent ff8d209807
commit 9f2ef69c71
9 changed files with 111 additions and 16 deletions

View File

@ -39,6 +39,7 @@ import * as transmit from "./transmit";
import * as ui_report from "./ui_report";
import * as upload from "./upload";
import {user_settings} from "./user_settings";
import * as user_topics from "./user_topics";
import * as util from "./util";
import * as zcommand from "./zcommand";
@ -515,6 +516,28 @@ export function initialize() {
},
);
$("#compose_banners").on(
"click",
`.${CSS.escape(
compose_banner.CLASSNAMES.unmute_topic_notification,
)} .compose_banner_action_button`,
(event) => {
event.preventDefault();
const $target = $(event.target).parents(".compose_banner");
const stream_id = Number.parseInt($target.attr("data-stream-id"), 10);
const topic_name = $target.attr("data-topic-name");
user_topics.set_user_topic_visibility_policy(
stream_id,
topic_name,
user_topics.all_visibility_policies.UNMUTED,
false,
true,
);
},
);
$("#compose_banners").on(
"click",
`.${CSS.escape(

View File

@ -17,9 +17,15 @@ const MESSAGE_SENT_CLASSNAMES = {
narrow_to_recipient: "narrow_to_recipient",
scheduled_message_banner: "scheduled_message_banner",
};
// Technically, unmute_topic_notification is a message sent banner, but
// it has distinct behavior / look - it has an associated action button,
// does not disappear on scroll - so we don't include it here, as it needs
// to be handled separately.
export const CLASSNAMES = {
...MESSAGE_SENT_CLASSNAMES,
// unmute topic notifications are styled like warnings but have distinct behaviour
unmute_topic_notification: "unmute_topic_notification warning-style",
// warnings
topic_resolved: "topic_resolved",
recipient_not_subscribed: "recipient_not_subscribed",
@ -43,10 +49,13 @@ export const CLASSNAMES = {
user_not_subscribed: "user_not_subscribed",
};
export function clear_message_sent_banners(): void {
export function clear_message_sent_banners(include_unmute_banner = true): void {
for (const classname of Object.values(MESSAGE_SENT_CLASSNAMES)) {
$(`#compose_banners .${CSS.escape(classname)}`).remove();
}
if (include_unmute_banner) {
clear_unmute_topic_notifications();
}
scroll_to_message_banner_message_id = null;
}
@ -65,6 +74,10 @@ export function clear_warnings(): void {
$(`#compose_banners .${CSS.escape(WARNING)}`).remove();
}
export function clear_unmute_topic_notifications(): void {
$(`#compose_banners .${CLASSNAMES.unmute_topic_notification.replaceAll(" ", ".")}`).remove();
}
export function clear_all(): void {
$(`#compose_banners`).empty();
}

View File

@ -198,7 +198,7 @@ export function scroll_finished() {
compose_banner.scroll_to_message_banner_message_id,
);
if ($message_row.length > 0 && !message_viewport.is_message_below_viewport($message_row)) {
compose_banner.clear_message_sent_banners();
compose_banner.clear_message_sent_banners(false);
}
}

View File

@ -1,6 +1,7 @@
import $ from "jquery";
import render_message_sent_banner from "../templates/compose_banner/message_sent_banner.hbs";
import render_unmute_topic_banner from "../templates/compose_banner/unmute_topic_banner.hbs";
import * as alert_words from "./alert_words";
import * as blueslip from "./blueslip";
@ -166,6 +167,21 @@ export function is_window_focused() {
return window_focused;
}
function notify_unmute(muted_narrow, stream_id, topic_name) {
const $unmute_notification = $(
render_unmute_topic_banner({
muted_narrow,
stream_id,
topic_name,
classname: compose_banner.CLASSNAMES.unmute_topic_notification,
banner_type: "",
button_text: $t({defaultMessage: "Unmute topic"}),
}),
);
compose_banner.clear_unmute_topic_notifications();
$("#compose_banners").append($unmute_notification);
}
export function notify_above_composebox(
banner_text,
classname,
@ -182,7 +198,9 @@ export function notify_above_composebox(
link_text,
}),
);
compose_banner.clear_message_sent_banners();
// We pass in include_unmute_banner as false because we don't want to
// clear any unmute_banner associated with this same message.
compose_banner.clear_message_sent_banners(false);
$("#compose_banners").append($notification);
}
@ -559,6 +577,20 @@ function get_message_header(message) {
);
}
export function get_muted_narrow(message) {
if (
message.type === "stream" &&
stream_data.is_muted(message.stream_id) &&
!user_topics.is_topic_unmuted(message.stream_id, message.topic)
) {
return "stream";
}
if (message.type === "stream" && user_topics.is_topic_muted(message.stream_id, message.topic)) {
return "topic";
}
return undefined;
}
export function get_local_notify_mix_reason(message) {
const $row = message_lists.current.get_row(message.id);
if ($row.length > 0) {
@ -567,14 +599,6 @@ export function get_local_notify_mix_reason(message) {
return undefined;
}
if (message.type === "stream" && user_topics.is_topic_muted(message.stream_id, message.topic)) {
return $t({defaultMessage: "Sent! Your message was sent to a topic you have muted."});
}
if (message.type === "stream" && stream_data.is_muted(message.stream_id)) {
return $t({defaultMessage: "Sent! Your message was sent to a stream you have muted."});
}
// offscreen because it is outside narrow
// we can only look for these on non-search (can_apply_locally) messages
// see also: exports.notify_messages_outside_current_search
@ -619,6 +643,15 @@ export function notify_local_mixes(messages, need_user_to_scroll) {
continue;
}
const muted_narrow = get_muted_narrow(message);
if (muted_narrow) {
notify_unmute(muted_narrow, message.stream_id, message.topic);
// We don't `continue` after showing the unmute banner, allowing multiple
// banners (at max 2 including the unmute banner) to be shown at once,
// as it's common for the unmute case to occur simultaneously with
// another banner's case, like sending a message to another narrow.
}
let banner_text = get_local_notify_mix_reason(message);
const link_msg_id = message.id;
@ -728,7 +761,7 @@ export function register_click_handlers() {
const message_id = $(e.currentTarget).data("message-id");
message_lists.current.select_id(message_id);
navigate.scroll_to_selected();
compose_banner.clear_message_sent_banners();
compose_banner.clear_message_sent_banners(false);
e.stopPropagation();
e.preventDefault();
},

View File

@ -2,6 +2,7 @@ import render_topic_muted from "../templates/topic_muted.hbs";
import * as blueslip from "./blueslip";
import * as channel from "./channel";
import * as compose_banner from "./compose_banner";
import * as feedback_widget from "./feedback_widget";
import {FoldDict} from "./fold_dict";
import {$t} from "./i18n";
@ -75,7 +76,13 @@ export function get_user_topics_for_visibility_policy(visibility_policy) {
return topics;
}
export function set_user_topic_visibility_policy(stream_id, topic, visibility_policy, from_hotkey) {
export function set_user_topic_visibility_policy(
stream_id,
topic,
visibility_policy,
from_hotkey,
from_banner,
) {
const data = {
stream_id,
topic,
@ -90,6 +97,10 @@ export function set_user_topic_visibility_policy(stream_id, topic, visibility_po
feedback_widget.dismiss();
return;
}
if (from_banner) {
compose_banner.clear_unmute_topic_notifications();
return;
}
if (!from_hotkey) {
return;
}

View File

@ -323,7 +323,11 @@
}
}
&.warning {
/* warning and warning-style classes have the same CSS; this is since
the warning class has some associated javascript which we do not want
for some of the banners, for which we use the warning-style class. */
&.warning,
&.warning-style {
background-color: hsl(50deg 75% 92%);
border-color: hsl(38deg 44% 27% / 40%);
color: hsl(38deg 44% 27%);

View File

@ -226,7 +226,8 @@
}
}
&.warning {
&.warning,
&.warning-style {
background-color: hsl(53deg 100% 11%);
border-color: hsl(38deg 44% 60% / 40%);
color: hsl(50deg 45% 80%);

View File

@ -0,0 +1,10 @@
{{#> compose_banner }}
<p>
{{#if (eq muted_narrow "stream")}}
{{#tr}}Your message was sent to a stream you have muted.{{/tr}}
{{else if (eq muted_narrow "topic")}}
{{#tr}}Your message was sent to a topic you have muted.{{/tr}}
{{/if}}
{{#tr}}You will not receive notifications about new messages.{{/tr}}
</p>
{{/compose_banner}}

View File

@ -8,7 +8,7 @@ exports.mock_banners = () => {
// zjquery doesn't support `remove`, which is used when clearing the compose box.
// TODO: improve how we test this so that we don't have to mock things like this.
for (const classname of Object.values(compose_banner.CLASSNAMES)) {
$(`#compose_banners .${classname}`).remove = () => {};
$(`#compose_banners .${classname.replaceAll(" ", ".")}`).remove = () => {};
}
$("#compose_banners .warning").remove = () => {};
$("#compose_banners .error").remove = () => {};