narrow: Cut import of hashchange.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2023-10-06 16:08:53 -07:00 committed by Tim Abbott
parent 4adfd57378
commit 564e91f3a8
7 changed files with 95 additions and 88 deletions

View File

@ -61,6 +61,7 @@ EXEMPT_FILES = make_set(
"web/src/billing/upgrade.ts", "web/src/billing/upgrade.ts",
"web/src/blueslip.ts", "web/src/blueslip.ts",
"web/src/blueslip_stacktrace.ts", "web/src/blueslip_stacktrace.ts",
"web/src/browser_history.ts",
"web/src/click_handlers.js", "web/src/click_handlers.js",
"web/src/compose.js", "web/src/compose.js",
"web/src/compose_actions.js", "web/src/compose_actions.js",

View File

@ -102,3 +102,54 @@ export function update_hash_internally_if_required(hash: string): void {
export function return_to_web_public_hash(): void { export function return_to_web_public_hash(): void {
window.location.hash = state.spectator_old_hash ?? `#${user_settings.default_view}`; window.location.hash = state.spectator_old_hash ?? `#${user_settings.default_view}`;
} }
export function get_full_url(hash: string): string {
const location = window.location;
if (hash.charAt(0) !== "#" && hash !== "") {
hash = "#" + hash;
}
// IE returns pathname as undefined and missing the leading /
let pathname = location.pathname;
if (pathname === undefined) {
pathname = "/";
} else if (pathname === "" || pathname.charAt(0) !== "/") {
pathname = "/" + pathname;
}
// Build a full URL to not have same origin problems
const url = location.protocol + "//" + location.host + pathname + hash;
return url;
}
export function set_hash(hash: string): void {
if (hash === window.location.hash) {
// Avoid adding duplicate entries in browser history.
return;
}
if (history.pushState) {
const url = get_full_url(hash);
try {
history.pushState(null, "", url);
update_web_public_hash(hash);
} catch (error) {
if (error instanceof TypeError) {
// The window has been destroyed and the history object has been marked dead, so cannot
// be updated. Silently do nothing, since there's nothing we can do.
} else {
throw error;
}
}
} else {
// pushState has 97% global support according to caniuse. So, we will ideally never reach here.
// TODO: Delete this case if we don't see any error reports in a while.
if (hash === "" || hash === "#") {
// Setting empty hash here would scroll to the top.
hash = user_settings.default_view;
}
blueslip.error("browser does not support pushState");
window.location.hash = hash;
}
}

View File

@ -35,57 +35,6 @@ import {user_settings} from "./user_settings";
// Read https://zulip.readthedocs.io/en/latest/subsystems/hashchange-system.html // Read https://zulip.readthedocs.io/en/latest/subsystems/hashchange-system.html
// or locally: docs/subsystems/hashchange-system.md // or locally: docs/subsystems/hashchange-system.md
function get_full_url(hash) {
const location = window.location;
if (hash.charAt(0) !== "#" && hash !== "") {
hash = "#" + hash;
}
// IE returns pathname as undefined and missing the leading /
let pathname = location.pathname;
if (pathname === undefined) {
pathname = "/";
} else if (pathname === "" || pathname.charAt(0) !== "/") {
pathname = "/" + pathname;
}
// Build a full URL to not have same origin problems
const url = location.protocol + "//" + location.host + pathname + hash;
return url;
}
function set_hash(hash) {
if (hash === window.location.hash) {
// Avoid adding duplicate entries in browser history.
return;
}
if (history.pushState) {
const url = get_full_url(hash);
try {
history.pushState(null, null, url);
browser_history.update_web_public_hash(hash);
} catch (error) {
if (error instanceof TypeError) {
// The window has been destroyed and the history object has been marked dead, so cannot
// be updated. Silently do nothing, since there's nothing we can do.
} else {
throw error;
}
}
} else {
// pushState has 97% global support according to caniuse. So, we will ideally never reach here.
// TODO: Delete this case if we don't see any error reports in a while.
if (hash === "" || hash === "#") {
// Setting empty hash here would scroll to the top.
hash = user_settings.default_view;
}
blueslip.error("browser does not support pushState");
window.location.hash = hash;
}
}
function maybe_hide_recent_view() { function maybe_hide_recent_view() {
if (recent_view_util.is_visible()) { if (recent_view_util.is_visible()) {
recent_view_ui.hide(); recent_view_ui.hide();
@ -102,22 +51,6 @@ function maybe_hide_inbox() {
return false; return false;
} }
export function changehash(newhash) {
if (browser_history.state.changing_hash) {
return;
}
message_viewport.stop_auto_scrolling();
set_hash(newhash);
}
export function save_narrow(operators) {
if (browser_history.state.changing_hash) {
return;
}
const new_hash = hash_util.operators_to_hash(operators);
changehash(new_hash);
}
function show_all_message_view() { function show_all_message_view() {
const coming_from_recent_view = maybe_hide_recent_view(); const coming_from_recent_view = maybe_hide_recent_view();
const coming_from_inbox = maybe_hide_inbox(); const coming_from_inbox = maybe_hide_inbox();
@ -140,7 +73,7 @@ export function set_hash_to_default_view() {
// hash. So, we use `pushState` which simply updates the current URL // hash. So, we use `pushState` which simply updates the current URL
// but doesn't trigger `hashchange`. So, we trigger hashchange directly // but doesn't trigger `hashchange`. So, we trigger hashchange directly
// here to let it handle the whole rendering process for us. // here to let it handle the whole rendering process for us.
set_hash(""); browser_history.set_hash("");
hashchanged(false); hashchanged(false);
} }
} }
@ -310,12 +243,12 @@ function do_hashchange_overlay(old_hash) {
history.replaceState( history.replaceState(
null, null,
"", "",
get_full_url(base + "/" + settings_panel_object.current_tab()), browser_history.get_full_url(base + "/" + settings_panel_object.current_tab()),
); );
} }
if (base === "streams" && !section) { if (base === "streams" && !section) {
history.replaceState(null, "", get_full_url("streams/subscribed")); history.replaceState(null, "", browser_history.get_full_url("streams/subscribed"));
} }
// Start by handling the specific case of going // Start by handling the specific case of going

View File

@ -14,7 +14,7 @@ import * as compose_state from "./compose_state";
import * as condense from "./condense"; import * as condense from "./condense";
import {Filter} from "./filter"; import {Filter} from "./filter";
import * as hash_parser from "./hash_parser"; import * as hash_parser from "./hash_parser";
import * as hashchange from "./hashchange"; import * as hash_util from "./hash_util";
import * as inbox_ui from "./inbox_ui"; import * as inbox_ui from "./inbox_ui";
import * as inbox_util from "./inbox_util"; import * as inbox_util from "./inbox_util";
import * as left_sidebar_navigation_area from "./left_sidebar_navigation_area"; import * as left_sidebar_navigation_area from "./left_sidebar_navigation_area";
@ -28,6 +28,7 @@ import {MessageListData} from "./message_list_data";
import * as message_lists from "./message_lists"; import * as message_lists from "./message_lists";
import * as message_store from "./message_store"; import * as message_store from "./message_store";
import * as message_view_header from "./message_view_header"; import * as message_view_header from "./message_view_header";
import * as message_viewport from "./message_viewport";
import * as narrow_banner from "./narrow_banner"; import * as narrow_banner from "./narrow_banner";
import * as narrow_history from "./narrow_history"; import * as narrow_history from "./narrow_history";
import * as narrow_state from "./narrow_state"; import * as narrow_state from "./narrow_state";
@ -90,6 +91,22 @@ export function handle_middle_pane_transition() {
} }
} }
export function changehash(newhash) {
if (browser_history.state.changing_hash) {
return;
}
message_viewport.stop_auto_scrolling();
browser_history.set_hash(newhash);
}
export function save_narrow(operators) {
if (browser_history.state.changing_hash) {
return;
}
const new_hash = hash_util.operators_to_hash(operators);
changehash(new_hash);
}
export function activate(raw_operators, opts) { export function activate(raw_operators, opts) {
/* Main entry point for switching to a new view / message list. /* Main entry point for switching to a new view / message list.
Note that for historical reasons related to the current Note that for historical reasons related to the current
@ -479,7 +496,7 @@ export function activate(raw_operators, opts) {
// Disabled when the URL fragment was the source // Disabled when the URL fragment was the source
// of this narrow. // of this narrow.
if (opts.change_hash) { if (opts.change_hash) {
hashchange.save_narrow(operators); save_narrow(operators);
} }
handle_post_view_change(msg_list); handle_post_view_change(msg_list);
@ -1019,7 +1036,7 @@ export function deactivate(coming_from_all_messages = true, is_actively_scrollin
reset_ui_state(); reset_ui_state();
handle_middle_pane_transition(); handle_middle_pane_transition();
hashchange.save_narrow(); save_narrow();
if (message_lists.current.selected_id() !== -1) { if (message_lists.current.selected_id() !== -1) {
const preserve_pre_narrowing_screen_position = const preserve_pre_narrowing_screen_position =

View File

@ -2,8 +2,8 @@ import * as activity from "./activity";
import * as blueslip from "./blueslip"; import * as blueslip from "./blueslip";
import * as compose from "./compose"; import * as compose from "./compose";
import * as compose_actions from "./compose_actions"; import * as compose_actions from "./compose_actions";
import * as hashchange from "./hashchange";
import {localstorage} from "./localstorage"; import {localstorage} from "./localstorage";
import * as narrow from "./narrow";
import {page_params} from "./page_params"; import {page_params} from "./page_params";
// Check if we're doing a compose-preserving reload. This must be // Check if we're doing a compose-preserving reload. This must be
@ -27,7 +27,7 @@ export function initialize() {
// exist, but be log it so that it's available for future // exist, but be log it so that it's available for future
// debugging if an exception happens later. // debugging if an exception happens later.
blueslip.info("Invalid hash change reload token"); blueslip.info("Invalid hash change reload token");
hashchange.changehash(""); narrow.changehash("");
return; return;
} }
ls.remove(hash_fragment); ls.remove(hash_fragment);
@ -87,5 +87,5 @@ export function initialize() {
} }
activity.set_new_user_input(false); activity.set_new_user_input(false);
hashchange.changehash(vars.oldhash); narrow.changehash(vars.oldhash);
} }

View File

@ -18,7 +18,6 @@ const admin = mock_esm("../src/admin");
const drafts_overlay_ui = mock_esm("../src/drafts_overlay_ui"); const drafts_overlay_ui = mock_esm("../src/drafts_overlay_ui");
const info_overlay = mock_esm("../src/info_overlay"); const info_overlay = mock_esm("../src/info_overlay");
const message_viewport = mock_esm("../src/message_viewport"); const message_viewport = mock_esm("../src/message_viewport");
const narrow = mock_esm("../src/narrow");
const overlays = mock_esm("../src/overlays"); const overlays = mock_esm("../src/overlays");
const popovers = mock_esm("../src/popovers"); const popovers = mock_esm("../src/popovers");
const recent_view_ui = mock_esm("../src/recent_view_ui"); const recent_view_ui = mock_esm("../src/recent_view_ui");
@ -32,6 +31,7 @@ const browser_history = zrequire("browser_history");
const people = zrequire("people"); const people = zrequire("people");
const hash_util = zrequire("hash_util"); const hash_util = zrequire("hash_util");
const hashchange = zrequire("hashchange"); const hashchange = zrequire("hashchange");
const narrow = zrequire("../src/narrow");
const stream_data = zrequire("stream_data"); const stream_data = zrequire("stream_data");
run_test("operators_round_trip", () => { run_test("operators_round_trip", () => {
@ -120,7 +120,7 @@ run_test("people_slugs", () => {
assert.deepEqual(narrow, [{operator: "pm-with", operand: "alice@example.com", negated: false}]); assert.deepEqual(narrow, [{operator: "pm-with", operand: "alice@example.com", negated: false}]);
}); });
function test_helper({override, change_tab}) { function test_helper({override, override_rewire, change_tab}) {
let events = []; let events = [];
let narrow_terms; let narrow_terms;
@ -143,7 +143,7 @@ function test_helper({override, change_tab}) {
stub(ui_report, "error"); stub(ui_report, "error");
if (change_tab) { if (change_tab) {
override(narrow, "activate", (terms) => { override_rewire(narrow, "activate", (terms) => {
narrow_terms = terms; narrow_terms = terms;
events.push("narrow.activate"); events.push("narrow.activate");
}); });
@ -164,11 +164,11 @@ function test_helper({override, change_tab}) {
}; };
} }
run_test("hash_interactions", ({override}) => { run_test("hash_interactions", ({override, override_rewire}) => {
$window_stub = $.create("window-stub"); $window_stub = $.create("window-stub");
user_settings.default_view = "recent_topics"; user_settings.default_view = "recent_topics";
const helper = test_helper({override, change_tab: true}); const helper = test_helper({override, override_rewire, change_tab: true});
let recent_view_ui_shown = false; let recent_view_ui_shown = false;
override(recent_view_ui, "show", () => { override(recent_view_ui, "show", () => {
@ -331,13 +331,13 @@ run_test("hash_interactions", ({override}) => {
helper.assert_events([[ui_util, "blur_active_element"]]); helper.assert_events([[ui_util, "blur_active_element"]]);
}); });
run_test("save_narrow", ({override}) => { run_test("save_narrow", ({override, override_rewire}) => {
const helper = test_helper({override}); const helper = test_helper({override, override_rewire});
let operators = [{operator: "is", operand: "dm"}]; let operators = [{operator: "is", operand: "dm"}];
blueslip.expect("error", "browser does not support pushState"); blueslip.expect("error", "browser does not support pushState");
hashchange.save_narrow(operators); narrow.save_narrow(operators);
helper.assert_events([[message_viewport, "stop_auto_scrolling"]]); helper.assert_events([[message_viewport, "stop_auto_scrolling"]]);
assert.equal(window.location.hash, "#narrow/is/dm"); assert.equal(window.location.hash, "#narrow/is/dm");
@ -350,7 +350,7 @@ run_test("save_narrow", ({override}) => {
operators = [{operator: "is", operand: "starred"}]; operators = [{operator: "is", operand: "starred"}];
helper.clear_events(); helper.clear_events();
hashchange.save_narrow(operators); narrow.save_narrow(operators);
helper.assert_events([[message_viewport, "stop_auto_scrolling"]]); helper.assert_events([[message_viewport, "stop_auto_scrolling"]]);
assert.equal(url_pushed, "http://zulip.zulipdev.com/#narrow/is/starred"); assert.equal(url_pushed, "http://zulip.zulipdev.com/#narrow/is/starred");
}); });

View File

@ -11,11 +11,13 @@ mock_esm("../src/resize", {
}); });
const all_messages_data = mock_esm("../src/all_messages_data"); const all_messages_data = mock_esm("../src/all_messages_data");
const browser_history = mock_esm("../src/browser_history", {
state: {changing_hash: false},
});
const compose_actions = mock_esm("../src/compose_actions"); const compose_actions = mock_esm("../src/compose_actions");
const compose_banner = mock_esm("../src/compose_banner"); const compose_banner = mock_esm("../src/compose_banner");
const compose_closed_ui = mock_esm("../src/compose_closed_ui"); const compose_closed_ui = mock_esm("../src/compose_closed_ui");
const compose_recipient = mock_esm("../src/compose_recipient"); const compose_recipient = mock_esm("../src/compose_recipient");
const hashchange = mock_esm("../src/hashchange");
const message_fetch = mock_esm("../src/message_fetch"); const message_fetch = mock_esm("../src/message_fetch");
const message_list = mock_esm("../src/message_list"); const message_list = mock_esm("../src/message_list");
const message_lists = mock_esm("../src/message_lists", { const message_lists = mock_esm("../src/message_lists", {
@ -28,6 +30,7 @@ const message_lists = mock_esm("../src/message_lists", {
const message_feed_top_notices = mock_esm("../src/message_feed_top_notices"); const message_feed_top_notices = mock_esm("../src/message_feed_top_notices");
const message_feed_loading = mock_esm("../src/message_feed_loading"); const message_feed_loading = mock_esm("../src/message_feed_loading");
const message_view_header = mock_esm("../src/message_view_header"); const message_view_header = mock_esm("../src/message_view_header");
const message_viewport = mock_esm("../src/message_viewport");
const narrow_history = mock_esm("../src/narrow_history"); const narrow_history = mock_esm("../src/narrow_history");
const narrow_title = mock_esm("../src/narrow_title"); const narrow_title = mock_esm("../src/narrow_title");
const stream_list = mock_esm("../src/stream_list"); const stream_list = mock_esm("../src/stream_list");
@ -76,16 +79,17 @@ function test_helper({override}) {
}); });
} }
stub(browser_history, "set_hash");
stub(compose_banner, "clear_message_sent_banners"); stub(compose_banner, "clear_message_sent_banners");
stub(compose_actions, "on_narrow"); stub(compose_actions, "on_narrow");
stub(compose_closed_ui, "update_reply_recipient_label"); stub(compose_closed_ui, "update_reply_recipient_label");
stub(narrow_history, "save_narrow_state_and_flush"); stub(narrow_history, "save_narrow_state_and_flush");
stub(hashchange, "save_narrow");
stub(message_feed_loading, "hide_indicators"); stub(message_feed_loading, "hide_indicators");
stub(message_feed_top_notices, "hide_top_of_narrow_notices"); stub(message_feed_top_notices, "hide_top_of_narrow_notices");
stub(narrow_title, "update_narrow_title"); stub(narrow_title, "update_narrow_title");
stub(stream_list, "handle_narrow_activated"); stub(stream_list, "handle_narrow_activated");
stub(message_view_header, "render_title_area"); stub(message_view_header, "render_title_area");
stub(message_viewport, "stop_auto_scrolling");
stub(left_sidebar_navigation_area, "handle_narrow_activated"); stub(left_sidebar_navigation_area, "handle_narrow_activated");
stub(typing_events, "render_notifications_for_narrow"); stub(typing_events, "render_notifications_for_narrow");
stub(compose_recipient, "update_narrow_to_recipient_visibility"); stub(compose_recipient, "update_narrow_to_recipient_visibility");
@ -185,7 +189,8 @@ run_test("basics", ({override}) => {
[compose_banner, "clear_message_sent_banners"], [compose_banner, "clear_message_sent_banners"],
[unread_ops, "process_visible"], [unread_ops, "process_visible"],
[narrow_history, "save_narrow_state_and_flush"], [narrow_history, "save_narrow_state_and_flush"],
[hashchange, "save_narrow"], [message_viewport, "stop_auto_scrolling"],
[browser_history, "set_hash"],
[typing_events, "render_notifications_for_narrow"], [typing_events, "render_notifications_for_narrow"],
[compose_closed_ui, "update_buttons_for_stream"], [compose_closed_ui, "update_buttons_for_stream"],
[compose_closed_ui, "update_reply_recipient_label"], [compose_closed_ui, "update_reply_recipient_label"],