From 8711730e6b5ed5bb65415f680212d72f5fd0d24a Mon Sep 17 00:00:00 2001 From: Pratik Chanda Date: Tue, 5 Aug 2025 18:53:48 +0530 Subject: [PATCH] left_sidebar: Add menu to change channel folder setting in left sidebar. Earlier, you could change setting to show/hide channel folders in left sidebar from setting overlay. This commit adds an menu option to toggle channel folders setting in the left sidebar. The menu is hidden if there are no subscribed channels with channel folder. Fixes #35574. --- tools/test-js-with-node | 1 + web/src/channel_folders_popover.ts | 54 +++++++++++++++++++ web/src/popover_menus.ts | 4 +- web/src/stream_list.ts | 13 +++++ web/src/ui_init.js | 2 + web/styles/left_sidebar.css | 30 +++++++++-- web/templates/left_sidebar.hbs | 1 + ...idebar_channels_folder_setting_popover.hbs | 14 +++++ 8 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 web/src/channel_folders_popover.ts create mode 100644 web/templates/popovers/left_sidebar/left_sidebar_channels_folder_setting_popover.hbs diff --git a/tools/test-js-with-node b/tools/test-js-with-node index bb73200bcd..cb3ace9588 100755 --- a/tools/test-js-with-node +++ b/tools/test-js-with-node @@ -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_popover.ts", "web/src/channel_folders_ui.ts", "web/src/click_handlers.ts", "web/src/color_picker_popover.ts", diff --git a/web/src/channel_folders_popover.ts b/web/src/channel_folders_popover.ts new file mode 100644 index 0000000000..0d3b4b8a43 --- /dev/null +++ b/web/src/channel_folders_popover.ts @@ -0,0 +1,54 @@ +import $ from "jquery"; +import assert from "minimalistic-assert"; +import type * as tippy from "tippy.js"; + +import render_left_sidebar_channels_folder_setting_popover from "../templates/popovers/left_sidebar/left_sidebar_channels_folder_setting_popover.hbs"; + +import * as channel from "./channel.ts"; +import * as popover_menus from "./popover_menus.ts"; +import {parse_html} from "./ui_util.ts"; +import {user_settings} from "./user_settings.ts"; + +function do_change_show_channel_folders(instance: tippy.Instance): void { + const show_channel_folders = user_settings.web_left_sidebar_show_channel_folders; + const data = { + web_left_sidebar_show_channel_folders: JSON.stringify(!show_channel_folders), + }; + void channel.patch({ + url: "/json/settings", + data, + }); + popover_menus.hide_current_popover_if_visible(instance); +} + +export function initialize(): void { + popover_menus.register_popover_menu("#left-sidebar-search .channel-folders-sidebar-menu-icon", { + ...popover_menus.left_sidebar_tippy_options, + theme: "popover-menu", + onMount(instance) { + const $popper = $(instance.popper); + assert(instance.reference instanceof HTMLElement); + $popper.one("click", "#left_sidebar_channel_folders", () => { + do_change_show_channel_folders(instance); + }); + }, + onShow(instance) { + const show_channel_folders = user_settings.web_left_sidebar_show_channel_folders; + // Assuming that the instance can be shown, track and + // prep the instance for showing + popover_menus.popover_instances.show_channels_sidebar = instance; + instance.setContent( + parse_html( + render_left_sidebar_channels_folder_setting_popover({show_channel_folders}), + ), + ); + popover_menus.on_show_prep(instance); + + return undefined; + }, + onHidden(instance) { + instance.destroy(); + popover_menus.popover_instances.show_channels_sidebar = null; + }, + }); +} diff --git a/web/src/popover_menus.ts b/web/src/popover_menus.ts index 3cc839b6d2..849cd298e7 100644 --- a/web/src/popover_menus.ts +++ b/web/src/popover_menus.ts @@ -32,7 +32,8 @@ type PopoverName = | "help_menu" | "buddy_list" | "stream_actions_popover" - | "color_picker_popover"; + | "color_picker_popover" + | "show_channels_sidebar"; export const popover_instances: Record = { compose_control_buttons: null, @@ -54,6 +55,7 @@ export const popover_instances: Record = { buddy_list: null, stream_actions_popover: null, color_picker_popover: null, + show_channels_sidebar: null, }; // Font size in em for popover derived from popover font size being diff --git a/web/src/stream_list.ts b/web/src/stream_list.ts index 34773239f1..4cf0f43049 100644 --- a/web/src/stream_list.ts +++ b/web/src/stream_list.ts @@ -284,6 +284,17 @@ export function update_unread_counts_visibility(): void { // `update_section_unread_count`, since they depend on unread counts. } +function maybe_change_channel_folders_option_visibility(): void { + const $channel_folders_sidebar_option = $( + "#left-sidebar-search .channel-folders-sidebar-menu-icon", + ); + if (channel_folders.user_has_folders()) { + $channel_folders_sidebar_option.show(); + } else { + $channel_folders_sidebar_option.hide(); + } +} + export function build_stream_list(force_rerender: boolean): void { // The stream list in the left sidebar contains 3 sections: // pinned, normal, and dormant streams, with headings above them @@ -303,6 +314,8 @@ export function build_stream_list(force_rerender: boolean): void { return; } + maybe_change_channel_folders_option_visibility(); + function add_sidebar_li(stream_id: number, $list: JQuery, inactive_or_muted = false): void { const sidebar_row = stream_sidebar.get_row(stream_id); assert(sidebar_row !== undefined); diff --git a/web/src/ui_init.js b/web/src/ui_init.js index a278cca726..4fba43b6aa 100644 --- a/web/src/ui_init.js +++ b/web/src/ui_init.js @@ -22,6 +22,7 @@ import * as blueslip from "./blueslip.ts"; import * as bot_data from "./bot_data.ts"; import * as channel from "./channel.ts"; import * as channel_folders from "./channel_folders.ts"; +import * as channel_folders_popover from "./channel_folders_popover.ts"; import * as click_handlers from "./click_handlers.ts"; import * as color_picker_popover from "./color_picker_popover.ts"; import * as common from "./common.ts"; @@ -587,6 +588,7 @@ export async function initialize_everything(state_data) { stream_popover.initialize(); color_picker_popover.initialize(); add_stream_options_popover.initialize(); + channel_folders_popover.initialize(); click_handlers.initialize(); scheduled_messages_overlay_ui.initialize(); compose_paste.initialize({ diff --git a/web/styles/left_sidebar.css b/web/styles/left_sidebar.css index dbc8718e54..3dedd90ce7 100644 --- a/web/styles/left_sidebar.css +++ b/web/styles/left_sidebar.css @@ -55,7 +55,8 @@ .add-stream-icon-container { grid-area: add-channel; display: grid; - margin: 2px 0; + /* 2px right margin here to match the new topic margin-right */ + margin: 2px 1px 2px 0; border-radius: 3px; .add_stream_icon { @@ -331,8 +332,10 @@ #left-sidebar-search { display: grid; - grid-template-areas: "filter-container add-channel"; - grid-template-columns: minmax(0, 1fr) var(--left-sidebar-header-icon-width); + grid-template-areas: "filter-container add-channel channel-folders-option"; + grid-template-columns: + minmax(0, 1fr) var(--left-sidebar-header-icon-width) + auto; position: sticky; top: 0; /* Must be more than .stream-list-subsection-header */ @@ -340,6 +343,27 @@ background: var(--color-background); /* Must be padding not margin so that the sticky headers don't show behind it */ padding: 0 var(--left-sidebar-right-margin) 3px 5px; + + .channel-folders-sidebar-menu-icon { + grid-area: channel-folders-option; + display: grid; + /* width excluding left & right margin */ + width: calc(var(--left-sidebar-vdots-width) - 3px); + cursor: pointer; + place-items: center center; + border-radius: 3px; + margin: 2px 2px 2px 1px; + color: var(--color-vdots-visible); + + & i.zulip-icon { + font-size: 1.0625em; + } + + &:hover { + color: var(--color-vdots-hover); + background-color: var(--color-background-sidebar-action-hover); + } + } } .stream-list-subsection-header { diff --git a/web/templates/left_sidebar.hbs b/web/templates/left_sidebar.hbs index 5943caef17..c7f5aa9cf5 100644 --- a/web/templates/left_sidebar.hbs +++ b/web/templates/left_sidebar.hbs @@ -8,6 +8,7 @@ +