mirror of https://github.com/zulip/zulip.git
streams: Allow creating stream pills to submit Add subscriber form.
We update the pills typeahead logic to also include stream results and pass the "stream" key in `opts` to enable this option for the Add subscriber form. This commit implements the feature of adding all the subscribers of another stream in the "Add subscribers" UI, with the help of a new "stream_pill.js` file. We temporarily add `user_pill.js` to the EXEMPT_FILES list as typeahead will be set up in `stream_edit.js` file which does not have any dedicated tests file. Work towards #15186.
This commit is contained in:
parent
b93371aa9f
commit
c6d9a87d6f
|
@ -249,6 +249,7 @@
|
||||||
"stream_topic_history": false,
|
"stream_topic_history": false,
|
||||||
"stream_list": false,
|
"stream_list": false,
|
||||||
"stream_muting": false,
|
"stream_muting": false,
|
||||||
|
"stream_pill": false,
|
||||||
"stream_popover": false,
|
"stream_popover": false,
|
||||||
"stream_sort": false,
|
"stream_sort": false,
|
||||||
"stream_ui_updates": false,
|
"stream_ui_updates": false,
|
||||||
|
|
|
@ -32,6 +32,7 @@ import "../localstorage.js";
|
||||||
import "../drafts.js";
|
import "../drafts.js";
|
||||||
import "../input_pill.js";
|
import "../input_pill.js";
|
||||||
import "../user_pill.js";
|
import "../user_pill.js";
|
||||||
|
import "../stream_pill.js";
|
||||||
import "../compose_pm_pill.js";
|
import "../compose_pm_pill.js";
|
||||||
import "../channel.js";
|
import "../channel.js";
|
||||||
import "../setup.js";
|
import "../setup.js";
|
||||||
|
|
|
@ -145,6 +145,7 @@ declare let stream_edit: any;
|
||||||
declare let stream_events: any;
|
declare let stream_events: any;
|
||||||
declare let stream_list: any;
|
declare let stream_list: any;
|
||||||
declare let stream_muting: any;
|
declare let stream_muting: any;
|
||||||
|
declare let stream_pill: any;
|
||||||
declare let stream_popover: any;
|
declare let stream_popover: any;
|
||||||
declare let stream_sort: any;
|
declare let stream_sort: any;
|
||||||
declare let stream_ui_updates: any;
|
declare let stream_ui_updates: any;
|
||||||
|
|
|
@ -7,18 +7,35 @@ exports.set_up = function (input, pills, opts) {
|
||||||
if (!opts.source) {
|
if (!opts.source) {
|
||||||
source = () => user_pill.typeahead_source(pills);
|
source = () => user_pill.typeahead_source(pills);
|
||||||
}
|
}
|
||||||
|
const include_streams = (query) => opts.stream && query.trim().startsWith("#");
|
||||||
|
|
||||||
input.typeahead({
|
input.typeahead({
|
||||||
items: 5,
|
items: 5,
|
||||||
fixed: true,
|
fixed: true,
|
||||||
dropup: true,
|
dropup: true,
|
||||||
source,
|
source() {
|
||||||
|
if (include_streams(this.query)) {
|
||||||
|
return stream_pill.typeahead_source(pills);
|
||||||
|
}
|
||||||
|
|
||||||
|
return source();
|
||||||
|
},
|
||||||
highlighter(item) {
|
highlighter(item) {
|
||||||
|
if (include_streams(this.query)) {
|
||||||
|
return typeahead_helper.render_stream(item);
|
||||||
|
}
|
||||||
|
|
||||||
return typeahead_helper.render_person(item);
|
return typeahead_helper.render_person(item);
|
||||||
},
|
},
|
||||||
matcher(item) {
|
matcher(item) {
|
||||||
let query = this.query.toLowerCase();
|
let query = this.query.toLowerCase();
|
||||||
query = query.replace(/\u00A0/g, String.fromCharCode(32));
|
query = query.replace(/\u00A0/g, String.fromCharCode(32));
|
||||||
|
|
||||||
|
if (include_streams(query)) {
|
||||||
|
query = query.trim().substring(1);
|
||||||
|
return item.name.toLowerCase().indexOf(query) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!settings_data.show_email()) {
|
if (!settings_data.show_email()) {
|
||||||
return item.full_name.toLowerCase().includes(query);
|
return item.full_name.toLowerCase().includes(query);
|
||||||
}
|
}
|
||||||
|
@ -28,10 +45,19 @@ exports.set_up = function (input, pills, opts) {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
sorter(matches) {
|
sorter(matches) {
|
||||||
|
if (include_streams(this.query)) {
|
||||||
|
return typeahead_helper.sort_streams(matches, this.query.trim().substring(1));
|
||||||
|
}
|
||||||
|
|
||||||
return typeahead_helper.sort_recipientbox_typeahead(this.query, matches, "");
|
return typeahead_helper.sort_recipientbox_typeahead(this.query, matches, "");
|
||||||
},
|
},
|
||||||
updater(user) {
|
updater(item) {
|
||||||
user_pill.append_user(user, pills);
|
if (include_streams(this.query)) {
|
||||||
|
stream_pill.append_stream(item, pills);
|
||||||
|
} else {
|
||||||
|
user_pill.append_user(item, pills);
|
||||||
|
}
|
||||||
|
|
||||||
input.trigger("focus");
|
input.trigger("focus");
|
||||||
if (opts.update_func) {
|
if (opts.update_func) {
|
||||||
opts.update_func();
|
opts.update_func();
|
||||||
|
|
|
@ -203,16 +203,25 @@ function submit_add_subscriber_form(e) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const user_ids = user_pill.get_user_ids(exports.pill_widget);
|
|
||||||
const stream_subscription_info_elem = $(".stream_subscription_info").expectOne();
|
const stream_subscription_info_elem = $(".stream_subscription_info").expectOne();
|
||||||
|
let user_ids = user_pill.get_user_ids(exports.pill_widget);
|
||||||
|
user_ids = user_ids.concat(stream_pill.get_user_ids(exports.pill_widget));
|
||||||
|
user_ids = new Set(user_ids);
|
||||||
|
|
||||||
if (user_ids.length === 0) {
|
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);
|
||||||
|
}
|
||||||
|
if (user_ids.size === 0) {
|
||||||
stream_subscription_info_elem
|
stream_subscription_info_elem
|
||||||
.text(i18n.t("No user to subscribe."))
|
.text(i18n.t("No user to subscribe."))
|
||||||
.addClass("text-error")
|
.addClass("text-error")
|
||||||
.removeClass("text-success");
|
.removeClass("text-success");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
user_ids = Array.from(user_ids);
|
||||||
|
|
||||||
function invite_success(data) {
|
function invite_success(data) {
|
||||||
exports.pill_widget.clear();
|
exports.pill_widget.clear();
|
||||||
|
@ -337,7 +346,7 @@ function show_subscription_settings(sub) {
|
||||||
simplebar_container: $(".subscriber_list_container"),
|
simplebar_container: $(".subscriber_list_container"),
|
||||||
});
|
});
|
||||||
|
|
||||||
const opts = {source: get_users_for_subscriber_typeahead};
|
const opts = {source: get_users_for_subscriber_typeahead, stream: true};
|
||||||
pill_typeahead.set_up(sub_settings.find(".input"), exports.pill_widget, opts);
|
pill_typeahead.set_up(sub_settings.find(".input"), exports.pill_widget, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function get_user_ids_from_subs(items) {
|
||||||
|
let user_ids = [];
|
||||||
|
const stream_ids = items.map((item) => item.stream_id);
|
||||||
|
for (const stream_id of stream_ids) {
|
||||||
|
const sub = stream_data.get_sub_by_id(stream_id);
|
||||||
|
if (!sub) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
user_ids = user_ids.concat(sub.subscribers.map());
|
||||||
|
}
|
||||||
|
return user_ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.get_user_ids = function (pill_widget) {
|
||||||
|
const items = pill_widget.items();
|
||||||
|
let user_ids = get_user_ids_from_subs(items);
|
||||||
|
user_ids = Array.from(new Set(user_ids));
|
||||||
|
|
||||||
|
user_ids = user_ids.filter(Boolean);
|
||||||
|
return user_ids;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.append_stream = function (stream, pill_widget) {
|
||||||
|
pill_widget.appendValidatedData({
|
||||||
|
display_value: "#" + stream.name + ": " + stream.subscriber_count + " users",
|
||||||
|
stream_id: stream.stream_id,
|
||||||
|
});
|
||||||
|
pill_widget.clear_text();
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.get_stream_ids = function (pill_widget) {
|
||||||
|
const items = pill_widget.items();
|
||||||
|
let stream_ids = items.map((item) => item.stream_id);
|
||||||
|
stream_ids = stream_ids.filter(Boolean);
|
||||||
|
|
||||||
|
return stream_ids;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.filter_taken_streams = function (items, pill_widget) {
|
||||||
|
const taken_stream_ids = exports.get_stream_ids(pill_widget);
|
||||||
|
items = items.filter((item) => !taken_stream_ids.includes(item.stream_id));
|
||||||
|
return items;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.typeahead_source = function (pill_widget) {
|
||||||
|
const potential_streams = stream_data.get_unsorted_subs();
|
||||||
|
return exports.filter_taken_streams(potential_streams, pill_widget);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.stream_pill = exports;
|
|
@ -12,7 +12,7 @@
|
||||||
<div class="add_subscribers_container">
|
<div class="add_subscribers_container">
|
||||||
<div class="pill-container person_picker">
|
<div class="pill-container person_picker">
|
||||||
<div class="input" contenteditable="true"
|
<div class="input" contenteditable="true"
|
||||||
data-placeholder="{{t 'Add subscribers' }}"></div>
|
data-placeholder="{{t 'Add subscribers. Use #streamname to subscribe a whole stream' }}"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="add_subscriber_btn_wrapper inline-block">
|
<div class="add_subscriber_btn_wrapper inline-block">
|
||||||
<button type="submit" name="add_subscriber" class="button add-subscriber-button small rounded" tabindex="0">
|
<button type="submit" name="add_subscriber" class="button add-subscriber-button small rounded" tabindex="0">
|
||||||
|
|
|
@ -136,6 +136,7 @@ EXEMPT_FILES = {
|
||||||
'static/js/stream_edit.js',
|
'static/js/stream_edit.js',
|
||||||
'static/js/stream_list.js',
|
'static/js/stream_list.js',
|
||||||
'static/js/stream_muting.js',
|
'static/js/stream_muting.js',
|
||||||
|
'static/js/stream_pill.js',
|
||||||
'static/js/stream_popover.js',
|
'static/js/stream_popover.js',
|
||||||
'static/js/stream_ui_updates.js',
|
'static/js/stream_ui_updates.js',
|
||||||
'static/js/submessage.js',
|
'static/js/submessage.js',
|
||||||
|
@ -157,6 +158,7 @@ EXEMPT_FILES = {
|
||||||
'static/js/unread_ops.js',
|
'static/js/unread_ops.js',
|
||||||
'static/js/unread_ui.js',
|
'static/js/unread_ui.js',
|
||||||
'static/js/upload_widget.js',
|
'static/js/upload_widget.js',
|
||||||
|
'static/js/user_pill.js',
|
||||||
'static/js/user_status_ui.js',
|
'static/js/user_status_ui.js',
|
||||||
'static/js/zcommand.js',
|
'static/js/zcommand.js',
|
||||||
'static/js/zform.js',
|
'static/js/zform.js',
|
||||||
|
|
Loading…
Reference in New Issue