composebox_typeahead: Use local `completing` instead of typeahead `this`.

This commit is contained in:
evykassirer 2024-03-29 16:16:24 -07:00 committed by Tim Abbott
parent c7b12c996c
commit ba41eace94
2 changed files with 84 additions and 61 deletions

View File

@ -48,6 +48,15 @@ export const max_num_items = 8;
export let emoji_collection = [];
let completing;
export function get_or_set_completing_for_tests(val) {
if (val !== undefined) {
completing = val;
}
return completing;
}
export function update_emoji_data() {
emoji_collection = [];
for (const emoji_dict of emoji.emojis_by_name.values()) {
@ -633,7 +642,6 @@ export function get_sorted_filtered_items(query) {
// We are still hacking info onto the "this" from
// bootstrap. Yuck.
const completing = this.completing;
const token = this.token;
const opts = get_stream_topic_data(this);
@ -705,7 +713,7 @@ export function get_candidates(query) {
if (current_token[0] === " ") {
current_token = current_token.slice(1);
}
this.completing = "syntax";
completing = "syntax";
this.token = current_token;
// If the code formatting button was triggered, we want to show a blank option
// to improve the discoverability of the possibility of specifying a language.
@ -730,24 +738,24 @@ export function get_candidates(query) {
if (current_token[1] === " ") {
return false;
}
this.completing = "emoji";
completing = "emoji";
this.token = current_token.slice(1);
return emoji_collection;
}
if (ALLOWED_MARKDOWN_FEATURES.mention && current_token[0] === "@") {
current_token = current_token.slice(1);
this.completing = "mention";
completing = "mention";
// Silent mentions
let is_silent = false;
if (current_token.startsWith("_")) {
this.completing = "silent_mention";
completing = "silent_mention";
is_silent = true;
current_token = current_token.slice(1);
}
current_token = filter_mention_name(current_token);
if (!current_token && typeof current_token === "boolean") {
this.completing = null;
completing = null;
return false;
}
this.token = current_token;
@ -762,7 +770,7 @@ export function get_candidates(query) {
if (ALLOWED_MARKDOWN_FEATURES.slash && current_token[0] === "/") {
current_token = current_token.slice(1);
this.completing = "slash";
completing = "slash";
this.token = current_token;
return get_slash_commands_data();
}
@ -782,7 +790,7 @@ export function get_candidates(query) {
return false;
}
this.completing = "stream";
completing = "stream";
this.token = current_token;
return stream_data.get_unsorted_subs();
}
@ -793,7 +801,7 @@ export function get_candidates(query) {
const stream_regex = /#\*\*([^*>]+)\*\*\s?>$/;
const should_jump_inside_typeahead = stream_regex.test(split[0]);
if (should_jump_inside_typeahead) {
this.completing = "topic_jump";
completing = "topic_jump";
this.token = ">";
// We return something so that the typeahead is shown, but ultimately
return [""];
@ -803,7 +811,7 @@ export function get_candidates(query) {
const stream_topic_regex = /#\*\*([^*>]+)>([^*]*)$/;
const should_begin_typeahead = stream_topic_regex.test(split[0]);
if (should_begin_typeahead) {
this.completing = "topic_list";
completing = "topic_list";
const tokens = stream_topic_regex.exec(split[0]);
if (tokens[1]) {
const stream_name = tokens[1];
@ -826,7 +834,7 @@ export function get_candidates(query) {
if (ALLOWED_MARKDOWN_FEATURES.timestamp) {
const time_jump_regex = /<time(:([^>]*?)>?)?$/;
if (time_jump_regex.test(split[0])) {
this.completing = "time_jump";
completing = "time_jump";
return [$t({defaultMessage: "Mention a time-zone-aware time"})];
}
}
@ -834,7 +842,7 @@ export function get_candidates(query) {
}
export function content_highlighter_html(item) {
switch (this.completing) {
switch (completing) {
case "emoji":
return typeahead_helper.render_emoji(item);
case "mention":
@ -871,7 +879,7 @@ export function content_typeahead_selected(item, event) {
// highlight offsets for that purpose.
const highlight = {};
switch (this.completing) {
switch (completing) {
case "emoji":
// leading and trailing spaces are required for emoji,
// except if it begins a message or a new line.
@ -889,7 +897,7 @@ export function content_typeahead_selected(item, event) {
break;
case "silent_mention":
case "mention": {
const is_silent = this.completing === "silent_mention";
const is_silent = completing === "silent_mention";
beginning = beginning.slice(0, -this.token.length - 1);
if (beginning.endsWith("@_*")) {
beginning = beginning.slice(0, -3);
@ -1081,7 +1089,7 @@ export function sort_results(completing, matches, token) {
}
export function compose_automated_selection() {
if (this.completing === "topic_jump") {
if (completing === "topic_jump") {
// automatically jump inside stream mention on typing > just after
// a stream mention, to begin stream+topic mention typeahead (topic_list).
return true;
@ -1090,7 +1098,7 @@ export function compose_automated_selection() {
}
export function compose_trigger_selection(event) {
if (this.completing === "stream" && event.key === ">") {
if (completing === "stream" && event.key === ">") {
// complete stream typeahead partially to immediately start the topic_list typeahead.
return true;
}
@ -1126,7 +1134,7 @@ export function initialize_topic_edit_typeahead(form_field, stream_name, dropup)
function get_header_html() {
let tip_text = "";
switch (this.completing) {
switch (completing) {
case "stream":
tip_text = $t({defaultMessage: "Press > for list of topics"});
break;

View File

@ -462,7 +462,7 @@ test("content_typeahead_selected", ({override}) => {
set_timeout_called = false;
// emoji
fake_this.completing = "emoji";
ct.get_or_set_completing_for_tests("emoji");
fake_this.query = ":octo";
fake_this.token = "octo";
const item = {
@ -486,7 +486,7 @@ test("content_typeahead_selected", ({override}) => {
assert.equal(actual_value, expected_value);
// mention
fake_this.completing = "mention";
ct.get_or_set_completing_for_tests("mention");
override(compose_validate, "warn_if_mentioning_unsubscribed_user", noop);
override(
@ -548,7 +548,7 @@ test("content_typeahead_selected", ({override}) => {
assert.equal(actual_value, expected_value);
// silent mention
fake_this.completing = "silent_mention";
ct.get_or_set_completing_for_tests("silent_mention");
fake_this.query = "@_kin";
fake_this.token = "kin";
with_overrides(({disallow}) => {
@ -593,37 +593,37 @@ test("content_typeahead_selected", ({override}) => {
assert.equal(actual_value, expected_value);
fake_this.query = "/m";
fake_this.completing = "slash";
ct.get_or_set_completing_for_tests("slash");
actual_value = ct.content_typeahead_selected.call(fake_this, me_slash);
expected_value = "/me translated: is …";
assert.equal(actual_value, expected_value);
fake_this.query = "/da";
fake_this.completing = "slash";
ct.get_or_set_completing_for_tests("slash");
actual_value = ct.content_typeahead_selected.call(fake_this, dark_slash);
expected_value = "/dark ";
assert.equal(actual_value, expected_value);
fake_this.query = "/ni";
fake_this.completing = "slash";
ct.get_or_set_completing_for_tests("slash");
actual_value = ct.content_typeahead_selected.call(fake_this, dark_slash);
expected_value = "/dark ";
assert.equal(actual_value, expected_value);
fake_this.query = "/li";
fake_this.completing = "slash";
ct.get_or_set_completing_for_tests("slash");
actual_value = ct.content_typeahead_selected.call(fake_this, light_slash);
expected_value = "/light ";
assert.equal(actual_value, expected_value);
fake_this.query = "/da";
fake_this.completing = "slash";
ct.get_or_set_completing_for_tests("slash");
actual_value = ct.content_typeahead_selected.call(fake_this, light_slash);
expected_value = "/light ";
assert.equal(actual_value, expected_value);
// stream
fake_this.completing = "stream";
ct.get_or_set_completing_for_tests("stream");
let warned_for_stream_link = false;
override(compose_validate, "warn_if_private_stream_is_linked", (linked_stream) => {
assert.equal(linked_stream, sweden_stream);
@ -649,7 +649,7 @@ test("content_typeahead_selected", ({override}) => {
assert.equal(actual_value, expected_value);
// topic_list
fake_this.completing = "topic_list";
ct.get_or_set_completing_for_tests("topic_list");
fake_this.query = "Hello #**Sweden>test";
fake_this.token = "test";
@ -664,7 +664,7 @@ test("content_typeahead_selected", ({override}) => {
assert.equal(actual_value, expected_value);
// syntax
fake_this.completing = "syntax";
ct.get_or_set_completing_for_tests("syntax");
fake_this.query = "~~~p";
fake_this.token = "p";
@ -698,7 +698,7 @@ test("content_typeahead_selected", ({override}) => {
expected_value = "```python\nsome existing code";
assert.equal(actual_value, expected_value);
fake_this.completing = "something-else";
ct.get_or_set_completing_for_tests("something-else");
fake_this.query = "foo";
actual_value = ct.content_typeahead_selected.call(fake_this, {});
@ -1010,7 +1010,8 @@ test("initialize", ({override, override_rewire, mock_template}) => {
//
// Again, here we only verify that the highlighter has been set to
// content_highlighter_html.
fake_this = {completing: "mention", token: "othello"};
ct.get_or_set_completing_for_tests("mention");
fake_this = {token: "othello"};
actual_value = options.highlighter_html.call(fake_this, othello);
expected_value =
` <span class="user_circle_empty user_circle"></span>\n` +
@ -1021,7 +1022,8 @@ test("initialize", ({override, override_rewire, mock_template}) => {
// Reset the email such that this does not affect further tests.
othello.delivery_email = null;
fake_this = {completing: "mention", token: "hamletcharacters"};
ct.get_or_set_completing_for_tests("mention");
fake_this = {token: "hamletcharacters"};
actual_value = options.highlighter_html.call(fake_this, hamletcharacters);
expected_value =
' <i class="typeahead-image icon fa fa-group no-presence-circle" aria-hidden="true"></i>\n<strong>hamletcharacters</strong>&nbsp;&nbsp;\n<small class="autocomplete_secondary">Characters of Hamlet</small>\n';
@ -1031,35 +1033,39 @@ test("initialize", ({override, override_rewire, mock_template}) => {
function match(fake_this, item) {
const token = fake_this.token;
const completing = fake_this.completing;
const completing = ct.get_or_set_completing_for_tests();
return ct.compose_content_matcher(completing, token)(item);
}
fake_this = {completing: "emoji", token: "ta"};
ct.get_or_set_completing_for_tests("emoji");
fake_this = {token: "ta"};
assert.equal(match(fake_this, make_emoji(emoji_tada)), true);
assert.equal(match(fake_this, make_emoji(emoji_moneybag)), false);
fake_this = {completing: "stream", token: "swed"};
ct.get_or_set_completing_for_tests("stream");
fake_this = {token: "swed"};
assert.equal(match(fake_this, sweden_stream), true);
assert.equal(match(fake_this, denmark_stream), false);
fake_this = {completing: "syntax", token: "py"};
ct.get_or_set_completing_for_tests("syntax");
fake_this = {token: "py"};
assert.equal(match(fake_this, "python"), true);
assert.equal(match(fake_this, "javascript"), false);
fake_this = {completing: "non-existing-completion"};
ct.get_or_set_completing_for_tests("non-existing-completion");
assert.equal(match(fake_this), undefined);
function sort_items(fake_this, item) {
const token = fake_this.token;
const completing = fake_this.completing;
const completing = ct.get_or_set_completing_for_tests();
return ct.sort_results(completing, item, token);
}
// options.sorter()
fake_this = {completing: "emoji", token: "ta"};
ct.get_or_set_completing_for_tests("emoji");
fake_this = {token: "ta"};
actual_value = sort_items(fake_this, [
make_emoji(emoji_stadium),
make_emoji(emoji_tada),
@ -1067,7 +1073,8 @@ test("initialize", ({override, override_rewire, mock_template}) => {
expected_value = [make_emoji(emoji_tada), make_emoji(emoji_stadium)];
assert.deepEqual(actual_value, expected_value);
fake_this = {completing: "emoji", token: "th"};
ct.get_or_set_completing_for_tests("emoji");
fake_this = {token: "th"};
actual_value = sort_items(fake_this, [
make_emoji(emoji_thermometer),
make_emoji(emoji_thumbs_up),
@ -1075,7 +1082,8 @@ test("initialize", ({override, override_rewire, mock_template}) => {
expected_value = [make_emoji(emoji_thumbs_up), make_emoji(emoji_thermometer)];
assert.deepEqual(actual_value, expected_value);
fake_this = {completing: "emoji", token: "he"};
ct.get_or_set_completing_for_tests("emoji");
fake_this = {token: "he"};
actual_value = sort_items(fake_this, [
make_emoji(emoji_headphones),
make_emoji(emoji_heart),
@ -1083,17 +1091,20 @@ test("initialize", ({override, override_rewire, mock_template}) => {
expected_value = [make_emoji(emoji_heart), make_emoji(emoji_headphones)];
assert.deepEqual(actual_value, expected_value);
fake_this = {completing: "slash", token: "m"};
ct.get_or_set_completing_for_tests("slash");
fake_this = {token: "m"};
actual_value = sort_items(fake_this, [my_slash, me_slash]);
expected_value = [me_slash, my_slash];
assert.deepEqual(actual_value, expected_value);
fake_this = {completing: "slash", token: "da"};
ct.get_or_set_completing_for_tests("slash");
fake_this = {token: "da"};
actual_value = sort_items(fake_this, [dark_slash, light_slash]);
expected_value = [dark_slash, light_slash];
assert.deepEqual(actual_value, expected_value);
fake_this = {completing: "stream", token: "de"};
ct.get_or_set_completing_for_tests("stream");
fake_this = {token: "de"};
actual_value = sort_items(fake_this, [sweden_stream, denmark_stream]);
expected_value = [denmark_stream, sweden_stream];
assert.deepEqual(actual_value, expected_value);
@ -1101,12 +1112,14 @@ test("initialize", ({override, override_rewire, mock_template}) => {
// Matches in the descriptions affect the order as well.
// Testing "co" for "cold", in both streams' description. It's at the
// beginning of Sweden's description, so that one should go first.
fake_this = {completing: "stream", token: "co"};
ct.get_or_set_completing_for_tests("stream");
fake_this = {token: "co"};
actual_value = sort_items(fake_this, [denmark_stream, sweden_stream]);
expected_value = [sweden_stream, denmark_stream];
assert.deepEqual(actual_value, expected_value);
fake_this = {completing: "syntax", token: "ap"};
ct.get_or_set_completing_for_tests("syntax");
fake_this = {token: "ap"};
actual_value = sort_items(fake_this, ["abap", "applescript"]);
expected_value = ["applescript", "abap"];
assert.deepEqual(actual_value, expected_value);
@ -1125,7 +1138,8 @@ test("initialize", ({override, override_rewire, mock_template}) => {
);
stream_list_sort.set_filter_out_inactives();
fake_this = {completing: "stream", token: "s"};
ct.get_or_set_completing_for_tests("stream");
fake_this = {token: "s"};
actual_value = sort_items(fake_this, [sweden_stream, serbia_stream]);
expected_value = [sweden_stream, serbia_stream];
assert.deepEqual(actual_value, expected_value);
@ -1141,12 +1155,13 @@ test("initialize", ({override, override_rewire, mock_template}) => {
expected_value = [sweden_stream, serbia_stream];
assert.deepEqual(actual_value, expected_value);
fake_this = {completing: "stream", token: "ser"};
ct.get_or_set_completing_for_tests("stream");
fake_this = {token: "ser"};
actual_value = sort_items(fake_this, [denmark_stream, serbia_stream]);
expected_value = [serbia_stream, denmark_stream];
assert.deepEqual(actual_value, expected_value);
fake_this = {completing: "non-existing-completion"};
ct.get_or_set_completing_for_tests("non-existing-completion");
assert.equal(sort_items(fake_this), undefined);
compose_textarea_typeahead_called = true;
@ -1560,32 +1575,32 @@ test("tokenizing", () => {
});
test("content_highlighter_html", ({override_rewire}) => {
let fake_this = {completing: "emoji"};
ct.get_or_set_completing_for_tests("emoji");
const emoji = {emoji_name: "person shrugging", emoji_url: "¯\\_(ツ)_/¯"};
let th_render_typeahead_item_called = false;
override_rewire(typeahead_helper, "render_emoji", (item) => {
assert.deepEqual(item, emoji);
th_render_typeahead_item_called = true;
});
ct.content_highlighter_html.call(fake_this, emoji);
ct.content_highlighter_html(emoji);
fake_this = {completing: "mention"};
ct.get_or_set_completing_for_tests("mention");
let th_render_person_called = false;
override_rewire(typeahead_helper, "render_person", (person) => {
assert.deepEqual(person, othello);
th_render_person_called = true;
});
ct.content_highlighter_html.call(fake_this, othello);
ct.content_highlighter_html(othello);
let th_render_user_group_called = false;
override_rewire(typeahead_helper, "render_user_group", (user_group) => {
assert.deepEqual(user_group, backend);
th_render_user_group_called = true;
});
ct.content_highlighter_html.call(fake_this, backend);
ct.content_highlighter_html(backend);
// We don't have any fancy rendering for slash commands yet.
fake_this = {completing: "slash"};
ct.get_or_set_completing_for_tests("slash");
let th_render_slash_command_called = false;
const me_slash = {
text: "/me (Action message)",
@ -1596,26 +1611,26 @@ test("content_highlighter_html", ({override_rewire}) => {
});
th_render_slash_command_called = true;
});
ct.content_highlighter_html.call(fake_this, me_slash);
ct.content_highlighter_html(me_slash);
fake_this = {completing: "stream"};
ct.get_or_set_completing_for_tests("stream");
let th_render_stream_called = false;
override_rewire(typeahead_helper, "render_stream", (stream) => {
assert.deepEqual(stream, denmark_stream);
th_render_stream_called = true;
});
ct.content_highlighter_html.call(fake_this, denmark_stream);
ct.content_highlighter_html(denmark_stream);
fake_this = {completing: "syntax"};
ct.get_or_set_completing_for_tests("syntax");
th_render_typeahead_item_called = false;
override_rewire(typeahead_helper, "render_typeahead_item", (item) => {
assert.deepEqual(item, {primary: "py"});
th_render_typeahead_item_called = true;
});
ct.content_highlighter_html.call(fake_this, "py");
ct.content_highlighter_html("py");
fake_this = {completing: "something-else"};
assert.ok(!ct.content_highlighter_html.call(fake_this));
ct.get_or_set_completing_for_tests("something-else");
assert.ok(!ct.content_highlighter_html());
// Verify that all stub functions have been called.
assert.ok(th_render_typeahead_item_called);