mirror of
https://github.com/zulip/zulip.git
synced 2025-11-03 05:23:35 +00:00
hashchange: Convert module to TypeScript. (#32670)
This commit is contained in:
@@ -17,7 +17,7 @@ Some examples are:
|
||||
channel.)
|
||||
|
||||
The main module in the frontend that manages this all is
|
||||
`web/src/hashchange.js` (plus `hash_util.js` for all the parsing
|
||||
`web/src/hashchange.ts` (plus `hash_util.js` for all the parsing
|
||||
code), which is unfortunately one of our thorniest modules. Part of
|
||||
the reason that it's thorny is that it needs to support a lot of
|
||||
different flows:
|
||||
|
||||
@@ -113,7 +113,7 @@ EXEMPT_FILES = make_set(
|
||||
"web/src/global.ts",
|
||||
"web/src/group_setting_pill.ts",
|
||||
"web/src/hash_util.ts",
|
||||
"web/src/hashchange.js",
|
||||
"web/src/hashchange.ts",
|
||||
"web/src/hbs.d.ts",
|
||||
"web/src/hotkey.js",
|
||||
"web/src/inbox_ui.ts",
|
||||
|
||||
@@ -285,7 +285,7 @@ export function build_page(): void {
|
||||
}
|
||||
}
|
||||
|
||||
export function launch(section: string, user_settings_tab: string): void {
|
||||
export function launch(section: string, user_settings_tab: string | undefined): void {
|
||||
settings_sections.reset_sections();
|
||||
|
||||
settings.open_settings_overlay();
|
||||
|
||||
@@ -8,7 +8,7 @@ import {user_settings} from "./user_settings.ts";
|
||||
|
||||
export const state: {
|
||||
is_internal_change: boolean;
|
||||
hash_before_overlay: string | null;
|
||||
hash_before_overlay: string | null | undefined;
|
||||
old_hash: string;
|
||||
changing_hash: boolean;
|
||||
spectator_old_hash: string | null;
|
||||
@@ -36,7 +36,7 @@ export function old_hash(): string {
|
||||
return state.old_hash;
|
||||
}
|
||||
|
||||
export function set_hash_before_overlay(hash: string): void {
|
||||
export function set_hash_before_overlay(hash: string | undefined): void {
|
||||
state.hash_before_overlay = hash;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import * as compose_state from "./compose_state.ts";
|
||||
import {media_breakpoints_num} from "./css_variables.ts";
|
||||
import * as emoji_picker from "./emoji_picker.ts";
|
||||
import * as hash_util from "./hash_util.ts";
|
||||
import * as hashchange from "./hashchange.js";
|
||||
import * as hashchange from "./hashchange.ts";
|
||||
import * as message_edit from "./message_edit.ts";
|
||||
import * as message_lists from "./message_lists.ts";
|
||||
import * as message_store from "./message_store.ts";
|
||||
|
||||
@@ -68,7 +68,7 @@ links:
|
||||
#invite
|
||||
|
||||
When you click on the links there is a function
|
||||
called hashchanged() in web/src/hashchange.js
|
||||
called hashchanged() in web/src/hashchange.ts
|
||||
that gets invoked. (We register this as a listener
|
||||
for the hashchange event.) This function then
|
||||
launches the appropriate modal for each menu item.
|
||||
|
||||
@@ -46,7 +46,7 @@ export function is_same_server_message_link(url: string): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
export function is_overlay_hash(hash: string): boolean {
|
||||
export function is_overlay_hash(hash: string | undefined): boolean {
|
||||
// Hash changes within this list are overlays and should not unnarrow (etc.)
|
||||
const overlay_list = [
|
||||
// In 2024, stream was renamed to channel in the Zulip API and UI.
|
||||
|
||||
@@ -37,7 +37,23 @@ import {user_settings} from "./user_settings.ts";
|
||||
// Read https://zulip.readthedocs.io/en/latest/subsystems/hashchange-system.html
|
||||
// or locally: docs/subsystems/hashchange-system.md
|
||||
|
||||
function maybe_hide_recent_view() {
|
||||
export type JQueryHashChangeEvent<TDelegateTarget, TData, TCurrentTarget, TTarget> =
|
||||
JQuery.EventBase<TDelegateTarget, TData, TCurrentTarget, TTarget> & {
|
||||
type: "hashchanged";
|
||||
originalEvent: HashChangeEvent;
|
||||
};
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace JQuery {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
||||
interface TypeToTriggeredEventMap<TDelegateTarget, TData, TCurrentTarget, TTarget> {
|
||||
hashchange: JQueryHashChangeEvent<TDelegateTarget, TData, TCurrentTarget, TTarget>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function maybe_hide_recent_view(): boolean {
|
||||
if (recent_view_util.is_visible()) {
|
||||
recent_view_ui.hide();
|
||||
return true;
|
||||
@@ -45,7 +61,7 @@ function maybe_hide_recent_view() {
|
||||
return false;
|
||||
}
|
||||
|
||||
function maybe_hide_inbox() {
|
||||
function maybe_hide_inbox(): boolean {
|
||||
if (inbox_util.is_visible()) {
|
||||
inbox_ui.hide();
|
||||
return true;
|
||||
@@ -53,26 +69,27 @@ function maybe_hide_inbox() {
|
||||
return false;
|
||||
}
|
||||
|
||||
function show_all_message_view() {
|
||||
function show_all_message_view(): void {
|
||||
// Don't export this function outside of this module since
|
||||
// `change_hash` is false here which means it is should only
|
||||
// be called after hash is updated in the URL.
|
||||
const current_state = browser_history.state_data_schema.nullable().parse(window.history.state);
|
||||
message_view.show([{operator: "in", operand: "home"}], {
|
||||
trigger: "hashchange",
|
||||
change_hash: false,
|
||||
then_select_id: window.history.state?.narrow_pointer,
|
||||
then_select_offset: window.history.state?.narrow_offset,
|
||||
then_select_id: current_state?.narrow_pointer,
|
||||
then_select_offset: current_state?.narrow_offset,
|
||||
});
|
||||
}
|
||||
|
||||
function is_somebody_else_profile_open() {
|
||||
function is_somebody_else_profile_open(): boolean {
|
||||
return (
|
||||
user_profile.get_user_id_if_user_profile_modal_open() !== undefined &&
|
||||
user_profile.get_user_id_if_user_profile_modal_open() !== people.my_current_user_id()
|
||||
);
|
||||
}
|
||||
|
||||
function handle_invalid_users_section_url(user_settings_tab) {
|
||||
function handle_invalid_users_section_url(user_settings_tab: string): string {
|
||||
const valid_user_settings_tab_values = new Set(["active", "deactivated", "invitations"]);
|
||||
if (!valid_user_settings_tab_values.has(user_settings_tab)) {
|
||||
const valid_users_section_url = "#organization/users/active";
|
||||
@@ -82,7 +99,7 @@ function handle_invalid_users_section_url(user_settings_tab) {
|
||||
return user_settings_tab;
|
||||
}
|
||||
|
||||
function get_user_settings_tab(section) {
|
||||
function get_user_settings_tab(section: string): string | undefined {
|
||||
if (section === "users") {
|
||||
const current_user_settings_tab = hash_parser.get_current_nth_hash_section(2);
|
||||
return handle_invalid_users_section_url(current_user_settings_tab);
|
||||
@@ -90,7 +107,7 @@ function get_user_settings_tab(section) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function set_hash_to_home_view(triggered_by_escape_key = false) {
|
||||
export function set_hash_to_home_view(triggered_by_escape_key = false): void {
|
||||
if (browser_history.is_current_hash_home_view()) {
|
||||
return;
|
||||
}
|
||||
@@ -118,7 +135,7 @@ export function set_hash_to_home_view(triggered_by_escape_key = false) {
|
||||
hashchanged(false);
|
||||
}
|
||||
|
||||
function show_home_view() {
|
||||
function show_home_view(): void {
|
||||
// This function should only be called from the hashchange
|
||||
// handlers, as it does not set the hash to "".
|
||||
//
|
||||
@@ -153,7 +170,7 @@ function show_home_view() {
|
||||
}
|
||||
|
||||
// Returns true if this function performed a narrow
|
||||
function do_hashchange_normal(from_reload, restore_selected_id) {
|
||||
function do_hashchange_normal(from_reload: boolean, restore_selected_id: boolean): boolean {
|
||||
message_viewport.stop_auto_scrolling();
|
||||
|
||||
// NB: In Firefox, window.location.hash is URI-decoded.
|
||||
@@ -184,7 +201,7 @@ function do_hashchange_normal(from_reload, restore_selected_id) {
|
||||
show_home_view();
|
||||
return false;
|
||||
}
|
||||
const narrow_opts = {
|
||||
const narrow_opts: message_view.ShowMessageViewOpts = {
|
||||
change_hash: false, // already set
|
||||
trigger: "hash change",
|
||||
show_more_topics: false,
|
||||
@@ -197,7 +214,9 @@ function do_hashchange_normal(from_reload, restore_selected_id) {
|
||||
}
|
||||
}
|
||||
|
||||
const data_for_hash = window.history.state;
|
||||
const data_for_hash = browser_history.state_data_schema
|
||||
.nullable()
|
||||
.parse(window.history.state);
|
||||
if (restore_selected_id && data_for_hash) {
|
||||
narrow_opts.then_select_id = data_for_hash.narrow_pointer;
|
||||
narrow_opts.then_select_offset = data_for_hash.narrow_offset;
|
||||
@@ -251,7 +270,7 @@ function do_hashchange_normal(from_reload, restore_selected_id) {
|
||||
case "#settings":
|
||||
case "#about-zulip":
|
||||
case "#scheduled":
|
||||
blueslip.error("overlay logic skipped for: " + hash);
|
||||
blueslip.error("overlay logic skipped for: " + hash[0]);
|
||||
break;
|
||||
default:
|
||||
show_home_view();
|
||||
@@ -259,7 +278,7 @@ function do_hashchange_normal(from_reload, restore_selected_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function do_hashchange_overlay(old_hash) {
|
||||
function do_hashchange_overlay(old_hash: string | undefined): void {
|
||||
if (old_hash === undefined) {
|
||||
// The user opened the app with an overlay hash; we need to
|
||||
// show the user's home view behind it.
|
||||
@@ -486,9 +505,13 @@ function do_hashchange_overlay(old_hash) {
|
||||
}
|
||||
}
|
||||
|
||||
function hashchanged(from_reload, e, restore_selected_id = true) {
|
||||
function hashchanged(
|
||||
from_reload: boolean,
|
||||
e?: JQuery.Event | HashChangeEvent,
|
||||
restore_selected_id = true,
|
||||
): boolean | undefined {
|
||||
const current_hash = window.location.hash;
|
||||
const old_hash = e && (e.oldURL ? new URL(e.oldURL).hash : browser_history.old_hash());
|
||||
const old_hash = e && ("oldURL" in e ? new URL(e.oldURL).hash : browser_history.old_hash());
|
||||
const is_hash_web_public_compatible = browser_history.update_web_public_hash(current_hash);
|
||||
|
||||
const was_internal_change = browser_history.save_old_hash();
|
||||
@@ -528,7 +551,7 @@ function hashchanged(from_reload, e, restore_selected_id = true) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function initialize() {
|
||||
export function initialize(): void {
|
||||
// We don't want browser to restore the scroll
|
||||
// position of the new hash in the current hash.
|
||||
window.history.scrollRestoration = "manual";
|
||||
@@ -538,8 +561,8 @@ export function initialize() {
|
||||
});
|
||||
hashchanged(true);
|
||||
|
||||
$("body").on("click", "a", (e) => {
|
||||
const href = e.currentTarget.getAttribute("href");
|
||||
$("body").on("click", "a", function (this: HTMLAnchorElement, e: JQuery.ClickEvent) {
|
||||
const href = this.href;
|
||||
if (href === window.location.hash && href.includes("/near/")) {
|
||||
// The clicked on a link, perhaps a "said" reference, that
|
||||
// matches the current view. Such a click doesn't trigger
|
||||
@@ -23,7 +23,7 @@ import * as feedback_widget from "./feedback_widget.ts";
|
||||
import * as gear_menu from "./gear_menu.ts";
|
||||
import * as giphy from "./giphy.ts";
|
||||
import * as hash_util from "./hash_util.ts";
|
||||
import * as hashchange from "./hashchange.js";
|
||||
import * as hashchange from "./hashchange.ts";
|
||||
import * as inbox_ui from "./inbox_ui.ts";
|
||||
import * as lightbox from "./lightbox.ts";
|
||||
import * as list_util from "./list_util.ts";
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import $ from "jquery";
|
||||
import assert from "minimalistic-assert";
|
||||
|
||||
import * as blueslip from "./blueslip.ts";
|
||||
import * as browser_history from "./browser_history.ts";
|
||||
@@ -59,7 +60,7 @@ export class SettingsPanelMenu {
|
||||
hash_prefix: string;
|
||||
$curr_li: JQuery;
|
||||
current_tab: string;
|
||||
current_user_settings_tab: string;
|
||||
current_user_settings_tab: string | undefined;
|
||||
org_user_settings_toggler: Toggle;
|
||||
|
||||
constructor(opts: {$main_elem: JQuery; hash_prefix: string}) {
|
||||
@@ -176,13 +177,13 @@ export class SettingsPanelMenu {
|
||||
this.current_tab = tab;
|
||||
}
|
||||
|
||||
set_user_settings_tab(tab: string): void {
|
||||
set_user_settings_tab(tab: string | undefined): void {
|
||||
this.current_user_settings_tab = tab;
|
||||
}
|
||||
|
||||
activate_section_or_default(
|
||||
section: string,
|
||||
user_settings_tab: string,
|
||||
section: string | undefined,
|
||||
user_settings_tab?: string,
|
||||
activate_section_for_mobile = true,
|
||||
): void {
|
||||
popovers.hide_all();
|
||||
@@ -220,6 +221,7 @@ export class SettingsPanelMenu {
|
||||
browser_history.update_hash_internally_if_required(settings_section_hash);
|
||||
}
|
||||
if (section === "users" && this.org_user_settings_toggler !== undefined) {
|
||||
assert(user_settings_tab !== undefined);
|
||||
this.show_org_user_settings_toggler();
|
||||
this.org_user_settings_toggler.goto(user_settings_tab);
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ import * as emojisets from "./emojisets.ts";
|
||||
import * as gear_menu from "./gear_menu.ts";
|
||||
import * as giphy from "./giphy.ts";
|
||||
import * as giphy_state from "./giphy_state.ts";
|
||||
import * as hashchange from "./hashchange.js";
|
||||
import * as hashchange from "./hashchange.ts";
|
||||
import * as hotkey from "./hotkey.js";
|
||||
import * as i18n from "./i18n.ts";
|
||||
import * as inbox_ui from "./inbox_ui.ts";
|
||||
@@ -659,7 +659,7 @@ export function initialize_everything(state_data) {
|
||||
},
|
||||
});
|
||||
|
||||
// All overlays, and also activity_ui, must be initialized before hashchange.js
|
||||
// All overlays, and also activity_ui, must be initialized before hashchange.ts
|
||||
hashchange.initialize();
|
||||
|
||||
emoji_picker.initialize();
|
||||
|
||||
@@ -11,7 +11,7 @@ let $window_stub;
|
||||
set_global("to_$", () => $window_stub);
|
||||
|
||||
set_global("document", "document-stub");
|
||||
const history = set_global("history", {});
|
||||
const history = set_global("history", {state: null});
|
||||
|
||||
const admin = mock_esm("../src/admin");
|
||||
const drafts_overlay_ui = mock_esm("../src/drafts_overlay_ui");
|
||||
|
||||
Reference in New Issue
Block a user