mirror of
https://github.com/zulip/zulip.git
synced 2025-10-24 08:33:43 +00:00
left_sidebar: Hide inactive channels in channel folders.
This commit is contained in:
@@ -88,8 +88,12 @@ function should_mask_header_unread_count(
|
||||
}
|
||||
|
||||
type SectionUnreadCount = {
|
||||
// These both include inactive unreads as well.
|
||||
unmuted: number;
|
||||
muted: number;
|
||||
// These are used for the "+ n inactive channels" button.
|
||||
inactive_unmuted: number;
|
||||
inactive_muted: number;
|
||||
};
|
||||
|
||||
export function update_dom_with_unread_counts(
|
||||
@@ -110,15 +114,25 @@ export function update_dom_with_unread_counts(
|
||||
const pinned_unread_counts: SectionUnreadCount = {
|
||||
unmuted: 0,
|
||||
muted: 0,
|
||||
// Not used for the pinned section, but included here to make typing easier
|
||||
inactive_unmuted: 0,
|
||||
inactive_muted: 0,
|
||||
};
|
||||
const folder_unread_counts = new Map<number, SectionUnreadCount>();
|
||||
// TODO: In an upcoming commit, the normal and inactive sections will be
|
||||
// merged. For this commit, the normal section has no inactive channels
|
||||
// and the inactive section has no active channels.
|
||||
const normal_section_unread_counts: SectionUnreadCount = {
|
||||
unmuted: 0,
|
||||
muted: 0,
|
||||
inactive_unmuted: 0,
|
||||
inactive_muted: 0,
|
||||
};
|
||||
const inactive_section_unread_counts: SectionUnreadCount = {
|
||||
unmuted: 0,
|
||||
muted: 0,
|
||||
inactive_unmuted: 0,
|
||||
inactive_muted: 0,
|
||||
};
|
||||
|
||||
for (const [stream_id, stream_count_info] of counts.stream_count.entries()) {
|
||||
@@ -131,12 +145,18 @@ export function update_dom_with_unread_counts(
|
||||
const unread_counts = folder_unread_counts.get(sub.folder_id) ?? {
|
||||
unmuted: 0,
|
||||
muted: 0,
|
||||
inactive_unmuted: 0,
|
||||
inactive_muted: 0,
|
||||
};
|
||||
if (!folder_unread_counts.has(sub.folder_id)) {
|
||||
folder_unread_counts.set(sub.folder_id, unread_counts);
|
||||
}
|
||||
unread_counts.unmuted += stream_count_info.unmuted_count;
|
||||
unread_counts.muted += stream_count_info.muted_count;
|
||||
if (!stream_list_sort.has_recent_activity(sub)) {
|
||||
unread_counts.inactive_unmuted += stream_count_info.unmuted_count;
|
||||
unread_counts.inactive_muted += stream_count_info.muted_count;
|
||||
}
|
||||
} else if (stream_list_sort.has_recent_activity(sub)) {
|
||||
normal_section_unread_counts.unmuted += stream_count_info.unmuted_count;
|
||||
normal_section_unread_counts.muted += stream_count_info.muted_count;
|
||||
@@ -186,6 +206,11 @@ export function update_dom_with_unread_counts(
|
||||
unread_counts?.unmuted ?? 0,
|
||||
unread_counts?.muted ?? 0,
|
||||
);
|
||||
update_section_unread_count(
|
||||
$(`#stream-list-${folder_id}-container .show-inactive-channels`),
|
||||
unread_counts?.inactive_unmuted ?? 0,
|
||||
unread_counts?.inactive_muted ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
if (!skip_animations) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import * as tippy from "tippy.js";
|
||||
import render_filter_topics from "../templates/filter_topics.hbs";
|
||||
import render_go_to_channel_feed_tooltip from "../templates/go_to_channel_feed_tooltip.hbs";
|
||||
import render_go_to_channel_list_of_topics_tooltip from "../templates/go_to_channel_list_of_topics_tooltip.hbs";
|
||||
import render_show_inactive_channels from "../templates/show_inactive_channels.hbs";
|
||||
import render_stream_list_section_container from "../templates/stream_list_section_container.hbs";
|
||||
import render_stream_privacy from "../templates/stream_privacy.hbs";
|
||||
import render_stream_sidebar_row from "../templates/stream_sidebar_row.hbs";
|
||||
@@ -60,6 +61,7 @@ export function rewire_stream_cursor(value: typeof stream_cursor): void {
|
||||
let has_scrolled = false;
|
||||
|
||||
const collapsed_sections = new Set<string>();
|
||||
const sections_showing_inactive = new Set<string>();
|
||||
|
||||
export function is_zoomed_in(): boolean {
|
||||
return zoomed_in;
|
||||
@@ -305,11 +307,19 @@ export function build_stream_list(force_rerender: boolean): void {
|
||||
return;
|
||||
}
|
||||
|
||||
function add_sidebar_li(stream_id: number, $list: JQuery): void {
|
||||
function add_sidebar_li(
|
||||
stream_id: number,
|
||||
$list: JQuery,
|
||||
inactive_in_channel_folder = false,
|
||||
): void {
|
||||
const sidebar_row = stream_sidebar.get_row(stream_id);
|
||||
assert(sidebar_row !== undefined);
|
||||
sidebar_row.update_whether_active();
|
||||
$list.append($(sidebar_row.get_li()));
|
||||
const $li = sidebar_row.get_li();
|
||||
if (inactive_in_channel_folder) {
|
||||
$li.addClass("inactive-in-channel-folder");
|
||||
}
|
||||
$list.append($li);
|
||||
}
|
||||
|
||||
clear_topics();
|
||||
@@ -322,17 +332,34 @@ export function build_stream_list(force_rerender: boolean): void {
|
||||
$("#stream_filters").append(
|
||||
$(stream_list_section_container_html(section, can_create_streams)),
|
||||
);
|
||||
const is_empty = section.streams.length === 0 && section.muted_streams.length === 0;
|
||||
const is_empty =
|
||||
section.streams.length === 0 &&
|
||||
section.muted_streams.length === 0 &&
|
||||
section.inactive_streams.length === 0;
|
||||
$(`#stream-list-${section.id}-container`).toggleClass("no-display", is_empty);
|
||||
|
||||
for (const stream_id of [...section.streams, ...section.muted_streams]) {
|
||||
add_sidebar_li(stream_id, $(`#stream-list-${section.id}`));
|
||||
}
|
||||
// This should only be relevant for folders
|
||||
for (const stream_id of section.inactive_streams) {
|
||||
add_sidebar_li(stream_id, $(`#stream-list-${section.id}`), true);
|
||||
}
|
||||
if (section.inactive_streams.length > 0) {
|
||||
$(`#stream-list-${section.id}`).append(
|
||||
$(
|
||||
render_show_inactive_channels({
|
||||
inactive_count: section.inactive_streams.length,
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
// Rerendering can moving channels between folders and change heading unread counts.
|
||||
left_sidebar_navigation_area.update_dom_with_unread_counts(unread.get_counts(), false);
|
||||
sidebar_ui.update_unread_counts_visibility();
|
||||
collapse_collapsed_sections();
|
||||
set_sections_states();
|
||||
$("#streams_list").toggleClass("is_searching", get_search_term() !== "");
|
||||
}
|
||||
|
||||
/* When viewing a channel in a collapsed folder, we show that active
|
||||
@@ -365,7 +392,7 @@ function toggle_section_collapse($container: JQuery): void {
|
||||
maybe_hide_topic_bracket(section_id);
|
||||
}
|
||||
|
||||
function collapse_collapsed_sections(): void {
|
||||
function set_sections_states(): void {
|
||||
for (const section_id of collapsed_sections) {
|
||||
const $container = $(`#stream-list-${section_id}-container`);
|
||||
$container.toggleClass("collapsed", true);
|
||||
@@ -374,6 +401,9 @@ function collapse_collapsed_sections(): void {
|
||||
.toggleClass("rotate-icon-down", false)
|
||||
.toggleClass("rotate-icon-right", true);
|
||||
}
|
||||
for (const section_id of sections_showing_inactive) {
|
||||
$(`#stream-list-${section_id}-container`).toggleClass("showing-inactive", true);
|
||||
}
|
||||
}
|
||||
|
||||
export function get_stream_li(stream_id: number): JQuery | undefined {
|
||||
@@ -1141,6 +1171,23 @@ export function set_event_handlers({
|
||||
e.stopPropagation();
|
||||
},
|
||||
);
|
||||
|
||||
$("#streams_list").on(
|
||||
"click",
|
||||
".stream-list-toggle-inactive-channels",
|
||||
function (this: HTMLElement, e: JQuery.ClickEvent) {
|
||||
e.stopPropagation();
|
||||
const $section_container = $(this).closest(".stream-list-section-container");
|
||||
$section_container.toggleClass("showing-inactive");
|
||||
const showing_inactive = $section_container.hasClass("showing-inactive");
|
||||
const section_id = $section_container.attr("data-section-id")!;
|
||||
if (showing_inactive) {
|
||||
sections_showing_inactive.add(section_id);
|
||||
} else {
|
||||
sections_showing_inactive.delete(section_id);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function searching(): boolean {
|
||||
|
||||
@@ -30,6 +30,7 @@ function current_section_ids_for_streams(): Map<number, StreamListSection> {
|
||||
for (const stream_id of [
|
||||
...section.streams,
|
||||
...section.muted_streams,
|
||||
...section.inactive_streams,
|
||||
]) {
|
||||
map.set(stream_id, section);
|
||||
}
|
||||
@@ -94,6 +95,7 @@ export type StreamListSection = {
|
||||
section_title: string;
|
||||
streams: number[];
|
||||
muted_streams: number[]; // Not used for the inactive section
|
||||
inactive_streams: number[]; // Only used for folder sections
|
||||
};
|
||||
|
||||
type StreamListSortResult = {
|
||||
@@ -121,18 +123,21 @@ export function sort_groups(stream_ids: number[], search_term: string): StreamLi
|
||||
section_title: $t({defaultMessage: "PINNED CHANNELS"}),
|
||||
streams: [],
|
||||
muted_streams: [],
|
||||
inactive_streams: [],
|
||||
};
|
||||
const normal_section: StreamListSection = {
|
||||
id: "normal-streams",
|
||||
section_title: $t({defaultMessage: "OTHER CHANNELS"}),
|
||||
streams: [],
|
||||
muted_streams: [],
|
||||
inactive_streams: [],
|
||||
};
|
||||
const dormant_section: StreamListSection = {
|
||||
id: "dormant-streams",
|
||||
section_title: $t({defaultMessage: "INACTIVE CHANNELS"}),
|
||||
streams: [],
|
||||
muted_streams: [], // Not used for the dormant section
|
||||
inactive_streams: [],
|
||||
};
|
||||
|
||||
const folder_sections = new Map<number, StreamListSection>();
|
||||
@@ -158,10 +163,13 @@ export function sort_groups(stream_ids: number[], search_term: string): StreamLi
|
||||
section_title: folder.name.toUpperCase(),
|
||||
streams: [],
|
||||
muted_streams: [],
|
||||
inactive_streams: [],
|
||||
};
|
||||
folder_sections.set(sub.folder_id, section);
|
||||
}
|
||||
if (sub.is_muted) {
|
||||
if (!has_recent_activity(sub)) {
|
||||
section.inactive_streams.push(stream_id);
|
||||
} else if (sub.is_muted) {
|
||||
section.muted_streams.push(stream_id);
|
||||
} else {
|
||||
section.streams.push(stream_id);
|
||||
@@ -196,6 +204,7 @@ export function sort_groups(stream_ids: number[], search_term: string): StreamLi
|
||||
for (const section of new_sections) {
|
||||
section.streams.sort(compare_function);
|
||||
section.muted_streams.sort(compare_function);
|
||||
section.inactive_streams.sort(compare_function);
|
||||
}
|
||||
|
||||
const same_as_before =
|
||||
@@ -207,16 +216,18 @@ export function sort_groups(stream_ids: number[], search_term: string): StreamLi
|
||||
new_section.id === current_section.id &&
|
||||
new_section.section_title === current_section.section_title &&
|
||||
util.array_compare(new_section.streams, current_section.streams) &&
|
||||
util.array_compare(new_section.muted_streams, current_section.muted_streams)
|
||||
util.array_compare(new_section.muted_streams, current_section.muted_streams) &&
|
||||
util.array_compare(new_section.inactive_streams, current_section.inactive_streams)
|
||||
);
|
||||
});
|
||||
|
||||
if (!same_as_before) {
|
||||
first_render_completed = true;
|
||||
current_sections = new_sections;
|
||||
all_streams = new_sections.flatMap((section) => [
|
||||
all_streams = current_sections.flatMap((section) => [
|
||||
...section.streams,
|
||||
...section.muted_streams,
|
||||
...section.inactive_streams,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
#left-sidebar-navigation-list .selected-home-view {
|
||||
#left-sidebar-navigation-list .selected-home-view,
|
||||
.show-inactive-channels {
|
||||
&.hide-unread-messages-count {
|
||||
.masked_unread_count {
|
||||
display: flex;
|
||||
@@ -108,6 +109,7 @@
|
||||
}
|
||||
|
||||
#left-sidebar-navigation-list .selected-home-view:hover,
|
||||
.stream-list-toggle-inactive-channels:hover .show-inactive-channels,
|
||||
.selected-home-view.top-left-active-filter {
|
||||
&.hide-unread-messages-count {
|
||||
.masked_unread_count {
|
||||
@@ -403,6 +405,67 @@
|
||||
}
|
||||
}
|
||||
|
||||
.stream-list-toggle-inactive-channels {
|
||||
padding-left: var(--left-sidebar-toggle-width-offset);
|
||||
|
||||
.show-inactive-channels,
|
||||
.hide-inactive-channels {
|
||||
/* Override the action heading font size, so that em measurements
|
||||
on child elements are sized properly */
|
||||
font-size: var(--base-font-size-px);
|
||||
/* We also want the count text to be the normal font style. */
|
||||
font-variant: inherit;
|
||||
padding: 0;
|
||||
|
||||
.stream-list-toggle-inactive-channels-text {
|
||||
font-size: var(--font-size-sidebar-action-heading);
|
||||
font-variant: var(--font-variant-sidebar-action-heading);
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.stream-list-section-container:not(.showing-inactive) {
|
||||
.inactive-in-channel-folder {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.stream-list-toggle-inactive-channels {
|
||||
.hide-inactive-channels {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#streams_list.is_searching {
|
||||
.show-inactive-channels,
|
||||
.hide-inactive-channels {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.inactive-in-channel-folder {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.stream-list-section-container.showing-inactive {
|
||||
.stream-list-toggle-inactive-channels {
|
||||
.show-inactive-channels {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.show-inactive-channels,
|
||||
.hide-inactive-channels {
|
||||
display: grid;
|
||||
grid-template:
|
||||
"content markers-and-unreads three-dot-placeholder" auto
|
||||
/ minmax(0, 1fr) minmax(0, max-content) var(--left-sidebar-vdots-width);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stream-list-section {
|
||||
margin: 0;
|
||||
}
|
||||
@@ -413,6 +476,7 @@
|
||||
|
||||
.stream-list-section-container.collapsed {
|
||||
.narrow-filter:not(.stream-expanded),
|
||||
.stream-list-toggle-inactive-channels,
|
||||
.topic-list-item:not(.active-sub-filter),
|
||||
&.hide-topic-bracket ul.topic-list.topic-list-has-topics::before,
|
||||
&.hide-topic-bracket ul.topic-list.topic-list-has-topics::after {
|
||||
@@ -1545,7 +1609,8 @@ li.top_left_scheduled_messages {
|
||||
|
||||
.dm-markers-and-unreads,
|
||||
.stream-markers-and-unreads,
|
||||
.topic-markers-and-unreads {
|
||||
.topic-markers-and-unreads,
|
||||
.show-inactive-channels .markers-and-unreads {
|
||||
grid-area: markers-and-unreads;
|
||||
display: flex;
|
||||
/* Present a uniform space between icons */
|
||||
|
||||
20
web/templates/show_inactive_channels.hbs
Normal file
20
web/templates/show_inactive_channels.hbs
Normal file
@@ -0,0 +1,20 @@
|
||||
<div class="stream-list-toggle-inactive-channels bottom_left_row zoom-in-hide">
|
||||
<div class="show-inactive-channels sidebar-topic-action-heading">
|
||||
<div class="stream-list-toggle-inactive-channels-text">
|
||||
{{#tr}}
|
||||
+ {inactive_count, plural, =1 {# inactive channel} other {# inactive channels}}
|
||||
{{/tr}}
|
||||
</div>
|
||||
<div class="markers-and-unreads">
|
||||
<span class="unread_count quiet-count"></span>
|
||||
<span class="masked_unread_count">
|
||||
<i class="zulip-icon zulip-icon-masked-unread"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hide-inactive-channels sidebar-topic-action-heading">
|
||||
<div class="stream-list-toggle-inactive-channels-text">
|
||||
{{t "Hide inactive channels"}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -103,18 +103,21 @@ test("no_subscribed_streams", () => {
|
||||
sections: [
|
||||
{
|
||||
id: "pinned-streams",
|
||||
inactive_streams: [],
|
||||
muted_streams: [],
|
||||
section_title: "translated: PINNED CHANNELS",
|
||||
streams: [],
|
||||
},
|
||||
{
|
||||
id: "normal-streams",
|
||||
inactive_streams: [],
|
||||
muted_streams: [],
|
||||
section_title: "translated: CHANNELS",
|
||||
streams: [],
|
||||
},
|
||||
{
|
||||
id: "dormant-streams",
|
||||
inactive_streams: [],
|
||||
muted_streams: [],
|
||||
section_title: "translated: INACTIVE CHANNELS",
|
||||
streams: [],
|
||||
|
||||
Reference in New Issue
Block a user