mirror of https://github.com/zulip/zulip.git
popovers: Extract `user_group_popover` into separate module.
This is a preparatory commit before we migrate `user_group_popover` from Bootstrap to Tippy library. The previous implementation was weirdly sharing the logic around `current_message_info_popover_elem` with the user info popovers based on a message; very likely an unfortunate latent bug caused by copy/paste. To address that, we need to add dedicated functions like get_user_group_popover_items to avoid breaking keyboard navigation with this extraction.
This commit is contained in:
parent
1765ce23b0
commit
7777c55b22
|
@ -222,6 +222,7 @@ EXEMPT_FILES = make_set(
|
|||
"web/src/user_group_create_members_data.ts",
|
||||
"web/src/user_group_edit.js",
|
||||
"web/src/user_group_edit_members.js",
|
||||
"web/src/user_group_popover.js",
|
||||
"web/src/user_group_ui_updates.js",
|
||||
"web/src/user_groups.ts",
|
||||
"web/src/user_groups_settings_ui.js",
|
||||
|
|
|
@ -48,6 +48,7 @@ import * as stream_popover from "./stream_popover";
|
|||
import * as stream_settings_ui from "./stream_settings_ui";
|
||||
import * as topic_zoom from "./topic_zoom";
|
||||
import * as unread_ops from "./unread_ops";
|
||||
import * as user_group_popover from "./user_group_popover";
|
||||
import {user_settings} from "./user_settings";
|
||||
import * as user_topics_ui from "./user_topics_ui";
|
||||
|
||||
|
@ -386,6 +387,11 @@ function handle_popover_events(event_name) {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (user_group_popover.is_open()) {
|
||||
user_group_popover.handle_keyboard(event_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,6 @@ import url_template_lib from "url-template";
|
|||
|
||||
import render_no_arrow_popover from "../templates/no_arrow_popover.hbs";
|
||||
import render_playground_links_popover_content from "../templates/playground_links_popover_content.hbs";
|
||||
import render_user_group_info_popover from "../templates/user_group_info_popover.hbs";
|
||||
import render_user_group_info_popover_content from "../templates/user_group_info_popover_content.hbs";
|
||||
import render_user_info_popover_content from "../templates/user_info_popover_content.hbs";
|
||||
import render_user_info_popover_manage_menu from "../templates/user_info_popover_manage_menu.hbs";
|
||||
import render_user_info_popover_title from "../templates/user_info_popover_title.hbs";
|
||||
|
@ -40,12 +38,11 @@ import * as settings_users from "./settings_users";
|
|||
import * as stream_popover from "./stream_popover";
|
||||
import * as timerender from "./timerender";
|
||||
import * as ui_report from "./ui_report";
|
||||
import * as user_groups from "./user_groups";
|
||||
import * as user_group_popover from "./user_group_popover";
|
||||
import * as user_profile from "./user_profile";
|
||||
import {user_settings} from "./user_settings";
|
||||
import * as user_status from "./user_status";
|
||||
import * as user_status_ui from "./user_status_ui";
|
||||
import * as util from "./util";
|
||||
|
||||
let $current_message_info_popover_elem;
|
||||
let $current_user_info_popover_elem;
|
||||
|
@ -162,20 +159,6 @@ function load_medium_avatar(user, $elt) {
|
|||
});
|
||||
}
|
||||
|
||||
function calculate_info_popover_placement(size, $elt) {
|
||||
const ypos = $elt.get_offset_to_window().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";
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function hide_user_info_popover_manage_menu() {
|
||||
if ($current_user_info_popover_manage_menu !== undefined) {
|
||||
$current_user_info_popover_manage_menu.popover("destroy");
|
||||
|
@ -342,9 +325,6 @@ function render_user_info_popover(
|
|||
load_medium_avatar(user, $(".popover-avatar"));
|
||||
}
|
||||
|
||||
// exporting for testability
|
||||
export const _test_calculate_info_popover_placement = calculate_info_popover_placement;
|
||||
|
||||
// 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
|
||||
|
@ -448,62 +428,6 @@ function get_user_info_popover_manage_menu_items() {
|
|||
return $(".user_info_popover_manage_menu li:not(.divider):visible a", popover_data.$tip);
|
||||
}
|
||||
|
||||
function fetch_group_members(member_ids) {
|
||||
return member_ids
|
||||
.map((m) => people.maybe_get_user_by_id(m))
|
||||
.filter((m) => m !== undefined)
|
||||
.map((p) => ({
|
||||
...p,
|
||||
user_circle_class: buddy_data.get_user_circle_class(p.user_id),
|
||||
is_active: people.is_active_user_for_popover(p.user_id),
|
||||
user_last_seen_time_status: buddy_data.user_last_seen_time_status(p.user_id),
|
||||
}));
|
||||
}
|
||||
|
||||
function sort_group_members(members) {
|
||||
return members.sort((a, b) => util.strcmp(a.full_name, b.fullname));
|
||||
}
|
||||
|
||||
// exporting these functions for testing purposes
|
||||
export const _test_fetch_group_members = fetch_group_members;
|
||||
|
||||
export const _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) {
|
||||
const $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
|
||||
const popover_size = 390;
|
||||
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;
|
||||
}
|
||||
message_lists.current.select_id(message.id);
|
||||
const $elt = $(element);
|
||||
if ($elt.data("popover") === undefined) {
|
||||
const 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: render_user_group_info_popover({class: "message-info-popover"}),
|
||||
content: render_user_group_info_popover_content(args),
|
||||
html: true,
|
||||
trigger: "manual",
|
||||
fixed: true,
|
||||
});
|
||||
$elt.popover("show");
|
||||
$current_message_info_popover_elem = $elt;
|
||||
}
|
||||
}
|
||||
|
||||
function get_action_menu_menu_items() {
|
||||
const $current_actions_popover_elem = $("[data-tippy-root] .actions_popover");
|
||||
if (!$current_actions_popover_elem) {
|
||||
|
@ -760,20 +684,6 @@ export function register_click_handlers() {
|
|||
show_user_info_popover_for_message(this, user, message);
|
||||
});
|
||||
|
||||
$("#main_div").on("click", ".user-group-mention", function (e) {
|
||||
const user_group_id = Number.parseInt($(this).attr("data-user-group-id"), 10);
|
||||
const $row = $(this).closest(".message_row");
|
||||
e.stopPropagation();
|
||||
const message = message_lists.current.get(rows.id($row));
|
||||
try {
|
||||
const group = user_groups.get_user_group_from_id(user_group_id);
|
||||
show_user_group_info_popover(this, group, message);
|
||||
} catch {
|
||||
// This user group has likely been deleted.
|
||||
blueslip.info("Unable to find user group in message" + message.sender_id);
|
||||
}
|
||||
});
|
||||
|
||||
$("#main_div, #preview_content, #message-history").on(
|
||||
"click",
|
||||
".code_external_link",
|
||||
|
@ -1066,6 +976,7 @@ export function any_active() {
|
|||
// Expanded sidebars on mobile view count as popovers as well.
|
||||
return (
|
||||
popover_menus.any_active() ||
|
||||
user_group_popover.is_open() ||
|
||||
user_sidebar_popped() ||
|
||||
stream_popover.stream_popped() ||
|
||||
message_info_popped() ||
|
||||
|
@ -1086,6 +997,7 @@ export function hide_all_except_sidebars(opts) {
|
|||
}
|
||||
emoji_picker.hide_emoji_popover();
|
||||
stream_popover.hide_stream_popover();
|
||||
user_group_popover.hide();
|
||||
hide_all_user_info_popovers();
|
||||
hide_playground_links_popover();
|
||||
|
||||
|
|
|
@ -117,6 +117,7 @@ import * as unread_ui from "./unread_ui";
|
|||
import * as upload from "./upload";
|
||||
import * as user_group_edit from "./user_group_edit";
|
||||
import * as user_group_edit_members from "./user_group_edit_members";
|
||||
import * as user_group_popover from "./user_group_popover";
|
||||
import * as user_groups from "./user_groups";
|
||||
import * as user_group_settings_ui from "./user_groups_settings_ui";
|
||||
import {initialize_user_settings, user_settings} from "./user_settings";
|
||||
|
@ -761,6 +762,7 @@ export function initialize_everything() {
|
|||
initialize_unread_ui();
|
||||
activity.initialize();
|
||||
emoji_picker.initialize();
|
||||
user_group_popover.initialize();
|
||||
pm_list.initialize();
|
||||
topic_list.initialize({
|
||||
on_topic_click(stream_id, topic) {
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
import $ from "jquery";
|
||||
|
||||
import render_user_group_info_popover from "../templates/user_group_info_popover.hbs";
|
||||
import render_user_group_info_popover_content from "../templates/user_group_info_popover_content.hbs";
|
||||
|
||||
import * as blueslip from "./blueslip";
|
||||
import * as buddy_data from "./buddy_data";
|
||||
import * as message_lists from "./message_lists";
|
||||
import * as message_viewport from "./message_viewport";
|
||||
import * as people from "./people";
|
||||
import {hide_all, popover_items_handle_keyboard} from "./popovers";
|
||||
import * as rows from "./rows";
|
||||
import * as user_groups from "./user_groups";
|
||||
import * as util from "./util";
|
||||
|
||||
let $current_user_group_popover_elem;
|
||||
|
||||
export function hide() {
|
||||
if (is_open()) {
|
||||
$current_user_group_popover_elem.popover("destroy");
|
||||
$current_user_group_popover_elem = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function is_open() {
|
||||
return $current_user_group_popover_elem !== undefined;
|
||||
}
|
||||
|
||||
function get_user_group_popover_items() {
|
||||
if (!$current_user_group_popover_elem) {
|
||||
blueslip.error("Trying to get menu items when action popover is closed.");
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const popover_data = $current_user_group_popover_elem.data("popover");
|
||||
if (!popover_data) {
|
||||
blueslip.error("Cannot find popover data for actions menu.");
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return $("li:not(.divider):visible a", popover_data.$tip);
|
||||
}
|
||||
|
||||
export function handle_keyboard(key) {
|
||||
const $items = get_user_group_popover_items();
|
||||
popover_items_handle_keyboard(key, $items);
|
||||
}
|
||||
|
||||
// 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
|
||||
export function show_user_group_info_popover(element, group, message) {
|
||||
const $last_popover_elem = $current_user_group_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
|
||||
const popover_size = 390;
|
||||
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;
|
||||
}
|
||||
message_lists.current.select_id(message.id);
|
||||
const $elt = $(element);
|
||||
if ($elt.data("popover") === undefined) {
|
||||
const 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: render_user_group_info_popover({class: "message-info-popover"}),
|
||||
content: render_user_group_info_popover_content(args),
|
||||
html: true,
|
||||
trigger: "manual",
|
||||
fixed: true,
|
||||
});
|
||||
$elt.popover("show");
|
||||
$current_user_group_popover_elem = $elt;
|
||||
}
|
||||
}
|
||||
|
||||
export function register_click_handlers() {
|
||||
$("#main_div").on("click", ".user-group-mention", function (e) {
|
||||
const user_group_id = Number.parseInt($(this).attr("data-user-group-id"), 10);
|
||||
const $row = $(this).closest(".message_row");
|
||||
e.stopPropagation();
|
||||
const message = message_lists.current.get(rows.id($row));
|
||||
try {
|
||||
const group = user_groups.get_user_group_from_id(user_group_id);
|
||||
show_user_group_info_popover(this, group, message);
|
||||
} catch {
|
||||
// This user group has likely been deleted.
|
||||
blueslip.info("Unable to find user group in message" + message.sender_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function fetch_group_members(member_ids) {
|
||||
return member_ids
|
||||
.map((m) => people.maybe_get_user_by_id(m))
|
||||
.filter((m) => m !== undefined)
|
||||
.map((p) => ({
|
||||
...p,
|
||||
user_circle_class: buddy_data.get_user_circle_class(p.user_id),
|
||||
is_active: people.is_active_user_for_popover(p.user_id),
|
||||
user_last_seen_time_status: buddy_data.user_last_seen_time_status(p.user_id),
|
||||
}));
|
||||
}
|
||||
|
||||
function sort_group_members(members) {
|
||||
return members.sort((a, b) => util.strcmp(a.full_name, b.fullname));
|
||||
}
|
||||
|
||||
function calculate_info_popover_placement(size, $elt) {
|
||||
const ypos = $elt.get_offset_to_window().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";
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// exporting these functions for testing purposes
|
||||
export const _test_fetch_group_members = fetch_group_members;
|
||||
|
||||
export const _test_sort_group_members = sort_group_members;
|
||||
|
||||
export const _test_calculate_info_popover_placement = calculate_info_popover_placement;
|
||||
|
||||
export function initialize() {
|
||||
register_click_handlers();
|
||||
}
|
Loading…
Reference in New Issue