2020-04-08 13:59:56 +02:00
|
|
|
const render_recent_topics_body = require('../templates/recent_topics_table.hbs');
|
2020-05-22 08:16:08 +02:00
|
|
|
const render_recent_topic_row = require('../templates/recent_topic_row.hbs');
|
2020-03-21 14:42:10 +01:00
|
|
|
const topics = new Map(); // Key is stream-id:topic.
|
2020-05-22 21:04:03 +02:00
|
|
|
// Sets the number of avatars to display.
|
|
|
|
// Rest of the avatars, if present, are displayed as {+x}
|
|
|
|
const MAX_AVATAR = 4;
|
2020-05-23 09:04:51 +02:00
|
|
|
let filters = new Set();
|
2020-03-21 14:42:10 +01:00
|
|
|
|
|
|
|
exports.process_messages = function (messages) {
|
2020-05-22 08:16:08 +02:00
|
|
|
// Since a complete re-render is expensive, we
|
|
|
|
// only do it if there are more than 5 messages
|
|
|
|
// to process.
|
|
|
|
let do_inplace_rerender = true;
|
|
|
|
if (messages.length > 5) {
|
|
|
|
do_inplace_rerender = false;
|
|
|
|
}
|
2020-03-21 14:42:10 +01:00
|
|
|
for (const msg of messages) {
|
2020-05-22 08:16:08 +02:00
|
|
|
exports.process_message(msg, do_inplace_rerender);
|
|
|
|
}
|
|
|
|
if (!do_inplace_rerender) {
|
|
|
|
exports.complete_rerender();
|
2020-03-21 14:42:10 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-05-22 08:16:08 +02:00
|
|
|
exports.process_message = function (msg, do_inplace_rerender) {
|
2020-03-21 14:42:10 +01:00
|
|
|
if (msg.type !== 'stream') {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Initialize topic data
|
|
|
|
const key = msg.stream_id + ':' + msg.topic;
|
|
|
|
if (!topics.has(key)) {
|
|
|
|
topics.set(key, {
|
|
|
|
last_msg_id: -1,
|
|
|
|
participated: false,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
// Update topic data
|
|
|
|
const is_ours = people.is_my_user_id(msg.sender_id);
|
|
|
|
const topic_data = topics.get(key);
|
|
|
|
if (topic_data.last_msg_id < msg.id) {
|
|
|
|
topic_data.last_msg_id = msg.id;
|
|
|
|
}
|
|
|
|
topic_data.participated = is_ours || topic_data.participated;
|
|
|
|
|
2020-05-22 08:16:08 +02:00
|
|
|
if (do_inplace_rerender) {
|
|
|
|
exports.inplace_rerender(key);
|
2020-05-15 14:27:36 +02:00
|
|
|
}
|
2020-05-22 08:16:08 +02:00
|
|
|
return true;
|
2020-05-15 14:27:36 +02:00
|
|
|
};
|
|
|
|
|
2020-03-21 14:42:10 +01:00
|
|
|
function get_sorted_topics() {
|
|
|
|
// Sort all recent topics by last message time.
|
|
|
|
return new Map(Array.from(topics.entries()).sort(function (a, b) {
|
|
|
|
return b[1].last_msg_id - a[1].last_msg_id;
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
exports.get = function () {
|
|
|
|
return get_sorted_topics();
|
|
|
|
};
|
|
|
|
|
2020-04-16 20:47:18 +02:00
|
|
|
|
2020-05-22 08:16:08 +02:00
|
|
|
function format_topic(topic_data) {
|
|
|
|
const last_msg = message_store.get(topic_data.last_msg_id);
|
|
|
|
const stream = last_msg.stream;
|
|
|
|
const stream_id = last_msg.stream_id;
|
2020-05-26 10:33:35 +02:00
|
|
|
const stream_info = stream_data.get_sub(stream);
|
2020-05-22 08:16:08 +02:00
|
|
|
const topic = last_msg.topic;
|
|
|
|
const time = new XDate(last_msg.timestamp * 1000);
|
|
|
|
const last_msg_time = timerender.last_seen_status_from_date(time);
|
2020-05-23 09:04:51 +02:00
|
|
|
|
|
|
|
// We hide the row according to filters or if it's muted.
|
|
|
|
let hidden = muting.is_topic_muted(stream_id, topic);
|
2020-05-22 08:16:08 +02:00
|
|
|
const unread_count = unread.unread_topic_counter.get(stream_id, topic);
|
2020-05-23 09:04:51 +02:00
|
|
|
if (unread_count === 0 && filters.has('unread')) {
|
|
|
|
hidden = true;
|
|
|
|
}
|
|
|
|
if (!topic_data.participated && filters.has('participated')) {
|
|
|
|
hidden = true;
|
|
|
|
}
|
2020-05-22 21:04:03 +02:00
|
|
|
|
|
|
|
// Display in most recent sender first order
|
|
|
|
const all_senders = recent_senders.get_topic_recent_senders(stream_id, topic);
|
|
|
|
const senders = all_senders.slice(-MAX_AVATAR);
|
|
|
|
const senders_info = people.sender_info_with_small_avatar_urls_for_sender_ids(senders);
|
|
|
|
|
2020-05-22 08:16:08 +02:00
|
|
|
return {
|
2020-05-26 10:33:35 +02:00
|
|
|
// stream info
|
2020-05-22 08:16:08 +02:00
|
|
|
stream_id: stream_id,
|
|
|
|
stream: stream,
|
2020-05-26 10:33:35 +02:00
|
|
|
stream_color: stream_info.color,
|
|
|
|
invite_only: stream_info.invite_only,
|
|
|
|
is_web_public: stream_info.is_web_public,
|
|
|
|
stream_url: hash_util.by_stream_uri(stream_id),
|
|
|
|
|
2020-05-22 08:16:08 +02:00
|
|
|
topic: topic,
|
|
|
|
unread_count: unread_count,
|
|
|
|
last_msg_time: last_msg_time,
|
|
|
|
topic_url: hash_util.by_stream_topic_uri(stream_id, topic),
|
|
|
|
hidden: hidden,
|
2020-05-22 21:04:03 +02:00
|
|
|
senders: senders_info,
|
|
|
|
count_senders: Math.max(0, all_senders.length - MAX_AVATAR),
|
2020-05-22 08:16:08 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function format_all_topics() {
|
|
|
|
const topics_array = [];
|
|
|
|
for (const [, value] of exports.get()) {
|
|
|
|
topics_array.push(format_topic(value));
|
|
|
|
}
|
|
|
|
return topics_array;
|
|
|
|
}
|
|
|
|
|
|
|
|
function get_topic_row(topic_key) {
|
|
|
|
// topic_key = stream_id + ":" + topic
|
|
|
|
return $("#" + $.escapeSelector("recent_topic:" + topic_key));
|
|
|
|
}
|
|
|
|
|
2020-05-26 12:16:05 +02:00
|
|
|
exports.process_topic_edit = function (old_stream_id, old_topic, new_topic, new_stream_id) {
|
|
|
|
// See `recent_senders.process_topic_edit` for
|
|
|
|
// logic behind this and important notes on use of this function.
|
|
|
|
topics.delete(old_stream_id + ':' + old_topic);
|
|
|
|
get_topic_row(old_stream_id + ':' + old_topic).remove();
|
|
|
|
|
|
|
|
const old_topic_msgs = message_util.get_messages_in_topic(old_stream_id, old_topic);
|
|
|
|
exports.process_messages(old_topic_msgs);
|
|
|
|
|
|
|
|
new_stream_id = new_stream_id || old_stream_id;
|
|
|
|
const new_topic_msgs = message_util.get_messages_in_topic(new_stream_id, new_topic);
|
|
|
|
exports.process_messages(new_topic_msgs);
|
|
|
|
};
|
|
|
|
|
2020-05-22 08:16:08 +02:00
|
|
|
exports.inplace_rerender = function (topic_key) {
|
|
|
|
// We remove topic from the UI and reinsert it.
|
|
|
|
// This makes sure we maintain the correct order
|
|
|
|
// of topics.
|
|
|
|
const topic_data = topics.get(topic_key);
|
|
|
|
if (topic_data === undefined) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const formatted_values = format_topic(topic_data);
|
|
|
|
const topic_row = get_topic_row(topic_key);
|
|
|
|
topic_row.remove();
|
|
|
|
|
|
|
|
const rendered_row = render_recent_topic_row(formatted_values);
|
|
|
|
|
|
|
|
const sorted_topic_keys = Array.from(get_sorted_topics().keys());
|
|
|
|
const topic_index = sorted_topic_keys.findIndex(
|
|
|
|
function (key) {
|
|
|
|
if (key === topic_key) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
if (topic_index === 0) {
|
|
|
|
// Note: In this function length of sorted_topic_keys is always >= 2,
|
|
|
|
// since it is called after a complete_rerender has taken place.
|
|
|
|
// A complete_rerender only takes place after there is a topic to
|
|
|
|
// display. So, this can at min be the second topic we are dealing with.
|
|
|
|
get_topic_row(sorted_topic_keys[1]).before(rendered_row);
|
|
|
|
}
|
|
|
|
get_topic_row(sorted_topic_keys[topic_index - 1]).after(rendered_row);
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.update_topic_is_muted = function (stream_id, topic, is_muted) {
|
|
|
|
const key = stream_id + ":" + topic;
|
|
|
|
if (!topics.has(key)) {
|
|
|
|
// we receive mute request for a topic we are
|
|
|
|
// not tracking currently
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_muted) {
|
|
|
|
get_topic_row(key).hide();
|
|
|
|
} else {
|
|
|
|
get_topic_row(key).show();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
2020-05-22 17:11:19 +02:00
|
|
|
exports.update_topic_unread_count = function (message) {
|
|
|
|
const topic_key = message.stream_id + ":" + message.topic;
|
|
|
|
exports.inplace_rerender(topic_key);
|
|
|
|
};
|
|
|
|
|
2020-05-23 09:04:51 +02:00
|
|
|
exports.set_filter = function (filter) {
|
|
|
|
const filter_elem = $('#recent_topics_filter_buttons')
|
|
|
|
.find('[data-filter="' + filter + '"]');
|
|
|
|
|
|
|
|
if (filter === 'all' && filters.size !== 0) {
|
|
|
|
filters = new Set();
|
|
|
|
} else if (filter_elem.hasClass('btn-recent-selected')) {
|
|
|
|
filters.delete(filter);
|
|
|
|
} else {
|
|
|
|
filters.add(filter);
|
|
|
|
}
|
|
|
|
exports.complete_rerender();
|
|
|
|
};
|
|
|
|
|
2020-05-22 08:16:08 +02:00
|
|
|
exports.complete_rerender = function () {
|
|
|
|
// NOTE: This function is grows expensive with
|
|
|
|
// number of topics. Only call when necessary.
|
|
|
|
// This functions takes around 1ms per topic to process.
|
|
|
|
const rendered_body = render_recent_topics_body({
|
|
|
|
recent_topics: format_all_topics(),
|
2020-05-23 09:39:47 +02:00
|
|
|
filter_participated: filters.has('participated'),
|
|
|
|
filter_unread: filters.has('unread'),
|
2020-05-22 08:16:08 +02:00
|
|
|
});
|
2020-04-08 13:59:56 +02:00
|
|
|
$('#recent_topics_table').html(rendered_body);
|
2020-05-23 09:04:51 +02:00
|
|
|
|
|
|
|
if (filters.size === 0) {
|
|
|
|
$('#recent_topics_filter_buttons')
|
|
|
|
.find('[data-filter="all"]')
|
|
|
|
.addClass('btn-recent-selected');
|
|
|
|
} else {
|
|
|
|
for (const filter of filters) {
|
|
|
|
$('#recent_topics_filter_buttons')
|
|
|
|
.find('[data-filter="' + filter + '"]')
|
|
|
|
.addClass('btn-recent-selected');
|
|
|
|
}
|
|
|
|
}
|
2020-05-22 08:16:08 +02:00
|
|
|
};
|
2020-04-08 13:59:56 +02:00
|
|
|
|
2020-05-22 08:16:08 +02:00
|
|
|
exports.launch = function () {
|
2020-04-08 13:59:56 +02:00
|
|
|
overlays.open_overlay({
|
|
|
|
name: 'recent_topics',
|
|
|
|
overlay: $('#recent_topics_overlay'),
|
|
|
|
on_close: function () {
|
|
|
|
hashchange.exit_overlay();
|
|
|
|
},
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2020-03-21 14:42:10 +01:00
|
|
|
window.recent_topics = exports;
|