2021-03-11 05:43:45 +01:00
|
|
|
import $ from "jquery";
|
2022-02-21 10:51:58 +01:00
|
|
|
import _ from "lodash";
|
2021-03-11 05:43:45 +01:00
|
|
|
|
2023-09-06 23:35:51 +02:00
|
|
|
import render_recent_view_filters from "../templates/recent_view_filters.hbs";
|
|
|
|
import render_recent_view_row from "../templates/recent_view_row.hbs";
|
|
|
|
import render_recent_view_body from "../templates/recent_view_table.hbs";
|
2022-10-20 13:51:35 +02:00
|
|
|
import render_user_with_status_icon from "../templates/user_with_status_icon.hbs";
|
2021-02-28 01:27:48 +01:00
|
|
|
|
2023-03-28 23:13:24 +02:00
|
|
|
import * as blueslip from "./blueslip";
|
2022-04-24 06:13:19 +02:00
|
|
|
import * as buddy_data from "./buddy_data";
|
2021-03-18 13:57:28 +01:00
|
|
|
import * as compose_closed_ui from "./compose_closed_ui";
|
2023-08-15 01:21:55 +02:00
|
|
|
import * as compose_state from "./compose_state";
|
2021-02-28 01:27:48 +01:00
|
|
|
import * as hash_util from "./hash_util";
|
2021-06-03 21:51:44 +02:00
|
|
|
import {$t} from "./i18n";
|
2023-06-15 17:07:10 +02:00
|
|
|
import * as left_sidebar_navigation_area from "./left_sidebar_navigation_area";
|
2021-02-28 01:27:48 +01:00
|
|
|
import * as ListWidget from "./list_widget";
|
2022-02-25 06:08:08 +01:00
|
|
|
import * as loading from "./loading";
|
2021-02-28 01:27:48 +01:00
|
|
|
import {localstorage} from "./localstorage";
|
|
|
|
import * as message_store from "./message_store";
|
|
|
|
import * as message_util from "./message_util";
|
2023-10-11 11:39:30 +02:00
|
|
|
import * as modals from "./modals";
|
2023-03-15 22:19:06 +01:00
|
|
|
import * as muted_users from "./muted_users";
|
2023-10-11 11:39:30 +02:00
|
|
|
import * as overlays from "./overlays";
|
2022-04-14 05:02:53 +02:00
|
|
|
import {page_params} from "./page_params";
|
2021-02-28 01:27:48 +01:00
|
|
|
import * as people from "./people";
|
2023-02-14 06:47:36 +01:00
|
|
|
import * as popovers from "./popovers";
|
2021-02-28 01:27:48 +01:00
|
|
|
import * as recent_senders from "./recent_senders";
|
2023-09-06 23:22:20 +02:00
|
|
|
import {get, process_message, topics} from "./recent_view_data";
|
2023-09-06 23:21:13 +02:00
|
|
|
import {get_key_from_message, get_topic_key, is_visible, set_visible} from "./recent_view_util";
|
2023-04-25 18:01:02 +02:00
|
|
|
import * as scroll_util from "./scroll_util";
|
2023-09-28 14:40:04 +02:00
|
|
|
import * as sidebar_ui from "./sidebar_ui";
|
2021-02-28 01:27:48 +01:00
|
|
|
import * as stream_data from "./stream_data";
|
2021-04-15 17:02:54 +02:00
|
|
|
import * as sub_store from "./sub_store";
|
2021-02-28 01:27:48 +01:00
|
|
|
import * as timerender from "./timerender";
|
2023-02-14 06:47:36 +01:00
|
|
|
import * as ui_util from "./ui_util";
|
2021-02-28 21:30:38 +01:00
|
|
|
import * as unread from "./unread";
|
2022-10-20 13:51:35 +02:00
|
|
|
import * as user_status from "./user_status";
|
2022-08-14 15:33:13 +02:00
|
|
|
import * as user_topics from "./user_topics";
|
2023-09-23 08:20:22 +02:00
|
|
|
import * as views_util from "./views_util";
|
2020-08-20 21:24:06 +02:00
|
|
|
|
2020-06-01 17:17:41 +02:00
|
|
|
let topics_widget;
|
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;
|
2021-06-03 21:51:44 +02:00
|
|
|
const MAX_EXTRA_SENDERS = 10;
|
2020-03-21 14:42:10 +01:00
|
|
|
|
2020-06-20 10:17:44 +02:00
|
|
|
// Use this to set the focused element.
|
|
|
|
//
|
|
|
|
// We set it's value to `table` in case the
|
|
|
|
// focus in one of the table rows, since the
|
|
|
|
// table rows are constantly updated and tracking
|
|
|
|
// the selected element in them would be tedious via
|
|
|
|
// jquery.
|
|
|
|
//
|
|
|
|
// So, we use table as a grid system and
|
|
|
|
// track the coordinates of the focus element via
|
|
|
|
// `row_focus` and `col_focus`.
|
2022-01-25 11:36:19 +01:00
|
|
|
export let $current_focus_elem = "table";
|
2021-06-10 14:18:46 +02:00
|
|
|
|
2023-09-06 23:04:07 +02:00
|
|
|
// If user clicks a topic in Recent Conversations, then
|
2021-09-06 17:43:52 +02:00
|
|
|
// we store that topic here so that we can restore focus
|
|
|
|
// to that topic when user revisits.
|
|
|
|
let last_visited_topic = "";
|
2020-06-20 10:17:44 +02:00
|
|
|
let row_focus = 0;
|
|
|
|
// Start focus on the topic column, so Down+Enter works to visit a topic.
|
|
|
|
let col_focus = 1;
|
|
|
|
|
2021-03-13 16:45:47 +01:00
|
|
|
export const COLUMNS = {
|
|
|
|
stream: 0,
|
|
|
|
topic: 1,
|
2022-10-21 11:49:15 +02:00
|
|
|
read: 2,
|
|
|
|
mute: 3,
|
2021-03-13 16:45:47 +01:00
|
|
|
};
|
|
|
|
|
2023-09-07 00:02:24 +02:00
|
|
|
// The number of selectable actions in a Recent Conversations view.
|
|
|
|
// Used to implement wraparound of elements with the right/left keys.
|
|
|
|
// Must be increased when we add new actions, or rethought if we add
|
|
|
|
// optional actions that only appear in some rows.
|
2022-04-24 06:13:19 +02:00
|
|
|
const MAX_SELECTABLE_TOPIC_COLS = 4;
|
2023-06-16 12:37:10 +02:00
|
|
|
const MAX_SELECTABLE_DIRECT_MESSAGE_COLS = 3;
|
2020-06-20 10:17:44 +02:00
|
|
|
|
2020-09-21 01:43:18 +02:00
|
|
|
// we use localstorage to persist the recent topic filters
|
|
|
|
const ls_key = "recent_topic_filters";
|
|
|
|
const ls = localstorage();
|
|
|
|
|
|
|
|
let filters = new Set();
|
2021-02-28 01:27:48 +01:00
|
|
|
|
2022-10-18 19:54:34 +02:00
|
|
|
const recent_conversation_key_prefix = "recent_conversation:";
|
2022-04-24 06:13:19 +02:00
|
|
|
|
2021-03-01 21:08:50 +01:00
|
|
|
export function clear_for_tests() {
|
|
|
|
filters.clear();
|
|
|
|
topics.clear();
|
|
|
|
topics_widget = undefined;
|
|
|
|
}
|
|
|
|
|
2021-02-28 01:27:48 +01:00
|
|
|
export function save_filters() {
|
2023-03-02 01:58:25 +01:00
|
|
|
ls.set(ls_key, [...filters]);
|
2021-02-28 01:27:48 +01:00
|
|
|
}
|
2020-09-21 01:43:18 +02:00
|
|
|
|
2023-08-15 01:21:55 +02:00
|
|
|
export function is_in_focus() {
|
2023-09-06 23:04:07 +02:00
|
|
|
// Check if user is focused on Recent Conversations.
|
2023-08-15 01:21:55 +02:00
|
|
|
return (
|
|
|
|
is_visible() &&
|
|
|
|
!compose_state.composing() &&
|
|
|
|
!popovers.any_active() &&
|
2023-09-28 14:40:04 +02:00
|
|
|
!sidebar_ui.any_sidebar_expanded_as_overlay() &&
|
2023-10-11 11:39:30 +02:00
|
|
|
!overlays.any_active() &&
|
|
|
|
!modals.any_active() &&
|
2023-08-15 01:21:55 +02:00
|
|
|
!$(".home-page-input").is(":focus")
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-02-28 01:27:48 +01:00
|
|
|
export function set_default_focus() {
|
2020-06-20 10:17:44 +02:00
|
|
|
// If at any point we are confused about the currently
|
|
|
|
// focused element, we switch focus to search.
|
2023-09-06 23:42:15 +02:00
|
|
|
$current_focus_elem = $("#recent_view_search");
|
2022-01-25 11:36:19 +01:00
|
|
|
$current_focus_elem.trigger("focus");
|
2021-05-10 14:05:30 +02:00
|
|
|
compose_closed_ui.set_standard_text_for_reply_button();
|
2020-06-20 10:17:44 +02:00
|
|
|
}
|
|
|
|
|
2023-10-04 19:07:01 +02:00
|
|
|
// When there are no messages loaded, we don't show a banner yet.
|
|
|
|
const NO_MESSAGES_LOADED = 0;
|
|
|
|
// When some messages are loaded, but we're still loading newer messages,
|
|
|
|
// we show a simple loading banner.
|
|
|
|
const SOME_MESSAGES_LOADED = 1;
|
|
|
|
// Once we've found the newest message, we allow the user to load
|
|
|
|
// more messages further back in time.
|
|
|
|
const SOME_MESSAGES_LOADED_INCLUDING_NEWEST = 2;
|
|
|
|
// Once all messages are loaded, we hide the banner.
|
|
|
|
const ALL_MESSAGES_LOADED = 3;
|
|
|
|
|
|
|
|
let loading_state = NO_MESSAGES_LOADED;
|
2023-10-17 12:16:46 +02:00
|
|
|
let oldest_message_timestamp = Number.POSITIVE_INFINITY;
|
|
|
|
|
|
|
|
export function set_oldest_message_date(msg_list_data) {
|
|
|
|
const has_found_oldest = msg_list_data.fetch_status.has_found_oldest();
|
|
|
|
const has_found_newest = msg_list_data.fetch_status.has_found_newest();
|
|
|
|
|
|
|
|
oldest_message_timestamp = Math.min(msg_list_data.first().timestamp, oldest_message_timestamp);
|
2023-10-04 19:07:01 +02:00
|
|
|
|
|
|
|
if (has_found_oldest) {
|
|
|
|
loading_state = ALL_MESSAGES_LOADED;
|
|
|
|
} else if (has_found_newest) {
|
|
|
|
loading_state = SOME_MESSAGES_LOADED_INCLUDING_NEWEST;
|
|
|
|
} else {
|
|
|
|
loading_state = SOME_MESSAGES_LOADED;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We might be loading messages in another narrow before the recent view
|
|
|
|
// is shown, so we keep the state updated and update the banner only
|
|
|
|
// once it's actually rendered.
|
|
|
|
if ($("#recent_view_table table tbody").length) {
|
|
|
|
update_load_more_banner();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function update_load_more_banner() {
|
|
|
|
if (loading_state === NO_MESSAGES_LOADED) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (loading_state === ALL_MESSAGES_LOADED) {
|
|
|
|
$(".recent-view-load-more-container").toggleClass("notvisible", true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// There are some messages loaded, but not all messages yet. The banner was
|
|
|
|
// hidden on page load, and we make sure to show it now that there are messages
|
|
|
|
// we can display.
|
|
|
|
$(".recent-view-load-more-container").toggleClass("notvisible", false);
|
|
|
|
|
|
|
|
// Until we've found the newest message, we only show the banner with a messages
|
|
|
|
// explaining we're still fetching messages. We don't allow the user to fetch
|
|
|
|
// more messages.
|
|
|
|
if (loading_state === SOME_MESSAGES_LOADED) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const $button = $(".recent-view-load-more-container .fetch-messages-button");
|
|
|
|
const $button_label = $(".recent-view-load-more-container .button-label");
|
|
|
|
const $banner_text = $(".recent-view-load-more-container .last-fetched-message");
|
|
|
|
|
|
|
|
$button.toggleClass("notvisible", false);
|
|
|
|
|
|
|
|
const time_obj = new Date(oldest_message_timestamp * 1000);
|
|
|
|
const time_string = timerender.get_localized_date_or_time_for_format(
|
|
|
|
time_obj,
|
|
|
|
"full_weekday_dayofyear_year_time",
|
|
|
|
);
|
|
|
|
$banner_text.text($t({defaultMessage: "Showing messages since {time_string}."}, {time_string}));
|
|
|
|
|
|
|
|
$button_label.toggleClass("invisible", false);
|
|
|
|
$button.prop("disabled", false);
|
|
|
|
loading.destroy_indicator(
|
|
|
|
$(".recent-view-load-more-container .fetch-messages-button .loading-indicator"),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-05-06 06:31:56 +02:00
|
|
|
function get_min_load_count(already_rendered_count, load_count) {
|
|
|
|
const extra_rows_for_viewing_pleasure = 15;
|
|
|
|
if (row_focus > already_rendered_count + load_count) {
|
|
|
|
return row_focus + extra_rows_for_viewing_pleasure - already_rendered_count;
|
|
|
|
}
|
|
|
|
return load_count;
|
|
|
|
}
|
|
|
|
|
2021-06-10 14:18:46 +02:00
|
|
|
function is_table_focused() {
|
2022-01-25 11:36:19 +01:00
|
|
|
return $current_focus_elem === "table";
|
2021-06-10 14:18:46 +02:00
|
|
|
}
|
|
|
|
|
2022-04-24 06:13:19 +02:00
|
|
|
function get_row_type(row) {
|
|
|
|
// Return "private" or "stream"
|
|
|
|
// We use CSS method for finding row type until topics_widget gets initialized.
|
|
|
|
if (!topics_widget) {
|
2023-09-06 23:38:15 +02:00
|
|
|
const $topic_rows = $("#recent_view_table table tbody tr");
|
2022-04-24 06:13:19 +02:00
|
|
|
const $topic_row = $topic_rows.eq(row);
|
|
|
|
const is_private = $topic_row.attr("data-private");
|
|
|
|
if (is_private) {
|
|
|
|
return "private";
|
|
|
|
}
|
|
|
|
return "stream";
|
|
|
|
}
|
|
|
|
|
|
|
|
const current_list = topics_widget.get_current_list();
|
|
|
|
const current_row = current_list[row];
|
|
|
|
return current_row.type;
|
|
|
|
}
|
|
|
|
|
|
|
|
function get_max_selectable_cols(row) {
|
2023-06-16 13:23:39 +02:00
|
|
|
// returns maximum number of columns in stream message or direct message row.
|
2022-04-24 06:13:19 +02:00
|
|
|
const type = get_row_type(row);
|
|
|
|
if (type === "private") {
|
2023-06-16 12:37:10 +02:00
|
|
|
return MAX_SELECTABLE_DIRECT_MESSAGE_COLS;
|
2022-04-24 06:13:19 +02:00
|
|
|
}
|
|
|
|
return MAX_SELECTABLE_TOPIC_COLS;
|
|
|
|
}
|
|
|
|
|
2021-05-17 02:36:29 +02:00
|
|
|
function set_table_focus(row, col, using_keyboard) {
|
2023-09-06 23:38:15 +02:00
|
|
|
const $topic_rows = $("#recent_view_table table tbody tr");
|
2022-01-25 11:36:19 +01:00
|
|
|
if ($topic_rows.length === 0 || row < 0 || row >= $topic_rows.length) {
|
2020-06-20 10:17:44 +02:00
|
|
|
row_focus = 0;
|
|
|
|
// return focus back to filters if we cannot focus on the table.
|
|
|
|
set_default_focus();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-11-01 09:04:33 +01:00
|
|
|
const unread = has_unread(row);
|
|
|
|
if (col === 2 && !unread) {
|
2022-10-30 06:53:27 +01:00
|
|
|
col = 1;
|
|
|
|
col_focus = 1;
|
|
|
|
}
|
2022-11-01 09:04:33 +01:00
|
|
|
const type = get_row_type(row);
|
|
|
|
if (col === 3 && type === "private") {
|
|
|
|
col = unread ? 2 : 1;
|
|
|
|
col_focus = col;
|
|
|
|
}
|
|
|
|
|
2022-01-25 11:36:19 +01:00
|
|
|
const $topic_row = $topic_rows.eq(row);
|
2021-05-17 02:36:29 +02:00
|
|
|
// We need to allow table to render first before setting focus.
|
|
|
|
setTimeout(
|
2023-09-06 23:43:47 +02:00
|
|
|
() => $topic_row.find(".recent_view_focusable").eq(col).children().trigger("focus"),
|
2021-05-17 02:36:29 +02:00
|
|
|
0,
|
|
|
|
);
|
2022-01-25 11:36:19 +01:00
|
|
|
$current_focus_elem = "table";
|
2021-03-18 13:57:28 +01:00
|
|
|
|
2021-05-17 02:36:29 +02:00
|
|
|
if (using_keyboard) {
|
|
|
|
const scroll_element = document.querySelector(
|
2023-09-06 23:38:15 +02:00
|
|
|
"#recent_view_table .table_fix_head .simplebar-content-wrapper",
|
2021-05-17 02:36:29 +02:00
|
|
|
);
|
|
|
|
const half_height_of_visible_area = scroll_element.offsetHeight / 2;
|
2022-01-25 11:36:19 +01:00
|
|
|
const topic_offset = topic_offset_to_visible_area($topic_row);
|
2021-05-17 02:36:29 +02:00
|
|
|
|
|
|
|
if (topic_offset === "above") {
|
|
|
|
scroll_element.scrollBy({top: -1 * half_height_of_visible_area});
|
|
|
|
} else if (topic_offset === "below") {
|
|
|
|
scroll_element.scrollBy({top: half_height_of_visible_area});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-24 06:13:19 +02:00
|
|
|
// TODO: This fake "message" object is designed to allow using the
|
|
|
|
// get_recipient_label helper inside compose_closed_ui. Surely
|
|
|
|
// there's a more readable way to write this code.
|
2023-10-06 23:10:08 +02:00
|
|
|
// Similar code is present in Inbox.
|
2022-04-24 06:13:19 +02:00
|
|
|
let message;
|
|
|
|
if (type === "private") {
|
|
|
|
message = {
|
|
|
|
display_reply_to: $topic_row.find(".recent_topic_name a").text(),
|
|
|
|
};
|
|
|
|
} else {
|
2023-07-26 22:07:21 +02:00
|
|
|
const stream_name = $topic_row.find(".recent_topic_stream a").text();
|
|
|
|
const stream = stream_data.get_sub_by_name(stream_name);
|
2022-04-24 06:13:19 +02:00
|
|
|
message = {
|
2023-07-26 22:07:21 +02:00
|
|
|
stream_id: stream?.stream_id,
|
2022-04-24 06:13:19 +02:00
|
|
|
topic: $topic_row.find(".recent_topic_name a").text(),
|
|
|
|
};
|
|
|
|
}
|
2021-03-18 13:57:28 +01:00
|
|
|
compose_closed_ui.update_reply_recipient_label(message);
|
2020-06-20 10:17:44 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-03-18 13:57:28 +01:00
|
|
|
export function get_focused_row_message() {
|
|
|
|
if (is_table_focused()) {
|
2023-09-06 23:38:15 +02:00
|
|
|
const $topic_rows = $("#recent_view_table table tbody tr");
|
2022-01-25 11:36:19 +01:00
|
|
|
if ($topic_rows.length === 0) {
|
2021-09-08 08:50:23 +02:00
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
2022-01-25 11:36:19 +01:00
|
|
|
const $topic_row = $topic_rows.eq(row_focus);
|
2022-04-24 06:13:19 +02:00
|
|
|
const conversation_id = $topic_row.attr("id").slice(recent_conversation_key_prefix.length);
|
|
|
|
const topic_last_msg_id = topics.get(conversation_id).last_msg_id;
|
2021-03-18 13:57:28 +01:00
|
|
|
return message_store.get(topic_last_msg_id);
|
|
|
|
}
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
2021-08-24 17:19:37 +02:00
|
|
|
export function revive_current_focus() {
|
2020-06-20 10:17:44 +02:00
|
|
|
// After re-render, the current_focus_elem is no longer linked
|
|
|
|
// to the focused element, this function attempts to revive the
|
|
|
|
// link and focus to the element prior to the rerender.
|
2021-02-15 15:33:37 +01:00
|
|
|
|
2021-03-30 10:38:06 +02:00
|
|
|
// We try to avoid setting focus when user
|
2023-09-06 23:04:07 +02:00
|
|
|
// is not focused on Recent Conversations.
|
2021-03-30 10:38:06 +02:00
|
|
|
if (!is_in_focus()) {
|
2021-02-15 15:33:37 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-01-25 11:36:19 +01:00
|
|
|
if (!$current_focus_elem) {
|
2020-06-20 10:17:44 +02:00
|
|
|
set_default_focus();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-04-02 12:36:02 +02:00
|
|
|
if (is_table_focused()) {
|
2021-09-06 17:43:52 +02:00
|
|
|
if (last_visited_topic) {
|
2022-08-19 21:39:42 +02:00
|
|
|
// If the only message in the topic was deleted,
|
2023-09-06 23:04:07 +02:00
|
|
|
// then the topic will not be in Recent Conversations data.
|
2022-08-19 21:39:42 +02:00
|
|
|
if (topics.get(last_visited_topic) !== undefined) {
|
|
|
|
const topic_last_msg_id = topics.get(last_visited_topic).last_msg_id;
|
|
|
|
const current_list = topics_widget.get_current_list();
|
|
|
|
const last_visited_topic_index = current_list.findIndex(
|
|
|
|
(topic) => topic.last_msg_id === topic_last_msg_id,
|
|
|
|
);
|
|
|
|
if (last_visited_topic_index >= 0) {
|
|
|
|
row_focus = last_visited_topic_index;
|
|
|
|
}
|
2021-09-06 17:43:52 +02:00
|
|
|
}
|
|
|
|
last_visited_topic = "";
|
|
|
|
}
|
2020-06-20 10:17:44 +02:00
|
|
|
set_table_focus(row_focus, col_focus);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-01-25 11:36:19 +01:00
|
|
|
const filter_button = $current_focus_elem.data("filter");
|
2020-06-20 10:17:44 +02:00
|
|
|
if (!filter_button) {
|
|
|
|
set_default_focus();
|
|
|
|
} else {
|
2023-09-06 23:41:43 +02:00
|
|
|
$current_focus_elem = $("#recent_view_filter_buttons").find(
|
2021-02-03 23:23:32 +01:00
|
|
|
`[data-filter='${CSS.escape(filter_button)}']`,
|
2020-07-15 00:34:28 +02:00
|
|
|
);
|
2022-01-25 11:36:19 +01:00
|
|
|
$current_focus_elem.trigger("focus");
|
2020-06-20 10:17:44 +02:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-02-25 06:08:08 +01:00
|
|
|
export function show_loading_indicator() {
|
2023-09-06 23:41:07 +02:00
|
|
|
loading.make_indicator($("#recent_view_loading_messages_indicator"));
|
2023-09-18 12:31:52 +02:00
|
|
|
$("#recent_view_table tbody").removeClass("required-text");
|
2022-02-25 06:08:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
export function hide_loading_indicator() {
|
2023-09-06 23:40:39 +02:00
|
|
|
$("#recent_view_bottom_whitespace").hide();
|
2023-09-06 23:41:07 +02:00
|
|
|
loading.destroy_indicator($("#recent_view_loading_messages_indicator"), {
|
2022-02-25 06:08:08 +01:00
|
|
|
abs_positioned: false,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-10-17 12:16:46 +02:00
|
|
|
export function process_messages(messages, msg_list_data) {
|
|
|
|
// This code path processes messages from 3 sources:
|
|
|
|
// 1. Newly sent messages from the server_events system. This is safe to
|
|
|
|
// process because we always will have the latest previously sent messages.
|
|
|
|
// 2. Messages in all_messages_data, the main cache of contiguous
|
|
|
|
// message history that the client maintains.
|
|
|
|
// 3. Latest messages fetched specifically for Recent view when
|
|
|
|
// the browser first loads. We will be able to remove this once
|
|
|
|
// all_messages_data is fetched in a better order.
|
|
|
|
|
2022-11-18 17:00:42 +01:00
|
|
|
let conversation_data_updated = false;
|
2022-11-17 15:42:56 +01:00
|
|
|
if (messages.length > 0) {
|
|
|
|
for (const msg of messages) {
|
2022-11-18 17:00:42 +01:00
|
|
|
if (process_message(msg)) {
|
|
|
|
conversation_data_updated = true;
|
|
|
|
}
|
2021-05-06 19:47:28 +02:00
|
|
|
}
|
2022-11-18 17:00:42 +01:00
|
|
|
}
|
|
|
|
|
2023-10-17 12:16:46 +02:00
|
|
|
if (msg_list_data) {
|
|
|
|
// Update the recent view UI's understanding of which messages
|
|
|
|
// we have available for the recent view.
|
|
|
|
set_oldest_message_date(msg_list_data);
|
|
|
|
}
|
|
|
|
|
2022-11-18 17:00:42 +01:00
|
|
|
// Only rerender if conversation data actually changed.
|
|
|
|
if (conversation_data_updated) {
|
2021-05-06 19:47:28 +02:00
|
|
|
complete_rerender();
|
2020-03-21 14:42:10 +01:00
|
|
|
}
|
2021-02-28 01:27:48 +01:00
|
|
|
}
|
2020-03-21 14:42:10 +01:00
|
|
|
|
2022-04-24 06:13:19 +02:00
|
|
|
function message_to_conversation_unread_count(msg) {
|
|
|
|
if (msg.type === "private") {
|
2022-10-22 07:15:44 +02:00
|
|
|
return unread.num_unread_for_user_ids_string(msg.to_user_ids);
|
2020-07-12 16:45:42 +02:00
|
|
|
}
|
2022-04-24 06:13:19 +02:00
|
|
|
return unread.num_unread_for_topic(msg.stream_id, msg.topic);
|
|
|
|
}
|
|
|
|
|
2022-10-20 13:49:03 +02:00
|
|
|
export function get_pm_tooltip_data(user_ids_string) {
|
|
|
|
const user_id = Number.parseInt(user_ids_string, 10);
|
|
|
|
const person = people.get_by_user_id(user_id);
|
|
|
|
|
|
|
|
if (person.is_bot) {
|
|
|
|
const bot_owner = people.get_bot_owner_user(person);
|
|
|
|
|
|
|
|
if (bot_owner) {
|
|
|
|
const bot_owner_name = $t(
|
|
|
|
{defaultMessage: "Owner: {name}"},
|
|
|
|
{name: bot_owner.full_name},
|
|
|
|
);
|
|
|
|
|
|
|
|
return {
|
|
|
|
first_line: person.full_name,
|
|
|
|
second_line: bot_owner_name,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bot does not have an owner.
|
|
|
|
return {
|
|
|
|
first_line: person.full_name,
|
|
|
|
second_line: "",
|
|
|
|
third_line: "",
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
const last_seen = buddy_data.user_last_seen_time_status(user_id);
|
|
|
|
|
|
|
|
// Users does not have a status.
|
|
|
|
return {
|
|
|
|
first_line: last_seen,
|
|
|
|
second_line: "",
|
|
|
|
third_line: "",
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-04-24 06:13:19 +02:00
|
|
|
function format_conversation(conversation_data) {
|
|
|
|
const context = {};
|
|
|
|
const last_msg = message_store.get(conversation_data.last_msg_id);
|
2021-02-05 21:20:14 +01:00
|
|
|
const time = new Date(last_msg.timestamp * 1000);
|
2022-04-24 06:13:19 +02:00
|
|
|
const type = last_msg.type;
|
2023-05-01 07:02:10 +02:00
|
|
|
context.full_last_msg_date_time = timerender.get_full_datetime_clarification(time);
|
2022-04-24 06:13:19 +02:00
|
|
|
context.conversation_key = get_key_from_message(last_msg);
|
|
|
|
context.unread_count = message_to_conversation_unread_count(last_msg);
|
2023-04-14 14:49:35 +02:00
|
|
|
context.last_msg_time = timerender.relative_time_string_from_date({
|
|
|
|
date: time,
|
|
|
|
});
|
2022-04-24 06:13:19 +02:00
|
|
|
context.is_private = last_msg.type === "private";
|
|
|
|
let all_senders;
|
|
|
|
let senders;
|
|
|
|
let displayed_other_senders;
|
|
|
|
let extra_sender_ids;
|
|
|
|
|
|
|
|
if (type === "stream") {
|
|
|
|
const stream_info = sub_store.get(last_msg.stream_id);
|
|
|
|
|
|
|
|
// Stream info
|
|
|
|
context.stream_id = last_msg.stream_id;
|
2023-07-26 22:07:21 +02:00
|
|
|
context.stream_name = stream_data.get_stream_name_from_id(last_msg.stream_id);
|
2023-04-22 23:10:34 +02:00
|
|
|
context.stream_muted = stream_info.is_muted;
|
2022-04-24 06:13:19 +02:00
|
|
|
context.stream_color = stream_info.color;
|
|
|
|
context.stream_url = hash_util.by_stream_url(context.stream_id);
|
|
|
|
context.invite_only = stream_info.invite_only;
|
|
|
|
context.is_web_public = stream_info.is_web_public;
|
|
|
|
// Topic info
|
|
|
|
context.topic = last_msg.topic;
|
|
|
|
context.topic_url = hash_util.by_stream_topic_url(context.stream_id, context.topic);
|
|
|
|
|
|
|
|
// We hide the row according to filters or if it's muted.
|
|
|
|
// We only supply the data to the topic rows and let jquery
|
|
|
|
// display / hide them according to filters instead of
|
|
|
|
// doing complete re-render.
|
|
|
|
context.topic_muted = Boolean(user_topics.is_topic_muted(context.stream_id, context.topic));
|
2023-04-22 23:10:34 +02:00
|
|
|
context.topic_unmuted = Boolean(
|
|
|
|
user_topics.is_topic_unmuted(context.stream_id, context.topic),
|
|
|
|
);
|
2022-10-14 17:37:47 +02:00
|
|
|
context.mention_in_unread = unread.topic_has_any_unread_mentions(
|
|
|
|
context.stream_id,
|
|
|
|
context.topic,
|
|
|
|
);
|
2022-04-24 06:13:19 +02:00
|
|
|
|
2023-07-27 14:57:55 +02:00
|
|
|
context.visibility_policy = user_topics.get_topic_visibility_policy(
|
|
|
|
context.stream_id,
|
|
|
|
context.topic,
|
|
|
|
);
|
|
|
|
// The following two fields are not specific to this context, but this is the
|
|
|
|
// easiest way we've figured out for passing the data to the template rendering.
|
|
|
|
context.development = page_params.development_environment;
|
|
|
|
context.all_visibility_policies = user_topics.all_visibility_policies;
|
|
|
|
|
2022-11-24 07:37:21 +01:00
|
|
|
// Since the css for displaying senders in reverse order is much simpler,
|
|
|
|
// we provide our handlebars with senders in opposite order.
|
|
|
|
// Display in most recent sender first order.
|
|
|
|
all_senders = recent_senders
|
|
|
|
.get_topic_recent_senders(context.stream_id, context.topic)
|
|
|
|
.reverse();
|
2022-04-24 06:13:19 +02:00
|
|
|
senders = all_senders.slice(-MAX_AVATAR);
|
|
|
|
|
|
|
|
// Collect extra sender fullname for tooltip
|
|
|
|
extra_sender_ids = all_senders.slice(0, -MAX_AVATAR);
|
|
|
|
displayed_other_senders = extra_sender_ids.slice(-MAX_EXTRA_SENDERS);
|
|
|
|
} else if (type === "private") {
|
2023-06-16 13:23:39 +02:00
|
|
|
// Direct message info
|
2022-10-21 12:00:06 +02:00
|
|
|
context.user_ids_string = last_msg.to_user_ids;
|
2022-10-20 13:51:35 +02:00
|
|
|
context.rendered_pm_with = last_msg.display_recipient
|
|
|
|
.filter(
|
|
|
|
(recipient) =>
|
|
|
|
!people.is_my_user_id(recipient.id) || last_msg.display_recipient.length === 1,
|
|
|
|
)
|
|
|
|
.map((user) =>
|
|
|
|
render_user_with_status_icon({
|
2023-03-13 05:24:16 +01:00
|
|
|
name: people.get_display_full_name(user.id),
|
2022-10-20 13:51:35 +02:00
|
|
|
status_emoji_info: user_status.get_status_emoji(user.id),
|
|
|
|
}),
|
|
|
|
)
|
2023-03-13 05:24:16 +01:00
|
|
|
.sort()
|
2022-10-20 13:51:35 +02:00
|
|
|
.join(", ");
|
2022-04-24 06:13:19 +02:00
|
|
|
context.recipient_id = last_msg.recipient_id;
|
|
|
|
context.pm_url = last_msg.pm_with_url;
|
|
|
|
context.is_group = last_msg.display_recipient.length > 2;
|
|
|
|
|
|
|
|
if (!context.is_group) {
|
2022-10-20 13:49:03 +02:00
|
|
|
const user_id = Number.parseInt(last_msg.to_user_ids, 10);
|
|
|
|
const user = people.get_by_user_id(user_id);
|
|
|
|
if (user.is_bot) {
|
2023-04-18 00:36:34 +02:00
|
|
|
// We display the bot icon rather than a user circle for bots.
|
|
|
|
context.is_bot = true;
|
2022-10-20 13:49:03 +02:00
|
|
|
} else {
|
|
|
|
context.user_circle_class = buddy_data.get_user_circle_class(user_id);
|
|
|
|
}
|
2022-04-24 06:13:19 +02:00
|
|
|
}
|
2022-11-18 18:25:51 +01:00
|
|
|
|
2022-11-19 07:39:00 +01:00
|
|
|
// Since the css for displaying senders in reverse order is much simpler,
|
|
|
|
// we provide our handlebars with senders in opposite order.
|
|
|
|
// Display in most recent sender first order.
|
|
|
|
// To match the behavior for streams, we display the set of users who've actually
|
|
|
|
// participated, with the most recent participants first. It could make sense to
|
2023-06-16 13:23:39 +02:00
|
|
|
// display the other recipients on the direct message conversation with different
|
|
|
|
// styling, but it's important to not destroy the information of "who's actually
|
|
|
|
// talked".
|
2022-11-19 07:39:00 +01:00
|
|
|
all_senders = recent_senders
|
|
|
|
.get_pm_recent_senders(context.user_ids_string)
|
|
|
|
.participants.reverse();
|
|
|
|
senders = all_senders.slice(-MAX_AVATAR);
|
2022-11-18 18:25:51 +01:00
|
|
|
// Collect extra senders fullname for tooltip.
|
|
|
|
extra_sender_ids = all_senders.slice(0, -MAX_AVATAR);
|
2023-02-11 00:32:22 +01:00
|
|
|
displayed_other_senders = extra_sender_ids.slice(-MAX_EXTRA_SENDERS);
|
2022-04-24 06:13:19 +02:00
|
|
|
}
|
|
|
|
|
2023-09-06 23:25:36 +02:00
|
|
|
context.senders = people.sender_info_for_recent_view_row(senders);
|
2022-04-24 06:13:19 +02:00
|
|
|
context.other_senders_count = Math.max(0, all_senders.length - MAX_AVATAR);
|
|
|
|
extra_sender_ids = all_senders.slice(0, -MAX_AVATAR);
|
2021-06-03 21:51:44 +02:00
|
|
|
const displayed_other_names = people.get_display_full_names(displayed_other_senders.reverse());
|
|
|
|
|
|
|
|
if (extra_sender_ids.length > MAX_EXTRA_SENDERS) {
|
|
|
|
// We display only 10 extra senders in tooltips,
|
|
|
|
// and just display remaining number of senders.
|
|
|
|
const remaining_senders = extra_sender_ids.length - MAX_EXTRA_SENDERS;
|
2021-09-08 09:26:34 +02:00
|
|
|
// Pluralization syntax from:
|
|
|
|
// https://formatjs.io/docs/core-concepts/icu-syntax/#plural-format
|
2021-06-03 21:51:44 +02:00
|
|
|
displayed_other_names.push(
|
2021-09-08 09:26:34 +02:00
|
|
|
$t(
|
|
|
|
{
|
|
|
|
defaultMessage:
|
|
|
|
"and {remaining_senders, plural, one {1 other} other {# others}}.",
|
|
|
|
},
|
|
|
|
{remaining_senders},
|
|
|
|
),
|
2021-06-03 21:51:44 +02:00
|
|
|
);
|
|
|
|
}
|
2022-04-24 06:13:19 +02:00
|
|
|
context.other_sender_names_html = displayed_other_names
|
2022-02-21 10:51:58 +01:00
|
|
|
.map((name) => _.escape(name))
|
|
|
|
.join("<br />");
|
2022-04-24 06:13:19 +02:00
|
|
|
context.last_msg_url = hash_util.by_conversation_and_time_url(last_msg);
|
2021-06-03 21:51:44 +02:00
|
|
|
|
2022-04-24 06:13:19 +02:00
|
|
|
return context;
|
2020-05-22 08:16:08 +02:00
|
|
|
}
|
|
|
|
|
2020-06-01 17:17:41 +02:00
|
|
|
function get_topic_row(topic_data) {
|
|
|
|
const msg = message_store.get(topic_data.last_msg_id);
|
2022-04-24 06:13:19 +02:00
|
|
|
const topic_key = get_key_from_message(msg);
|
|
|
|
return $(`#${CSS.escape(recent_conversation_key_prefix + topic_key)}`);
|
2020-05-22 08:16:08 +02:00
|
|
|
}
|
|
|
|
|
2021-02-28 01:27:48 +01:00
|
|
|
export function process_topic_edit(old_stream_id, old_topic, new_topic, new_stream_id) {
|
2020-05-26 12:16:05 +02:00
|
|
|
// See `recent_senders.process_topic_edit` for
|
|
|
|
// logic behind this and important notes on use of this function.
|
2020-06-12 12:46:30 +02:00
|
|
|
topics.delete(get_topic_key(old_stream_id, old_topic));
|
2020-05-26 12:16:05 +02:00
|
|
|
|
|
|
|
const old_topic_msgs = message_util.get_messages_in_topic(old_stream_id, old_topic);
|
2021-02-28 01:27:48 +01:00
|
|
|
process_messages(old_topic_msgs);
|
2020-05-26 12:16:05 +02:00
|
|
|
|
|
|
|
new_stream_id = new_stream_id || old_stream_id;
|
|
|
|
const new_topic_msgs = message_util.get_messages_in_topic(new_stream_id, new_topic);
|
2021-02-28 01:27:48 +01:00
|
|
|
process_messages(new_topic_msgs);
|
|
|
|
}
|
2020-05-26 12:16:05 +02:00
|
|
|
|
2023-08-05 23:56:40 +02:00
|
|
|
export function topic_in_search_results(keyword, stream_name, topic) {
|
2020-06-01 17:17:41 +02:00
|
|
|
if (keyword === "") {
|
|
|
|
return true;
|
|
|
|
}
|
2023-08-05 23:56:40 +02:00
|
|
|
const text = (stream_name + " " + topic).toLowerCase();
|
2020-07-29 22:22:29 +02:00
|
|
|
const search_words = keyword.toLowerCase().split(/\s+/);
|
|
|
|
return search_words.every((word) => text.includes(word));
|
2021-02-28 01:27:48 +01:00
|
|
|
}
|
2020-06-01 17:17:41 +02:00
|
|
|
|
2021-02-28 01:27:48 +01:00
|
|
|
export function update_topics_of_deleted_message_ids(message_ids) {
|
2020-08-07 09:15:47 +02:00
|
|
|
const topics_to_rerender = message_util.get_topics_for_message_ids(message_ids);
|
2020-07-15 09:36:03 +02:00
|
|
|
|
|
|
|
for (const [stream_id, topic] of topics_to_rerender.values()) {
|
|
|
|
topics.delete(get_topic_key(stream_id, topic));
|
|
|
|
const msgs = message_util.get_messages_in_topic(stream_id, topic);
|
2021-02-28 01:27:48 +01:00
|
|
|
process_messages(msgs);
|
2020-07-15 09:36:03 +02:00
|
|
|
}
|
2021-02-28 01:27:48 +01:00
|
|
|
}
|
2020-07-15 09:36:03 +02:00
|
|
|
|
2021-02-28 01:27:48 +01:00
|
|
|
export function filters_should_hide_topic(topic_data) {
|
2020-06-01 17:17:41 +02:00
|
|
|
const msg = message_store.get(topic_data.last_msg_id);
|
2021-04-15 17:02:54 +02:00
|
|
|
const sub = sub_store.get(msg.stream_id);
|
2020-06-01 17:17:41 +02:00
|
|
|
|
2022-04-24 06:13:19 +02:00
|
|
|
if ((sub === undefined || !sub.subscribed) && topic_data.type === "stream") {
|
2021-01-06 15:23:07 +01:00
|
|
|
// Never try to process deactivated & unsubscribed stream msgs.
|
2020-07-12 16:45:42 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
if (filters.has("unread")) {
|
2022-04-24 06:13:19 +02:00
|
|
|
const unread_count = message_to_conversation_unread_count(msg);
|
|
|
|
if (unread_count === 0) {
|
2020-06-14 09:29:29 +02:00
|
|
|
return true;
|
|
|
|
}
|
2020-06-12 12:21:34 +02:00
|
|
|
}
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
if (!topic_data.participated && filters.has("participated")) {
|
2020-05-29 11:56:19 +02:00
|
|
|
return true;
|
2020-06-12 12:21:34 +02:00
|
|
|
}
|
|
|
|
|
2022-04-24 06:13:19 +02:00
|
|
|
if (!filters.has("include_muted") && topic_data.type === "stream") {
|
2023-10-01 11:18:56 +02:00
|
|
|
// We want to show the unmuted or followed topics within muted
|
|
|
|
// streams in Recent Conversations.
|
|
|
|
const topic_unmuted_or_followed = Boolean(
|
|
|
|
user_topics.is_topic_unmuted_or_followed(msg.stream_id, msg.topic),
|
|
|
|
);
|
2022-08-14 15:33:13 +02:00
|
|
|
const topic_muted = Boolean(user_topics.is_topic_muted(msg.stream_id, msg.topic));
|
2020-06-14 09:29:29 +02:00
|
|
|
const stream_muted = stream_data.is_muted(msg.stream_id);
|
2023-10-01 11:18:56 +02:00
|
|
|
if (topic_muted || (stream_muted && !topic_unmuted_or_followed)) {
|
2020-06-14 09:29:29 +02:00
|
|
|
return true;
|
|
|
|
}
|
2020-06-12 12:21:34 +02:00
|
|
|
}
|
|
|
|
|
2022-04-24 06:13:19 +02:00
|
|
|
if (!filters.has("include_private") && topic_data.type === "private") {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-03-15 22:19:06 +01:00
|
|
|
if (filters.has("include_private") && topic_data.type === "private") {
|
|
|
|
const recipients = people.split_to_ints(msg.to_user_ids);
|
|
|
|
|
|
|
|
if (recipients.every((id) => muted_users.is_user_muted(id))) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-06 23:42:15 +02:00
|
|
|
const search_keyword = $("#recent_view_search").val();
|
2023-07-26 22:07:21 +02:00
|
|
|
const stream_name = stream_data.get_stream_name_from_id(msg.stream_id);
|
|
|
|
if (!topic_in_search_results(search_keyword, stream_name, msg.topic)) {
|
2020-06-01 17:17:41 +02:00
|
|
|
return true;
|
2020-05-29 11:56:19 +02:00
|
|
|
}
|
2020-06-12 12:21:34 +02:00
|
|
|
|
2020-05-29 11:56:19 +02:00
|
|
|
return false;
|
2021-02-28 01:27:48 +01:00
|
|
|
}
|
2020-05-29 11:56:19 +02:00
|
|
|
|
2021-02-28 01:27:48 +01:00
|
|
|
export function inplace_rerender(topic_key) {
|
|
|
|
if (!is_visible()) {
|
2020-05-22 08:16:08 +02:00
|
|
|
return false;
|
|
|
|
}
|
2020-06-01 17:17:41 +02:00
|
|
|
if (!topics.has(topic_key)) {
|
|
|
|
return false;
|
2020-05-22 08:16:08 +02:00
|
|
|
}
|
2020-05-29 11:56:19 +02:00
|
|
|
|
2020-06-01 17:17:41 +02:00
|
|
|
const topic_data = topics.get(topic_key);
|
2023-04-28 08:55:36 +02:00
|
|
|
const $topic_row = get_topic_row(topic_data);
|
2022-11-07 13:07:46 +01:00
|
|
|
// We cannot rely on `topic_widget.meta.filtered_list` to know
|
|
|
|
// if a topic is rendered since the `filtered_list` might have
|
|
|
|
// already been updated via other calls.
|
2023-04-28 08:55:36 +02:00
|
|
|
const is_topic_rendered = $topic_row.length;
|
2022-10-29 15:55:36 +02:00
|
|
|
// Resorting the topics_widget is important for the case where we
|
|
|
|
// are rerendering because of message editing or new messages
|
|
|
|
// arriving, since those operations often change the sort key.
|
|
|
|
topics_widget.filter_and_sort();
|
|
|
|
const current_topics_list = topics_widget.get_current_list();
|
2022-11-07 13:07:46 +01:00
|
|
|
if (is_topic_rendered && filters_should_hide_topic(topic_data)) {
|
2022-11-18 03:55:31 +01:00
|
|
|
// Since the row needs to be removed from DOM, we need to adjust `row_focus`
|
|
|
|
// if the row being removed is focused and is the last row in the list.
|
|
|
|
// This prevents the row_focus either being reset to the first row or
|
|
|
|
// middle of the visible table rows.
|
|
|
|
// We need to get the current focused row details from DOM since we cannot
|
|
|
|
// rely on `current_topics_list` since it has already been updated and row
|
|
|
|
// doesn't exist inside it.
|
|
|
|
const row_is_focused = get_focused_row_message()?.id === topic_data.last_msg_id;
|
2022-10-29 15:55:36 +02:00
|
|
|
if (row_is_focused && row_focus >= current_topics_list.length) {
|
|
|
|
row_focus = current_topics_list.length - 1;
|
|
|
|
}
|
2023-04-28 08:55:36 +02:00
|
|
|
topics_widget.remove_rendered_row($topic_row);
|
2022-11-07 13:07:46 +01:00
|
|
|
} else if (!is_topic_rendered && filters_should_hide_topic(topic_data)) {
|
2022-10-29 15:55:36 +02:00
|
|
|
// In case `topic_row` is not present, our job is already done here
|
|
|
|
// since it has not been rendered yet and we already removed it from
|
|
|
|
// the filtered list in `topic_widget`. So, it won't be displayed in
|
|
|
|
// the future too.
|
2022-11-07 13:07:46 +01:00
|
|
|
} else if (is_topic_rendered && !filters_should_hide_topic(topic_data)) {
|
2022-10-29 15:55:36 +02:00
|
|
|
// Only a re-render is required in this case.
|
|
|
|
topics_widget.render_item(topic_data);
|
2020-05-29 11:56:19 +02:00
|
|
|
} else {
|
2022-11-07 13:07:46 +01:00
|
|
|
// Final case: !is_topic_rendered && !filters_should_hide_topic(topic_data).
|
2023-04-28 09:40:02 +02:00
|
|
|
topics_widget.insert_rendered_row(topic_data, () =>
|
|
|
|
current_topics_list.findIndex(
|
|
|
|
(list_item) => list_item.last_msg_id === topic_data.last_msg_id,
|
|
|
|
),
|
|
|
|
);
|
2020-05-29 11:56:19 +02:00
|
|
|
}
|
2022-10-29 15:55:36 +02:00
|
|
|
setTimeout(revive_current_focus, 0);
|
2020-06-01 17:17:41 +02:00
|
|
|
return true;
|
2021-02-28 01:27:48 +01:00
|
|
|
}
|
2020-05-22 08:16:08 +02:00
|
|
|
|
2023-04-22 01:28:00 +02:00
|
|
|
export function update_topic_visibility_policy(stream_id, topic) {
|
2020-06-12 12:46:30 +02:00
|
|
|
const key = get_topic_key(stream_id, topic);
|
2020-05-22 08:16:08 +02:00
|
|
|
if (!topics.has(key)) {
|
|
|
|
// we receive mute request for a topic we are
|
|
|
|
// not tracking currently
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-02-28 01:27:48 +01:00
|
|
|
inplace_rerender(key);
|
2020-05-22 08:16:08 +02:00
|
|
|
return true;
|
2021-02-28 01:27:48 +01:00
|
|
|
}
|
2020-05-22 08:16:08 +02:00
|
|
|
|
2021-02-28 01:27:48 +01:00
|
|
|
export function update_topic_unread_count(message) {
|
2022-04-24 06:13:19 +02:00
|
|
|
const topic_key = get_key_from_message(message);
|
2021-02-28 01:27:48 +01:00
|
|
|
inplace_rerender(topic_key);
|
|
|
|
}
|
2020-05-22 17:11:19 +02:00
|
|
|
|
2021-02-28 01:27:48 +01:00
|
|
|
export function set_filter(filter) {
|
2020-06-12 13:07:10 +02:00
|
|
|
// This function updates the `filters` variable
|
|
|
|
// after user clicks on one of the filter buttons
|
|
|
|
// based on `btn-recent-selected` class and current
|
|
|
|
// set `filters`.
|
|
|
|
|
|
|
|
// Get the button which was clicked.
|
2023-09-06 23:41:43 +02:00
|
|
|
const $filter_elem = $("#recent_view_filter_buttons").find(
|
2021-02-03 23:23:32 +01:00
|
|
|
`[data-filter="${CSS.escape(filter)}"]`,
|
|
|
|
);
|
2020-05-23 09:04:51 +02:00
|
|
|
|
2020-06-12 13:07:10 +02:00
|
|
|
// If user clicks `All`, we clear all filters.
|
2020-07-15 01:29:15 +02:00
|
|
|
if (filter === "all" && filters.size !== 0) {
|
2020-05-23 09:04:51 +02:00
|
|
|
filters = new Set();
|
2020-07-15 00:34:28 +02:00
|
|
|
// If the button was already selected, remove the filter.
|
2022-01-25 11:36:19 +01:00
|
|
|
} else if ($filter_elem.hasClass("btn-recent-selected")) {
|
2020-05-23 09:04:51 +02:00
|
|
|
filters.delete(filter);
|
2020-07-15 00:34:28 +02:00
|
|
|
// If the button was not selected, we add the filter.
|
2020-05-23 09:04:51 +02:00
|
|
|
} else {
|
|
|
|
filters.add(filter);
|
|
|
|
}
|
2020-09-21 01:43:18 +02:00
|
|
|
|
2021-02-28 01:27:48 +01:00
|
|
|
save_filters();
|
|
|
|
}
|
2020-05-23 09:04:51 +02:00
|
|
|
|
2020-05-29 11:56:19 +02:00
|
|
|
function show_selected_filters() {
|
2020-06-12 13:07:10 +02:00
|
|
|
// Add `btn-selected-filter` to the buttons to show
|
|
|
|
// which filters are applied.
|
2020-05-23 09:04:51 +02:00
|
|
|
if (filters.size === 0) {
|
2023-09-06 23:41:43 +02:00
|
|
|
$("#recent_view_filter_buttons")
|
2020-05-23 09:04:51 +02:00
|
|
|
.find('[data-filter="all"]')
|
2021-02-18 06:39:15 +01:00
|
|
|
.addClass("btn-recent-selected")
|
|
|
|
.attr("aria-checked", "true");
|
2020-05-23 09:04:51 +02:00
|
|
|
} else {
|
|
|
|
for (const filter of filters) {
|
2023-09-06 23:41:43 +02:00
|
|
|
$("#recent_view_filter_buttons")
|
2021-02-03 23:23:32 +01:00
|
|
|
.find(`[data-filter="${CSS.escape(filter)}"]`)
|
2021-02-18 06:39:15 +01:00
|
|
|
.addClass("btn-recent-selected")
|
|
|
|
.attr("aria-checked", "true");
|
2020-05-23 09:04:51 +02:00
|
|
|
}
|
|
|
|
}
|
2020-05-29 11:56:19 +02:00
|
|
|
}
|
|
|
|
|
2021-02-28 01:27:48 +01:00
|
|
|
export function update_filters_view() {
|
2023-09-06 23:35:51 +02:00
|
|
|
const rendered_filters = render_recent_view_filters({
|
2020-07-15 01:29:15 +02:00
|
|
|
filter_participated: filters.has("participated"),
|
|
|
|
filter_unread: filters.has("unread"),
|
|
|
|
filter_muted: filters.has("include_muted"),
|
2022-04-24 06:13:19 +02:00
|
|
|
filter_pm: filters.has("include_private"),
|
2022-04-14 05:02:53 +02:00
|
|
|
is_spectator: page_params.is_spectator,
|
2020-05-29 11:56:19 +02:00
|
|
|
});
|
|
|
|
$("#recent_filters_group").html(rendered_filters);
|
|
|
|
show_selected_filters();
|
|
|
|
|
2020-06-01 17:17:41 +02:00
|
|
|
topics_widget.hard_redraw();
|
2021-02-28 01:27:48 +01:00
|
|
|
}
|
2020-05-29 11:56:19 +02:00
|
|
|
|
2022-04-24 06:13:19 +02:00
|
|
|
function sort_comparator(a, b) {
|
|
|
|
// compares strings in lowercase and returns -1, 0, 1
|
|
|
|
if (a.toLowerCase() > b.toLowerCase()) {
|
2020-06-01 13:24:29 +02:00
|
|
|
return 1;
|
2022-04-24 06:13:19 +02:00
|
|
|
} else if (a.toLowerCase() === b.toLowerCase()) {
|
2020-06-01 13:24:29 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-04-24 06:13:19 +02:00
|
|
|
function stream_sort(a, b) {
|
|
|
|
if (a.type === b.type) {
|
|
|
|
const a_msg = message_store.get(a.last_msg_id);
|
|
|
|
const b_msg = message_store.get(b.last_msg_id);
|
|
|
|
|
|
|
|
if (a.type === "stream") {
|
2023-07-26 22:07:21 +02:00
|
|
|
const a_stream_name = stream_data.get_stream_name_from_id(a_msg.stream_id);
|
|
|
|
const b_stream_name = stream_data.get_stream_name_from_id(b_msg.stream_id);
|
|
|
|
return sort_comparator(a_stream_name, b_stream_name);
|
2022-04-24 06:13:19 +02:00
|
|
|
}
|
|
|
|
return sort_comparator(a_msg.display_reply_to, b_msg.display_reply_to);
|
2020-06-01 13:24:29 +02:00
|
|
|
}
|
2022-04-24 06:13:19 +02:00
|
|
|
// if type is not same sort between "private" and "stream"
|
|
|
|
return sort_comparator(a.type, b.type);
|
|
|
|
}
|
|
|
|
|
|
|
|
function topic_sort_key(conversation_data) {
|
|
|
|
const message = message_store.get(conversation_data.last_msg_id);
|
|
|
|
if (message.type === "private") {
|
|
|
|
return message.display_reply_to;
|
|
|
|
}
|
|
|
|
return message.topic;
|
|
|
|
}
|
|
|
|
|
|
|
|
function topic_sort(a, b) {
|
|
|
|
return sort_comparator(topic_sort_key(a), topic_sort_key(b));
|
2020-06-01 13:24:29 +02:00
|
|
|
}
|
|
|
|
|
2023-09-13 14:52:55 +02:00
|
|
|
function unread_count(conversation_data) {
|
|
|
|
const message = message_store.get(conversation_data.last_msg_id);
|
|
|
|
return message_to_conversation_unread_count(message);
|
|
|
|
}
|
|
|
|
|
|
|
|
function unread_sort(a, b) {
|
|
|
|
const a_unread_count = unread_count(a);
|
|
|
|
const b_unread_count = unread_count(b);
|
|
|
|
if (a_unread_count !== b_unread_count) {
|
|
|
|
return a_unread_count - b_unread_count;
|
|
|
|
}
|
|
|
|
return a.last_msg_id - b.last_msg_id;
|
|
|
|
}
|
|
|
|
|
2021-05-17 02:36:29 +02:00
|
|
|
function topic_offset_to_visible_area(topic_row) {
|
2023-03-28 23:13:24 +02:00
|
|
|
const $topic_row = $(topic_row);
|
|
|
|
if ($topic_row.length === 0) {
|
|
|
|
// TODO: There is a possibility of topic_row being undefined here
|
|
|
|
// which logically doesn't makes sense. Find out the case and
|
|
|
|
// document it here.
|
|
|
|
// We return undefined here since we don't know anything about the
|
|
|
|
// topic and the callers will take care of undefined being returned.
|
|
|
|
return undefined;
|
|
|
|
}
|
2023-09-06 23:38:15 +02:00
|
|
|
const $scroll_container = $("#recent_view_table .table_fix_head");
|
2023-10-20 10:40:48 +02:00
|
|
|
const thead_height = $scroll_container.find("thead").outerHeight(true);
|
|
|
|
const scroll_container_props = $scroll_container[0].getBoundingClientRect();
|
2021-05-06 06:34:36 +02:00
|
|
|
|
2023-10-20 10:40:48 +02:00
|
|
|
// Since user cannot see row under thead, exclude it as part of the scroll container.
|
|
|
|
const scroll_container_top = scroll_container_props.top + thead_height;
|
|
|
|
const compose_height = $("#compose").outerHeight(true);
|
|
|
|
const scroll_container_bottom = scroll_container_props.bottom - compose_height;
|
2021-05-06 06:34:36 +02:00
|
|
|
|
2023-10-20 10:40:48 +02:00
|
|
|
const topic_props = $topic_row[0].getBoundingClientRect();
|
2021-05-06 06:34:36 +02:00
|
|
|
|
2021-05-17 02:36:29 +02:00
|
|
|
// Topic is above the visible scroll region.
|
2023-10-20 10:40:48 +02:00
|
|
|
if (topic_props.top < scroll_container_top) {
|
2021-05-17 02:36:29 +02:00
|
|
|
return "above";
|
|
|
|
// Topic is below the visible scroll region.
|
2023-10-20 10:40:48 +02:00
|
|
|
} else if (topic_props.bottom > scroll_container_bottom) {
|
2021-05-17 02:36:29 +02:00
|
|
|
return "below";
|
|
|
|
}
|
|
|
|
|
|
|
|
// Topic is visible
|
|
|
|
return "visible";
|
2021-05-06 06:34:36 +02:00
|
|
|
}
|
|
|
|
|
2023-04-18 03:43:03 +02:00
|
|
|
function recenter_focus_if_off_screen() {
|
2023-09-06 23:38:15 +02:00
|
|
|
const table_wrapper_element = document.querySelector("#recent_view_table .table_fix_head");
|
|
|
|
const $topic_rows = $("#recent_view_table table tbody tr");
|
2021-05-17 02:36:29 +02:00
|
|
|
|
2023-05-12 05:30:45 +02:00
|
|
|
if (row_focus >= $topic_rows.length) {
|
2021-05-06 06:34:36 +02:00
|
|
|
// User used a filter which reduced
|
|
|
|
// the number of visible rows.
|
|
|
|
return;
|
|
|
|
}
|
2022-01-25 11:36:19 +01:00
|
|
|
let $topic_row = $topic_rows.eq(row_focus);
|
|
|
|
const topic_offset = topic_offset_to_visible_area($topic_row);
|
2023-03-28 23:13:24 +02:00
|
|
|
if (topic_offset === undefined) {
|
|
|
|
// We don't need to return here since technically topic_offset is not visible.
|
2023-04-24 15:57:45 +02:00
|
|
|
blueslip.error("Unable to get topic from row", {row_focus});
|
2023-03-28 23:13:24 +02:00
|
|
|
}
|
|
|
|
|
2021-05-17 02:36:29 +02:00
|
|
|
if (topic_offset !== "visible") {
|
2021-05-06 06:34:36 +02:00
|
|
|
// Get the element at the center of the table.
|
2021-05-17 02:36:29 +02:00
|
|
|
const position = table_wrapper_element.getBoundingClientRect();
|
2021-05-06 06:34:36 +02:00
|
|
|
const topic_center_x = (position.left + position.right) / 2;
|
|
|
|
const topic_center_y = (position.top + position.bottom) / 2;
|
|
|
|
|
2022-01-25 11:36:19 +01:00
|
|
|
$topic_row = $(document.elementFromPoint(topic_center_x, topic_center_y)).closest("tr");
|
2021-05-06 06:34:36 +02:00
|
|
|
|
2022-01-25 11:36:19 +01:00
|
|
|
row_focus = $topic_rows.index($topic_row);
|
2021-05-06 06:34:36 +02:00
|
|
|
set_table_focus(row_focus, col_focus);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function is_scroll_position_for_render(scroll_container) {
|
|
|
|
const table_bottom_margin = 100; // Extra margin at the bottom of table.
|
|
|
|
const table_row_height = 50;
|
|
|
|
return (
|
|
|
|
scroll_container.scrollTop +
|
|
|
|
scroll_container.clientHeight +
|
|
|
|
table_bottom_margin +
|
|
|
|
table_row_height >
|
|
|
|
scroll_container.scrollHeight
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-02-28 01:27:48 +01:00
|
|
|
export function complete_rerender() {
|
|
|
|
if (!is_visible()) {
|
2020-09-24 07:50:36 +02:00
|
|
|
return;
|
2020-06-01 17:17:41 +02:00
|
|
|
}
|
2021-05-12 17:49:58 +02:00
|
|
|
|
|
|
|
// Show topics list
|
2023-03-02 01:58:25 +01:00
|
|
|
const mapped_topic_values = [...get().values()];
|
2021-05-12 17:49:58 +02:00
|
|
|
|
|
|
|
if (topics_widget) {
|
|
|
|
topics_widget.replace_list_data(mapped_topic_values);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-09-06 23:35:51 +02:00
|
|
|
const rendered_body = render_recent_view_body({
|
2020-07-15 01:29:15 +02:00
|
|
|
filter_participated: filters.has("participated"),
|
|
|
|
filter_unread: filters.has("unread"),
|
|
|
|
filter_muted: filters.has("include_muted"),
|
2022-04-24 06:13:19 +02:00
|
|
|
filter_pm: filters.has("include_private"),
|
2023-09-06 23:42:15 +02:00
|
|
|
search_val: $("#recent_view_search").val() || "",
|
2022-04-14 05:02:53 +02:00
|
|
|
is_spectator: page_params.is_spectator,
|
2020-05-29 11:56:19 +02:00
|
|
|
});
|
2023-09-06 23:38:15 +02:00
|
|
|
$("#recent_view_table").html(rendered_body);
|
2023-01-27 07:35:31 +01:00
|
|
|
|
|
|
|
// `show_selected_filters` needs to be called after the Recent
|
|
|
|
// Conversations view has been added to the DOM, to ensure that filters
|
|
|
|
// have the correct classes (checked or not) if Recent Conversations
|
|
|
|
// was not the first view loaded in the app.
|
|
|
|
show_selected_filters();
|
|
|
|
|
2023-10-04 19:07:01 +02:00
|
|
|
// Update the banner now that it's rendered.
|
|
|
|
update_load_more_banner();
|
|
|
|
|
2023-09-06 23:38:15 +02:00
|
|
|
const $container = $("#recent_view_table table tbody");
|
2022-01-25 11:36:19 +01:00
|
|
|
$container.empty();
|
|
|
|
topics_widget = ListWidget.create($container, mapped_topic_values, {
|
2023-09-06 23:38:15 +02:00
|
|
|
name: "recent_view_table",
|
2023-05-01 13:44:40 +02:00
|
|
|
get_item: ListWidget.default_get_item,
|
2023-09-06 23:38:15 +02:00
|
|
|
$parent_container: $("#recent_view_table"),
|
2023-09-16 00:03:52 +02:00
|
|
|
modifier_html(item) {
|
2023-09-06 23:35:51 +02:00
|
|
|
return render_recent_view_row(format_conversation(item));
|
2020-06-01 17:17:41 +02:00
|
|
|
},
|
|
|
|
filter: {
|
2020-06-12 12:21:34 +02:00
|
|
|
// We use update_filters_view & filters_should_hide_topic to do all the
|
2020-06-01 17:17:41 +02:00
|
|
|
// filtering for us, which is called using click_handlers.
|
2020-07-20 22:18:43 +02:00
|
|
|
predicate(topic_data) {
|
2021-02-28 01:27:48 +01:00
|
|
|
return !filters_should_hide_topic(topic_data);
|
2020-06-01 17:17:41 +02:00
|
|
|
},
|
|
|
|
},
|
2020-06-01 13:24:29 +02:00
|
|
|
sort_fields: {
|
2020-07-20 22:18:43 +02:00
|
|
|
stream_sort,
|
|
|
|
topic_sort,
|
2023-09-13 14:52:55 +02:00
|
|
|
unread_sort,
|
2023-05-03 07:06:19 +02:00
|
|
|
...ListWidget.generic_sort_functions("numeric", ["last_msg_id"]),
|
2020-06-01 13:24:29 +02:00
|
|
|
},
|
2020-06-01 17:17:41 +02:00
|
|
|
html_selector: get_topic_row,
|
2023-09-06 23:38:15 +02:00
|
|
|
$simplebar_container: $("#recent_view_table .table_fix_head"),
|
2022-11-01 09:04:33 +01:00
|
|
|
callback_after_render: () => setTimeout(revive_current_focus, 0),
|
2021-05-06 06:34:36 +02:00
|
|
|
is_scroll_position_for_render,
|
2023-03-07 19:59:13 +01:00
|
|
|
post_scroll__pre_render_callback() {
|
|
|
|
// Hide popovers on scroll in recent conversations.
|
|
|
|
popovers.hide_all();
|
|
|
|
|
|
|
|
// Update the focused element for keyboard navigation if needed.
|
2023-04-18 03:43:03 +02:00
|
|
|
recenter_focus_if_off_screen();
|
2023-03-07 19:59:13 +01:00
|
|
|
},
|
2021-05-06 06:31:56 +02:00
|
|
|
get_min_load_count,
|
2020-06-01 17:17:41 +02:00
|
|
|
});
|
2021-02-28 01:27:48 +01:00
|
|
|
}
|
2020-04-08 13:59:56 +02:00
|
|
|
|
2021-02-28 01:27:48 +01:00
|
|
|
export function show() {
|
2023-09-23 08:20:22 +02:00
|
|
|
views_util.show({
|
|
|
|
highlight_view_in_left_sidebar: left_sidebar_navigation_area.highlight_recent_view,
|
|
|
|
$view: $("#recent_view"),
|
|
|
|
// We want to show `new stream message` instead of
|
|
|
|
// `new topic`, which we are already doing in this
|
|
|
|
// function. So, we reuse it here.
|
2023-10-04 18:36:15 +02:00
|
|
|
update_compose: compose_closed_ui.update_buttons_for_non_stream_views,
|
2023-09-23 08:20:22 +02:00
|
|
|
is_recent_view: true,
|
|
|
|
is_visible,
|
|
|
|
set_visible,
|
|
|
|
complete_rerender,
|
|
|
|
});
|
2021-02-28 01:27:48 +01:00
|
|
|
}
|
2020-06-20 10:17:44 +02:00
|
|
|
|
2020-07-02 13:47:28 +02:00
|
|
|
function filter_buttons() {
|
2020-07-15 01:29:15 +02:00
|
|
|
return $("#recent_filters_group").children();
|
2020-07-02 13:47:28 +02:00
|
|
|
}
|
|
|
|
|
2021-02-28 01:27:48 +01:00
|
|
|
export function hide() {
|
2023-09-23 08:20:22 +02:00
|
|
|
views_util.hide({
|
|
|
|
$view: $("#recent_view"),
|
|
|
|
set_visible,
|
|
|
|
});
|
2021-02-28 01:27:48 +01:00
|
|
|
}
|
2020-07-05 12:19:09 +02:00
|
|
|
|
2021-03-12 08:08:48 +01:00
|
|
|
function is_focus_at_last_table_row() {
|
2022-10-19 20:38:18 +02:00
|
|
|
return row_focus >= topics_widget.get_current_list().length - 1;
|
2021-03-12 08:08:48 +01:00
|
|
|
}
|
|
|
|
|
2022-07-22 16:32:51 +02:00
|
|
|
function has_unread(row) {
|
|
|
|
const last_msg_id = topics_widget.get_current_list()[row].last_msg_id;
|
|
|
|
const last_msg = message_store.get(last_msg_id);
|
2022-10-21 11:50:18 +02:00
|
|
|
if (last_msg.type === "stream") {
|
|
|
|
return unread.num_unread_for_topic(last_msg.stream_id, last_msg.topic) > 0;
|
|
|
|
}
|
|
|
|
return unread.num_unread_for_user_ids_string(last_msg.to_user_ids) > 0;
|
2022-07-22 16:32:51 +02:00
|
|
|
}
|
|
|
|
|
2021-09-06 17:43:52 +02:00
|
|
|
export function focus_clicked_element(topic_row_index, col, topic_key) {
|
2022-01-25 11:36:19 +01:00
|
|
|
$current_focus_elem = "table";
|
2021-03-13 16:45:47 +01:00
|
|
|
col_focus = col;
|
2021-09-06 15:22:32 +02:00
|
|
|
row_focus = topic_row_index;
|
2021-09-06 17:43:52 +02:00
|
|
|
|
|
|
|
if (col === COLUMNS.topic) {
|
|
|
|
last_visited_topic = topic_key;
|
|
|
|
}
|
2021-05-10 14:21:37 +02:00
|
|
|
// Set compose_closed_ui reply button text. The rest of the table
|
|
|
|
// focus logic should be a noop.
|
|
|
|
set_table_focus(row_focus, col_focus);
|
2021-03-13 16:45:47 +01:00
|
|
|
}
|
|
|
|
|
2022-07-22 16:32:51 +02:00
|
|
|
function left_arrow_navigation(row, col) {
|
2022-04-24 06:13:19 +02:00
|
|
|
const type = get_row_type(row);
|
|
|
|
|
|
|
|
if (type === "stream" && col === MAX_SELECTABLE_TOPIC_COLS - 1 && !has_unread(row)) {
|
2022-07-22 16:32:51 +02:00
|
|
|
col_focus -= 1;
|
|
|
|
}
|
2022-07-22 15:13:28 +02:00
|
|
|
|
2022-04-24 06:13:19 +02:00
|
|
|
col_focus -= 1;
|
2022-07-22 15:13:28 +02:00
|
|
|
if (col_focus < 0) {
|
2022-04-24 06:13:19 +02:00
|
|
|
col_focus = get_max_selectable_cols(row) - 1;
|
2022-07-22 15:13:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-22 16:32:51 +02:00
|
|
|
function right_arrow_navigation(row, col) {
|
2022-04-24 06:13:19 +02:00
|
|
|
const type = get_row_type(row);
|
|
|
|
|
|
|
|
if (type === "stream" && col === 1 && !has_unread(row)) {
|
2022-07-22 16:32:51 +02:00
|
|
|
col_focus += 1;
|
|
|
|
}
|
2022-07-22 15:13:28 +02:00
|
|
|
|
2022-04-24 06:13:19 +02:00
|
|
|
col_focus += 1;
|
|
|
|
if (col_focus >= get_max_selectable_cols(row)) {
|
2022-07-22 15:13:28 +02:00
|
|
|
col_focus = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-22 16:32:51 +02:00
|
|
|
function up_arrow_navigation(row, col) {
|
2022-04-24 06:13:19 +02:00
|
|
|
row_focus -= 1;
|
|
|
|
if (row_focus < 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const type = get_row_type(row);
|
|
|
|
|
|
|
|
if (type === "stream" && col === 2 && row - 1 >= 0 && !has_unread(row - 1)) {
|
2022-07-22 16:32:51 +02:00
|
|
|
col_focus = 1;
|
|
|
|
}
|
2022-07-22 15:13:28 +02:00
|
|
|
}
|
|
|
|
|
2022-10-30 06:53:27 +01:00
|
|
|
function down_arrow_navigation() {
|
2022-07-22 15:13:28 +02:00
|
|
|
row_focus += 1;
|
|
|
|
}
|
|
|
|
|
2022-10-18 08:00:19 +02:00
|
|
|
function get_page_up_down_delta() {
|
2023-09-06 23:38:15 +02:00
|
|
|
const table_height = $("#recent_view_table .table_fix_head").height();
|
|
|
|
const table_header_height = $("#recent_view_table table thead").height();
|
2022-10-18 08:00:19 +02:00
|
|
|
const compose_box_height = $("#compose").height();
|
|
|
|
// One usually wants PageDown to move what had been the bottom row
|
|
|
|
// to now be at the top, so one can be confident one will see
|
|
|
|
// every row using it. This offset helps achieve that goal.
|
|
|
|
//
|
|
|
|
// See navigate.amount_to_paginate for similar logic in the message feed.
|
|
|
|
const scrolling_reduction_to_maintain_context = 75;
|
|
|
|
|
|
|
|
const delta =
|
|
|
|
table_height -
|
|
|
|
table_header_height -
|
|
|
|
compose_box_height -
|
|
|
|
scrolling_reduction_to_maintain_context;
|
|
|
|
return delta;
|
|
|
|
}
|
|
|
|
|
|
|
|
function page_up_navigation() {
|
2023-04-25 18:01:02 +02:00
|
|
|
const $scroll_container = scroll_util.get_scroll_element(
|
2023-09-06 23:38:15 +02:00
|
|
|
$("#recent_view_table .table_fix_head"),
|
2023-04-25 18:01:02 +02:00
|
|
|
);
|
2022-10-18 08:00:19 +02:00
|
|
|
const delta = get_page_up_down_delta();
|
2022-10-19 20:41:51 +02:00
|
|
|
const new_scrollTop = $scroll_container.scrollTop() - delta;
|
|
|
|
if (new_scrollTop <= 0) {
|
|
|
|
row_focus = 0;
|
|
|
|
}
|
|
|
|
$scroll_container.scrollTop(new_scrollTop);
|
2022-10-18 08:00:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function page_down_navigation() {
|
2023-04-25 18:01:02 +02:00
|
|
|
const $scroll_container = scroll_util.get_scroll_element(
|
2023-09-06 23:38:15 +02:00
|
|
|
$("#recent_view_table .table_fix_head"),
|
2023-04-25 18:01:02 +02:00
|
|
|
);
|
2022-10-18 08:00:19 +02:00
|
|
|
const delta = get_page_up_down_delta();
|
2022-10-19 20:41:51 +02:00
|
|
|
const new_scrollTop = $scroll_container.scrollTop() + delta;
|
2023-09-06 23:38:15 +02:00
|
|
|
const table_height = $("#recent_view_table .table_fix_head").height();
|
2022-10-19 20:41:51 +02:00
|
|
|
if (new_scrollTop >= table_height) {
|
|
|
|
row_focus = topics_widget.get_current_list().length - 1;
|
|
|
|
}
|
|
|
|
$scroll_container.scrollTop(new_scrollTop);
|
2022-10-18 08:00:19 +02:00
|
|
|
}
|
|
|
|
|
2022-04-24 06:13:19 +02:00
|
|
|
function check_row_type_transition(row, col) {
|
|
|
|
// This function checks if the row is transitioning
|
2023-06-16 13:23:39 +02:00
|
|
|
// from type "Direct messages" to "Stream" or vice versa.
|
2022-04-24 06:13:19 +02:00
|
|
|
// This helps in setting the col_focus as maximum column
|
|
|
|
// of both the type are different.
|
|
|
|
if (row < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const max_col = get_max_selectable_cols(row);
|
|
|
|
if (col > max_col - 1) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-03-16 15:15:33 +01:00
|
|
|
export function change_focused_element($elt, input_key) {
|
2020-06-20 10:17:44 +02:00
|
|
|
// Called from hotkeys.js; like all logic in that module,
|
|
|
|
// returning true will cause the caller to do
|
|
|
|
// preventDefault/stopPropagation; false will let the browser
|
|
|
|
// handle the key.
|
|
|
|
|
2023-09-06 23:42:15 +02:00
|
|
|
if ($elt.attr("id") === "recent_view_search") {
|
2020-06-20 10:17:44 +02:00
|
|
|
// Since the search box a text area, we want the browser to handle
|
|
|
|
// Left/Right and selection within the widget; but if the user
|
|
|
|
// arrows off the edges, we should move focus to the adjacent widgets..
|
2023-09-06 23:42:15 +02:00
|
|
|
const textInput = $("#recent_view_search").get(0);
|
2020-06-20 10:17:44 +02:00
|
|
|
const start = textInput.selectionStart;
|
|
|
|
const end = textInput.selectionEnd;
|
|
|
|
const text_length = textInput.value.length;
|
|
|
|
let is_selected = false;
|
|
|
|
if (end - start > 0) {
|
|
|
|
is_selected = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (input_key) {
|
2021-04-25 22:54:23 +02:00
|
|
|
// Allow browser to handle all
|
2021-03-10 13:56:10 +01:00
|
|
|
// character keypresses.
|
2020-07-02 14:02:51 +02:00
|
|
|
case "vim_left":
|
|
|
|
case "vim_right":
|
|
|
|
case "vim_down":
|
|
|
|
case "vim_up":
|
2023-09-06 23:54:45 +02:00
|
|
|
case "open_recent_view":
|
2020-07-02 14:02:51 +02:00
|
|
|
return false;
|
|
|
|
case "shift_tab":
|
2022-01-25 11:36:19 +01:00
|
|
|
$current_focus_elem = filter_buttons().last();
|
2020-07-02 14:02:51 +02:00
|
|
|
break;
|
2020-07-15 02:14:03 +02:00
|
|
|
case "left_arrow":
|
|
|
|
if (start !== 0 || is_selected) {
|
|
|
|
return false;
|
|
|
|
}
|
2022-01-25 11:36:19 +01:00
|
|
|
$current_focus_elem = filter_buttons().last();
|
2020-07-15 02:14:03 +02:00
|
|
|
break;
|
2020-07-02 14:02:51 +02:00
|
|
|
case "tab":
|
2022-01-25 11:36:19 +01:00
|
|
|
$current_focus_elem = filter_buttons().first();
|
2020-07-02 14:02:51 +02:00
|
|
|
break;
|
2020-07-15 02:14:03 +02:00
|
|
|
case "right_arrow":
|
|
|
|
if (end !== text_length || is_selected) {
|
|
|
|
return false;
|
|
|
|
}
|
2022-01-25 11:36:19 +01:00
|
|
|
$current_focus_elem = filter_buttons().first();
|
2020-07-15 02:14:03 +02:00
|
|
|
break;
|
|
|
|
case "down_arrow":
|
|
|
|
set_table_focus(row_focus, col_focus);
|
|
|
|
return true;
|
|
|
|
case "click":
|
2020-07-15 00:34:28 +02:00
|
|
|
// Note: current_focus_elem can be different here, so we just
|
2020-07-20 21:24:26 +02:00
|
|
|
// set current_focus_elem to the input box, we don't want .trigger("focus") on
|
2020-07-15 00:34:28 +02:00
|
|
|
// it since it is already focused.
|
2020-08-11 01:47:44 +02:00
|
|
|
// We only do this for search because we don't want the focus to
|
2020-07-15 00:34:28 +02:00
|
|
|
// go away from the input box when `revive_current_focus` is called
|
|
|
|
// on rerender when user is typing.
|
2023-09-06 23:42:15 +02:00
|
|
|
$current_focus_elem = $("#recent_view_search");
|
2021-05-10 14:05:30 +02:00
|
|
|
compose_closed_ui.set_standard_text_for_reply_button();
|
2020-07-15 02:14:03 +02:00
|
|
|
return true;
|
2021-01-04 12:02:59 +01:00
|
|
|
case "escape":
|
2021-04-02 12:36:02 +02:00
|
|
|
if (is_table_focused()) {
|
2021-03-09 13:21:32 +01:00
|
|
|
return false;
|
|
|
|
}
|
2021-01-04 12:02:59 +01:00
|
|
|
set_table_focus(row_focus, col_focus);
|
|
|
|
return true;
|
2020-06-20 10:17:44 +02:00
|
|
|
}
|
2021-03-16 15:15:33 +01:00
|
|
|
} else if ($elt.hasClass("btn-recent-filters")) {
|
2020-06-20 10:17:44 +02:00
|
|
|
switch (input_key) {
|
2021-03-13 16:45:47 +01:00
|
|
|
case "click":
|
2022-01-25 11:36:19 +01:00
|
|
|
$current_focus_elem = $elt;
|
2021-03-13 16:45:47 +01:00
|
|
|
return true;
|
2020-07-02 14:02:51 +02:00
|
|
|
case "shift_tab":
|
|
|
|
case "vim_left":
|
2020-07-15 02:14:03 +02:00
|
|
|
case "left_arrow":
|
2021-03-16 15:15:33 +01:00
|
|
|
if (filter_buttons().first()[0] === $elt[0]) {
|
2023-09-06 23:42:15 +02:00
|
|
|
$current_focus_elem = $("#recent_view_search");
|
2020-07-15 02:14:03 +02:00
|
|
|
} else {
|
2022-01-25 11:36:19 +01:00
|
|
|
$current_focus_elem = $elt.prev();
|
2020-07-15 02:14:03 +02:00
|
|
|
}
|
|
|
|
break;
|
2020-07-02 14:02:51 +02:00
|
|
|
case "tab":
|
|
|
|
case "vim_right":
|
2020-07-15 02:14:03 +02:00
|
|
|
case "right_arrow":
|
2021-03-16 15:15:33 +01:00
|
|
|
if (filter_buttons().last()[0] === $elt[0]) {
|
2023-09-06 23:42:15 +02:00
|
|
|
$current_focus_elem = $("#recent_view_search");
|
2020-07-15 02:14:03 +02:00
|
|
|
} else {
|
2022-01-25 11:36:19 +01:00
|
|
|
$current_focus_elem = $elt.next();
|
2020-07-15 02:14:03 +02:00
|
|
|
}
|
|
|
|
break;
|
2020-07-02 14:02:51 +02:00
|
|
|
case "vim_down":
|
2020-07-15 02:14:03 +02:00
|
|
|
case "down_arrow":
|
|
|
|
set_table_focus(row_focus, col_focus);
|
|
|
|
return true;
|
2021-03-09 13:21:32 +01:00
|
|
|
case "escape":
|
2021-04-02 12:36:02 +02:00
|
|
|
if (is_table_focused()) {
|
2021-03-09 13:21:32 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
set_table_focus(row_focus, col_focus);
|
|
|
|
return true;
|
2020-06-20 10:17:44 +02:00
|
|
|
}
|
2021-04-02 12:36:02 +02:00
|
|
|
} else if (is_table_focused()) {
|
2022-10-23 08:41:07 +02:00
|
|
|
// Don't process hotkeys in table if there are no rows.
|
|
|
|
if (!topics_widget || topics_widget.get_current_list().length === 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-06-20 10:17:44 +02:00
|
|
|
// For arrowing around the table of topics, we implement left/right
|
|
|
|
// wraparound. Going off the top or the bottom takes one
|
|
|
|
// to the navigation at the top (see set_table_focus).
|
|
|
|
switch (input_key) {
|
2021-03-09 13:21:32 +01:00
|
|
|
case "escape":
|
|
|
|
return false;
|
2023-09-06 23:54:45 +02:00
|
|
|
case "open_recent_view":
|
2020-09-23 09:43:59 +02:00
|
|
|
set_default_focus();
|
|
|
|
return true;
|
2020-07-02 14:02:51 +02:00
|
|
|
case "shift_tab":
|
|
|
|
case "vim_left":
|
2020-07-15 02:14:03 +02:00
|
|
|
case "left_arrow":
|
2022-07-22 16:32:51 +02:00
|
|
|
left_arrow_navigation(row_focus, col_focus);
|
2020-07-15 02:14:03 +02:00
|
|
|
break;
|
2020-07-02 14:02:51 +02:00
|
|
|
case "tab":
|
|
|
|
case "vim_right":
|
2020-07-15 02:14:03 +02:00
|
|
|
case "right_arrow":
|
2022-07-22 16:32:51 +02:00
|
|
|
right_arrow_navigation(row_focus, col_focus);
|
2020-07-15 02:14:03 +02:00
|
|
|
break;
|
2022-10-30 06:53:27 +01:00
|
|
|
case "down_arrow":
|
2020-07-02 14:02:51 +02:00
|
|
|
case "vim_down":
|
2021-03-12 08:08:48 +01:00
|
|
|
// We stop user at last table row
|
|
|
|
// so that user doesn't end up in
|
|
|
|
// input box where it is impossible to
|
|
|
|
// get out of using vim_up / vim_down
|
|
|
|
// keys. This also blocks the user from
|
|
|
|
// having `jjjj` typed in the input box
|
|
|
|
// when continuously pressing `j`.
|
|
|
|
if (is_focus_at_last_table_row()) {
|
|
|
|
return true;
|
|
|
|
}
|
2022-10-30 06:53:27 +01:00
|
|
|
down_arrow_navigation();
|
2020-07-15 02:14:03 +02:00
|
|
|
break;
|
2020-07-02 14:02:51 +02:00
|
|
|
case "vim_up":
|
2021-03-12 08:08:48 +01:00
|
|
|
// See comment on vim_down.
|
|
|
|
// Similarly, blocks the user from
|
|
|
|
// having `kkkk` typed in the input box
|
|
|
|
// when continuously pressing `k`.
|
|
|
|
if (row_focus === 0) {
|
|
|
|
return true;
|
|
|
|
}
|
2022-07-22 16:32:51 +02:00
|
|
|
up_arrow_navigation(row_focus, col_focus);
|
2021-03-12 08:08:48 +01:00
|
|
|
break;
|
2020-07-15 02:14:03 +02:00
|
|
|
case "up_arrow":
|
2022-07-22 16:32:51 +02:00
|
|
|
up_arrow_navigation(row_focus, col_focus);
|
2022-04-24 06:13:19 +02:00
|
|
|
break;
|
2022-10-18 08:00:19 +02:00
|
|
|
case "page_up":
|
|
|
|
page_up_navigation();
|
|
|
|
return true;
|
|
|
|
case "page_down":
|
|
|
|
page_down_navigation();
|
|
|
|
return true;
|
2020-06-20 10:17:44 +02:00
|
|
|
}
|
2022-04-24 06:13:19 +02:00
|
|
|
|
|
|
|
if (check_row_type_transition(row_focus, col_focus)) {
|
|
|
|
col_focus = get_max_selectable_cols(row_focus) - 1;
|
|
|
|
}
|
|
|
|
|
2021-05-17 02:36:29 +02:00
|
|
|
set_table_focus(row_focus, col_focus, true);
|
2020-06-20 10:17:44 +02:00
|
|
|
return true;
|
|
|
|
}
|
2022-01-25 11:36:19 +01:00
|
|
|
if ($current_focus_elem && input_key !== "escape") {
|
|
|
|
$current_focus_elem.trigger("focus");
|
|
|
|
if ($current_focus_elem.hasClass("btn-recent-filters")) {
|
2021-05-10 14:05:30 +02:00
|
|
|
compose_closed_ui.set_standard_text_for_reply_button();
|
2021-05-07 18:38:01 +02:00
|
|
|
}
|
2021-03-09 13:21:32 +01:00
|
|
|
return true;
|
2020-06-20 10:17:44 +02:00
|
|
|
}
|
|
|
|
|
2020-09-24 12:33:12 +02:00
|
|
|
return false;
|
2021-02-28 01:27:48 +01:00
|
|
|
}
|
2023-01-27 07:19:45 +01:00
|
|
|
|
2023-10-04 19:07:01 +02:00
|
|
|
export function initialize({
|
|
|
|
on_click_participant,
|
|
|
|
on_mark_pm_as_read,
|
|
|
|
on_mark_topic_as_read,
|
|
|
|
maybe_load_older_messages,
|
|
|
|
}) {
|
2023-01-27 07:19:45 +01:00
|
|
|
// load filters from local storage.
|
|
|
|
if (!page_params.is_spectator) {
|
|
|
|
// A user may have a stored filter and can log out
|
|
|
|
// to see web public view. This ensures no filters are
|
|
|
|
// selected for spectators.
|
|
|
|
filters = new Set(ls.get(ls_key));
|
|
|
|
}
|
2023-02-14 06:47:36 +01:00
|
|
|
|
2023-09-22 12:59:50 +02:00
|
|
|
$("body").on("click", "#recent_view_table .recent_view_participant_avatar", function (e) {
|
|
|
|
const participant_user_id = Number.parseInt($(this).parent().attr("data-user-id"), 10);
|
2023-02-14 06:47:36 +01:00
|
|
|
e.stopPropagation();
|
2023-10-07 03:32:53 +02:00
|
|
|
on_click_participant(this, participant_user_id);
|
2023-02-14 06:47:36 +01:00
|
|
|
});
|
|
|
|
|
2023-04-22 23:10:34 +02:00
|
|
|
$("body").on(
|
|
|
|
"keydown",
|
|
|
|
".on_hover_topic_mute, .on_hover_topic_unmute",
|
|
|
|
ui_util.convert_enter_to_click,
|
|
|
|
);
|
2023-02-14 06:47:36 +01:00
|
|
|
|
2023-04-22 23:10:34 +02:00
|
|
|
// Mute topic in a unmuted stream
|
2023-09-06 23:38:15 +02:00
|
|
|
$("body").on("click", "#recent_view_table .stream_unmuted.on_hover_topic_mute", (e) => {
|
2023-02-14 06:47:36 +01:00
|
|
|
e.stopPropagation();
|
|
|
|
const $elt = $(e.target);
|
|
|
|
const topic_row_index = $elt.closest("tr").index();
|
|
|
|
focus_clicked_element(topic_row_index, COLUMNS.mute);
|
2023-06-28 13:24:25 +02:00
|
|
|
user_topics.set_visibility_policy_for_element(
|
|
|
|
$elt,
|
|
|
|
user_topics.all_visibility_policies.MUTED,
|
|
|
|
);
|
2023-02-14 06:47:36 +01:00
|
|
|
});
|
|
|
|
|
2023-04-22 23:10:34 +02:00
|
|
|
// Unmute topic in a unmuted stream
|
2023-09-06 23:38:15 +02:00
|
|
|
$("body").on("click", "#recent_view_table .stream_unmuted.on_hover_topic_unmute", (e) => {
|
2023-04-22 23:10:34 +02:00
|
|
|
e.stopPropagation();
|
|
|
|
const $elt = $(e.target);
|
|
|
|
const topic_row_index = $elt.closest("tr").index();
|
|
|
|
focus_clicked_element(topic_row_index, COLUMNS.mute);
|
2023-06-28 13:24:25 +02:00
|
|
|
user_topics.set_visibility_policy_for_element(
|
|
|
|
$elt,
|
|
|
|
user_topics.all_visibility_policies.INHERIT,
|
|
|
|
);
|
2023-04-22 23:10:34 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
// Unmute topic in a muted stream
|
2023-09-06 23:38:15 +02:00
|
|
|
$("body").on("click", "#recent_view_table .stream_muted.on_hover_topic_unmute", (e) => {
|
2023-04-22 23:10:34 +02:00
|
|
|
e.stopPropagation();
|
|
|
|
const $elt = $(e.target);
|
|
|
|
const topic_row_index = $elt.closest("tr").index();
|
|
|
|
focus_clicked_element(topic_row_index, COLUMNS.mute);
|
2023-06-28 13:24:25 +02:00
|
|
|
user_topics.set_visibility_policy_for_element(
|
|
|
|
$elt,
|
|
|
|
user_topics.all_visibility_policies.UNMUTED,
|
|
|
|
);
|
2023-04-22 23:10:34 +02:00
|
|
|
});
|
2023-02-14 06:47:36 +01:00
|
|
|
|
2023-04-22 23:10:34 +02:00
|
|
|
// Mute topic in a muted stream
|
2023-09-06 23:38:15 +02:00
|
|
|
$("body").on("click", "#recent_view_table .stream_muted.on_hover_topic_mute", (e) => {
|
2023-02-14 06:47:36 +01:00
|
|
|
e.stopPropagation();
|
|
|
|
const $elt = $(e.target);
|
|
|
|
const topic_row_index = $elt.closest("tr").index();
|
|
|
|
focus_clicked_element(topic_row_index, COLUMNS.mute);
|
2023-06-28 13:24:25 +02:00
|
|
|
user_topics.set_visibility_policy_for_element(
|
|
|
|
$elt,
|
|
|
|
user_topics.all_visibility_policies.INHERIT,
|
|
|
|
);
|
2023-02-14 06:47:36 +01:00
|
|
|
});
|
|
|
|
|
2023-09-06 23:42:15 +02:00
|
|
|
$("body").on("click", "#recent_view_search", (e) => {
|
2023-02-14 06:47:36 +01:00
|
|
|
e.stopPropagation();
|
|
|
|
change_focused_element($(e.target), "click");
|
|
|
|
});
|
|
|
|
|
2023-09-06 23:38:15 +02:00
|
|
|
$("body").on("click", "#recent_view_table .on_hover_topic_read", (e) => {
|
2023-02-14 06:47:36 +01:00
|
|
|
e.stopPropagation();
|
|
|
|
const $elt = $(e.currentTarget);
|
|
|
|
const topic_row_index = $elt.closest("tr").index();
|
|
|
|
focus_clicked_element(topic_row_index, COLUMNS.read);
|
|
|
|
const user_ids_string = $elt.attr("data-user-ids-string");
|
|
|
|
if (user_ids_string) {
|
2023-06-16 13:23:39 +02:00
|
|
|
// direct message row
|
2023-10-07 08:46:39 +02:00
|
|
|
on_mark_pm_as_read(user_ids_string);
|
2023-02-14 06:47:36 +01:00
|
|
|
} else {
|
|
|
|
// Stream row
|
|
|
|
const stream_id = Number.parseInt($elt.attr("data-stream-id"), 10);
|
|
|
|
const topic = $elt.attr("data-topic-name");
|
2023-10-07 08:46:39 +02:00
|
|
|
on_mark_topic_as_read(stream_id, topic);
|
2023-02-14 06:47:36 +01:00
|
|
|
}
|
2023-03-16 11:40:28 +01:00
|
|
|
// If `unread` filter is selected, the focused topic row gets removed
|
|
|
|
// and we automatically move one row down.
|
|
|
|
if (!filters.has("unread")) {
|
|
|
|
change_focused_element($elt, "down_arrow");
|
|
|
|
}
|
2023-02-14 06:47:36 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
$("body").on("keydown", ".on_hover_topic_read", ui_util.convert_enter_to_click);
|
|
|
|
|
|
|
|
$("body").on("click", ".btn-recent-filters", (e) => {
|
|
|
|
e.stopPropagation();
|
|
|
|
if (page_params.is_spectator) {
|
|
|
|
// Filter buttons are disabled for spectator.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
change_focused_element($(e.target), "click");
|
|
|
|
set_filter(e.currentTarget.dataset.filter);
|
|
|
|
update_filters_view();
|
|
|
|
revive_current_focus();
|
|
|
|
});
|
|
|
|
|
|
|
|
$("body").on("click", "td.recent_topic_stream", (e) => {
|
|
|
|
e.stopPropagation();
|
|
|
|
const topic_row_index = $(e.target).closest("tr").index();
|
|
|
|
focus_clicked_element(topic_row_index, COLUMNS.stream);
|
|
|
|
window.location.href = $(e.currentTarget).find("a").attr("href");
|
|
|
|
});
|
|
|
|
|
|
|
|
$("body").on("click", "td.recent_topic_name", (e) => {
|
|
|
|
e.stopPropagation();
|
|
|
|
// The element's parent may re-render while it is being passed to
|
|
|
|
// other functions, so, we get topic_key first.
|
|
|
|
const $topic_row = $(e.target).closest("tr");
|
|
|
|
const topic_key = $topic_row.attr("id").slice("recent_conversation:".length);
|
|
|
|
const topic_row_index = $topic_row.index();
|
|
|
|
focus_clicked_element(topic_row_index, COLUMNS.topic, topic_key);
|
|
|
|
window.location.href = $(e.currentTarget).find("a").attr("href");
|
|
|
|
});
|
|
|
|
|
|
|
|
// Search for all table rows (this combines stream & topic names)
|
|
|
|
$("body").on(
|
|
|
|
"keyup",
|
2023-09-06 23:42:15 +02:00
|
|
|
"#recent_view_search",
|
2023-02-14 06:47:36 +01:00
|
|
|
_.debounce(() => {
|
|
|
|
update_filters_view();
|
|
|
|
// Wait for user to go idle before initiating search.
|
|
|
|
}, 300),
|
|
|
|
);
|
|
|
|
|
2023-09-06 23:42:15 +02:00
|
|
|
$("body").on("click", "#recent_view_search_clear", (e) => {
|
2023-02-14 06:47:36 +01:00
|
|
|
e.stopPropagation();
|
2023-09-06 23:42:15 +02:00
|
|
|
$("#recent_view_search").val("");
|
2023-02-14 06:47:36 +01:00
|
|
|
update_filters_view();
|
|
|
|
});
|
2023-10-04 19:07:01 +02:00
|
|
|
|
|
|
|
$("body").on("click", ".recent-view-load-more-container .fetch-messages-button", () => {
|
|
|
|
maybe_load_older_messages();
|
|
|
|
$(".recent-view-load-more-container .button-label").toggleClass("invisible", true);
|
|
|
|
$(".recent-view-load-more-container .fetch-messages-button").prop("disabled", true);
|
|
|
|
loading.make_indicator(
|
|
|
|
$(".recent-view-load-more-container .fetch-messages-button .loading-indicator"),
|
|
|
|
{width: 20},
|
|
|
|
);
|
|
|
|
});
|
2023-01-27 07:19:45 +01:00
|
|
|
}
|