mirror of https://github.com/zulip/zulip.git
313 lines
11 KiB
JavaScript
313 lines
11 KiB
JavaScript
import autosize from "autosize";
|
|
import $ from "jquery";
|
|
|
|
import * as blueslip from "./blueslip";
|
|
import * as condense from "./condense";
|
|
import * as message_lists from "./message_lists";
|
|
import * as message_viewport from "./message_viewport";
|
|
import * as navbar_alerts from "./navbar_alerts";
|
|
import * as navigate from "./navigate";
|
|
import * as popovers from "./popovers";
|
|
import * as recent_topics_util from "./recent_topics_util";
|
|
import * as ui from "./ui";
|
|
import {user_settings} from "./user_settings";
|
|
import * as util from "./util";
|
|
|
|
let narrow_window = false;
|
|
|
|
function confine_to_range(lo, val, hi) {
|
|
if (val < lo) {
|
|
return lo;
|
|
}
|
|
if (val > hi) {
|
|
return hi;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
function size_blocks(blocks, usable_height) {
|
|
let sum_height = 0;
|
|
|
|
for (const block of blocks) {
|
|
sum_height += block.real_height;
|
|
}
|
|
|
|
for (const block of blocks) {
|
|
let ratio = block.real_height / sum_height;
|
|
ratio = confine_to_range(0.05, ratio, 0.85);
|
|
block.max_height = confine_to_range(80, usable_height * ratio, 1.2 * block.real_height);
|
|
}
|
|
}
|
|
|
|
function get_new_heights() {
|
|
const res = {};
|
|
const viewport_height = message_viewport.height();
|
|
const top_navbar_height = $("#top_navbar").safeOuterHeight(true);
|
|
const right_sidebar_shorcuts_height = $(".right-sidebar-shortcuts").safeOuterHeight(true) || 0;
|
|
|
|
res.bottom_whitespace_height = viewport_height * 0.4;
|
|
|
|
res.main_div_min_height = viewport_height - top_navbar_height;
|
|
|
|
res.stream_filters_max_height =
|
|
viewport_height -
|
|
Number.parseInt($("#left-sidebar").css("marginTop"), 10) -
|
|
Number.parseInt($(".narrows_panel").css("marginTop"), 10) -
|
|
Number.parseInt($(".narrows_panel").css("marginBottom"), 10) -
|
|
$("#global_filters").safeOuterHeight(true) -
|
|
$("#streams_header").safeOuterHeight(true);
|
|
|
|
// Don't let us crush the stream sidebar completely out of view
|
|
res.stream_filters_max_height = Math.max(80, res.stream_filters_max_height);
|
|
|
|
// RIGHT SIDEBAR
|
|
|
|
const usable_height =
|
|
viewport_height -
|
|
Number.parseInt($("#right-sidebar").css("marginTop"), 10) -
|
|
$("#userlist-header").safeOuterHeight(true) -
|
|
$("#user_search_section").safeOuterHeight(true) -
|
|
right_sidebar_shorcuts_height;
|
|
|
|
res.buddy_list_wrapper_max_height = Math.max(80, usable_height);
|
|
|
|
return res;
|
|
}
|
|
|
|
function left_userlist_get_new_heights() {
|
|
const res = {};
|
|
const viewport_height = message_viewport.height();
|
|
const viewport_width = message_viewport.width();
|
|
res.viewport_height = viewport_height;
|
|
res.viewport_width = viewport_width;
|
|
|
|
// main div
|
|
const top_navbar_height = $(".header").safeOuterHeight(true);
|
|
res.bottom_whitespace_height = viewport_height * 0.4;
|
|
res.main_div_min_height = viewport_height - top_navbar_height;
|
|
|
|
// left sidebar
|
|
const $stream_filters = $("#stream_filters").expectOne();
|
|
const $buddy_list_wrapper = $("#buddy_list_wrapper").expectOne();
|
|
|
|
const stream_filters_real_height = $stream_filters.prop("scrollHeight");
|
|
const user_list_real_height = ui.get_scroll_element($buddy_list_wrapper).prop("scrollHeight");
|
|
|
|
res.total_leftlist_height =
|
|
viewport_height -
|
|
Number.parseInt($("#left-sidebar").css("marginTop"), 10) -
|
|
Number.parseInt($(".narrows_panel").css("marginTop"), 10) -
|
|
Number.parseInt($(".narrows_panel").css("marginBottom"), 10) -
|
|
$("#global_filters").safeOuterHeight(true) -
|
|
$("#streams_header").safeOuterHeight(true) -
|
|
$("#userlist-header").safeOuterHeight(true) -
|
|
$("#user_search_section").safeOuterHeight(true) -
|
|
Number.parseInt($stream_filters.css("marginBottom"), 10);
|
|
|
|
const blocks = [
|
|
{
|
|
real_height: stream_filters_real_height,
|
|
},
|
|
{
|
|
real_height: user_list_real_height,
|
|
},
|
|
];
|
|
|
|
size_blocks(blocks, res.total_leftlist_height);
|
|
|
|
res.stream_filters_max_height = blocks[0].max_height;
|
|
res.buddy_list_wrapper_max_height = blocks[1].max_height;
|
|
|
|
return res;
|
|
}
|
|
|
|
export function watch_manual_resize(element) {
|
|
const box = document.querySelector(element);
|
|
|
|
if (!box) {
|
|
blueslip.error("Bad selector in watch_manual_resize: " + element);
|
|
return undefined;
|
|
}
|
|
|
|
const meta = {
|
|
box,
|
|
height: null,
|
|
mousedown: false,
|
|
};
|
|
|
|
const box_handler = function () {
|
|
meta.mousedown = true;
|
|
meta.height = meta.box.clientHeight;
|
|
};
|
|
meta.box.addEventListener("mousedown", box_handler);
|
|
|
|
// If the user resizes the textarea manually, we use the
|
|
// callback to stop autosize from adjusting the height.
|
|
// It will be re-enabled when this component is next opened.
|
|
const body_handler = function () {
|
|
if (meta.mousedown === true) {
|
|
meta.mousedown = false;
|
|
if (meta.height !== meta.box.clientHeight) {
|
|
meta.height = meta.box.clientHeight;
|
|
autosize.destroy($(element)).height(meta.height + "px");
|
|
}
|
|
}
|
|
};
|
|
document.body.addEventListener("mouseup", body_handler);
|
|
|
|
return [box_handler, body_handler];
|
|
}
|
|
|
|
export function reset_compose_message_max_height(bottom_whitespace_height) {
|
|
// If the compose-box is open, we set the `max-height` property of
|
|
// `compose-textarea` and `preview-textarea`, so that the
|
|
// compose-box's maximum extent does not overlap the last message
|
|
// in the current stream. We also leave a tiny bit of space after
|
|
// the last message of the current stream.
|
|
|
|
// Compute bottom_whitespace_height if not provided by caller.
|
|
if (bottom_whitespace_height === undefined) {
|
|
const h = narrow_window ? left_userlist_get_new_heights() : get_new_heights();
|
|
bottom_whitespace_height = h.bottom_whitespace_height;
|
|
}
|
|
|
|
// Take properties of the whichever message area is visible.
|
|
const $visible_textarea = $("#compose-textarea:visible, #preview_message_area:visible");
|
|
const compose_height = Number.parseInt($("#compose").outerHeight(), 10);
|
|
const compose_textarea_height = Number.parseInt($visible_textarea.outerHeight(), 10);
|
|
const compose_non_textarea_height = compose_height - compose_textarea_height;
|
|
|
|
// The `preview_message_area` can have a slightly different height
|
|
// than `compose-textarea` based on operating system. We just
|
|
// ensure that the last message is not overlapped by compose box.
|
|
$visible_textarea.css(
|
|
"max-height",
|
|
// The 10 here leaves space for the selected message border.
|
|
bottom_whitespace_height - compose_non_textarea_height - 10,
|
|
);
|
|
}
|
|
|
|
export function resize_bottom_whitespace(h) {
|
|
$("#bottom_whitespace").height(h.bottom_whitespace_height);
|
|
|
|
// The height of the compose box is tied to that of
|
|
// bottom_whitespace, so update it if necessary.
|
|
//
|
|
// reset_compose_message_max_height cannot compute the right
|
|
// height correctly while compose is hidden. This is OK, because
|
|
// we also resize compose every time it is opened.
|
|
if ($(".message_comp").is(":visible")) {
|
|
reset_compose_message_max_height(h.bottom_whitespace_height);
|
|
}
|
|
}
|
|
|
|
export function resize_stream_filters_container(h) {
|
|
h = narrow_window ? left_userlist_get_new_heights() : get_new_heights();
|
|
resize_bottom_whitespace(h);
|
|
$("#stream-filters-container").css("max-height", h.stream_filters_max_height);
|
|
}
|
|
|
|
export function resize_sidebars() {
|
|
let $sidebar;
|
|
|
|
if (user_settings.left_side_userlist) {
|
|
const css_narrow_mode = message_viewport.is_narrow();
|
|
|
|
$("#top_navbar").removeClass("rightside-userlist");
|
|
|
|
const $right_items = $(".right-sidebar-items").expectOne();
|
|
|
|
if (css_narrow_mode && !narrow_window) {
|
|
// move stuff to the left sidebar (skinny mode)
|
|
narrow_window = true;
|
|
popovers.set_userlist_placement("left");
|
|
$sidebar = $("#left-sidebar").expectOne();
|
|
$sidebar.append($right_items);
|
|
$("#buddy_list_wrapper").css("margin", "0px");
|
|
$("#userlist-toggle").css("display", "none");
|
|
$("#invite-user-link").hide();
|
|
} else if (!css_narrow_mode && narrow_window) {
|
|
// move stuff to the right sidebar (wide mode)
|
|
narrow_window = false;
|
|
popovers.set_userlist_placement("right");
|
|
$sidebar = $("#right-sidebar").expectOne();
|
|
$sidebar.append($right_items);
|
|
$("#buddy_list_wrapper").css("margin", "");
|
|
$("#userlist-toggle").css("display", "");
|
|
$("#invite-user-link").show();
|
|
}
|
|
}
|
|
|
|
const h = narrow_window ? left_userlist_get_new_heights() : get_new_heights();
|
|
|
|
$("#buddy_list_wrapper").css("max-height", h.buddy_list_wrapper_max_height);
|
|
$("#stream-filters-container").css("max-height", h.stream_filters_max_height);
|
|
|
|
return h;
|
|
}
|
|
|
|
export function resize_page_components() {
|
|
navbar_alerts.resize_app();
|
|
const h = resize_sidebars();
|
|
resize_bottom_whitespace(h);
|
|
}
|
|
|
|
let _old_width = $(window).width();
|
|
|
|
export function handler() {
|
|
const new_width = $(window).width();
|
|
|
|
// On mobile web, we want to avoid hiding a popover here on height change,
|
|
// especially if this resize was triggered by a virtual keyboard
|
|
// popping up when the user opened that very popover.
|
|
const mobile = util.is_mobile();
|
|
if (!mobile || new_width !== _old_width) {
|
|
popovers.hide_all();
|
|
}
|
|
|
|
if (new_width !== _old_width) {
|
|
_old_width = new_width;
|
|
condense.clear_message_content_height_cache();
|
|
}
|
|
resize_page_components();
|
|
|
|
// Re-compute and display/remove [More] links to messages
|
|
condense.condense_and_collapse($(".message_table .message_row"));
|
|
|
|
// This function might run onReady (if we're in a narrow window),
|
|
// but before we've loaded in the messages; in that case, don't
|
|
// try to scroll to one.
|
|
if (message_lists.current.selected_id() !== -1) {
|
|
if (mobile) {
|
|
popovers.set_suppress_scroll_hide();
|
|
}
|
|
|
|
navigate.scroll_to_selected();
|
|
}
|
|
}
|
|
|
|
export function initialize() {
|
|
// Hack: If the app is loaded directly to recent topics, then we
|
|
// need to arrange to call navbar_alerts.resize_app when we first
|
|
// visit a message list. This is a workaround for bugs where the
|
|
// floating recipient bar will be invisible (as well as other
|
|
// alignment issues) when they are initially rendered in the
|
|
// background because recent topics is displayed.
|
|
|
|
if (recent_topics_util.is_visible()) {
|
|
// We bind the handler for the message_feed_container shown event, such
|
|
// that it will only get executed once.
|
|
//
|
|
// The selector here is based on #gear-menu, to take advantage
|
|
// of the Bootstrap the 'show' event handler on that legacy
|
|
// data-toggle element.
|
|
$('#gear-menu a[data-toggle="tab"][href="#message_feed_container"]').one("show", () => {
|
|
// We use a requestAnimationFrame here to prevent this call from
|
|
// causing a forced reflow.
|
|
window.requestAnimationFrame(() => {
|
|
navbar_alerts.resize_app();
|
|
});
|
|
});
|
|
}
|
|
}
|