mirror of https://github.com/zulip/zulip.git
message_list_tooltips: Convert module to TypeScript.
This commit is contained in:
parent
b0377d5d6d
commit
fd3c7728fc
|
@ -138,7 +138,7 @@ EXEMPT_FILES = make_set(
|
||||||
"web/src/message_list.js",
|
"web/src/message_list.js",
|
||||||
"web/src/message_list_data.ts",
|
"web/src/message_list_data.ts",
|
||||||
"web/src/message_list_hover.js",
|
"web/src/message_list_hover.js",
|
||||||
"web/src/message_list_tooltips.js",
|
"web/src/message_list_tooltips.ts",
|
||||||
"web/src/message_list_view.js",
|
"web/src/message_list_view.js",
|
||||||
"web/src/message_lists.ts",
|
"web/src/message_lists.ts",
|
||||||
"web/src/message_live_update.ts",
|
"web/src/message_live_update.ts",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import $ from "jquery";
|
import $ from "jquery";
|
||||||
import assert from "minimalistic-assert";
|
import assert from "minimalistic-assert";
|
||||||
import {delegate} from "tippy.js";
|
import {delegate} from "tippy.js";
|
||||||
|
import type * as tippy from "tippy.js";
|
||||||
|
|
||||||
import render_change_visibility_policy_button_tooltip from "../templates/change_visibility_policy_button_tooltip.hbs";
|
import render_change_visibility_policy_button_tooltip from "../templates/change_visibility_policy_button_tooltip.hbs";
|
||||||
import render_message_edit_notice_tooltip from "../templates/message_edit_notice_tooltip.hbs";
|
import render_message_edit_notice_tooltip from "../templates/message_edit_notice_tooltip.hbs";
|
||||||
|
@ -16,17 +17,23 @@ import * as timerender from "./timerender";
|
||||||
import {INTERACTIVE_HOVER_DELAY, LONG_HOVER_DELAY} from "./tippyjs";
|
import {INTERACTIVE_HOVER_DELAY, LONG_HOVER_DELAY} from "./tippyjs";
|
||||||
import {parse_html} from "./ui_util";
|
import {parse_html} from "./ui_util";
|
||||||
|
|
||||||
|
type Config = {
|
||||||
|
attributes: boolean;
|
||||||
|
childList: boolean;
|
||||||
|
subtree: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
// We need to store all message list instances together to destroy them in case of re-rendering.
|
// We need to store all message list instances together to destroy them in case of re-rendering.
|
||||||
const message_list_tippy_instances = new Set();
|
const message_list_tippy_instances = new Set<tippy.Instance>();
|
||||||
|
|
||||||
// This keeps track of all the instances created and destroyed.
|
// This keeps track of all the instances created and destroyed.
|
||||||
const store_message_list_instances_plugin = {
|
const store_message_list_instances_plugin = {
|
||||||
fn() {
|
fn() {
|
||||||
return {
|
return {
|
||||||
onCreate(instance) {
|
onCreate(instance: tippy.Instance) {
|
||||||
message_list_tippy_instances.add(instance);
|
message_list_tippy_instances.add(instance);
|
||||||
},
|
},
|
||||||
onDestroy(instance) {
|
onDestroy(instance: tippy.Instance) {
|
||||||
// To make sure the `message_list_tippy_instances` contains only instances
|
// To make sure the `message_list_tippy_instances` contains only instances
|
||||||
// that are present in the DOM, we need to delete instances that are destroyed
|
// that are present in the DOM, we need to delete instances that are destroyed
|
||||||
message_list_tippy_instances.delete(instance);
|
message_list_tippy_instances.delete(instance);
|
||||||
|
@ -35,7 +42,7 @@ const store_message_list_instances_plugin = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function message_list_tooltip(target, props = {}) {
|
function message_list_tooltip(target: string, props: Partial<tippy.Props> = {}): void {
|
||||||
const {onShow, ...other_props} = props;
|
const {onShow, ...other_props} = props;
|
||||||
delegate("body", {
|
delegate("body", {
|
||||||
target,
|
target,
|
||||||
|
@ -63,11 +70,11 @@ function message_list_tooltip(target, props = {}) {
|
||||||
// Defining observer outside ensures that at max only one observer is active at all times.
|
// Defining observer outside ensures that at max only one observer is active at all times.
|
||||||
let observer;
|
let observer;
|
||||||
function hide_tooltip_if_reference_removed(
|
function hide_tooltip_if_reference_removed(
|
||||||
target_node,
|
target_node: HTMLElement,
|
||||||
config,
|
config: Config,
|
||||||
instance,
|
instance: tippy.Instance,
|
||||||
nodes_to_check_for_removal,
|
nodes_to_check_for_removal: tippy.ReferenceElement[],
|
||||||
) {
|
): void {
|
||||||
// Use MutationObserver to check for removal of nodes on which tooltips
|
// Use MutationObserver to check for removal of nodes on which tooltips
|
||||||
// are still active.
|
// are still active.
|
||||||
if (!target_node) {
|
if (!target_node) {
|
||||||
|
@ -75,10 +82,12 @@ function hide_tooltip_if_reference_removed(
|
||||||
// In that case, we simply hide the tooltip.
|
// In that case, we simply hide the tooltip.
|
||||||
// We have to be smart about hiding the instance, so we hide it as soon
|
// We have to be smart about hiding the instance, so we hide it as soon
|
||||||
// as it is displayed.
|
// as it is displayed.
|
||||||
setTimeout(instance.hide, 0);
|
setTimeout(() => {
|
||||||
|
instance.hide();
|
||||||
|
}, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const callback = function (mutationsList) {
|
const callback = function (mutationsList: MutationRecord[]): void {
|
||||||
for (const mutation of mutationsList) {
|
for (const mutation of mutationsList) {
|
||||||
for (const node of nodes_to_check_for_removal) {
|
for (const node of nodes_to_check_for_removal) {
|
||||||
// Hide instance if reference's class changes.
|
// Hide instance if reference's class changes.
|
||||||
|
@ -99,7 +108,7 @@ function hide_tooltip_if_reference_removed(
|
||||||
// To prevent the appearance of tooltips whose reference is hidden or removed from the
|
// To prevent the appearance of tooltips whose reference is hidden or removed from the
|
||||||
// DOM during re-rendering, we need to destroy all the message list present instances,
|
// DOM during re-rendering, we need to destroy all the message list present instances,
|
||||||
// and then initialize triggers of the tooltips again after re-rendering.
|
// and then initialize triggers of the tooltips again after re-rendering.
|
||||||
export function destroy_all_message_list_tooltips() {
|
export function destroy_all_message_list_tooltips(): void {
|
||||||
for (const instance of message_list_tippy_instances) {
|
for (const instance of message_list_tippy_instances) {
|
||||||
if (instance.reference === document.body) {
|
if (instance.reference === document.body) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -109,7 +118,7 @@ export function destroy_all_message_list_tooltips() {
|
||||||
message_list_tippy_instances.clear();
|
message_list_tippy_instances.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initialize() {
|
export function initialize(): void {
|
||||||
message_list_tooltip(".tippy-narrow-tooltip", {
|
message_list_tooltip(".tippy-narrow-tooltip", {
|
||||||
delay: LONG_HOVER_DELAY,
|
delay: LONG_HOVER_DELAY,
|
||||||
onCreate(instance) {
|
onCreate(instance) {
|
||||||
|
@ -120,7 +129,7 @@ export function initialize() {
|
||||||
});
|
});
|
||||||
|
|
||||||
// message reaction tooltip showing who reacted.
|
// message reaction tooltip showing who reacted.
|
||||||
let observer;
|
let observer: MutationObserver;
|
||||||
message_list_tooltip(".message_reaction", {
|
message_list_tooltip(".message_reaction", {
|
||||||
delay: INTERACTIVE_HOVER_DELAY,
|
delay: INTERACTIVE_HOVER_DELAY,
|
||||||
placement: "bottom",
|
placement: "bottom",
|
||||||
|
@ -132,16 +141,19 @@ export function initialize() {
|
||||||
}
|
}
|
||||||
const $elem = $(instance.reference);
|
const $elem = $(instance.reference);
|
||||||
const local_id = $elem.attr("data-reaction-id");
|
const local_id = $elem.attr("data-reaction-id");
|
||||||
|
assert(local_id !== undefined);
|
||||||
|
assert(instance.reference instanceof HTMLElement);
|
||||||
const message_id = rows.get_message_id(instance.reference);
|
const message_id = rows.get_message_id(instance.reference);
|
||||||
const title = reactions.get_reaction_title_data(message_id, local_id);
|
const title = reactions.get_reaction_title_data(message_id, local_id);
|
||||||
instance.setContent(title);
|
instance.setContent(title);
|
||||||
|
|
||||||
const config = {attributes: false, childList: true, subtree: true};
|
const config = {attributes: false, childList: true, subtree: true};
|
||||||
const target = $elem.parents(".message-list.focused-message-list").get(0);
|
const target = $elem.parents(".message-list.focused-message-list").get(0);
|
||||||
|
assert(target !== undefined);
|
||||||
const nodes_to_check_for_removal = [
|
const nodes_to_check_for_removal = [
|
||||||
$elem.parents(".recipient_row").get(0),
|
$elem.parents(".recipient_row").get(0)!,
|
||||||
$elem.parents(".message_reactions").get(0),
|
$elem.parents(".message_reactions").get(0)!,
|
||||||
$elem.get(0),
|
$elem.get(0)!,
|
||||||
];
|
];
|
||||||
hide_tooltip_if_reference_removed(target, config, instance, nodes_to_check_for_removal);
|
hide_tooltip_if_reference_removed(target, config, instance, nodes_to_check_for_removal);
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -174,10 +186,11 @@ export function initialize() {
|
||||||
|
|
||||||
const config = {attributes: false, childList: true, subtree: true};
|
const config = {attributes: false, childList: true, subtree: true};
|
||||||
const target = $elem.parents(".message-list.focused-message-list").get(0);
|
const target = $elem.parents(".message-list.focused-message-list").get(0);
|
||||||
|
assert(target !== undefined);
|
||||||
const nodes_to_check_for_removal = [
|
const nodes_to_check_for_removal = [
|
||||||
$elem.parents(".recipient_row").get(0),
|
$elem.parents(".recipient_row").get(0)!,
|
||||||
$elem.parents(".message_reactions").get(0),
|
$elem.parents(".message_reactions").get(0)!,
|
||||||
$elem.get(0),
|
$elem.get(0)!,
|
||||||
];
|
];
|
||||||
hide_tooltip_if_reference_removed(target, config, instance, nodes_to_check_for_removal);
|
hide_tooltip_if_reference_removed(target, config, instance, nodes_to_check_for_removal);
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -196,7 +209,7 @@ export function initialize() {
|
||||||
if (tippy_content !== undefined) {
|
if (tippy_content !== undefined) {
|
||||||
instance.setContent(tippy_content);
|
instance.setContent(tippy_content);
|
||||||
} else {
|
} else {
|
||||||
const $template = $(`#${CSS.escape($elem.attr("data-tooltip-template-id"))}`);
|
const $template = $(`#${CSS.escape($elem.attr("data-tooltip-template-id")!)}`);
|
||||||
instance.setContent(parse_html($template.html()));
|
instance.setContent(parse_html($template.html()));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -218,8 +231,9 @@ export function initialize() {
|
||||||
// We need to check for removal of local class from message_row since
|
// We need to check for removal of local class from message_row since
|
||||||
// .slow-send-spinner is not removed (hidden) from DOM when message is sent.
|
// .slow-send-spinner is not removed (hidden) from DOM when message is sent.
|
||||||
const target = $elem.parents(".message_row").get(0);
|
const target = $elem.parents(".message_row").get(0);
|
||||||
|
assert(target !== undefined);
|
||||||
const config = {attributes: true, childList: false, subtree: false};
|
const config = {attributes: true, childList: false, subtree: false};
|
||||||
const nodes_to_check_for_removal = [$elem.get(0)];
|
const nodes_to_check_for_removal = [$elem.get(0)!];
|
||||||
hide_tooltip_if_reference_removed(target, config, instance, nodes_to_check_for_removal);
|
hide_tooltip_if_reference_removed(target, config, instance, nodes_to_check_for_removal);
|
||||||
},
|
},
|
||||||
onHidden(instance) {
|
onHidden(instance) {
|
||||||
|
@ -230,9 +244,10 @@ export function initialize() {
|
||||||
message_list_tooltip(".message-list .message_time", {
|
message_list_tooltip(".message-list .message_time", {
|
||||||
onShow(instance) {
|
onShow(instance) {
|
||||||
const $time_elem = $(instance.reference);
|
const $time_elem = $(instance.reference);
|
||||||
const $row = $time_elem.closest(".message_row");
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
|
const $row = $time_elem.closest(".message_row") as JQuery;
|
||||||
assert(message_lists.current !== undefined);
|
assert(message_lists.current !== undefined);
|
||||||
const message = message_lists.current.get(rows.id($row));
|
const message = message_lists.current.get(rows.id($row))!;
|
||||||
// Don't show time tooltip for locally echoed message.
|
// Don't show time tooltip for locally echoed message.
|
||||||
if (message.locally_echoed) {
|
if (message.locally_echoed) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -279,7 +294,8 @@ export function initialize() {
|
||||||
|
|
||||||
message_list_tooltip(".rendered_markdown time, .rendered_markdown .copy_codeblock",
|
message_list_tooltip(".rendered_markdown time, .rendered_markdown .copy_codeblock",
|
||||||
{
|
{
|
||||||
content: timerender.get_markdown_time_tooltip,
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
|
content: timerender.get_markdown_time_tooltip as tippy.Content,
|
||||||
onHidden(instance) {
|
onHidden(instance) {
|
||||||
instance.destroy();
|
instance.destroy();
|
||||||
},
|
},
|
||||||
|
@ -294,7 +310,7 @@ export function initialize() {
|
||||||
// Some message_inline_images aren't actually images with a title,
|
// Some message_inline_images aren't actually images with a title,
|
||||||
// for example youtube videos, so we default to the actual href
|
// for example youtube videos, so we default to the actual href
|
||||||
const title =
|
const title =
|
||||||
$(instance.reference).parent().attr("aria-label") ||
|
$(instance.reference).parent().attr("aria-label") ??
|
||||||
$(instance.reference).parent().attr("href");
|
$(instance.reference).parent().attr("href");
|
||||||
instance.setContent(parse_html(render_message_inline_image_tooltip({title})));
|
instance.setContent(parse_html(render_message_inline_image_tooltip({title})));
|
||||||
},
|
},
|
Loading…
Reference in New Issue