left_sidebar: Combine muted and inactive channels.

Part of #35552.
This commit is contained in:
Evy Kassirer
2025-07-31 12:39:49 -07:00
committed by Tim Abbott
parent 3ced9a1ad3
commit c56ddf9ba4
5 changed files with 97 additions and 91 deletions

View File

@@ -6,7 +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_show_inactive_or_muted_channels from "../templates/show_inactive_or_muted_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";
@@ -62,7 +62,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>();
const sections_showing_inactive_or_muted = new Set<string>();
export function is_zoomed_in(): boolean {
return zoomed_in;
@@ -308,17 +308,13 @@ export function build_stream_list(force_rerender: boolean): void {
return;
}
function add_sidebar_li(
stream_id: number,
$list: JQuery,
inactive_in_channel_folder = false,
): void {
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);
sidebar_row.update_whether_active();
const $li = sidebar_row.get_li();
if (inactive_in_channel_folder) {
$li.addClass("inactive-in-channel-folder");
if (inactive_or_muted) {
$li.addClass("inactive-or-muted-in-channel-folder");
}
$list.append($li);
}
@@ -339,17 +335,23 @@ export function build_stream_list(force_rerender: boolean): void {
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]) {
for (const stream_id of section.streams) {
add_sidebar_li(stream_id, $(`#stream-list-${section.id}`));
}
for (const stream_id of section.inactive_streams) {
add_sidebar_li(stream_id, $(`#stream-list-${section.id}`), true);
for (const stream_id of [...section.muted_streams, ...section.inactive_streams]) {
add_sidebar_li(
stream_id,
$(`#stream-list-${section.id}`),
section.id !== "pinned-streams",
);
}
if (section.inactive_streams.length > 0) {
const inactive_or_muted_count =
section.muted_streams.length + section.inactive_streams.length;
if (section.id !== "pinned-streams" && inactive_or_muted_count > 0) {
$(`#stream-list-${section.id}`).append(
$(
render_show_inactive_channels({
inactive_count: section.inactive_streams.length,
render_show_inactive_or_muted_channels({
inactive_or_muted_count,
}),
),
);
@@ -404,8 +406,8 @@ function set_sections_states(): 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);
for (const section_id of sections_showing_inactive_or_muted) {
$(`#stream-list-${section_id}-container`).toggleClass("showing-inactive-or-muted", true);
}
}
@@ -770,9 +772,9 @@ export let update_dom_with_unread_counts = function (counts: FullUnreadCountsDat
normal_section_unread_counts.muted,
);
update_section_unread_count(
$("#stream-list-normal-streams-container .show-inactive-channels"),
$("#stream-list-normal-streams-container .show-inactive-or-muted-channels"),
normal_section_unread_counts.inactive_unmuted,
normal_section_unread_counts.inactive_muted,
normal_section_unread_counts.inactive_muted + normal_section_unread_counts.muted,
);
for (const folder_id of channel_folders.get_all_folder_ids()) {
@@ -788,9 +790,9 @@ export let update_dom_with_unread_counts = function (counts: FullUnreadCountsDat
unread_counts.muted,
);
update_section_unread_count(
$(`#stream-list-${folder_id}-container .show-inactive-channels`),
$(`#stream-list-${folder_id}-container .show-inactive-or-muted-channels`),
unread_counts.inactive_unmuted,
unread_counts.inactive_muted,
unread_counts.inactive_muted + unread_counts.muted,
);
}
};
@@ -842,7 +844,11 @@ export function refresh_pinned_or_unpinned_stream(sub: StreamSubscription): void
export function refresh_muted_or_unmuted_stream(sub: StreamSubscription): void {
build_stream_sidebar_row(sub);
update_streams_sidebar();
// If a stream is inactive, it'll stay in the same inactive list in its
// StreamListSection (so `same_as_before` will be true), so we need to
// force the rerender to change its faded/unfaded appearance.
const force_rerender = !stream_list_sort.has_recent_activity(sub);
update_streams_sidebar(force_rerender);
}
export function get_sidebar_stream_topic_info(filter: Filter): {
@@ -979,9 +985,9 @@ export function initialize_stream_cursor(): void {
switch (opts.key.type) {
case "stream":
return get_stream_li(opts.key.stream_id);
case "inactive_toggle":
case "inactive_or_muted_toggle":
return $(
`#stream-list-${opts.key.section_id}-container .stream-list-toggle-inactive-channels`,
`#stream-list-${opts.key.section_id}-container .stream-list-toggle-inactive-or-muted-channels`,
);
default:
throw new Error("Unexpected key type");
@@ -991,14 +997,14 @@ export function initialize_stream_cursor(): void {
prev_key: (row) =>
stream_list_sort.prev_row(
row,
sections_showing_inactive,
sections_showing_inactive_or_muted,
collapsed_sections,
topic_list.active_stream_id(),
),
next_key: (row) =>
stream_list_sort.next_row(
row,
sections_showing_inactive,
sections_showing_inactive_or_muted,
collapsed_sections,
topic_list.active_stream_id(),
),
@@ -1270,8 +1276,8 @@ export function set_event_handlers({
case "stream":
on_sidebar_channel_click(row.stream_id, null, show_channel_feed);
break;
case "inactive_toggle":
toggle_inactive_channels($(`#stream-list-${row.section_id}-container`));
case "inactive_or_muted_toggle":
toggle_inactive_or_muted_channels($(`#stream-list-${row.section_id}-container`));
break;
default:
throw new Error("Unexpected key type");
@@ -1325,22 +1331,22 @@ export function set_event_handlers({
$("#streams_list").on(
"click",
".stream-list-toggle-inactive-channels",
".stream-list-toggle-inactive-or-muted-channels",
function (this: HTMLElement, e: JQuery.ClickEvent) {
e.stopPropagation();
toggle_inactive_channels($(this).closest(".stream-list-section-container"));
toggle_inactive_or_muted_channels($(this).closest(".stream-list-section-container"));
},
);
}
function toggle_inactive_channels($section_container: JQuery): void {
$section_container.toggleClass("showing-inactive");
const showing_inactive = $section_container.hasClass("showing-inactive");
function toggle_inactive_or_muted_channels($section_container: JQuery): void {
$section_container.toggleClass("showing-inactive-or-muted");
const showing_inactive_or_muted = $section_container.hasClass("showing-inactive-or-muted");
const section_id = $section_container.attr("data-section-id")!;
if (showing_inactive) {
sections_showing_inactive.add(section_id);
if (showing_inactive_or_muted) {
sections_showing_inactive_or_muted.add(section_id);
} else {
sections_showing_inactive.delete(section_id);
sections_showing_inactive_or_muted.delete(section_id);
}
}

View File

@@ -16,10 +16,10 @@ export type StreamListRow =
| {
type: "stream";
stream_id: number;
inactive: boolean;
inactive_or_muted: boolean;
}
| {
type: "inactive_toggle";
type: "inactive_or_muted_toggle";
section_id: string;
};
let all_rows: StreamListRow[] = [];
@@ -107,7 +107,7 @@ export type StreamListSection = {
section_title: string;
streams: number[];
muted_streams: number[];
inactive_streams: number[]; // Only used for folder sections
inactive_streams: number[];
order?: number; // Only used for folder sections
};
@@ -156,6 +156,8 @@ export function sort_groups(stream_ids: number[], search_term: string): StreamLi
if (sub.is_muted) {
pinned_section.muted_streams.push(stream_id);
} else {
// Inactive channels aren't treated differently when pinned,
// since the user wants chose to put them in the pinned section.
pinned_section.streams.push(stream_id);
}
} else if (user_settings.web_left_sidebar_show_channel_folders && sub.folder_id) {
@@ -223,23 +225,23 @@ export function sort_groups(stream_ids: number[], search_term: string): StreamLi
current_sections = new_sections;
all_rows = [];
for (const section of current_sections) {
for (const stream_id of [...section.streams, ...section.muted_streams]) {
for (const stream_id of section.streams) {
all_rows.push({
type: "stream",
stream_id,
inactive: false,
inactive_or_muted: false,
});
}
for (const stream_id of section.inactive_streams) {
for (const stream_id of [...section.muted_streams, ...section.inactive_streams]) {
all_rows.push({
type: "stream",
stream_id,
inactive: true,
inactive_or_muted: true,
});
}
if (section.inactive_streams.length > 0) {
if (section.inactive_streams.length > 0 || section.muted_streams.length > 0) {
all_rows.push({
type: "inactive_toggle",
type: "inactive_or_muted_toggle",
section_id: section.id,
});
}
@@ -259,7 +261,7 @@ export function first_row(): StreamListRow | undefined {
function is_visible_row(
row: StreamListRow,
section_id_map: Map<number, StreamListSection>,
sections_showing_inactive: Set<string>,
sections_showing_inactive_or_muted: Set<string>,
collapsed_sections: Set<string>,
active_stream_id: number | undefined,
): boolean {
@@ -270,10 +272,10 @@ function is_visible_row(
if (collapsed_sections.has(section.id) && active_stream_id !== stream_id) {
return false;
}
if (!sections_showing_inactive.has(section.id) && row.inactive) {
if (!sections_showing_inactive_or_muted.has(section.id) && row.inactive_or_muted) {
return false;
}
} else if (row.type === "inactive_toggle" && collapsed_sections.has(row.section_id)) {
} else if (row.type === "inactive_or_muted_toggle" && collapsed_sections.has(row.section_id)) {
return false;
}
return true;
@@ -281,7 +283,7 @@ function is_visible_row(
export function prev_row(
row: StreamListRow,
sections_showing_inactive: Set<string>,
sections_showing_inactive_or_muted: Set<string>,
collapsed_sections: Set<string>,
active_stream_id: number | undefined,
): StreamListRow | undefined {
@@ -294,7 +296,7 @@ export function prev_row(
is_visible_row(
prev_row,
section_id_map,
sections_showing_inactive,
sections_showing_inactive_or_muted,
collapsed_sections,
active_stream_id,
)
@@ -307,7 +309,7 @@ export function prev_row(
export function next_row(
row: StreamListRow,
sections_showing_inactive: Set<string>,
sections_showing_inactive_or_muted: Set<string>,
collapsed_sections: Set<string>,
active_stream_id: number | undefined,
): StreamListRow | undefined {
@@ -320,7 +322,7 @@ export function next_row(
is_visible_row(
next_row,
section_id_map,
sections_showing_inactive,
sections_showing_inactive_or_muted,
collapsed_sections,
active_stream_id,
)

View File

@@ -128,7 +128,7 @@
.stream-with-count.hide_unread_counts,
.stream-list-subsection-header.hide_unread_counts,
.show-inactive-channels.hide_unread_counts {
.show-inactive-or-muted-channels.hide_unread_counts {
.masked_unread_count {
display: flex;
}
@@ -401,11 +401,11 @@
}
}
.stream-list-toggle-inactive-channels {
.stream-list-toggle-inactive-or-muted-channels {
padding-left: var(--left-sidebar-toggle-width-offset);
.show-inactive-channels,
.hide-inactive-channels {
.show-inactive-or-muted-channels,
.hide-inactive-or-muted-channels {
/* Override the action heading font size, so that em measurements
on child elements are sized properly */
font-size: var(--base-font-size-px);
@@ -413,7 +413,7 @@
font-variant: inherit;
padding: 0;
.stream-list-toggle-inactive-channels-text {
.stream-list-toggle-inactive-or-muted-channels-text {
font-size: var(--font-size-sidebar-action-heading);
font-variant: var(--font-variant-sidebar-action-heading);
overflow-x: hidden;
@@ -422,39 +422,39 @@
}
}
.stream-list-section-container:not(.showing-inactive) {
.inactive-in-channel-folder {
.stream-list-section-container:not(.showing-inactive-or-muted) {
.inactive-or-muted-in-channel-folder {
display: none;
}
.stream-list-toggle-inactive-channels {
.hide-inactive-channels {
.stream-list-toggle-inactive-or-muted-channels {
.hide-inactive-or-muted-channels {
display: none;
}
}
}
#streams_list.is_searching {
.show-inactive-channels,
.hide-inactive-channels {
.show-inactive-or-muted-channels,
.hide-inactive-or-muted-channels {
display: none;
}
.inactive-in-channel-folder {
.inactive-or-muted-in-channel-folder {
display: block;
}
}
.stream-list-section-container.showing-inactive {
.stream-list-toggle-inactive-channels {
.show-inactive-channels {
.stream-list-section-container.showing-inactive-or-muted {
.stream-list-toggle-inactive-or-muted-channels {
.show-inactive-or-muted-channels {
display: none;
}
}
}
.show-inactive-channels,
.hide-inactive-channels {
.show-inactive-or-muted-channels,
.hide-inactive-or-muted-channels {
display: grid;
grid-template:
"content markers-and-unreads three-dot-placeholder" auto
@@ -476,7 +476,7 @@
.stream-list-section-container.collapsed {
.narrow-filter:not(.stream-expanded),
.stream-list-toggle-inactive-channels,
.stream-list-toggle-inactive-or-muted-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 {
@@ -635,7 +635,7 @@
}
}
.stream-list-toggle-inactive-channels.highlighted_row {
.stream-list-toggle-inactive-or-muted-channels.highlighted_row {
outline: 2px solid var(--color-outline-focus);
outline-offset: -2px;
background-color: var(--color-background-sidebar-action-heading-hover);

View File

@@ -1,20 +0,0 @@
<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>

View File

@@ -0,0 +1,18 @@
<div class="stream-list-toggle-inactive-or-muted-channels bottom_left_row zoom-in-hide">
<div class="show-inactive-or-muted-channels sidebar-topic-action-heading">
<div class="stream-list-toggle-inactive-or-muted-channels-text">
{{#tr}}+ {inactive_or_muted_count} inactive or muted{{/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-or-muted-channels sidebar-topic-action-heading">
<div class="stream-list-toggle-inactive-or-muted-channels-text">
{{#tr}}- {inactive_or_muted_count} inactive or muted{{/tr}}
</div>
</div>
</div>