mirror of https://github.com/zulip/zulip.git
setting(streams): Add pill support to subscribe all members of group.
We update the pills typeahead logic to also include user group results and pass the "user_group" key in `opts` to enable this option for Add subscriber form. The changes includes: * Exporting the `query_matches_name_description` function, to deduplicate the typeahead `matcher` logic. * Creating a `people.is_known_user` function, to deduplicate the typeahead `sorter` logic. * Add a new `user_group_pill.js`, to allow adding user group pills in input widgets that support pills. This has been tested manually as well as by adding some new node tests.
This commit is contained in:
parent
e4d5ea69a0
commit
ce4cf66f3f
|
@ -242,6 +242,8 @@ test_people("basics", () => {
|
|||
const email = "isaac@example.com";
|
||||
|
||||
assert(!people.is_known_user_id(32));
|
||||
assert(!people.is_known_user(isaac));
|
||||
assert(!people.is_known_user(undefined));
|
||||
assert(!people.is_valid_full_name_and_user_id(full_name, 32));
|
||||
assert.equal(people.get_user_id_from_name(full_name), undefined);
|
||||
|
||||
|
@ -251,6 +253,7 @@ test_people("basics", () => {
|
|||
|
||||
assert(people.is_valid_full_name_and_user_id(full_name, 32));
|
||||
assert(people.is_known_user_id(32));
|
||||
assert(people.is_known_user(isaac));
|
||||
assert.equal(people.get_active_human_count(), 2);
|
||||
|
||||
assert.equal(people.get_user_id_from_name(full_name), 32);
|
||||
|
|
|
@ -150,6 +150,9 @@ test_ui("populate_user_groups", (override) => {
|
|||
if (user_id === iago.user_id) {
|
||||
return iago;
|
||||
}
|
||||
if (user_id === alice.user_id) {
|
||||
return alice;
|
||||
}
|
||||
if (user_id === undefined) {
|
||||
return noop;
|
||||
}
|
||||
|
@ -158,6 +161,9 @@ test_ui("populate_user_groups", (override) => {
|
|||
get_by_user_id_called = true;
|
||||
return undefined;
|
||||
};
|
||||
people.is_known_user = function () {
|
||||
return people.get_by_user_id !== undefined && people.get_by_user_id !== noop;
|
||||
};
|
||||
|
||||
override(settings_user_groups, "can_edit", () => true);
|
||||
|
||||
|
@ -236,7 +242,7 @@ test_ui("populate_user_groups", (override) => {
|
|||
typeahead_helper.sort_recipients = function () {
|
||||
sort_recipients_typeahead_called = true;
|
||||
};
|
||||
config.sorter.call(fake_context);
|
||||
config.sorter.call(fake_context, []);
|
||||
assert(sort_recipients_typeahead_called);
|
||||
})();
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@ const people = zrequire("people");
|
|||
const stream_data = zrequire("stream_data");
|
||||
const stream_edit = zrequire("stream_edit");
|
||||
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 jill = {
|
||||
|
@ -62,6 +64,24 @@ for (const person of persons) {
|
|||
people.add_active_user(person);
|
||||
}
|
||||
|
||||
const admins = {
|
||||
name: "Admins",
|
||||
description: "foo",
|
||||
id: 1,
|
||||
members: [jill.user_id, mark.user_id],
|
||||
};
|
||||
const testers = {
|
||||
name: "Testers",
|
||||
description: "bar",
|
||||
id: 2,
|
||||
members: [mark.user_id, fred.user_id, me.user_id],
|
||||
};
|
||||
|
||||
const groups = [admins, testers];
|
||||
for (const group of groups) {
|
||||
user_groups.add(group);
|
||||
}
|
||||
|
||||
const denmark = {
|
||||
stream_id: 1,
|
||||
name: "Denmark",
|
||||
|
@ -144,21 +164,44 @@ test_ui("subscriber_pills", (override) => {
|
|||
assert.equal(typeof config.sorter, "function");
|
||||
assert.equal(typeof config.updater, "function");
|
||||
|
||||
const fake_this = {
|
||||
const fake_stream_this = {
|
||||
query: "#Denmark",
|
||||
};
|
||||
const fake_person_this = {
|
||||
query: "me",
|
||||
};
|
||||
const fake_group_this = {
|
||||
query: "test",
|
||||
};
|
||||
|
||||
(function test_highlighter() {
|
||||
const fake_stream = $.create("fake-stream");
|
||||
typeahead_helper.render_stream = () => fake_stream;
|
||||
assert.equal(config.highlighter.call(fake_this, denmark), fake_stream);
|
||||
const fake_html = $.create("fake-html");
|
||||
typeahead_helper.render_stream = function () {
|
||||
return fake_html;
|
||||
};
|
||||
assert.equal(config.highlighter.call(fake_stream_this, denmark), fake_html);
|
||||
|
||||
typeahead_helper.render_person_or_user_group = function () {
|
||||
return fake_html;
|
||||
};
|
||||
assert.equal(config.highlighter.call(fake_group_this, testers), fake_html);
|
||||
assert.equal(config.highlighter.call(fake_person_this, me), fake_html);
|
||||
})();
|
||||
|
||||
(function test_matcher() {
|
||||
let result = config.matcher.call(fake_this, denmark);
|
||||
let result = config.matcher.call(fake_stream_this, denmark);
|
||||
assert(result);
|
||||
result = config.matcher.call(fake_stream_this, sweden);
|
||||
assert(!result);
|
||||
|
||||
result = config.matcher.call(fake_this, sweden);
|
||||
result = config.matcher.call(fake_group_this, testers);
|
||||
assert(result);
|
||||
result = config.matcher.call(fake_group_this, admins);
|
||||
assert(!result);
|
||||
|
||||
result = config.matcher.call(fake_person_this, me);
|
||||
assert(result);
|
||||
result = config.matcher.call(fake_person_this, jill);
|
||||
assert(!result);
|
||||
})();
|
||||
|
||||
|
@ -167,8 +210,19 @@ test_ui("subscriber_pills", (override) => {
|
|||
typeahead_helper.sort_streams = () => {
|
||||
sort_streams_called = true;
|
||||
};
|
||||
config.sorter.call(fake_this);
|
||||
config.sorter.call(fake_stream_this);
|
||||
assert(sort_streams_called);
|
||||
|
||||
let sort_recipients_called = false;
|
||||
typeahead_helper.sort_recipients = function () {
|
||||
sort_recipients_called = true;
|
||||
};
|
||||
config.sorter.call(fake_group_this, [testers]);
|
||||
assert(sort_recipients_called);
|
||||
|
||||
sort_recipients_called = false;
|
||||
config.sorter.call(fake_person_this, [me]);
|
||||
assert(sort_recipients_called);
|
||||
})();
|
||||
|
||||
(function test_updater() {
|
||||
|
@ -178,21 +232,29 @@ test_ui("subscriber_pills", (override) => {
|
|||
}
|
||||
|
||||
assert.equal(number_of_pills(), 0);
|
||||
config.updater.call(fake_this, denmark);
|
||||
config.updater.call(fake_stream_this, denmark);
|
||||
assert.equal(number_of_pills(), 1);
|
||||
fake_this.query = me.email;
|
||||
config.updater.call(fake_this, me);
|
||||
config.updater.call(fake_person_this, me);
|
||||
assert.equal(number_of_pills(), 2);
|
||||
fake_this.query = "#Denmark";
|
||||
config.updater.call(fake_group_this, testers);
|
||||
assert.equal(number_of_pills(), 3);
|
||||
})();
|
||||
|
||||
(function test_source() {
|
||||
const result = config.source.call(fake_this);
|
||||
const taken_ids = stream_pill.get_stream_ids(stream_edit.pill_widget);
|
||||
const stream_ids = Array.from(result, (stream) => stream.stream_id).sort();
|
||||
let expected_ids = Array.from(subs, (stream) => stream.stream_id).sort();
|
||||
expected_ids = expected_ids.filter((id) => !taken_ids.includes(id));
|
||||
assert.deepEqual(stream_ids, expected_ids);
|
||||
let result = config.source.call(fake_stream_this);
|
||||
const stream_ids = result.map((stream) => stream.stream_id);
|
||||
const expected_stream_ids = [sweden.stream_id];
|
||||
assert.deepEqual(stream_ids, expected_stream_ids);
|
||||
|
||||
result = config.source.call(fake_group_this);
|
||||
const group_ids = result.map((group) => group.id).filter(Boolean);
|
||||
const expected_group_ids = [admins.id];
|
||||
assert.deepEqual(group_ids, expected_group_ids);
|
||||
|
||||
result = config.source.call(fake_person_this);
|
||||
const user_ids = result.map((user) => user.user_id).filter(Boolean);
|
||||
const expected_user_ids = [jill.user_id, fred.user_id];
|
||||
assert.deepEqual(user_ids, expected_user_ids);
|
||||
})();
|
||||
|
||||
input_typeahead_called = true;
|
||||
|
@ -228,9 +290,10 @@ test_ui("subscriber_pills", (override) => {
|
|||
peer_data.get_subscribers(denmark.stream_id),
|
||||
).filter((id) => id !== me.user_id);
|
||||
|
||||
// denmark.stream_id is stubbed. Thus request is
|
||||
// sent to add all subscribers of stream Denmark.
|
||||
expected_user_ids = potential_denmark_stream_subscribers;
|
||||
// `denmark` stream pill, `me` user pill and
|
||||
// `testers` user group pill are stubbed.
|
||||
// Thus request is sent to add all the users.
|
||||
expected_user_ids = [mark.user_id, fred.user_id];
|
||||
add_subscribers_handler(event);
|
||||
|
||||
add_subscribers_handler = $(subscriptions_table_selector).get_on_handler(
|
||||
|
@ -242,6 +305,7 @@ test_ui("subscriber_pills", (override) => {
|
|||
// Only Denmark stream pill is created and a
|
||||
// request is sent to add all it's subscribers.
|
||||
override(user_pill, "get_user_ids", () => []);
|
||||
override(user_group_pill, "get_user_ids", () => []);
|
||||
expected_user_ids = potential_denmark_stream_subscribers;
|
||||
add_subscribers_handler(event);
|
||||
|
||||
|
|
|
@ -89,7 +89,7 @@ export function query_matches_person(query, person) {
|
|||
return typeahead.query_matches_source_attrs(query, person, ["full_name", email_attr], " ");
|
||||
}
|
||||
|
||||
function query_matches_name_description(query, user_group_or_stream) {
|
||||
export function query_matches_name_description(query, user_group_or_stream) {
|
||||
return typeahead.query_matches_source_attrs(
|
||||
query,
|
||||
user_group_or_stream,
|
||||
|
|
|
@ -163,6 +163,10 @@ export function is_known_user_id(user_id) {
|
|||
return people_by_user_id_dict.has(user_id);
|
||||
}
|
||||
|
||||
export function is_known_user(user) {
|
||||
return user && is_known_user_id(user.user_id);
|
||||
}
|
||||
|
||||
function sort_numerically(user_ids) {
|
||||
user_ids.sort((a, b) => a - b);
|
||||
|
||||
|
|
|
@ -1,15 +1,32 @@
|
|||
import * as composebox_typeahead from "./composebox_typeahead";
|
||||
import * as people from "./people";
|
||||
import * as settings_data from "./settings_data";
|
||||
import * as stream_pill from "./stream_pill";
|
||||
import * as typeahead_helper from "./typeahead_helper";
|
||||
import * as user_group_pill from "./user_group_pill";
|
||||
import * as user_groups from "./user_groups";
|
||||
import * as user_pill from "./user_pill";
|
||||
|
||||
function person_matcher(query, item) {
|
||||
if (people.is_known_user(item)) {
|
||||
return composebox_typeahead.query_matches_person(query, item);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function group_matcher(query, item) {
|
||||
if (user_groups.is_user_group(item)) {
|
||||
return composebox_typeahead.query_matches_name_description(query, item);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function set_up(input, pills, opts) {
|
||||
let source = opts.source;
|
||||
if (!opts.source) {
|
||||
source = () => user_pill.typeahead_source(pills);
|
||||
}
|
||||
const include_streams = (query) => opts.stream && query.trim().startsWith("#");
|
||||
const include_user_groups = opts.user_group;
|
||||
|
||||
input.typeahead({
|
||||
items: 5,
|
||||
|
@ -20,6 +37,10 @@ export function set_up(input, pills, opts) {
|
|||
return stream_pill.typeahead_source(pills);
|
||||
}
|
||||
|
||||
if (include_user_groups) {
|
||||
return user_group_pill.typeahead_source(pills).concat(source());
|
||||
}
|
||||
|
||||
return source();
|
||||
},
|
||||
highlighter(item) {
|
||||
|
@ -27,6 +48,10 @@ export function set_up(input, pills, opts) {
|
|||
return typeahead_helper.render_stream(item);
|
||||
}
|
||||
|
||||
if (include_user_groups) {
|
||||
return typeahead_helper.render_person_or_user_group(item);
|
||||
}
|
||||
|
||||
return typeahead_helper.render_person(item);
|
||||
},
|
||||
matcher(item) {
|
||||
|
@ -38,24 +63,36 @@ export function set_up(input, pills, opts) {
|
|||
return item.name.toLowerCase().includes(query);
|
||||
}
|
||||
|
||||
if (!settings_data.show_email()) {
|
||||
return item.full_name.toLowerCase().includes(query);
|
||||
if (include_user_groups) {
|
||||
return group_matcher(query, item) || person_matcher(query, item);
|
||||
}
|
||||
const email = people.get_visible_email(item);
|
||||
return (
|
||||
email.toLowerCase().includes(query) || item.full_name.toLowerCase().includes(query)
|
||||
);
|
||||
|
||||
return person_matcher(query, item);
|
||||
},
|
||||
sorter(matches) {
|
||||
if (include_streams(this.query)) {
|
||||
return typeahead_helper.sort_streams(matches, this.query.trim().slice(1));
|
||||
}
|
||||
|
||||
return typeahead_helper.sort_recipients(matches, this.query, "");
|
||||
const users = matches.filter((ele) => people.is_known_user(ele));
|
||||
let groups;
|
||||
if (include_user_groups) {
|
||||
groups = matches.filter((ele) => user_groups.is_user_group(ele));
|
||||
}
|
||||
return typeahead_helper.sort_recipients(
|
||||
users,
|
||||
this.query,
|
||||
"",
|
||||
undefined,
|
||||
groups,
|
||||
undefined,
|
||||
);
|
||||
},
|
||||
updater(item) {
|
||||
if (include_streams(this.query)) {
|
||||
stream_pill.append_stream(item, pills);
|
||||
} else if (include_user_groups && user_groups.is_user_group(item)) {
|
||||
user_group_pill.append_user_group(item, pills);
|
||||
} else {
|
||||
user_pill.append_user(item, pills);
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import * as sub_store from "./sub_store";
|
|||
import * as subs from "./subs";
|
||||
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 * as util from "./util";
|
||||
|
||||
|
@ -243,6 +244,8 @@ function submit_add_subscriber_form(e) {
|
|||
const stream_subscription_info_elem = $(".stream_subscription_info").expectOne();
|
||||
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));
|
||||
|
||||
user_ids = new Set(user_ids);
|
||||
|
||||
if (user_ids.has(page_params.user_id) && sub.subscribed) {
|
||||
|
@ -400,7 +403,7 @@ function show_subscription_settings(sub) {
|
|||
simplebar_container: $(".subscriber_list_container"),
|
||||
});
|
||||
|
||||
const opts = {source: get_users_for_subscriber_typeahead, stream: true};
|
||||
const opts = {source: get_users_for_subscriber_typeahead, stream: true, user_group: true};
|
||||
pill_typeahead.set_up(sub_settings.find(".input"), pill_widget, opts);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
import * as user_groups from "./user_groups";
|
||||
|
||||
function get_user_ids_from_user_groups(items) {
|
||||
let user_ids = [];
|
||||
const group_ids = items.map((item) => item.id).filter(Boolean);
|
||||
for (const group_id of group_ids) {
|
||||
const user_group = user_groups.get_user_group_from_id(group_id);
|
||||
user_ids = user_ids.concat(Array.from(user_group.members));
|
||||
}
|
||||
return user_ids;
|
||||
}
|
||||
|
||||
export function get_user_ids(pill_widget) {
|
||||
const items = pill_widget.items();
|
||||
let user_ids = get_user_ids_from_user_groups(items);
|
||||
user_ids = Array.from(new Set(user_ids));
|
||||
|
||||
user_ids = user_ids.filter(Boolean);
|
||||
return user_ids;
|
||||
}
|
||||
|
||||
export function append_user_group(group, pill_widget) {
|
||||
if (group !== undefined && group !== null) {
|
||||
pill_widget.appendValidatedData({
|
||||
display_value: group.name + ": " + group.members.size + " users",
|
||||
id: group.id,
|
||||
});
|
||||
pill_widget.clear_text();
|
||||
}
|
||||
}
|
||||
|
||||
export function get_group_ids(pill_widget) {
|
||||
const items = pill_widget.items();
|
||||
let group_ids = items.map((item) => item.id);
|
||||
group_ids = group_ids.filter(Boolean);
|
||||
|
||||
return group_ids;
|
||||
}
|
||||
|
||||
export function filter_taken_groups(items, pill_widget) {
|
||||
const taken_group_ids = get_group_ids(pill_widget);
|
||||
items = items.filter((item) => !taken_group_ids.includes(item.id));
|
||||
return items;
|
||||
}
|
||||
|
||||
export function typeahead_source(pill_widget) {
|
||||
const groups = user_groups.get_realm_user_groups();
|
||||
return filter_taken_groups(groups, pill_widget);
|
||||
}
|
Loading…
Reference in New Issue