2019-11-02 00:06:25 +01:00
|
|
|
const render_message_reaction = require('../templates/message_reaction.hbs');
|
|
|
|
const Dict = require('./dict').Dict;
|
2019-02-08 11:56:33 +01:00
|
|
|
|
2017-06-28 21:29:45 +02:00
|
|
|
exports.view = {}; // function namespace
|
|
|
|
|
2017-10-31 22:33:28 +01:00
|
|
|
exports.get_local_reaction_id = function (reaction_info) {
|
2018-05-07 01:38:14 +02:00
|
|
|
return [
|
|
|
|
reaction_info.reaction_type,
|
|
|
|
reaction_info.emoji_name,
|
|
|
|
reaction_info.emoji_code,
|
|
|
|
].join(',');
|
2017-10-31 22:33:28 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
exports.get_reaction_info = function (reaction_id) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const reaction_info = reaction_id.split(',');
|
2017-10-31 22:33:28 +01:00
|
|
|
return {
|
|
|
|
reaction_type: reaction_info[0],
|
|
|
|
emoji_name: reaction_info[1],
|
|
|
|
emoji_code: reaction_info[2],
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2017-09-18 22:06:39 +02:00
|
|
|
exports.open_reactions_popover = function () {
|
2019-11-02 00:06:25 +01:00
|
|
|
const message = current_msg_list.selected_message();
|
|
|
|
let target = $(current_msg_list.selected_row()).find(".actions_hover")[0];
|
2017-09-18 22:06:39 +02:00
|
|
|
if (!message.sent_by_me) {
|
|
|
|
target = $(current_msg_list.selected_row()).find(".reaction_button")[0];
|
|
|
|
}
|
|
|
|
emoji_picker.toggle_emoji_popover(target, current_msg_list.selected_id());
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
2017-10-31 22:33:28 +01:00
|
|
|
exports.current_user_has_reacted_to_emoji = function (message, emoji_code, type) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const user_id = page_params.user_id;
|
2020-02-08 04:08:04 +01:00
|
|
|
return message.reactions.some(r => r.user.id === user_id &&
|
|
|
|
r.reaction_type === type &&
|
|
|
|
r.emoji_code === emoji_code);
|
2017-10-31 22:33:28 +01:00
|
|
|
};
|
2017-10-08 17:02:14 +02:00
|
|
|
|
2017-10-31 22:33:28 +01:00
|
|
|
function get_message(message_id) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const message = message_store.get(message_id);
|
2017-10-31 22:33:28 +01:00
|
|
|
if (!message) {
|
|
|
|
blueslip.error('reactions: Bad message id: ' + message_id);
|
2016-12-02 13:23:23 +01:00
|
|
|
return;
|
|
|
|
}
|
2017-10-31 22:33:28 +01:00
|
|
|
|
|
|
|
return message;
|
|
|
|
}
|
|
|
|
|
2017-12-19 00:55:40 +01:00
|
|
|
function create_reaction(message_id, reaction_info) {
|
|
|
|
return {
|
|
|
|
message_id: message_id,
|
|
|
|
user: {
|
|
|
|
user_id: page_params.user_id,
|
|
|
|
id: page_params.user_id,
|
|
|
|
},
|
|
|
|
local_id: exports.get_local_reaction_id(reaction_info),
|
|
|
|
reaction_type: reaction_info.reaction_type,
|
|
|
|
emoji_name: reaction_info.emoji_name,
|
|
|
|
emoji_code: reaction_info.emoji_code,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function update_ui_and_send_reaction_ajax(message_id, reaction_info) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const message = get_message(message_id);
|
|
|
|
const has_reacted = exports.current_user_has_reacted_to_emoji(
|
2017-10-31 22:33:28 +01:00
|
|
|
message,
|
|
|
|
reaction_info.emoji_code,
|
|
|
|
reaction_info.reaction_type
|
|
|
|
);
|
2019-11-02 00:06:25 +01:00
|
|
|
const operation = has_reacted ? 'remove' : 'add';
|
|
|
|
const reaction = create_reaction(message_id, reaction_info);
|
2017-12-19 00:55:40 +01:00
|
|
|
|
|
|
|
if (operation === "add") {
|
|
|
|
exports.add_reaction(reaction);
|
|
|
|
} else {
|
|
|
|
exports.remove_reaction(reaction);
|
|
|
|
}
|
2017-10-31 22:33:28 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const args = {
|
2017-10-31 22:33:28 +01:00
|
|
|
url: '/json/messages/' + message_id + '/reactions',
|
|
|
|
data: reaction_info,
|
2016-12-02 13:23:23 +01:00
|
|
|
success: function () {},
|
|
|
|
error: function (xhr) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const response = channel.xhr_error_message("Error sending reaction", xhr);
|
2017-03-24 23:46:20 +01:00
|
|
|
// Errors are somewhat commmon here, due to race conditions
|
|
|
|
// where the user tries to add/remove the reaction when there is already
|
|
|
|
// an in-flight request. We eventually want to make this a blueslip
|
|
|
|
// error, rather than a warning, but we need to implement either
|
|
|
|
// #4291 or #4295 first.
|
|
|
|
blueslip.warn(response);
|
2017-01-12 00:17:43 +01:00
|
|
|
},
|
2016-12-02 13:23:23 +01:00
|
|
|
};
|
|
|
|
if (operation === 'add') {
|
2017-10-31 22:33:28 +01:00
|
|
|
channel.post(args);
|
2016-12-02 13:23:23 +01:00
|
|
|
} else if (operation === 'remove') {
|
|
|
|
channel.del(args);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-31 22:33:28 +01:00
|
|
|
function get_user_list_for_message_reaction(message, local_id) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const matching_reactions = message.reactions.filter(function (reaction) {
|
2017-10-31 22:33:28 +01:00
|
|
|
return reaction.local_id === local_id;
|
2016-12-02 13:23:23 +01:00
|
|
|
});
|
|
|
|
return matching_reactions.map(function (reaction) {
|
|
|
|
return reaction.user.id;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-05-29 21:18:59 +02:00
|
|
|
exports.toggle_emoji_reaction = function (message_id, emoji_name) {
|
2018-03-11 18:55:20 +01:00
|
|
|
// This codepath doesn't support toggling a deactivated realm emoji.
|
|
|
|
// Since an user can interact with a deactivated realm emoji only by
|
|
|
|
// clicking on a reaction and that is handled by `process_reaction_click()`
|
|
|
|
// method. This codepath is to be used only where there is no chance of an
|
|
|
|
// user interacting with a deactivated realm emoji like emoji picker.
|
2019-11-02 00:06:25 +01:00
|
|
|
const reaction_info = {
|
2017-10-31 22:33:28 +01:00
|
|
|
emoji_name: emoji_name,
|
|
|
|
};
|
2017-05-11 17:00:24 +02:00
|
|
|
|
2020-02-06 00:17:30 +01:00
|
|
|
if (emoji.active_realm_emojis.has(emoji_name)) {
|
2017-10-31 22:33:28 +01:00
|
|
|
if (emoji_name === 'zulip') {
|
|
|
|
reaction_info.reaction_type = 'zulip_extra_emoji';
|
|
|
|
} else {
|
|
|
|
reaction_info.reaction_type = 'realm_emoji';
|
|
|
|
}
|
2020-02-06 00:17:30 +01:00
|
|
|
reaction_info.emoji_code = emoji.active_realm_emojis.get(emoji_name).id;
|
2017-10-31 22:33:28 +01:00
|
|
|
} else if (emoji_codes.name_to_codepoint.hasOwnProperty(emoji_name)) {
|
|
|
|
reaction_info.reaction_type = 'unicode_emoji';
|
|
|
|
reaction_info.emoji_code = emoji_codes.name_to_codepoint[emoji_name];
|
|
|
|
} else {
|
|
|
|
blueslip.warn('Bad emoji name: ' + emoji_name);
|
2017-05-11 17:00:24 +02:00
|
|
|
return;
|
2016-12-02 13:23:23 +01:00
|
|
|
}
|
2017-05-11 17:00:24 +02:00
|
|
|
|
2017-12-19 00:55:40 +01:00
|
|
|
update_ui_and_send_reaction_ajax(message_id, reaction_info);
|
2017-05-29 21:36:24 +02:00
|
|
|
|
|
|
|
// The next line isn't always necessary, but it is harmless/quick
|
|
|
|
// when no popovers are there.
|
|
|
|
emoji_picker.hide_emoji_popover();
|
2016-12-02 13:23:23 +01:00
|
|
|
};
|
|
|
|
|
2017-10-31 22:33:28 +01:00
|
|
|
exports.process_reaction_click = function (message_id, local_id) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const reaction_info = exports.get_reaction_info(local_id);
|
2017-10-31 22:33:28 +01:00
|
|
|
|
2017-12-19 00:55:40 +01:00
|
|
|
update_ui_and_send_reaction_ajax(message_id, reaction_info);
|
2017-10-31 22:33:28 +01:00
|
|
|
};
|
|
|
|
|
2016-12-02 13:23:23 +01:00
|
|
|
function full_name(user_id) {
|
|
|
|
if (user_id === page_params.user_id) {
|
|
|
|
return 'You (click to remove)';
|
|
|
|
}
|
2020-02-05 14:30:59 +01:00
|
|
|
return people.get_by_user_id(user_id).full_name;
|
2016-12-02 13:23:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function generate_title(emoji_name, user_ids) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const i = user_ids.indexOf(page_params.user_id);
|
2016-12-02 13:23:23 +01:00
|
|
|
if (i !== -1) {
|
|
|
|
// Move current user's id to start of list
|
|
|
|
user_ids.splice(i, 1);
|
|
|
|
user_ids.unshift(page_params.user_id);
|
|
|
|
}
|
2019-11-02 00:06:25 +01:00
|
|
|
const reacted_with_string = ' reacted with :' + emoji_name + ':';
|
|
|
|
const user_names = user_ids.map(full_name);
|
2016-12-02 13:23:23 +01:00
|
|
|
if (user_names.length === 1) {
|
|
|
|
return user_names[0] + reacted_with_string;
|
|
|
|
}
|
|
|
|
return _.initial(user_names).join(', ') + ' and ' + _.last(user_names) + reacted_with_string;
|
|
|
|
}
|
|
|
|
|
2019-04-04 14:56:54 +02:00
|
|
|
// Add a tooltip showing who reacted to a message.
|
|
|
|
exports.get_reaction_title_data = function (message_id, local_id) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const message = get_message(message_id);
|
|
|
|
const user_list = get_user_list_for_message_reaction(message, local_id);
|
|
|
|
const emoji_name = exports.get_reaction_info(local_id).emoji_name;
|
|
|
|
const title = generate_title(emoji_name, user_list);
|
2019-04-04 14:56:54 +02:00
|
|
|
|
|
|
|
return title;
|
|
|
|
};
|
|
|
|
|
2017-05-29 16:31:51 +02:00
|
|
|
exports.get_reaction_section = function (message_id) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const message_element = $('.message_table').find("[zid='" + message_id + "']");
|
|
|
|
const section = message_element.find('.message_reactions');
|
2017-05-29 16:31:51 +02:00
|
|
|
return section;
|
|
|
|
};
|
|
|
|
|
2017-10-31 22:33:28 +01:00
|
|
|
exports.find_reaction = function (message_id, local_id) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const reaction_section = exports.get_reaction_section(message_id);
|
|
|
|
const reaction = reaction_section.find("[data-reaction-id='" + local_id + "']");
|
2017-05-29 16:31:51 +02:00
|
|
|
return reaction;
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.get_add_reaction_button = function (message_id) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const reaction_section = exports.get_reaction_section(message_id);
|
|
|
|
const add_button = reaction_section.find('.reaction_button');
|
2017-05-29 16:31:51 +02:00
|
|
|
return add_button;
|
|
|
|
};
|
|
|
|
|
2017-05-29 19:24:31 +02:00
|
|
|
exports.set_reaction_count = function (reaction, count) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const count_element = reaction.find('.message_reaction_count');
|
2018-03-22 22:04:24 +01:00
|
|
|
count_element.text(count);
|
2017-05-29 19:24:31 +02:00
|
|
|
};
|
|
|
|
|
2016-12-02 13:23:23 +01:00
|
|
|
exports.add_reaction = function (event) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const message_id = event.message_id;
|
|
|
|
const message = message_store.get(message_id);
|
2017-10-31 22:33:28 +01:00
|
|
|
|
2017-01-17 09:10:41 +01:00
|
|
|
if (message === undefined) {
|
|
|
|
// If we don't have the message in cache, do nothing; if we
|
|
|
|
// ever fetch it from the server, it'll come with the
|
|
|
|
// latest reactions attached
|
|
|
|
return;
|
|
|
|
}
|
2017-05-29 16:31:51 +02:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const reacted = exports.current_user_has_reacted_to_emoji(message,
|
|
|
|
event.emoji_code,
|
|
|
|
event.reaction_type);
|
2018-06-06 18:50:09 +02:00
|
|
|
if (reacted && event.user.user_id === page_params.user_id) {
|
2017-12-19 00:55:40 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-05-29 16:31:51 +02:00
|
|
|
event.user.id = event.user.user_id;
|
2017-10-31 22:33:28 +01:00
|
|
|
event.local_id = exports.get_local_reaction_id(event);
|
2017-05-29 16:31:51 +02:00
|
|
|
|
2017-01-17 09:10:41 +01:00
|
|
|
message.reactions.push(event);
|
2017-05-29 16:31:51 +02:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const user_list = get_user_list_for_message_reaction(message, event.local_id);
|
|
|
|
const opts = {
|
2017-10-31 22:33:28 +01:00
|
|
|
message_id: event.message_id,
|
|
|
|
reaction_type: event.reaction_type,
|
|
|
|
emoji_name: event.emoji_name,
|
|
|
|
emoji_code: event.emoji_code,
|
|
|
|
user_id: event.user.id,
|
|
|
|
};
|
2017-05-29 16:31:51 +02:00
|
|
|
|
|
|
|
if (user_list.length > 1) {
|
2017-10-31 22:33:28 +01:00
|
|
|
opts.user_list = user_list;
|
|
|
|
exports.view.update_existing_reaction(opts);
|
2016-12-02 13:23:23 +01:00
|
|
|
} else {
|
2017-10-31 22:33:28 +01:00
|
|
|
exports.view.insert_new_reaction(opts);
|
2016-12-02 13:23:23 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-06-28 20:10:05 +02:00
|
|
|
exports.view.update_existing_reaction = function (opts) {
|
2017-05-29 16:31:51 +02:00
|
|
|
// Our caller ensures that this message already has a reaction
|
|
|
|
// for this emoji and sets up our user_list. This function
|
|
|
|
// simply updates the DOM.
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const message_id = opts.message_id;
|
|
|
|
const emoji_name = opts.emoji_name;
|
|
|
|
const user_list = opts.user_list;
|
|
|
|
const user_id = opts.user_id;
|
|
|
|
const local_id = exports.get_local_reaction_id(opts);
|
|
|
|
const reaction = exports.find_reaction(message_id, local_id);
|
2017-05-29 16:31:51 +02:00
|
|
|
|
2017-05-29 19:24:31 +02:00
|
|
|
exports.set_reaction_count(reaction, user_list.length);
|
2017-05-29 16:31:51 +02:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const new_label = generate_title(emoji_name, user_list);
|
2019-04-04 14:56:54 +02:00
|
|
|
reaction.attr('aria-label', new_label);
|
2017-05-29 16:31:51 +02:00
|
|
|
|
2017-06-28 20:10:05 +02:00
|
|
|
if (user_id === page_params.user_id) {
|
2017-05-29 16:31:51 +02:00
|
|
|
reaction.addClass("reacted");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-06-28 21:29:45 +02:00
|
|
|
exports.view.insert_new_reaction = function (opts) {
|
2017-05-29 16:31:51 +02:00
|
|
|
// Our caller ensures we are the first user to react to this
|
|
|
|
// message with this emoji, and it populates user_list for
|
|
|
|
// us. We then render the emoji/title/count and insert it
|
|
|
|
// before the add button.
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const message_id = opts.message_id;
|
|
|
|
const emoji_name = opts.emoji_name;
|
|
|
|
const emoji_code = opts.emoji_code;
|
|
|
|
const user_id = opts.user_id;
|
|
|
|
const user_list = [user_id];
|
2017-05-29 16:31:51 +02:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const context = {
|
2017-06-28 21:07:26 +02:00
|
|
|
message_id: message_id,
|
|
|
|
emoji_name: emoji_name,
|
2017-10-31 22:33:28 +01:00
|
|
|
emoji_code: emoji_code,
|
2017-06-28 21:07:26 +02:00
|
|
|
};
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const new_label = generate_title(emoji_name, user_list);
|
2017-05-29 16:31:51 +02:00
|
|
|
|
2017-10-31 22:33:28 +01:00
|
|
|
if (opts.reaction_type !== 'unicode_emoji') {
|
2017-06-28 21:07:26 +02:00
|
|
|
context.is_realm_emoji = true;
|
2020-02-06 00:08:06 +01:00
|
|
|
context.url = emoji.all_realm_emojis.get(emoji_code).emoji_url;
|
2017-05-29 16:31:51 +02:00
|
|
|
}
|
|
|
|
|
2017-06-28 21:07:26 +02:00
|
|
|
context.count = 1;
|
2019-04-04 14:56:54 +02:00
|
|
|
context.label = new_label;
|
2017-10-31 22:33:28 +01:00
|
|
|
context.local_id = exports.get_local_reaction_id(opts);
|
2018-06-06 18:19:09 +02:00
|
|
|
context.emoji_alt_code = page_params.emojiset === 'text';
|
2017-05-29 16:31:51 +02:00
|
|
|
|
2017-06-28 21:29:45 +02:00
|
|
|
if (opts.user_id === page_params.user_id) {
|
2017-06-28 21:07:26 +02:00
|
|
|
context.class = "message_reaction reacted";
|
2017-05-29 16:31:51 +02:00
|
|
|
} else {
|
2017-06-28 21:07:26 +02:00
|
|
|
context.class = "message_reaction";
|
2017-05-29 16:31:51 +02:00
|
|
|
}
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const new_reaction = $(render_message_reaction(context));
|
2017-05-29 16:31:51 +02:00
|
|
|
|
|
|
|
// Now insert it before the add button.
|
2019-11-02 00:06:25 +01:00
|
|
|
const reaction_button_element = exports.get_add_reaction_button(message_id);
|
2017-05-29 16:31:51 +02:00
|
|
|
new_reaction.insertBefore(reaction_button_element);
|
|
|
|
};
|
|
|
|
|
2016-12-02 13:23:23 +01:00
|
|
|
exports.remove_reaction = function (event) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const reaction_type = event.reaction_type;
|
|
|
|
const emoji_name = event.emoji_name;
|
|
|
|
const emoji_code = event.emoji_code;
|
|
|
|
const message_id = event.message_id;
|
|
|
|
const user_id = event.user.user_id;
|
|
|
|
let i = -1;
|
|
|
|
const message = message_store.get(message_id);
|
|
|
|
const local_id = exports.get_local_reaction_id(event);
|
2017-05-29 16:56:13 +02:00
|
|
|
|
2017-01-17 09:10:41 +01:00
|
|
|
if (message === undefined) {
|
|
|
|
// If we don't have the message in cache, do nothing; if we
|
|
|
|
// ever fetch it from the server, it'll come with the
|
|
|
|
// latest reactions attached
|
|
|
|
return;
|
|
|
|
}
|
2017-05-29 16:56:13 +02:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const not_reacted = !exports.current_user_has_reacted_to_emoji(message,
|
|
|
|
emoji_code,
|
|
|
|
reaction_type);
|
2018-06-06 18:50:09 +02:00
|
|
|
if (not_reacted && event.user.user_id === page_params.user_id) {
|
2017-12-19 00:55:40 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-05-29 16:56:13 +02:00
|
|
|
// Do the data part first:
|
|
|
|
// Remove reactions from our message object.
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
|
|
|
for (const [index, reaction] of message.reactions.entries()) {
|
2017-10-31 22:33:28 +01:00
|
|
|
if (reaction.local_id === local_id && reaction.user.id === user_id) {
|
2016-12-02 13:23:23 +01:00
|
|
|
i = index;
|
|
|
|
}
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
|
|
|
}
|
2017-05-29 16:56:13 +02:00
|
|
|
|
2016-12-02 13:23:23 +01:00
|
|
|
if (i !== -1) {
|
|
|
|
message.reactions.splice(i, 1);
|
|
|
|
}
|
2017-05-29 16:56:13 +02:00
|
|
|
|
|
|
|
// Compute the new user list for this reaction.
|
2019-11-02 00:06:25 +01:00
|
|
|
const user_list = get_user_list_for_message_reaction(message, local_id);
|
2017-05-29 16:56:13 +02:00
|
|
|
|
2017-06-28 21:41:35 +02:00
|
|
|
exports.view.remove_reaction({
|
|
|
|
message_id: message_id,
|
2017-10-31 22:33:28 +01:00
|
|
|
reaction_type: reaction_type,
|
2017-06-28 21:41:35 +02:00
|
|
|
emoji_name: emoji_name,
|
2017-10-31 22:33:28 +01:00
|
|
|
emoji_code: emoji_code,
|
2017-06-28 21:41:35 +02:00
|
|
|
user_list: user_list,
|
|
|
|
user_id: user_id,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.view.remove_reaction = function (opts) {
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const message_id = opts.message_id;
|
|
|
|
const emoji_name = opts.emoji_name;
|
|
|
|
const user_list = opts.user_list;
|
|
|
|
const user_id = opts.user_id;
|
|
|
|
const local_id = exports.get_local_reaction_id(opts);
|
|
|
|
const reaction = exports.find_reaction(message_id, local_id);
|
2017-05-29 16:56:13 +02:00
|
|
|
|
|
|
|
if (user_list.length === 0) {
|
|
|
|
// If this user was the only one reacting for this emoji, we simply
|
|
|
|
// remove the reaction and exit.
|
|
|
|
reaction.remove();
|
|
|
|
return;
|
2017-02-13 07:51:40 +01:00
|
|
|
}
|
2017-05-29 16:56:13 +02:00
|
|
|
|
|
|
|
// The emoji still has reactions from other users, so we need to update
|
|
|
|
// the title/count and, if the user is the current user, turn off the
|
|
|
|
// "reacted" class.
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const new_label = generate_title(emoji_name, user_list);
|
2019-04-04 14:56:54 +02:00
|
|
|
reaction.attr('aria-label', new_label);
|
|
|
|
|
|
|
|
// If the user is the current user, turn off the "reacted" class.
|
2017-05-29 16:56:13 +02:00
|
|
|
|
2017-05-29 19:24:31 +02:00
|
|
|
exports.set_reaction_count(reaction, user_list.length);
|
2017-05-29 16:56:13 +02:00
|
|
|
|
|
|
|
if (user_id === page_params.user_id) {
|
|
|
|
reaction.removeClass("reacted");
|
2016-12-02 13:23:23 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.get_emojis_used_by_user_for_message_id = function (message_id) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const user_id = page_params.user_id;
|
|
|
|
const message = message_store.get(message_id);
|
|
|
|
const reactions_by_user = message.reactions.filter(function (reaction) {
|
2016-12-02 13:23:23 +01:00
|
|
|
return reaction.user.id === user_id;
|
|
|
|
});
|
|
|
|
return reactions_by_user.map(function (reaction) {
|
|
|
|
return reaction.emoji_name;
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.get_message_reactions = function (message) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const message_reactions = new Dict();
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
|
|
|
|
|
|
|
for (const reaction of message.reactions) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const user_id = reaction.user.id;
|
2017-10-31 22:33:28 +01:00
|
|
|
reaction.local_id = exports.get_local_reaction_id(reaction);
|
2017-03-26 20:38:47 +02:00
|
|
|
if (!people.is_known_user_id(user_id)) {
|
|
|
|
blueslip.warn('Unknown user_id ' + user_id +
|
2018-07-10 08:31:15 +02:00
|
|
|
' in reaction for message ' + message.id);
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
|
|
|
continue;
|
2017-03-26 20:38:47 +02:00
|
|
|
}
|
2017-05-01 07:29:56 +02:00
|
|
|
reaction.user_ids = [];
|
2020-02-03 06:49:12 +01:00
|
|
|
let collapsed_reaction = message_reactions.get(reaction.local_id);
|
|
|
|
if (collapsed_reaction === undefined) {
|
|
|
|
collapsed_reaction = _.omit(reaction, 'user');
|
|
|
|
message_reactions.set(reaction.local_id, collapsed_reaction);
|
|
|
|
}
|
2017-05-01 07:29:56 +02:00
|
|
|
collapsed_reaction.user_ids.push(user_id);
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
|
|
|
}
|
|
|
|
|
2020-02-03 09:04:48 +01:00
|
|
|
const reactions = Array.from(message_reactions.values(), reaction => {
|
2017-10-31 22:33:28 +01:00
|
|
|
reaction.local_id = reaction.local_id;
|
|
|
|
reaction.reaction_type = reaction.reaction_type;
|
|
|
|
reaction.emoji_name = reaction.emoji_name;
|
|
|
|
reaction.emoji_code = reaction.emoji_code;
|
2017-05-01 07:29:56 +02:00
|
|
|
reaction.count = reaction.user_ids.length;
|
2019-04-04 14:56:54 +02:00
|
|
|
reaction.label = generate_title(reaction.emoji_name, reaction.user_ids);
|
2018-06-06 18:19:09 +02:00
|
|
|
reaction.emoji_alt_code = page_params.emojiset === 'text';
|
2017-05-01 07:29:56 +02:00
|
|
|
|
|
|
|
if (reaction.reaction_type !== 'unicode_emoji') {
|
2016-12-02 13:23:23 +01:00
|
|
|
reaction.is_realm_emoji = true;
|
2020-02-06 00:08:06 +01:00
|
|
|
reaction.url = emoji.all_realm_emojis.get(reaction.emoji_code).emoji_url;
|
2016-12-02 13:23:23 +01:00
|
|
|
}
|
js: Convert a.indexOf(…) !== -1 to a.includes(…).
Babel polyfills this for us for Internet Explorer.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import K from "ast-types/gen/kinds";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
recast.visit(ast, {
visitBinaryExpression(path) {
const { operator, left, right } = path.node;
if (
n.CallExpression.check(left) &&
n.MemberExpression.check(left.callee) &&
!left.callee.computed &&
n.Identifier.check(left.callee.property) &&
left.callee.property.name === "indexOf" &&
left.arguments.length === 1 &&
checkExpression(left.arguments[0]) &&
((["===", "!==", "==", "!=", ">", "<="].includes(operator) &&
n.UnaryExpression.check(right) &&
right.operator == "-" &&
n.Literal.check(right.argument) &&
right.argument.value === 1) ||
([">=", "<"].includes(operator) &&
n.Literal.check(right) &&
right.value === 0))
) {
const test = b.callExpression(
b.memberExpression(left.callee.object, b.identifier("includes")),
[left.arguments[0]]
);
path.replace(
["!==", "!=", ">", ">="].includes(operator)
? test
: b.unaryExpression("!", test)
);
changed = true;
}
this.traverse(path);
},
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-08 04:55:06 +01:00
|
|
|
if (reaction.user_ids.includes(page_params.user_id)) {
|
2017-02-13 07:51:40 +01:00
|
|
|
reaction.class = "message_reaction reacted";
|
|
|
|
} else {
|
|
|
|
reaction.class = "message_reaction";
|
|
|
|
}
|
2016-12-02 13:23:23 +01:00
|
|
|
return reaction;
|
|
|
|
});
|
|
|
|
return reactions;
|
|
|
|
};
|
|
|
|
|
2019-10-25 09:45:13 +02:00
|
|
|
window.reactions = exports;
|