streams-ui: Move folder related UI functions to new file.

This commit moves function for creating, editing and archiving
folders in a new file channel_folders_ui.ts so that they can
be defined separately than the files for stream settings UI.
This commit is contained in:
Sahil Batra 2025-08-06 16:24:10 +05:30 committed by Tim Abbott
parent 42014f2f79
commit fac28bf800
4 changed files with 171 additions and 153 deletions

View File

@ -65,6 +65,7 @@ EXEMPT_FILES = make_set(
"web/src/browser_history.ts",
"web/src/buddy_list.ts",
"web/src/buttons.ts",
"web/src/channel_folders_ui.ts",
"web/src/click_handlers.ts",
"web/src/color_picker_popover.ts",
"web/src/compose.js",

View File

@ -0,0 +1,164 @@
import $ from "jquery";
import * as z from "zod/mini";
import render_confirm_archive_channel_folder from "../templates/confirm_dialog/confirm_archive_channel_folder.hbs";
import render_create_channel_folder_modal from "../templates/stream_settings/create_channel_folder_modal.hbs";
import render_edit_channel_folder_modal from "../templates/stream_settings/edit_channel_folder_modal.hbs";
import * as channel from "./channel.ts";
import * as channel_folders from "./channel_folders.ts";
import * as confirm_dialog from "./confirm_dialog.ts";
import * as dialog_widget from "./dialog_widget.ts";
import {$t_html} from "./i18n.ts";
import * as people from "./people.ts";
import {realm} from "./state_data.ts";
import * as ui_report from "./ui_report.ts";
export function add_channel_folder(): void {
const html_body = render_create_channel_folder_modal({
max_channel_folder_name_length: realm.max_channel_folder_name_length,
max_channel_folder_description_length: realm.max_channel_folder_description_length,
});
function create_channel_folder(): void {
const close_on_success = true;
const data = {
name: $<HTMLInputElement>("input#new_channel_folder_name").val()!.trim(),
description: $<HTMLTextAreaElement>("textarea#new_channel_folder_description")
.val()!
.trim(),
};
dialog_widget.submit_api_request(
channel.post,
"/json/channel_folders/create",
data,
{
success_continuation(response_data) {
const id = z
.object({channel_folder_id: z.number()})
.parse(response_data).channel_folder_id;
// This is a temporary channel folder object added
// to channel folders data, so that the folder is
// immediately visible in the dropdown.
// This will be replaced with the actual object once
// the client receives channel_folder/add event.
const channel_folder = {
id,
name: data.name,
description: data.description,
is_archived: false,
rendered_description: "",
date_created: 0,
creator_id: people.my_current_user_id(),
};
channel_folders.add(channel_folder);
},
},
close_on_success,
);
}
dialog_widget.launch({
html_heading: $t_html({defaultMessage: "Create channel folder"}),
html_body,
id: "create_channel_folder",
html_submit_button: $t_html({defaultMessage: "Create"}),
on_click: create_channel_folder,
loading_spinner: true,
on_shown: () => $("#new_channel_folder_name").trigger("focus"),
});
}
function archive_folder(folder_id: number): void {
const stream_ids = channel_folders.get_stream_ids_in_folder(folder_id);
let successful_requests = 0;
function make_archive_folder_request(): void {
const url = "/json/channel_folders/" + folder_id.toString();
const data = {
is_archived: JSON.stringify(true),
};
dialog_widget.submit_api_request(channel.patch, url, data);
}
if (stream_ids.length === 0) {
make_archive_folder_request();
return;
}
function remove_channel_from_folder(stream_id: number): void {
const url = "/json/streams/" + stream_id.toString();
const data = {
folder_id: JSON.stringify(null),
};
void channel.patch({
url,
data,
success() {
successful_requests = successful_requests + 1;
if (successful_requests === stream_ids.length) {
// Make request to archive folder only after all channels
// are removed from the folder.
make_archive_folder_request();
}
},
error(xhr) {
ui_report.error(
$t_html({
defaultMessage: "Failed removing one or more channels from the folder",
}),
xhr,
$("#dialog_error"),
);
dialog_widget.hide_dialog_spinner();
},
});
}
for (const stream_id of stream_ids) {
remove_channel_from_folder(stream_id);
}
}
export function handle_archiving_channel_folder(folder_id: number): void {
confirm_dialog.launch({
html_heading: $t_html({defaultMessage: "Delete channel folder?"}),
html_body: render_confirm_archive_channel_folder(),
on_click() {
archive_folder(folder_id);
},
close_on_submit: false,
loading_spinner: true,
});
}
export function handle_editing_channel_folder(folder_id: number): void {
const folder = channel_folders.get_channel_folder_by_id(folder_id);
const html_body = render_edit_channel_folder_modal({
name: folder.name,
description: folder.description,
max_channel_folder_name_length: realm.max_channel_folder_name_length,
max_channel_folder_description_length: realm.max_channel_folder_description_length,
});
dialog_widget.launch({
html_heading: $t_html({defaultMessage: "Edit channel folder"}),
html_body,
id: "edit_channel_folder",
on_click() {
const url = "/json/channel_folders/" + folder_id.toString();
const data = {
name: $<HTMLInputElement>("input#edit_channel_folder_name").val()!.trim(),
description: $<HTMLTextAreaElement>("textarea#edit_channel_folder_description")
.val()!
.trim(),
};
dialog_widget.submit_api_request(channel.patch, url, data);
},
loading_spinner: true,
on_shown: () => $("#edit_channel_folder_name").trigger("focus"),
update_submit_disabled_state_on_change: true,
});
}

View File

@ -10,7 +10,6 @@ import render_inline_decorated_channel_name from "../templates/inline_decorated_
import render_change_stream_info_modal from "../templates/stream_settings/change_stream_info_modal.hbs";
import render_confirm_stream_privacy_change_modal from "../templates/stream_settings/confirm_stream_privacy_change_modal.hbs";
import render_copy_email_address_modal from "../templates/stream_settings/copy_email_address_modal.hbs";
import render_create_channel_folder_modal from "../templates/stream_settings/create_channel_folder_modal.hbs";
import render_stream_description from "../templates/stream_settings/stream_description.hbs";
import render_stream_settings from "../templates/stream_settings/stream_settings.hbs";
@ -18,7 +17,7 @@ import * as blueslip from "./blueslip.ts";
import type {Bot} from "./bot_data.ts";
import * as browser_history from "./browser_history.ts";
import * as channel from "./channel.ts";
import * as channel_folders from "./channel_folders.ts";
import * as channel_folders_ui from "./channel_folders_ui.ts";
import * as confirm_dialog from "./confirm_dialog.ts";
import {show_copied_confirmation} from "./copied_tooltip.ts";
import * as dialog_widget from "./dialog_widget.ts";
@ -914,56 +913,6 @@ export function initialize(): void {
);
$("#channels_overlay_container").on("click", ".create-channel-folder-button", () => {
const html_body = render_create_channel_folder_modal({
max_channel_folder_name_length: realm.max_channel_folder_name_length,
max_channel_folder_description_length: realm.max_channel_folder_description_length,
});
function create_channel_folder(): void {
const close_on_success = true;
const data = {
name: $<HTMLInputElement>("input#new_channel_folder_name").val()!.trim(),
description: $<HTMLTextAreaElement>("textarea#new_channel_folder_description")
.val()!
.trim(),
};
dialog_widget.submit_api_request(
channel.post,
"/json/channel_folders/create",
data,
{
success_continuation(response_data) {
const id = z
.object({channel_folder_id: z.number()})
.parse(response_data).channel_folder_id;
// This is a temporary channel folder object added
// to channel folders data, so that the folder is
// immediately visible in the dropdown.
// This will be replaced with the actual object once
// the client receives channel_folder/add event.
const channel_folder = {
id,
name: data.name,
description: data.description,
is_archived: false,
rendered_description: "",
date_created: 0,
creator_id: people.my_current_user_id(),
};
channel_folders.add(channel_folder);
},
},
close_on_success,
);
}
dialog_widget.launch({
html_heading: $t_html({defaultMessage: "Create channel folder"}),
html_body,
id: "create_channel_folder",
html_submit_button: $t_html({defaultMessage: "Create"}),
on_click: create_channel_folder,
loading_spinner: true,
on_shown: () => $("#new_channel_folder_name").trigger("focus"),
});
channel_folders_ui.add_channel_folder();
});
}

