message_scroll: Show scroll to bottom button.

Show/hide scroll to bottom button when the last message is
not visible in the current scroll position.

We adjust the bottom offset of the button based on compose box
height.

Fixes #19862
This commit is contained in:
Aman Agrawal 2022-02-14 16:43:32 +00:00 committed by Tim Abbott
parent ea07b6440c
commit 0eafa6039b
12 changed files with 145 additions and 2 deletions

View File

@ -13,8 +13,16 @@ const {page_params, user_settings} = require("../zjsunit/zpage_params");
const noop = () => {};
set_global("document", {});
set_global("document", {
querySelector: () => {},
});
set_global("navigator", {});
// eslint-disable-next-line prefer-arrow-callback
set_global("ResizeObserver", function () {
return {
observe: () => {},
};
});
const fake_now = 555;

View File

@ -14,7 +14,16 @@ const upload = mock_esm("../../static/js/upload");
mock_esm("../../static/js/resize", {
watch_manual_resize() {},
});
set_global("document", {
querySelector: () => {},
});
set_global("navigator", {});
// eslint-disable-next-line prefer-arrow-callback
set_global("ResizeObserver", function () {
return {
observe: () => {},
};
});
const server_events_dispatch = zrequire("server_events_dispatch");
const compose_ui = zrequire("compose_ui");

View File

@ -27,6 +27,7 @@ import * as message_lists from "./message_lists";
import * as message_store from "./message_store";
import * as muted_topics_ui from "./muted_topics_ui";
import * as narrow from "./narrow";
import * as navigate from "./navigate";
import * as notifications from "./notifications";
import * as overlays from "./overlays";
import {page_params} from "./page_params";
@ -249,6 +250,13 @@ export function initialize() {
window.location.href = $(this).attr("href");
});
$("body").on("click", "#scroll-to-bottom-button-clickable-area", (e) => {
e.preventDefault();
e.stopPropagation();
navigate.to_end();
});
// MESSAGE EDITING
$("body").on("click", ".edit_content_button", function (e) {

View File

@ -413,6 +413,13 @@ export function initialize() {
resize.watch_manual_resize("#compose-textarea");
// Update position of scroll to bottom button based on
// height of the compose box.
const update_scroll_to_bottom_position = new ResizeObserver(() => {
$("#scroll-to-bottom-button-container").css("bottom", $("#compose").outerHeight());
});
update_scroll_to_bottom_position.observe(document.querySelector("#compose"));
upload.feature_check($("#compose .compose_upload_file"));
$("#compose-all-everyone").on("click", ".compose-all-everyone-confirm", (event) => {

View File

@ -172,6 +172,7 @@ export function make_compose_box_full_size() {
$(".collapse_composebox_button").show();
$(".expand_composebox_button").hide();
$("#scroll-to-bottom-button-container").hide();
$("#compose-textarea").trigger("focus");
}

View File

@ -129,12 +129,57 @@ export function hide_top_of_narrow_notices() {
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.hide();
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.fadeOut(500);
}
}, 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").fadeIn(500);
}
$(document).on("keydown", (e) => {
if (e.shiftKey || e.ctrlKey || e.metaKey) {
return;
}
// Instantly hide scroll to bottom button on any keypress.
// Keyboard users are very less likely to use this button.
$("#scroll-to-bottom-button-container").hide();
});
export function is_actively_scrolling() {
return actively_scrolling;
}
export function scroll_finished() {
actively_scrolling = false;
hide_scroll_to_bottom();
if (!$("#message_feed_container").hasClass("active")) {
return;
@ -170,6 +215,7 @@ export function scroll_finished() {
let scroll_timer;
function scroll_finish() {
actively_scrolling = true;
show_scroll_to_bottom_button();
clearTimeout(scroll_timer);
scroll_timer = setTimeout(scroll_finished, 100);
}

View File

@ -185,7 +185,12 @@ export function initialize() {
// ensures that tooltip doesn't hide behind the message
// box or it is not limited by the parent container.
delegate("body", {
target: [".recipient_bar_icon", ".sidebar-title", "#user_filter_icon"],
target: [
".recipient_bar_icon",
".sidebar-title",
"#user_filter_icon",
"#scroll-to-bottom-button-clickable-area",
],
appendTo: () => document.body,
});

View File

@ -203,6 +203,7 @@ function initialize_compose_box() {
embedded: $("#compose").attr("data-embedded") === "",
file_upload_enabled: page_params.max_file_upload_size_mib > 0,
giphy_enabled: giphy.is_giphy_enabled(),
scroll_to_bottom_key: common.has_mac_keyboard() ? "Fn + Right arrow" : "End",
}),
);
$(`.enter_sends_${user_settings.enter_sends}`).show();

View File

@ -215,6 +215,8 @@
}
#compose-container {
display: flex;
flex-direction: column;
width: 100%;
/* This should match the value for .app-main */
max-width: 1400px;

View File

@ -148,6 +148,14 @@ body.dark-theme {
background-color: hsl(212, 28%, 18%);
}
#scroll-to-bottom-button-container {
background: transparent;
span {
color: hsl(0, 0%, 27%);
}
}
#compose_buttons
.reply_button_container
.compose_reply_button

View File

@ -3095,3 +3095,40 @@ select.inline_select_topic_edit {
margin: 0;
}
}
#scroll-to-bottom-button-container {
display: none;
z-index: 3;
position: absolute;
bottom: 41px;
right: 0;
#scroll-to-bottom-button-clickable-area {
width: 60px;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
&:hover #scroll-to-bottom-button {
background: hsl(240, 96%, 68%);
}
#scroll-to-bottom-button {
text-align: center;
width: 40px;
height: 40px;
background: hsl(240, 96%, 68%, 0.5);
border-radius: 50%;
i {
color: hsl(0, 0%, 100%);
margin: 0 auto;
font-size: 21px;
position: relative;
top: 8px;
}
}
}
}

View File

@ -1,4 +1,15 @@
<div id="compose-content">
{{!-- scroll to bottom button is not part of compose but
helps us align it at various screens sizes with
minimal css and no JS. We keep it `position: absolute` to prevent
it changing compose box layout in any way. --}}
<div id="scroll-to-bottom-button-container">
<div id="scroll-to-bottom-button-clickable-area" data-tippy-content="{{t 'Scroll to bottom' }} <span class='hotkey-hint'>({{scroll_to_bottom_key}})</span>" data-tippy-allowHtml="true">
<div id="scroll-to-bottom-button">
<i class="fa fa-chevron-down"></i>
</div>
</div>
</div>
<div id="compose_controls" class="new-style">
<div id="compose_buttons">
<span class="new_message_button reply_button_container">