zulip/static/js/reactions.js

199 lines
7.4 KiB
JavaScript
Raw Normal View History

Add frontend support for emoji reactions. This commit replaces the placeholder "clipboard" button with a reaction button. This is done on any message that can't be edited. Also, on messages sent by the user the actions popover (toggled by the down chevron icon) contains an option to add a reaction. When clicked, a popover with a search bar and a list of emojis is displayed. If the right sidebar is collapsed (the viewport is small), the popover is placed to the left of the button. Focus is set to the search bar. Typing in the search bar filters emojis. Emojis with which the user has reacted to this message are highlighted. Clicking them sends an API request to remove that reaction. Clicking on non-highlighted emojis sends an API request to add a reaction. When the popover loses focus it is closed. The frontend listens for reaction events. When an add-reaction event is received, the emoji is displayed at the bottom of the message with a count initialized to 1. If there was an existing reaction to the message with the same emoji, the count is incremented. Old messages fetched from the server contain reactions. They are displayed (along with title and count) at the bottom of each message. When clicking the emoji reaction at the bottom of the message, if the user has already reacted with that emoji to this message, the reaction is removed and the count is decremented. Otherwise, a reaction is added and the count is incremented. Hovering over the emoji reaction at the bottom of the message displays a list of users who have reacted with this emoji along with the emoji name. Hovering over the emoji reactions at the bottom of the message displays a button to add a reaction. Fixes #541.
2016-12-02 13:23:23 +01:00
var reactions = (function () {
var exports = {};
function send_reaction_ajax(message_id, emoji_name, operation) {
if (!emoji.emojis_by_name[emoji_name]) {
// Emoji doesn't exist
return;
}
var args = {
url: '/json/messages/' + message_id + '/emoji_reactions/' + encodeURIComponent(emoji_name),
data: {},
success: function () {},
error: function (xhr) {
var response = channel.xhr_error_message("Error sending reaction", xhr);
blueslip.error(response);
},
Add frontend support for emoji reactions. This commit replaces the placeholder "clipboard" button with a reaction button. This is done on any message that can't be edited. Also, on messages sent by the user the actions popover (toggled by the down chevron icon) contains an option to add a reaction. When clicked, a popover with a search bar and a list of emojis is displayed. If the right sidebar is collapsed (the viewport is small), the popover is placed to the left of the button. Focus is set to the search bar. Typing in the search bar filters emojis. Emojis with which the user has reacted to this message are highlighted. Clicking them sends an API request to remove that reaction. Clicking on non-highlighted emojis sends an API request to add a reaction. When the popover loses focus it is closed. The frontend listens for reaction events. When an add-reaction event is received, the emoji is displayed at the bottom of the message with a count initialized to 1. If there was an existing reaction to the message with the same emoji, the count is incremented. Old messages fetched from the server contain reactions. They are displayed (along with title and count) at the bottom of each message. When clicking the emoji reaction at the bottom of the message, if the user has already reacted with that emoji to this message, the reaction is removed and the count is decremented. Otherwise, a reaction is added and the count is incremented. Hovering over the emoji reaction at the bottom of the message displays a list of users who have reacted with this emoji along with the emoji name. Hovering over the emoji reactions at the bottom of the message displays a button to add a reaction. Fixes #541.
2016-12-02 13:23:23 +01:00
};
if (operation === 'add') {
channel.put(args);
} else if (operation === 'remove') {
channel.del(args);
}
}
function get_user_list_for_message_reaction(message_id, emoji_name) {
var message = message_store.get(message_id);
var matching_reactions = message.reactions.filter(function (reaction) {
return reaction.emoji_name === emoji_name;
});
return matching_reactions.map(function (reaction) {
return reaction.user.id;
});
}
exports.message_reaction_on_click = function (message_id, emoji_name) {
// When a message's reaction is clicked,
// if the user has reacted to this message with this emoji
// the reaction is removed
// otherwise, the reaction is added
var user_list = get_user_list_for_message_reaction(message_id, emoji_name);
var operation = 'remove';
if (user_list.indexOf(page_params.user_id) === -1) {
// User hasn't reacted with this emoji to this message
operation = 'add';
}
send_reaction_ajax(message_id, emoji_name, operation);
};
function reaction_popover_reaction_on_click() {
// When an emoji is clicked in the popover,
// if the user has reacted to this message with this emoji
// the reaction is removed
// otherwise, the reaction is added
var emoji_name = this.title;
var message_id = $(this).parent().attr('data-message-id');
var user_list = get_user_list_for_message_reaction(message_id, emoji_name);
var operation = 'add';
if (user_list.indexOf(page_params.user_id) !== -1) {
// User has reacted with this emoji to this message
$(this).removeClass('reacted');
operation = 'remove';
}
send_reaction_ajax(message_id, emoji_name, operation);
popovers.hide_reactions_popover();
Add frontend support for emoji reactions. This commit replaces the placeholder "clipboard" button with a reaction button. This is done on any message that can't be edited. Also, on messages sent by the user the actions popover (toggled by the down chevron icon) contains an option to add a reaction. When clicked, a popover with a search bar and a list of emojis is displayed. If the right sidebar is collapsed (the viewport is small), the popover is placed to the left of the button. Focus is set to the search bar. Typing in the search bar filters emojis. Emojis with which the user has reacted to this message are highlighted. Clicking them sends an API request to remove that reaction. Clicking on non-highlighted emojis sends an API request to add a reaction. When the popover loses focus it is closed. The frontend listens for reaction events. When an add-reaction event is received, the emoji is displayed at the bottom of the message with a count initialized to 1. If there was an existing reaction to the message with the same emoji, the count is incremented. Old messages fetched from the server contain reactions. They are displayed (along with title and count) at the bottom of each message. When clicking the emoji reaction at the bottom of the message, if the user has already reacted with that emoji to this message, the reaction is removed and the count is decremented. Otherwise, a reaction is added and the count is incremented. Hovering over the emoji reaction at the bottom of the message displays a list of users who have reacted with this emoji along with the emoji name. Hovering over the emoji reactions at the bottom of the message displays a button to add a reaction. Fixes #541.
2016-12-02 13:23:23 +01:00
}
function filter_emojis() {
var search = $(".reaction-popover-filter");
var search_term = search.expectOne().val().trim();
var reaction_list = $(".reaction-popover-reaction");
if (search_term !== '') {
reaction_list.filter(function () {
return this.title.indexOf(search_term) === -1;
}).css("display", "none");
reaction_list.filter(function () {
return this.title.indexOf(search_term) !== -1;
}).css("display", "block");
} else {
reaction_list.css("display", "block");
}
}
$(document).on('click', '.reaction-popover-reaction', reaction_popover_reaction_on_click);
$(document).on('input', '.reaction-popover-filter', filter_emojis);
function full_name(user_id) {
if (user_id === page_params.user_id) {
return 'You (click to remove)';
}
return people.get_person_from_user_id(user_id).full_name;
}
function generate_title(emoji_name, user_ids) {
var i = user_ids.indexOf(page_params.user_id);
if (i !== -1) {
// Move current user's id to start of list
user_ids.splice(i, 1);
user_ids.unshift(page_params.user_id);
}
var reacted_with_string = ' reacted with :' + emoji_name + ':';
var user_names = user_ids.map(full_name);
if (user_names.length === 1) {
return user_names[0] + reacted_with_string;
}
return _.initial(user_names).join(', ') + ' and ' + _.last(user_names) + reacted_with_string;
}
exports.add_reaction = function (event) {
event.emoji_name_css_class = emoji.emoji_name_to_css_class(event.emoji_name);
event.user.id = event.user.user_id;
message_store.get(event.message_id).reactions.push(event);
var message_element = $('.message_table').find("[zid='" + event.message_id + "']");
var message_reactions_element = message_element.find('.message_reactions');
var user_list = get_user_list_for_message_reaction(event.message_id, event.emoji_name);
var new_title = generate_title(event.emoji_name, user_list);
if (user_list.length === 1) {
if (emoji.realm_emojis[event.emoji_name]) {
event.is_realm_emoji = true;
event.url = emoji.realm_emojis[event.emoji_name].emoji_url;
}
event.count = 1;
event.title = new_title;
var reaction_button_element = message_reactions_element.find('.reaction_button');
$(templates.render('message_reaction', event)).insertBefore(reaction_button_element);
} else {
var reaction = message_reactions_element.find("[data-emoji-name='" + event.emoji_name + "']");
var count_element = reaction.find('.message_reaction_count');
count_element.html(user_list.length);
reaction.prop('title', new_title);
}
};
exports.remove_reaction = function (event) {
var emoji_name = event.emoji_name;
var message_id = event.message_id;
var user_id = event.user.user_id;
var message = message_store.get(message_id);
var i = -1;
_.each(message.reactions, function (reaction, index) {
if (reaction.emoji_name === emoji_name && reaction.user.id === user_id) {
i = index;
}
});
if (i !== -1) {
message.reactions.splice(i, 1);
}
var user_list = get_user_list_for_message_reaction(message_id, emoji_name);
var new_title = generate_title(emoji_name, user_list);
var message_element = $('.message_table').find("[zid='" + message_id + "']");
var message_reactions_element = message_element.find('.message_reactions');
var matching_reactions = message_reactions_element.find('[data-emoji-name="' + emoji_name + '"]');
var count_element = matching_reactions.find('.message_reaction_count');
matching_reactions.prop('title', new_title);
count_element.html(user_list.length);
if (user_list.length === 0) {
matching_reactions.remove();
}
};
exports.get_emojis_used_by_user_for_message_id = function (message_id) {
var user_id = page_params.user_id;
var message = message_store.get(message_id);
var reactions_by_user = message.reactions.filter(function (reaction) {
return reaction.user.id === user_id;
});
return reactions_by_user.map(function (reaction) {
return reaction.emoji_name;
});
};
exports.get_message_reactions = function (message) {
var message_reactions = new Dict();
_.each(message.reactions, function (reaction) {
var user_list = message_reactions.setdefault(reaction.emoji_name, []);
user_list.push(reaction.user.id);
});
var reactions = message_reactions.items().map(function (item) {
var reaction = {
emoji_name: item[0],
emoji_name_css_class: emoji.emoji_name_to_css_class(item[0]),
count: item[1].length,
title: generate_title(item[0], item[1]),
Add frontend support for emoji reactions. This commit replaces the placeholder "clipboard" button with a reaction button. This is done on any message that can't be edited. Also, on messages sent by the user the actions popover (toggled by the down chevron icon) contains an option to add a reaction. When clicked, a popover with a search bar and a list of emojis is displayed. If the right sidebar is collapsed (the viewport is small), the popover is placed to the left of the button. Focus is set to the search bar. Typing in the search bar filters emojis. Emojis with which the user has reacted to this message are highlighted. Clicking them sends an API request to remove that reaction. Clicking on non-highlighted emojis sends an API request to add a reaction. When the popover loses focus it is closed. The frontend listens for reaction events. When an add-reaction event is received, the emoji is displayed at the bottom of the message with a count initialized to 1. If there was an existing reaction to the message with the same emoji, the count is incremented. Old messages fetched from the server contain reactions. They are displayed (along with title and count) at the bottom of each message. When clicking the emoji reaction at the bottom of the message, if the user has already reacted with that emoji to this message, the reaction is removed and the count is decremented. Otherwise, a reaction is added and the count is incremented. Hovering over the emoji reaction at the bottom of the message displays a list of users who have reacted with this emoji along with the emoji name. Hovering over the emoji reactions at the bottom of the message displays a button to add a reaction. Fixes #541.
2016-12-02 13:23:23 +01:00
};
if (emoji.realm_emojis[reaction.emoji_name]) {
reaction.is_realm_emoji = true;
reaction.url = emoji.realm_emojis[reaction.emoji_name].emoji_url;
}
return reaction;
});
return reactions;
};
return exports;
}());
if (typeof module !== 'undefined') {
module.exports = reactions;
}