View File

@ -1,16 +1,14 @@
import $ from "jquery";
import * as z from "zod/mini";
import render_confirm_archive_channel_folder from "../templates/confirm_dialog/confirm_archive_channel_folder.hbs";
import render_unsubscribe_private_stream_modal from "../templates/confirm_dialog/confirm_unsubscribe_private_stream.hbs";
import render_inline_decorated_channel_name from "../templates/inline_decorated_channel_name.hbs";
import render_edit_channel_folder_modal from "../templates/stream_settings/edit_channel_folder_modal.hbs";
import render_selected_stream_title from "../templates/stream_settings/selected_stream_title.hbs";
import * as channel from "./channel.ts";
import * as channel_folders from "./channel_folders.ts";
import * as channel_folders_ui from "./channel_folders_ui.ts";
import * as confirm_dialog from "./confirm_dialog.ts";
import * as dialog_widget from "./dialog_widget.ts";
import type {DropdownWidget} from "./dropdown_widget.ts";
import * as dropdown_widget from "./dropdown_widget.ts";
import * as hash_util from "./hash_util.ts";
@ -22,7 +20,7 @@ import * as resize from "./resize.ts";
import * as settings_components from "./settings_components.ts";
import * as settings_config from "./settings_config.ts";
import * as settings_data from "./settings_data.ts";
import {current_user, realm} from "./state_data.ts";
import {current_user} from "./state_data.ts";
import * as stream_data from "./stream_data.ts";
import * as stream_settings_containers from "./stream_settings_containers.ts";
import * as stream_settings_data from "./stream_settings_data.ts";
@ -322,100 +320,6 @@ export function filter_includes_channel(sub: StreamSubscription): boolean {
return true;
}
function archive_folder(folder_id: number): void {
const stream_ids = channel_folders.get_stream_ids_in_folder(folder_id);
let successful_requests = 0;
function make_archive_folder_request(): void {
const url = "/json/channel_folders/" + folder_id.toString();
const data = {
is_archived: JSON.stringify(true),
};
dialog_widget.submit_api_request(channel.patch, url, data);
}
if (stream_ids.length === 0) {
make_archive_folder_request();
return;
}
function remove_channel_from_folder(stream_id: number): void {
const url = "/json/streams/" + stream_id.toString();
const data = {
folder_id: JSON.stringify(null),
};
void channel.patch({
url,
data,
success() {
successful_requests = successful_requests + 1;
if (successful_requests === stream_ids.length) {
// Make request to archive folder only after all channels
// are removed from the folder.
make_archive_folder_request();
}
},
error(xhr) {
ui_report.error(
$t_html({
defaultMessage: "Failed removing one or more channels from the folder",
}),
xhr,
$("#dialog_error"),
);
dialog_widget.hide_dialog_spinner();
},
});
}
for (const stream_id of stream_ids) {
remove_channel_from_folder(stream_id);
}
}
function handle_archiving_channel_folder(folder_id: number): void {
confirm_dialog.launch({
html_heading: $t_html({defaultMessage: "Delete channel folder?"}),
html_body: render_confirm_archive_channel_folder(),
on_click() {
archive_folder(folder_id);
},
close_on_submit: false,
loading_spinner: true,
});
}
function handle_editing_channel_folder(folder_id: number): void {
const folder = channel_folders.get_channel_folder_by_id(folder_id);
const html_body = render_edit_channel_folder_modal({
name: folder.name,
description: folder.description,
max_channel_folder_name_length: realm.max_channel_folder_name_length,
max_channel_folder_description_length: realm.max_channel_folder_description_length,
});
dialog_widget.launch({
html_heading: $t_html({defaultMessage: "Edit channel folder"}),
html_body,
id: "edit_channel_folder",
on_click() {
const url = "/json/channel_folders/" + folder_id.toString();
const data = {
name: $<HTMLInputElement>("input#edit_channel_folder_name").val()!.trim(),
description: $<HTMLTextAreaElement>("textarea#edit_channel_folder_description")
.val()!
.trim(),
};
dialog_widget.submit_api_request(channel.patch, url, data);
},
loading_spinner: true,
on_shown: () => $("#edit_channel_folder_name").trigger("focus"),
update_submit_disabled_state_on_change: true,
});
}
export function set_up_folder_dropdown_widget(sub?: StreamSubscription): DropdownWidget {
const folder_options = (): dropdown_widget.Option[] => {
const folders = channel_folders
@ -484,7 +388,7 @@ export function set_up_folder_dropdown_widget(sub?: StreamSubscription): Dropdow
$(event.target).closest(".list-item").attr("data-unique-id")!,
10,
);
handle_archiving_channel_folder(folder_id);
channel_folders_ui.handle_archiving_channel_folder(folder_id);
return;
}
@ -497,7 +401,7 @@ export function set_up_folder_dropdown_widget(sub?: StreamSubscription): Dropdow
$(event.target).closest(".list-item").attr("data-unique-id")!,
10,
);
handle_editing_channel_folder(folder_id);
channel_folders_ui.handle_editing_channel_folder(folder_id);
return;
}