mirror of https://github.com/zulip/zulip.git
popovers: Add a popover for group mentions.
This adds a click handler to `.user-group-mention` which works in a fashion that is quite similar to `.user-mention`. It generates and displays a popover. The popover has a list of members, their online status (if they are not bots) or their bot status if they are bots (it's not clear whether ultimately bots should be able to be members of usergroups, but I'm able to add one, so I thought it would be worth supporting). The popover's `UL` element has max-height and overflow-y atttributes so large groups will grow a scrollbar. Fixes #8300.
This commit is contained in:
parent
bc21344dc6
commit
786bda674c
|
@ -1350,6 +1350,53 @@ function render(template_name, args) {
|
||||||
|
|
||||||
}());
|
}());
|
||||||
|
|
||||||
|
(function user_group_info_popover() {
|
||||||
|
var html = render('user_group_info_popover');
|
||||||
|
global.write_handlebars_output("user_group_info_popover", html);
|
||||||
|
|
||||||
|
$(html).hasClass('popover message-info-popover group-info-popover');
|
||||||
|
}());
|
||||||
|
|
||||||
|
(function user_group_info_popover_content() {
|
||||||
|
var args = {
|
||||||
|
group_name: 'groupName',
|
||||||
|
group_description: 'groupDescription',
|
||||||
|
members: [
|
||||||
|
{
|
||||||
|
presence_status: 'active',
|
||||||
|
full_name: 'Active Alice',
|
||||||
|
user_last_seen_time_status: 'time',
|
||||||
|
is_bot: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
presence_status: 'offline',
|
||||||
|
full_name: 'Bot Bob',
|
||||||
|
user_last_seen_time_status: 'time',
|
||||||
|
is_bot: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
presence_status: 'offline',
|
||||||
|
full_name: 'Inactive Imogen',
|
||||||
|
user_last_seen_time_status: 'time',
|
||||||
|
is_bot: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
var html = render('user_group_info_popover_content', args);
|
||||||
|
global.write_handlebars_output("user_group_info_popover_content", html);
|
||||||
|
|
||||||
|
var allUsers = $(html).find("li");
|
||||||
|
assert.equal(allUsers[0].classList.contains("user_active"), true);
|
||||||
|
assert.equal(allUsers[2].classList.contains("user_offline"), true);
|
||||||
|
assert.equal($(allUsers[0]).text().trim(), 'Active Alice');
|
||||||
|
assert.equal($(allUsers[1]).text().trim(), 'Bot Bob');
|
||||||
|
assert.equal($(allUsers[2]).text().trim(), 'Inactive Imogen');
|
||||||
|
|
||||||
|
assert.equal($(html).find('.group-name').text().trim(), 'groupName');
|
||||||
|
assert.equal($(html).find('.group-description').text().trim(), 'groupDescription');
|
||||||
|
}());
|
||||||
|
|
||||||
(function user_info_popover() {
|
(function user_info_popover() {
|
||||||
var html = render('user_info_popover', {class: 'message-info-popover'});
|
var html = render('user_info_popover', {class: 'message-info-popover'});
|
||||||
global.write_handlebars_output("user_info_popover", html);
|
global.write_handlebars_output("user_info_popover", html);
|
||||||
|
|
|
@ -67,11 +67,28 @@ function user_last_seen_time_status(user_id) {
|
||||||
return timerender.last_seen_status_from_date(last_active_date.clone());
|
return timerender.last_seen_status_from_date(last_active_date.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function calculate_info_popover_placement(size, elt) {
|
||||||
|
var ypos = elt.offset().top;
|
||||||
|
|
||||||
|
if (!((ypos + (size / 2) < message_viewport.height()) &&
|
||||||
|
(ypos > (size / 2)))) {
|
||||||
|
if (((ypos + size) < message_viewport.height())) {
|
||||||
|
return 'bottom';
|
||||||
|
} else if (ypos > size) {
|
||||||
|
return 'top';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// exporting for testability
|
||||||
|
exports._test_calculate_info_popover_placement = calculate_info_popover_placement;
|
||||||
|
|
||||||
// element is the target element to pop off of
|
// element is the target element to pop off of
|
||||||
// user is the user whose profile to show
|
// user is the user whose profile to show
|
||||||
// message is the message containing it, which should be selected
|
// message is the message containing it, which should be selected
|
||||||
function show_user_info_popover(element, user, message) {
|
function show_user_info_popover(element, user, message) {
|
||||||
var last_popover_elem = current_message_info_popover_elem;
|
var last_popover_elem = current_message_info_popover_elem;
|
||||||
|
var popover_size = 428; // hardcoded pixel height of the popover
|
||||||
popovers.hide_all();
|
popovers.hide_all();
|
||||||
if (last_popover_elem !== undefined
|
if (last_popover_elem !== undefined
|
||||||
&& last_popover_elem.get()[0] === element) {
|
&& last_popover_elem.get()[0] === element) {
|
||||||
|
@ -104,21 +121,9 @@ function show_user_info_popover(element, user, message) {
|
||||||
is_bot: people.get_person_from_user_id(user.user_id).is_bot,
|
is_bot: people.get_person_from_user_id(user.user_id).is_bot,
|
||||||
};
|
};
|
||||||
|
|
||||||
var ypos = elt.offset().top;
|
|
||||||
var popover_size = 428;
|
|
||||||
var placement = "right";
|
|
||||||
|
|
||||||
if (!((ypos + (popover_size / 2) < message_viewport.height()) &&
|
|
||||||
(ypos > (popover_size / 2)))) {
|
|
||||||
if (((ypos + popover_size) < message_viewport.height())) {
|
|
||||||
placement = "bottom";
|
|
||||||
} else if (ypos > popover_size) {
|
|
||||||
placement = "top";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
elt.popover({
|
elt.popover({
|
||||||
placement: placement,
|
placement: calculate_info_popover_placement(popover_size, elt),
|
||||||
template: templates.render('user_info_popover', {class: "message-info-popover"}),
|
template: templates.render('user_info_popover', {class: "message-info-popover"}),
|
||||||
title: templates.render('user_info_popover_title',
|
title: templates.render('user_info_popover_title',
|
||||||
{user_avatar: "avatar/" + user.email}),
|
{user_avatar: "avatar/" + user.email}),
|
||||||
|
@ -133,6 +138,73 @@ function show_user_info_popover(element, user, message) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fetch_group_members(member_ids) {
|
||||||
|
return member_ids
|
||||||
|
.map(function (m) {
|
||||||
|
return people.get_person_from_user_id(m);
|
||||||
|
})
|
||||||
|
.filter(function (m) {
|
||||||
|
return m !== undefined;
|
||||||
|
})
|
||||||
|
.map(function (p) {
|
||||||
|
return Object.assign({}, p, {
|
||||||
|
presence_status: presence.get_status(p.user_id),
|
||||||
|
is_active: people.is_active_user_for_popover(p.user_id),
|
||||||
|
user_last_seen_time_status: user_last_seen_time_status(p.user_id),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function sort_group_members(members) {
|
||||||
|
return members
|
||||||
|
.sort(function (a, b) {
|
||||||
|
return a.full_name.localeCompare(b.full_name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// exporting these functions for testing purposes
|
||||||
|
exports._test_fetch_group_members = fetch_group_members;
|
||||||
|
exports._test_sort_group_members = sort_group_members;
|
||||||
|
|
||||||
|
// element is the target element to pop off of
|
||||||
|
// user is the user whose profile to show
|
||||||
|
// message is the message containing it, which should be selected
|
||||||
|
function show_user_group_info_popover(element, group, message) {
|
||||||
|
var last_popover_elem = current_message_info_popover_elem;
|
||||||
|
// hardcoded pixel height of the popover
|
||||||
|
// note that the actual size varies (in group size), but this is about as big as it gets
|
||||||
|
var popover_size = 390;
|
||||||
|
popovers.hide_all();
|
||||||
|
if (last_popover_elem !== undefined
|
||||||
|
&& last_popover_elem.get()[0] === element) {
|
||||||
|
// We want it to be the case that a user can dismiss a popover
|
||||||
|
// by clicking on the same element that caused the popover.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
current_msg_list.select_id(message.id);
|
||||||
|
var elt = $(element);
|
||||||
|
if (elt.data('popover') === undefined) {
|
||||||
|
var args = {
|
||||||
|
group_name: group.name,
|
||||||
|
group_description: group.description,
|
||||||
|
members: sort_group_members(fetch_group_members(group.members)),
|
||||||
|
};
|
||||||
|
elt.popover({
|
||||||
|
placement: calculate_info_popover_placement(popover_size, elt),
|
||||||
|
template: templates.render('user_group_info_popover', {class: "message-info-popover"}),
|
||||||
|
content: templates.render('user_group_info_popover_content', args),
|
||||||
|
trigger: "manual",
|
||||||
|
});
|
||||||
|
elt.popover("show");
|
||||||
|
$('.nav.nav-list.member-list').perfectScrollbar({
|
||||||
|
suppressScrollX: true,
|
||||||
|
useKeyboard: false,
|
||||||
|
wheelSpeed: 0.5,
|
||||||
|
});
|
||||||
|
current_message_info_popover_elem = elt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
exports.toggle_actions_popover = function (element, id) {
|
exports.toggle_actions_popover = function (element, id) {
|
||||||
var last_popover_elem = current_actions_popover_elem;
|
var last_popover_elem = current_actions_popover_elem;
|
||||||
popovers.hide_all();
|
popovers.hide_all();
|
||||||
|
@ -449,6 +521,19 @@ exports.register_click_handlers = function () {
|
||||||
show_user_info_popover(this, user, message);
|
show_user_info_popover(this, user, message);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#main_div").on("click", ".user-group-mention", function (e) {
|
||||||
|
var id = $(this).attr('data-user-group-id');
|
||||||
|
var row = $(this).closest(".message_row");
|
||||||
|
e.stopPropagation();
|
||||||
|
var message = current_msg_list.get(rows.id(row));
|
||||||
|
var group = user_groups.get_user_group_from_id(id);
|
||||||
|
if (group === undefined) {
|
||||||
|
blueslip.error('Unable to find user group in message' + message.sender_id);
|
||||||
|
} else {
|
||||||
|
show_user_group_info_popover(this, group, message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$('body').on('click', '.user_popover .narrow_to_private_messages', function (e) {
|
$('body').on('click', '.user_popover .narrow_to_private_messages', function (e) {
|
||||||
var user_id = $(e.target).parents('ul').attr('data-user-id');
|
var user_id = $(e.target).parents('ul').attr('data-user-id');
|
||||||
var email = people.get_person_from_user_id(user_id).email;
|
var email = people.get_person_from_user_id(user_id).email;
|
||||||
|
|
|
@ -96,6 +96,29 @@ ul.remind_me_popover .remind_icon {
|
||||||
width: 240px;
|
width: 240px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.group-info-popover .group-info {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-info-popover .group-info .group-name {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-info-popover .member-list {
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
list-style: none;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-info-popover .member-list .bot {
|
||||||
|
color: hsl(180, 5%, 74%);
|
||||||
|
vertical-align: top;
|
||||||
|
width: 20px;
|
||||||
|
padding-top: 3.5px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.user_popover .popover-title {
|
.user_popover .popover-title {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
<div class="popover message-info-popover group-info-popover">
|
||||||
|
<div class="popover-inner">
|
||||||
|
<div class="popover-content">
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,30 @@
|
||||||
|
{{! Contents of the "user group info" popup }}
|
||||||
|
<div class="group-info">
|
||||||
|
<div class="group-name"> {{group_name}} </div>
|
||||||
|
<div class="group-description">
|
||||||
|
{{group_description}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<ul class="nav nav-list member-list">
|
||||||
|
{{#each members}}
|
||||||
|
<li class="user_{{presence_status}}">
|
||||||
|
{{#if is_active }}
|
||||||
|
{{#if is_bot}}
|
||||||
|
<i class="zulip-icon bot" aria-hidden="true"></i>
|
||||||
|
{{else}}
|
||||||
|
<span class="user-status-indicator popover_user_presence" title="{{user_last_seen_time_status}}"></span>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
<span>{{full_name}}</span>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
<ul class="nav nav-list">
|
||||||
|
<li>
|
||||||
|
<a href="#organization/user-groups-admin">
|
||||||
|
<i class="icon-vector-cog"></i>
|
||||||
|
{{t 'Manage user groups' }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
Loading…
Reference in New Issue