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 @@ +