mirror of https://github.com/zulip/zulip.git
stream settings: Add pills in add subscribers input.
This commit changes the stream settings UI for adding subscribers to use our standard user pills in the input box, rather than just plain-text email addresses. This is important progress towards removing display email addresses from the Zulip UI. It also allows subscribing multiple users at the same time, which is a nice improvement.
This commit is contained in:
parent
18f8859baa
commit
e52b544213
|
@ -129,13 +129,13 @@ exports.update_stream_description = function (sub) {
|
|||
);
|
||||
};
|
||||
|
||||
exports.invite_user_to_stream = function (user_email, sub, success, failure) {
|
||||
exports.invite_user_to_stream = function (emails, 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_email])},
|
||||
principals: JSON.stringify(emails)},
|
||||
success: success,
|
||||
error: failure,
|
||||
});
|
||||
|
@ -185,6 +185,13 @@ function show_subscription_settings(sub_row) {
|
|||
stream_color.set_colorpicker_color(colorpicker, color);
|
||||
stream_ui_updates.update_add_subscriptions_elements(sub);
|
||||
|
||||
const container = $("#subscription_overlay .subscription_settings[data-stream-id='" + stream_id + "'] .pill-container");
|
||||
exports.pill_widget = input_pill.create({
|
||||
container: container,
|
||||
create_item_from_text: user_pill.create_item_from_email,
|
||||
get_text_from_item: user_pill.get_email_from_item,
|
||||
});
|
||||
|
||||
if (!sub.render_subscribers) {
|
||||
return;
|
||||
}
|
||||
|
@ -198,6 +205,11 @@ function show_subscription_settings(sub_row) {
|
|||
const users = exports.get_users_from_subscribers(sub.subscribers);
|
||||
exports.sort_but_pin_current_user_on_top(users);
|
||||
|
||||
function get_users_for_subscriber_typeahead() {
|
||||
const potential_subscribers = stream_data.potential_subscribers(sub);
|
||||
return user_pill.filter_taken_users(potential_subscribers, exports.pill_widget);
|
||||
}
|
||||
|
||||
list_render.create(list, users, {
|
||||
name: "stream_subscribers/" + stream_id,
|
||||
modifier: function (item) {
|
||||
|
@ -219,33 +231,10 @@ function show_subscription_settings(sub_row) {
|
|||
},
|
||||
});
|
||||
|
||||
sub_settings.find('input[name="principal"]').typeahead({
|
||||
source: () => stream_data.potential_subscribers(sub),
|
||||
items: 5,
|
||||
highlighter: function (item) {
|
||||
return typeahead_helper.render_person(item);
|
||||
},
|
||||
matcher: function (item) {
|
||||
const query = $.trim(this.query.toLowerCase());
|
||||
if (query === '' || query === item.email) {
|
||||
return false;
|
||||
}
|
||||
// Case-insensitive.
|
||||
if (!settings_data.show_email()) {
|
||||
return item.full_name.toLowerCase().includes(query);
|
||||
}
|
||||
return item.email.toLowerCase().includes(query) ||
|
||||
item.full_name.toLowerCase().includes(query);
|
||||
},
|
||||
sorter: function (matches) {
|
||||
const current_stream = compose_state.stream_name();
|
||||
return typeahead_helper.sort_recipientbox_typeahead(
|
||||
this.query, matches, current_stream);
|
||||
},
|
||||
updater: function (item) {
|
||||
return item.email;
|
||||
},
|
||||
});
|
||||
user_pill.set_up_typeahead_on_pills(sub_settings.find('.input'),
|
||||
exports.pill_widget,
|
||||
function () {},
|
||||
get_users_for_subscriber_typeahead);
|
||||
}
|
||||
|
||||
exports.is_notification_setting = function (setting_label) {
|
||||
|
@ -569,18 +558,23 @@ exports.initialize = function () {
|
|||
return;
|
||||
}
|
||||
|
||||
const text_box = settings_row.find('input[name="principal"]');
|
||||
const principal = $.trim(text_box.val());
|
||||
const user_ids = user_pill.get_user_ids(exports.pill_widget);
|
||||
const emails = user_ids.map(user_id => {
|
||||
const person = people.get_by_user_id(user_id);
|
||||
return person.email;
|
||||
});
|
||||
const stream_subscription_info_elem = $('.stream_subscription_info').expectOne();
|
||||
|
||||
function invite_success(data) {
|
||||
text_box.val('');
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(data.subscribed, principal)) {
|
||||
exports.pill_widget.clear();
|
||||
if (!Object.entries(data.already_subscribed).length) {
|
||||
stream_subscription_info_elem.text(i18n.t("Subscribed successfully!"));
|
||||
// The rest of the work is done via the subscription -> add event we will get
|
||||
} else {
|
||||
stream_subscription_info_elem.text(i18n.t("User already subscribed."));
|
||||
const already_subscribed_users = Object.keys(data.already_subscribed).join(', ');
|
||||
stream_subscription_info_elem.text(i18n.t(
|
||||
" __already_subscribed_users__ are already subscribed.", {already_subscribed_users: already_subscribed_users}));
|
||||
}
|
||||
stream_subscription_info_elem.addClass("text-success")
|
||||
.removeClass("text-error");
|
||||
|
@ -592,7 +586,7 @@ exports.initialize = function () {
|
|||
.addClass("text-error").removeClass("text-success");
|
||||
}
|
||||
|
||||
exports.invite_user_to_stream(principal, sub, invite_success, invite_failure);
|
||||
exports.invite_user_to_stream(emails, sub, invite_success, invite_failure);
|
||||
});
|
||||
|
||||
$("#subscriptions_table").on("submit", ".subscriber_list_remove form", function (e) {
|
||||
|
|
|
@ -207,7 +207,7 @@ exports.update_add_subscriptions_elements = function (sub) {
|
|||
|
||||
// Otherwise, we adjust whether the widgets are disabled based on
|
||||
// whether this user is authorized to add subscribers.
|
||||
const input_element = $('.add_subscribers_container').find('input[name="principal"]').expectOne();
|
||||
const input_element = $('.add_subscribers_container').find('.input').expectOne();
|
||||
const button_element = $('.add_subscribers_container').find('button[name="add_subscriber"]').expectOne();
|
||||
const allow_user_to_add_subs = sub.can_add_subscribers;
|
||||
|
||||
|
|
|
@ -117,14 +117,15 @@ exports.create_pills = function (pill_container) {
|
|||
return pills;
|
||||
};
|
||||
|
||||
exports.set_up_typeahead_on_pills = function (input, pills, update_func) {
|
||||
exports.set_up_typeahead_on_pills = function (input, pills, update_func, source) {
|
||||
if (!source) {
|
||||
source = () => exports.typeahead_source(pills);
|
||||
}
|
||||
input.typeahead({
|
||||
items: 5,
|
||||
fixed: true,
|
||||
dropup: true,
|
||||
source: function () {
|
||||
return exports.typeahead_source(pills);
|
||||
},
|
||||
source: source,
|
||||
highlighter: function (item) {
|
||||
return typeahead_helper.render_person(item);
|
||||
},
|
||||
|
|
|
@ -113,6 +113,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
.add_subscribers_container .pill-container {
|
||||
width: 100%;
|
||||
background-color: hsl(0, 0%, 100%);
|
||||
|
||||
.input:first-child:empty::before {
|
||||
opacity: 0.5;
|
||||
content: attr(data-placeholder);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
10%,
|
||||
90% {
|
||||
|
|
|
@ -312,17 +312,26 @@ form#add_new_subscription {
|
|||
.subscriber_list_add {
|
||||
width: 100%;
|
||||
margin: 10px auto;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.subscriber_list_add .search {
|
||||
float: left;
|
||||
.subscriber-search {
|
||||
margin: 10px 0 0 0;
|
||||
}
|
||||
|
||||
.subscriber_list_add .form-inline {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.add_subscribers_container {
|
||||
display: inline-flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
|
||||
.add_subscriber_btn_wrapper {
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.remove-subscriber-form {
|
||||
margin: 0px 0px 0px 0px;
|
||||
}
|
||||
|
@ -419,15 +428,15 @@ form#add_new_subscription {
|
|||
transition: all 0.3s ease;
|
||||
transform: translate(-13px, 0px);
|
||||
}
|
||||
}
|
||||
|
||||
.exit {
|
||||
font-weight: 600;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
color: hsl(0, 0%, 67%);
|
||||
cursor: pointer;
|
||||
.exit {
|
||||
font-weight: 600;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
color: hsl(0, 0%, 67%);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.exit-sign {
|
||||
|
|
|
@ -3,20 +3,25 @@
|
|||
<div class="subscriber_list_settings">
|
||||
<label class="sub_settings_title float-left">
|
||||
{{t "Stream membership" }}
|
||||
<div class="stream_subscription_info small"></div>
|
||||
</label>
|
||||
<div class="subscriber_list_add float-right">
|
||||
<div class="subscriber-search float-right">
|
||||
<input type="text" class="search" placeholder="{{t 'Search subscribers' }}" />
|
||||
</div>
|
||||
<div class="subscriber_list_add float-left">
|
||||
<form class="form-inline">
|
||||
<input type="text" class="search" placeholder="{{t 'Search subscribers' }}" />
|
||||
<div class="add_subscribers_container">
|
||||
<input type="text" name="principal" placeholder="{{t 'Name or email' }}" value="" class="input-block" autocomplete="off" />
|
||||
<div class="pill-container person_picker">
|
||||
<div class="input" contenteditable="true"
|
||||
data-placeholder="{{t 'Add subscribers' }}"></div>
|
||||
</div>
|
||||
<div class="add_subscriber_btn_wrapper inline-block">
|
||||
<button type="submit" name="add_subscriber" class="button add-subscriber-button small rounded" tabindex="-1">
|
||||
<button type="submit" name="add_subscriber" class="button add-subscriber-button small rounded" tabindex="0">
|
||||
{{t 'Add' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="stream_subscription_info"></div>
|
||||
</div>
|
||||
<div class="clear-float"></div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue