From 683eca97a715c25d6f302f5418abb6a7adfe3855 Mon Sep 17 00:00:00 2001 From: Sahil Batra Date: Wed, 3 Sep 2025 12:00:17 +0530 Subject: [PATCH] stream-settings: Replace "Not subscribed" tab with "Available". This commit replaces "Not subscribed" tab in stream settings with "Available" tab where only streams which the user can subscribe to are shown. Fixes #35919. --- .../src/components/NavigationSteps.astro | 6 +-- .../src/content/docs/channel-feed.mdx | 2 +- .../content/docs/introduction-to-channels.mdx | 2 +- web/e2e-tests/lib/common.ts | 2 +- web/src/hash_util.ts | 2 +- web/src/stream_edit.ts | 4 +- web/src/stream_settings_ui.ts | 44 ++++++++++--------- web/src/stream_ui_updates.ts | 20 +++++---- web/src/tippyjs.ts | 2 +- .../left_sidebar_stream_setting_popover.hbs | 2 +- .../stream_settings_overlay.hbs | 2 +- web/templates/subscribe_to_more_streams.hbs | 4 +- web/tests/stream_settings_ui.test.cjs | 40 ++++++----------- 13 files changed, 63 insertions(+), 69 deletions(-) diff --git a/starlight_help/src/components/NavigationSteps.astro b/starlight_help/src/components/NavigationSteps.astro index faf25b2f01..86c6e11a3e 100644 --- a/starlight_help/src/components/NavigationSteps.astro +++ b/starlight_help/src/components/NavigationSteps.astro @@ -280,9 +280,9 @@ const relative_link_mapping: Record< label: "All", relative_link: "/#channels/all", }, - "not-subscribed": { - label: "Not subscribed", - relative_link: "/#channels/notsubscribed", + available: { + label: "Available", + relative_link: "/#channels/available", }, }, template: ` diff --git a/starlight_help/src/content/docs/channel-feed.mdx b/starlight_help/src/content/docs/channel-feed.mdx index 468fdcf4d3..f37dab92a3 100644 --- a/starlight_help/src/content/docs/channel-feed.mdx +++ b/starlight_help/src/content/docs/channel-feed.mdx @@ -58,7 +58,7 @@ quick overview of recent messages in a channel. - + 1. Select a channel. 1. Click the channel name in the top bar. diff --git a/starlight_help/src/content/docs/introduction-to-channels.mdx b/starlight_help/src/content/docs/introduction-to-channels.mdx index 9fc4f3cd5e..dbf52b5a78 100644 --- a/starlight_help/src/content/docs/introduction-to-channels.mdx +++ b/starlight_help/src/content/docs/introduction-to-channels.mdx @@ -27,7 +27,7 @@ subscribe to [private](/help/channel-permissions#private-channels) channels. - + 1. Scroll through the list of channels. You can use the **search box** near the top of the menu to filter the list by channel name or description. diff --git a/web/e2e-tests/lib/common.ts b/web/e2e-tests/lib/common.ts index 42bbab85bc..6c65611e3c 100644 --- a/web/e2e-tests/lib/common.ts +++ b/web/e2e-tests/lib/common.ts @@ -560,7 +560,7 @@ export async function open_streams_modal(page: Page): Promise { await page.waitForSelector("#subscription_overlay", {visible: true}); const url = await page_url_with_fragment(page); - assert.ok(url.includes("#channels/notsubscribed")); + assert.ok(url.includes("#channels/available")); } export async function open_personal_menu(page: Page): Promise { diff --git a/web/src/hash_util.ts b/web/src/hash_util.ts index d2fdc5e1a2..05932e8395 100644 --- a/web/src/hash_util.ts +++ b/web/src/hash_util.ts @@ -253,7 +253,7 @@ export function channels_settings_edit_url( } export function channels_settings_section_url(section = "subscribed"): string { - const valid_section_values = new Set(["new", "subscribed", "all", "notsubscribed"]); + const valid_section_values = new Set(["new", "subscribed", "all", "available"]); if (!valid_section_values.has(section)) { blueslip.warn("invalid section for channels settings: " + section); return "#channels/subscribed"; diff --git a/web/src/stream_edit.ts b/web/src/stream_edit.ts index fda7b4f5c2..04fc1c652e 100644 --- a/web/src/stream_edit.ts +++ b/web/src/stream_edit.ts @@ -89,8 +89,8 @@ export function setup_subscriptions_tab_hash(tab_key_value: string): void { browser_history.update("#channels/subscribed"); break; } - case "not-subscribed": { - browser_history.update("#channels/notsubscribed"); + case "available": { + browser_history.update("#channels/available"); break; } default: { diff --git a/web/src/stream_settings_ui.ts b/web/src/stream_settings_ui.ts index 5c495aa670..4a6f0f16ae 100644 --- a/web/src/stream_settings_ui.ts +++ b/web/src/stream_settings_ui.ts @@ -539,8 +539,12 @@ function triage_stream(left_panel_params: LeftPanelParams, sub: StreamSubscripti return "rejected"; } - if (left_panel_params.show_not_subscribed && sub.subscribed) { - // reject subscribed streams + if ( + left_panel_params.show_available && + (sub.subscribed || !stream_data.can_toggle_subscription(sub)) + ) { + // reject subscribed streams and unsubscribed streams + // that user does not have permission to subscribe to. return "rejected"; } @@ -627,7 +631,7 @@ export function update_empty_left_panel_message(): void { has_streams = stream_data.subscribed_subs().length > 0 || $("#channels_overlay_container .stream-row:not(.notdisplayed)").length > 0; - } else if (stream_ui_updates.is_not_subscribed_stream_tab_active()) { + } else if (stream_ui_updates.is_available_stream_tab_active()) { has_streams = stream_data.unsubscribed_subs().length > 0 || $("#channels_overlay_container .stream-row:not(.notdisplayed)").length > 0; @@ -657,8 +661,8 @@ export function update_empty_left_panel_message(): void { $(".no-streams-to-show").children().hide(); if (stream_ui_updates.is_subscribed_stream_tab_active()) { $(".subscribed_streams_tab_empty_text").show(); - } else if (stream_ui_updates.is_not_subscribed_stream_tab_active()) { - $(".not_subscribed_streams_tab_empty_text").show(); + } else if (stream_ui_updates.is_available_stream_tab_active()) { + $(".available_streams_tab_empty_text").show(); } else { $(".all_streams_tab_empty_text").show(); } @@ -729,7 +733,7 @@ let sort_order = "by-stream-name"; type LeftPanelParams = { input: string; show_subscribed: boolean; - show_not_subscribed: boolean; + show_available: boolean; sort_order: string; }; @@ -739,7 +743,7 @@ export function get_left_panel_params(): LeftPanelParams { return { input, show_subscribed: stream_ui_updates.show_subscribed, - show_not_subscribed: stream_ui_updates.show_not_subscribed, + show_available: stream_ui_updates.show_available, sort_order, }; } @@ -757,17 +761,17 @@ export function switch_stream_tab(tab_name: string): void { switch (tab_name) { case "all-streams": { stream_ui_updates.set_show_subscribed(false); - stream_ui_updates.set_show_not_subscribed(false); + stream_ui_updates.set_show_available(false); break; } case "subscribed": { stream_ui_updates.set_show_subscribed(true); - stream_ui_updates.set_show_not_subscribed(false); + stream_ui_updates.set_show_available(false); break; } - case "not-subscribed": { + case "available": { stream_ui_updates.set_show_subscribed(false); - stream_ui_updates.set_show_not_subscribed(true); + stream_ui_updates.set_show_available(true); break; } // No default @@ -894,12 +898,12 @@ function setup_page(callback: () => void): void { // Reset our internal state to reflect that we're initially in // the "Subscribed" tab if we're reopening "Stream settings". stream_ui_updates.set_show_subscribed(true); - stream_ui_updates.set_show_not_subscribed(false); + stream_ui_updates.set_show_available(false); toggler = components.toggle({ child_wants_focus: true, values: [ {label: $t({defaultMessage: "Subscribed"}), key: "subscribed"}, - {label: $t({defaultMessage: "Not subscribed"}), key: "not-subscribed"}, + {label: $t({defaultMessage: "Available"}), key: "available"}, {label: $t({defaultMessage: "All"}), key: "all-streams"}, ], callback(_value, key) { @@ -913,7 +917,7 @@ function setup_page(callback: () => void): void { } if (current_user.is_guest) { toggler.disable_tab("all-streams"); - toggler.disable_tab("not-subscribed"); + toggler.disable_tab("available"); } // show the "Stream settings" header by default. @@ -1061,8 +1065,8 @@ export function change_state( return; } - if (section === "notsubscribed") { - toggler.goto("not-subscribed"); + if (section === "available") { + toggler.goto("available"); stream_edit.empty_right_panel(); return; } @@ -1193,20 +1197,20 @@ export function toggle_view(event: string): void { case "right_arrow": switch (stream_filter_tab_key) { case "subscribed": - toggler.goto("not-subscribed"); + toggler.goto("available"); break; - case "not-subscribed": + case "available": toggler.goto("all-streams"); break; } break; case "left_arrow": switch (stream_filter_tab_key) { - case "not-subscribed": + case "available": toggler.goto("subscribed"); break; case "all-streams": - toggler.goto("not-subscribed"); + toggler.goto("available"); break; } break; diff --git a/web/src/stream_ui_updates.ts b/web/src/stream_ui_updates.ts index aa87e6b18a..5f97c3ecd2 100644 --- a/web/src/stream_ui_updates.ts +++ b/web/src/stream_ui_updates.ts @@ -39,7 +39,7 @@ function settings_button_for_sub(sub: StreamSubscription): JQuery { } export let show_subscribed = true; -export let show_not_subscribed = false; +export let show_available = false; export function is_subscribed_stream_tab_active(): boolean { // Returns true if "Subscribed" tab in stream settings is open @@ -47,18 +47,18 @@ export function is_subscribed_stream_tab_active(): boolean { return show_subscribed; } -export function is_not_subscribed_stream_tab_active(): boolean { - // Returns true if "not-subscribed" tab in stream settings is open +export function is_available_stream_tab_active(): boolean { + // Returns true if "available" tab in stream settings is open // otherwise false. - return show_not_subscribed; + return show_available; } export function set_show_subscribed(value: boolean): void { show_subscribed = value; } -export function set_show_not_subscribed(value: boolean): void { - show_not_subscribed = value; +export function set_show_available(value: boolean): void { + show_available = value; } export function update_web_public_stream_privacy_option_state($container: JQuery): void { @@ -483,15 +483,17 @@ export function update_stream_row_in_settings_tab(sub: StreamSubscription): void // This function display/hide stream row in stream settings tab, // used to display immediate effect of add/removal subscription event. // If user is subscribed or unsubscribed to stream, it will show sub or unsub - // row under "Subscribed" or "Not subscribed" (only if the stream is public) tab, otherwise + // row under "Subscribed" or "Available" (only if the stream is public) tab, otherwise // if stream is not public hide stream row under tab. - if (is_subscribed_stream_tab_active() || is_not_subscribed_stream_tab_active()) { + if (is_subscribed_stream_tab_active() || is_available_stream_tab_active()) { const $row = row_for_stream_id(sub.stream_id); if ( (is_subscribed_stream_tab_active() && sub.subscribed) || - (is_not_subscribed_stream_tab_active() && !sub.subscribed) + (is_available_stream_tab_active() && + !sub.subscribed && + stream_data.can_toggle_subscription(sub)) ) { if (stream_settings_components.filter_includes_channel(sub)) { $row.removeClass("notdisplayed"); diff --git a/web/src/tippyjs.ts b/web/src/tippyjs.ts index e0133c1ca1..3b47dc5c7f 100644 --- a/web/src/tippyjs.ts +++ b/web/src/tippyjs.ts @@ -443,7 +443,7 @@ export function initialize(): void { tippy.delegate("body", { target: [ - "[data-tab-key='not-subscribed'].disabled", + "[data-tab-key='available'].disabled", "[data-tab-key='all-streams'].disabled", ].join(","), content: $t({ diff --git a/web/templates/popovers/left_sidebar/left_sidebar_stream_setting_popover.hbs b/web/templates/popovers/left_sidebar/left_sidebar_stream_setting_popover.hbs index ab491dfa46..bc33cac28a 100644 --- a/web/templates/popovers/left_sidebar/left_sidebar_stream_setting_popover.hbs +++ b/web/templates/popovers/left_sidebar/left_sidebar_stream_setting_popover.hbs @@ -1,7 +1,7 @@
-
+
{{t 'No channels to show.'}} {{t 'View all channels'}} diff --git a/web/templates/subscribe_to_more_streams.hbs b/web/templates/subscribe_to_more_streams.hbs index d3027ea365..532f18beac 100644 --- a/web/templates/subscribe_to_more_streams.hbs +++ b/web/templates/subscribe_to_more_streams.hbs @@ -1,10 +1,10 @@ {{#if exactly_one_unsubscribed_stream}} - {{else if can_subscribe_stream_count}} - diff --git a/web/tests/stream_settings_ui.test.cjs b/web/tests/stream_settings_ui.test.cjs index 95cf5c1d99..91925ba363 100644 --- a/web/tests/stream_settings_ui.test.cjs +++ b/web/tests/stream_settings_ui.test.cjs @@ -263,58 +263,46 @@ run_test("redraw_left_panel", ({override, mock_template}) => { } // Search with single keyword - test_filter({input: "Po", show_subscribed: false, show_not_subscribed: false}, [ - poland, - pomona, - ]); + test_filter({input: "Po", show_subscribed: false, show_available: false}, [poland, pomona]); assert.ok(ui_called); // The denmark row is active, even though it's not displayed. assert.ok($denmark_row.hasClass("active")); // Search with multiple keywords - test_filter({input: "Denmark, Pol", show_subscribed: false, show_not_subscribed: false}, [ + test_filter({input: "Denmark, Pol", show_subscribed: false, show_available: false}, [ denmark, poland, ]); - test_filter({input: "Den, Pol", show_subscribed: false, show_not_subscribed: false}, [ + test_filter({input: "Den, Pol", show_subscribed: false, show_available: false}, [ denmark, poland, ]); // Search is case-insensitive - test_filter({input: "po", show_subscribed: false, show_not_subscribed: false}, [ - poland, - pomona, - ]); + test_filter({input: "po", show_subscribed: false, show_available: false}, [poland, pomona]); // Search handles unusual characters like C++ - test_filter({input: "c++", show_subscribed: false, show_not_subscribed: false}, [cpp]); + test_filter({input: "c++", show_subscribed: false, show_available: false}, [cpp]); // Search subscribed streams only - test_filter({input: "d", show_subscribed: true, show_not_subscribed: false}, [poland]); + test_filter({input: "d", show_subscribed: true, show_available: false}, [poland]); // Search unsubscribed streams only - test_filter({input: "d", show_subscribed: false, show_not_subscribed: true}, [abcd, denmark]); + test_filter({input: "d", show_subscribed: false, show_available: true}, [abcd, denmark]); // Search terms match stream description - test_filter({input: "Co", show_subscribed: false, show_not_subscribed: false}, [ - denmark, - pomona, - ]); + test_filter({input: "Co", show_subscribed: false, show_available: false}, [denmark, pomona]); // Search names AND descriptions - test_filter({input: "Mon", show_subscribed: false, show_not_subscribed: false}, [ - pomona, - poland, - ]); + test_filter({input: "Mon", show_subscribed: false, show_available: false}, [pomona, poland]); // Explicitly order streams by name test_filter( { input: "", show_subscribed: false, - show_not_subscribed: false, + show_available: false, sort_order: "by-stream-name", }, [abcd, cpp, denmark, jerry, poland, pomona, utopia, zzyzx], @@ -325,7 +313,7 @@ run_test("redraw_left_panel", ({override, mock_template}) => { { input: "", show_subscribed: false, - show_not_subscribed: false, + show_available: false, sort_order: "by-subscriber-count", }, [utopia, abcd, poland, cpp, zzyzx, denmark, jerry, pomona], @@ -336,7 +324,7 @@ run_test("redraw_left_panel", ({override, mock_template}) => { { input: "", show_subscribed: false, - show_not_subscribed: false, + show_available: false, sort_order: "by-weekly-traffic", }, [poland, utopia, cpp, zzyzx, jerry, abcd, pomona, denmark], @@ -347,7 +335,7 @@ run_test("redraw_left_panel", ({override, mock_template}) => { { input: "", show_subscribed: true, - show_not_subscribed: false, + show_available: false, sort_order: "by-subscriber-count", }, [poland, cpp, zzyzx, pomona], @@ -358,7 +346,7 @@ run_test("redraw_left_panel", ({override, mock_template}) => { { input: "", show_subscribed: false, - show_not_subscribed: true, + show_available: true, sort_order: "by-subscriber-count", }, [utopia, abcd, denmark, jerry],