compose_validate: Convert module to TypeScript.

This commit is contained in:
Temidayo32 2024-03-06 18:08:03 +00:00 committed by Tim Abbott
parent fd3c7728fc
commit 533a6153b2
3 changed files with 91 additions and 55 deletions

View File

@ -80,7 +80,7 @@ EXEMPT_FILES = make_set(
"web/src/compose_textarea.ts",
"web/src/compose_tooltips.js",
"web/src/compose_ui.ts",
"web/src/compose_validate.js",
"web/src/compose_validate.ts",
"web/src/composebox_typeahead.js",
"web/src/condense.ts",
"web/src/confirm_dialog.ts",

View File

@ -1,4 +1,6 @@
import $ from "jquery";
import assert from "minimalistic-assert";
import {z} from "zod";
import * as resolved_topic from "../shared/src/resolved_topic";
import render_compose_banner from "../templates/compose_banner/compose_banner.hbs";
@ -26,6 +28,8 @@ import * as settings_data from "./settings_data";
import {current_user, realm} from "./state_data";
import * as stream_data from "./stream_data";
import * as sub_store from "./sub_store";
import type {StreamSubscription} from "./sub_store";
import type {UserOrMention} from "./typeahead_helper";
import * as util from "./util";
let user_acknowledged_stream_wildcard = false;
@ -33,31 +37,42 @@ let upload_in_progress = false;
let message_too_long = false;
let recipient_disallowed = false;
type StreamWildcardOptions = {
stream_id: number;
$banner_container: JQuery;
scheduling_message: boolean;
stream_wildcard_mention: string | null;
};
export let wildcard_mention_threshold = 15;
export function set_upload_in_progress(status) {
const server_subscription_exists_schema = z.object({
subscribed: z.boolean(),
});
export function set_upload_in_progress(status: boolean): void {
upload_in_progress = status;
update_send_button_status();
}
function set_message_too_long(status) {
function set_message_too_long(status: boolean): void {
message_too_long = status;
update_send_button_status();
}
export function set_recipient_disallowed(status) {
export function set_recipient_disallowed(status: boolean): void {
recipient_disallowed = status;
update_send_button_status();
}
function update_send_button_status() {
function update_send_button_status(): void {
$(".message-send-controls").toggleClass(
"disabled-message-send-controls",
message_too_long || upload_in_progress || recipient_disallowed,
);
}
export function get_disabled_send_tooltip() {
export function get_disabled_send_tooltip(): string {
if (message_too_long) {
return $t({defaultMessage: "Message length shouldn't be greater than 10000 characters."});
} else if (upload_in_progress) {
@ -66,7 +81,7 @@ export function get_disabled_send_tooltip() {
return "";
}
export function needs_subscribe_warning(user_id, stream_id) {
export function needs_subscribe_warning(user_id: number, stream_id: number): boolean {
// This returns true if all of these conditions are met:
// * the user is valid
// * the user is not already subscribed to the stream
@ -100,7 +115,7 @@ export function needs_subscribe_warning(user_id, stream_id) {
return true;
}
function get_stream_id_for_textarea($textarea) {
function get_stream_id_for_textarea($textarea: JQuery<HTMLTextAreaElement>): number | undefined {
// Returns the stream ID, if any, associated with the textarea:
// The recipient of a message being edited, or the target
// recipient of a message being drafted in the compose box.
@ -123,7 +138,10 @@ function get_stream_id_for_textarea($textarea) {
return compose_state.stream_id();
}
export function warn_if_private_stream_is_linked(linked_stream, $textarea) {
export function warn_if_private_stream_is_linked(
linked_stream: StreamSubscription,
$textarea: JQuery<HTMLTextAreaElement>,
): void {
const stream_id = get_stream_id_for_textarea($textarea);
if (!stream_id) {
@ -184,20 +202,21 @@ export function warn_if_private_stream_is_linked(linked_stream, $textarea) {
}
}
export function warn_if_mentioning_unsubscribed_user(mentioned, $textarea) {
export function warn_if_mentioning_unsubscribed_user(
mentioned: UserOrMention,
$textarea: JQuery<HTMLTextAreaElement>,
): void {
// Disable for Zephyr mirroring realms, since we never have subscriber lists there
if (realm.realm_is_zephyr_mirror_realm) {
return;
}
const user_id = mentioned.user_id;
if (mentioned.is_broadcast) {
return; // don't check if @all/@everyone/@stream
}
const user_id = mentioned.user_id;
const stream_id = get_stream_id_for_textarea($textarea);
if (!stream_id) {
return;
}
@ -239,12 +258,12 @@ export function warn_if_mentioning_unsubscribed_user(mentioned, $textarea) {
// the warning for composing to a resolved topic, if present. Also clears
// the state for whether this warning has already been shown in the
// current narrow.
export function clear_topic_resolved_warning() {
export function clear_topic_resolved_warning(): void {
compose_state.set_recipient_viewed_topic_resolved_banner(false);
$(`#compose_banners .${CSS.escape(compose_banner.CLASSNAMES.topic_resolved)}`).remove();
}
export function warn_if_topic_resolved(topic_changed) {
export function warn_if_topic_resolved(topic_changed: boolean): void {
// This function is called with topic_changed=false on every
// keypress when typing a message, so it should not do anything
// expensive in that case.
@ -297,8 +316,9 @@ export function warn_if_topic_resolved(topic_changed) {
}
}
export function warn_if_in_search_view() {
if (narrow_state.filter() && !narrow_state.filter().supports_collapsing_recipients()) {
export function warn_if_in_search_view(): void {
const filter = narrow_state.filter();
if (filter && !filter.supports_collapsing_recipients()) {
const context = {
banner_type: compose_banner.WARNING,
banner_text: $t({
@ -314,7 +334,7 @@ export function warn_if_in_search_view() {
}
}
function show_stream_wildcard_warnings(opts) {
function show_stream_wildcard_warnings(opts: StreamWildcardOptions): void {
const subscriber_count = peer_data.get_subscriber_count(opts.stream_id) || 0;
const stream_name = sub_store.maybe_get_stream_name(opts.stream_id);
const is_edit_container = opts.$banner_container.closest(".edit_form_banners").length > 0;
@ -357,16 +377,16 @@ function show_stream_wildcard_warnings(opts) {
user_acknowledged_stream_wildcard = false;
}
export function clear_stream_wildcard_warnings($banner_container) {
export function clear_stream_wildcard_warnings($banner_container: JQuery): void {
const classname = compose_banner.CLASSNAMES.wildcard_warning;
$banner_container.find(`.${CSS.escape(classname)}`).remove();
}
export function set_user_acknowledged_stream_wildcard_flag(value) {
export function set_user_acknowledged_stream_wildcard_flag(value: boolean): void {
user_acknowledged_stream_wildcard = value;
}
export function get_invalid_recipient_emails() {
export function get_invalid_recipient_emails(): string[] {
const private_recipients = util.extract_pm_recipients(
compose_state.private_message_recipient(),
);
@ -377,7 +397,10 @@ export function get_invalid_recipient_emails() {
return invalid_recipients;
}
function check_unsubscribed_stream_for_send(stream_name, autosubscribe) {
function check_unsubscribed_stream_for_send(
stream_name: string,
autosubscribe: boolean,
): string | undefined {
let result;
if (!autosubscribe) {
return "not-subscribed";
@ -387,18 +410,19 @@ function check_unsubscribed_stream_for_send(stream_name, autosubscribe) {
// *Synchronously* try to subscribe to the stream before sending
// the message. This is deprecated and we hope to remove it; see
// #4650.
channel.post({
void channel.post({
url: "/json/subscriptions/exists",
data: {stream: stream_name, autosubscribe: true},
async: false,
success(data) {
if (data.subscribed) {
const clean_data = server_subscription_exists_schema.parse(data);
if (clean_data.subscribed) {
result = "subscribed";
} else {
result = "not-subscribed";
}
},
error(xhr) {
error(xhr: JQuery.jqXHR) {
if (xhr.status === 404) {
result = "does-not-exist";
} else {
@ -409,14 +433,18 @@ function check_unsubscribed_stream_for_send(stream_name, autosubscribe) {
return result;
}
function is_recipient_large_stream() {
return (
compose_state.stream_id() &&
peer_data.get_subscriber_count(compose_state.stream_id()) > wildcard_mention_threshold
);
function is_recipient_large_stream(): boolean {
const stream_id = compose_state.stream_id();
if (stream_id === undefined) {
return false;
}
return peer_data.get_subscriber_count(stream_id) > wildcard_mention_threshold;
}
export function topic_participant_count_more_than_threshold(stream_id, topic) {
export function topic_participant_count_more_than_threshold(
stream_id: number,
topic: string,
): boolean {
// Topic participants:
// Users who either sent or reacted to the messages in the topic.
const participant_ids = new Set();
@ -455,17 +483,15 @@ export function topic_participant_count_more_than_threshold(stream_id, topic) {
return false;
}
function is_recipient_large_topic() {
return (
compose_state.stream_id() &&
topic_participant_count_more_than_threshold(
compose_state.stream_id(),
compose_state.topic(),
)
);
function is_recipient_large_topic(): boolean {
const stream_id = compose_state.stream_id();
if (stream_id === undefined) {
return false;
}
return topic_participant_count_more_than_threshold(stream_id, compose_state.topic());
}
function wildcard_mention_policy_authorizes_user() {
function wildcard_mention_policy_authorizes_user(): boolean {
if (
realm.realm_wildcard_mention_policy ===
settings_config.wildcard_mention_policy_values.by_everyone.code
@ -500,8 +526,8 @@ function wildcard_mention_policy_authorizes_user() {
return true;
}
const person = people.get_by_user_id(current_user.user_id);
const current_datetime = new Date(Date.now());
const person_date_joined = new Date(person.date_joined);
const current_datetime = new Date(Date.now()).getTime();
const person_date_joined = new Date(person.date_joined).getTime();
const days = (current_datetime - person_date_joined) / 1000 / 86400;
return days >= realm.realm_waiting_period_threshold && !current_user.is_guest;
@ -509,19 +535,19 @@ function wildcard_mention_policy_authorizes_user() {
return !current_user.is_guest;
}
export function stream_wildcard_mention_allowed() {
export function stream_wildcard_mention_allowed(): boolean {
return !is_recipient_large_stream() || wildcard_mention_policy_authorizes_user();
}
export function topic_wildcard_mention_allowed() {
export function topic_wildcard_mention_allowed(): boolean {
return !is_recipient_large_topic() || wildcard_mention_policy_authorizes_user();
}
export function set_wildcard_mention_threshold(value) {
export function set_wildcard_mention_threshold(value: number): void {
wildcard_mention_threshold = value;
}
export function validate_stream_message_mentions(opts) {
export function validate_stream_message_mentions(opts: StreamWildcardOptions): boolean {
const subscriber_count = peer_data.get_subscriber_count(opts.stream_id) || 0;
// If the user is attempting to do a wildcard mention in a large
@ -558,7 +584,7 @@ export function validate_stream_message_mentions(opts) {
return true;
}
export function validation_error(error_type, stream_name) {
export function validation_error(error_type: string, stream_name: string): boolean {
const $banner_container = $("#compose_banners");
switch (error_type) {
case "does-not-exist":
@ -580,6 +606,8 @@ export function validation_error(error_type, stream_name) {
return false;
}
const sub = stream_data.get_sub(stream_name);
// We expect this to be a does-not-exist error if it was undefined.
assert(sub !== undefined);
const new_row_html = render_compose_banner({
banner_type: compose_banner.ERROR,
banner_text: $t({
@ -601,16 +629,17 @@ export function validation_error(error_type, stream_name) {
return true;
}
export function validate_stream_message_address_info(stream_name) {
export function validate_stream_message_address_info(stream_name: string): boolean {
if (stream_data.is_subscribed_by_name(stream_name)) {
return true;
}
const autosubscribe = page_params.narrow_stream !== undefined;
const error_type = check_unsubscribed_stream_for_send(stream_name, autosubscribe);
assert(error_type !== undefined);
return validation_error(error_type, stream_name);
}
function validate_stream_message(scheduling_message) {
function validate_stream_message(scheduling_message: boolean): boolean {
const stream_id = compose_state.stream_id();
const $banner_container = $("#compose_banners");
if (stream_id === undefined) {
@ -640,7 +669,7 @@ function validate_stream_message(scheduling_message) {
const sub = stream_data.get_sub_by_id(stream_id);
if (!sub) {
return validation_error("does-not-exist", stream_id);
return validation_error("does-not-exist", stream_id.toString());
}
if (!stream_data.can_post_messages_in_stream(sub)) {
@ -675,7 +704,7 @@ function validate_stream_message(scheduling_message) {
// The function checks whether the recipients are users of the realm or cross realm users (bots
// for now)
function validate_private_message() {
function validate_private_message(): boolean {
const user_ids = compose_pm_pill.get_user_ids();
const $banner_container = $("#compose_banners");
@ -744,7 +773,7 @@ function validate_private_message() {
return true;
}
export function check_overflow_text() {
export function check_overflow_text(): number {
// This function is called when typing every character in the
// compose box, so it's important that it not doing anything
// expensive.
@ -795,7 +824,7 @@ export function check_overflow_text() {
return text.length;
}
export function validate_message_length() {
export function validate_message_length(): boolean {
if (compose_state.message_content().length > realm.max_message_length) {
$("textarea#compose-textarea").addClass("flash");
setTimeout(() => $("textarea#compose-textarea").removeClass("flash"), 1500);
@ -804,7 +833,7 @@ export function validate_message_length() {
return true;
}
export function validate(scheduling_message) {
export function validate(scheduling_message: boolean): boolean {
const message_content = compose_state.message_content();
if (/^\s*$/.test(message_content)) {
$("textarea#compose-textarea").toggleClass("invalid", true);
@ -832,7 +861,11 @@ export function validate(scheduling_message) {
return validate_stream_message(scheduling_message);
}
export function convert_mentions_to_silent_in_direct_messages(mention_text, full_name, user_id) {
export function convert_mentions_to_silent_in_direct_messages(
mention_text: string,
full_name: string,
user_id: number,
): string {
if (compose_state.get_message_type() !== "private") {
return mention_text;
}

View File

@ -106,6 +106,7 @@ export const realm_schema = z.object({
max_avatar_file_size_mib: z.number(),
max_icon_file_size_mib: z.number(),
max_logo_file_size_mib: z.number(),
max_message_length: z.number(),
max_topic_length: z.number(),
realm_add_custom_emoji_policy: z.number(),
realm_allow_edit_history: z.boolean(),
@ -150,6 +151,7 @@ export const realm_schema = z.object({
realm_jitsi_server_url: z.nullable(z.string()),
realm_logo_source: z.string(),
realm_logo_url: z.string(),
realm_mandatory_topics: z.boolean(),
realm_move_messages_between_streams_policy: z.number(),
realm_name_changes_disabled: z.boolean(),
realm_name: z.string(),
@ -166,6 +168,7 @@ export const realm_schema = z.object({
realm_user_group_edit_policy: z.number(),
realm_video_chat_provider: z.number(),
realm_waiting_period_threshold: z.number(),
realm_wildcard_mention_policy: z.number(),
server_avatar_changes_disabled: z.boolean(),
server_jitsi_server_url: z.nullable(z.string()),
server_name_changes_disabled: z.boolean(),