mirror of
https://github.com/zulip/zulip.git
synced 2025-11-06 06:53:25 +00:00
stream edit: Extract stream_subscribers_ui.js.
This is a fairly straightforward extraction. It's good to test this with Iago, and then go into Manage Streams and add/remove subscribers for a stream like devel. I copy/pasted two small functions that will soon diverge from stream_edit. The get_stream_id function will either use a module variable (since we're generally only editing subscribers for one stream, and we already have the singleton assumption with `input_pill`) or a more strict CSS selector. And then get_sub_for_target depends on get_stream_id. We may not always need full subs, anyway, and when we adapt some of this code for creating streams, things are likely to change. I stopped exporting a couple functions that have no callers outside of this module. The main entry point for the module is enable_subscriber_management. We continue to export invite_user_to_stream and remove_user_from_stream, which should possibly be just pulled into their own module to lessen some dependencies, but they don't have too much baggage, since they just wrap channel calls.
This commit is contained in:
@@ -29,8 +29,8 @@ const rendered_markdown = mock_esm("../../static/js/rendered_markdown");
|
||||
const resize = mock_esm("../../static/js/resize");
|
||||
const sent_messages = mock_esm("../../static/js/sent_messages");
|
||||
const server_events = mock_esm("../../static/js/server_events");
|
||||
const stream_edit = mock_esm("../../static/js/stream_edit");
|
||||
const stream_settings_ui = mock_esm("../../static/js/stream_settings_ui");
|
||||
const stream_subscribers_ui = mock_esm("../../static/js/stream_subscribers_ui");
|
||||
const transmit = mock_esm("../../static/js/transmit");
|
||||
|
||||
const compose_closed_ui = zrequire("compose_closed_ui");
|
||||
@@ -625,7 +625,7 @@ test_ui("on_events", ({override}) => {
|
||||
people.add_active_user(mentioned);
|
||||
|
||||
let invite_user_to_stream_called = false;
|
||||
override(stream_edit, "invite_user_to_stream", (user_ids, sub, success) => {
|
||||
override(stream_subscribers_ui, "invite_user_to_stream", (user_ids, sub, success) => {
|
||||
invite_user_to_stream_called = true;
|
||||
assert.deepEqual(user_ids, [mentioned.user_id]);
|
||||
assert.equal(sub, subscription);
|
||||
|
||||
@@ -40,6 +40,7 @@ const stream_pill = zrequire("stream_pill");
|
||||
const user_groups = zrequire("user_groups");
|
||||
const user_group_pill = zrequire("user_group_pill");
|
||||
const user_pill = zrequire("user_pill");
|
||||
const stream_subscribers_ui = zrequire("stream_subscribers_ui");
|
||||
const stream_ui_updates = zrequire("stream_ui_updates");
|
||||
const settings_config = zrequire("settings_config");
|
||||
|
||||
@@ -110,6 +111,7 @@ for (const sub of subs) {
|
||||
function test_ui(label, f) {
|
||||
run_test(label, ({override, mock_template}) => {
|
||||
page_params.user_id = me.user_id;
|
||||
stream_subscribers_ui.initialize();
|
||||
stream_edit.initialize();
|
||||
f({override, mock_template});
|
||||
});
|
||||
@@ -180,7 +182,7 @@ test_ui("subscriber_pills", ({override, mock_template}) => {
|
||||
let expected_user_ids = [];
|
||||
let input_typeahead_called = false;
|
||||
let add_subscribers_request = false;
|
||||
override(stream_edit, "invite_user_to_stream", (user_ids, sub) => {
|
||||
override(stream_subscribers_ui, "invite_user_to_stream", (user_ids, sub) => {
|
||||
assert.equal(sub.stream_id, denmark.stream_id);
|
||||
assert.deepEqual(user_ids.sort(), expected_user_ids.sort());
|
||||
add_subscribers_request = true;
|
||||
@@ -266,7 +268,7 @@ test_ui("subscriber_pills", ({override, mock_template}) => {
|
||||
|
||||
(function test_updater() {
|
||||
function number_of_pills() {
|
||||
const pills = stream_edit.pill_widget.items();
|
||||
const pills = stream_subscribers_ui.pill_widget.items();
|
||||
return pills.length;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@ import * as rows from "./rows";
|
||||
import * as sent_messages from "./sent_messages";
|
||||
import * as server_events from "./server_events";
|
||||
import * as stream_data from "./stream_data";
|
||||
import * as stream_edit from "./stream_edit";
|
||||
import * as stream_settings_ui from "./stream_settings_ui";
|
||||
import * as stream_subscribers_ui from "./stream_subscribers_ui";
|
||||
import * as sub_store from "./sub_store";
|
||||
import * as transmit from "./transmit";
|
||||
import * as ui_report from "./ui_report";
|
||||
@@ -495,7 +495,7 @@ export function initialize() {
|
||||
|
||||
const sub = sub_store.get(stream_id);
|
||||
|
||||
stream_edit.invite_user_to_stream([user_id], sub, success, xhr_failure);
|
||||
stream_subscribers_ui.invite_user_to_stream([user_id], sub, success, xhr_failure);
|
||||
});
|
||||
|
||||
$("#compose_invite_users").on("click", ".compose_invite_close", (event) => {
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import $ from "jquery";
|
||||
|
||||
import render_settings_deactivation_stream_modal from "../templates/confirm_dialog/confirm_deactivate_stream.hbs";
|
||||
import render_unsubscribe_private_stream_modal from "../templates/confirm_dialog/confirm_unsubscribe_private_stream.hbs";
|
||||
import render_change_stream_info_modal from "../templates/stream_settings/change_stream_info_modal.hbs";
|
||||
import render_stream_description from "../templates/stream_settings/stream_description.hbs";
|
||||
import render_stream_member_list_entry from "../templates/stream_settings/stream_member_list_entry.hbs";
|
||||
import render_stream_settings from "../templates/stream_settings/stream_settings.hbs";
|
||||
import render_stream_subscription_request_result from "../templates/stream_settings/stream_subscription_request_result.hbs";
|
||||
import render_stream_types from "../templates/stream_settings/stream_types.hbs";
|
||||
|
||||
import * as blueslip from "./blueslip";
|
||||
@@ -17,32 +14,23 @@ import * as confirm_dialog from "./confirm_dialog";
|
||||
import * as dialog_widget from "./dialog_widget";
|
||||
import * as hash_util from "./hash_util";
|
||||
import {$t, $t_html} from "./i18n";
|
||||
import * as input_pill from "./input_pill";
|
||||
import * as ListWidget from "./list_widget";
|
||||
import * as narrow_state from "./narrow_state";
|
||||
import {page_params} from "./page_params";
|
||||
import * as peer_data from "./peer_data";
|
||||
import * as people from "./people";
|
||||
import * as pill_typeahead from "./pill_typeahead";
|
||||
import * as settings_config from "./settings_config";
|
||||
import * as settings_data from "./settings_data";
|
||||
import * as settings_ui from "./settings_ui";
|
||||
import * as stream_color from "./stream_color";
|
||||
import * as stream_data from "./stream_data";
|
||||
import * as stream_pill from "./stream_pill";
|
||||
import * as stream_settings_containers from "./stream_settings_containers";
|
||||
import * as stream_settings_data from "./stream_settings_data";
|
||||
import * as stream_settings_ui from "./stream_settings_ui";
|
||||
import * as stream_subscribers_ui from "./stream_subscribers_ui";
|
||||
import * as stream_ui_updates from "./stream_ui_updates";
|
||||
import * as sub_store from "./sub_store";
|
||||
import * as ui from "./ui";
|
||||
import * as ui_report from "./ui_report";
|
||||
import * as user_group_pill from "./user_group_pill";
|
||||
import * as user_pill from "./user_pill";
|
||||
import {user_settings} from "./user_settings";
|
||||
import * as util from "./util";
|
||||
|
||||
export let pill_widget;
|
||||
export let toggler;
|
||||
export let select_tab = "personal_settings";
|
||||
|
||||
@@ -175,16 +163,6 @@ export function open_edit_panel_empty() {
|
||||
setup_subscriptions_tab_hash(tab_key);
|
||||
}
|
||||
|
||||
function format_member_list_elem(person) {
|
||||
return render_stream_member_list_entry({
|
||||
name: person.full_name,
|
||||
user_id: person.user_id,
|
||||
email: settings_data.email_for_user_settings(person),
|
||||
displaying_for_admin: page_params.is_admin,
|
||||
show_email: settings_data.show_email(),
|
||||
});
|
||||
}
|
||||
|
||||
export function update_stream_name(sub, new_name) {
|
||||
const edit_container = stream_settings_containers.get_edit_container(sub);
|
||||
edit_container.find(".email-address").text(sub.email_address);
|
||||
@@ -200,162 +178,6 @@ export function update_stream_description(sub) {
|
||||
edit_container.find(".stream-description").html(html);
|
||||
}
|
||||
|
||||
export function invite_user_to_stream(user_ids, sub, success, failure) {
|
||||
// TODO: use stream_id when backend supports it
|
||||
const stream_name = sub.name;
|
||||
return channel.post({
|
||||
url: "/json/users/me/subscriptions",
|
||||
data: {
|
||||
subscriptions: JSON.stringify([{name: stream_name}]),
|
||||
principals: JSON.stringify(user_ids),
|
||||
},
|
||||
success,
|
||||
error: failure,
|
||||
});
|
||||
}
|
||||
|
||||
function show_stream_subscription_request_result({
|
||||
message,
|
||||
add_class,
|
||||
remove_class,
|
||||
subscribed_users,
|
||||
already_subscribed_users,
|
||||
ignored_deactivated_users,
|
||||
}) {
|
||||
const stream_subscription_req_result_elem = $(
|
||||
".stream_subscription_request_result",
|
||||
).expectOne();
|
||||
const html = render_stream_subscription_request_result({
|
||||
message,
|
||||
subscribed_users,
|
||||
already_subscribed_users,
|
||||
ignored_deactivated_users,
|
||||
});
|
||||
ui.get_content_element(stream_subscription_req_result_elem).html(html);
|
||||
if (add_class) {
|
||||
stream_subscription_req_result_elem.addClass(add_class);
|
||||
}
|
||||
if (remove_class) {
|
||||
stream_subscription_req_result_elem.removeClass(remove_class);
|
||||
}
|
||||
}
|
||||
|
||||
function submit_add_subscriber_form(e) {
|
||||
const sub = get_sub_for_target(e.target);
|
||||
if (!sub) {
|
||||
blueslip.error(".subscriber_list_add form submit fails");
|
||||
return;
|
||||
}
|
||||
|
||||
let user_ids = user_pill.get_user_ids(pill_widget);
|
||||
user_ids = user_ids.concat(stream_pill.get_user_ids(pill_widget));
|
||||
user_ids = user_ids.concat(user_group_pill.get_user_ids(pill_widget));
|
||||
const deactivated_users = new Set();
|
||||
user_ids = user_ids.filter((user_id) => {
|
||||
if (!people.is_person_active(user_id)) {
|
||||
deactivated_users.add(user_id);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
user_ids = new Set(user_ids);
|
||||
|
||||
if (user_ids.has(page_params.user_id) && sub.subscribed) {
|
||||
// We don't want to send a request to subscribe ourselves
|
||||
// if we are already subscribed to this stream. This
|
||||
// case occurs when creating user pills from a stream.
|
||||
user_ids.delete(page_params.user_id);
|
||||
}
|
||||
let ignored_deactivated_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 (user_ids.size === 0) {
|
||||
show_stream_subscription_request_result({
|
||||
message: $t({defaultMessage: "No user to subscribe."}),
|
||||
add_class: "text-error",
|
||||
remove_class: "text-success",
|
||||
ignored_deactivated_users,
|
||||
});
|
||||
return;
|
||||
}
|
||||
user_ids = Array.from(user_ids);
|
||||
|
||||
function invite_success(data) {
|
||||
pill_widget.clear();
|
||||
const subscribed_users = Object.keys(data.subscribed).map((email) =>
|
||||
people.get_by_email(email),
|
||||
);
|
||||
const already_subscribed_users = Object.keys(data.already_subscribed).map((email) =>
|
||||
people.get_by_email(email),
|
||||
);
|
||||
|
||||
show_stream_subscription_request_result({
|
||||
add_class: "text-success",
|
||||
remove_class: "text-error",
|
||||
subscribed_users,
|
||||
already_subscribed_users,
|
||||
ignored_deactivated_users,
|
||||
});
|
||||
}
|
||||
|
||||
function invite_failure(xhr) {
|
||||
const error = JSON.parse(xhr.responseText);
|
||||
show_stream_subscription_request_result({
|
||||
message: error.msg,
|
||||
add_class: "text-error",
|
||||
remove_class: "text-success",
|
||||
});
|
||||
}
|
||||
|
||||
invite_user_to_stream(user_ids, sub, invite_success, invite_failure);
|
||||
}
|
||||
|
||||
export function remove_user_from_stream(user_id, sub, success, failure) {
|
||||
// TODO: use stream_id when backend supports it
|
||||
const stream_name = sub.name;
|
||||
return channel.del({
|
||||
url: "/json/users/me/subscriptions",
|
||||
data: {subscriptions: JSON.stringify([stream_name]), principals: JSON.stringify([user_id])},
|
||||
success,
|
||||
error: failure,
|
||||
});
|
||||
}
|
||||
|
||||
export function create_item_from_text(text, current_items) {
|
||||
const funcs = [
|
||||
stream_pill.create_item_from_stream_name,
|
||||
user_group_pill.create_item_from_group_name,
|
||||
user_pill.create_item_from_email,
|
||||
];
|
||||
for (const func of funcs) {
|
||||
const item = func(text, current_items);
|
||||
if (item) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function get_text_from_item(item) {
|
||||
const funcs = [
|
||||
stream_pill.get_stream_name_from_item,
|
||||
user_group_pill.get_group_name_from_item,
|
||||
user_pill.get_email_from_item,
|
||||
];
|
||||
for (const func of funcs) {
|
||||
const text = func(item);
|
||||
if (text) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function show_subscription_settings(sub) {
|
||||
const edit_container = stream_settings_containers.get_edit_container(sub);
|
||||
|
||||
@@ -372,58 +194,7 @@ function show_subscription_settings(sub) {
|
||||
stream_ui_updates.initialize_cant_subscribe_popover(sub);
|
||||
}
|
||||
|
||||
enable_subscriber_management({sub, parent_container: edit_container});
|
||||
}
|
||||
|
||||
function enable_subscriber_management({sub, parent_container}) {
|
||||
const stream_id = sub.stream_id;
|
||||
|
||||
const pill_container = parent_container.find(".pill-container");
|
||||
|
||||
pill_widget = input_pill.create({
|
||||
container: pill_container,
|
||||
create_item_from_text,
|
||||
get_text_from_item,
|
||||
});
|
||||
|
||||
const user_ids = peer_data.get_subscribers(stream_id);
|
||||
const users = people.get_users_from_ids(user_ids);
|
||||
people.sort_but_pin_current_user_on_top(users);
|
||||
|
||||
function get_users_for_subscriber_typeahead() {
|
||||
const potential_subscribers = peer_data.potential_subscribers(stream_id);
|
||||
return user_pill.filter_taken_users(potential_subscribers, pill_widget);
|
||||
}
|
||||
|
||||
const list_container = parent_container.find(".subscriber_table");
|
||||
list_container.empty();
|
||||
|
||||
const simplebar_container = parent_container.find(".subscriber_list_container");
|
||||
|
||||
ListWidget.create(list_container, users, {
|
||||
name: "stream_subscribers/" + stream_id,
|
||||
modifier(item) {
|
||||
return format_member_list_elem(item);
|
||||
},
|
||||
filter: {
|
||||
element: $(`[data-stream-id='${CSS.escape(stream_id)}'] .search`),
|
||||
predicate(person, value) {
|
||||
const matcher = people.build_person_matcher(value);
|
||||
const match = matcher(person);
|
||||
|
||||
return match;
|
||||
},
|
||||
},
|
||||
simplebar_container,
|
||||
});
|
||||
|
||||
const opts = {
|
||||
user_source: get_users_for_subscriber_typeahead,
|
||||
stream: true,
|
||||
user_group: true,
|
||||
user: true,
|
||||
};
|
||||
pill_typeahead.set_up(pill_container.find(".input"), pill_widget, opts);
|
||||
stream_subscribers_ui.enable_subscriber_management({sub, parent_container: edit_container});
|
||||
}
|
||||
|
||||
export function is_notification_setting(setting_label) {
|
||||
@@ -837,76 +608,6 @@ export function initialize() {
|
||||
stream_setting_changed,
|
||||
);
|
||||
|
||||
$("#subscriptions_table").on("keyup", ".subscriber_list_add form", (e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
submit_add_subscriber_form(e);
|
||||
}
|
||||
});
|
||||
|
||||
$("#subscriptions_table").on("submit", ".subscriber_list_add form", (e) => {
|
||||
e.preventDefault();
|
||||
submit_add_subscriber_form(e);
|
||||
});
|
||||
|
||||
$("#subscriptions_table").on("submit", ".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 sub = get_sub_for_target(e.target);
|
||||
if (!sub) {
|
||||
blueslip.error(".subscriber_list_remove form submit fails");
|
||||
return;
|
||||
}
|
||||
let message;
|
||||
|
||||
function removal_success(data) {
|
||||
if (data.removed.length > 0) {
|
||||
// Remove the user from the subscriber list.
|
||||
list_entry.remove();
|
||||
message = $t({defaultMessage: "Unsubscribed successfully!"});
|
||||
// The rest of the work is done via the subscription -> remove event we will get
|
||||
} else {
|
||||
message = $t({defaultMessage: "User is already not subscribed."});
|
||||
}
|
||||
show_stream_subscription_request_result({
|
||||
message,
|
||||
add_class: "text-success",
|
||||
remove_class: "text-remove",
|
||||
});
|
||||
}
|
||||
|
||||
function removal_failure() {
|
||||
show_stream_subscription_request_result({
|
||||
message: $t({defaultMessage: "Error removing user from this stream."}),
|
||||
add_class: "text-error",
|
||||
remove_class: "text-success",
|
||||
});
|
||||
}
|
||||
|
||||
function remove_user_from_private_stream() {
|
||||
remove_user_from_stream(target_user_id, sub, removal_success, removal_failure);
|
||||
}
|
||||
|
||||
if (sub.invite_only && people.is_my_user_id(target_user_id)) {
|
||||
const html_body = render_unsubscribe_private_stream_modal();
|
||||
|
||||
confirm_dialog.launch({
|
||||
html_heading: $t_html(
|
||||
{defaultMessage: "Unsubscribe from {stream_name}"},
|
||||
{stream_name: sub.name},
|
||||
),
|
||||
html_body,
|
||||
on_click: remove_user_from_private_stream,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
remove_user_from_stream(target_user_id, sub, removal_success, removal_failure);
|
||||
});
|
||||
|
||||
// This handler isn't part of the normal edit interface; it's the convenient
|
||||
// checkmark in the subscriber list.
|
||||
$("#subscriptions_table").on("click", ".sub_unsub_button", (e) => {
|
||||
|
||||
336
static/js/stream_subscribers_ui.js
Normal file
336
static/js/stream_subscribers_ui.js
Normal file
@@ -0,0 +1,336 @@
|
||||
import $ from "jquery";
|
||||
|
||||
import render_unsubscribe_private_stream_modal from "../templates/confirm_dialog/confirm_unsubscribe_private_stream.hbs";
|
||||
import render_stream_member_list_entry from "../templates/stream_settings/stream_member_list_entry.hbs";
|
||||
import render_stream_subscription_request_result from "../templates/stream_settings/stream_subscription_request_result.hbs";
|
||||
|
||||
import * as blueslip from "./blueslip";
|
||||
import * as channel from "./channel";
|
||||
import * as confirm_dialog from "./confirm_dialog";
|
||||
import {$t, $t_html} from "./i18n";
|
||||
import * as input_pill from "./input_pill";
|
||||
import * as ListWidget from "./list_widget";
|
||||
import {page_params} from "./page_params";
|
||||
import * as peer_data from "./peer_data";
|
||||
import * as people from "./people";
|
||||
import * as pill_typeahead from "./pill_typeahead";
|
||||
import * as settings_data from "./settings_data";
|
||||
import * as stream_pill from "./stream_pill";
|
||||
import * as sub_store from "./sub_store";
|
||||
import * as ui from "./ui";
|
||||
import * as user_group_pill from "./user_group_pill";
|
||||
import * as user_pill from "./user_pill";
|
||||
|
||||
export let pill_widget;
|
||||
|
||||
function create_item_from_text(text, current_items) {
|
||||
const funcs = [
|
||||
stream_pill.create_item_from_stream_name,
|
||||
user_group_pill.create_item_from_group_name,
|
||||
user_pill.create_item_from_email,
|
||||
];
|
||||
for (const func of funcs) {
|
||||
const item = func(text, current_items);
|
||||
if (item) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function get_text_from_item(item) {
|
||||
const funcs = [
|
||||
stream_pill.get_stream_name_from_item,
|
||||
user_group_pill.get_group_name_from_item,
|
||||
user_pill.get_email_from_item,
|
||||
];
|
||||
for (const func of funcs) {
|
||||
const text = func(item);
|
||||
if (text) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function format_member_list_elem(person) {
|
||||
return render_stream_member_list_entry({
|
||||
name: person.full_name,
|
||||
user_id: person.user_id,
|
||||
email: settings_data.email_for_user_settings(person),
|
||||
displaying_for_admin: page_params.is_admin,
|
||||
show_email: settings_data.show_email(),
|
||||
});
|
||||
}
|
||||
|
||||
function get_stream_id(target) {
|
||||
// TODO: Use more precise selector.
|
||||
const row = $(target).closest(
|
||||
".stream-row, .stream_settings_header, .subscription_settings, .save-button",
|
||||
);
|
||||
return Number.parseInt(row.attr("data-stream-id"), 10);
|
||||
}
|
||||
|
||||
function get_sub_for_target(target) {
|
||||
const stream_id = get_stream_id(target);
|
||||
if (!stream_id) {
|
||||
blueslip.error("Cannot find stream id for target");
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const sub = sub_store.get(stream_id);
|
||||
if (!sub) {
|
||||
blueslip.error("get_sub_for_target() failed id lookup: " + stream_id);
|
||||
return undefined;
|
||||
}
|
||||
return sub;
|
||||
}
|
||||
|
||||
function show_stream_subscription_request_result({
|
||||
message,
|
||||
add_class,
|
||||
remove_class,
|
||||
subscribed_users,
|
||||
already_subscribed_users,
|
||||
ignored_deactivated_users,
|
||||
}) {
|
||||
const stream_subscription_req_result_elem = $(
|
||||
".stream_subscription_request_result",
|
||||
).expectOne();
|
||||
const html = render_stream_subscription_request_result({
|
||||
message,
|
||||
subscribed_users,
|
||||
already_subscribed_users,
|
||||
ignored_deactivated_users,
|
||||
});
|
||||
ui.get_content_element(stream_subscription_req_result_elem).html(html);
|
||||
if (add_class) {
|
||||
stream_subscription_req_result_elem.addClass(add_class);
|
||||
}
|
||||
if (remove_class) {
|
||||
stream_subscription_req_result_elem.removeClass(remove_class);
|
||||
}
|
||||
}
|
||||
|
||||
export function enable_subscriber_management({sub, parent_container}) {
|
||||
const stream_id = sub.stream_id;
|
||||
|
||||
const pill_container = parent_container.find(".pill-container");
|
||||
|
||||
pill_widget = input_pill.create({
|
||||
container: pill_container,
|
||||
create_item_from_text,
|
||||
get_text_from_item,
|
||||
});
|
||||
|
||||
const user_ids = peer_data.get_subscribers(stream_id);
|
||||
const users = people.get_users_from_ids(user_ids);
|
||||
people.sort_but_pin_current_user_on_top(users);
|
||||
|
||||
function get_users_for_subscriber_typeahead() {
|
||||
const potential_subscribers = peer_data.potential_subscribers(stream_id);
|
||||
return user_pill.filter_taken_users(potential_subscribers, pill_widget);
|
||||
}
|
||||
|
||||
const list_container = parent_container.find(".subscriber_table");
|
||||
list_container.empty();
|
||||
|
||||
const simplebar_container = parent_container.find(".subscriber_list_container");
|
||||
|
||||
ListWidget.create(list_container, users, {
|
||||
name: "stream_subscribers/" + stream_id,
|
||||
modifier(item) {
|
||||
return format_member_list_elem(item);
|
||||
},
|
||||
filter: {
|
||||
element: $(`[data-stream-id='${CSS.escape(stream_id)}'] .search`),
|
||||
predicate(person, value) {
|
||||
const matcher = people.build_person_matcher(value);
|
||||
const match = matcher(person);
|
||||
|
||||
return match;
|
||||
},
|
||||
},
|
||||
simplebar_container,
|
||||
});
|
||||
|
||||
const opts = {
|
||||
user_source: get_users_for_subscriber_typeahead,
|
||||
stream: true,
|
||||
user_group: true,
|
||||
user: true,
|
||||
};
|
||||
pill_typeahead.set_up(pill_container.find(".input"), pill_widget, opts);
|
||||
}
|
||||
|
||||
export function invite_user_to_stream(user_ids, sub, success, failure) {
|
||||
// TODO: use stream_id when backend supports it
|
||||
const stream_name = sub.name;
|
||||
return channel.post({
|
||||
url: "/json/users/me/subscriptions",
|
||||
data: {
|
||||
subscriptions: JSON.stringify([{name: stream_name}]),
|
||||
principals: JSON.stringify(user_ids),
|
||||
},
|
||||
success,
|
||||
error: failure,
|
||||
});
|
||||
}
|
||||
|
||||
export function remove_user_from_stream(user_id, sub, success, failure) {
|
||||
// TODO: use stream_id when backend supports it
|
||||
const stream_name = sub.name;
|
||||
return channel.del({
|
||||
url: "/json/users/me/subscriptions",
|
||||
data: {subscriptions: JSON.stringify([stream_name]), principals: JSON.stringify([user_id])},
|
||||
success,
|
||||
error: failure,
|
||||
});
|
||||
}
|
||||
|
||||
function submit_add_subscriber_form(e) {
|
||||
const sub = get_sub_for_target(e.target);
|
||||
if (!sub) {
|
||||
blueslip.error(".subscriber_list_add form submit fails");
|
||||
return;
|
||||
}
|
||||
|
||||
let user_ids = user_pill.get_user_ids(pill_widget);
|
||||
user_ids = user_ids.concat(stream_pill.get_user_ids(pill_widget));
|
||||
user_ids = user_ids.concat(user_group_pill.get_user_ids(pill_widget));
|
||||
const deactivated_users = new Set();
|
||||
user_ids = user_ids.filter((user_id) => {
|
||||
if (!people.is_person_active(user_id)) {
|
||||
deactivated_users.add(user_id);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
user_ids = new Set(user_ids);
|
||||
|
||||
if (user_ids.has(page_params.user_id) && sub.subscribed) {
|
||||
// We don't want to send a request to subscribe ourselves
|
||||
// if we are already subscribed to this stream. This
|
||||
// case occurs when creating user pills from a stream.
|
||||
user_ids.delete(page_params.user_id);
|
||||
}
|
||||
let ignored_deactivated_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 (user_ids.size === 0) {
|
||||
show_stream_subscription_request_result({
|
||||
message: $t({defaultMessage: "No user to subscribe."}),
|
||||
add_class: "text-error",
|
||||
remove_class: "text-success",
|
||||
ignored_deactivated_users,
|
||||
});
|
||||
return;
|
||||
}
|
||||
user_ids = Array.from(user_ids);
|
||||
|
||||
function invite_success(data) {
|
||||
pill_widget.clear();
|
||||
const subscribed_users = Object.keys(data.subscribed).map((email) =>
|
||||
people.get_by_email(email),
|
||||
);
|
||||
const already_subscribed_users = Object.keys(data.already_subscribed).map((email) =>
|
||||
people.get_by_email(email),
|
||||
);
|
||||
|
||||
show_stream_subscription_request_result({
|
||||
add_class: "text-success",
|
||||
remove_class: "text-error",
|
||||
subscribed_users,
|
||||
already_subscribed_users,
|
||||
ignored_deactivated_users,
|
||||
});
|
||||
}
|
||||
|
||||
function invite_failure(xhr) {
|
||||
const error = JSON.parse(xhr.responseText);
|
||||
show_stream_subscription_request_result({
|
||||
message: error.msg,
|
||||
add_class: "text-error",
|
||||
remove_class: "text-success",
|
||||
});
|
||||
}
|
||||
|
||||
invite_user_to_stream(user_ids, sub, invite_success, invite_failure);
|
||||
}
|
||||
|
||||
export function initialize() {
|
||||
$("#subscriptions_table").on("keyup", ".subscriber_list_add form", (e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
submit_add_subscriber_form(e);
|
||||
}
|
||||
});
|
||||
|
||||
$("#subscriptions_table").on("submit", ".subscriber_list_add form", (e) => {
|
||||
e.preventDefault();
|
||||
submit_add_subscriber_form(e);
|
||||
});
|
||||
|
||||
$("#subscriptions_table").on("submit", ".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 sub = get_sub_for_target(e.target);
|
||||
if (!sub) {
|
||||
blueslip.error(".subscriber_list_remove form submit fails");
|
||||
return;
|
||||
}
|
||||
let message;
|
||||
|
||||
function removal_success(data) {
|
||||
if (data.removed.length > 0) {
|
||||
// Remove the user from the subscriber list.
|
||||
list_entry.remove();
|
||||
message = $t({defaultMessage: "Unsubscribed successfully!"});
|
||||
// The rest of the work is done via the subscription -> remove event we will get
|
||||
} else {
|
||||
message = $t({defaultMessage: "User is already not subscribed."});
|
||||
}
|
||||
show_stream_subscription_request_result({
|
||||
message,
|
||||
add_class: "text-success",
|
||||
remove_class: "text-remove",
|
||||
});
|
||||
}
|
||||
|
||||
function removal_failure() {
|
||||
show_stream_subscription_request_result({
|
||||
message: $t({defaultMessage: "Error removing user from this stream."}),
|
||||
add_class: "text-error",
|
||||
remove_class: "text-success",
|
||||
});
|
||||
}
|
||||
|
||||
function remove_user_from_private_stream() {
|
||||
remove_user_from_stream(target_user_id, sub, removal_success, removal_failure);
|
||||
}
|
||||
|
||||
if (sub.invite_only && people.is_my_user_id(target_user_id)) {
|
||||
const html_body = render_unsubscribe_private_stream_modal();
|
||||
|
||||
confirm_dialog.launch({
|
||||
html_heading: $t_html(
|
||||
{defaultMessage: "Unsubscribe from {stream_name}"},
|
||||
{stream_name: sub.name},
|
||||
),
|
||||
html_body,
|
||||
on_click: remove_user_from_private_stream,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
remove_user_from_stream(target_user_id, sub, removal_success, removal_failure);
|
||||
});
|
||||
}
|
||||
@@ -81,6 +81,7 @@ import * as stream_data from "./stream_data";
|
||||
import * as stream_edit from "./stream_edit";
|
||||
import * as stream_list from "./stream_list";
|
||||
import * as stream_settings_ui from "./stream_settings_ui";
|
||||
import * as stream_subscribers_ui from "./stream_subscribers_ui";
|
||||
import * as timerender from "./timerender";
|
||||
import * as tippyjs from "./tippyjs";
|
||||
import * as topic_list from "./topic_list";
|
||||
@@ -568,6 +569,7 @@ export function initialize_everything() {
|
||||
initialize_kitchen_sink_stuff();
|
||||
echo.initialize();
|
||||
stream_edit.initialize();
|
||||
stream_subscribers_ui.initialize();
|
||||
stream_data.initialize(stream_data_params);
|
||||
pm_conversations.recent.initialize(pm_conversations_params);
|
||||
muted_topics.initialize();
|
||||
|
||||
@@ -20,7 +20,7 @@ import * as settings_account from "./settings_account";
|
||||
import * as settings_data from "./settings_data";
|
||||
import * as settings_profile_fields from "./settings_profile_fields";
|
||||
import * as stream_data from "./stream_data";
|
||||
import * as stream_edit from "./stream_edit";
|
||||
import * as stream_subscribers_ui from "./stream_subscribers_ui";
|
||||
import * as sub_store from "./sub_store";
|
||||
import * as ui_report from "./ui_report";
|
||||
import * as user_groups from "./user_groups";
|
||||
@@ -213,7 +213,7 @@ function handle_remove_stream_subscription(target_user_id, sub, success, failure
|
||||
});
|
||||
} else {
|
||||
// Unsubscribed by admin.
|
||||
stream_edit.remove_user_from_stream(target_user_id, sub, success, failure);
|
||||
stream_subscribers_ui.remove_user_from_stream(target_user_id, sub, success, failure);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -158,6 +158,7 @@ EXEMPT_FILES = {
|
||||
"static/js/stream_list.js",
|
||||
"static/js/stream_muting.js",
|
||||
"static/js/stream_popover.js",
|
||||
"static/js/stream_subscribers_ui.js",
|
||||
"static/js/stream_ui_updates.js",
|
||||
"static/js/submessage.js",
|
||||
"static/js/stream_settings_ui.js",
|
||||
|
||||
Reference in New Issue
Block a user