zulip/web/src/typing_data.ts
opmkumar 2a15da47d9 message_edit: Show typing indicator for message editing.
This commit adds typing indicators for message editing in stream
as well as in dm, if the send typing notification
for corresponding is enabled.

Based on earlier work in #28585.

Co-authored-by: Rohan Gudimetla <rohan.gudimetla07@gmail.com>

Fixes #25719.
2025-02-12 15:08:56 -08:00

109 lines
3.3 KiB
TypeScript

import * as muted_users from "./muted_users.ts";
import * as util from "./util.ts";
// See docs/subsystems/typing-indicators.md for details on typing indicators.
const typists_dict = new Map<string, number[]>();
const edit_message_typing_ids = new Set<number>();
const inbound_timer_dict = new Map<string, ReturnType<typeof setInterval> | undefined>();
export function clear_for_testing(): void {
typists_dict.clear();
inbound_timer_dict.clear();
}
export function get_direct_message_conversation_key(group: number[]): string {
const ids = util.sorted_ids(group);
return "direct:" + ids.join(",");
}
export function get_topic_key(stream_id: number, topic: string): string {
topic = topic.toLowerCase(); // Topics are case-insensitive
return "topic:" + JSON.stringify({stream_id, topic});
}
export function add_typist(key: string, typist: number): void {
const current = typists_dict.get(key) ?? [];
if (!current.includes(typist)) {
current.push(typist);
}
typists_dict.set(key, util.sorted_ids(current));
}
export function remove_typist(key: string, typist: number): boolean {
let current = typists_dict.get(key) ?? [];
if (!current.includes(typist)) {
return false;
}
current = current.filter((user_id) => user_id !== typist);
typists_dict.set(key, current);
return true;
}
export function get_group_typists(group: number[]): number[] {
const key = get_direct_message_conversation_key(group);
const user_ids = typists_dict.get(key) ?? [];
return muted_users.filter_muted_user_ids(user_ids);
}
export function get_all_direct_message_typists(): number[] {
let typists: number[] = [];
for (const [key, value] of typists_dict) {
if (key.startsWith("direct:")) {
typists.push(...value);
}
}
typists = util.sorted_ids(typists);
return muted_users.filter_muted_user_ids(typists);
}
export function get_topic_typists(stream_id: number, topic: string): number[] {
const typists = typists_dict.get(get_topic_key(stream_id, topic)) ?? [];
return muted_users.filter_muted_user_ids(typists);
}
export function clear_typing_data(): void {
for (const [, timer] of inbound_timer_dict.entries()) {
clearTimeout(timer);
}
inbound_timer_dict.clear();
typists_dict.clear();
}
export function add_edit_message_typing_id(message_id: number): void {
if (!edit_message_typing_ids.has(message_id)) {
edit_message_typing_ids.add(message_id);
}
}
export function remove_edit_message_typing_id(message_id: number): boolean {
if (!edit_message_typing_ids.has(message_id)) {
return false;
}
edit_message_typing_ids.delete(message_id);
return true;
}
export function is_message_editing(message_id: number): boolean {
return edit_message_typing_ids.has(message_id);
}
// The next functions aren't pure data, but it is easy
// enough to mock the setTimeout/clearTimeout functions.
export function clear_inbound_timer(key: string): void {
const timer = inbound_timer_dict.get(key);
if (timer) {
clearTimeout(timer);
inbound_timer_dict.set(key, undefined);
}
}
export function kickstart_inbound_timer(key: string, delay: number, callback: () => void): void {
clear_inbound_timer(key);
const timer = setTimeout(callback, delay);
inbound_timer_dict.set(key, timer);
}