stream_settings: Display archived channels.

By default, archived channels will be hidden.
This commit is contained in:
sanchi-t
2025-02-01 11:17:23 +05:30
committed by Tim Abbott
parent f9babb37a2
commit 1aef79078c
9 changed files with 148 additions and 20 deletions

View File

@@ -256,18 +256,13 @@ export function validate_channels_settings_hash(hash: string): string {
const stream_id = Number.parseInt(section, 10);
const sub = sub_store.get(stream_id);
// There are a few situations where we can't display stream settings:
// 1. This is a stream that's been archived. (sub.is_archived=true)
// 2. The stream ID is invalid. (sub=undefined)
// 3. The current user is a guest, and was unsubscribed from the stream
// 1. The stream ID is invalid. (sub=undefined)
// 2. The current user is a guest, and was unsubscribed from the stream
// stream in the current session. (In future sessions, the stream will
// not be in sub_store).
//
// In all these cases we redirect the user to 'subscribed' tab.
if (
sub === undefined ||
sub.is_archived ||
(page_params.is_guest && !stream_data.is_subscribed(stream_id))
) {
// In both cases we redirect the user to 'subscribed' tab.
if (sub === undefined || (page_params.is_guest && !stream_data.is_subscribed(stream_id))) {
return channels_settings_section_url();
}

View File

@@ -355,6 +355,10 @@ export function subscribed_stream_ids(): number[] {
return subscribed_subs().map((sub) => sub.stream_id);
}
export function get_archived_subs(): StreamSubscription[] {
return [...stream_info.values()].filter((sub) => sub.is_archived);
}
export function muted_stream_ids(): number[] {
return subscribed_subs()
.filter((sub) => sub.is_muted)

View File

@@ -28,6 +28,12 @@ export type SettingsSubscription = StreamSubscription & {
subscriber_count: number;
};
export const FILTERS = {
ALL_CHANNELS: "all_channels",
NON_ARCHIVED_CHANNELS: "non_archived_channels",
ARCHIVED_CHANNELS: "archived_channels",
};
export function get_sub_for_settings(sub: StreamSubscription): SettingsSubscription {
return {
...sub,
@@ -66,7 +72,7 @@ function get_subs_for_settings(subs: StreamSubscription[]): SettingsSubscription
// delegating, so that we can more efficiently compute subscriber counts
// (in bulk). If that plan appears to have been aborted, feel free to
// inline this.
return subs.filter((sub) => !sub.is_archived).map((sub) => get_sub_for_settings(sub));
return subs.map((sub) => get_sub_for_settings(sub));
}
export function get_updated_unsorted_subs(): SettingsSubscription[] {

View File

@@ -1,6 +1,7 @@
import $ from "jquery";
import _ from "lodash";
import assert from "minimalistic-assert";
import type * as tippy from "tippy.js";
import render_stream_creation_confirmation_banner from "../templates/modal_banner/stream_creation_confirmation_banner.hbs";
import render_stream_info_banner from "../templates/modal_banner/stream_info_banner.hbs";
@@ -15,6 +16,7 @@ import type {Toggle} from "./components.ts";
import * as compose_banner from "./compose_banner.ts";
import * as compose_recipient from "./compose_recipient.ts";
import * as compose_state from "./compose_state.ts";
import * as dropdown_widget from "./dropdown_widget.ts";
import * as hash_parser from "./hash_parser.ts";
import * as hash_util from "./hash_util.ts";
import {$t} from "./i18n.ts";
@@ -46,6 +48,10 @@ import * as stream_ui_updates from "./stream_ui_updates.ts";
import * as sub_store from "./sub_store.ts";
import type {StreamSubscription} from "./sub_store.ts";
import * as util from "./util.ts";
import * as views_util from "./views_util.ts";
let archived_status_dropdown_filter: string;
let filters_dropdown_widget: dropdown_widget.DropdownWidget;
export function is_sub_already_present(sub: StreamSubscription): boolean {
return stream_ui_updates.row_for_stream_id(sub.stream_id).length > 0;
@@ -384,6 +390,20 @@ export function update_settings_for_unsubscribed(slim_sub: StreamSubscription):
}
function triage_stream(left_panel_params: LeftPanelParams, sub: StreamSubscription): string {
const current_channel_visibility_filter = archived_status_dropdown_filter;
const channel_visibility_filters = stream_settings_data.FILTERS;
if (
current_channel_visibility_filter === channel_visibility_filters.NON_ARCHIVED_CHANNELS &&
sub.is_archived
) {
return "rejected";
}
if (
current_channel_visibility_filter === channel_visibility_filters.ARCHIVED_CHANNELS &&
!sub.is_archived
) {
return "rejected";
}
if (left_panel_params.show_subscribed && !sub.subscribed) {
// reject non-subscribed streams
return "rejected";
@@ -485,12 +505,15 @@ export function update_empty_left_panel_message(): void {
has_streams = stream_data.get_unsorted_subs().length;
}
const has_hidden_streams =
const all_channels_hidden =
$("#channels_overlay_container .stream-row:not(.notdisplayed)").length === 0;
const has_search_query =
$<HTMLInputElement>("#stream_filter input[type='text']").val()!.trim() !== "";
// Show "no channels match" text if all channels are hidden and there's a search query.
if (has_hidden_streams && has_search_query) {
const has_filter =
archived_status_dropdown_filter !== stream_settings_data.FILTERS.ALL_CHANNELS;
// Both search queries and filters can lead to all channels being hidden.
if (all_channels_hidden && (has_search_query || (has_filter && has_streams))) {
$(".no-streams-to-show").children().hide();
$(".no_stream_match_filter_empty_text").show();
$(".no-streams-to-show").show();
@@ -639,6 +662,66 @@ export function switch_stream_sort(tab_name: string): void {
redraw_left_panel();
}
function filters_dropdown_options(current_value: string | number | undefined): {
unique_id: string;
name: string;
bold_current_selection: boolean;
}[] {
return [
{
unique_id: stream_settings_data.FILTERS.ARCHIVED_CHANNELS,
name: $t({defaultMessage: "Archived channels"}),
bold_current_selection:
current_value === stream_settings_data.FILTERS.ARCHIVED_CHANNELS,
},
{
unique_id: stream_settings_data.FILTERS.NON_ARCHIVED_CHANNELS,
name: $t({defaultMessage: "Non-archived channels"}),
bold_current_selection:
current_value === stream_settings_data.FILTERS.NON_ARCHIVED_CHANNELS,
},
{
unique_id: stream_settings_data.FILTERS.ALL_CHANNELS,
name: $t({defaultMessage: "Archived and non-archived"}),
bold_current_selection: current_value === stream_settings_data.FILTERS.ALL_CHANNELS,
},
];
}
export function set_filters_for_tests(filter_widget: dropdown_widget.DropdownWidget): void {
filters_dropdown_widget = filter_widget;
}
function filter_click_handler(
event: JQuery.TriggeredEvent,
dropdown: tippy.Instance,
widget: dropdown_widget.DropdownWidget,
): void {
event.preventDefault();
event.stopPropagation();
const filter_id = $(event.currentTarget).attr("data-unique-id");
assert(filter_id !== undefined);
// We don't support multiple filters, so we clear existing and add the new filter.
archived_status_dropdown_filter = filter_id;
redraw_left_panel();
dropdown.hide();
widget.render();
}
function set_up_dropdown_widget(): void {
archived_status_dropdown_filter = stream_settings_data.FILTERS.NON_ARCHIVED_CHANNELS;
filters_dropdown_widget = new dropdown_widget.DropdownWidget({
...views_util.COMMON_DROPDOWN_WIDGET_PARAMS,
get_options: filters_dropdown_options,
widget_name: "stream_settings_filter",
item_click_callback: filter_click_handler,
$events_container: $("#stream_filter"),
default_id: archived_status_dropdown_filter,
});
filters_dropdown_widget.setup();
}
function setup_page(callback: () => void): void {
// We should strongly consider only setting up the page once,
// but I am writing these comments write before a big release,
@@ -723,6 +806,7 @@ function setup_page(callback: () => void): void {
const new_stream_announcements_stream_sub = stream_data.get_sub_by_name(
new_stream_announcements_stream,
);
const realm_has_archived_channels = stream_data.get_archived_subs().length > 0;
const template_data = {
new_stream_announcements_stream_sub,
@@ -747,6 +831,7 @@ function setup_page(callback: () => void): void {
disable_message_retention_setting:
!realm.zulip_plan_is_not_limited || !current_user.is_owner,
group_setting_labels: settings_config.all_group_setting_labels.stream,
realm_has_archived_channels,
};
const rendered = render_stream_settings_overlay(template_data);
@@ -756,6 +841,7 @@ function setup_page(callback: () => void): void {
initialize_components();
redraw_left_panel();
stream_create.set_up_handlers();
set_up_dropdown_widget();
const throttled_redraw_left_panel = _.throttle(redraw_left_panel, 50);
$("#stream_filter input[type='text']").on("input", () => {
@@ -876,6 +962,23 @@ export function change_state(
toggler.goto(left_side_tab);
}
switch_to_stream_row(stream_id);
const sub = stream_data.get_sub_by_id(stream_id);
if (sub) {
const FILTERS = stream_settings_data.FILTERS;
const should_update_filter =
(archived_status_dropdown_filter === FILTERS.NON_ARCHIVED_CHANNELS &&
sub.is_archived) ||
(archived_status_dropdown_filter === FILTERS.ARCHIVED_CHANNELS && !sub.is_archived);
if (should_update_filter) {
if (sub.is_archived) {
archived_status_dropdown_filter = FILTERS.ARCHIVED_CHANNELS;
} else {
archived_status_dropdown_filter = FILTERS.NON_ARCHIVED_CHANNELS;
}
filters_dropdown_widget.render(archived_status_dropdown_filter);
}
}
return;
}

View File

@@ -642,11 +642,21 @@ h4.user_group_setting_subsection_title {
padding: 8px 10px;
display: grid;
grid-template:
"search-input clear-search more-options-button" auto / minmax(0, 1fr)
"search-input clear-search dropdown-widget" auto / minmax(0, 1fr)
30px;
border-bottom: 1px solid var(--color-border-modal-bar);
}
#stream_settings_filter_widget {
margin-left: 10px;
gap: 3px;
width: auto;
}
.stream_settings_filter_container.hide_filter {
display: none;
}
#user_group_visibility_settings_widget {
grid-area: more-options-button;
margin-left: 10px;

View File

@@ -1,5 +1,4 @@
{{#if stream_settings_link}}
{{#unless stream.is_archived}}
<a class="message-header-stream-settings-button tippy-zulip-tooltip" data-tooltip-template-id="stream-details-tooltip-template" data-tippy-placement="bottom" href="{{stream_settings_link}}">
{{> navbar_icon_and_title . }}
</a>
@@ -13,11 +12,6 @@
{{/unless}}
</div>
</template>
{{else}}
<span class="navbar_title">
{{> navbar_icon_and_title . }}
</span>
{{/unless}}
<span class="narrow_description rendered_markdown single-line-rendered-markdown">
{{#if rendered_narrow_description}}
{{rendered_markdown rendered_narrow_description}}

View File

@@ -25,6 +25,9 @@
<button type="button" class="clear_search_button" id="clear_search_stream_name">
<i class="zulip-icon zulip-icon-close" aria-hidden="true"></i>
</button>
<div class="stream_settings_filter_container {{#unless realm_has_archived_channels}}hide_filter{{/unless}}">
{{> ../dropdown_widget widget_name="stream_settings_filter"}}
</div>
</div>
<div class="no-streams-to-show">
<div class="subscribed_streams_tab_empty_text">

View File

@@ -716,6 +716,7 @@ test("delete_sub", () => {
stream_data.add_sub(canada);
const num_subscribed_subs = stream_data.num_subscribed_subs();
const archived_subs = stream_data.get_archived_subs();
assert.ok(stream_data.is_subscribed(canada.stream_id));
assert.equal(stream_data.get_sub("Canada").stream_id, canada.stream_id);
@@ -728,6 +729,7 @@ test("delete_sub", () => {
assert.ok(stream_data.get_sub("Canada"));
assert.ok(sub_store.get(canada.stream_id));
assert.equal(stream_data.num_subscribed_subs(), num_subscribed_subs);
assert.equal(stream_data.get_archived_subs().length, archived_subs.length + 1);
blueslip.expect("warn", "Failed to archive stream 99999");
stream_data.delete_sub(99999);

View File

@@ -209,6 +209,11 @@ run_test("redraw_left_panel", ({override, mock_template}) => {
populated_subs = data.subscriptions;
});
const filters_dropdown_widget = {
render: function render() {},
};
stream_settings_ui.set_filters_for_tests(filters_dropdown_widget);
stream_settings_ui.render_left_panel_superset();
const sub_stubs = [];
@@ -223,6 +228,11 @@ run_test("redraw_left_panel", ({override, mock_template}) => {
$.create("#channels_overlay_container .stream-row", {children: sub_stubs});
const $no_streams_message = $(".no-streams-to-show");
const $child_element = $(".subscribed_streams_tab_empty_text");
$no_streams_message.children = () => $child_element;
$child_element.hide = () => [];
let ui_called = false;
scroll_util.reset_scrollbar = ($elem) => {
ui_called = true;
@@ -360,6 +370,7 @@ run_test("redraw_left_panel", ({override, mock_template}) => {
test_filter({input: "d", show_subscribed: true}, [poland]);
assert.ok($(".stream-row-denmark").hasClass("active"));
$(".stream-row.active").attr("data-stream-id", 101);
stream_settings_ui.switch_stream_tab("subscribed");
assert.ok(!$(".stream-row-denmark").hasClass("active"));
assert.ok(!$(".right .settings").visible());