mirror of
https://github.com/zulip/zulip.git
synced 2025-11-02 04:53:36 +00:00
The tooltip for the "Announce Stream" hint was not consistent with the rest of the settings so it has now been replaced with the standard tippy tooltip. The "?" icon has also been replaced by the "i" icon to match the other settings. Fixes: #21312.
407 lines
14 KiB
JavaScript
407 lines
14 KiB
JavaScript
import $ from "jquery";
|
|
import tippy from "tippy.js";
|
|
|
|
import render_announce_stream_docs from "../templates/announce_stream_docs.hbs";
|
|
import render_subscription_invites_warning_modal from "../templates/confirm_dialog/confirm_subscription_invites_warning.hbs";
|
|
|
|
import * as channel from "./channel";
|
|
import * as confirm_dialog from "./confirm_dialog";
|
|
import {$t, $t_html} from "./i18n";
|
|
import * as loading from "./loading";
|
|
import {page_params} from "./page_params";
|
|
import * as people from "./people";
|
|
import * as settings_data from "./settings_data";
|
|
import * as stream_create_subscribers from "./stream_create_subscribers";
|
|
import * as stream_data from "./stream_data";
|
|
import * as stream_settings_ui from "./stream_settings_ui";
|
|
import * as ui_report from "./ui_report";
|
|
import {parse_html} from "./ui_util";
|
|
|
|
let created_stream;
|
|
|
|
export function reset_created_stream() {
|
|
created_stream = undefined;
|
|
}
|
|
|
|
export function set_name(stream) {
|
|
created_stream = stream;
|
|
}
|
|
|
|
export function get_name() {
|
|
return created_stream;
|
|
}
|
|
|
|
class StreamSubscriptionError {
|
|
report_no_subs_to_stream() {
|
|
$("#stream_subscription_error").text(
|
|
$t({defaultMessage: "You cannot create a stream with no subscribers!"}),
|
|
);
|
|
$("#stream_subscription_error").show();
|
|
}
|
|
|
|
cant_create_stream_without_susbscribing() {
|
|
$("#stream_subscription_error").text(
|
|
$t({
|
|
defaultMessage:
|
|
"You must be an organization administrator to create a stream without subscribing.",
|
|
}),
|
|
);
|
|
$("#stream_subscription_error").show();
|
|
}
|
|
|
|
clear_errors() {
|
|
$("#stream_subscription_error").hide();
|
|
}
|
|
}
|
|
const stream_subscription_error = new StreamSubscriptionError();
|
|
|
|
class StreamNameError {
|
|
report_already_exists() {
|
|
$("#stream_name_error").text(
|
|
$t({defaultMessage: "A stream with this name already exists"}),
|
|
);
|
|
$("#stream_name_error").show();
|
|
}
|
|
|
|
clear_errors() {
|
|
$("#stream_name_error").hide();
|
|
}
|
|
|
|
report_empty_stream() {
|
|
$("#stream_name_error").text($t({defaultMessage: "A stream needs to have a name"}));
|
|
$("#stream_name_error").show();
|
|
}
|
|
|
|
select() {
|
|
$("#create_stream_name").trigger("focus").trigger("select");
|
|
}
|
|
|
|
pre_validate(stream_name) {
|
|
// Don't worry about empty strings...we just want to call this
|
|
// to warn users early before they start doing too much work
|
|
// after they make the effort to type in a stream name. (The
|
|
// use case here is that I go to create a stream, only to find
|
|
// out it already exists, and I was just too lazy to look at
|
|
// the public streams that I'm not subscribed to yet. Once I
|
|
// realize the stream already exists, I may want to cancel.)
|
|
if (stream_name && stream_data.get_sub(stream_name)) {
|
|
this.report_already_exists();
|
|
return;
|
|
}
|
|
|
|
this.clear_errors();
|
|
}
|
|
|
|
validate_for_submit(stream_name) {
|
|
if (!stream_name) {
|
|
this.report_empty_stream();
|
|
this.select();
|
|
return false;
|
|
}
|
|
|
|
if (stream_data.get_sub(stream_name)) {
|
|
this.report_already_exists();
|
|
this.select();
|
|
return false;
|
|
}
|
|
|
|
// If we got this far, then we think we have a new unique stream
|
|
// name, so we'll submit to the server. (It's still plausible,
|
|
// however, that there's some invite-only stream that we don't
|
|
// know about locally that will cause a name collision.)
|
|
return true;
|
|
}
|
|
}
|
|
const stream_name_error = new StreamNameError();
|
|
|
|
// Stores the previous state of the stream creation checkbox.
|
|
let stream_announce_previous_value =
|
|
settings_data.user_can_create_public_streams() ||
|
|
settings_data.user_can_create_web_public_streams();
|
|
|
|
// Within the new stream modal...
|
|
function update_announce_stream_state() {
|
|
// If there is no notifications_stream, we simply hide the widget.
|
|
if (!stream_data.realm_has_notifications_stream()) {
|
|
$("#announce-new-stream").hide();
|
|
return;
|
|
}
|
|
|
|
// If the stream is invite only, disable the "Announce stream" option.
|
|
// Otherwise enable it.
|
|
const $announce_stream_checkbox = $("#announce-new-stream input");
|
|
const $announce_stream_label = $("#announce-new-stream");
|
|
let disable_it = false;
|
|
const privacy_type = $("input:radio[name=privacy]:checked").val();
|
|
const is_invite_only =
|
|
privacy_type === "invite-only" || privacy_type === "invite-only-public-history";
|
|
$announce_stream_label.removeClass("control-label-disabled");
|
|
|
|
// Here, we arrange to save the state of the announce checkbox
|
|
// when switching to creating a private stream; we will restore it
|
|
// when switching back to a public stream. This input-disabled
|
|
// check prevents overwriting stream_announce_previous_value with
|
|
// the false when switching between private stream types.
|
|
if (!$announce_stream_checkbox.prop("disabled")) {
|
|
stream_announce_previous_value = $announce_stream_checkbox.prop("checked");
|
|
}
|
|
|
|
if (is_invite_only) {
|
|
disable_it = true;
|
|
$announce_stream_checkbox.prop("checked", false);
|
|
$announce_stream_label.addClass("control-label-disabled");
|
|
} else {
|
|
// If the stream was already public, this will be a noop.
|
|
$announce_stream_checkbox.prop("checked", stream_announce_previous_value);
|
|
}
|
|
|
|
$announce_stream_checkbox.prop("disabled", disable_it);
|
|
$("#announce-new-stream").show();
|
|
}
|
|
|
|
function create_stream() {
|
|
const data = {};
|
|
const stream_name = $("#create_stream_name").val().trim();
|
|
const description = $("#create_stream_description").val().trim();
|
|
created_stream = stream_name;
|
|
|
|
// Even though we already check to make sure that while typing the user cannot enter
|
|
// newline characters (by pressing the Enter key) it would still be possible to copy
|
|
// and paste over a description with newline characters in it. Prevent that.
|
|
if (description.includes("\n")) {
|
|
ui_report.client_error(
|
|
$t_html({defaultMessage: "The stream description cannot contain newline characters."}),
|
|
$(".stream_create_info"),
|
|
);
|
|
return undefined;
|
|
}
|
|
data.subscriptions = JSON.stringify([{name: stream_name, description}]);
|
|
|
|
let invite_only;
|
|
let history_public_to_subscribers;
|
|
let is_web_public;
|
|
const privacy_setting = $("#stream_creation_form input[name=privacy]:checked").val();
|
|
|
|
switch (privacy_setting) {
|
|
case "invite-only": {
|
|
invite_only = true;
|
|
history_public_to_subscribers = false;
|
|
is_web_public = false;
|
|
|
|
break;
|
|
}
|
|
case "invite-only-public-history": {
|
|
invite_only = true;
|
|
history_public_to_subscribers = true;
|
|
is_web_public = false;
|
|
|
|
break;
|
|
}
|
|
case "web-public": {
|
|
invite_only = false;
|
|
history_public_to_subscribers = true;
|
|
is_web_public = true;
|
|
|
|
break;
|
|
}
|
|
default: {
|
|
invite_only = false;
|
|
history_public_to_subscribers = true;
|
|
is_web_public = false;
|
|
}
|
|
}
|
|
|
|
data.is_web_public = JSON.stringify(is_web_public);
|
|
data.invite_only = JSON.stringify(invite_only);
|
|
data.history_public_to_subscribers = JSON.stringify(history_public_to_subscribers);
|
|
|
|
const stream_post_policy = Number.parseInt(
|
|
$("#stream_creation_form select[name=stream-post-policy]").val(),
|
|
10,
|
|
);
|
|
|
|
data.stream_post_policy = JSON.stringify(stream_post_policy);
|
|
|
|
let message_retention_selection = $(
|
|
"#stream_creation_form select[name=stream_message_retention_setting]",
|
|
).val();
|
|
if (message_retention_selection === "retain_for_period") {
|
|
message_retention_selection = Number.parseInt(
|
|
$("#stream_creation_form input[name=stream-message-retention-days]").val(),
|
|
10,
|
|
);
|
|
}
|
|
|
|
data.message_retention_days = JSON.stringify(message_retention_selection);
|
|
|
|
const announce =
|
|
stream_data.realm_has_notifications_stream() &&
|
|
$("#announce-new-stream input").prop("checked");
|
|
data.announce = JSON.stringify(announce);
|
|
|
|
// TODO: We can eliminate the user_ids -> principals conversion
|
|
// once we upgrade the backend to accept user_ids.
|
|
const user_ids = stream_create_subscribers.get_principals();
|
|
data.principals = JSON.stringify(user_ids);
|
|
|
|
loading.make_indicator($("#stream_creating_indicator"), {
|
|
text: $t({defaultMessage: "Creating stream..."}),
|
|
});
|
|
|
|
// Subscribe yourself and possible other people to a new stream.
|
|
return channel.post({
|
|
url: "/json/users/me/subscriptions",
|
|
data,
|
|
success() {
|
|
$("#create_stream_name").val("");
|
|
$("#create_stream_description").val("");
|
|
ui_report.success(
|
|
$t_html({defaultMessage: "Stream successfully created!"}),
|
|
$(".stream_create_info"),
|
|
);
|
|
loading.destroy_indicator($("#stream_creating_indicator"));
|
|
// The rest of the work is done via the subscribe event we will get
|
|
},
|
|
error(xhr) {
|
|
const msg = JSON.parse(xhr.responseText).msg;
|
|
if (msg.includes("access")) {
|
|
// If we can't access the stream, we can safely assume it's
|
|
// a duplicate stream that we are not invited to.
|
|
//
|
|
// BUG: This check should be using error codes, not
|
|
// parsing the error string, so it works correctly
|
|
// with i18n. And likely we should be reporting the
|
|
// error text directly rather than turning it into
|
|
// "Error creating stream"?
|
|
stream_name_error.report_already_exists(stream_name);
|
|
stream_name_error.trigger("select");
|
|
}
|
|
|
|
ui_report.error(
|
|
$t_html({defaultMessage: "Error creating stream"}),
|
|
xhr,
|
|
$(".stream_create_info"),
|
|
);
|
|
loading.destroy_indicator($("#stream_creating_indicator"));
|
|
},
|
|
});
|
|
}
|
|
|
|
export function new_stream_clicked(stream_name) {
|
|
// this changes the tab switcher (settings/preview) which isn't necessary
|
|
// to a add new stream title.
|
|
stream_settings_ui.show_subs_pane.create_stream();
|
|
$(".stream-row.active").removeClass("active");
|
|
|
|
if (stream_name !== "") {
|
|
$("#create_stream_name").val(stream_name);
|
|
}
|
|
show_new_stream_modal();
|
|
$("#create_stream_name").trigger("focus");
|
|
}
|
|
|
|
function clear_error_display() {
|
|
stream_name_error.clear_errors();
|
|
$(".stream_create_info").hide();
|
|
stream_subscription_error.clear_errors();
|
|
}
|
|
|
|
export function show_new_stream_modal() {
|
|
$("#stream-creation").removeClass("hide");
|
|
$(".right .settings").hide();
|
|
stream_settings_ui.hide_or_disable_stream_privacy_options_if_required($("#stream-creation"));
|
|
|
|
stream_create_subscribers.build_widgets();
|
|
|
|
// Select the first visible and enabled choice for stream privacy.
|
|
$("#make-invite-only input:visible:not([disabled])").first().prop("checked", true);
|
|
// Make the options default to the same each time:
|
|
// "announce stream" on.
|
|
$("#stream_creation_form .stream-message-retention-days-input").hide();
|
|
$("#stream_creation_form select[name=stream_message_retention_setting]").val("realm_default");
|
|
|
|
// Add listener to .show stream-message-retention-days-input that we've hidden above
|
|
$("#stream_creation_form .stream_message_retention_setting").on("change", (e) => {
|
|
if (e.target.value === "retain_for_period") {
|
|
$("#stream_creation_form .stream-message-retention-days-input").show();
|
|
} else {
|
|
$("#stream_creation_form .stream-message-retention-days-input").hide();
|
|
}
|
|
});
|
|
|
|
update_announce_stream_state();
|
|
clear_error_display();
|
|
}
|
|
|
|
export function set_up_handlers() {
|
|
const $people_to_add_holder = $("#people_to_add").expectOne();
|
|
stream_create_subscribers.create_handlers($people_to_add_holder);
|
|
|
|
const $container = $("#stream-creation").expectOne();
|
|
|
|
$container.on("change", ".stream-privacy-values input", update_announce_stream_state);
|
|
|
|
$container.on("click", ".finalize_create_stream", (e) => {
|
|
e.preventDefault();
|
|
clear_error_display();
|
|
|
|
const stream_name = $("#create_stream_name").val().trim();
|
|
const name_ok = stream_name_error.validate_for_submit(stream_name);
|
|
|
|
if (!name_ok) {
|
|
return;
|
|
}
|
|
|
|
const principals = stream_create_subscribers.get_principals();
|
|
if (principals.length === 0) {
|
|
stream_subscription_error.report_no_subs_to_stream();
|
|
return;
|
|
}
|
|
if (!principals.includes(people.my_current_user_id()) && !page_params.is_admin) {
|
|
stream_subscription_error.cant_create_stream_without_susbscribing();
|
|
return;
|
|
}
|
|
|
|
if (principals.length >= 50) {
|
|
const html_body = render_subscription_invites_warning_modal({
|
|
stream_name,
|
|
count: principals.length,
|
|
});
|
|
|
|
confirm_dialog.launch({
|
|
html_heading: $t_html({defaultMessage: "Large number of subscribers"}),
|
|
html_body,
|
|
on_click: () => {
|
|
create_stream();
|
|
},
|
|
});
|
|
} else {
|
|
create_stream();
|
|
}
|
|
});
|
|
|
|
$container.on("input", "#create_stream_name", () => {
|
|
const stream_name = $("#create_stream_name").val().trim();
|
|
|
|
// This is an inexpensive check.
|
|
stream_name_error.pre_validate(stream_name);
|
|
});
|
|
|
|
tippy("#announce-stream-docs", {
|
|
content: () =>
|
|
parse_html(
|
|
render_announce_stream_docs({
|
|
notifications_stream: stream_data.get_notifications_stream(),
|
|
}),
|
|
),
|
|
});
|
|
|
|
// Do not allow the user to enter newline characters while typing out the
|
|
// stream's description during it's creation.
|
|
$container.on("keydown", "#create_stream_description", (e) => {
|
|
if (e.key === "Enter") {
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
}
|