Files
zulip/static/js/recent_senders.js
Anders Kaseorg 6f764ce4b3 message_list: Downgrade message_list.all to MessageListData.
This data structure has never been one that we actually render into
the DOM; instead, its role is to support clicking into view that
contain muted streams and topics quickly.

This downgrade makes that situation much more explicit, and is also
useful refactoring to help simpify the upcoming changes in #16746.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-03-30 08:33:47 -07:00

142 lines
5.3 KiB
JavaScript

import {FoldDict} from "./fold_dict";
import * as message_util from "./message_util";
// topic_senders[stream_id][topic_id][sender_id] = latest_message_id
const topic_senders = new Map();
// topic_senders[stream_id][sender_id] = latest_message_id
const stream_senders = new Map();
export function clear_for_testing() {
topic_senders.clear();
stream_senders.clear();
}
export function process_message_for_senders(message) {
const stream_id = message.stream_id;
const topic = message.topic;
// Process most recent sender to topic
const topic_dict = topic_senders.get(stream_id) || new FoldDict();
const topic_sender_message_ids = topic_dict.get(topic) || new Map();
let old_message_id = topic_sender_message_ids.get(message.sender_id);
if (old_message_id === undefined || old_message_id < message.id) {
topic_sender_message_ids.set(message.sender_id, message.id);
}
topic_dict.set(topic, topic_sender_message_ids);
topic_senders.set(stream_id, topic_dict);
// Process most recent sender to whole stream
const sender_message_ids = stream_senders.get(stream_id) || new Map();
old_message_id = sender_message_ids.get(message.sender_id);
if (old_message_id === undefined || old_message_id < message.id) {
sender_message_ids.set(message.sender_id, message.id);
}
stream_senders.set(stream_id, sender_message_ids);
}
export function process_topic_edit(old_stream_id, old_topic, new_topic, new_stream_id) {
// When topic-editing occurs, we need to update the set of known
// senders in each stream/topic pair. This is complicated by the
// fact that the event we receive from the server does not
// communicate which senders were present before-and-after; so our
// strategy is to just rebuild the data structure for the topic
// from message_store data.
// This removes the old topic_dict
const old_topic_dict = topic_senders.get(old_stream_id);
old_topic_dict.delete(old_topic);
// Re-processing every message in both the old and new topics is
// expensive. It also potentially loses data, because
// `all_messages_data` only has contiguous message history, not
// the complete set of message IDs we've received to the
// `message_store` from the server (E.g. from when we narrowed to
// a stream). But it's the most correct implementation we can
// sensibly do with existing data structures.
const old_topic_msgs = message_util.get_messages_in_topic(old_stream_id, old_topic);
for (const msg of old_topic_msgs) {
process_message_for_senders(msg);
}
// use new_stream_id if topic was moved to a new stream,
// otherwise we just use old_stream_id, implying that
// just topic was renamed.
new_stream_id = new_stream_id || old_stream_id;
const new_topic_msgs = message_util.get_messages_in_topic(new_stream_id, new_topic);
for (const msg of new_topic_msgs) {
process_message_for_senders(msg);
}
// Note that we don't delete anything from stream_senders here.
// Our view is that it's probably better to not do so; users who
// recently posted to a stream are relevant for typeahead even if
// the messages were moved to another stream or deleted.
}
export function update_topics_of_deleted_message_ids(message_ids) {
const topics_to_update = message_util.get_topics_for_message_ids(message_ids);
for (const [stream_id, topic] of topics_to_update.values()) {
const topic_dict = topic_senders.get(stream_id);
topic_dict.delete(topic);
const topic_msgs = message_util.get_messages_in_topic(stream_id, topic);
for (const msg of topic_msgs) {
process_message_for_senders(msg);
}
}
}
export function compare_by_recency(user_a, user_b, stream_id, topic) {
let a_message_id;
let b_message_id;
const topic_dict = topic_senders.get(stream_id);
if (topic !== undefined && topic_dict !== undefined) {
const sender_message_ids = topic_dict.get(topic);
if (sender_message_ids !== undefined) {
b_message_id = sender_message_ids.get(user_b.user_id) || Number.NEGATIVE_INFINITY;
a_message_id = sender_message_ids.get(user_a.user_id) || Number.NEGATIVE_INFINITY;
if (a_message_id !== b_message_id) {
return b_message_id - a_message_id;
}
}
}
// Check recency for whole stream as tiebreaker
const stream_dict = stream_senders.get(stream_id);
if (stream_dict !== undefined) {
b_message_id = stream_dict.get(user_b.user_id) || Number.NEGATIVE_INFINITY;
a_message_id = stream_dict.get(user_a.user_id) || Number.NEGATIVE_INFINITY;
if (a_message_id !== b_message_id) {
return b_message_id - a_message_id;
}
}
return 0;
}
export function get_topic_recent_senders(stream_id, topic) {
const topic_dict = topic_senders.get(stream_id);
if (topic_dict === undefined) {
return [];
}
const sender_message_ids = topic_dict.get(topic);
if (sender_message_ids === undefined) {
return [];
}
const sorted_senders = Array.from(sender_message_ids.entries()).sort((s1, s2) => s1[1] - s2[1]);
const recent_senders = [];
for (const item of sorted_senders) {
recent_senders.push(item[0]);
}
return recent_senders;
}