mirror of
https://github.com/zulip/zulip.git
synced 2025-11-05 22:43:42 +00:00
The banner telling the user to scroll down to the message previously didn't disappear when the user scrolled past it manually, which is not ideal. Keep track of which message is associated with this notification, and clear the banner when the message scrolls above the bottom of the viewport.
291 lines
9.6 KiB
JavaScript
291 lines
9.6 KiB
JavaScript
import $ from "jquery";
|
|
import _ from "lodash";
|
|
|
|
import * as floating_recipient_bar from "./floating_recipient_bar";
|
|
import * as hash_util from "./hash_util";
|
|
import * as loading from "./loading";
|
|
import * as message_fetch from "./message_fetch";
|
|
import * as message_lists from "./message_lists";
|
|
import * as message_viewport from "./message_viewport";
|
|
import * as narrow_banner from "./narrow_banner";
|
|
import * as narrow_state from "./narrow_state";
|
|
import * as notifications from "./notifications";
|
|
import * as recent_topics_util from "./recent_topics_util";
|
|
import * as unread from "./unread";
|
|
import * as unread_ops from "./unread_ops";
|
|
import * as unread_ui from "./unread_ui";
|
|
|
|
let actively_scrolling = false;
|
|
|
|
// Tracks whether the next scroll that will complete is initiated by
|
|
// code, not the user, and thus should avoid moving the selected
|
|
// message.
|
|
let update_selection_on_next_scroll = true;
|
|
|
|
export function suppress_selection_update_on_next_scroll() {
|
|
update_selection_on_next_scroll = false;
|
|
}
|
|
|
|
// Whether a keyboard shortcut is triggering a message feed scroll event.
|
|
let keyboard_triggered_current_scroll = false;
|
|
|
|
export function mark_keyboard_triggered_current_scroll() {
|
|
keyboard_triggered_current_scroll = true;
|
|
}
|
|
|
|
let loading_older_messages_indicator_showing = false;
|
|
let loading_newer_messages_indicator_showing = false;
|
|
|
|
export function show_loading_older() {
|
|
if (!loading_older_messages_indicator_showing) {
|
|
loading.make_indicator($("#loading_older_messages_indicator"), {abs_positioned: true});
|
|
loading_older_messages_indicator_showing = true;
|
|
floating_recipient_bar.hide();
|
|
}
|
|
}
|
|
|
|
export function hide_loading_older() {
|
|
if (loading_older_messages_indicator_showing) {
|
|
loading.destroy_indicator($("#loading_older_messages_indicator"));
|
|
loading_older_messages_indicator_showing = false;
|
|
}
|
|
}
|
|
|
|
export function show_loading_newer() {
|
|
if (!loading_newer_messages_indicator_showing) {
|
|
$(".bottom-messages-logo").show();
|
|
loading.make_indicator($("#loading_newer_messages_indicator"), {abs_positioned: true});
|
|
loading_newer_messages_indicator_showing = true;
|
|
floating_recipient_bar.hide();
|
|
}
|
|
}
|
|
|
|
export function hide_loading_newer() {
|
|
if (loading_newer_messages_indicator_showing) {
|
|
$(".bottom-messages-logo").hide();
|
|
loading.destroy_indicator($("#loading_newer_messages_indicator"));
|
|
loading_newer_messages_indicator_showing = false;
|
|
}
|
|
}
|
|
|
|
export function hide_indicators() {
|
|
hide_loading_older();
|
|
hide_loading_newer();
|
|
}
|
|
|
|
export function show_history_limit_notice() {
|
|
$(".top-messages-logo").hide();
|
|
$(".history-limited-box").show();
|
|
narrow_banner.hide_empty_narrow_message();
|
|
}
|
|
|
|
export function hide_history_limit_notice() {
|
|
$(".top-messages-logo").show();
|
|
$(".history-limited-box").hide();
|
|
}
|
|
|
|
export function hide_end_of_results_notice() {
|
|
$(".all-messages-search-caution").hide();
|
|
}
|
|
|
|
export function show_end_of_results_notice() {
|
|
$(".all-messages-search-caution").show();
|
|
|
|
// Set the link to point to this search with streams:public added.
|
|
// Note that element we adjust is not visible to spectators.
|
|
const operators = narrow_state.filter().operators();
|
|
const update_hash = hash_util.search_public_streams_notice_url(operators);
|
|
$(".all-messages-search-caution a.search-shared-history").attr("href", update_hash);
|
|
}
|
|
|
|
export function update_top_of_narrow_notices(msg_list) {
|
|
// Assumes that the current state is all notices hidden (i.e. this
|
|
// will not hide a notice that should not be there)
|
|
if (msg_list !== message_lists.current) {
|
|
return;
|
|
}
|
|
|
|
if (
|
|
msg_list.data.fetch_status.has_found_oldest() &&
|
|
message_lists.current !== message_lists.home
|
|
) {
|
|
const filter = narrow_state.filter();
|
|
if (filter === undefined && recent_topics_util.is_visible()) {
|
|
// user moved away from the narrow / filter to recent topics.
|
|
return;
|
|
}
|
|
// Potentially display the notice that lets users know
|
|
// that not all messages were searched. One could
|
|
// imagine including `filter.is_search()` in these
|
|
// conditions, but there's a very legitimate use case
|
|
// for moderation of searching for all messages sent
|
|
// by a potential spammer user.
|
|
if (
|
|
!filter.contains_only_private_messages() &&
|
|
!filter.includes_full_stream_history() &&
|
|
!filter.is_personal_filter()
|
|
) {
|
|
show_end_of_results_notice();
|
|
}
|
|
}
|
|
|
|
if (msg_list.data.fetch_status.history_limited()) {
|
|
show_history_limit_notice();
|
|
}
|
|
}
|
|
|
|
export function hide_top_of_narrow_notices() {
|
|
hide_end_of_results_notice();
|
|
hide_history_limit_notice();
|
|
}
|
|
|
|
let hide_scroll_to_bottom_timer;
|
|
export function hide_scroll_to_bottom() {
|
|
const $show_scroll_to_bottom_button = $("#scroll-to-bottom-button-container");
|
|
if (message_viewport.bottom_message_visible() || message_lists.current.empty()) {
|
|
// If last message is visible, just hide the
|
|
// scroll to bottom button.
|
|
$show_scroll_to_bottom_button.removeClass("show");
|
|
return;
|
|
}
|
|
|
|
// Wait before hiding to allow user time to click on the button.
|
|
hide_scroll_to_bottom_timer = setTimeout(() => {
|
|
// Don't hide if user is hovered on it.
|
|
if (
|
|
!narrow_state.narrowed_by_topic_reply() &&
|
|
!$show_scroll_to_bottom_button.get(0).matches(":hover")
|
|
) {
|
|
$show_scroll_to_bottom_button.removeClass("show");
|
|
}
|
|
}, 3000);
|
|
}
|
|
|
|
export function show_scroll_to_bottom_button() {
|
|
if (message_viewport.bottom_message_visible()) {
|
|
// Only show scroll to bottom button when
|
|
// last message is not visible in the
|
|
// current scroll position.
|
|
return;
|
|
}
|
|
|
|
clearTimeout(hide_scroll_to_bottom_timer);
|
|
$("#scroll-to-bottom-button-container").addClass("show");
|
|
}
|
|
|
|
$(document).on("keydown", (e) => {
|
|
if (e.shiftKey || e.ctrlKey || e.metaKey) {
|
|
return;
|
|
}
|
|
|
|
// Hide scroll to bottom button on any keypress.
|
|
// Keyboard users are very less likely to use this button.
|
|
$("#scroll-to-bottom-button-container").removeClass("show");
|
|
});
|
|
|
|
export function is_actively_scrolling() {
|
|
return actively_scrolling;
|
|
}
|
|
|
|
export function scroll_finished() {
|
|
actively_scrolling = false;
|
|
hide_scroll_to_bottom();
|
|
|
|
if (recent_topics_util.is_visible()) {
|
|
return;
|
|
}
|
|
|
|
if (notifications.scroll_to_message_banner_message_id !== null) {
|
|
const $message_row = message_lists.current.get_row(
|
|
notifications.scroll_to_message_banner_message_id,
|
|
);
|
|
if ($message_row.length > 0 && !message_viewport.is_message_below_viewport($message_row)) {
|
|
notifications.clear_compose_notifications();
|
|
}
|
|
}
|
|
|
|
if (update_selection_on_next_scroll) {
|
|
message_viewport.keep_pointer_in_view();
|
|
} else {
|
|
update_selection_on_next_scroll = true;
|
|
}
|
|
|
|
floating_recipient_bar.update();
|
|
|
|
if (message_viewport.at_top()) {
|
|
message_fetch.maybe_load_older_messages({
|
|
msg_list: message_lists.current,
|
|
});
|
|
}
|
|
|
|
if (message_viewport.at_bottom()) {
|
|
message_fetch.maybe_load_newer_messages({
|
|
msg_list: message_lists.current,
|
|
});
|
|
}
|
|
|
|
// When the window scrolls, it may cause some messages to
|
|
// enter the screen and become read. Calling
|
|
// unread_ops.process_visible will update necessary
|
|
// data structures and DOM elements.
|
|
setTimeout(unread_ops.process_visible, 0);
|
|
}
|
|
|
|
let scroll_timer;
|
|
function scroll_finish() {
|
|
actively_scrolling = true;
|
|
|
|
// Don't present the "scroll to bottom" widget if the current
|
|
// scroll was triggered by the keyboard.
|
|
if (!keyboard_triggered_current_scroll) {
|
|
show_scroll_to_bottom_button();
|
|
}
|
|
keyboard_triggered_current_scroll = false;
|
|
|
|
clearTimeout(scroll_timer);
|
|
scroll_timer = setTimeout(scroll_finished, 100);
|
|
}
|
|
|
|
export function initialize() {
|
|
message_viewport.$message_pane.on(
|
|
"scroll",
|
|
_.throttle(() => {
|
|
unread_ops.process_visible();
|
|
scroll_finish();
|
|
}, 50),
|
|
);
|
|
|
|
// Scroll handler that marks messages as read when you scroll past them.
|
|
$(document).on("message_selected.zulip", (event) => {
|
|
if (event.id === -1) {
|
|
return;
|
|
}
|
|
|
|
if (event.mark_read && event.previously_selected_id !== -1) {
|
|
// Mark messages between old pointer and new pointer as read
|
|
let messages;
|
|
if (event.id < event.previously_selected_id) {
|
|
messages = event.msg_list.message_range(event.id, event.previously_selected_id);
|
|
} else {
|
|
messages = event.msg_list.message_range(event.previously_selected_id, event.id);
|
|
}
|
|
if (event.msg_list.can_mark_messages_read()) {
|
|
unread_ops.notify_server_messages_read(messages, {from: "pointer"});
|
|
} else if (
|
|
unread.get_unread_messages(messages).length !== 0 &&
|
|
// The below checks might seem redundant, but it's
|
|
// possible this logic, which runs after a delay, lost
|
|
// a race with switching to another view, like Recent
|
|
// Topics, and we don't want to displ[ay this banner
|
|
// in such a view.
|
|
//
|
|
// This can likely be fixed more cleanly with another approach.
|
|
narrow_state.filter() !== undefined &&
|
|
message_lists.current === event.msg_list
|
|
) {
|
|
unread_ui.notify_messages_remain_unread();
|
|
}
|
|
}
|
|
});
|
|
}
|