2021-03-11 05:43:45 +01:00
|
|
|
import $ from "jquery";
|
|
|
|
|
2024-11-12 03:59:37 +01:00
|
|
|
import * as typing_status from "../shared/src/typing_status.ts";
|
|
|
|
import type {Recipient} from "../shared/src/typing_status.ts";
|
2020-08-01 03:43:15 +02:00
|
|
|
|
2024-11-12 03:59:37 +01:00
|
|
|
import * as blueslip from "./blueslip.ts";
|
|
|
|
import * as channel from "./channel.ts";
|
|
|
|
import * as compose_pm_pill from "./compose_pm_pill.ts";
|
|
|
|
import * as compose_state from "./compose_state.ts";
|
|
|
|
import * as people from "./people.ts";
|
|
|
|
import {realm} from "./state_data.ts";
|
|
|
|
import * as stream_data from "./stream_data.ts";
|
|
|
|
import {user_settings} from "./user_settings.ts";
|
2020-08-20 21:24:06 +02:00
|
|
|
|
2024-01-23 22:15:42 +01:00
|
|
|
type TypingAPIRequest = {op: "start" | "stop"} & (
|
|
|
|
| {
|
2024-02-07 06:50:40 +01:00
|
|
|
type: string;
|
2024-01-23 22:15:42 +01:00
|
|
|
to: string;
|
|
|
|
}
|
|
|
|
| {
|
|
|
|
type: string;
|
|
|
|
stream_id: string;
|
|
|
|
topic: string;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2017-03-22 15:11:41 +01:00
|
|
|
// This module handles the outbound side of typing indicators.
|
|
|
|
// We detect changes in the compose box and notify the server
|
2024-02-10 19:50:32 +01:00
|
|
|
// when we are typing. For the inbound side see typing_events.ts.
|
2023-08-17 14:42:41 +02:00
|
|
|
// See docs/subsystems/typing-indicators.md for more details.
|
|
|
|
|
2024-01-23 22:15:42 +01:00
|
|
|
function send_typing_notification_ajax(data: TypingAPIRequest): void {
|
|
|
|
void channel.post({
|
2020-07-15 01:29:15 +02:00
|
|
|
url: "/json/typing",
|
2020-12-28 20:34:17 +01:00
|
|
|
data,
|
2020-07-20 22:18:43 +02:00
|
|
|
error(xhr) {
|
2023-07-18 20:19:22 +02:00
|
|
|
if (xhr.readyState !== 0) {
|
|
|
|
blueslip.warn("Failed to send typing event: " + xhr.responseText);
|
|
|
|
}
|
2016-10-12 20:57:59 +02:00
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-01-23 22:15:42 +01:00
|
|
|
function send_direct_message_typing_notification(
|
|
|
|
user_ids_array: number[],
|
|
|
|
operation: "start" | "stop",
|
|
|
|
): void {
|
2020-12-28 20:34:17 +01:00
|
|
|
const data = {
|
|
|
|
to: JSON.stringify(user_ids_array),
|
2024-02-07 06:50:40 +01:00
|
|
|
type: "direct",
|
2020-12-28 20:34:17 +01:00
|
|
|
op: operation,
|
|
|
|
};
|
|
|
|
send_typing_notification_ajax(data);
|
|
|
|
}
|
|
|
|
|
2024-01-23 22:15:42 +01:00
|
|
|
function send_stream_typing_notification(
|
|
|
|
stream_id: number,
|
|
|
|
topic: string,
|
|
|
|
operation: "start" | "stop",
|
|
|
|
): void {
|
2020-12-28 20:34:17 +01:00
|
|
|
const data = {
|
|
|
|
type: "stream",
|
2023-10-12 09:13:34 +02:00
|
|
|
stream_id: JSON.stringify(stream_id),
|
2020-12-28 20:34:17 +01:00
|
|
|
topic,
|
|
|
|
op: operation,
|
|
|
|
};
|
|
|
|
send_typing_notification_ajax(data);
|
|
|
|
}
|
|
|
|
|
2024-01-23 22:15:42 +01:00
|
|
|
function send_typing_notification_based_on_message_type(
|
|
|
|
to: Recipient,
|
|
|
|
operation: "start" | "stop",
|
|
|
|
): void {
|
2024-01-23 19:46:07 +01:00
|
|
|
if (to.message_type === "direct" && user_settings.send_private_typing_notifications) {
|
|
|
|
send_direct_message_typing_notification(to.ids, operation);
|
|
|
|
} else if (to.message_type === "stream" && user_settings.send_stream_typing_notifications) {
|
2020-12-28 20:34:17 +01:00
|
|
|
send_stream_typing_notification(to.stream_id, to.topic, operation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-23 22:15:42 +01:00
|
|
|
function get_user_ids_array(): number[] | null {
|
2019-11-02 00:06:25 +01:00
|
|
|
const user_ids_string = compose_pm_pill.get_user_ids_string();
|
2018-08-25 17:22:48 +02:00
|
|
|
if (user_ids_string === "") {
|
2019-10-22 02:52:01 +02:00
|
|
|
return null;
|
2017-03-22 00:41:09 +01:00
|
|
|
}
|
2019-06-06 21:54:55 +02:00
|
|
|
|
|
|
|
return people.user_ids_string_to_ids_array(user_ids_string);
|
2017-03-22 00:41:09 +01:00
|
|
|
}
|
2016-10-12 20:57:59 +02:00
|
|
|
|
2024-01-23 22:15:42 +01:00
|
|
|
function is_valid_conversation(): boolean {
|
2019-11-02 00:06:25 +01:00
|
|
|
const compose_empty = !compose_state.has_message_content();
|
2017-03-22 00:41:09 +01:00
|
|
|
if (compose_empty) {
|
|
|
|
return false;
|
2016-10-12 20:57:59 +02:00
|
|
|
}
|
2017-03-22 00:41:09 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-01-23 22:15:42 +01:00
|
|
|
function get_current_time(): number {
|
2020-12-22 11:54:49 +01:00
|
|
|
return Date.now();
|
2016-10-12 20:57:59 +02:00
|
|
|
}
|
|
|
|
|
2024-01-23 22:15:42 +01:00
|
|
|
function notify_server_start(to: Recipient): void {
|
2020-12-28 20:34:17 +01:00
|
|
|
send_typing_notification_based_on_message_type(to, "start");
|
2017-03-22 00:41:09 +01:00
|
|
|
}
|
|
|
|
|
2024-01-23 22:15:42 +01:00
|
|
|
function notify_server_stop(to: Recipient): void {
|
2020-12-28 20:34:17 +01:00
|
|
|
send_typing_notification_based_on_message_type(to, "stop");
|
2017-03-22 00:41:09 +01:00
|
|
|
}
|
|
|
|
|
2024-01-23 22:15:42 +01:00
|
|
|
export function get_recipient(): Recipient | null {
|
2020-12-28 20:34:17 +01:00
|
|
|
const message_type = compose_state.get_message_type();
|
|
|
|
if (message_type === "private") {
|
2024-10-15 16:35:34 +02:00
|
|
|
const user_ids = get_user_ids_array();
|
|
|
|
// compose box with no valid user pills.
|
|
|
|
if (user_ids === null) {
|
|
|
|
return null;
|
|
|
|
}
|
2024-01-23 19:46:07 +01:00
|
|
|
return {
|
|
|
|
message_type: "direct",
|
2024-02-11 18:25:56 +01:00
|
|
|
notification_event_type: "typing",
|
2024-10-15 16:35:34 +02:00
|
|
|
ids: user_ids,
|
2024-01-23 19:46:07 +01:00
|
|
|
};
|
2020-12-28 20:34:17 +01:00
|
|
|
}
|
|
|
|
if (message_type === "stream") {
|
|
|
|
const stream_name = compose_state.stream_name();
|
2024-02-22 21:41:16 +01:00
|
|
|
const stream_id = stream_data.get_stream_id(stream_name);
|
|
|
|
if (stream_id === undefined) {
|
|
|
|
// compose box with no stream selected.
|
|
|
|
return null;
|
|
|
|
}
|
2020-12-28 20:34:17 +01:00
|
|
|
const topic = compose_state.topic();
|
2024-10-16 13:37:36 +02:00
|
|
|
if (realm.realm_mandatory_topics && topic === "") {
|
|
|
|
// compose box with empty topic string.
|
|
|
|
return null;
|
|
|
|
}
|
2024-01-23 19:46:07 +01:00
|
|
|
return {
|
|
|
|
message_type: "stream",
|
2024-02-11 18:25:56 +01:00
|
|
|
notification_event_type: "typing",
|
2024-01-23 19:46:07 +01:00
|
|
|
stream_id,
|
|
|
|
topic,
|
|
|
|
};
|
2020-12-28 20:34:17 +01:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2021-02-10 17:08:06 +01:00
|
|
|
|
2024-01-23 22:15:42 +01:00
|
|
|
export function initialize(): void {
|
2019-11-02 00:06:25 +01:00
|
|
|
const worker = {
|
2020-07-20 22:18:43 +02:00
|
|
|
get_current_time,
|
|
|
|
notify_server_start,
|
|
|
|
notify_server_stop,
|
2018-03-16 12:39:46 +01:00
|
|
|
};
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
$(document).on("input", "#compose-textarea", () => {
|
2018-03-16 12:39:46 +01:00
|
|
|
// If our previous state was no typing notification, send a
|
|
|
|
// start-typing notice immediately.
|
2021-02-10 17:08:06 +01:00
|
|
|
const new_recipient = is_valid_conversation() ? get_recipient() : null;
|
2023-08-17 14:42:41 +02:00
|
|
|
typing_status.update(
|
|
|
|
worker,
|
|
|
|
new_recipient,
|
2024-02-13 02:08:24 +01:00
|
|
|
realm.server_typing_started_wait_period_milliseconds,
|
|
|
|
realm.server_typing_stopped_wait_period_milliseconds,
|
2023-08-17 14:42:41 +02:00
|
|
|
);
|
2018-03-16 12:39:46 +01:00
|
|
|
});
|
2017-03-22 00:41:09 +01:00
|
|
|
|
2018-03-16 12:39:46 +01:00
|
|
|
// We send a stop-typing notification immediately when compose is
|
|
|
|
// closed/cancelled
|
2020-07-15 01:29:15 +02:00
|
|
|
$(document).on("compose_canceled.zulip compose_finished.zulip", () => {
|
2023-12-31 01:11:08 +01:00
|
|
|
typing_status.update(
|
|
|
|
worker,
|
|
|
|
null,
|
2024-02-13 02:08:24 +01:00
|
|
|
realm.server_typing_started_wait_period_milliseconds,
|
|
|
|
realm.server_typing_stopped_wait_period_milliseconds,
|
2023-12-31 01:11:08 +01:00
|
|
|
);
|
2018-03-16 12:39:46 +01:00
|
|
|
});
|
2021-02-10 17:08:06 +01:00
|
|
|
}
|