ts: Migrate `channel.js` module to TypeScript.

This commit is contained in:
Lalit Kumar Singh 2023-08-16 15:53:31 +05:30 committed by Tim Abbott
parent f3830a3e1e
commit 4f3a971e64
6 changed files with 44 additions and 32 deletions

View File

@ -185,7 +185,7 @@ js_rules = RuleList(
"exclude": { "exclude": {
# Internal modules can do direct network calls # Internal modules can do direct network calls
"web/src/blueslip.ts", "web/src/blueslip.ts",
"web/src/channel.js", "web/src/channel.ts",
# External modules that don't include channel.js # External modules that don't include channel.js
"web/src/stats/", "web/src/stats/",
"web/src/portico/", "web/src/portico/",

View File

@ -8,17 +8,36 @@ import * as reload_state from "./reload_state";
import {normalize_path, shouldCreateSpanForRequest} from "./sentry"; import {normalize_path, shouldCreateSpanForRequest} from "./sentry";
import * as spectators from "./spectators"; import * as spectators from "./spectators";
// We omit `success` handler from original `AjaxSettings` type because it types
// the `data` parameter as `any` type and we want to avoid that.
type AjaxRequestHandlerOptions = Omit<JQuery.AjaxSettings, "success"> & {
url: string;
ignore_reload?: boolean;
success?: (
data: unknown,
textStatus: JQuery.Ajax.SuccessTextStatus,
jqXHR: JQuery.jqXHR<unknown>,
) => void;
error?: JQuery.Ajax.ErrorCallback<unknown>;
};
type PatchRequestData =
| {processData: false; data: FormData}
| {processData?: true; data: Record<string, unknown>};
export type AjaxRequestHandler = typeof call;
let password_change_in_progress = false; let password_change_in_progress = false;
export let password_changes = 0; export let password_changes = 0;
export function set_password_change_in_progress(value) { export function set_password_change_in_progress(value: boolean): void {
password_change_in_progress = value; password_change_in_progress = value;
if (!value) { if (!value) {
password_changes += 1; password_changes += 1;
} }
} }
function call(args) { function call(args: AjaxRequestHandlerOptions): JQuery.jqXHR<unknown> | undefined {
if (reload_state.is_in_progress() && !args.ignore_reload) { if (reload_state.is_in_progress() && !args.ignore_reload) {
// If we're in the process of reloading, most HTTP requests // If we're in the process of reloading, most HTTP requests
// are useless, with exceptions like cleaning up our event // are useless, with exceptions like cleaning up our event
@ -36,7 +55,7 @@ function call(args) {
method: args.type, method: args.type,
}, },
}; };
let span; let span: Sentry.Span | undefined;
if (!shouldCreateSpanForRequest(args.url)) { if (!shouldCreateSpanForRequest(args.url)) {
// Leave the span unset, so we don't record a transaction // Leave the span unset, so we don't record a transaction
} else { } else {
@ -56,10 +75,7 @@ function call(args) {
// Wrap the error handlers to reload the page if we get a CSRF error // Wrap the error handlers to reload the page if we get a CSRF error
// (What probably happened is that the user logged out in another tab). // (What probably happened is that the user logged out in another tab).
let orig_error = args.error; const orig_error = args.error ?? (() => {});
if (orig_error === undefined) {
orig_error = function () {};
}
args.error = function wrapped_error(xhr, error_type, xhn) { args.error = function wrapped_error(xhr, error_type, xhn) {
if (span !== undefined) { if (span !== undefined) {
span.setHttpStatus(xhr.status); span.setHttpStatus(xhr.status);
@ -119,10 +135,8 @@ function call(args) {
} }
orig_error(xhr, error_type, xhn); orig_error(xhr, error_type, xhn);
}; };
let orig_success = args.success;
if (orig_success === undefined) { const orig_success = args.success ?? (() => {});
orig_success = function () {};
}
args.success = function wrapped_success(data, textStatus, jqXHR) { args.success = function wrapped_success(data, textStatus, jqXHR) {
if (span !== undefined) { if (span !== undefined) {
span.setHttpStatus(jqXHR.status); span.setHttpStatus(jqXHR.status);
@ -151,28 +165,30 @@ function call(args) {
} }
} }
export function get(options) { export function get(options: AjaxRequestHandlerOptions): JQuery.jqXHR<unknown> | undefined {
const args = {type: "GET", dataType: "json", ...options}; const args = {type: "GET", dataType: "json", ...options};
return call(args); return call(args);
} }
export function post(options) { export function post(options: AjaxRequestHandlerOptions): JQuery.jqXHR<unknown> | undefined {
const args = {type: "POST", dataType: "json", ...options}; const args = {type: "POST", dataType: "json", ...options};
return call(args); return call(args);
} }
export function put(options) { export function put(options: AjaxRequestHandlerOptions): JQuery.jqXHR<unknown> | undefined {
const args = {type: "PUT", dataType: "json", ...options}; const args = {type: "PUT", dataType: "json", ...options};
return call(args); return call(args);
} }
// Not called exports.delete because delete is a reserved word in JS // Not called exports.delete because delete is a reserved word in JS
export function del(options) { export function del(options: AjaxRequestHandlerOptions): JQuery.jqXHR<unknown> | undefined {
const args = {type: "DELETE", dataType: "json", ...options}; const args = {type: "DELETE", dataType: "json", ...options};
return call(args); return call(args);
} }
export function patch(options) { export function patch(
options: Omit<AjaxRequestHandlerOptions, "data"> & PatchRequestData,
): JQuery.jqXHR<unknown> | undefined {
// Send a PATCH as a POST in order to work around QtWebkit // Send a PATCH as a POST in order to work around QtWebkit
// (Linux/Windows desktop app) not supporting PATCH body. // (Linux/Windows desktop app) not supporting PATCH body.
if (options.processData === false) { if (options.processData === false) {
@ -185,7 +201,10 @@ export function patch(options) {
return post(options); return post(options);
} }
export function xhr_error_message(message, xhr) { export function xhr_error_message(
message: string | null,
xhr: JQuery.jqXHR<unknown>,
): string | null {
if (xhr.status.toString().charAt(0) === "4" && xhr.responseJSON?.msg) { if (xhr.status.toString().charAt(0) === "4" && xhr.responseJSON?.msg) {
// Only display the error response for 4XX, where we've crafted // Only display the error response for 4XX, where we've crafted
// a nice response. // a nice response.
@ -196,5 +215,6 @@ export function xhr_error_message(message, xhr) {
message = server_response_html; message = server_response_html;
} }
} }
return message; return message;
} }

View File

@ -3,10 +3,10 @@ import _ from "lodash";
import render_dialog_widget from "../templates/dialog_widget.hbs"; import render_dialog_widget from "../templates/dialog_widget.hbs";
import type {AjaxRequestHandler} from "./channel";
import {$t_html} from "./i18n"; import {$t_html} from "./i18n";
import * as loading from "./loading"; import * as loading from "./loading";
import * as overlays from "./overlays"; import * as overlays from "./overlays";
import type {AjaxRequestHandler} from "./types";
import * as ui_report from "./ui_report"; import * as ui_report from "./ui_report";
/* /*
@ -237,7 +237,7 @@ export function submit_api_request(
}: RequestOpts = {}, }: RequestOpts = {},
): void { ): void {
show_dialog_spinner(); show_dialog_spinner();
request_method({ void request_method({
url, url,
data, data,
success(response_data, textStatus, jqXHR) { success(response_data, textStatus, jqXHR) {

View File

@ -4,13 +4,14 @@ const t1 = performance.now();
export const page_params: { export const page_params: {
apps_page_url: string; apps_page_url: string;
corporate_enabled: boolean; corporate_enabled: boolean;
development_environment: boolean;
language_list: { language_list: {
code: string; code: string;
locale: string; locale: string;
name: string; name: string;
percent_translated: number | undefined; percent_translated: number | undefined;
}[]; }[];
development_environment: boolean; login_page: string;
is_admin: boolean; is_admin: boolean;
is_bot: boolean; is_bot: boolean;
is_guest: boolean; is_guest: boolean;

View File

@ -2,9 +2,9 @@ import $ from "jquery";
import checkbox_image from "../images/checkbox-green.svg"; import checkbox_image from "../images/checkbox-green.svg";
import type {AjaxRequestHandler} from "./channel";
import {$t, $t_html} from "./i18n"; import {$t, $t_html} from "./i18n";
import * as loading from "./loading"; import * as loading from "./loading";
import type {AjaxRequestHandler} from "./types";
import * as ui_report from "./ui_report"; import * as ui_report from "./ui_report";
type RequestOpts = { type RequestOpts = {
@ -51,7 +51,7 @@ export function do_settings_change(
const remove_after = sticky ? undefined : 1000; const remove_after = sticky ? undefined : 1000;
const appear_after = 500; const appear_after = 500;
request_method({ void request_method({
url, url,
data, data,
success(response_data) { success(response_data) {

View File

@ -171,12 +171,3 @@ export type UpdateMessageEvent = {
// TODO/typescript: Move the User and Stream placeholder // TODO/typescript: Move the User and Stream placeholder
// types to their appropriate modules. // types to their appropriate modules.
export type User = Record<string, never>; export type User = Record<string, never>;
// TODO/typescript: Move this to channel
export type AjaxRequestHandler = (args: {
url: string;
data?: Record<string, unknown> | string | unknown[];
ignoreReload?: boolean;
success?(response_data: unknown, textStatus: string, jqXHR: JQuery.jqXHR): void;
error?(xhr: JQuery.jqXHR, error_type: string, xhn: string): void;
}) => void;