mirror of https://github.com/zulip/zulip.git
typeahead: Show @-topic typeahead whenever it might be possible to use.
Earlier, when a topic had less than 15 participants, the @-topic typeahead was not visible. It should be visible irrespective of the 'realm_wildcard_mention_policy' setting when the participant count is not greater than 15. The participant count for a topic can't always be calculated accurately in the client as some of the messages might still be loading. We show @-topic in the typeahead whenever it might be possible to use it. We will give an error later if you aren't allowed to use it. Fixes #27852.
This commit is contained in:
parent
590d43f475
commit
451ddf4c84
|
@ -14,9 +14,12 @@ import * as compose_pm_pill from "./compose_pm_pill";
|
||||||
import * as compose_state from "./compose_state";
|
import * as compose_state from "./compose_state";
|
||||||
import * as compose_ui from "./compose_ui";
|
import * as compose_ui from "./compose_ui";
|
||||||
import {$t} from "./i18n";
|
import {$t} from "./i18n";
|
||||||
|
import * as message_store from "./message_store";
|
||||||
import {page_params} from "./page_params";
|
import {page_params} from "./page_params";
|
||||||
import * as peer_data from "./peer_data";
|
import * as peer_data from "./peer_data";
|
||||||
import * as people from "./people";
|
import * as people from "./people";
|
||||||
|
import * as reactions from "./reactions";
|
||||||
|
import * as recent_senders from "./recent_senders";
|
||||||
import * as settings_config from "./settings_config";
|
import * as settings_config from "./settings_config";
|
||||||
import * as settings_data from "./settings_data";
|
import * as settings_data from "./settings_data";
|
||||||
import * as stream_data from "./stream_data";
|
import * as stream_data from "./stream_data";
|
||||||
|
@ -383,6 +386,55 @@ function is_recipient_large_stream() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function topic_participant_count_more_than_threshold(stream_id, topic) {
|
||||||
|
// Topic participants:
|
||||||
|
// Users who either sent or reacted to the messages in the topic.
|
||||||
|
const participant_ids = new Set();
|
||||||
|
|
||||||
|
const sender_ids = recent_senders.get_topic_recent_senders(stream_id, topic);
|
||||||
|
for (const id of sender_ids) {
|
||||||
|
participant_ids.add(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If senders count is greater than threshold, no need to calculate reactors.
|
||||||
|
if (participant_ids.size > wildcard_mention_threshold) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const sender_id of sender_ids) {
|
||||||
|
const message_ids = recent_senders.get_topic_message_ids_for_sender(
|
||||||
|
stream_id,
|
||||||
|
topic,
|
||||||
|
sender_id,
|
||||||
|
);
|
||||||
|
for (const message_id of message_ids) {
|
||||||
|
const message = message_store.get(message_id);
|
||||||
|
if (message) {
|
||||||
|
const message_reactions = reactions.get_message_reactions(message);
|
||||||
|
const reactor_ids = message_reactions.flatMap((obj) => obj.user_ids);
|
||||||
|
for (const id of reactor_ids) {
|
||||||
|
participant_ids.add(id);
|
||||||
|
}
|
||||||
|
if (participant_ids.size > wildcard_mention_threshold) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_recipient_large_topic() {
|
||||||
|
return (
|
||||||
|
compose_state.stream_id() &&
|
||||||
|
topic_participant_count_more_than_threshold(
|
||||||
|
compose_state.stream_id(),
|
||||||
|
compose_state.topic(),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function wildcard_mention_policy_authorizes_user() {
|
function wildcard_mention_policy_authorizes_user() {
|
||||||
if (
|
if (
|
||||||
page_params.realm_wildcard_mention_policy ===
|
page_params.realm_wildcard_mention_policy ===
|
||||||
|
@ -427,10 +479,14 @@ function wildcard_mention_policy_authorizes_user() {
|
||||||
return !page_params.is_guest;
|
return !page_params.is_guest;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function wildcard_mention_allowed() {
|
export function stream_wildcard_mention_allowed() {
|
||||||
return !is_recipient_large_stream() || wildcard_mention_policy_authorizes_user();
|
return !is_recipient_large_stream() || wildcard_mention_policy_authorizes_user();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function topic_wildcard_mention_allowed() {
|
||||||
|
return !is_recipient_large_topic() || wildcard_mention_policy_authorizes_user();
|
||||||
|
}
|
||||||
|
|
||||||
export function set_wildcard_mention_threshold(value) {
|
export function set_wildcard_mention_threshold(value) {
|
||||||
wildcard_mention_threshold = value;
|
wildcard_mention_threshold = value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -387,13 +387,15 @@ function get_wildcard_string(mention) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function broadcast_mentions() {
|
export function broadcast_mentions() {
|
||||||
if (!compose_validate.wildcard_mention_allowed()) {
|
let wildcard_mention_array = [];
|
||||||
return [];
|
if (compose_state.get_message_type() === "private") {
|
||||||
}
|
wildcard_mention_array = ["all", "everyone"];
|
||||||
const wildcard_mention_array = ["all", "everyone"];
|
} else if (compose_validate.stream_wildcard_mention_allowed()) {
|
||||||
if (compose_state.get_message_type() === "stream") {
|
wildcard_mention_array = ["all", "everyone", "stream", "topic"];
|
||||||
wildcard_mention_array.push("stream", "topic");
|
} else if (compose_validate.topic_wildcard_mention_allowed()) {
|
||||||
|
wildcard_mention_array = ["topic"];
|
||||||
}
|
}
|
||||||
|
|
||||||
return wildcard_mention_array.map((mention, idx) => ({
|
return wildcard_mention_array.map((mention, idx) => ({
|
||||||
special_item_text: `${mention} (${get_wildcard_string(mention)})`,
|
special_item_text: `${mention} (${get_wildcard_string(mention)})`,
|
||||||
email: mention,
|
email: mention,
|
||||||
|
|
|
@ -248,3 +248,11 @@ export function get_pm_recent_senders(user_ids_string) {
|
||||||
pm_senders_info.participants.sort(compare_pm_user_ids_by_recency);
|
pm_senders_info.participants.sort(compare_pm_user_ids_by_recency);
|
||||||
return pm_senders_info;
|
return pm_senders_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function get_topic_message_ids_for_sender(stream_id, topic, sender_id) {
|
||||||
|
const id_tracker = topic_senders?.get(stream_id)?.get(topic)?.get(sender_id);
|
||||||
|
if (id_tracker === undefined) {
|
||||||
|
return new Set();
|
||||||
|
}
|
||||||
|
return id_tracker.ids;
|
||||||
|
}
|
||||||
|
|
|
@ -324,7 +324,7 @@ test_ui("get_invalid_recipient_emails", ({override_rewire}) => {
|
||||||
assert.deepEqual(compose_validate.get_invalid_recipient_emails(), []);
|
assert.deepEqual(compose_validate.get_invalid_recipient_emails(), []);
|
||||||
});
|
});
|
||||||
|
|
||||||
test_ui("test_wildcard_mention_allowed", ({override_rewire}) => {
|
test_ui("test_stream_wildcard_mention_allowed", ({override_rewire}) => {
|
||||||
page_params.user_id = me.user_id;
|
page_params.user_id = me.user_id;
|
||||||
|
|
||||||
// First, check for large streams (>15 subscribers) where the wildcard mention
|
// First, check for large streams (>15 subscribers) where the wildcard mention
|
||||||
|
@ -335,39 +335,39 @@ test_ui("test_wildcard_mention_allowed", ({override_rewire}) => {
|
||||||
settings_config.wildcard_mention_policy_values.by_everyone.code;
|
settings_config.wildcard_mention_policy_values.by_everyone.code;
|
||||||
page_params.is_guest = true;
|
page_params.is_guest = true;
|
||||||
page_params.is_admin = false;
|
page_params.is_admin = false;
|
||||||
assert.ok(compose_validate.wildcard_mention_allowed());
|
assert.ok(compose_validate.stream_wildcard_mention_allowed());
|
||||||
|
|
||||||
page_params.realm_wildcard_mention_policy =
|
page_params.realm_wildcard_mention_policy =
|
||||||
settings_config.wildcard_mention_policy_values.nobody.code;
|
settings_config.wildcard_mention_policy_values.nobody.code;
|
||||||
page_params.is_admin = true;
|
page_params.is_admin = true;
|
||||||
assert.ok(!compose_validate.wildcard_mention_allowed());
|
assert.ok(!compose_validate.stream_wildcard_mention_allowed());
|
||||||
|
|
||||||
page_params.realm_wildcard_mention_policy =
|
page_params.realm_wildcard_mention_policy =
|
||||||
settings_config.wildcard_mention_policy_values.by_members.code;
|
settings_config.wildcard_mention_policy_values.by_members.code;
|
||||||
page_params.is_guest = true;
|
page_params.is_guest = true;
|
||||||
page_params.is_admin = false;
|
page_params.is_admin = false;
|
||||||
assert.ok(!compose_validate.wildcard_mention_allowed());
|
assert.ok(!compose_validate.stream_wildcard_mention_allowed());
|
||||||
|
|
||||||
page_params.is_guest = false;
|
page_params.is_guest = false;
|
||||||
assert.ok(compose_validate.wildcard_mention_allowed());
|
assert.ok(compose_validate.stream_wildcard_mention_allowed());
|
||||||
|
|
||||||
page_params.realm_wildcard_mention_policy =
|
page_params.realm_wildcard_mention_policy =
|
||||||
settings_config.wildcard_mention_policy_values.by_moderators_only.code;
|
settings_config.wildcard_mention_policy_values.by_moderators_only.code;
|
||||||
page_params.is_moderator = false;
|
page_params.is_moderator = false;
|
||||||
assert.ok(!compose_validate.wildcard_mention_allowed());
|
assert.ok(!compose_validate.stream_wildcard_mention_allowed());
|
||||||
|
|
||||||
page_params.is_moderator = true;
|
page_params.is_moderator = true;
|
||||||
assert.ok(compose_validate.wildcard_mention_allowed());
|
assert.ok(compose_validate.stream_wildcard_mention_allowed());
|
||||||
|
|
||||||
page_params.realm_wildcard_mention_policy =
|
page_params.realm_wildcard_mention_policy =
|
||||||
settings_config.wildcard_mention_policy_values.by_admins_only.code;
|
settings_config.wildcard_mention_policy_values.by_admins_only.code;
|
||||||
page_params.is_admin = false;
|
page_params.is_admin = false;
|
||||||
assert.ok(!compose_validate.wildcard_mention_allowed());
|
assert.ok(!compose_validate.stream_wildcard_mention_allowed());
|
||||||
|
|
||||||
// TODO: Add a by_admins_only case when we implement stream-level administrators.
|
// TODO: Add a by_admins_only case when we implement stream-level administrators.
|
||||||
|
|
||||||
page_params.is_admin = true;
|
page_params.is_admin = true;
|
||||||
assert.ok(compose_validate.wildcard_mention_allowed());
|
assert.ok(compose_validate.stream_wildcard_mention_allowed());
|
||||||
|
|
||||||
page_params.realm_wildcard_mention_policy =
|
page_params.realm_wildcard_mention_policy =
|
||||||
settings_config.wildcard_mention_policy_values.by_full_members.code;
|
settings_config.wildcard_mention_policy_values.by_full_members.code;
|
||||||
|
@ -375,9 +375,9 @@ test_ui("test_wildcard_mention_allowed", ({override_rewire}) => {
|
||||||
person.date_joined = new Date(Date.now());
|
person.date_joined = new Date(Date.now());
|
||||||
page_params.realm_waiting_period_threshold = 10;
|
page_params.realm_waiting_period_threshold = 10;
|
||||||
|
|
||||||
assert.ok(compose_validate.wildcard_mention_allowed());
|
assert.ok(compose_validate.stream_wildcard_mention_allowed());
|
||||||
page_params.is_admin = false;
|
page_params.is_admin = false;
|
||||||
assert.ok(!compose_validate.wildcard_mention_allowed());
|
assert.ok(!compose_validate.stream_wildcard_mention_allowed());
|
||||||
|
|
||||||
// Now, check for small streams (<=15 subscribers) where the wildcard mention
|
// Now, check for small streams (<=15 subscribers) where the wildcard mention
|
||||||
// policy doesn't matter; everyone is allowed to use wildcard mentions.
|
// policy doesn't matter; everyone is allowed to use wildcard mentions.
|
||||||
|
@ -386,7 +386,7 @@ test_ui("test_wildcard_mention_allowed", ({override_rewire}) => {
|
||||||
settings_config.wildcard_mention_policy_values.by_admins_only.code;
|
settings_config.wildcard_mention_policy_values.by_admins_only.code;
|
||||||
page_params.is_admin = false;
|
page_params.is_admin = false;
|
||||||
page_params.is_guest = true;
|
page_params.is_guest = true;
|
||||||
assert.ok(compose_validate.wildcard_mention_allowed());
|
assert.ok(compose_validate.stream_wildcard_mention_allowed());
|
||||||
});
|
});
|
||||||
|
|
||||||
test_ui("validate_stream_message", ({override_rewire, mock_template}) => {
|
test_ui("validate_stream_message", ({override_rewire, mock_template}) => {
|
||||||
|
|
|
@ -22,7 +22,7 @@ const compose_ui = mock_esm("../src/compose_ui", {
|
||||||
const compose_validate = mock_esm("../src/compose_validate", {
|
const compose_validate = mock_esm("../src/compose_validate", {
|
||||||
validate_message_length: () => true,
|
validate_message_length: () => true,
|
||||||
warn_if_topic_resolved: noop,
|
warn_if_topic_resolved: noop,
|
||||||
wildcard_mention_allowed: () => true,
|
stream_wildcard_mention_allowed: () => true,
|
||||||
});
|
});
|
||||||
const input_pill = mock_esm("../src/input_pill");
|
const input_pill = mock_esm("../src/input_pill");
|
||||||
const message_user_ids = mock_esm("../src/message_user_ids", {
|
const message_user_ids = mock_esm("../src/message_user_ids", {
|
||||||
|
@ -82,10 +82,16 @@ run_test("verify wildcard mentions typeahead for stream message", () => {
|
||||||
assert.equal(mention_stream.special_item_text, "stream (translated: Notify stream)");
|
assert.equal(mention_stream.special_item_text, "stream (translated: Notify stream)");
|
||||||
assert.equal(mention_topic.special_item_text, "topic (translated: Notify topic)");
|
assert.equal(mention_topic.special_item_text, "topic (translated: Notify topic)");
|
||||||
|
|
||||||
compose_validate.wildcard_mention_allowed = () => false;
|
compose_validate.stream_wildcard_mention_allowed = () => false;
|
||||||
|
compose_validate.topic_wildcard_mention_allowed = () => true;
|
||||||
|
const mention_topic_only = ct.broadcast_mentions()[0];
|
||||||
|
assert.equal(mention_topic_only.full_name, "topic");
|
||||||
|
|
||||||
|
compose_validate.stream_wildcard_mention_allowed = () => false;
|
||||||
|
compose_validate.topic_wildcard_mention_allowed = () => false;
|
||||||
const mentionNobody = ct.broadcast_mentions();
|
const mentionNobody = ct.broadcast_mentions();
|
||||||
assert.equal(mentionNobody.length, 0);
|
assert.equal(mentionNobody.length, 0);
|
||||||
compose_validate.wildcard_mention_allowed = () => true;
|
compose_validate.stream_wildcard_mention_allowed = () => true;
|
||||||
});
|
});
|
||||||
|
|
||||||
run_test("verify wildcard mentions typeahead for direct message", () => {
|
run_test("verify wildcard mentions typeahead for direct message", () => {
|
||||||
|
|
|
@ -184,6 +184,11 @@ test("process_stream_message", () => {
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Messages sent by sender1 in stream1 > topic1
|
||||||
|
assert.equal(rs.get_topic_message_ids_for_sender(stream1, topic1, sender1).size, 2);
|
||||||
|
// Messages sent by sender1 in stream1 > topic2
|
||||||
|
assert.equal(rs.get_topic_message_ids_for_sender(stream1, topic2, sender1).size, 0);
|
||||||
|
|
||||||
// Same stream, but different topics
|
// Same stream, but different topics
|
||||||
const message6 = make_stream_message({
|
const message6 = make_stream_message({
|
||||||
stream_id: stream3,
|
stream_id: stream3,
|
||||||
|
|
Loading…
Reference in New Issue