mirror of https://github.com/zulip/zulip.git
mention: Fix mention highlighting in unsubscribed streams.
Rules followed: 1. Bold and highlighted background if the mention was processed as a mention that includes you. 2. Bold personal mention (but not highlighted) if you were mentioned but not subscribed at the time. 3. Otherwise not bold, no highlighting. As we plan to keep the mention pill CSS the same if a user was mentioned via that personal/wildcard/usergroup mention irrespective of whether the user is subscribed or not, we use usermessage flags to determine when to add 'user-mention-me' class. Fixes #27654.
This commit is contained in:
parent
73152671e3
commit
0c159c5f47
|
@ -417,7 +417,16 @@ export class MessageListView {
|
||||||
// message didn't include a user mention, then it was a usergroup/wildcard
|
// message didn't include a user mention, then it was a usergroup/wildcard
|
||||||
// mention (which is the only other option for `mentioned` being true).
|
// mention (which is the only other option for `mentioned` being true).
|
||||||
if (message_container.msg.mentioned_me_directly && is_user_mention) {
|
if (message_container.msg.mentioned_me_directly && is_user_mention) {
|
||||||
|
// Highlight messages having personal mentions only in DMs and subscribed streams.
|
||||||
|
if (
|
||||||
|
message_container.msg.type === "private" ||
|
||||||
|
stream_data.is_user_subscribed(
|
||||||
|
message_container.msg.stream_id,
|
||||||
|
people.my_current_user_id(),
|
||||||
|
)
|
||||||
|
) {
|
||||||
message_container.mention_classname = "direct_mention";
|
message_container.mention_classname = "direct_mention";
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
message_container.mention_classname = "group_mention";
|
message_container.mention_classname = "group_mention";
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ import * as people from "./people";
|
||||||
import * as realm_playground from "./realm_playground";
|
import * as realm_playground from "./realm_playground";
|
||||||
import * as rows from "./rows";
|
import * as rows from "./rows";
|
||||||
import * as rtl from "./rtl";
|
import * as rtl from "./rtl";
|
||||||
import * as stream_data from "./stream_data";
|
|
||||||
import * as sub_store from "./sub_store";
|
import * as sub_store from "./sub_store";
|
||||||
import * as timerender from "./timerender";
|
import * as timerender from "./timerender";
|
||||||
import * as user_groups from "./user_groups";
|
import * as user_groups from "./user_groups";
|
||||||
|
@ -66,6 +65,21 @@ function get_user_group_id_for_mention_button(elem) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function get_message_for_message_content($content) {
|
||||||
|
// TODO: This selector is designed to exclude drafts/scheduled
|
||||||
|
// messages. Arguably those settings should be unconditionally
|
||||||
|
// marked with user-mention-me, but rows.id doesn't support
|
||||||
|
// those elements, and we should address that quirk for
|
||||||
|
// mentions holistically.
|
||||||
|
const $message_row = $content.closest(".message_row");
|
||||||
|
if (!$message_row.length || $message_row.closest(".overlay-message-row").length) {
|
||||||
|
// There's no containing message when rendering a preview.
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const message_id = rows.id($message_row);
|
||||||
|
return message_store.get(message_id);
|
||||||
|
}
|
||||||
|
|
||||||
// Helper function to update a mentioned user's name.
|
// Helper function to update a mentioned user's name.
|
||||||
export function set_name_in_mention_element(element, name, user_id) {
|
export function set_name_in_mention_element(element, name, user_id) {
|
||||||
if (user_id !== undefined && people.should_add_guest_user_indicator(user_id)) {
|
if (user_id !== undefined && people.should_add_guest_user_indicator(user_id)) {
|
||||||
|
@ -101,30 +115,24 @@ export const update_elements = ($content) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// personal and stream wildcard mentions
|
||||||
$content.find(".user-mention").each(function () {
|
$content.find(".user-mention").each(function () {
|
||||||
const user_id = get_user_id_for_mention_button(this);
|
const user_id = get_user_id_for_mention_button(this);
|
||||||
|
const message = get_message_for_message_content($content);
|
||||||
// We give special highlights to the mention buttons
|
// We give special highlights to the mention buttons
|
||||||
// that refer to the current user.
|
// that refer to the current user.
|
||||||
if (user_id === "*" || people.is_my_user_id(user_id)) {
|
if (user_id === "*" && message && message.stream_wildcard_mentioned) {
|
||||||
const $message_header_stream = $content
|
|
||||||
.closest(".recipient_row")
|
|
||||||
.find(".message_header.message_header_stream");
|
|
||||||
// If stream message
|
|
||||||
if ($message_header_stream.length) {
|
|
||||||
const stream_id = Number.parseInt(
|
|
||||||
$message_header_stream.attr("data-stream-id"),
|
|
||||||
10,
|
|
||||||
);
|
|
||||||
// Don't highlight the mention if user is not subscribed to the stream.
|
|
||||||
if (stream_data.is_user_subscribed(stream_id, people.my_current_user_id())) {
|
|
||||||
// Either a wildcard mention or us, so mark it.
|
|
||||||
$(this).addClass("user-mention-me");
|
$(this).addClass("user-mention-me");
|
||||||
}
|
}
|
||||||
} else {
|
if (
|
||||||
// Always highlight wildcard mentions in direct messages.
|
user_id !== "*" &&
|
||||||
|
people.is_my_user_id(user_id) &&
|
||||||
|
message &&
|
||||||
|
message.mentioned_me_directly
|
||||||
|
) {
|
||||||
$(this).addClass("user-mention-me");
|
$(this).addClass("user-mention-me");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (user_id && user_id !== "*" && !$(this).find(".highlight").length) {
|
if (user_id && user_id !== "*" && !$(this).find(".highlight").length) {
|
||||||
// If it's a mention of a specific user, edit the
|
// If it's a mention of a specific user, edit the
|
||||||
// mention text to show the user's current name,
|
// mention text to show the user's current name,
|
||||||
|
@ -140,20 +148,9 @@ export const update_elements = ($content) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
$content.find(".topic-mention").each(function () {
|
$content.find(".topic-mention").each(function () {
|
||||||
// TODO: This selector is designed to exclude drafts/scheduled
|
const message = get_message_for_message_content($content);
|
||||||
// messages. Arguably those settings should be unconditionally
|
|
||||||
// marked with user-mention-me, but rows.id doesn't support
|
|
||||||
// those elements, and we should address that quirk for
|
|
||||||
// mentions holistically.
|
|
||||||
const $message_row = $content.closest(".message_row");
|
|
||||||
if (!$message_row.length || $message_row.closest(".overlay-message-row").length) {
|
|
||||||
// There's no containing message when rendering a preview.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const message_id = rows.id($message_row);
|
|
||||||
const message = message_store.get(message_id);
|
|
||||||
|
|
||||||
if (message.topic_wildcard_mentioned) {
|
if (message && message.topic_wildcard_mentioned) {
|
||||||
$(this).addClass("user-mention-me");
|
$(this).addClass("user-mention-me");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -185,11 +185,6 @@
|
||||||
&.user-mention-me {
|
&.user-mention-me {
|
||||||
color: var(--color-text-self-direct-mention);
|
color: var(--color-text-self-direct-mention);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|
||||||
&[data-user-id="*"] {
|
|
||||||
color: var(--color-text-self-group-mention);
|
|
||||||
background-color: var(--color-background-text-group-mention);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
@ -197,6 +192,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user-mention[data-user-id="*"],
|
||||||
.user-group-mention,
|
.user-group-mention,
|
||||||
.topic-mention {
|
.topic-mention {
|
||||||
color: var(--color-text-other-mention);
|
color: var(--color-text-other-mention);
|
||||||
|
|
|
@ -88,14 +88,34 @@ const $array = (array) => {
|
||||||
return {each};
|
return {each};
|
||||||
};
|
};
|
||||||
|
|
||||||
function set_closest_dot_find_result($content, value) {
|
function set_message_for_message_content($content, value) {
|
||||||
|
// no message row found
|
||||||
|
if (value === undefined) {
|
||||||
$content.closest = (closest_opts) => {
|
$content.closest = (closest_opts) => {
|
||||||
assert.equal(closest_opts, ".recipient_row");
|
assert.equal(closest_opts, ".message_row");
|
||||||
const find = (find_opts) => {
|
return [];
|
||||||
assert.equal(find_opts, ".message_header.message_header_stream");
|
|
||||||
return value;
|
|
||||||
};
|
};
|
||||||
return {find};
|
return;
|
||||||
|
}
|
||||||
|
// message row found
|
||||||
|
const $message_row = $.create(".message-row");
|
||||||
|
$content.closest = (closest_opts) => {
|
||||||
|
assert.equal(closest_opts, ".message_row");
|
||||||
|
return $message_row;
|
||||||
|
};
|
||||||
|
$message_row.length = 1;
|
||||||
|
$message_row.closest = (closest_opts) => {
|
||||||
|
assert.equal(closest_opts, ".overlay-message-row");
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
const message_id = 100;
|
||||||
|
rows.id = (message_row) => {
|
||||||
|
assert.equal(message_row, $message_row);
|
||||||
|
return message_id;
|
||||||
|
};
|
||||||
|
message_store.get = (message_id_opt) => {
|
||||||
|
assert.equal(message_id_opt, message_id);
|
||||||
|
return value;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +132,7 @@ const get_content_element = () => {
|
||||||
$content.set_find_results("div.spoiler-header", $array([]));
|
$content.set_find_results("div.spoiler-header", $array([]));
|
||||||
$content.set_find_results("div.codehilite", $array([]));
|
$content.set_find_results("div.codehilite", $array([]));
|
||||||
$content.set_find_results(".message_inline_video video", $array([]));
|
$content.set_find_results(".message_inline_video video", $array([]));
|
||||||
set_closest_dot_find_result($content, []);
|
set_message_for_message_content($content, undefined);
|
||||||
|
|
||||||
// Fend off dumb security bugs by forcing devs to be
|
// Fend off dumb security bugs by forcing devs to be
|
||||||
// intentional about HTML manipulation.
|
// intentional about HTML manipulation.
|
||||||
|
@ -186,12 +206,16 @@ run_test("user-mention", () => {
|
||||||
assert.equal($polonius.text(), "never-been-set");
|
assert.equal($polonius.text(), "never-been-set");
|
||||||
|
|
||||||
rm.update_elements($content);
|
rm.update_elements($content);
|
||||||
|
assert.ok(!$iago.hasClass("user-mention-me"));
|
||||||
// Final asserts
|
|
||||||
assert.ok($iago.hasClass("user-mention-me"));
|
|
||||||
assert.equal($iago.text(), `@${iago.full_name}`);
|
assert.equal($iago.text(), `@${iago.full_name}`);
|
||||||
assert.equal($cordelia.text(), `@${cordelia.full_name}`);
|
assert.equal($cordelia.text(), `@${cordelia.full_name}`);
|
||||||
assert.equal($polonius.text(), `translated: @${polonius.full_name} (guest)`);
|
assert.equal($polonius.text(), `translated: @${polonius.full_name} (guest)`);
|
||||||
|
|
||||||
|
// message row found
|
||||||
|
const message = {mentioned_me_directly: true};
|
||||||
|
set_message_for_message_content($content, message);
|
||||||
|
rm.update_elements($content);
|
||||||
|
assert.ok($iago.hasClass("user-mention-me"));
|
||||||
});
|
});
|
||||||
|
|
||||||
run_test("user-mention without guest indicator", () => {
|
run_test("user-mention without guest indicator", () => {
|
||||||
|
@ -206,49 +230,20 @@ run_test("user-mention without guest indicator", () => {
|
||||||
assert.equal($polonius.text(), `@${polonius.full_name}`);
|
assert.equal($polonius.text(), `@${polonius.full_name}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
run_test("user-mention PM (wildcard)", () => {
|
run_test("user-mention (stream wildcard)", () => {
|
||||||
// Setup
|
// Setup
|
||||||
const $content = get_content_element();
|
const $content = get_content_element();
|
||||||
const $mention = $.create("mention");
|
const $mention = $.create("mention");
|
||||||
$mention.attr("data-user-id", "*");
|
$mention.attr("data-user-id", "*");
|
||||||
$content.set_find_results(".user-mention", $array([$mention]));
|
$content.set_find_results(".user-mention", $array([$mention]));
|
||||||
|
const message = {stream_wildcard_mentioned: true};
|
||||||
|
set_message_for_message_content($content, message);
|
||||||
|
|
||||||
assert.ok(!$mention.hasClass("user-mention-me"));
|
assert.ok(!$mention.hasClass("user-mention-me"));
|
||||||
rm.update_elements($content);
|
rm.update_elements($content);
|
||||||
assert.ok($mention.hasClass("user-mention-me"));
|
assert.ok($mention.hasClass("user-mention-me"));
|
||||||
});
|
});
|
||||||
|
|
||||||
run_test("user-mention Stream subbed (wildcard)", ({override_rewire}) => {
|
|
||||||
// Setup
|
|
||||||
const $content = get_content_element();
|
|
||||||
const $mention = $.create("mention");
|
|
||||||
$mention.attr("data-user-id", "*");
|
|
||||||
$content.set_find_results(".user-mention", $array([$mention]));
|
|
||||||
const attr = () => stream.stream_id;
|
|
||||||
set_closest_dot_find_result($content, {attr, length: 1});
|
|
||||||
override_rewire(stream_data, "is_user_subscribed", () => true);
|
|
||||||
|
|
||||||
assert.ok(!$mention.hasClass("user-mention-me"));
|
|
||||||
rm.update_elements($content);
|
|
||||||
assert.ok($mention.hasClass("user-mention-me"));
|
|
||||||
});
|
|
||||||
|
|
||||||
run_test("user-mention Stream not subbed (wildcard)", ({override_rewire}) => {
|
|
||||||
// Setup
|
|
||||||
const $content = get_content_element();
|
|
||||||
const $mention = $.create("mention");
|
|
||||||
$mention.attr("data-user-id", "*");
|
|
||||||
$content.set_find_results(".user-mention", $array([$mention]));
|
|
||||||
const attr = () => 1;
|
|
||||||
set_closest_dot_find_result($content, {attr, length: 1});
|
|
||||||
override_rewire(stream_data, "is_user_subscribed", () => false);
|
|
||||||
|
|
||||||
// Don't add user-mention-me class.
|
|
||||||
assert.ok(!$mention.hasClass("user-mention-me"));
|
|
||||||
rm.update_elements($content);
|
|
||||||
assert.ok(!$mention.hasClass("user-mention-me"));
|
|
||||||
});
|
|
||||||
|
|
||||||
run_test("user-mention (email)", () => {
|
run_test("user-mention (email)", () => {
|
||||||
// Setup
|
// Setup
|
||||||
const $content = get_content_element();
|
const $content = get_content_element();
|
||||||
|
@ -277,39 +272,16 @@ run_test("topic-mention", () => {
|
||||||
const $mention = $.create("mention");
|
const $mention = $.create("mention");
|
||||||
$content.set_find_results(".topic-mention", $array([$mention]));
|
$content.set_find_results(".topic-mention", $array([$mention]));
|
||||||
|
|
||||||
// no message row found
|
// when no message row found
|
||||||
$content.closest = (closest_opts) => {
|
|
||||||
assert.equal(closest_opts, ".message_row");
|
|
||||||
return [];
|
|
||||||
};
|
|
||||||
assert.ok(!$mention.hasClass("user-mention-me"));
|
assert.ok(!$mention.hasClass("user-mention-me"));
|
||||||
rm.update_elements($content);
|
rm.update_elements($content);
|
||||||
assert.ok(!$mention.hasClass("user-mention-me"));
|
assert.ok(!$mention.hasClass("user-mention-me"));
|
||||||
|
|
||||||
// message row found
|
// message row found
|
||||||
const $message_row = $.create(".message-row");
|
|
||||||
$content.closest = (closest_opts) => {
|
|
||||||
assert.equal(closest_opts, ".message_row");
|
|
||||||
return $message_row;
|
|
||||||
};
|
|
||||||
$message_row.length = 1;
|
|
||||||
$message_row.closest = (closest_opts) => {
|
|
||||||
assert.equal(closest_opts, ".overlay-message-row");
|
|
||||||
return [];
|
|
||||||
};
|
|
||||||
const message_id = 100;
|
|
||||||
rows.id = (message_row) => {
|
|
||||||
assert.equal(message_row, $message_row);
|
|
||||||
return message_id;
|
|
||||||
};
|
|
||||||
const message = {
|
const message = {
|
||||||
stream_id: 1,
|
|
||||||
topic_wildcard_mentioned: true,
|
topic_wildcard_mentioned: true,
|
||||||
};
|
};
|
||||||
message_store.get = (message_id_opt) => {
|
set_message_for_message_content($content, message);
|
||||||
assert.equal(message_id_opt, message_id);
|
|
||||||
return message;
|
|
||||||
};
|
|
||||||
|
|
||||||
assert.ok(!$mention.hasClass("user-mention-me"));
|
assert.ok(!$mention.hasClass("user-mention-me"));
|
||||||
rm.update_elements($content);
|
rm.update_elements($content);
|
||||||
|
@ -321,29 +293,11 @@ run_test("topic-mention not topic participant", () => {
|
||||||
const $content = get_content_element();
|
const $content = get_content_element();
|
||||||
const $mention = $.create("mention");
|
const $mention = $.create("mention");
|
||||||
$content.set_find_results(".topic-mention", $array([$mention]));
|
$content.set_find_results(".topic-mention", $array([$mention]));
|
||||||
const $message_row = $.create(".message-row");
|
|
||||||
$content.closest = (closest_opts) => {
|
|
||||||
assert.equal(closest_opts, ".message_row");
|
|
||||||
return $message_row;
|
|
||||||
};
|
|
||||||
$message_row.length = 1;
|
|
||||||
$message_row.closest = (closest_opts) => {
|
|
||||||
assert.equal(closest_opts, ".overlay-message-row");
|
|
||||||
return [];
|
|
||||||
};
|
|
||||||
const message_id = 100;
|
|
||||||
rows.id = (message_row) => {
|
|
||||||
assert.equal(message_row, $message_row);
|
|
||||||
return message_id;
|
|
||||||
};
|
|
||||||
const message = {
|
const message = {
|
||||||
stream_id: 1,
|
|
||||||
topic_wildcard_mentioned: false,
|
topic_wildcard_mentioned: false,
|
||||||
};
|
};
|
||||||
message_store.get = (message_id_opt) => {
|
set_message_for_message_content($content, message);
|
||||||
assert.equal(message_id_opt, message_id);
|
|
||||||
return message;
|
|
||||||
};
|
|
||||||
|
|
||||||
assert.ok(!$mention.hasClass("user-mention-me"));
|
assert.ok(!$mention.hasClass("user-mention-me"));
|
||||||
rm.update_elements($content);
|
rm.update_elements($content);
|
||||||
|
|
Loading…
Reference in New Issue