mirror of
https://github.com/zulip/zulip.git
synced 2025-10-23 16:14:02 +00:00
channel-folders: Add UI to create new channel folder.
This commit adds a button besides the folder dropdowin in stream settings UI which can be used to create a new folder.
This commit is contained in:
@@ -8,6 +8,9 @@ export type ChannelFolder = z.infer<typeof channel_folder_schema>;
|
||||
let channel_folder_name_dict: FoldDict<ChannelFolder>;
|
||||
let channel_folder_by_id_dict: Map<number, ChannelFolder>;
|
||||
|
||||
export const MAX_CHANNEL_FOLDER_NAME_LENGTH = 100;
|
||||
export const MAX_CHANNEL_FOLDER_DESCRIPTION_LENGTH = 1024;
|
||||
|
||||
export function add(channel_folder: ChannelFolder): void {
|
||||
channel_folder_name_dict.set(channel_folder.name, channel_folder);
|
||||
channel_folder_by_id_dict.set(channel_folder.id, channel_folder);
|
||||
|
@@ -9,6 +9,7 @@ import * as blueslip from "./blueslip.ts";
|
||||
import * as bot_data from "./bot_data.ts";
|
||||
import * as browser_history from "./browser_history.ts";
|
||||
import {buddy_list} from "./buddy_list.ts";
|
||||
import * as channel_folders from "./channel_folders.ts";
|
||||
import * as compose_call from "./compose_call.ts";
|
||||
import * as compose_call_ui from "./compose_call_ui.ts";
|
||||
import * as compose_closed_ui from "./compose_closed_ui.ts";
|
||||
@@ -113,6 +114,18 @@ export function dispatch_normal_event(event) {
|
||||
attachments_ui.update_attachments(event);
|
||||
break;
|
||||
|
||||
case "channel_folder":
|
||||
switch (event.op) {
|
||||
case "add": {
|
||||
channel_folders.add(event.channel_folder);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
blueslip.error("Unexpected event type channel_folder/" + event.op);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case "custom_profile_fields":
|
||||
realm.custom_profile_fields = event.fields;
|
||||
settings_profile_fields.populate_profile_fields(realm.custom_profile_fields);
|
||||
|
@@ -10,6 +10,7 @@ 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";
|
||||
|
||||
@@ -17,6 +18,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 confirm_dialog from "./confirm_dialog.ts";
|
||||
import {show_copied_confirmation} from "./copied_tooltip.ts";
|
||||
import * as dialog_widget from "./dialog_widget.ts";
|
||||
@@ -920,4 +922,58 @@ 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: channel_folders.MAX_CHANNEL_FOLDER_NAME_LENGTH,
|
||||
max_channel_folder_description_length:
|
||||
channel_folders.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,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@@ -869,6 +869,7 @@ function setup_page(callback: () => void): void {
|
||||
group_setting_labels: settings_config.all_group_setting_labels.stream,
|
||||
realm_has_archived_channels,
|
||||
has_billing_access: settings_data.user_has_billing_access(),
|
||||
is_admin: current_user.is_admin,
|
||||
};
|
||||
|
||||
const rendered = render_stream_settings_overlay(template_data);
|
||||
|
@@ -1323,7 +1323,8 @@ div.settings-radio-input-parent {
|
||||
}
|
||||
|
||||
#change_user_group_description,
|
||||
#change_stream_description {
|
||||
#change_stream_description,
|
||||
#new_channel_folder_description {
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
margin-bottom: 4px;
|
||||
@@ -1359,6 +1360,15 @@ div.settings-radio-input-parent {
|
||||
}
|
||||
|
||||
#subscription_overlay .channel-folder-widget-container {
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
width: auto;
|
||||
/* Set minimum width such that the total width of dropdown
|
||||
button and "Create folder" button is at least equal to the
|
||||
minimum width of pill inputs for permission settings. */
|
||||
min-width: var(--modal-input-width);
|
||||
flex-wrap: wrap;
|
||||
|
||||
.dropdown_widget_value {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
@@ -0,0 +1,12 @@
|
||||
<div>
|
||||
<label for="new_channel_folder_name" class="modal-field-label">
|
||||
{{t 'Channel folder name' }}
|
||||
</label>
|
||||
<input type="text" id="new_channel_folder_name" class="modal_text_input" name="channel_folder_name" maxlength="{{ max_channel_folder_name_length }}" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="new_channel_folder_description" class="modal-field-label">
|
||||
{{t 'Description' }}
|
||||
</label>
|
||||
<textarea id="new_channel_folder_description" class="settings_textarea" name="channel_folder_description" maxlength="{{ max_channel_folder_description_length }}"></textarea>
|
||||
</div>
|
@@ -88,6 +88,7 @@
|
||||
prefix="id_"
|
||||
group_setting_labels=../group_setting_labels
|
||||
channel_folder_widget_name="folder_id"
|
||||
is_admin=../is_admin
|
||||
}}
|
||||
{{/with}}
|
||||
<div class="stream_details_box">
|
||||
|
@@ -52,12 +52,26 @@
|
||||
</div>
|
||||
|
||||
<div class="input-group channel-folder-container">
|
||||
{{!-- This is a modified version of dropdown_widget_with_label.hbs
|
||||
component so that we can show dropdown button and button to create
|
||||
a new folder on same line without having to add much CSS with
|
||||
hardcoded margin and padding values. --}}
|
||||
<label class="settings-field-label" for="{{channel_folder_widget_name}}_widget">
|
||||
{{t "Channel folder"}}
|
||||
</label>
|
||||
<span class="prop-element hide" id="id_{{channel_folder_widget_name}}" data-setting-widget-type="dropdown-list-widget" data-setting-value-type="number"></span>
|
||||
<div class="dropdown_widget_with_label_wrapper channel-folder-widget-container">
|
||||
{{> ../dropdown_widget widget_name=channel_folder_widget_name}}
|
||||
|
||||
{{#if is_admin}}
|
||||
{{> ../components/action_button
|
||||
label=(t "Create new folder")
|
||||
attention="quiet"
|
||||
intent="neutral"
|
||||
type="button"
|
||||
custom_classes="create-channel-folder-button"
|
||||
}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -137,6 +137,7 @@ page_params.test_suite = false;
|
||||
|
||||
// For data-oriented modules, just use them, don't stub them.
|
||||
const alert_words = zrequire("alert_words");
|
||||
const channel_folders = zrequire("channel_folders");
|
||||
const emoji = zrequire("emoji");
|
||||
const message_store = zrequire("message_store");
|
||||
const people = zrequire("people");
|
||||
@@ -487,6 +488,23 @@ run_test("scheduled_messages", ({override}) => {
|
||||
}
|
||||
});
|
||||
|
||||
run_test("channel_folders", () => {
|
||||
channel_folders.initialize({channel_folders: []});
|
||||
|
||||
const event = event_fixtures.channel_folder__add;
|
||||
{
|
||||
dispatch(event);
|
||||
|
||||
const folders = channel_folders.get_channel_folders();
|
||||
assert.equal(folders.length, 1);
|
||||
assert.equal(folders[0].id, event.channel_folder.id);
|
||||
assert.equal(folders[0].name, event.channel_folder.name);
|
||||
}
|
||||
|
||||
blueslip.expect("error", "Unexpected event type channel_folder/other");
|
||||
server_events_dispatch.dispatch_normal_event({type: "channel_folder", op: "other"});
|
||||
});
|
||||
|
||||
run_test("realm settings", ({override}) => {
|
||||
override(current_user, "is_admin", true);
|
||||
override(realm, "realm_date_created", new Date("2023-01-01Z"));
|
||||
|
@@ -137,6 +137,20 @@ exports.fixtures = {
|
||||
upload_space_used: 90000,
|
||||
},
|
||||
|
||||
channel_folder__add: {
|
||||
type: "channel_folder",
|
||||
op: "add",
|
||||
channel_folder: {
|
||||
id: 1,
|
||||
name: "Frontend",
|
||||
description: "Channels for frontend discussions",
|
||||
rendered_description: "<p>Channels for frontend discussions</p>",
|
||||
date_created: 1681662420,
|
||||
creator_id: 10,
|
||||
is_archived: false,
|
||||
},
|
||||
},
|
||||
|
||||
channel_typing_edit_message__start: {
|
||||
type: "typing_edit_message",
|
||||
op: "start",
|
||||
|
Reference in New Issue
Block a user