message_list: Hide group DMs where everyone is muted.

This commit hides group DMs where all recipients are muted from
different views.

Fixes: zulip#34886.

Co-authored-by: Aman Agrawal <amanagr@zulip.com>
This commit is contained in:
Pratik Chanda
2025-07-08 21:46:34 +05:30
committed by Tim Abbott
parent 6b6a02f932
commit 81ca9e1b36
4 changed files with 36 additions and 33 deletions

View File

@@ -1073,8 +1073,12 @@ export class Filter {
return this.has_operator("search");
}
is_non_group_direct_message(): boolean {
return this.has_operator("dm") && this.operands("dm")[0]!.split(",").length === 1;
is_search_for_specific_group_or_user(): boolean {
return (
this.has_operator("dm") ||
this.has_operator("dm-including") ||
this.has_operator("sender")
);
}
contains_no_partial_conversations(): boolean {

View File

@@ -237,8 +237,10 @@ export class MessageListData {
}
messages_filtered_for_user_mutes(messages: Message[]): Message[] {
if (this.filter.is_non_group_direct_message()) {
// We are in a 1:1 direct message narrow, so do not do any filtering.
// Don't exclude messages sent by muted users if we're
// searching for a specific group or user, since the user
// presumably wants to see those messages.
if (this.filter.is_search_for_specific_group_or_user()) {
return [...messages];
}
@@ -247,16 +249,9 @@ export class MessageListData {
return true;
}
const recipients = util.extract_pm_recipients(message.to_user_ids);
if (recipients.length > 1) {
// Direct message group message
return true;
}
const recipient_id = Number.parseInt(util.the(recipients), 10);
return (
!muted_users.is_user_muted(recipient_id) &&
!muted_users.is_user_muted(message.sender_id)
);
const recipient_ids = recipients.map((recipient) => Number.parseInt(recipient, 10));
const unmuted_recipient_ids = muted_users.filter_muted_user_ids(recipient_ids);
return unmuted_recipient_ids.length > 0;
});
}

View File

@@ -403,7 +403,7 @@ test("basics", () => {
terms = [{operator: "dm", operand: "joe@example.com"}];
filter = new Filter(terms);
assert.ok(filter.is_non_group_direct_message());
assert.ok(filter.is_search_for_specific_group_or_user());
assert.ok(filter.contains_only_private_messages());
assert.ok(filter.can_mark_messages_read());
assert.ok(filter.contains_no_partial_conversations());
@@ -421,7 +421,7 @@ test("basics", () => {
{operator: "near", operand: "17"},
];
filter = new Filter(terms);
assert.ok(filter.is_non_group_direct_message());
assert.ok(filter.is_search_for_specific_group_or_user());
assert.ok(filter.contains_only_private_messages());
assert.ok(!filter.can_mark_messages_read());
assert.ok(filter.contains_no_partial_conversations());
@@ -436,7 +436,7 @@ test("basics", () => {
terms = [{operator: "dm", operand: "joe@example.com,jack@example.com"}];
filter = new Filter(terms);
assert.ok(!filter.is_non_group_direct_message());
assert.ok(filter.is_search_for_specific_group_or_user());
assert.ok(filter.contains_only_private_messages());
assert.ok(filter.can_mark_messages_read());
assert.ok(filter.contains_no_partial_conversations());
@@ -474,7 +474,7 @@ test("basics", () => {
terms = [{operator: "dm-including", operand: "joe@example.com"}];
filter = new Filter(terms);
assert.ok(!filter.is_non_group_direct_message());
assert.ok(filter.is_search_for_specific_group_or_user());
assert.ok(filter.contains_only_private_messages());
assert.ok(!filter.has_operator("search"));
assert.ok(!filter.can_mark_messages_read());

View File

@@ -140,19 +140,21 @@ run_test("muting", () => {
// mentions override muting
{id: 3, type: "stream", stream_id: 1, topic: "muted", mentioned: true},
// 10 = muted user, 9 = non-muted user, 11 = you
// 10,12 = muted users, 9 = non-muted user, 11 = you
// muted to group direct message
{id: 4, type: "private", to_user_ids: "9,10,11", sender_id: 10},
{id: 4, type: "private", to_user_ids: "9,10", sender_id: 10},
// non-muted to group direct message
{id: 5, type: "private", to_user_ids: "9,10,11", sender_id: 9},
{id: 5, type: "private", to_user_ids: "9,10", sender_id: 9},
// muted to 1:1 direct message
{id: 6, type: "private", to_user_ids: "11", sender_id: 10},
{id: 6, type: "private", to_user_ids: "10", sender_id: 10},
// non-muted to 1:1 direct message
{id: 7, type: "private", to_user_ids: "11", sender_id: 9},
{id: 7, type: "private", to_user_ids: "9", sender_id: 9},
// 1:1 direct message to muted
{id: 8, type: "private", to_user_ids: "10", sender_id: 11},
// 1:1 direct message to non-muted
{id: 9, type: "private", to_user_ids: "9", sender_id: 11},
// group direct message with everyone muted
{id: 10, type: "private", to_user_ids: "10,12", sender_id: 10},
];
user_topics.update_user_topics(
@@ -162,6 +164,7 @@ run_test("muting", () => {
user_topics.all_visibility_policies.MUTED,
);
muted_users.add_muted_user(10);
muted_users.add_muted_user(12);
// `messages_filtered_for_topic_mutes` should skip filtering
// messages if `excludes_muted_topics` is false.
@@ -180,12 +183,13 @@ run_test("muting", () => {
{id: 3, type: "stream", stream_id: 1, topic: "muted", mentioned: true}, // mentions override muting
// `messages_filtered_for_topic_mutes` does not affect direct messages
{id: 4, type: "private", to_user_ids: "9,10,11", sender_id: 10},
{id: 5, type: "private", to_user_ids: "9,10,11", sender_id: 9},
{id: 6, type: "private", to_user_ids: "11", sender_id: 10},
{id: 7, type: "private", to_user_ids: "11", sender_id: 9},
{id: 4, type: "private", to_user_ids: "9,10", sender_id: 10},
{id: 5, type: "private", to_user_ids: "9,10", sender_id: 9},
{id: 6, type: "private", to_user_ids: "10", sender_id: 10},
{id: 7, type: "private", to_user_ids: "9", sender_id: 9},
{id: 8, type: "private", to_user_ids: "10", sender_id: 11},
{id: 9, type: "private", to_user_ids: "9", sender_id: 11},
{id: 10, type: "private", to_user_ids: "10,12", sender_id: 10},
]);
const res_user = mld.messages_filtered_for_user_mutes(msgs);
@@ -195,11 +199,11 @@ run_test("muting", () => {
{id: 2, type: "stream", stream_id: 1, topic: "whatever"},
{id: 3, type: "stream", stream_id: 1, topic: "muted", mentioned: true},
// muted to group direct message
{id: 4, type: "private", to_user_ids: "9,10,11", sender_id: 10},
{id: 4, type: "private", to_user_ids: "9,10", sender_id: 10},
// non-muted to group direct message
{id: 5, type: "private", to_user_ids: "9,10,11", sender_id: 9},
{id: 5, type: "private", to_user_ids: "9,10", sender_id: 9},
// non-muted to 1:1 direct message
{id: 7, type: "private", to_user_ids: "11", sender_id: 9},
{id: 7, type: "private", to_user_ids: "9", sender_id: 9},
// 1:1 direct message to non-muted
{id: 9, type: "private", to_user_ids: "9", sender_id: 11},
]);
@@ -210,9 +214,9 @@ run_test("muting", () => {
assert.deepEqual(filtered_messages, [
{id: 2, type: "stream", stream_id: 1, topic: "whatever"},
{id: 3, type: "stream", stream_id: 1, topic: "muted", mentioned: true},
{id: 4, type: "private", to_user_ids: "9,10,11", sender_id: 10},
{id: 5, type: "private", to_user_ids: "9,10,11", sender_id: 9},
{id: 7, type: "private", to_user_ids: "11", sender_id: 9},
{id: 4, type: "private", to_user_ids: "9,10", sender_id: 10},
{id: 5, type: "private", to_user_ids: "9,10", sender_id: 9},
{id: 7, type: "private", to_user_ids: "9", sender_id: 9},
{id: 9, type: "private", to_user_ids: "9", sender_id: 11},
]);