2021-03-11 05:43:45 +01:00
|
|
|
import $ from "jquery";
|
2021-02-28 21:32:47 +01:00
|
|
|
import _ from "lodash";
|
2021-04-09 09:30:31 +02:00
|
|
|
import tippy from "tippy.js";
|
2021-02-28 21:32:47 +01:00
|
|
|
|
2021-06-21 09:40:03 +02:00
|
|
|
import render_unsubscribe_private_stream_modal from "../templates/confirm_dialog/confirm_unsubscribe_private_stream.hbs";
|
2021-07-19 19:13:25 +02:00
|
|
|
import render_browse_streams_list from "../templates/stream_settings/browse_streams_list.hbs";
|
|
|
|
import render_browse_streams_list_item from "../templates/stream_settings/browse_streams_list_item.hbs";
|
2021-07-18 13:49:31 +02:00
|
|
|
import render_stream_settings from "../templates/stream_settings/stream_settings.hbs";
|
2021-07-19 07:04:32 +02:00
|
|
|
import render_stream_settings_overlay from "../templates/stream_settings/stream_settings_overlay.hbs";
|
2021-02-28 21:32:47 +01:00
|
|
|
|
2021-03-16 23:38:59 +01:00
|
|
|
import * as blueslip from "./blueslip";
|
2021-03-22 16:09:12 +01:00
|
|
|
import * as browser_history from "./browser_history";
|
2021-02-28 21:32:47 +01:00
|
|
|
import * as channel from "./channel";
|
|
|
|
import * as components from "./components";
|
|
|
|
import * as compose_state from "./compose_state";
|
2020-08-07 00:38:19 +02:00
|
|
|
import * as confirm_dialog from "./confirm_dialog";
|
2021-02-28 21:32:47 +01:00
|
|
|
import * as hash_util from "./hash_util";
|
2021-04-13 06:51:54 +02:00
|
|
|
import {$t, $t_html} from "./i18n";
|
2021-02-28 21:32:47 +01:00
|
|
|
import * as loading from "./loading";
|
|
|
|
import * as message_live_update from "./message_live_update";
|
|
|
|
import * as message_view_header from "./message_view_header";
|
|
|
|
import * as overlays from "./overlays";
|
2021-03-25 22:35:45 +01:00
|
|
|
import {page_params} from "./page_params";
|
2021-02-28 21:32:47 +01:00
|
|
|
import * as people from "./people";
|
|
|
|
import * as scroll_util from "./scroll_util";
|
|
|
|
import * as search_util from "./search_util";
|
2021-04-28 21:58:33 +02:00
|
|
|
import * as settings_data from "./settings_data";
|
2021-02-28 21:32:47 +01:00
|
|
|
import * as stream_create from "./stream_create";
|
|
|
|
import * as stream_data from "./stream_data";
|
|
|
|
import * as stream_edit from "./stream_edit";
|
|
|
|
import * as stream_list from "./stream_list";
|
|
|
|
import * as stream_muting from "./stream_muting";
|
2021-04-04 15:15:18 +02:00
|
|
|
import * as stream_settings_data from "./stream_settings_data";
|
2021-02-28 21:32:47 +01:00
|
|
|
import * as stream_ui_updates from "./stream_ui_updates";
|
2021-04-15 17:02:54 +02:00
|
|
|
import * as sub_store from "./sub_store";
|
2021-02-28 21:33:10 +01:00
|
|
|
import * as ui from "./ui";
|
2021-02-28 21:32:47 +01:00
|
|
|
import * as ui_report from "./ui_report";
|
|
|
|
import * as util from "./util";
|
|
|
|
|
|
|
|
export const show_subs_pane = {
|
2020-07-20 22:18:43 +02:00
|
|
|
nothing_selected() {
|
2021-07-04 18:40:52 +02:00
|
|
|
$(".settings, #stream-creation").hide();
|
|
|
|
$(".nothing-selected").show();
|
2021-07-04 18:12:08 +02:00
|
|
|
$("#subscription_overlay .stream-info-title").text($t({defaultMessage: "Stream settings"}));
|
2017-04-24 04:11:25 +02:00
|
|
|
},
|
2021-07-04 18:12:08 +02:00
|
|
|
settings(stream_name) {
|
2021-07-04 18:40:52 +02:00
|
|
|
$(".settings, #stream-creation").hide();
|
|
|
|
$(".settings").show();
|
2021-07-04 18:12:08 +02:00
|
|
|
$("#subscription_overlay .stream-info-title").text(
|
|
|
|
$t({defaultMessage: "Settings for #{stream_name}"}, {stream_name}),
|
|
|
|
);
|
2020-07-08 23:54:13 +02:00
|
|
|
},
|
|
|
|
create_stream() {
|
2021-07-04 18:40:52 +02:00
|
|
|
$(".nothing-selected, .settings, #stream-creation").hide();
|
|
|
|
$("#stream-creation").show();
|
2021-07-04 18:12:08 +02:00
|
|
|
$("#subscription_overlay .stream-info-title").text($t({defaultMessage: "Create stream"}));
|
2017-04-24 04:11:25 +02:00
|
|
|
},
|
|
|
|
};
|
2016-11-04 22:18:23 +01:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function row_for_stream_id(stream_id) {
|
2021-02-03 23:23:32 +01:00
|
|
|
return $(`.stream-row[data-stream-id='${CSS.escape(stream_id)}']`);
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2017-02-15 17:38:44 +01:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function is_sub_already_present(sub) {
|
|
|
|
return row_for_stream_id(sub.stream_id).length > 0;
|
|
|
|
}
|
2021-02-17 18:48:10 +01:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function update_left_panel_row(sub) {
|
|
|
|
const row = row_for_stream_id(sub.stream_id);
|
2021-02-17 19:08:18 +01:00
|
|
|
|
|
|
|
if (row.length === 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
blueslip.debug(`Updating row in left panel of stream settings for: ${sub.name}`);
|
2021-04-04 15:15:18 +02:00
|
|
|
const setting_sub = stream_settings_data.get_sub_for_settings(sub);
|
2021-07-19 19:13:25 +02:00
|
|
|
const html = render_browse_streams_list_item(setting_sub);
|
2021-02-17 19:08:18 +01:00
|
|
|
const new_row = $(html);
|
|
|
|
|
|
|
|
// TODO: Clean up this hack when we eliminate `notdisplayed`
|
|
|
|
if (row.hasClass("notdisplayed")) {
|
|
|
|
new_row.addClass("notdisplayed");
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Remove this if/when we just handle "active" when rendering templates.
|
|
|
|
if (row.hasClass("active")) {
|
|
|
|
new_row.addClass("active");
|
|
|
|
}
|
|
|
|
|
|
|
|
row.replaceWith(new_row);
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2021-02-17 19:08:18 +01:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function settings_button_for_sub(sub) {
|
2018-03-23 23:21:57 +01:00
|
|
|
// We don't do expectOne() here, because this button is only
|
|
|
|
// visible if the user has that stream selected in the streams UI.
|
2021-02-03 23:23:32 +01:00
|
|
|
return $(
|
2021-07-21 07:25:53 +02:00
|
|
|
`.stream_settings_header[data-stream-id='${CSS.escape(sub.stream_id)}'] .subscribe-button`,
|
2021-02-03 23:23:32 +01:00
|
|
|
);
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2017-01-27 00:40:42 +01:00
|
|
|
|
2017-03-23 05:15:16 +01:00
|
|
|
function get_row_data(row) {
|
2020-10-07 09:17:30 +02:00
|
|
|
const row_id = Number.parseInt(row.attr("data-stream-id"), 10);
|
2017-03-23 05:15:16 +01:00
|
|
|
if (row_id) {
|
2021-04-15 17:02:54 +02:00
|
|
|
const row_object = sub_store.get(row_id);
|
2017-03-25 01:27:52 +01:00
|
|
|
return {
|
|
|
|
id: row_id,
|
|
|
|
object: row_object,
|
|
|
|
};
|
2017-03-23 05:15:16 +01:00
|
|
|
}
|
2020-09-24 07:50:36 +02:00
|
|
|
return undefined;
|
2017-03-23 05:15:16 +01:00
|
|
|
}
|
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function get_active_data() {
|
2020-07-15 01:29:15 +02:00
|
|
|
const active_row = $("div.stream-row.active");
|
2020-10-07 09:17:30 +02:00
|
|
|
const valid_active_id = Number.parseInt(active_row.attr("data-stream-id"), 10);
|
2020-07-09 15:30:44 +02:00
|
|
|
const active_tabs = $(".subscriptions-container").find("div.ind-tab.selected");
|
2017-03-25 01:27:52 +01:00
|
|
|
return {
|
|
|
|
row: active_row,
|
|
|
|
id: valid_active_id,
|
2020-07-09 15:30:44 +02:00
|
|
|
tabs: active_tabs,
|
2017-03-25 01:27:52 +01:00
|
|
|
};
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2017-03-23 05:15:16 +01:00
|
|
|
|
2013-11-08 10:12:05 +01:00
|
|
|
function selectText(element) {
|
2021-06-25 22:56:06 +02:00
|
|
|
const sel = window.getSelection();
|
|
|
|
const range = document.createRange();
|
|
|
|
range.selectNodeContents(element);
|
|
|
|
|
|
|
|
sel.removeAllRanges();
|
|
|
|
sel.addRange(range);
|
2013-11-08 10:12:05 +01:00
|
|
|
}
|
|
|
|
|
2013-01-23 17:51:01 +01:00
|
|
|
function should_list_all_streams() {
|
2017-04-20 08:03:44 +02:00
|
|
|
return !page_params.realm_is_zephyr_mirror_realm;
|
2013-01-23 17:51:01 +01:00
|
|
|
}
|
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function set_muted(sub, is_muted, status_element) {
|
2020-07-22 00:43:11 +02:00
|
|
|
stream_muting.update_is_muted(sub, is_muted);
|
2020-07-15 01:29:15 +02:00
|
|
|
stream_edit.set_stream_property(sub, "is_muted", sub.is_muted, status_element);
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2013-02-12 22:32:14 +01:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function toggle_pin_to_top_stream(sub) {
|
2020-07-15 01:29:15 +02:00
|
|
|
stream_edit.set_stream_property(sub, "pin_to_top", !sub.pin_to_top);
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2016-07-01 07:26:09 +02:00
|
|
|
|
2020-05-15 02:12:16 +02:00
|
|
|
let subscribed_only = true;
|
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function is_subscribed_stream_tab_active() {
|
2019-04-01 10:59:02 +02:00
|
|
|
// Returns true if "Subscribed" tab in stream settings is open
|
|
|
|
// otherwise false.
|
2020-05-15 02:12:16 +02:00
|
|
|
return subscribed_only;
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2019-04-01 10:59:02 +02:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function update_stream_name(sub, new_name) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const old_name = sub.name;
|
2019-07-19 20:12:06 +02:00
|
|
|
|
2013-08-21 23:21:31 +02:00
|
|
|
// Rename the stream internally.
|
2017-03-05 00:35:45 +01:00
|
|
|
stream_data.rename_sub(sub, new_name);
|
2019-11-02 00:06:25 +01:00
|
|
|
const stream_id = sub.stream_id;
|
2013-08-21 23:21:31 +02:00
|
|
|
|
2013-10-21 22:26:19 +02:00
|
|
|
// Update the left sidebar.
|
2016-10-30 17:33:23 +01:00
|
|
|
stream_list.rename_stream(sub, new_name);
|
2013-08-21 23:21:31 +02:00
|
|
|
|
2016-11-05 00:11:45 +01:00
|
|
|
// Update the stream settings
|
2017-04-24 04:11:25 +02:00
|
|
|
stream_edit.update_stream_name(sub, new_name);
|
2016-11-05 00:11:45 +01:00
|
|
|
|
2016-10-29 01:30:51 +02:00
|
|
|
// Update the subscriptions page
|
2021-02-28 21:32:47 +01:00
|
|
|
const sub_row = row_for_stream_id(stream_id);
|
2016-11-05 00:10:01 +01:00
|
|
|
sub_row.find(".stream-name").text(new_name);
|
2016-10-29 01:30:51 +02:00
|
|
|
|
2013-08-21 23:21:31 +02:00
|
|
|
// Update the message feed.
|
2017-01-05 17:34:27 +01:00
|
|
|
message_live_update.update_stream_name(stream_id, new_name);
|
2019-07-19 19:42:10 +02:00
|
|
|
|
2019-07-19 20:12:06 +02:00
|
|
|
// Update compose_state if needed
|
|
|
|
if (compose_state.stream_name() === old_name) {
|
|
|
|
compose_state.stream_name(new_name);
|
|
|
|
}
|
2020-02-03 17:01:11 +01:00
|
|
|
|
2020-06-16 00:27:26 +02:00
|
|
|
// Update navbar if needed
|
2020-07-08 23:44:01 +02:00
|
|
|
message_view_header.maybe_rerender_title_area_for_stream(sub);
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2013-08-21 23:21:31 +02:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function update_stream_description(sub, description, rendered_description) {
|
2014-01-24 18:19:27 +01:00
|
|
|
sub.description = description;
|
2021-04-04 19:07:25 +02:00
|
|
|
sub.rendered_description = rendered_description;
|
|
|
|
stream_data.clean_up_description(sub);
|
2014-01-24 18:19:27 +01:00
|
|
|
|
2016-11-04 22:11:23 +01:00
|
|
|
// Update stream row
|
2021-02-28 21:32:47 +01:00
|
|
|
const sub_row = row_for_stream_id(sub.stream_id);
|
2020-02-28 23:59:07 +01:00
|
|
|
sub_row.find(".description").html(util.clean_user_content_links(sub.rendered_description));
|
2016-11-04 22:11:23 +01:00
|
|
|
|
|
|
|
// Update stream settings
|
2017-04-24 04:11:25 +02:00
|
|
|
stream_edit.update_stream_description(sub);
|
2020-02-03 17:01:11 +01:00
|
|
|
|
2020-05-16 20:11:30 +02:00
|
|
|
// Update navbar if needed
|
2020-07-08 23:44:01 +02:00
|
|
|
message_view_header.maybe_rerender_title_area_for_stream(sub);
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2014-01-24 18:19:27 +01:00
|
|
|
|
2021-06-18 16:15:44 +02:00
|
|
|
export function update_stream_privacy(slim_sub, values) {
|
|
|
|
stream_data.update_stream_privacy(slim_sub, values);
|
|
|
|
const sub = stream_settings_data.get_sub_for_settings(slim_sub);
|
2019-05-07 07:12:14 +02:00
|
|
|
|
|
|
|
// Update UI elements
|
2021-02-28 21:32:47 +01:00
|
|
|
update_left_panel_row(sub);
|
2020-02-04 21:50:55 +01:00
|
|
|
stream_ui_updates.update_stream_subscription_type_text(sub);
|
2019-05-07 07:12:14 +02:00
|
|
|
stream_ui_updates.update_change_stream_privacy_settings(sub);
|
2019-05-07 07:13:17 +02:00
|
|
|
stream_ui_updates.update_settings_button_for_sub(sub);
|
|
|
|
stream_ui_updates.update_add_subscriptions_elements(sub);
|
2019-05-07 07:12:14 +02:00
|
|
|
stream_list.redraw_stream_privacy(sub);
|
2020-06-08 23:04:12 +02:00
|
|
|
|
2020-06-16 00:27:26 +02:00
|
|
|
// Update navbar if needed
|
2020-07-08 23:44:01 +02:00
|
|
|
message_view_header.maybe_rerender_title_area_for_stream(sub);
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2019-05-07 07:12:14 +02:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function update_stream_post_policy(sub, new_value) {
|
2020-02-04 21:50:55 +01:00
|
|
|
stream_data.update_stream_post_policy(sub, new_value);
|
|
|
|
stream_ui_updates.update_stream_subscription_type_text(sub);
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2019-05-07 07:12:14 +02:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function update_message_retention_setting(sub, new_value) {
|
2020-06-16 13:59:02 +02:00
|
|
|
stream_data.update_message_retention_setting(sub, new_value);
|
|
|
|
stream_ui_updates.update_stream_subscription_type_text(sub);
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2020-06-16 13:59:02 +02:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function set_color(stream_id, color) {
|
2021-04-15 17:02:54 +02:00
|
|
|
const sub = sub_store.get(stream_id);
|
2020-07-15 01:29:15 +02:00
|
|
|
stream_edit.set_stream_property(sub, "color", color);
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2013-05-17 21:35:17 +02:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function update_subscribers_ui(sub) {
|
|
|
|
update_left_panel_row(sub);
|
2019-05-03 07:26:56 +02:00
|
|
|
stream_ui_updates.update_subscribers_list(sub);
|
2020-07-08 23:44:01 +02:00
|
|
|
message_view_header.maybe_rerender_title_area_for_stream(sub);
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2020-06-22 23:17:23 +02:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function add_sub_to_table(sub) {
|
|
|
|
if (is_sub_already_present(sub)) {
|
2018-04-10 15:49:03 +02:00
|
|
|
// If a stream is already listed/added in subscription modal,
|
2019-04-01 11:21:29 +02:00
|
|
|
// display stream in `Subscribed` tab and return.
|
|
|
|
// This can happen in some corner cases (which might
|
2018-04-10 15:49:03 +02:00
|
|
|
// be backend bugs) where a realm adminsitrator is subscribed
|
|
|
|
// to a private stream, in which case they might get two
|
|
|
|
// stream-create events.
|
2019-04-01 11:21:29 +02:00
|
|
|
stream_ui_updates.update_stream_row_in_settings_tab(sub);
|
2018-04-08 07:06:42 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-04-04 15:15:18 +02:00
|
|
|
const setting_sub = stream_settings_data.get_sub_for_settings(sub);
|
2021-07-19 19:13:25 +02:00
|
|
|
const html = render_browse_streams_list_item(setting_sub);
|
2021-02-17 20:57:19 +01:00
|
|
|
const new_row = $(html);
|
2021-01-21 21:14:53 +01:00
|
|
|
|
2017-04-22 22:22:25 +02:00
|
|
|
if (stream_create.get_name() === sub.name) {
|
2021-02-17 20:57:19 +01:00
|
|
|
ui.get_content_element($(".streams-list")).prepend(new_row);
|
2019-03-01 01:40:05 +01:00
|
|
|
ui.reset_scrollbar($(".streams-list"));
|
2017-02-18 00:03:43 +01:00
|
|
|
} else {
|
2021-02-17 20:57:19 +01:00
|
|
|
ui.get_content_element($(".streams-list")).append(new_row);
|
2017-02-18 00:03:43 +01:00
|
|
|
}
|
2021-02-17 20:57:19 +01:00
|
|
|
|
2021-07-18 13:49:31 +02:00
|
|
|
const settings_html = render_stream_settings(sub);
|
2019-03-01 01:40:05 +01:00
|
|
|
ui.get_content_element($(".subscriptions .settings")).append($(settings_html));
|
2016-11-01 22:32:10 +01:00
|
|
|
|
2017-04-22 22:22:25 +02:00
|
|
|
if (stream_create.get_name() === sub.name) {
|
2018-03-10 18:39:49 +01:00
|
|
|
// This `stream_create.get_name()` check tells us whether the
|
|
|
|
// stream was just created in this browser window; it's a hack
|
|
|
|
// to work around the server_events code flow not having a
|
|
|
|
// good way to associate with this request because the stream
|
|
|
|
// ID isn't known yet. These are appended to the top of the
|
|
|
|
// list, so they are more visible.
|
2021-02-28 21:32:47 +01:00
|
|
|
row_for_stream_id(sub.stream_id).trigger("click");
|
2017-04-22 22:22:25 +02:00
|
|
|
stream_create.reset_created_stream();
|
2016-11-01 22:32:10 +01:00
|
|
|
}
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2013-01-23 19:43:11 +01:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function remove_stream(stream_id) {
|
2017-02-15 17:38:44 +01:00
|
|
|
// It is possible that row is empty when we deactivate a
|
|
|
|
// stream, but we let jQuery silently handle that.
|
2021-02-28 21:32:47 +01:00
|
|
|
const row = row_for_stream_id(stream_id);
|
2017-02-15 17:38:44 +01:00
|
|
|
row.remove();
|
2021-04-15 17:02:54 +02:00
|
|
|
const sub = sub_store.get(stream_id);
|
2018-08-07 15:41:00 +02:00
|
|
|
if (stream_edit.is_sub_settings_active(sub)) {
|
2020-05-02 00:52:45 +02:00
|
|
|
stream_edit.open_edit_panel_empty();
|
2018-08-07 15:41:00 +02:00
|
|
|
}
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2017-02-15 17:38:44 +01:00
|
|
|
|
2021-05-27 21:01:49 +02:00
|
|
|
export function update_settings_for_subscribed(slim_sub) {
|
|
|
|
const sub = stream_settings_data.get_sub_for_settings(slim_sub);
|
2019-05-03 07:59:31 +02:00
|
|
|
stream_ui_updates.update_add_subscriptions_elements(sub);
|
2020-07-15 00:34:28 +02:00
|
|
|
$(
|
2021-02-03 23:23:32 +01:00
|
|
|
`.subscription_settings[data-stream-id='${CSS.escape(
|
|
|
|
sub.stream_id,
|
|
|
|
)}'] #preview-stream-button`,
|
2020-07-15 00:34:28 +02:00
|
|
|
).show();
|
2017-01-20 22:03:52 +01:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
if (is_sub_already_present(sub)) {
|
|
|
|
update_left_panel_row(sub);
|
2021-06-26 15:28:24 +02:00
|
|
|
stream_ui_updates.update_toggler_for_sub(sub);
|
2019-04-01 11:21:29 +02:00
|
|
|
stream_ui_updates.update_stream_row_in_settings_tab(sub);
|
2019-03-31 13:00:32 +02:00
|
|
|
stream_ui_updates.update_settings_button_for_sub(sub);
|
2019-04-02 18:43:02 +02:00
|
|
|
stream_ui_updates.update_change_stream_privacy_settings(sub);
|
2013-01-22 23:16:04 +01:00
|
|
|
} else {
|
2021-02-28 21:32:47 +01:00
|
|
|
add_sub_to_table(sub);
|
2012-10-18 21:37:07 +02:00
|
|
|
}
|
2013-03-21 22:13:17 +01:00
|
|
|
|
2019-05-03 07:26:56 +02:00
|
|
|
stream_ui_updates.update_subscribers_list(sub);
|
2017-10-11 23:45:03 +02:00
|
|
|
|
2017-03-17 23:07:20 +01:00
|
|
|
// Display the swatch and subscription stream_settings
|
2019-04-02 18:37:24 +02:00
|
|
|
stream_ui_updates.update_regular_sub_settings(sub);
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2012-10-18 21:37:07 +02:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function show_active_stream_in_left_panel() {
|
2021-03-15 09:25:22 +01:00
|
|
|
const selected_row = hash_util.get_current_hash_section();
|
2018-11-29 22:22:21 +01:00
|
|
|
|
2020-10-07 09:17:30 +02:00
|
|
|
if (Number.parseFloat(selected_row)) {
|
2021-02-28 21:32:47 +01:00
|
|
|
const sub_row = row_for_stream_id(selected_row);
|
2018-11-29 22:22:21 +01:00
|
|
|
sub_row.addClass("active");
|
|
|
|
}
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2018-11-29 22:22:21 +01:00
|
|
|
|
2021-06-18 16:15:44 +02:00
|
|
|
export function update_settings_for_unsubscribed(slim_sub) {
|
|
|
|
const sub = stream_settings_data.get_sub_for_settings(slim_sub);
|
2021-02-28 21:32:47 +01:00
|
|
|
update_left_panel_row(sub);
|
2021-02-17 19:13:44 +01:00
|
|
|
stream_ui_updates.update_subscribers_list(sub);
|
2021-06-26 15:28:24 +02:00
|
|
|
stream_ui_updates.update_toggler_for_sub(sub);
|
2019-03-31 13:00:32 +02:00
|
|
|
stream_ui_updates.update_settings_button_for_sub(sub);
|
2019-04-02 18:37:24 +02:00
|
|
|
stream_ui_updates.update_regular_sub_settings(sub);
|
2019-04-02 18:43:02 +02:00
|
|
|
stream_ui_updates.update_change_stream_privacy_settings(sub);
|
2017-04-24 04:11:25 +02:00
|
|
|
|
2018-04-07 06:05:52 +02:00
|
|
|
stream_data.update_stream_email_address(sub, "");
|
2019-05-03 07:59:31 +02:00
|
|
|
// If user unsubscribed from private stream then user cannot subscribe to
|
|
|
|
// stream without invitation and cannot add subscribers to stream.
|
2021-04-04 17:23:40 +02:00
|
|
|
if (!stream_data.can_toggle_subscription(sub)) {
|
2019-05-03 07:59:31 +02:00
|
|
|
stream_ui_updates.update_add_subscriptions_elements(sub);
|
2017-10-11 23:45:03 +02:00
|
|
|
}
|
2019-05-09 07:34:31 +02:00
|
|
|
if (page_params.is_guest) {
|
|
|
|
stream_edit.open_edit_panel_empty();
|
|
|
|
}
|
2017-10-11 23:45:03 +02:00
|
|
|
|
2018-02-09 19:45:40 +01:00
|
|
|
// Remove private streams from subscribed streams list.
|
2019-04-01 11:04:41 +02:00
|
|
|
stream_ui_updates.update_stream_row_in_settings_tab(sub);
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2013-03-29 20:27:41 +01:00
|
|
|
|
2021-02-15 18:46:17 +01:00
|
|
|
function triage_stream(left_panel_params, sub) {
|
|
|
|
if (left_panel_params.subscribed_only && !sub.subscribed) {
|
2018-07-29 19:28:06 +02:00
|
|
|
// reject non-subscribed streams
|
2020-12-22 11:26:39 +01:00
|
|
|
return "rejected";
|
2018-07-29 19:28:06 +02:00
|
|
|
}
|
|
|
|
|
2021-02-15 18:46:17 +01:00
|
|
|
const search_terms = search_util.get_search_terms(left_panel_params.input);
|
2018-07-29 16:11:48 +02:00
|
|
|
|
2018-07-29 19:40:32 +02:00
|
|
|
function match(attr) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const val = sub[attr];
|
2018-07-29 19:40:32 +02:00
|
|
|
|
|
|
|
return search_util.vanilla_match({
|
2020-07-20 22:18:43 +02:00
|
|
|
val,
|
|
|
|
search_terms,
|
2018-07-29 19:40:32 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
if (match("name")) {
|
|
|
|
return "name_match";
|
2018-07-29 19:40:32 +02:00
|
|
|
}
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
if (match("description")) {
|
|
|
|
return "desc_match";
|
2018-07-29 19:40:32 +02:00
|
|
|
}
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
return "rejected";
|
2016-12-26 10:14:18 +01:00
|
|
|
}
|
|
|
|
|
2021-02-15 18:46:17 +01:00
|
|
|
function get_stream_id_buckets(stream_ids, left_panel_params) {
|
2018-07-29 20:20:46 +02:00
|
|
|
// When we simplify the settings UI, we can get
|
|
|
|
// rid of the "others" bucket.
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const buckets = {
|
2018-07-29 20:20:46 +02:00
|
|
|
name: [],
|
|
|
|
desc: [],
|
|
|
|
other: [],
|
|
|
|
};
|
|
|
|
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
|
|
|
for (const stream_id of stream_ids) {
|
2021-04-15 17:02:54 +02:00
|
|
|
const sub = sub_store.get(stream_id);
|
2021-02-15 18:46:17 +01:00
|
|
|
const match_status = triage_stream(left_panel_params, sub);
|
2018-07-29 20:20:46 +02:00
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
if (match_status === "name_match") {
|
2018-07-29 20:20:46 +02:00
|
|
|
buckets.name.push(stream_id);
|
2020-07-15 01:29:15 +02:00
|
|
|
} else if (match_status === "desc_match") {
|
2018-07-29 20:20:46 +02:00
|
|
|
buckets.desc.push(stream_id);
|
|
|
|
} else {
|
|
|
|
buckets.other.push(stream_id);
|
|
|
|
}
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
|
|
|
}
|
2018-07-29 20:20:46 +02:00
|
|
|
|
2021-04-04 15:15:18 +02:00
|
|
|
stream_settings_data.sort_for_stream_settings(buckets.name, left_panel_params.sort_order);
|
|
|
|
stream_settings_data.sort_for_stream_settings(buckets.desc, left_panel_params.sort_order);
|
2018-07-29 20:20:46 +02:00
|
|
|
|
|
|
|
return buckets;
|
|
|
|
}
|
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function render_left_panel_superset() {
|
2021-02-15 18:18:51 +01:00
|
|
|
// For annoying legacy reasons we render all the subs we are
|
|
|
|
// allowed to know about and put them in the DOM, then we do
|
|
|
|
// a second pass where we filter/sort them.
|
2021-02-04 00:30:50 +01:00
|
|
|
const html = blueslip.measure_time("render left panel", () => {
|
2021-04-04 15:15:18 +02:00
|
|
|
const sub_rows = stream_settings_data.get_updated_unsorted_subs();
|
2020-01-15 15:05:44 +01:00
|
|
|
|
2021-01-31 13:57:52 +01:00
|
|
|
const template_data = {
|
|
|
|
subscriptions: sub_rows,
|
|
|
|
};
|
2020-01-15 15:05:44 +01:00
|
|
|
|
2021-07-19 19:13:25 +02:00
|
|
|
return render_browse_streams_list(template_data);
|
2021-01-31 13:57:52 +01:00
|
|
|
});
|
2020-01-15 15:05:44 +01:00
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
ui.get_content_element($("#subscriptions_table .streams-list")).html(html);
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2018-07-24 14:27:32 +02:00
|
|
|
|
2021-02-15 18:46:17 +01:00
|
|
|
// LeftPanelParams { input: String, subscribed_only: Boolean, sort_order: String }
|
2021-03-24 21:44:43 +01:00
|
|
|
export function redraw_left_panel(left_panel_params = get_left_panel_params()) {
|
2021-02-17 12:55:17 +01:00
|
|
|
// We only get left_panel_params passed in from tests. Real
|
|
|
|
// code calls get_left_panel_params().
|
2021-02-28 21:32:47 +01:00
|
|
|
show_active_stream_in_left_panel();
|
2017-10-11 23:00:36 +02:00
|
|
|
|
2019-12-30 12:51:16 +01:00
|
|
|
function stream_id_for_row(row) {
|
2020-10-07 09:17:30 +02:00
|
|
|
return Number.parseInt($(row).attr("data-stream-id"), 10);
|
2019-12-30 12:51:16 +01:00
|
|
|
}
|
|
|
|
|
2020-02-12 07:49:46 +01:00
|
|
|
const widgets = new Map();
|
2017-01-03 13:26:48 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const stream_ids = [];
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
|
|
|
|
|
|
|
for (const row of $("#subscriptions_table .stream-row")) {
|
2019-12-30 12:51:16 +01:00
|
|
|
const stream_id = stream_id_for_row(row);
|
2018-07-29 20:20:46 +02:00
|
|
|
stream_ids.push(stream_id);
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
|
|
|
}
|
2016-10-12 08:53:57 +02:00
|
|
|
|
2021-02-15 18:46:17 +01:00
|
|
|
const buckets = get_stream_id_buckets(stream_ids, left_panel_params);
|
2018-07-29 19:40:32 +02:00
|
|
|
|
2018-07-29 20:20:46 +02:00
|
|
|
// If we just re-built the DOM from scratch we wouldn't need
|
|
|
|
// all this hidden/notdisplayed logic.
|
2020-02-12 07:50:20 +01:00
|
|
|
const hidden_ids = new Set();
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
|
|
|
|
|
|
|
for (const stream_id of buckets.other) {
|
2020-02-12 07:50:20 +01:00
|
|
|
hidden_ids.add(stream_id);
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
|
|
|
}
|
2018-07-29 20:20:46 +02:00
|
|
|
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
|
|
|
for (const row of $("#subscriptions_table .stream-row")) {
|
2019-12-30 12:51:16 +01:00
|
|
|
const stream_id = stream_id_for_row(row);
|
2018-07-29 20:20:46 +02:00
|
|
|
|
|
|
|
// Below code goes away if we don't do sort-DOM-in-place.
|
2020-02-12 07:50:20 +01:00
|
|
|
if (hidden_ids.has(stream_id)) {
|
2020-07-15 01:29:15 +02:00
|
|
|
$(row).addClass("notdisplayed");
|
2018-05-06 21:43:17 +02:00
|
|
|
} else {
|
2020-07-15 01:29:15 +02:00
|
|
|
$(row).removeClass("notdisplayed");
|
2016-10-12 08:53:57 +02:00
|
|
|
}
|
2018-03-15 05:24:00 +01:00
|
|
|
|
2020-02-12 07:49:46 +01:00
|
|
|
widgets.set(stream_id, $(row).detach());
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
|
|
|
}
|
2016-11-01 22:32:10 +01:00
|
|
|
|
2019-01-09 14:30:35 +01:00
|
|
|
ui.reset_scrollbar($("#subscription_overlay .streams-list"));
|
2017-07-28 13:26:19 +02:00
|
|
|
|
2020-07-15 00:34:28 +02:00
|
|
|
const all_stream_ids = [...buckets.name, ...buckets.desc, ...buckets.other];
|
2017-01-03 13:26:48 +01:00
|
|
|
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
|
|
|
for (const stream_id of all_stream_ids) {
|
2020-07-15 00:34:28 +02:00
|
|
|
ui.get_content_element($("#subscriptions_table .streams-list")).append(
|
|
|
|
widgets.get(stream_id),
|
|
|
|
);
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
|
|
|
}
|
2021-02-28 21:32:47 +01:00
|
|
|
maybe_reset_right_panel();
|
2021-02-16 17:35:06 +01:00
|
|
|
|
|
|
|
// return this for test convenience
|
|
|
|
return [...buckets.name, ...buckets.desc];
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2016-10-12 08:53:57 +02:00
|
|
|
|
2020-03-31 17:13:01 +02:00
|
|
|
let sort_order = "by-stream-name";
|
2018-04-04 21:29:32 +02:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function get_left_panel_params() {
|
2020-03-21 19:12:23 +01:00
|
|
|
const search_box = $("#stream_filter input[type='text']");
|
2019-11-02 00:06:25 +01:00
|
|
|
const input = search_box.expectOne().val().trim();
|
|
|
|
const params = {
|
2020-07-20 22:18:43 +02:00
|
|
|
input,
|
|
|
|
subscribed_only,
|
|
|
|
sort_order,
|
2018-11-29 23:11:07 +01:00
|
|
|
};
|
|
|
|
return params;
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2018-11-29 23:11:07 +01:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function maybe_reset_right_panel() {
|
2018-11-29 23:21:06 +01:00
|
|
|
if ($(".stream-row.active").hasClass("notdisplayed")) {
|
|
|
|
$(".right .settings").hide();
|
|
|
|
$(".nothing-selected").show();
|
|
|
|
$(".stream-row.active").removeClass("active");
|
|
|
|
}
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2018-11-29 23:21:06 +01:00
|
|
|
|
2018-04-14 14:24:10 +02:00
|
|
|
// Make it explicit that our toggler is not created right away.
|
2021-02-28 21:32:47 +01:00
|
|
|
export let toggler;
|
2018-04-14 14:24:10 +02:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function switch_stream_tab(tab_name) {
|
2018-12-02 01:09:10 +01:00
|
|
|
/*
|
|
|
|
This switches the stream tab, but it doesn't update
|
|
|
|
the toggler widget. You may instead want to
|
|
|
|
use `toggler.goto`.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (tab_name === "all-streams") {
|
|
|
|
subscribed_only = false;
|
|
|
|
} else if (tab_name === "subscribed") {
|
|
|
|
subscribed_only = true;
|
2018-04-14 14:24:10 +02:00
|
|
|
}
|
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
redraw_left_panel();
|
2019-04-06 12:55:16 +02:00
|
|
|
stream_edit.setup_subscriptions_tab_hash(tab_name);
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2018-04-14 14:24:10 +02:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function switch_stream_sort(tab_name) {
|
2020-07-15 00:34:28 +02:00
|
|
|
if (
|
|
|
|
tab_name === "by-stream-name" ||
|
|
|
|
tab_name === "by-subscriber-count" ||
|
|
|
|
tab_name === "by-weekly-traffic"
|
|
|
|
) {
|
2020-03-31 17:13:01 +02:00
|
|
|
sort_order = tab_name;
|
|
|
|
} else {
|
|
|
|
sort_order = "by-stream-name";
|
|
|
|
}
|
2021-02-28 21:32:47 +01:00
|
|
|
redraw_left_panel();
|
|
|
|
}
|
2020-03-31 17:13:01 +02:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function setup_page(callback) {
|
2018-04-14 15:04:35 +02:00
|
|
|
// We should strongly consider only setting up the page once,
|
|
|
|
// but I am writing these comments write before a big release,
|
|
|
|
// so it's too risky a change for now.
|
|
|
|
//
|
|
|
|
// The history behind setting up the page from scratch every
|
2020-10-23 02:43:28 +02:00
|
|
|
// time we go into "Manage streams" is that we used to have
|
2018-04-14 15:04:35 +02:00
|
|
|
// some live-update issues, so being able to re-launch the
|
|
|
|
// streams page is kind of a workaround for those bugs, since
|
|
|
|
// we will re-populate the widget.
|
|
|
|
//
|
|
|
|
// For now, every time we go back into the widget we'll
|
|
|
|
// continue the strategy that we re-render everything from scratch.
|
|
|
|
// Also, we'll always go back to the "Subscribed" tab.
|
2016-11-01 22:32:10 +01:00
|
|
|
function initialize_components() {
|
2020-03-31 17:13:01 +02:00
|
|
|
// Sort by name by default when opening "Manage streams".
|
|
|
|
sort_order = "by-stream-name";
|
2021-02-16 13:50:09 +01:00
|
|
|
const sort_toggler = components.toggle({
|
2020-03-31 17:13:01 +02:00
|
|
|
values: [
|
2020-07-15 00:34:28 +02:00
|
|
|
{
|
2021-04-13 06:51:54 +02:00
|
|
|
label_html: `<i class="fa fa-sort-alpha-asc tippy-bottom tippy-zulip-tooltip" data-tippy-content="${$t(
|
|
|
|
{defaultMessage: "Sort by name"},
|
2021-02-06 03:36:45 +01:00
|
|
|
)}"></i>`,
|
2020-07-15 00:34:28 +02:00
|
|
|
key: "by-stream-name",
|
|
|
|
},
|
|
|
|
{
|
2021-04-13 06:51:54 +02:00
|
|
|
label_html: `<i class="fa fa-user-o tippy-bottom tippy-zulip-tooltip" data-tippy-content="${$t(
|
|
|
|
{defaultMessage: "Sort by number of subscribers"},
|
2020-07-15 00:34:28 +02:00
|
|
|
)}"></i>`,
|
|
|
|
key: "by-subscriber-count",
|
|
|
|
},
|
|
|
|
{
|
2021-04-13 06:51:54 +02:00
|
|
|
label_html: `<i class="fa fa-bar-chart tippy-bottom tippy-zulip-tooltip" data-tippy-content="${$t(
|
|
|
|
{defaultMessage: "Sort by estimated weekly traffic"},
|
2020-07-15 00:34:28 +02:00
|
|
|
)}"></i>`,
|
|
|
|
key: "by-weekly-traffic",
|
|
|
|
},
|
2020-03-31 17:13:01 +02:00
|
|
|
],
|
2020-04-20 22:41:03 +02:00
|
|
|
html_class: "stream_sorter_toggle",
|
2020-07-20 22:18:43 +02:00
|
|
|
callback(value, key) {
|
2021-02-28 21:32:47 +01:00
|
|
|
switch_stream_sort(key);
|
2020-03-31 17:13:01 +02:00
|
|
|
},
|
|
|
|
});
|
2021-02-16 13:50:09 +01:00
|
|
|
$("#subscriptions_table .search-container").prepend(sort_toggler.get());
|
2020-03-31 17:13:01 +02:00
|
|
|
|
2021-04-09 09:30:31 +02:00
|
|
|
// place subs tooltips at bottom
|
|
|
|
tippy(".tippy-bottom", {
|
|
|
|
placement: "bottom",
|
|
|
|
});
|
|
|
|
|
2019-12-01 22:04:53 +01:00
|
|
|
// Reset our internal state to reflect that we're initially in
|
|
|
|
// the "Subscribed" tab if we're reopening "Manage streams".
|
|
|
|
subscribed_only = true;
|
2021-02-28 21:32:47 +01:00
|
|
|
toggler = components.toggle({
|
2018-07-15 20:10:41 +02:00
|
|
|
child_wants_focus: true,
|
2016-11-01 22:32:10 +01:00
|
|
|
values: [
|
2021-04-13 06:51:54 +02:00
|
|
|
{label: $t({defaultMessage: "Subscribed"}), key: "subscribed"},
|
|
|
|
{label: $t({defaultMessage: "All streams"}), key: "all-streams"},
|
2016-11-01 22:32:10 +01:00
|
|
|
],
|
2020-07-20 22:18:43 +02:00
|
|
|
callback(value, key) {
|
2021-02-28 21:32:47 +01:00
|
|
|
switch_stream_tab(key);
|
2017-01-12 00:17:43 +01:00
|
|
|
},
|
2018-04-14 14:24:10 +02:00
|
|
|
});
|
2016-11-01 22:32:10 +01:00
|
|
|
|
|
|
|
if (should_list_all_streams()) {
|
2021-02-28 21:32:47 +01:00
|
|
|
const toggler_elem = toggler.get();
|
2018-04-14 14:24:10 +02:00
|
|
|
$("#subscriptions_table .search-container").prepend(toggler_elem);
|
2016-11-01 22:32:10 +01:00
|
|
|
}
|
2019-05-08 09:12:02 +02:00
|
|
|
if (page_params.is_guest) {
|
2021-02-28 21:32:47 +01:00
|
|
|
toggler.disable_tab("all-streams");
|
2019-05-08 09:12:02 +02:00
|
|
|
}
|
2016-11-01 22:32:10 +01:00
|
|
|
|
2017-01-15 20:21:33 +01:00
|
|
|
// show the "Stream settings" header by default.
|
2016-11-01 22:32:10 +01:00
|
|
|
$(".display-type #stream_settings_title").show();
|
|
|
|
}
|
2013-10-25 17:27:07 +02:00
|
|
|
|
2018-05-30 18:10:43 +02:00
|
|
|
function populate_and_fill() {
|
2020-07-15 01:29:15 +02:00
|
|
|
$("#subscriptions_table").empty();
|
2016-10-12 08:53:57 +02:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const template_data = {
|
2021-04-28 21:58:33 +02:00
|
|
|
can_create_streams: settings_data.user_can_create_streams(),
|
2017-01-12 00:17:43 +01:00
|
|
|
hide_all_streams: !should_list_all_streams(),
|
2021-04-10 17:50:58 +02:00
|
|
|
max_name_length: page_params.max_stream_name_length,
|
|
|
|
max_description_length: page_params.max_stream_description_length,
|
2020-06-25 23:26:17 +02:00
|
|
|
is_owner: page_params.is_owner,
|
2020-07-10 14:25:21 +02:00
|
|
|
stream_privacy_policy_values: stream_data.stream_privacy_policy_values,
|
2020-02-04 21:50:55 +01:00
|
|
|
stream_post_policy_values: stream_data.stream_post_policy_values,
|
2020-06-15 17:00:00 +02:00
|
|
|
zulip_plan_is_not_limited: page_params.zulip_plan_is_not_limited,
|
2021-06-17 22:16:08 +02:00
|
|
|
org_level_message_retention_setting:
|
|
|
|
stream_edit.get_display_text_for_realm_message_retention_setting(),
|
2020-06-15 17:00:00 +02:00
|
|
|
upgrade_text_for_wide_organization_logo:
|
|
|
|
page_params.upgrade_text_for_wide_organization_logo,
|
2021-04-07 18:52:22 +02:00
|
|
|
disable_message_retention_setting:
|
|
|
|
!page_params.zulip_plan_is_not_limited || !page_params.is_owner,
|
2014-01-15 23:30:36 +01:00
|
|
|
};
|
2017-10-11 23:00:36 +02:00
|
|
|
|
2021-07-19 07:04:32 +02:00
|
|
|
const rendered = render_stream_settings_overlay(template_data);
|
2020-07-15 01:29:15 +02:00
|
|
|
$("#subscriptions_table").append(rendered);
|
2018-07-24 14:27:32 +02:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
render_left_panel_superset();
|
2016-11-01 22:32:10 +01:00
|
|
|
initialize_components();
|
2021-02-28 21:32:47 +01:00
|
|
|
redraw_left_panel();
|
2018-03-25 18:46:10 +02:00
|
|
|
stream_create.set_up_handlers();
|
2013-08-26 20:22:22 +02:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
const throttled_redraw_left_panel = _.throttle(redraw_left_panel, 50);
|
2020-07-02 01:45:54 +02:00
|
|
|
$("#stream_filter input[type='text']").on("input", () => {
|
2016-12-15 07:36:30 +01:00
|
|
|
// Debounce filtering in case a user is typing quickly
|
2021-02-15 18:18:51 +01:00
|
|
|
throttled_redraw_left_panel();
|
2016-11-01 22:32:10 +01:00
|
|
|
});
|
|
|
|
|
2021-05-04 08:56:45 +02:00
|
|
|
// When hitting Enter in the stream creation box, we open the
|
|
|
|
// "create stream" UI with the stream name prepopulated. This
|
|
|
|
// is only useful if the user has permission to create
|
|
|
|
// streams, either explicitly via user_can_create_streams, or
|
|
|
|
// implicitly because page_params.realm_is_zephyr_mirror_realm.
|
|
|
|
$("#stream_filter input[type='text']").on("keypress", (e) => {
|
2021-05-31 19:23:29 +02:00
|
|
|
if (e.key !== "Enter") {
|
2021-05-04 08:56:45 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (
|
|
|
|
settings_data.user_can_create_streams() ||
|
|
|
|
page_params.realm_is_zephyr_mirror_realm
|
|
|
|
) {
|
|
|
|
open_create_stream();
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2020-07-02 01:45:54 +02:00
|
|
|
$("#clear_search_stream_name").on("click", () => {
|
2020-03-21 19:12:23 +01:00
|
|
|
$("#stream_filter input[type='text']").val("");
|
2021-02-28 21:32:47 +01:00
|
|
|
redraw_left_panel();
|
2020-03-21 19:12:23 +01:00
|
|
|
});
|
|
|
|
|
2016-11-01 22:32:10 +01:00
|
|
|
if (callback) {
|
|
|
|
callback();
|
|
|
|
}
|
2013-01-23 17:41:04 +01:00
|
|
|
}
|
|
|
|
|
2016-10-25 21:50:14 +02:00
|
|
|
populate_and_fill();
|
2013-04-04 22:30:28 +02:00
|
|
|
|
2016-10-25 21:50:14 +02:00
|
|
|
if (!should_list_all_streams()) {
|
2021-04-13 06:51:54 +02:00
|
|
|
$(".create_stream_button").val($t({defaultMessage: "Subscribe"}));
|
2013-01-23 17:51:01 +01:00
|
|
|
}
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2012-10-03 22:24:17 +02:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function switch_to_stream_row(stream_id) {
|
|
|
|
const stream_row = row_for_stream_id(stream_id);
|
2020-07-06 16:30:48 +02:00
|
|
|
const container = $(".streams-list");
|
2018-12-02 00:16:08 +01:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
get_active_data().row.removeClass("active");
|
2018-12-02 00:16:08 +01:00
|
|
|
stream_row.addClass("active");
|
|
|
|
|
2020-07-06 16:30:48 +02:00
|
|
|
scroll_util.scroll_element_into_container(stream_row, container);
|
2018-12-02 00:16:08 +01:00
|
|
|
|
|
|
|
// It's dubious that we need this timeout any more.
|
2020-07-02 01:45:54 +02:00
|
|
|
setTimeout(() => {
|
2021-02-28 21:32:47 +01:00
|
|
|
if (stream_id === get_active_data().id) {
|
2020-07-20 21:24:26 +02:00
|
|
|
stream_row.trigger("click");
|
2018-12-02 00:16:08 +01:00
|
|
|
}
|
|
|
|
}, 100);
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2018-12-02 00:16:08 +01:00
|
|
|
|
2021-06-08 16:20:39 +02:00
|
|
|
function show_right_section() {
|
|
|
|
$(".right").addClass("show");
|
|
|
|
$(".subscriptions-header").addClass("slide-left");
|
|
|
|
}
|
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function change_state(section) {
|
2018-12-05 20:41:20 +01:00
|
|
|
// if in #streams/new form.
|
|
|
|
if (section === "new") {
|
2019-05-10 07:28:20 +02:00
|
|
|
if (!page_params.is_guest) {
|
2021-02-28 21:32:47 +01:00
|
|
|
do_open_create_stream();
|
2021-06-08 16:21:41 +02:00
|
|
|
show_right_section();
|
2019-05-10 07:28:20 +02:00
|
|
|
} else {
|
2021-02-28 21:32:47 +01:00
|
|
|
toggler.goto("subscribed");
|
2019-05-10 07:28:20 +02:00
|
|
|
}
|
2018-12-02 00:24:45 +01:00
|
|
|
return;
|
|
|
|
}
|
2017-02-21 18:13:32 +01:00
|
|
|
|
2018-12-05 20:41:20 +01:00
|
|
|
if (section === "all") {
|
2021-02-28 21:32:47 +01:00
|
|
|
toggler.goto("all-streams");
|
2018-12-05 20:41:20 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (section === "subscribed") {
|
2021-02-28 21:32:47 +01:00
|
|
|
toggler.goto("subscribed");
|
2018-12-05 20:41:20 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if the section is a valid number.
|
|
|
|
if (/\d+/.test(section)) {
|
2020-10-07 09:17:30 +02:00
|
|
|
const stream_id = Number.parseInt(section, 10);
|
2019-05-10 07:28:20 +02:00
|
|
|
// Guest users can not access unsubscribed streams
|
|
|
|
// So redirect guest users to 'subscribed' tab
|
|
|
|
// for any unsubscribed stream settings hash
|
|
|
|
if (page_params.is_guest && !stream_data.id_is_subscribed(stream_id)) {
|
2021-02-28 21:32:47 +01:00
|
|
|
toggler.goto("subscribed");
|
2019-05-10 07:28:20 +02:00
|
|
|
} else {
|
2021-06-08 16:21:41 +02:00
|
|
|
show_right_section();
|
2021-02-28 21:32:47 +01:00
|
|
|
switch_to_stream_row(stream_id);
|
2019-05-10 07:28:20 +02:00
|
|
|
}
|
2018-12-05 20:41:20 +01:00
|
|
|
return;
|
2018-12-02 00:24:45 +01:00
|
|
|
}
|
2018-12-05 20:41:20 +01:00
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
blueslip.warn("invalid section for streams: " + section);
|
2021-02-28 21:32:47 +01:00
|
|
|
toggler.goto("subscribed");
|
|
|
|
}
|
2017-02-21 18:13:32 +01:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function launch(section) {
|
|
|
|
setup_page(() => {
|
2017-05-27 15:40:54 +02:00
|
|
|
overlays.open_overlay({
|
2020-07-15 01:29:15 +02:00
|
|
|
name: "subscriptions",
|
2017-05-06 01:11:18 +02:00
|
|
|
overlay: $("#subscription_overlay"),
|
2020-07-20 22:18:43 +02:00
|
|
|
on_close() {
|
2021-03-22 16:09:12 +01:00
|
|
|
browser_history.exit_overlay();
|
2021-03-09 08:09:29 +01:00
|
|
|
$(".colorpicker").spectrum("destroy");
|
2020-05-06 00:26:35 +02:00
|
|
|
},
|
2017-05-06 01:11:18 +02:00
|
|
|
});
|
2021-02-28 21:32:47 +01:00
|
|
|
change_state(section);
|
2016-11-01 22:32:10 +01:00
|
|
|
});
|
2021-02-28 21:32:47 +01:00
|
|
|
if (!get_active_data().id) {
|
2021-06-08 16:26:12 +02:00
|
|
|
if (section === "new") {
|
|
|
|
$("#create_stream_name").trigger("focus");
|
|
|
|
} else {
|
|
|
|
$("#search_stream_name").trigger("focus");
|
|
|
|
}
|
2017-03-26 12:26:22 +02:00
|
|
|
}
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2016-11-01 22:32:10 +01:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function switch_rows(event) {
|
|
|
|
const active_data = get_active_data();
|
2019-11-02 00:06:25 +01:00
|
|
|
let switch_row;
|
2021-03-15 09:24:10 +01:00
|
|
|
if (hash_util.is_create_new_stream_narrow()) {
|
2017-04-05 10:49:19 +02:00
|
|
|
// Prevent switching stream rows when creating a new stream
|
|
|
|
return false;
|
2020-07-15 01:29:15 +02:00
|
|
|
} else if (!active_data.id || active_data.row.hasClass("notdisplayed")) {
|
|
|
|
switch_row = $("div.stream-row:not(.notdisplayed)").first();
|
|
|
|
if ($("#search_stream_name").is(":focus")) {
|
2020-07-20 21:24:26 +02:00
|
|
|
$("#search_stream_name").trigger("blur");
|
2017-03-26 12:26:22 +02:00
|
|
|
}
|
2017-05-25 23:53:19 +02:00
|
|
|
} else {
|
2020-07-15 01:29:15 +02:00
|
|
|
if (event === "up_arrow") {
|
|
|
|
switch_row = active_data.row.prevAll().not(".notdisplayed").first();
|
|
|
|
} else if (event === "down_arrow") {
|
|
|
|
switch_row = active_data.row.nextAll().not(".notdisplayed").first();
|
2017-05-25 23:53:19 +02:00
|
|
|
}
|
2020-07-15 01:29:15 +02:00
|
|
|
if ($("#search_stream_name").is(":focus")) {
|
2017-03-26 12:26:22 +02:00
|
|
|
// remove focus from Filter streams input instead of switching rows
|
|
|
|
// if Filter streams input is focused
|
2020-07-20 21:24:26 +02:00
|
|
|
return $("#search_stream_name").trigger("blur");
|
2017-03-26 12:26:22 +02:00
|
|
|
}
|
2017-03-22 02:49:52 +01:00
|
|
|
}
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const row_data = get_row_data(switch_row);
|
2017-05-25 23:53:19 +02:00
|
|
|
if (row_data) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const stream_id = row_data.id;
|
2021-02-28 21:32:47 +01:00
|
|
|
switch_to_stream_row(stream_id);
|
2020-07-15 01:29:15 +02:00
|
|
|
} else if (event === "up_arrow" && !row_data) {
|
2020-07-20 21:24:26 +02:00
|
|
|
$("#search_stream_name").trigger("focus");
|
2017-03-22 02:49:52 +01:00
|
|
|
}
|
2017-04-05 10:49:19 +02:00
|
|
|
return true;
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2017-03-22 02:49:52 +01:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function keyboard_sub() {
|
|
|
|
const active_data = get_active_data();
|
2019-11-02 00:06:25 +01:00
|
|
|
const row_data = get_row_data(active_data.row);
|
2017-03-23 05:15:16 +01:00
|
|
|
if (row_data) {
|
2020-08-07 00:38:19 +02:00
|
|
|
sub_or_unsub(row_data.object, false);
|
2017-03-23 03:06:38 +01:00
|
|
|
}
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2017-03-23 03:06:38 +01:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function toggle_view(event) {
|
|
|
|
const active_data = get_active_data();
|
2020-07-09 15:30:44 +02:00
|
|
|
const stream_filter_tab = $(active_data.tabs[0]).text();
|
2018-12-02 01:09:10 +01:00
|
|
|
|
2020-07-09 15:30:44 +02:00
|
|
|
if (event === "right_arrow" && stream_filter_tab === "Subscribed") {
|
2021-02-28 21:32:47 +01:00
|
|
|
toggler.goto("all-streams");
|
2020-07-09 15:30:44 +02:00
|
|
|
} else if (event === "left_arrow" && stream_filter_tab === "All streams") {
|
2021-02-28 21:32:47 +01:00
|
|
|
toggler.goto("subscribed");
|
2017-03-23 06:02:01 +01:00
|
|
|
}
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2017-03-23 06:02:01 +01:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function view_stream() {
|
|
|
|
const active_data = get_active_data();
|
2019-11-02 00:06:25 +01:00
|
|
|
const row_data = get_row_data(active_data.row);
|
2017-03-23 06:41:31 +01:00
|
|
|
if (row_data) {
|
2020-07-15 00:34:28 +02:00
|
|
|
const stream_narrow_hash =
|
|
|
|
"#narrow/stream/" + hash_util.encode_stream_name(row_data.object.name);
|
2021-03-22 16:09:12 +01:00
|
|
|
browser_history.go_to_location(stream_narrow_hash);
|
2017-03-23 06:41:31 +01:00
|
|
|
}
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2017-03-23 06:41:31 +01:00
|
|
|
|
2020-04-23 21:43:37 +02:00
|
|
|
/* For the given stream_row, remove the tick and replace by a spinner. */
|
|
|
|
function display_subscribe_toggle_spinner(stream_row) {
|
2020-04-16 14:47:43 +02:00
|
|
|
/* Prevent sending multiple requests by removing the button class. */
|
2020-07-15 01:29:15 +02:00
|
|
|
$(stream_row).find(".check").removeClass("sub_unsub_button");
|
2020-04-16 14:47:43 +02:00
|
|
|
|
|
|
|
/* Hide the tick. */
|
2020-04-23 21:43:37 +02:00
|
|
|
const tick = $(stream_row).find("svg");
|
2020-04-16 14:47:43 +02:00
|
|
|
tick.addClass("hide");
|
|
|
|
|
|
|
|
/* Add a spinner to show the request is in process. */
|
2020-04-23 21:43:37 +02:00
|
|
|
const spinner = $(stream_row).find(".sub_unsub_status").expectOne();
|
2020-04-16 14:47:43 +02:00
|
|
|
spinner.show();
|
|
|
|
loading.make_indicator(spinner);
|
|
|
|
}
|
|
|
|
|
2020-04-23 21:43:37 +02:00
|
|
|
/* For the given stream_row, add the tick and delete the spinner. */
|
|
|
|
function hide_subscribe_toggle_spinner(stream_row) {
|
2020-04-16 14:47:43 +02:00
|
|
|
/* Re-enable the button to handle requests. */
|
2020-07-15 01:29:15 +02:00
|
|
|
$(stream_row).find(".check").addClass("sub_unsub_button");
|
2020-04-16 14:47:43 +02:00
|
|
|
|
|
|
|
/* Show the tick. */
|
2020-04-23 21:43:37 +02:00
|
|
|
const tick = $(stream_row).find("svg");
|
2020-04-16 14:47:43 +02:00
|
|
|
tick.removeClass("hide");
|
|
|
|
|
|
|
|
/* Destroy the spinner. */
|
2020-04-23 21:43:37 +02:00
|
|
|
const spinner = $(stream_row).find(".sub_unsub_status").expectOne();
|
2020-04-16 14:47:43 +02:00
|
|
|
loading.destroy_indicator(spinner);
|
|
|
|
}
|
|
|
|
|
2020-04-23 21:43:37 +02:00
|
|
|
function ajaxSubscribe(stream, color, stream_row) {
|
2013-01-31 21:09:34 +01:00
|
|
|
// Subscribe yourself to a single stream.
|
2019-11-02 00:06:25 +01:00
|
|
|
let true_stream_name;
|
2013-01-31 21:09:34 +01:00
|
|
|
|
2020-04-23 21:43:37 +02:00
|
|
|
if (stream_row !== undefined) {
|
|
|
|
display_subscribe_toggle_spinner(stream_row);
|
2020-04-16 14:47:43 +02:00
|
|
|
}
|
2013-12-18 19:55:18 +01:00
|
|
|
return channel.post({
|
2015-11-30 21:39:40 +01:00
|
|
|
url: "/json/users/me/subscriptions",
|
2020-07-20 22:18:43 +02:00
|
|
|
data: {subscriptions: JSON.stringify([{name: stream, color}])},
|
|
|
|
success(resp, statusText, xhr) {
|
2017-05-27 15:40:54 +02:00
|
|
|
if (overlays.streams_open()) {
|
2017-01-19 23:23:37 +01:00
|
|
|
$("#create_stream_name").val("");
|
|
|
|
}
|
2013-01-31 21:09:34 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const res = JSON.parse(xhr.responseText);
|
2013-01-31 21:09:34 +01:00
|
|
|
if (!$.isEmptyObject(res.already_subscribed)) {
|
|
|
|
// Display the canonical stream capitalization.
|
2017-01-20 23:11:08 +01:00
|
|
|
true_stream_name = res.already_subscribed[people.my_current_email()][0];
|
2020-07-15 00:34:28 +02:00
|
|
|
ui_report.success(
|
2021-04-13 05:18:25 +02:00
|
|
|
$t_html(
|
|
|
|
{defaultMessage: "Already subscribed to {stream}"},
|
|
|
|
{stream: true_stream_name},
|
|
|
|
),
|
2020-07-15 00:34:28 +02:00
|
|
|
$(".stream_change_property_info"),
|
|
|
|
);
|
2012-11-07 23:25:04 +01:00
|
|
|
}
|
2013-03-29 20:49:05 +01:00
|
|
|
// The rest of the work is done via the subscribe event we will get
|
2020-04-16 14:47:43 +02:00
|
|
|
|
2020-04-23 21:43:37 +02:00
|
|
|
if (stream_row !== undefined) {
|
|
|
|
hide_subscribe_toggle_spinner(stream_row);
|
2020-04-16 14:47:43 +02:00
|
|
|
}
|
2012-11-07 23:25:04 +01:00
|
|
|
},
|
2020-07-20 22:18:43 +02:00
|
|
|
error(xhr) {
|
2020-04-23 21:43:37 +02:00
|
|
|
if (stream_row !== undefined) {
|
|
|
|
hide_subscribe_toggle_spinner(stream_row);
|
2020-04-16 14:47:43 +02:00
|
|
|
}
|
2020-07-15 00:34:28 +02:00
|
|
|
ui_report.error(
|
2021-04-13 05:18:25 +02:00
|
|
|
$t_html({defaultMessage: "Error adding subscription"}),
|
2020-07-15 00:34:28 +02:00
|
|
|
xhr,
|
|
|
|
$(".stream_change_property_info"),
|
|
|
|
);
|
2017-01-12 00:17:43 +01:00
|
|
|
},
|
2012-11-07 23:25:04 +01:00
|
|
|
});
|
|
|
|
}
|
2012-10-09 21:01:41 +02:00
|
|
|
|
2020-04-23 21:43:37 +02:00
|
|
|
function ajaxUnsubscribe(sub, stream_row) {
|
2017-03-05 03:47:28 +01:00
|
|
|
// TODO: use stream_id when backend supports it
|
2020-04-23 21:43:37 +02:00
|
|
|
if (stream_row !== undefined) {
|
|
|
|
display_subscribe_toggle_spinner(stream_row);
|
2020-04-16 14:47:43 +02:00
|
|
|
}
|
2016-12-23 02:37:10 +01:00
|
|
|
return channel.del({
|
|
|
|
url: "/json/users/me/subscriptions",
|
2020-07-16 22:40:18 +02:00
|
|
|
data: {subscriptions: JSON.stringify([sub.name])},
|
2020-07-20 22:18:43 +02:00
|
|
|
success() {
|
2018-01-03 14:24:49 +01:00
|
|
|
$(".stream_change_property_info").hide();
|
2013-03-29 20:49:05 +01:00
|
|
|
// The rest of the work is done via the unsubscribe event we will get
|
2020-04-16 14:47:43 +02:00
|
|
|
|
2020-04-23 21:43:37 +02:00
|
|
|
if (stream_row !== undefined) {
|
|
|
|
hide_subscribe_toggle_spinner(stream_row);
|
2020-04-16 14:47:43 +02:00
|
|
|
}
|
2012-10-03 22:24:17 +02:00
|
|
|
},
|
2020-07-20 22:18:43 +02:00
|
|
|
error(xhr) {
|
2020-04-23 21:43:37 +02:00
|
|
|
if (stream_row !== undefined) {
|
|
|
|
hide_subscribe_toggle_spinner(stream_row);
|
2020-04-16 14:47:43 +02:00
|
|
|
}
|
2020-07-15 00:34:28 +02:00
|
|
|
ui_report.error(
|
2021-04-13 05:18:25 +02:00
|
|
|
$t_html({defaultMessage: "Error removing subscription"}),
|
2020-07-15 00:34:28 +02:00
|
|
|
xhr,
|
|
|
|
$(".stream_change_property_info"),
|
|
|
|
);
|
2017-01-12 00:17:43 +01:00
|
|
|
},
|
2012-10-03 22:24:17 +02:00
|
|
|
});
|
2013-01-10 22:15:55 +01:00
|
|
|
}
|
2012-11-16 20:11:08 +01:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function do_open_create_stream() {
|
2018-12-01 21:18:20 +01:00
|
|
|
// Only call this directly for hash changes.
|
|
|
|
// Prefer open_create_stream().
|
|
|
|
|
2020-07-22 03:39:41 +02:00
|
|
|
const stream = $("#search_stream_name").val().trim();
|
2013-01-31 21:09:34 +01:00
|
|
|
|
2017-04-22 22:22:25 +02:00
|
|
|
if (!should_list_all_streams()) {
|
|
|
|
// Realms that don't allow listing streams should simply be subscribed to.
|
|
|
|
stream_create.set_name(stream);
|
|
|
|
ajaxSubscribe($("#search_stream_name").val());
|
|
|
|
return;
|
2013-08-27 19:14:18 +02:00
|
|
|
}
|
|
|
|
|
2017-04-22 22:22:25 +02:00
|
|
|
stream_create.new_stream_clicked(stream);
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2013-01-31 21:09:34 +01:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function open_create_stream() {
|
|
|
|
do_open_create_stream();
|
2021-03-22 16:09:12 +01:00
|
|
|
browser_history.update("#streams/new");
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2018-12-01 21:18:20 +01:00
|
|
|
|
2020-08-07 00:38:19 +02:00
|
|
|
export function unsubscribe_from_private_stream(sub, from_stream_popover) {
|
|
|
|
let modal_parent = $("#subscriptions_table");
|
|
|
|
const html_body = render_unsubscribe_private_stream_modal();
|
|
|
|
|
|
|
|
if (from_stream_popover) {
|
|
|
|
modal_parent = $(".left-sidebar-modal-holder");
|
|
|
|
}
|
|
|
|
|
|
|
|
function unsubscribe_from_stream() {
|
|
|
|
let stream_row;
|
|
|
|
if (overlays.streams_open()) {
|
|
|
|
stream_row = $(
|
|
|
|
"#subscriptions_table div.stream-row[data-stream-id='" + sub.stream_id + "']",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
ajaxUnsubscribe(sub, stream_row);
|
|
|
|
}
|
|
|
|
|
|
|
|
confirm_dialog.launch({
|
|
|
|
parent: modal_parent,
|
2021-04-13 05:24:31 +02:00
|
|
|
html_heading: $t_html(
|
|
|
|
{defaultMessage: "Unsubscribe from {stream_name}"},
|
|
|
|
{stream_name: sub.name},
|
|
|
|
),
|
2020-08-07 00:38:19 +02:00
|
|
|
html_body,
|
|
|
|
on_click: unsubscribe_from_stream,
|
2021-06-09 16:35:08 +02:00
|
|
|
fade: true,
|
2020-08-07 00:38:19 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export function sub_or_unsub(sub, from_stream_popover, stream_row) {
|
2017-03-04 17:55:16 +01:00
|
|
|
if (sub.subscribed) {
|
2020-08-07 00:38:19 +02:00
|
|
|
// TODO: This next line should allow guests to access web-public streams.
|
|
|
|
if (sub.invite_only || page_params.is_guest) {
|
|
|
|
unsubscribe_from_private_stream(sub, from_stream_popover);
|
|
|
|
return;
|
|
|
|
}
|
2020-04-23 21:43:37 +02:00
|
|
|
ajaxUnsubscribe(sub, stream_row);
|
2017-03-04 17:55:16 +01:00
|
|
|
} else {
|
2020-04-23 21:43:37 +02:00
|
|
|
ajaxSubscribe(sub.name, sub.color, stream_row);
|
2017-03-04 17:55:16 +01:00
|
|
|
}
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|
2017-03-04 17:55:16 +01:00
|
|
|
|
2021-02-28 21:32:47 +01:00
|
|
|
export function initialize() {
|
2020-07-02 01:45:54 +02:00
|
|
|
$("#subscriptions_table").on("click", ".create_stream_button", (e) => {
|
2012-10-31 18:36:08 +01:00
|
|
|
e.preventDefault();
|
2021-02-28 21:32:47 +01:00
|
|
|
open_create_stream();
|
2013-01-31 21:09:34 +01:00
|
|
|
});
|
|
|
|
|
2021-06-22 05:39:36 +02:00
|
|
|
$(".subscriptions").on("click", "#stream_creation_form [data-dismiss]", (e) => {
|
2016-11-01 22:32:10 +01:00
|
|
|
e.preventDefault();
|
2016-12-13 21:58:11 +01:00
|
|
|
// we want to make sure that the click is not just a simulated
|
2020-08-11 02:09:14 +02:00
|
|
|
// click; this fixes an issue where hitting "Enter" would
|
2016-12-13 21:58:11 +01:00
|
|
|
// trigger this code path due to bootstrap magic.
|
|
|
|
if (e.clientY !== 0) {
|
2021-02-28 21:32:47 +01:00
|
|
|
show_subs_pane.nothing_selected();
|
2016-12-13 21:58:11 +01:00
|
|
|
}
|
2016-11-01 22:32:10 +01:00
|
|
|
});
|
2013-08-27 19:14:18 +02:00
|
|
|
|
2016-12-02 14:06:06 +01:00
|
|
|
$("#subscriptions_table").on("click", ".email-address", function () {
|
2013-11-08 10:12:05 +01:00
|
|
|
selectText(this);
|
|
|
|
});
|
|
|
|
|
2021-06-08 16:20:39 +02:00
|
|
|
$("#subscriptions_table").on("click", ".stream-row, .create_stream_button", show_right_section);
|
2017-02-09 00:07:52 +01:00
|
|
|
|
2020-07-02 01:45:54 +02:00
|
|
|
$("#subscriptions_table").on("click", ".fa-chevron-left", () => {
|
2017-02-09 00:07:52 +01:00
|
|
|
$(".right").removeClass("show");
|
|
|
|
$(".subscriptions-header").removeClass("slide-left");
|
|
|
|
});
|
|
|
|
|
2021-05-06 21:49:45 +02:00
|
|
|
{
|
2019-11-02 00:06:25 +01:00
|
|
|
const sel = ".search-container, .streams-list, .subscriptions-header";
|
2016-11-01 22:32:10 +01:00
|
|
|
|
2020-07-02 01:45:54 +02:00
|
|
|
$("#subscriptions_table").on("click", sel, (e) => {
|
2016-11-01 22:32:10 +01:00
|
|
|
if ($(e.target).is(sel)) {
|
2018-12-01 21:53:09 +01:00
|
|
|
stream_edit.open_edit_panel_empty();
|
2016-11-01 22:32:10 +01:00
|
|
|
}
|
|
|
|
});
|
2021-05-06 21:49:45 +02:00
|
|
|
}
|
2021-02-28 21:32:47 +01:00
|
|
|
}
|