mirror of
https://github.com/zulip/zulip.git
synced 2025-10-28 18:43:52 +00:00
This commit was originally automatically generated using `tools/lint --only=eslint --fix`. It was then modified by tabbott to contain only changes to a set of files that are unlikely to result in significant merge conflicts with any open pull request, excluding about 20 files. His plan is to merge the remaining changes with more precise care, potentially involving merging parts of conflicting pull requests before running the `eslint --fix` operation. Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
314 lines
9.1 KiB
JavaScript
314 lines
9.1 KiB
JavaScript
// Read https://zulip.readthedocs.io/en/latest/subsystems/hashchange-system.html
|
|
// or locally: docs/subsystems/hashchange-system.md
|
|
let changing_hash = false;
|
|
|
|
function get_full_url(hash) {
|
|
const location = window.location;
|
|
|
|
if (hash === '' || hash.charAt(0) !== '#') {
|
|
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 (history.pushState) {
|
|
const url = get_full_url(hash);
|
|
history.pushState(null, null, url);
|
|
} else {
|
|
blueslip.warn('browser does not support pushState');
|
|
window.location.hash = hash;
|
|
}
|
|
}
|
|
|
|
exports.changehash = function (newhash) {
|
|
if (changing_hash) {
|
|
return;
|
|
}
|
|
message_viewport.stop_auto_scrolling();
|
|
set_hash(newhash);
|
|
favicon.reset();
|
|
};
|
|
|
|
exports.save_narrow = function (operators) {
|
|
if (changing_hash) {
|
|
return;
|
|
}
|
|
const new_hash = hash_util.operators_to_hash(operators);
|
|
exports.changehash(new_hash);
|
|
};
|
|
|
|
function activate_home_tab() {
|
|
ui_util.change_tab_to("#home");
|
|
narrow.deactivate();
|
|
floating_recipient_bar.update();
|
|
}
|
|
|
|
const state = {
|
|
is_internal_change: false,
|
|
hash_before_overlay: null,
|
|
old_hash: typeof window !== "undefined" ? window.location.hash : "#",
|
|
};
|
|
|
|
function is_overlay_hash(hash) {
|
|
// Hash changes within this list are overlays and should not unnarrow (etc.)
|
|
const overlay_list = ["streams", "drafts", "settings", "organization", "invite"];
|
|
const main_hash = hash_util.get_hash_category(hash);
|
|
|
|
return overlay_list.indexOf(main_hash) > -1;
|
|
}
|
|
|
|
// Returns true if this function performed a narrow
|
|
function do_hashchange_normal(from_reload) {
|
|
message_viewport.stop_auto_scrolling();
|
|
|
|
// NB: In Firefox, window.location.hash is URI-decoded.
|
|
// Even if the URL bar says #%41%42%43%44, the value here will
|
|
// be #ABCD.
|
|
const hash = window.location.hash.split("/");
|
|
switch (hash[0]) {
|
|
case "#narrow": {
|
|
ui_util.change_tab_to("#home");
|
|
const operators = hash_util.parse_narrow(hash);
|
|
if (operators === undefined) {
|
|
// If the narrow URL didn't parse, clear
|
|
// window.location.hash and send them to the home tab
|
|
set_hash('');
|
|
activate_home_tab();
|
|
return false;
|
|
}
|
|
const narrow_opts = {
|
|
change_hash: false, // already set
|
|
trigger: 'hash change',
|
|
};
|
|
if (from_reload) {
|
|
blueslip.debug('We are narrowing as part of a reload.');
|
|
if (page_params.initial_narrow_pointer !== undefined) {
|
|
home_msg_list.pre_narrow_offset = page_params.initial_offset;
|
|
narrow_opts.then_select_id = page_params.initial_narrow_pointer;
|
|
narrow_opts.then_select_offset = page_params.initial_narrow_offset;
|
|
}
|
|
}
|
|
narrow.activate(operators, narrow_opts);
|
|
floating_recipient_bar.update();
|
|
return true;
|
|
}
|
|
case "":
|
|
case "#":
|
|
activate_home_tab();
|
|
break;
|
|
case "#keyboard-shortcuts":
|
|
info_overlay.show("keyboard-shortcuts");
|
|
break;
|
|
case "#message-formatting":
|
|
info_overlay.show("message-formatting");
|
|
break;
|
|
case "#search-operators":
|
|
info_overlay.show("search-operators");
|
|
break;
|
|
case "#drafts":
|
|
case "#invite":
|
|
case "#streams":
|
|
case "#organization":
|
|
case "#settings":
|
|
blueslip.error('overlay logic skipped for: ' + hash);
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function do_hashchange_overlay(old_hash) {
|
|
const base = hash_util.get_hash_category(window.location.hash);
|
|
const old_base = hash_util.get_hash_category(old_hash);
|
|
let section = hash_util.get_hash_section(window.location.hash);
|
|
|
|
const coming_from_overlay = is_overlay_hash(old_hash || '#');
|
|
|
|
// Start by handling the specific case of going
|
|
// from something like streams/all to streams_subscribed.
|
|
//
|
|
// In most situations we skip by this logic and load
|
|
// the new overlay.
|
|
if (coming_from_overlay) {
|
|
if (base === old_base) {
|
|
if (base === 'streams') {
|
|
subs.change_state(section);
|
|
return;
|
|
}
|
|
|
|
if (base === 'settings') {
|
|
if (!section) {
|
|
// We may be on a really old browser or somebody
|
|
// hand-typed a hash.
|
|
blueslip.warn('missing section for settings');
|
|
section = 'your-account';
|
|
}
|
|
settings_panel_menu.normal_settings.activate_section(section);
|
|
return;
|
|
}
|
|
|
|
if (base === 'organization') {
|
|
if (!section) {
|
|
// We may be on a really old browser or somebody
|
|
// hand-typed a hash.
|
|
blueslip.warn('missing section for organization');
|
|
section = 'organization-profile';
|
|
}
|
|
settings_panel_menu.org_settings.activate_section(section);
|
|
return;
|
|
}
|
|
|
|
// TODO: handle other cases like internal settings
|
|
// changes.
|
|
return;
|
|
}
|
|
}
|
|
|
|
// It's not super likely that an overlay is already open,
|
|
// but you can jump from /settings to /streams by using
|
|
// the browser's history menu or hand-editing the URL or
|
|
// whatever. If so, just close the overlays.
|
|
if (base !== old_base) {
|
|
overlays.close_for_hash_change();
|
|
}
|
|
|
|
// NORMAL FLOW: basically, launch the overlay:
|
|
|
|
if (!coming_from_overlay) {
|
|
state.hash_before_overlay = old_hash;
|
|
}
|
|
|
|
if (base === "streams") {
|
|
subs.launch(section);
|
|
return;
|
|
}
|
|
|
|
if (base === "drafts") {
|
|
drafts.launch();
|
|
return;
|
|
}
|
|
|
|
if (base === 'settings') {
|
|
if (!section) {
|
|
section = settings_panel_menu.normal_settings.current_tab();
|
|
const settings_hash = '#settings/' + section;
|
|
exports.replace_hash(settings_hash);
|
|
}
|
|
|
|
settings.launch(section);
|
|
return;
|
|
}
|
|
|
|
if (base === 'organization') {
|
|
if (!section) {
|
|
section = settings_panel_menu.org_settings.current_tab();
|
|
const org_hash = '#organization/' + section;
|
|
exports.replace_hash(org_hash);
|
|
}
|
|
|
|
admin.launch(section);
|
|
return;
|
|
}
|
|
|
|
if (base === "invite") {
|
|
invite.launch();
|
|
return;
|
|
}
|
|
}
|
|
|
|
function hashchanged(from_reload, e) {
|
|
if (state.is_internal_change) {
|
|
state.is_internal_change = false;
|
|
return;
|
|
}
|
|
|
|
let old_hash;
|
|
if (e) {
|
|
old_hash = "#" + (e.oldURL || state.old_hash).split(/#/).slice(1).join("");
|
|
state.old_hash = window.location.hash;
|
|
}
|
|
|
|
if (is_overlay_hash(window.location.hash)) {
|
|
do_hashchange_overlay(old_hash);
|
|
return;
|
|
}
|
|
|
|
// We are changing to a "main screen" view.
|
|
overlays.close_for_hash_change();
|
|
changing_hash = true;
|
|
const ret = do_hashchange_normal(from_reload);
|
|
changing_hash = false;
|
|
return ret;
|
|
}
|
|
|
|
exports.update_browser_history = function (new_hash) {
|
|
const old_hash = window.location.hash;
|
|
|
|
if (!new_hash.startsWith('#')) {
|
|
blueslip.error('programming error: prefix hashes with #: ' + new_hash);
|
|
return;
|
|
}
|
|
|
|
if (old_hash === new_hash) {
|
|
// If somebody is calling us with the same hash we already have, it's
|
|
// probably harmless, and we just ignore it. But it could be a symptom
|
|
// of disorganized code that's prone to an infinite loop of repeatedly
|
|
// assigning the same hash.
|
|
blueslip.info('ignoring probably-harmless call to update_browser_history: ' + new_hash);
|
|
return;
|
|
}
|
|
|
|
state.old_hash = old_hash;
|
|
state.is_internal_change = true;
|
|
window.location.hash = new_hash;
|
|
};
|
|
|
|
exports.replace_hash = function (hash) {
|
|
if (!window.history.replaceState) {
|
|
// We may have strange behavior with the back button.
|
|
blueslip.warn('browser does not support replaceState');
|
|
return;
|
|
}
|
|
|
|
const url = get_full_url(hash);
|
|
window.history.replaceState(null, null, url);
|
|
};
|
|
|
|
exports.go_to_location = function (hash) {
|
|
// Call this function when you WANT the hashchanged
|
|
// function to run.
|
|
window.location.hash = hash;
|
|
};
|
|
|
|
exports.initialize = function () {
|
|
$(window).on('hashchange', function (e) {
|
|
hashchanged(false, e.originalEvent);
|
|
});
|
|
hashchanged(true);
|
|
};
|
|
|
|
exports.exit_overlay = function (callback) {
|
|
if (is_overlay_hash(window.location.hash)) {
|
|
ui_util.blur_active_element();
|
|
const new_hash = state.hash_before_overlay || "#";
|
|
exports.update_browser_history(new_hash);
|
|
if (typeof callback === "function") {
|
|
callback();
|
|
}
|
|
|
|
}
|
|
};
|
|
|
|
window.hashchange = exports;
|