mirror of https://github.com/zulip/zulip.git
compose: Show banner to explain non interleaved view messages fading.
In a non interleaved view when composing a message to another conversation we fade messages which the user is not replying to, to reduce the chance they send a message to a recipient they didn't intend to. Also, it reduces the visual/cognitive processing required to figure out where their message is going to go. But, it's not necessarily clear to users that what the fading means, so this commit adds a one-time compose banner to explain what's going on the first time this comes up. Fixes part of #29076.
This commit is contained in:
parent
818bad300e
commit
35380b095f
|
@ -407,6 +407,7 @@ export function cancel(): void {
|
|||
hide_box();
|
||||
clear_box();
|
||||
compose_banner.clear_message_sent_banners();
|
||||
compose_banner.clear_non_interleaved_view_messages_fading_banner();
|
||||
call_hooks(compose_cancel_hooks);
|
||||
compose_state.set_message_type(undefined);
|
||||
compose_pm_pill.clear();
|
||||
|
|
|
@ -34,6 +34,7 @@ const MESSAGE_SENT_CLASSNAMES = {
|
|||
|
||||
export const CLASSNAMES = {
|
||||
...MESSAGE_SENT_CLASSNAMES,
|
||||
non_interleaved_view_messages_fading: "non_interleaved_view_messages_fading",
|
||||
// unmute topic notifications are styled like warnings but have distinct behaviour
|
||||
unmute_topic_notification: "unmute_topic_notification warning-style",
|
||||
// warnings
|
||||
|
@ -147,6 +148,10 @@ export function clear_search_view_banner(): void {
|
|||
$(`#compose_banners .${CSS.escape(CLASSNAMES.search_view)}`).remove();
|
||||
}
|
||||
|
||||
export function clear_non_interleaved_view_messages_fading_banner(): void {
|
||||
$(`#compose_banners .${CSS.escape(CLASSNAMES.non_interleaved_view_messages_fading)}`).remove();
|
||||
}
|
||||
|
||||
export function clear_all(): void {
|
||||
scroll_util.get_content_element($(`#compose_banners`)).empty();
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import $ from "jquery";
|
|||
import assert from "minimalistic-assert";
|
||||
|
||||
import render_automatic_new_visibility_policy_banner from "../templates/compose_banner/automatic_new_visibility_policy_banner.hbs";
|
||||
import render_compose_banner from "../templates/compose_banner/compose_banner.hbs";
|
||||
import render_jump_to_sent_message_conversation_banner from "../templates/compose_banner/jump_to_sent_message_conversation_banner.hbs";
|
||||
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";
|
||||
|
@ -304,6 +305,35 @@ export function notify_messages_outside_current_search(messages: Message[]): voi
|
|||
}
|
||||
}
|
||||
|
||||
export function maybe_show_one_time_non_interleaved_view_messages_fading_banner(): void {
|
||||
// Remove message fading banner if exists. Helps in live-updating banner.
|
||||
compose_banner.clear_non_interleaved_view_messages_fading_banner();
|
||||
|
||||
if (!onboarding_steps.ONE_TIME_NOTICES_TO_DISPLAY.has("non_interleaved_view_messages_fading")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait to display the banner the first time until there's actually fading.
|
||||
const faded_messages_exist = $(".focused-message-list .recipient_row").hasClass("message-fade");
|
||||
if (!faded_messages_exist) {
|
||||
return;
|
||||
}
|
||||
|
||||
const context = {
|
||||
banner_type: compose_banner.INFO,
|
||||
classname: compose_banner.CLASSNAMES.non_interleaved_view_messages_fading,
|
||||
banner_text: $t({
|
||||
defaultMessage:
|
||||
"Messages in your view are faded to remind you that you are viewing a different conversation from the one you are composing to.",
|
||||
}),
|
||||
button_text: $t({defaultMessage: "Got it"}),
|
||||
hide_close_button: true,
|
||||
};
|
||||
const new_row_html = render_compose_banner(context);
|
||||
|
||||
compose_banner.append_compose_banner_to_banner_list($(new_row_html), $("#compose_banners"));
|
||||
}
|
||||
|
||||
export function reify_message_id(opts: {old_id: number; new_id: number}): void {
|
||||
const old_id = opts.old_id;
|
||||
const new_id = opts.new_id;
|
||||
|
|
|
@ -8,6 +8,7 @@ import * as compose_actions from "./compose_actions";
|
|||
import * as compose_banner from "./compose_banner";
|
||||
import * as compose_call from "./compose_call";
|
||||
import * as compose_call_ui from "./compose_call_ui";
|
||||
import * as compose_notifications from "./compose_notifications";
|
||||
import * as compose_recipient from "./compose_recipient";
|
||||
import * as compose_send_menu_popover from "./compose_send_menu_popover";
|
||||
import * as compose_state from "./compose_state";
|
||||
|
@ -317,6 +318,19 @@ export function initialize() {
|
|||
},
|
||||
);
|
||||
|
||||
const non_interleaved_view_messages_fading_banner_selector = `.${CSS.escape(compose_banner.CLASSNAMES.non_interleaved_view_messages_fading)}`;
|
||||
$("body").on(
|
||||
"click",
|
||||
`${non_interleaved_view_messages_fading_banner_selector} .main-view-banner-action-button`,
|
||||
(event) => {
|
||||
event.preventDefault();
|
||||
$(event.target)
|
||||
.parents(`${non_interleaved_view_messages_fading_banner_selector}`)
|
||||
.remove();
|
||||
onboarding_steps.post_onboarding_step_as_read("non_interleaved_view_messages_fading");
|
||||
},
|
||||
);
|
||||
|
||||
for (const classname of Object.values(compose_banner.CLASSNAMES)) {
|
||||
const classname_selector = `.${CSS.escape(classname)}`;
|
||||
$("body").on("click", `${classname_selector} .main-view-banner-close-button`, (event) => {
|
||||
|
@ -478,6 +492,7 @@ export function initialize() {
|
|||
|
||||
$("textarea#compose-textarea").on("focus", () => {
|
||||
compose_recipient.update_placeholder_text();
|
||||
compose_notifications.maybe_show_one_time_non_interleaved_view_messages_fading_banner();
|
||||
});
|
||||
|
||||
$("#compose_recipient_box").on("click", "#recipient_box_clear_topic_button", () => {
|
||||
|
|
|
@ -796,6 +796,11 @@ test_ui("on_events", ({override, override_rewire}) => {
|
|||
};
|
||||
|
||||
override_rewire(compose_recipient, "update_placeholder_text", noop);
|
||||
override(
|
||||
compose_notifications,
|
||||
"maybe_show_one_time_non_interleaved_view_messages_fading_banner",
|
||||
noop,
|
||||
);
|
||||
|
||||
handler(event);
|
||||
|
||||
|
|
|
@ -35,6 +35,9 @@ ONE_TIME_NOTICES: list[OneTimeNotice] = [
|
|||
OneTimeNotice(
|
||||
name="jump_to_conversation_banner",
|
||||
),
|
||||
OneTimeNotice(
|
||||
name="non_interleaved_view_messages_fading",
|
||||
),
|
||||
]
|
||||
|
||||
# We may introduce onboarding step of types other than 'one time notice'
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 5.0.5 on 2024-04-23 07:16
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("zerver", "0554_imageattachment"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="onboardingstep",
|
||||
name="onboarding_step",
|
||||
field=models.CharField(max_length=40),
|
||||
),
|
||||
]
|
|
@ -7,7 +7,7 @@ from zerver.models.users import UserProfile
|
|||
|
||||
class OnboardingStep(models.Model):
|
||||
user = models.ForeignKey(UserProfile, on_delete=CASCADE)
|
||||
onboarding_step = models.CharField(max_length=30)
|
||||
onboarding_step = models.CharField(max_length=40)
|
||||
timestamp = models.DateTimeField(default=timezone_now)
|
||||
|
||||
class Meta:
|
||||
|
|
|
@ -25,10 +25,11 @@ class TestGetNextOnboardingSteps(ZulipTestCase):
|
|||
|
||||
do_mark_onboarding_step_as_read(self.user, "intro_inbox_view_modal")
|
||||
onboarding_steps = get_next_onboarding_steps(self.user)
|
||||
self.assert_length(onboarding_steps, 3)
|
||||
self.assert_length(onboarding_steps, 4)
|
||||
self.assertEqual(onboarding_steps[0]["name"], "intro_recent_view_modal")
|
||||
self.assertEqual(onboarding_steps[1]["name"], "first_stream_created_banner")
|
||||
self.assertEqual(onboarding_steps[2]["name"], "jump_to_conversation_banner")
|
||||
self.assertEqual(onboarding_steps[3]["name"], "non_interleaved_view_messages_fading")
|
||||
|
||||
with self.settings(TUTORIAL_ENABLED=False):
|
||||
onboarding_steps = get_next_onboarding_steps(self.user)
|
||||
|
|
Loading…
Reference in New Issue