stream-settings: Handle invalid urls correctly.

There were couple of problems in our handling of invalid or
incomplete URLs-
- The browser back button behavior breaks if someone enters
url with invalid or inaccessible stream ID, incorrect stream
name, "#streams/new" url without permission to create streams.
- On typing stream edit URLs with incorrect right side tab or
without any right side tab, we showed "general" section, which
is fine, but the URL was still incorrect.

This commit fixes the above mentioned problems-
- We now update the right side tab to "general" if right
side tab is invalid or there is no right side tab.
- All the URL updates to fix invalid urls are done using
"history.replaceState" to make sure the browser back button
behaves as expected.
- All the code for checking the urls is done in hashchange.js
itself, so we can remove some code from change_state.
This commit is contained in:
Sahil Batra 2024-02-01 14:56:22 +05:30 committed by Tim Abbott
parent 7da8f9650c
commit eea5ee8923
4 changed files with 62 additions and 38 deletions

View File

@ -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;
}

View File

@ -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) {

View File

@ -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();
}

View File

@ -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");