2023-10-03 11:48:40 +02:00
|
|
|
import ClipboardJS from "clipboard";
|
|
|
|
import $ from "jquery";
|
|
|
|
|
2023-10-05 06:31:40 +02:00
|
|
|
import render_actions_popover from "../templates/popovers/actions_popover.hbs";
|
2023-10-03 11:48:40 +02:00
|
|
|
|
|
|
|
import * as blueslip from "./blueslip";
|
2023-10-06 01:43:50 +02:00
|
|
|
import * as compose_reply from "./compose_reply";
|
2023-10-03 11:48:40 +02:00
|
|
|
import * as condense from "./condense";
|
|
|
|
import {show_copied_confirmation} from "./copied_tooltip";
|
|
|
|
import * as emoji_picker from "./emoji_picker";
|
|
|
|
import * as message_edit from "./message_edit";
|
|
|
|
import * as message_lists from "./message_lists";
|
|
|
|
import * as message_viewport from "./message_viewport";
|
|
|
|
import * as popover_menus from "./popover_menus";
|
|
|
|
import * as popover_menus_data from "./popover_menus_data";
|
2023-10-21 11:38:49 +02:00
|
|
|
import * as popovers from "./popovers";
|
2023-10-03 11:48:40 +02:00
|
|
|
import * as read_receipts from "./read_receipts";
|
|
|
|
import * as rows from "./rows";
|
|
|
|
import * as stream_popover from "./stream_popover";
|
|
|
|
import {parse_html} from "./ui_util";
|
|
|
|
import * as unread_ops from "./unread_ops";
|
|
|
|
|
|
|
|
let message_actions_popover_keyboard_toggle = false;
|
|
|
|
|
|
|
|
function get_action_menu_menu_items() {
|
|
|
|
const $current_actions_popover_elem = $("[data-tippy-root] .actions_popover");
|
|
|
|
if (!$current_actions_popover_elem) {
|
|
|
|
blueslip.error("Trying to get menu items when action popover is closed.");
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $current_actions_popover_elem.find("li:not(.divider):visible a");
|
|
|
|
}
|
|
|
|
|
|
|
|
function focus_first_action_popover_item() {
|
|
|
|
// For now I recommend only calling this when the user opens the menu with a hotkey.
|
|
|
|
// Our popup menus act kind of funny when you mix keyboard and mouse.
|
|
|
|
const $items = get_action_menu_menu_items();
|
|
|
|
popover_menus.focus_first_popover_item($items);
|
|
|
|
}
|
|
|
|
|
|
|
|
export function toggle_message_actions_menu(message) {
|
2023-10-21 11:38:49 +02:00
|
|
|
if (popover_menus.is_message_actions_popover_displayed()) {
|
|
|
|
popovers.hide_all();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-10-03 11:48:40 +02:00
|
|
|
if (message.locally_echoed || message_edit.is_editing(message.id)) {
|
|
|
|
// Don't open the popup for locally echoed messages for now.
|
|
|
|
// It creates bugs with things like keyboard handlers when
|
|
|
|
// we get the server response.
|
|
|
|
// We also suppress the popup for messages in an editing state,
|
|
|
|
// including previews, when a user tries to reach them from the
|
|
|
|
// keyboard.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-10-21 11:38:49 +02:00
|
|
|
// Since this can be called via hotkey, we need to
|
|
|
|
// hide any other popovers that may be open before.
|
|
|
|
if (popovers.any_active()) {
|
|
|
|
popovers.hide_all();
|
|
|
|
}
|
|
|
|
|
2023-10-03 11:48:40 +02:00
|
|
|
message_viewport.maybe_scroll_to_show_message_top();
|
|
|
|
const $popover_reference = $(".selected_message .actions_hover .message-actions-menu-button");
|
|
|
|
message_actions_popover_keyboard_toggle = true;
|
|
|
|
$popover_reference.trigger("click");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function initialize() {
|
|
|
|
popover_menus.register_popover_menu(".actions_hover .message-actions-menu-button", {
|
|
|
|
// 320px is our minimum supported width for mobile. We will allow the value to flex
|
|
|
|
// to a max of 350px but we shouldn't make the popover wider than this.
|
|
|
|
maxWidth: "min(max(320px, 100vw), 350px)",
|
|
|
|
placement: "bottom",
|
|
|
|
popperOptions: {
|
|
|
|
modifiers: [
|
|
|
|
{
|
|
|
|
// The placement is set to bottom, but if that placement does not fit,
|
|
|
|
// the opposite top placement will be used.
|
|
|
|
name: "flip",
|
|
|
|
options: {
|
|
|
|
fallbackPlacements: ["top", "left"],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
onShow(instance) {
|
|
|
|
popover_menus.on_show_prep(instance);
|
|
|
|
const $row = $(instance.reference).closest(".message_row");
|
|
|
|
const message_id = rows.id($row);
|
|
|
|
const args = popover_menus_data.get_actions_popover_content_context(message_id);
|
2023-10-04 08:19:28 +02:00
|
|
|
instance.setContent(parse_html(render_actions_popover(args)));
|
2023-10-03 11:48:40 +02:00
|
|
|
$row.addClass("has_actions_popover");
|
|
|
|
},
|
|
|
|
onMount(instance) {
|
|
|
|
if (message_actions_popover_keyboard_toggle) {
|
|
|
|
focus_first_action_popover_item();
|
|
|
|
message_actions_popover_keyboard_toggle = false;
|
|
|
|
}
|
|
|
|
popover_menus.popover_instances.message_actions = instance;
|
|
|
|
|
|
|
|
// We want click events to propagate to `instance` so that
|
|
|
|
// instance.hide gets called.
|
|
|
|
const $popper = $(instance.popper);
|
|
|
|
$popper.one("click", ".respond_button", (e) => {
|
2023-10-05 23:44:17 +02:00
|
|
|
const message_id = $(e.currentTarget).data("message-id");
|
|
|
|
compose_reply.quote_and_reply({trigger: "popover respond", message_id});
|
2023-10-03 11:48:40 +02:00
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
instance.hide();
|
|
|
|
});
|
|
|
|
|
|
|
|
$popper.one("click", ".popover_edit_message, .popover_view_source", (e) => {
|
|
|
|
const message_id = $(e.currentTarget).data("message-id");
|
|
|
|
const $row = message_lists.current.get_row(message_id);
|
|
|
|
message_edit.start($row);
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
instance.hide();
|
|
|
|
});
|
|
|
|
|
|
|
|
$popper.one("click", ".popover_move_message", (e) => {
|
|
|
|
const message_id = $(e.currentTarget).data("message-id");
|
|
|
|
const message = message_lists.current.get(message_id);
|
|
|
|
stream_popover.build_move_topic_to_stream_popover(
|
|
|
|
message.stream_id,
|
|
|
|
message.topic,
|
|
|
|
false,
|
|
|
|
message,
|
|
|
|
);
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
instance.hide();
|
|
|
|
});
|
|
|
|
|
|
|
|
$popper.one("click", ".mark_as_unread", (e) => {
|
|
|
|
const message_id = $(e.currentTarget).data("message-id");
|
|
|
|
unread_ops.mark_as_unread_from_here(message_id);
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
instance.hide();
|
|
|
|
});
|
|
|
|
|
|
|
|
$popper.one("click", ".popover_toggle_collapse", (e) => {
|
|
|
|
const message_id = $(e.currentTarget).data("message-id");
|
|
|
|
const $row = message_lists.current.get_row(message_id);
|
|
|
|
const message = message_lists.current.get(rows.id($row));
|
|
|
|
if ($row) {
|
|
|
|
if (message.collapsed) {
|
|
|
|
condense.uncollapse($row);
|
|
|
|
} else {
|
|
|
|
condense.collapse($row);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
instance.hide();
|
|
|
|
});
|
|
|
|
|
|
|
|
$popper.one("click", ".rehide_muted_user_message", (e) => {
|
|
|
|
const message_id = $(e.currentTarget).data("message-id");
|
|
|
|
const $row = message_lists.current.get_row(message_id);
|
|
|
|
const message = message_lists.current.get(rows.id($row));
|
|
|
|
const message_container = message_lists.current.view.message_containers.get(
|
|
|
|
message.id,
|
|
|
|
);
|
|
|
|
if ($row && !message_container.is_hidden) {
|
|
|
|
message_lists.current.view.hide_revealed_message(message_id);
|
|
|
|
}
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
instance.hide();
|
|
|
|
});
|
|
|
|
|
|
|
|
$popper.one("click", ".view_read_receipts", (e) => {
|
|
|
|
const message_id = $(e.currentTarget).data("message-id");
|
|
|
|
read_receipts.show_user_list(message_id);
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
instance.hide();
|
|
|
|
});
|
|
|
|
|
|
|
|
$popper.one("click", ".delete_message", (e) => {
|
|
|
|
const message_id = $(e.currentTarget).data("message-id");
|
|
|
|
message_edit.delete_message(message_id);
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
instance.hide();
|
|
|
|
});
|
|
|
|
|
|
|
|
$popper.one("click", ".reaction_button", (e) => {
|
|
|
|
const message_id = $(e.currentTarget).data("message-id");
|
|
|
|
// Don't propagate the click event since `toggle_emoji_popover` opens a
|
|
|
|
// emoji_picker which we don't want to hide after actions popover is hidden.
|
|
|
|
e.stopPropagation();
|
|
|
|
e.preventDefault();
|
|
|
|
emoji_picker.toggle_emoji_popover(instance.reference.parentElement, message_id, {
|
|
|
|
placement: "bottom",
|
|
|
|
});
|
|
|
|
instance.hide();
|
|
|
|
});
|
|
|
|
|
|
|
|
new ClipboardJS($popper.find(".copy_link")[0]).on("success", () => {
|
|
|
|
show_copied_confirmation($(instance.reference).closest(".message_controls")[0]);
|
|
|
|
setTimeout(() => {
|
|
|
|
// The Clipboard library works by focusing to a hidden textarea.
|
|
|
|
// We unfocus this so keyboard shortcuts, etc., will work again.
|
|
|
|
$(":focus").trigger("blur");
|
|
|
|
}, 0);
|
|
|
|
instance.hide();
|
|
|
|
});
|
|
|
|
},
|
|
|
|
onHidden(instance) {
|
|
|
|
const $row = $(instance.reference).closest(".message_row");
|
|
|
|
$row.removeClass("has_actions_popover");
|
|
|
|
instance.destroy();
|
|
|
|
popover_menus.popover_instances.message_actions = undefined;
|
|
|
|
message_actions_popover_keyboard_toggle = false;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|