diff --git a/web/src/hash_util.ts b/web/src/hash_util.ts index 5106b693ec..db8b8de0de 100644 --- a/web/src/hash_util.ts +++ b/web/src/hash_util.ts @@ -1,7 +1,10 @@ import * as internal_url from "../shared/src/internal_url"; +import * as blueslip from "./blueslip"; import type {Message} from "./message_store"; +import {page_params} from "./page_params"; import * as people from "./people"; +import * as settings_data from "./settings_data"; import * as stream_data from "./stream_data"; import * as sub_store from "./sub_store"; import type {StreamSubscription} from "./sub_store"; @@ -188,3 +191,50 @@ export function parse_narrow(hash: string): Term[] | undefined { } return terms; } + +export function validate_stream_settings_hash(hash: string): string { + const hash_components = hash.slice(1).split(/\//); + const section = hash_components[1]; + + const can_create_streams = + settings_data.user_can_create_public_streams() || + settings_data.user_can_create_web_public_streams() || + settings_data.user_can_create_private_streams(); + if (section === "new" && !can_create_streams) { + return "#streams/subscribed"; + } + + if (/\d+/.test(section)) { + 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=undefined) + // 2. The stream ID is invalid. (sub=undefined) + // 3. 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 || (page_params.is_guest && !stream_data.is_subscribed(stream_id))) { + return "#streams/subscribed"; + } + + const stream_name = hash_components[2]; + let right_side_tab = hash_components[3]; + const valid_right_side_tab_values = new Set(["general", "personal", "subscribers"]); + if (sub.name === stream_name && valid_right_side_tab_values.has(right_side_tab)) { + return hash; + } + if (!valid_right_side_tab_values.has(right_side_tab)) { + right_side_tab = "general"; + } + return stream_edit_url(sub, right_side_tab); + } + + const valid_section_values = ["new", "subscribed", "all"]; + if (!valid_section_values.includes(section)) { + blueslip.warn("invalid section for streams: " + section); + return "#streams/subscribed"; + } + return hash; +} diff --git a/web/src/hashchange.js b/web/src/hashchange.js index f0b5d14e1d..d53f6df335 100644 --- a/web/src/hashchange.js +++ b/web/src/hashchange.js @@ -240,8 +240,12 @@ function do_hashchange_overlay(old_hash) { ); } - if (base === "streams" && !section) { - history.replaceState(null, "", browser_history.get_full_url("streams/subscribed")); + if (base === "streams") { + const valid_hash = hash_util.validate_stream_settings_hash(window.location.hash); + if (valid_hash !== window.location.hash) { + history.replaceState(null, "", browser_history.get_full_url(valid_hash)); + section = hash_parser.get_current_hash_section(); + } } if (base === "groups" && !section) { diff --git a/web/src/stream_settings_ui.js b/web/src/stream_settings_ui.js index 990daf264b..ee5bdcb7fa 100644 --- a/web/src/stream_settings_ui.js +++ b/web/src/stream_settings_ui.js @@ -702,17 +702,8 @@ function show_right_section() { export function change_state(section, right_side_tab) { // if in #streams/new form. if (section === "new") { - const can_create_streams = - settings_data.user_can_create_private_streams() || - settings_data.user_can_create_public_streams() || - settings_data.user_can_create_web_public_streams(); - if (can_create_streams) { - do_open_create_stream(); - show_right_section(); - } else { - toggler.goto("subscribed"); - stream_edit.empty_right_panel(); - } + do_open_create_stream(); + show_right_section(); return; } @@ -722,36 +713,15 @@ export function change_state(section, right_side_tab) { return; } - if (section === "subscribed") { - toggler.goto("subscribed"); - stream_edit.empty_right_panel(); - return; - } - // if the section is a valid number. if (/\d+/.test(section)) { 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=undefined) - // 2. The stream ID is invalid. (sub=undefined) - // 3. 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 || (current_user.is_guest && !stream_data.is_subscribed(stream_id))) { - toggler.goto("subscribed"); - stream_edit.empty_right_panel(); - } else { - show_right_section(); - stream_edit_toggler.set_select_tab(right_side_tab); - switch_to_stream_row(stream_id); - } + show_right_section(); + stream_edit_toggler.set_select_tab(right_side_tab); + switch_to_stream_row(stream_id); return; } - blueslip.warn("invalid section for streams: " + section); toggler.goto("subscribed"); stream_edit.empty_right_panel(); } diff --git a/web/tests/hashchange.test.js b/web/tests/hashchange.test.js index c64dac4916..d71ae08c4f 100644 --- a/web/tests/hashchange.test.js +++ b/web/tests/hashchange.test.js @@ -260,7 +260,7 @@ run_test("hash_interactions", ({override, override_rewire}) => { [ui_report, "error"], ]); - window.location.hash = "#streams/whatever"; + window.location.hash = "#streams/subscribed"; helper.clear_events(); $window_stub.trigger("hashchange");