import $ from "jquery"; import render_leave_user_group_modal from "../templates/confirm_dialog/confirm_unsubscribe_private_stream.hbs"; import render_user_group_member_list_entry from "../templates/stream_settings/stream_member_list_entry.hbs"; import render_user_group_subscription_request_result from "../templates/stream_settings/stream_subscription_request_result.hbs"; import * as add_subscribers_pill from "./add_subscribers_pill"; import * as blueslip from "./blueslip"; import * as channel from "./channel"; import * as confirm_dialog from "./confirm_dialog"; import * as hash_util from "./hash_util"; import {$t, $t_html} from "./i18n"; import * as ListWidget from "./list_widget"; import {page_params} from "./page_params"; import * as people from "./people"; import * as ui from "./ui"; import * as user_group_edit from "./user_group_edit"; import * as user_groups from "./user_groups"; export let pill_widget; let current_group_id; let member_list_widget; function get_potential_members() { const group = user_groups.get_user_group_from_id(current_group_id); function is_potential_member(person) { // user verbose style filter to have room // to add more potential checks easily. if (group.members.has(person.user_id)) { return false; } return true; } return people.filter_all_users(is_potential_member); } export function update_member_list_widget(group_id, member_ids) { if (!hash_util.is_editing_group(group_id)) { return; } const users = people.get_users_from_ids(member_ids); people.sort_but_pin_current_user_on_top(users); member_list_widget.replace_list_data(users); } function format_member_list_elem(person) { return render_user_group_member_list_entry({ name: person.full_name, user_id: person.user_id, is_current_user: person.user_id === page_params.user_id, email: person.delivery_email, can_edit_subscribers: user_group_edit.can_edit(current_group_id), }); } function make_list_widget({$parent_container, name, user_ids}) { const users = people.get_users_from_ids(user_ids); people.sort_but_pin_current_user_on_top(users); const $list_container = $parent_container.find(".member_table"); $list_container.empty(); const $simplebar_container = $parent_container.find(".member_list_container"); return ListWidget.create($list_container, users, { name, modifier(item) { return format_member_list_elem(item); }, filter: { $element: $parent_container.find(".search"), predicate(person, value) { const matcher = people.build_person_matcher(value); const match = matcher(person); return match; }, }, $simplebar_container, }); } export function enable_member_management({group, $parent_container}) { const group_id = group.id; const $pill_container = $parent_container.find(".pill-container"); // current_group_id and pill_widget are module-level variables current_group_id = group_id; pill_widget = add_subscribers_pill.create({ $pill_container, get_potential_subscribers: get_potential_members, }); member_list_widget = make_list_widget({ $parent_container, name: "user_group_members", user_ids: Array.from(group.members), }); } function show_user_group_membership_request_result({ message, add_class, remove_class, subscribed_users, already_subscribed_users, ignored_deactivated_users, }) { const $user_group_subscription_req_result_elem = $( ".user_group_subscription_request_result", ).expectOne(); const html = render_user_group_subscription_request_result({ message, subscribed_users, already_subscribed_users, ignored_deactivated_users, }); ui.get_content_element($user_group_subscription_req_result_elem).html(html); if (add_class) { $user_group_subscription_req_result_elem.addClass(add_class); } if (remove_class) { $user_group_subscription_req_result_elem.removeClass(remove_class); } } function edit_user_group_membership({group, added = [], removed = [], success, error}) { channel.post({ url: "/json/user_groups/" + group.id + "/members", data: { add: JSON.stringify(added), delete: JSON.stringify(removed), }, success, error, }); } function add_new_members({pill_user_ids}) { const group = user_groups.get_user_group_from_id(current_group_id); if (!group) { return; } const deactivated_users = new Set(); const already_added_users = new Set(); const active_user_ids = pill_user_ids.filter((user_id) => { if (!people.is_person_active(user_id)) { deactivated_users.add(user_id); return false; } if (user_groups.is_user_in_group(group.id, user_id)) { // we filter out already subscribed users before sending // add member request as the endpoint is not so robust and // fails complete request if any already subscribed member // is present in the request. already_added_users.add(user_id); return false; } return true; }); const user_id_set = new Set(active_user_ids); if ( user_id_set.has(page_params.user_id) && user_groups.is_user_in_group(group.id, page_params.user_id) ) { // We don't want to send a request to add ourselves if we // are already added to this group. This case occurs // when creating user pills from a stream or user group. user_id_set.delete(page_params.user_id); } let ignored_deactivated_users; let ignored_already_added_users; if (deactivated_users.size > 0) { ignored_deactivated_users = Array.from(deactivated_users); ignored_deactivated_users = ignored_deactivated_users.map((user_id) => people.get_by_user_id(user_id), ); } if (already_added_users.size > 0) { ignored_already_added_users = Array.from(already_added_users); ignored_already_added_users = ignored_already_added_users.map((user_id) => people.get_by_user_id(user_id), ); } if (user_id_set.size === 0) { show_user_group_membership_request_result({ message: $t({defaultMessage: "No user to subscribe."}), add_class: "text-error", remove_class: "text-success", already_subscribed_users: ignored_already_added_users, ignored_deactivated_users, }); return; } const user_ids = Array.from(user_id_set); function invite_success() { pill_widget.clear(); show_user_group_membership_request_result({ message: $t({defaultMessage: "Added successfully."}), add_class: "text-success", remove_class: "text-error", already_subscribed_users: ignored_already_added_users, ignored_deactivated_users, }); } function invite_failure(xhr) { const error = JSON.parse(xhr.responseText); show_user_group_membership_request_result({ message: error.msg, add_class: "text-error", remove_class: "text-success", }); } edit_user_group_membership({ group, added: user_ids, success: invite_success, error: invite_failure, }); } function remove_member({group_id, target_user_id, $list_entry}) { const group = user_groups.get_user_group_from_id(current_group_id); if (!group) { return; } function removal_success() { if (group_id !== current_group_id) { blueslip.info("Response for subscription removal came too late."); return; } $list_entry.remove(); const message = $t({defaultMessage: "Removed successfully."}); show_user_group_membership_request_result({ message, add_class: "text-success", remove_class: "text-remove", }); } function removal_failure() { show_user_group_membership_request_result({ message: $t({defaultMessage: "Error removing user from this group."}), add_class: "text-error", remove_class: "text-success", }); } function do_remove_user_from_group() { edit_user_group_membership({ group, removed: [target_user_id], success: removal_success, error: removal_failure, }); } if (people.is_my_user_id(target_user_id) && !page_params.is_admin) { const html_body = render_leave_user_group_modal({ message: $t({ defaultMessage: "Once you leave this group, you will not be able to rejoin.", }), }); confirm_dialog.launch({ html_heading: $t_html({defaultMessage: "Leave {group_name}"}, {group_name: group.name}), html_body, on_click: do_remove_user_from_group, }); return; } do_remove_user_from_group(); } export function initialize() { add_subscribers_pill.set_up_handlers({ get_pill_widget: () => pill_widget, $parent_container: $("#manage_groups_container"), pill_selector: ".edit_members_for_user_group .pill-container", button_selector: ".edit_members_for_user_group .add-subscriber-button", action: add_new_members, }); $("#manage_groups_container").on( "submit", ".edit_members_for_user_group .subscriber_list_remove form", (e) => { e.preventDefault(); const $list_entry = $(e.target).closest("tr"); const target_user_id = Number.parseInt($list_entry.attr("data-subscriber-id"), 10); const group_id = current_group_id; remove_member({group_id, target_user_id, $list_entry}); }, ); }