mirror of https://github.com/zulip/zulip.git
reload: Convert module to TypeScript.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
parent
0be5cc232c
commit
97ffccb45f
|
@ -97,7 +97,7 @@ reload itself:
|
|||
start looking for a good time to reload, based on when the user is
|
||||
idle (ideally, we'd reload when they're not looking and restore
|
||||
state so that the user never knew it happened!). The logic for
|
||||
doing this is in `web/src/reload.js`; but regardless we'll reload
|
||||
doing this is in `web/src/reload.ts`; but regardless we'll reload
|
||||
within 30 minutes unconditionally.
|
||||
|
||||
An important detail in server-initiated reloads is that we
|
||||
|
|
|
@ -189,7 +189,7 @@ EXEMPT_FILES = make_set(
|
|||
"web/src/realm_playground.ts",
|
||||
"web/src/realm_user_settings_defaults.ts",
|
||||
"web/src/recent_view_ui.ts",
|
||||
"web/src/reload.js",
|
||||
"web/src/reload.ts",
|
||||
"web/src/reload_setup.js",
|
||||
"web/src/reminder.js",
|
||||
"web/src/resize.ts",
|
||||
|
|
|
@ -71,8 +71,6 @@ async function test_reload_hash(page: Page): Promise<void> {
|
|||
const initial_hash = await page.evaluate(() => window.location.hash);
|
||||
|
||||
await page.evaluate(() => {
|
||||
// We haven't converted reload.js to TypeScript yet.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
zulip_test.initiate_reload({immediate: true});
|
||||
});
|
||||
await page.waitForNavigation();
|
||||
|
|
|
@ -52,7 +52,7 @@ export let client_is_active = document.hasFocus();
|
|||
|
||||
// new_user_input is a more strict version of client_is_active used
|
||||
// primarily for analytics. We initialize this to true, to count new
|
||||
// page loads, but set it to false in the onload function in reload.js
|
||||
// page loads, but set it to false in the onload function in reload.ts
|
||||
// if this was a server-initiated-reload to avoid counting a
|
||||
// server-initiated reload as user activity.
|
||||
export let new_user_input = true;
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import $ from "jquery";
|
||||
import assert from "minimalistic-assert";
|
||||
import {z} from "zod";
|
||||
|
||||
import * as blueslip from "./blueslip";
|
||||
import * as compose_state from "./compose_state";
|
||||
import {csrf_token} from "./csrf";
|
||||
import * as drafts from "./drafts";
|
||||
import * as hash_util from "./hash_util";
|
||||
import type {LocalStorage} from "./localstorage";
|
||||
import {localstorage} from "./localstorage";
|
||||
import * as message_lists from "./message_lists";
|
||||
import {page_params} from "./page_params";
|
||||
|
@ -14,19 +17,21 @@ import * as util from "./util";
|
|||
|
||||
// Read https://zulip.readthedocs.io/en/latest/subsystems/hashchange-system.html
|
||||
|
||||
const reload_hooks = [];
|
||||
const token_metadata_schema = z.object({url: z.string(), timestamp: z.number()});
|
||||
|
||||
export function add_reload_hook(hook) {
|
||||
const reload_hooks: (() => void)[] = [];
|
||||
|
||||
export function add_reload_hook(hook: () => void): void {
|
||||
reload_hooks.push(hook);
|
||||
}
|
||||
|
||||
function call_reload_hooks() {
|
||||
function call_reload_hooks(): void {
|
||||
for (const hook of reload_hooks) {
|
||||
hook();
|
||||
}
|
||||
}
|
||||
|
||||
function preserve_state(send_after_reload, save_compose) {
|
||||
function preserve_state(send_after_reload: boolean, save_compose: boolean): void {
|
||||
if (!localstorage.supported()) {
|
||||
// If local storage is not supported by the browser, we can't
|
||||
// save the browser's position across reloads (since there's
|
||||
|
@ -46,13 +51,16 @@ function preserve_state(send_after_reload, save_compose) {
|
|||
}
|
||||
|
||||
let url = "#reload:send_after_reload=" + Number(send_after_reload);
|
||||
assert(csrf_token !== undefined);
|
||||
url += "+csrf_token=" + encodeURIComponent(csrf_token);
|
||||
|
||||
if (save_compose) {
|
||||
const msg_type = compose_state.get_message_type();
|
||||
if (msg_type === "stream") {
|
||||
const stream_id = compose_state.stream_id();
|
||||
assert(stream_id !== undefined);
|
||||
url += "+msg_type=stream";
|
||||
url += "+stream_id=" + encodeURIComponent(compose_state.stream_id());
|
||||
url += "+stream_id=" + encodeURIComponent(stream_id);
|
||||
url += "+topic=" + encodeURIComponent(compose_state.topic());
|
||||
} else if (msg_type === "private") {
|
||||
url += "+msg_type=private";
|
||||
|
@ -94,7 +102,7 @@ function preserve_state(send_after_reload, save_compose) {
|
|||
// TODO: Remove the now-unnecessary URL-encoding logic above and
|
||||
// just pass the actual data structures through local storage.
|
||||
const token = util.random_int(0, 1024 * 1024 * 1024 * 1024);
|
||||
const metadata = {
|
||||
const metadata: z.infer<typeof token_metadata_schema> = {
|
||||
url,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
@ -102,33 +110,39 @@ function preserve_state(send_after_reload, save_compose) {
|
|||
window.location.replace("#reload:" + token);
|
||||
}
|
||||
|
||||
export function is_stale_refresh_token(token_metadata, now) {
|
||||
export function is_stale_refresh_token(token_metadata: unknown, now: number): boolean {
|
||||
const parsed = token_metadata_schema.safeParse(token_metadata);
|
||||
// TODO/compatibility: the metadata was changed from a string
|
||||
// to a map containing the string and a timestamp. For now we'll
|
||||
// delete all tokens that only contain the url. Remove this
|
||||
// early return once you can no longer directly upgrade from
|
||||
// Zulip 5.x to the current version.
|
||||
if (!token_metadata.timestamp) {
|
||||
if (!parsed.success) {
|
||||
return true;
|
||||
}
|
||||
const {timestamp} = parsed.data;
|
||||
|
||||
// The time between reload token generation and use should usually be
|
||||
// fewer than 30 seconds, but we keep tokens around for a week just in case
|
||||
// (e.g. a tab could fail to load and be refreshed a while later).
|
||||
const milliseconds_in_a_day = 1000 * 60 * 60 * 24;
|
||||
const timedelta = now - token_metadata.timestamp;
|
||||
const timedelta = now - timestamp;
|
||||
const days_since_token_creation = timedelta / milliseconds_in_a_day;
|
||||
return days_since_token_creation > 7;
|
||||
}
|
||||
|
||||
function delete_stale_tokens(ls) {
|
||||
function delete_stale_tokens(ls: LocalStorage): void {
|
||||
const now = Date.now();
|
||||
ls.removeDataRegexWithCondition("reload:\\d+", (metadata) =>
|
||||
is_stale_refresh_token(metadata, now),
|
||||
);
|
||||
}
|
||||
|
||||
function do_reload_app(send_after_reload, save_compose, message_html) {
|
||||
function do_reload_app(
|
||||
send_after_reload: boolean,
|
||||
save_compose: boolean,
|
||||
message_html: string,
|
||||
): void {
|
||||
if (reload_state.is_in_progress()) {
|
||||
blueslip.log("do_reload_app: Doing nothing since reload_in_progress");
|
||||
return;
|
||||
|
@ -164,7 +178,7 @@ function do_reload_app(send_after_reload, save_compose, message_html) {
|
|||
});
|
||||
}, 5000);
|
||||
|
||||
function retry_reload() {
|
||||
function retry_reload(): void {
|
||||
blueslip.log("Retrying page reload due to 30s timer");
|
||||
window.location.reload();
|
||||
}
|
||||
|
@ -184,7 +198,7 @@ export function initiate({
|
|||
save_compose = true,
|
||||
send_after_reload = false,
|
||||
message_html = "Reloading ...",
|
||||
}) {
|
||||
}): void {
|
||||
if (immediate) {
|
||||
do_reload_app(send_after_reload, save_compose, message_html);
|
||||
}
|
||||
|
@ -215,14 +229,13 @@ export function initiate({
|
|||
// makes it simple to reason about: We know that reloads will be
|
||||
// spread over at least 5 minutes in all cases.
|
||||
|
||||
let idle_control;
|
||||
let idle_control: ReturnType<JQuery["idle"]>;
|
||||
const random_variance = util.random_int(0, 1000 * 60 * 5);
|
||||
const unconditional_timeout = 1000 * 60 * 30 + random_variance;
|
||||
const composing_idle_timeout = 1000 * 60 * 7 + random_variance;
|
||||
const basic_idle_timeout = 1000 * 60 * 1 + random_variance;
|
||||
let compose_started_handler;
|
||||
|
||||
function reload_from_idle() {
|
||||
function reload_from_idle(): void {
|
||||
do_reload_app(false, save_compose, message_html);
|
||||
}
|
||||
|
||||
|
@ -232,22 +245,22 @@ export function initiate({
|
|||
// particularly disruptive.
|
||||
setTimeout(reload_from_idle, unconditional_timeout);
|
||||
|
||||
const compose_done_handler = function () {
|
||||
function compose_done_handler(): void {
|
||||
// If the user sends their message or otherwise closes
|
||||
// compose, we return them to the not-composing timeouts.
|
||||
idle_control.cancel();
|
||||
idle_control = $(document).idle({idle: basic_idle_timeout, onIdle: reload_from_idle});
|
||||
$(document).off("compose_canceled.zulip compose_finished.zulip", compose_done_handler);
|
||||
$(document).on("compose_started.zulip", compose_started_handler);
|
||||
};
|
||||
compose_started_handler = function () {
|
||||
}
|
||||
function compose_started_handler(): void {
|
||||
// If the user stops being idle and starts composing a
|
||||
// message, switch to the compose-open timeouts.
|
||||
idle_control.cancel();
|
||||
idle_control = $(document).idle({idle: composing_idle_timeout, onIdle: reload_from_idle});
|
||||
$(document).off("compose_started.zulip", compose_started_handler);
|
||||
$(document).on("compose_canceled.zulip compose_finished.zulip", compose_done_handler);
|
||||
};
|
||||
}
|
||||
|
||||
if (compose_state.composing()) {
|
||||
idle_control = $(document).idle({idle: composing_idle_timeout, onIdle: reload_from_idle});
|
|
@ -2,7 +2,7 @@
|
|||
We want his module to load pretty early in the process
|
||||
of starting the app, so that people.js can load early.
|
||||
All the heavy lifting for reload logic happens in
|
||||
reload.js, which has lots of UI dependencies. If we
|
||||
reload.ts, which has lots of UI dependencies. If we
|
||||
didn't split out this module, our whole dependency tree
|
||||
would be kind of upside down.
|
||||
*/
|
||||
|
|
|
@ -10,7 +10,6 @@ export {get_by_user_id as get_person_by_user_id, get_user_id_from_name} from "./
|
|||
export {last_visible as last_visible_row, id as row_id} from "./rows";
|
||||
export {cancel as cancel_compose} from "./compose_actions";
|
||||
export {page_params, page_params_parse_time} from "./base_page_params";
|
||||
// @ts-expect-error We haven't converted reload.js yet
|
||||
export {initiate as initiate_reload} from "./reload";
|
||||
export {page_load_time} from "./setup";
|
||||
export {current_user, realm} from "./state_data";
|
||||
|
|
|
@ -5,7 +5,7 @@ const {strict: assert} = require("assert");
|
|||
const {zrequire} = require("./lib/namespace");
|
||||
const {run_test, noop} = require("./lib/test");
|
||||
|
||||
// override file-level function call in reload.js
|
||||
// override file-level function call in reload.ts
|
||||
window.addEventListener = noop;
|
||||
const reload = zrequire("reload");
|
||||
|
||||
|
|
Loading…
Reference in New Issue