2021-03-11 05:43:45 +01:00
|
|
|
import $ from "jquery";
|
2024-02-10 19:35:11 +01:00
|
|
|
import assert from "minimalistic-assert";
|
2021-03-11 05:43:45 +01:00
|
|
|
|
2021-02-28 01:25:24 +01:00
|
|
|
import render_typing_notifications from "../templates/typing_notifications.hbs";
|
2020-08-01 03:43:15 +02:00
|
|
|
|
2021-02-28 01:25:24 +01:00
|
|
|
import * as narrow_state from "./narrow_state";
|
|
|
|
import * as people from "./people";
|
2024-02-13 02:08:24 +01:00
|
|
|
import {current_user, realm} from "./state_data";
|
2021-02-28 01:25:24 +01:00
|
|
|
import * as typing_data from "./typing_data";
|
2020-08-20 21:24:06 +02:00
|
|
|
|
2017-11-08 17:55:36 +01:00
|
|
|
// See docs/subsystems/typing-indicators.md for details on typing indicators.
|
2017-09-25 20:33:29 +02:00
|
|
|
|
2017-03-22 15:11:41 +01:00
|
|
|
// This code handles the inbound side of typing notifications.
|
|
|
|
// When another user is typing, we process the events here.
|
|
|
|
//
|
|
|
|
// We also handle the local event of re-narrowing.
|
2024-02-10 19:50:32 +01:00
|
|
|
// (For the outbound code, see typing.ts.)
|
2017-03-22 15:11:41 +01:00
|
|
|
|
2021-01-06 20:22:28 +01:00
|
|
|
// If number of users typing exceed this,
|
|
|
|
// we render "Several people are typing..."
|
|
|
|
const MAX_USERS_TO_DISPLAY_NAME = 3;
|
|
|
|
|
2024-02-10 19:50:32 +01:00
|
|
|
// Note!: There are also timing constants in typing_status.ts
|
2017-03-22 15:11:41 +01:00
|
|
|
// that make typing indicators work.
|
|
|
|
|
2024-02-10 19:35:11 +01:00
|
|
|
type UserInfo = {
|
|
|
|
email: string;
|
|
|
|
user_id: number;
|
|
|
|
};
|
|
|
|
|
|
|
|
type TypingEvent = {
|
|
|
|
id: number;
|
|
|
|
op: "start" | "stop";
|
|
|
|
type: "typing";
|
|
|
|
} & (
|
|
|
|
| {
|
|
|
|
message_type: "stream";
|
|
|
|
sender: UserInfo;
|
|
|
|
stream_id: number;
|
|
|
|
topic: string;
|
|
|
|
}
|
|
|
|
| {
|
|
|
|
message_type: "direct";
|
|
|
|
recipients: UserInfo[];
|
|
|
|
sender: UserInfo;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
function get_users_typing_for_narrow(): number[] {
|
2024-01-08 05:41:52 +01:00
|
|
|
if (narrow_state.narrowed_by_topic_reply()) {
|
2024-02-10 19:03:45 +01:00
|
|
|
const current_stream_id = narrow_state.stream_id();
|
|
|
|
const current_topic = narrow_state.topic();
|
2024-02-22 22:09:46 +01:00
|
|
|
if (current_stream_id === undefined) {
|
|
|
|
// narrowed to a stream which doesn't exist.
|
|
|
|
return [];
|
|
|
|
}
|
2024-02-10 19:35:11 +01:00
|
|
|
assert(current_topic !== undefined);
|
2024-02-10 19:03:45 +01:00
|
|
|
return typing_data.get_topic_typists(current_stream_id, current_topic);
|
2021-01-04 16:21:54 +01:00
|
|
|
}
|
|
|
|
|
2017-04-25 15:25:31 +02:00
|
|
|
if (!narrow_state.narrowed_to_pms()) {
|
2021-01-04 16:21:54 +01:00
|
|
|
// Narrow is neither "dm:" nor "is:dm" nor topic.
|
2017-03-22 15:11:41 +01:00
|
|
|
return [];
|
|
|
|
}
|
2017-04-25 15:25:31 +02:00
|
|
|
|
2024-01-11 13:23:11 +01:00
|
|
|
const terms = narrow_state.search_terms();
|
2024-05-30 19:08:00 +02:00
|
|
|
if (terms[0] === undefined) {
|
2024-01-11 13:23:11 +01:00
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
const first_term = terms[0];
|
2023-04-11 21:04:33 +02:00
|
|
|
if (first_term.operator === "dm") {
|
2017-03-22 15:11:41 +01:00
|
|
|
// Get list of users typing in this conversation
|
2019-11-02 00:06:25 +01:00
|
|
|
const narrow_emails_string = first_term.operand;
|
2017-03-22 15:11:41 +01:00
|
|
|
// TODO: Create people.emails_strings_to_user_ids.
|
2019-11-02 00:06:25 +01:00
|
|
|
const narrow_user_ids_string = people.reply_to_to_user_ids_string(narrow_emails_string);
|
2017-06-15 17:32:13 +02:00
|
|
|
if (!narrow_user_ids_string) {
|
|
|
|
return [];
|
|
|
|
}
|
2020-07-15 00:34:28 +02:00
|
|
|
const narrow_user_ids = narrow_user_ids_string
|
|
|
|
.split(",")
|
2020-10-07 09:17:30 +02:00
|
|
|
.map((user_id_string) => Number.parseInt(user_id_string, 10));
|
2024-02-13 02:08:16 +01:00
|
|
|
const group = [...narrow_user_ids, current_user.user_id];
|
2017-03-22 15:11:41 +01:00
|
|
|
return typing_data.get_group_typists(group);
|
|
|
|
}
|
2021-03-23 18:13:08 +01:00
|
|
|
// Get all users typing (in all direct message conversations with current user)
|
|
|
|
return typing_data.get_all_direct_message_typists();
|
2017-03-22 15:11:41 +01:00
|
|
|
}
|
|
|
|
|
2024-02-10 19:35:11 +01:00
|
|
|
export function render_notifications_for_narrow(): void {
|
2019-11-02 00:06:25 +01:00
|
|
|
const user_ids = get_users_typing_for_narrow();
|
2023-12-05 16:06:43 +01:00
|
|
|
const users_typing = user_ids
|
|
|
|
.map((user_id) => people.get_user_by_id_assert_valid(user_id))
|
|
|
|
.filter((person) => !person.is_inaccessible_user);
|
2021-01-06 20:22:28 +01:00
|
|
|
const num_of_users_typing = users_typing.length;
|
|
|
|
|
|
|
|
if (num_of_users_typing === 0) {
|
2020-07-15 01:29:15 +02:00
|
|
|
$("#typing_notifications").hide();
|
2017-03-22 15:11:41 +01:00
|
|
|
} else {
|
2021-01-06 20:22:28 +01:00
|
|
|
$("#typing_notifications").html(
|
|
|
|
render_typing_notifications({
|
|
|
|
users: users_typing,
|
2021-07-28 22:17:45 +02:00
|
|
|
several_users: num_of_users_typing > MAX_USERS_TO_DISPLAY_NAME,
|
2021-01-06 20:22:28 +01:00
|
|
|
}),
|
|
|
|
);
|
2020-07-15 01:29:15 +02:00
|
|
|
$("#typing_notifications").show();
|
2017-03-22 15:11:41 +01:00
|
|
|
}
|
2021-02-28 01:25:24 +01:00
|
|
|
}
|
2017-03-22 15:11:41 +01:00
|
|
|
|
2024-02-10 19:35:11 +01:00
|
|
|
function get_key(event: TypingEvent): string {
|
2024-02-10 18:22:36 +01:00
|
|
|
if (event.message_type === "stream") {
|
|
|
|
return typing_data.get_topic_key(event.stream_id, event.topic);
|
2021-01-04 16:21:54 +01:00
|
|
|
}
|
2024-02-10 18:22:36 +01:00
|
|
|
if (event.message_type === "direct") {
|
|
|
|
const recipients = event.recipients.map((user) => user.user_id);
|
|
|
|
recipients.sort();
|
|
|
|
return typing_data.get_direct_message_conversation_key(recipients);
|
|
|
|
}
|
|
|
|
throw new Error("Invalid typing notification type", event);
|
2021-01-04 16:21:54 +01:00
|
|
|
}
|
2017-03-22 15:11:41 +01:00
|
|
|
|
2024-02-10 19:35:11 +01:00
|
|
|
export function hide_notification(event: TypingEvent): void {
|
2021-01-04 16:21:54 +01:00
|
|
|
const key = get_key(event);
|
|
|
|
typing_data.clear_inbound_timer(key);
|
2017-03-22 15:11:41 +01:00
|
|
|
|
2021-01-04 16:21:54 +01:00
|
|
|
const removed = typing_data.remove_typist(key, event.sender.user_id);
|
2017-03-22 15:11:41 +01:00
|
|
|
|
|
|
|
if (removed) {
|
2021-02-28 01:25:24 +01:00
|
|
|
render_notifications_for_narrow();
|
2017-03-22 15:11:41 +01:00
|
|
|
}
|
2021-02-28 01:25:24 +01:00
|
|
|
}
|
2017-03-22 15:11:41 +01:00
|
|
|
|
2024-02-10 19:35:11 +01:00
|
|
|
export function display_notification(event: TypingEvent): void {
|
2019-11-02 00:06:25 +01:00
|
|
|
const sender_id = event.sender.user_id;
|
2017-03-22 15:11:41 +01:00
|
|
|
|
2021-01-04 16:21:54 +01:00
|
|
|
const key = get_key(event);
|
|
|
|
typing_data.add_typist(key, sender_id);
|
2017-03-22 15:11:41 +01:00
|
|
|
|
2021-02-28 01:25:24 +01:00
|
|
|
render_notifications_for_narrow();
|
2017-03-22 16:20:16 +01:00
|
|
|
|
2023-12-31 01:12:04 +01:00
|
|
|
typing_data.kickstart_inbound_timer(
|
|
|
|
key,
|
2024-02-13 02:08:24 +01:00
|
|
|
realm.server_typing_started_expiry_period_milliseconds,
|
2023-12-31 01:12:04 +01:00
|
|
|
() => {
|
|
|
|
hide_notification(event);
|
|
|
|
},
|
|
|
|
);
|
2021-02-28 01:25:24 +01:00
|
|
|
}
|
2024-04-12 19:30:29 +02:00
|
|
|
|
|
|
|
export function disable_typing_notification(): void {
|
|
|
|
typing_data.clear_typing_data();
|
|
|
|
render_notifications_for_narrow();
|
|
|
|
}
|