diff --git a/tools/test-js-with-node b/tools/test-js-with-node index 06ed8a4971..ac76619121 100755 --- a/tools/test-js-with-node +++ b/tools/test-js-with-node @@ -101,7 +101,7 @@ EXEMPT_FILES = make_set( "web/src/inbox_ui.js", "web/src/inbox_util.js", "web/src/info_overlay.js", - "web/src/invite.js", + "web/src/invite.ts", "web/src/lightbox.js", "web/src/list_util.ts", "web/src/list_widget.ts", diff --git a/web/src/hbs.d.ts b/web/src/hbs.d.ts index fa21b755b4..35bc9a539c 100644 --- a/web/src/hbs.d.ts +++ b/web/src/hbs.d.ts @@ -1,4 +1,4 @@ declare module "*.hbs" { - const render: (context: unknown) => string; + const render: (context?: unknown) => string; export = render; } diff --git a/web/src/invite.js b/web/src/invite.ts similarity index 78% rename from web/src/invite.js rename to web/src/invite.ts index 7d5c9d7067..e9c1f6fe2b 100644 --- a/web/src/invite.js +++ b/web/src/invite.ts @@ -2,6 +2,7 @@ import autosize from "autosize"; import ClipboardJS from "clipboard"; import {add} from "date-fns"; import $ from "jquery"; +import assert from "minimalistic-assert"; import copy_invite_link from "../templates/copy_invite_link.hbs"; import render_invitation_failed_error from "../templates/invitation_failed_error.hbs"; @@ -26,7 +27,7 @@ import * as util from "./util"; let custom_expiration_time_input = 10; let custom_expiration_time_unit = "days"; -function reset_error_messages() { +function reset_error_messages(): void { $("#dialog_error").hide().text("").removeClass(common.status_classes); if (page_params.development_environment) { @@ -34,39 +35,52 @@ function reset_error_messages() { } } -function get_common_invitation_data() { - const invite_as = Number.parseInt($("#invite_as").val(), 10); - let expires_in = $("#expires_in").val(); +function get_common_invitation_data(): { + csrfmiddlewaretoken: string; + invite_as: number; + stream_ids: string; + invite_expires_in_minutes: string; + invitee_emails?: string; +} { + const invite_as = Number.parseInt( + $("select:not([multiple])#invite_as").val()!, + 10, + ); + const raw_expires_in = $( + "select:not([multiple])#expires_in", + ).val()!; // See settings_config.expires_in_values for why we do this conversion. - if (expires_in === "null") { - expires_in = JSON.stringify(null); - } else if (expires_in === "custom") { - expires_in = Number.parseFloat(get_expiration_time_in_minutes()); + let expires_in: number | null; + if (raw_expires_in === "null") { + expires_in = null; + } else if (raw_expires_in === "custom") { + expires_in = get_expiration_time_in_minutes(); } else { - expires_in = Number.parseFloat($("#expires_in").val()); + expires_in = Number.parseFloat(raw_expires_in); } - let stream_ids = []; + let stream_ids: number[] = []; const default_stream_ids = stream_data.get_default_stream_ids(); if (default_stream_ids.length !== 0 && $("#invite_select_default_streams").prop("checked")) { stream_ids = default_stream_ids; } else { - $("#invite-stream-checkboxes input:checked").each(function () { - const stream_id = Number.parseInt($(this).val(), 10); + $("#invite-stream-checkboxes input:checked").each(function () { + const stream_id = Number.parseInt($(this).val()!, 10); stream_ids.push(stream_id); }); } + assert(csrf_token !== undefined); const data = { csrfmiddlewaretoken: csrf_token, invite_as, stream_ids: JSON.stringify(stream_ids), - invite_expires_in_minutes: expires_in, + invite_expires_in_minutes: JSON.stringify(expires_in), }; return data; } -function beforeSend() { +function beforeSend(): void { reset_error_messages(); // TODO: You could alternatively parse the textarea here, and return errors to // the user if they don't match certain constraints (i.e. not real email addresses, @@ -76,16 +90,18 @@ function beforeSend() { const loading_text = $("#invite-user-modal .dialog_submit_button").data("loading-text"); $("#invite-user-modal .dialog_submit_button").text(loading_text); $("#invite-user-modal .dialog_submit_button").prop("disabled", true); - return true; } -function submit_invitation_form() { +function submit_invitation_form(): void { + const $expires_in = $( + "select:not([multiple])#expires_in", + ); const $invite_status = $("#dialog_error"); - const $invitee_emails = $("#invitee_emails"); + const $invitee_emails = $("textarea#invitee_emails"); const data = get_common_invitation_data(); - data.invitee_emails = $("#invitee_emails").val(); + data.invitee_emails = $invitee_emails.val()!; - channel.post({ + void channel.post({ url: "/json/invites", data, beforeSend, @@ -101,14 +117,14 @@ function submit_invitation_form() { $("#dev_env_msg").html(rendered_email_msg).addClass("alert-info").show(); } - if ($("#expires_in").val() === "custom") { + if ($expires_in.val() === "custom") { // Hide the custom inputs if the custom input is set // to one of the dropdown's standard options. const time_in_minutes = get_expiration_time_in_minutes(); for (const option of Object.values(settings_config.expires_in_values)) { if (option.value === time_in_minutes) { $("#custom-invite-expiration-time").hide(); - $("#expires_in").val(time_in_minutes); + $expires_in.val(time_in_minutes); return; } } @@ -153,16 +169,16 @@ function submit_invitation_form() { $("#invite-user-modal .dialog_submit_button").text($t({defaultMessage: "Invite"})); $("#invite-user-modal .dialog_submit_button").prop("disabled", false); $("#invite-user-modal .dialog_exit_button").prop("disabled", false); - $("#invitee_emails").trigger("focus"); + $("textarea#invitee_emails").trigger("focus"); scroll_util.get_scroll_element($("#invite-user-modal"))[0].scrollTop = 0; }, }); } -function generate_multiuse_invite() { +function generate_multiuse_invite(): void { const $invite_status = $("#dialog_error"); const data = get_common_invitation_data(); - channel.post({ + void channel.post({ url: "/json/invites/multiuse", data, beforeSend, @@ -185,14 +201,13 @@ function generate_multiuse_invite() { }); } -export function get_invite_streams() { +export function get_invite_streams(): stream_data.InviteStreamData[] { const streams = stream_data.get_invite_stream_data(); streams.sort((a, b) => util.strcmp(a.name, b.name)); return streams; } -function valid_to(expires_in) { - const time_valid = Number.parseFloat(expires_in); +function valid_to(time_valid: number): string { if (!time_valid) { return $t({defaultMessage: "Never expires"}); } @@ -205,7 +220,7 @@ function valid_to(expires_in) { return $t({defaultMessage: "Expires on {date} at {time}"}, {date, time}); } -function get_expiration_time_in_minutes() { +function get_expiration_time_in_minutes(): number { switch (custom_expiration_time_unit) { case "hours": return custom_expiration_time_input * 60; @@ -218,27 +233,35 @@ function get_expiration_time_in_minutes() { } } -function set_expires_on_text() { - if ($("#expires_in").val() === "custom") { +function set_expires_on_text(): void { + const $expires_in = $( + "select:not([multiple])#expires_in", + ); + if ($expires_in.val() === "custom") { $("#expires_on").hide(); $("#custom_expires_on").text(valid_to(get_expiration_time_in_minutes())); } else { $("#expires_on").show(); - $("#expires_on").text(valid_to($("#expires_in").val())); + $("#expires_on").text(valid_to(Number.parseFloat($expires_in.val()!))); } } -function set_custom_time_inputs_visibility() { - if ($("#expires_in").val() === "custom") { +function set_custom_time_inputs_visibility(): void { + const $expires_in = $( + "select:not([multiple])#expires_in", + ); + if ($expires_in.val() === "custom") { $("#custom-expiration-time-input").val(custom_expiration_time_input); - $("#custom-expiration-time-unit").val(custom_expiration_time_unit); + $( + "select:not([multiple])#custom-expiration-time-unit", + ).val(custom_expiration_time_unit); $("#custom-invite-expiration-time").show(); } else { $("#custom-invite-expiration-time").hide(); } } -function set_streams_to_join_list_visibility() { +function set_streams_to_join_list_visibility(): void { const default_streams_selected = $("#invite_select_default_streams").prop("checked"); if (default_streams_selected) { $("#streams_to_add .invite-stream-controls").hide(); @@ -249,7 +272,7 @@ function set_streams_to_join_list_visibility() { } } -function open_invite_user_modal(e) { +function open_invite_user_modal(e: JQuery.ClickEvent): void { e.stopPropagation(); e.preventDefault(); @@ -269,7 +292,12 @@ function open_invite_user_modal(e) { user_has_email_set: !settings_data.user_email_not_configured(), }); - function invite_user_modal_post_render() { + function invite_user_modal_post_render(): void { + const $expires_in = $( + "select:not([multiple])#expires_in", + ); + const $invitee_emails = $("textarea#invitee_emails"); + $("#invite-user-modal .dialog_submit_button").prop("disabled", true); $("#email_invite_radio").prop("checked", true); @@ -281,16 +309,16 @@ function open_invite_user_modal(e) { const user_has_email_set = !settings_data.user_email_not_configured(); - autosize($("#invitee_emails").trigger("focus")); + autosize($invitee_emails.trigger("focus")); set_custom_time_inputs_visibility(); set_expires_on_text(); set_streams_to_join_list_visibility(); - function toggle_invite_submit_button() { + function toggle_invite_submit_button(): void { $("#invite-user-modal .dialog_submit_button").prop( "disabled", - $("#invitee_emails").val().trim() === "" && + $invitee_emails.val()!.trim() === "" && !$("#generate_multiuse_invite_radio").is(":checked"), ); } @@ -323,12 +351,12 @@ function open_invite_user_modal(e) { reset_error_messages(); }); - $("#expires_in").on("change", () => { + $expires_in.on("change", () => { set_custom_time_inputs_visibility(); set_expires_on_text(); }); - $("#expires_on").text(valid_to($("#expires_in").val())); + $("#expires_on").text(valid_to(Number.parseFloat($expires_in.val()!))); $("#custom-expiration-time-input").on("keydown", (e) => { if (e.key === "Enter") { @@ -338,8 +366,12 @@ function open_invite_user_modal(e) { }); $(".custom-expiration-time").on("change", () => { - custom_expiration_time_input = $("#custom-expiration-time-input").val(); - custom_expiration_time_unit = $("#custom-expiration-time-unit").val(); + custom_expiration_time_input = Number.parseFloat( + $("#custom-expiration-time-input").val()!, + ); + custom_expiration_time_unit = $( + "select:not([multiple])#custom-expiration-time-unit", + ).val()!; $("#custom_expires_on").text(valid_to(get_expiration_time_in_minutes())); }); @@ -371,7 +403,7 @@ function open_invite_user_modal(e) { } } - function invite_users() { + function invite_users(): void { const is_generate_invite_link = $("#generate_multiuse_invite_radio").prop("checked"); if (is_generate_invite_link) { generate_multiuse_invite(); @@ -391,6 +423,6 @@ function open_invite_user_modal(e) { }); } -export function initialize() { +export function initialize(): void { $(document).on("click", ".invite-user-link", open_invite_user_modal); } diff --git a/web/src/page_params.ts b/web/src/page_params.ts index b116bb992d..86f2ccaa2c 100644 --- a/web/src/page_params.ts +++ b/web/src/page_params.ts @@ -15,6 +15,7 @@ export const page_params: { login_page: string; delivery_email: string; is_admin: boolean; + is_billing_admin: boolean; is_bot: boolean; is_guest: boolean; is_moderator: boolean; diff --git a/web/src/stream_data.ts b/web/src/stream_data.ts index e0ce1a9f9e..44b4e9849e 100644 --- a/web/src/stream_data.ts +++ b/web/src/stream_data.ts @@ -33,7 +33,7 @@ type ApiGenericStreamSubscription = | ApiStreamSubscription | (Stream & {stream_weekly_traffic: number | null; subscribers: number[]}); -type InviteStreamData = { +export type InviteStreamData = { name: string; stream_id: number; invite_only: boolean;