zulip/web/src/user_group_popover.ts

189 lines
6.5 KiB
TypeScript

import $ from "jquery";
import assert from "minimalistic-assert";
import type * as tippy from "tippy.js";
import render_user_group_info_popover from "../templates/popovers/user_group_info_popover.hbs";
import * as blueslip from "./blueslip";
import * as buddy_data from "./buddy_data";
import * as hash_util from "./hash_util";
import * as message_lists from "./message_lists";
import * as people from "./people";
import type {User} from "./people";
import * as popover_menus from "./popover_menus";
import * as rows from "./rows";
import {current_user} from "./state_data";
import * as ui_util from "./ui_util";
import * as user_groups from "./user_groups";
import * as util from "./util";
let user_group_popover_instance: tippy.Instance | undefined;
type PopoverGroupMember = User & {user_circle_class: string; user_last_seen_time_status: string};
export function hide(): void {
if (user_group_popover_instance !== undefined) {
user_group_popover_instance.destroy();
user_group_popover_instance = undefined;
}
}
export function is_open(): boolean {
return Boolean(user_group_popover_instance);
}
function get_user_group_popover_items(): JQuery | undefined {
if (user_group_popover_instance === undefined) {
blueslip.error("Trying to get menu items when user group popover is closed.");
return undefined;
}
const $popover = $(user_group_popover_instance.popper);
if (!$popover) {
blueslip.error("Cannot find user group popover data");
return undefined;
}
return $("li:not(.divider):visible a", $popover);
}
export function handle_keyboard(key: string): void {
const $items = get_user_group_popover_items();
popover_menus.popover_items_handle_keyboard(key, $items);
}
// element is the target element to pop off of;
// the element could be user group pill or mentions in a message;
// in case of message, message_id is the message id containing it;
// in case of user group pill, message_id is not used;
export function toggle_user_group_info_popover(
element: tippy.ReferenceElement,
message_id: number | undefined,
): void {
if (is_open()) {
hide();
return;
}
const $elt = $(element);
const user_group_id_str = $elt.attr("data-user-group-id");
assert(user_group_id_str !== undefined);
const user_group_id = Number.parseInt(user_group_id_str, 10);
const group = user_groups.get_user_group_from_id(user_group_id);
popover_menus.toggle_popover_menu(
element,
{
theme: "popover-menu",
placement: "right",
popperOptions: {
modifiers: [
{
name: "flip",
options: {
fallbackPlacements: ["left", "top", "bottom"],
},
},
],
},
onCreate(instance) {
if (message_id) {
assert(message_lists.current !== undefined);
message_lists.current.select_id(message_id);
}
user_group_popover_instance = instance;
const args = {
group_name: user_groups.get_display_group_name(group.name),
group_description: group.description,
members: sort_group_members(
fetch_group_members([...user_groups.get_recursive_group_members(group)]),
),
group_edit_url: hash_util.group_edit_url(group, "general"),
is_guest: current_user.is_guest,
is_system_group: group.is_system_group,
deactivated: group.deactivated,
};
instance.setContent(ui_util.parse_html(render_user_group_info_popover(args)));
},
onHidden() {
hide();
},
},
{
show_as_overlay_on_mobile: true,
show_as_overlay_always: false,
},
);
}
export function register_click_handlers(): void {
$("#main_div").on("click", ".user-group-mention", function (this: HTMLElement, e) {
e.stopPropagation();
const $elt = $(this);
const $row = $elt.closest(".message_row");
const message_id = rows.id($row);
assert(message_lists.current !== undefined);
const message = message_lists.current.get(message_id);
assert(message !== undefined);
try {
toggle_user_group_info_popover(this, message.id);
} catch {
// This user group has likely been deleted.
blueslip.info("Unable to find user group in message" + message.sender_id);
}
});
// Show the user_group_popover when pill clicked in subscriber settings.
$("body").on(
"click",
".person_picker .pill[data-user-group-id]",
function (this: HTMLElement, e) {
e.stopPropagation();
toggle_user_group_info_popover(this, undefined);
},
);
// Note: Message feeds and drafts have their own direct event listeners
// that run before this one and call stopPropagation.
$("body").on("click", ".messagebox .user-group-mention", function (this: HTMLElement, e) {
e.stopPropagation();
toggle_user_group_info_popover(this, undefined);
});
}
function fetch_group_members(member_ids: number[]): PopoverGroupMember[] {
return (
member_ids
.map((m: number) => people.get_user_by_id_assert_valid(m))
// We need to include inaccessible users here separately, since
// we do not include them in active_user_dict, but we want to
// show them in the popover as "Unknown user".
.filter(
(m: User) => people.is_active_user_for_popover(m.user_id) || m.is_inaccessible_user,
)
.map((p: User) => ({
...p,
user_circle_class: buddy_data.get_user_circle_class(p.user_id),
user_last_seen_time_status: buddy_data.user_last_seen_time_status(p.user_id),
}))
);
}
function sort_group_members(members: PopoverGroupMember[]): PopoverGroupMember[] {
return members.sort((a: PopoverGroupMember, b: PopoverGroupMember) =>
util.strcmp(a.full_name, b.full_name),
);
}
// 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 function initialize(): void {
register_click_handlers();
}