mirror of https://github.com/zulip/zulip.git
drafts: Extract drafts_overlay_ui module.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
parent
d5064fc7b2
commit
c4d338a6ab
|
@ -90,6 +90,7 @@ EXEMPT_FILES = make_set(
|
||||||
"web/src/desktop_integration.js",
|
"web/src/desktop_integration.js",
|
||||||
"web/src/dialog_widget.ts",
|
"web/src/dialog_widget.ts",
|
||||||
"web/src/drafts.js",
|
"web/src/drafts.js",
|
||||||
|
"web/src/drafts_overlay_ui.js",
|
||||||
"web/src/dropdown_widget.js",
|
"web/src/dropdown_widget.js",
|
||||||
"web/src/echo.js",
|
"web/src/echo.js",
|
||||||
"web/src/emoji_picker.js",
|
"web/src/emoji_picker.js",
|
||||||
|
|
|
@ -5,22 +5,14 @@ import _ from "lodash";
|
||||||
import tippy from "tippy.js";
|
import tippy from "tippy.js";
|
||||||
|
|
||||||
import render_confirm_delete_all_drafts from "../templates/confirm_dialog/confirm_delete_all_drafts.hbs";
|
import render_confirm_delete_all_drafts from "../templates/confirm_dialog/confirm_delete_all_drafts.hbs";
|
||||||
import render_draft_table_body from "../templates/draft_table_body.hbs";
|
|
||||||
|
|
||||||
import * as blueslip from "./blueslip";
|
import * as blueslip from "./blueslip";
|
||||||
import * as browser_history from "./browser_history";
|
|
||||||
import * as compose_actions from "./compose_actions";
|
|
||||||
import * as compose_state from "./compose_state";
|
import * as compose_state from "./compose_state";
|
||||||
import * as confirm_dialog from "./confirm_dialog";
|
import * as confirm_dialog from "./confirm_dialog";
|
||||||
import {$t, $t_html} from "./i18n";
|
import {$t, $t_html} from "./i18n";
|
||||||
import {localstorage} from "./localstorage";
|
import {localstorage} from "./localstorage";
|
||||||
import * as markdown from "./markdown";
|
import * as markdown from "./markdown";
|
||||||
import * as messages_overlay_ui from "./messages_overlay_ui";
|
|
||||||
import * as narrow from "./narrow";
|
|
||||||
import * as narrow_state from "./narrow_state";
|
|
||||||
import * as overlays from "./overlays";
|
|
||||||
import * as people from "./people";
|
import * as people from "./people";
|
||||||
import * as rendered_markdown from "./rendered_markdown";
|
|
||||||
import * as stream_color from "./stream_color";
|
import * as stream_color from "./stream_color";
|
||||||
import * as stream_data from "./stream_data";
|
import * as stream_data from "./stream_data";
|
||||||
import * as sub_store from "./sub_store";
|
import * as sub_store from "./sub_store";
|
||||||
|
@ -28,7 +20,7 @@ import * as timerender from "./timerender";
|
||||||
import * as ui_util from "./ui_util";
|
import * as ui_util from "./ui_util";
|
||||||
import * as util from "./util";
|
import * as util from "./util";
|
||||||
|
|
||||||
function set_count(count) {
|
export function set_count(count) {
|
||||||
const $drafts_li = $(".top_left_drafts");
|
const $drafts_li = $(".top_left_drafts");
|
||||||
ui_util.update_unread_count_in_dom($drafts_li, count);
|
ui_util.update_unread_count_in_dom($drafts_li, count);
|
||||||
}
|
}
|
||||||
|
@ -274,37 +266,7 @@ export function update_draft(opts = {}) {
|
||||||
return new_draft_id;
|
return new_draft_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function restore_draft(draft_id) {
|
export const DRAFT_LIFETIME = 30;
|
||||||
const draft = draft_model.getDraft(draft_id);
|
|
||||||
if (!draft) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const compose_args = {...restore_message(draft), draft_id};
|
|
||||||
|
|
||||||
if (compose_args.type === "stream") {
|
|
||||||
if (draft.stream_id !== "" && draft.topic !== "") {
|
|
||||||
narrow.activate(
|
|
||||||
[
|
|
||||||
{operator: "stream", operand: compose_args.stream_name},
|
|
||||||
{operator: "topic", operand: compose_args.topic},
|
|
||||||
],
|
|
||||||
{trigger: "restore draft"},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (compose_args.private_message_recipient !== "") {
|
|
||||||
narrow.activate([{operator: "dm", operand: compose_args.private_message_recipient}], {
|
|
||||||
trigger: "restore draft",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
overlays.close_overlay("drafts");
|
|
||||||
compose_actions.start(compose_args.type, compose_args);
|
|
||||||
}
|
|
||||||
|
|
||||||
const DRAFT_LIFETIME = 30;
|
|
||||||
|
|
||||||
export function remove_old_drafts() {
|
export function remove_old_drafts() {
|
||||||
const old_date = subDays(new Date(), DRAFT_LIFETIME).getTime();
|
const old_date = subDays(new Date(), DRAFT_LIFETIME).getTime();
|
||||||
|
@ -399,334 +361,6 @@ export function format_draft(draft) {
|
||||||
return formatted;
|
return formatted;
|
||||||
}
|
}
|
||||||
|
|
||||||
function remove_draft($draft_row) {
|
|
||||||
// Deletes the draft and removes it from the list
|
|
||||||
const draft_id = $draft_row.data("draft-id");
|
|
||||||
|
|
||||||
draft_model.deleteDraft(draft_id);
|
|
||||||
|
|
||||||
$draft_row.remove();
|
|
||||||
|
|
||||||
if ($("#drafts_table .overlay-message-row").length === 0) {
|
|
||||||
$("#drafts_table .no-drafts").show();
|
|
||||||
}
|
|
||||||
update_rendered_drafts(
|
|
||||||
$("#drafts-from-conversation .overlay-message-row").length > 0,
|
|
||||||
$("#other-drafts .overlay-message-row").length > 0,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function update_rendered_drafts(has_drafts_from_conversation, has_other_drafts) {
|
|
||||||
if (has_drafts_from_conversation) {
|
|
||||||
$("#drafts-from-conversation").show();
|
|
||||||
} else {
|
|
||||||
// Since there are no relevant drafts from this conversation left, switch to the "all drafts" view and remove headers.
|
|
||||||
$("#drafts-from-conversation").hide();
|
|
||||||
$("#other-drafts-header").hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!has_other_drafts) {
|
|
||||||
$("#other-drafts").hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function current_recipient_data() {
|
|
||||||
// Prioritize recipients from the compose box first. If the compose
|
|
||||||
// box isn't open, just return data from the current narrow.
|
|
||||||
if (!compose_state.composing()) {
|
|
||||||
const stream_name = narrow_state.stream_name();
|
|
||||||
return {
|
|
||||||
stream_name,
|
|
||||||
topic: narrow_state.topic(),
|
|
||||||
private_recipients: narrow_state.pm_emails_string(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (compose_state.get_message_type() === "stream") {
|
|
||||||
const stream_name = compose_state.stream_name();
|
|
||||||
return {
|
|
||||||
stream_name,
|
|
||||||
topic: compose_state.topic(),
|
|
||||||
private_recipients: undefined,
|
|
||||||
};
|
|
||||||
} else if (compose_state.get_message_type() === "private") {
|
|
||||||
return {
|
|
||||||
stream_name: undefined,
|
|
||||||
topic: undefined,
|
|
||||||
private_recipients: compose_state.private_message_recipient(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
stream_name: undefined,
|
|
||||||
topic: undefined,
|
|
||||||
private_recipients: undefined,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function filter_drafts_by_compose_box_and_recipient(drafts) {
|
|
||||||
const {stream_name, topic, private_recipients} = current_recipient_data();
|
|
||||||
const stream_id = stream_name ? stream_data.get_stream_id(stream_name) : undefined;
|
|
||||||
const narrow_drafts_ids = [];
|
|
||||||
for (const [id, draft] of Object.entries(drafts)) {
|
|
||||||
// Match by stream and topic.
|
|
||||||
if (
|
|
||||||
stream_id &&
|
|
||||||
topic &&
|
|
||||||
draft.topic &&
|
|
||||||
util.same_recipient(draft, {type: "stream", stream_id, topic})
|
|
||||||
) {
|
|
||||||
narrow_drafts_ids.push(id);
|
|
||||||
}
|
|
||||||
// Match by only stream.
|
|
||||||
else if (draft.type === "stream" && stream_id && !topic && draft.stream_id === stream_id) {
|
|
||||||
narrow_drafts_ids.push(id);
|
|
||||||
}
|
|
||||||
// Match by direct message recipient.
|
|
||||||
else if (
|
|
||||||
draft.type === "private" &&
|
|
||||||
private_recipients &&
|
|
||||||
_.isEqual(
|
|
||||||
draft.private_message_recipient
|
|
||||||
.split(",")
|
|
||||||
.map((s) => s.trim())
|
|
||||||
.sort(),
|
|
||||||
private_recipients
|
|
||||||
.split(",")
|
|
||||||
.map((s) => s.trim())
|
|
||||||
.sort(),
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
narrow_drafts_ids.push(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return _.pick(drafts, narrow_drafts_ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
const keyboard_handling_context = {
|
|
||||||
get_items_ids() {
|
|
||||||
const draft_arrow = draft_model.get();
|
|
||||||
return Object.getOwnPropertyNames(draft_arrow);
|
|
||||||
},
|
|
||||||
on_enter() {
|
|
||||||
// This handles when pressing Enter while looking at drafts.
|
|
||||||
// It restores draft that is focused.
|
|
||||||
const draft_id_arrow = this.get_items_ids();
|
|
||||||
const focused_draft_id = messages_overlay_ui.get_focused_element_id(this);
|
|
||||||
if (Object.hasOwn(document.activeElement.parentElement.dataset, "draftId")) {
|
|
||||||
restore_draft(focused_draft_id);
|
|
||||||
} else {
|
|
||||||
const first_draft = draft_id_arrow.at(-1);
|
|
||||||
restore_draft(first_draft);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
on_delete() {
|
|
||||||
// Allows user to delete drafts with Backspace
|
|
||||||
const focused_element_id = messages_overlay_ui.get_focused_element_id(this);
|
|
||||||
if (focused_element_id === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const $focused_row = messages_overlay_ui.row_with_focus(this);
|
|
||||||
messages_overlay_ui.focus_on_sibling_element(this);
|
|
||||||
remove_draft($focused_row);
|
|
||||||
},
|
|
||||||
items_container_selector: "drafts-container",
|
|
||||||
items_list_selector: "drafts-list",
|
|
||||||
row_item_selector: "overlay-message-row",
|
|
||||||
box_item_selector: "overlay-message-info-box",
|
|
||||||
id_attribute_name: "data-draft-id",
|
|
||||||
};
|
|
||||||
|
|
||||||
export function handle_keyboard_events(event_key) {
|
|
||||||
messages_overlay_ui.modals_handle_events(event_key, keyboard_handling_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function launch() {
|
|
||||||
function format_drafts(data) {
|
|
||||||
for (const [id, draft] of Object.entries(data)) {
|
|
||||||
draft.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
const unsorted_raw_drafts = Object.values(data);
|
|
||||||
|
|
||||||
const sorted_raw_drafts = unsorted_raw_drafts.sort(
|
|
||||||
(draft_a, draft_b) => draft_b.updatedAt - draft_a.updatedAt,
|
|
||||||
);
|
|
||||||
|
|
||||||
const sorted_formatted_drafts = sorted_raw_drafts
|
|
||||||
.map((draft_row) => format_draft(draft_row))
|
|
||||||
.filter(Boolean);
|
|
||||||
|
|
||||||
return sorted_formatted_drafts;
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_header_for_narrow_drafts() {
|
|
||||||
const {stream_name, topic, private_recipients} = current_recipient_data();
|
|
||||||
if (private_recipients) {
|
|
||||||
return $t(
|
|
||||||
{defaultMessage: "Drafts from conversation with {recipient}"},
|
|
||||||
{
|
|
||||||
recipient: people.emails_to_full_names_string(private_recipients.split(",")),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const recipient = topic ? `#${stream_name} > ${topic}` : `#${stream_name}`;
|
|
||||||
return $t({defaultMessage: "Drafts from {recipient}"}, {recipient});
|
|
||||||
}
|
|
||||||
|
|
||||||
function render_widgets(narrow_drafts, other_drafts) {
|
|
||||||
$("#drafts_table").empty();
|
|
||||||
|
|
||||||
const narrow_drafts_header = get_header_for_narrow_drafts();
|
|
||||||
|
|
||||||
const rendered = render_draft_table_body({
|
|
||||||
narrow_drafts_header,
|
|
||||||
narrow_drafts,
|
|
||||||
other_drafts,
|
|
||||||
draft_lifetime: DRAFT_LIFETIME,
|
|
||||||
});
|
|
||||||
const $drafts_table = $("#drafts_table");
|
|
||||||
$drafts_table.append(rendered);
|
|
||||||
if ($("#drafts_table .overlay-message-row").length > 0) {
|
|
||||||
$("#drafts_table .no-drafts").hide();
|
|
||||||
// Update possible dynamic elements.
|
|
||||||
const $rendered_drafts = $drafts_table.find(
|
|
||||||
".message_content.rendered_markdown.restore-overlay-message",
|
|
||||||
);
|
|
||||||
$rendered_drafts.each(function () {
|
|
||||||
rendered_markdown.update_elements($(this));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
update_rendered_drafts(narrow_drafts.length > 0, other_drafts.length > 0);
|
|
||||||
update_bulk_delete_ui();
|
|
||||||
}
|
|
||||||
|
|
||||||
function setup_event_handlers() {
|
|
||||||
$("#drafts_table .restore-overlay-message").on("click", function (e) {
|
|
||||||
if (document.getSelection().type === "Range") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
const $draft_row = $(this).closest(".overlay-message-row");
|
|
||||||
const $draft_id = $draft_row.data("draft-id");
|
|
||||||
restore_draft($draft_id);
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#drafts_table .overlay_message_controls .delete-overlay-message").on(
|
|
||||||
"click",
|
|
||||||
function () {
|
|
||||||
const $draft_row = $(this).closest(".overlay-message-row");
|
|
||||||
|
|
||||||
remove_draft($draft_row);
|
|
||||||
update_bulk_delete_ui();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
$("#drafts_table .overlay_message_controls .draft-selection-checkbox").on("click", (e) => {
|
|
||||||
const is_checked = is_checkbox_icon_checked($(e.target));
|
|
||||||
toggle_checkbox_icon_state($(e.target), !is_checked);
|
|
||||||
update_bulk_delete_ui();
|
|
||||||
});
|
|
||||||
|
|
||||||
$(".select-drafts-button").on("click", (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
const $unchecked_checkboxes = $(".draft-selection-checkbox").filter(function () {
|
|
||||||
return !is_checkbox_icon_checked($(this));
|
|
||||||
});
|
|
||||||
const check_boxes = $unchecked_checkboxes.length > 0;
|
|
||||||
$(".draft-selection-checkbox").each(function () {
|
|
||||||
toggle_checkbox_icon_state($(this), check_boxes);
|
|
||||||
});
|
|
||||||
update_bulk_delete_ui();
|
|
||||||
});
|
|
||||||
|
|
||||||
$(".delete-selected-drafts-button").on("click", () => {
|
|
||||||
$(".drafts-list")
|
|
||||||
.find(".draft-selection-checkbox.fa-check-square")
|
|
||||||
.closest(".overlay-message-row")
|
|
||||||
.each(function () {
|
|
||||||
remove_draft($(this));
|
|
||||||
});
|
|
||||||
update_bulk_delete_ui();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const drafts = draft_model.get();
|
|
||||||
const narrow_drafts = filter_drafts_by_compose_box_and_recipient(drafts);
|
|
||||||
const other_drafts = _.pick(
|
|
||||||
drafts,
|
|
||||||
_.difference(Object.keys(drafts), Object.keys(narrow_drafts)),
|
|
||||||
);
|
|
||||||
const formatted_narrow_drafts = format_drafts(narrow_drafts);
|
|
||||||
const formatted_other_drafts = format_drafts(other_drafts);
|
|
||||||
|
|
||||||
render_widgets(formatted_narrow_drafts, formatted_other_drafts);
|
|
||||||
|
|
||||||
// We need to force a style calculation on the newly created
|
|
||||||
// element in order for the CSS transition to take effect.
|
|
||||||
$("#draft_overlay").css("opacity");
|
|
||||||
|
|
||||||
open_overlay();
|
|
||||||
const first_element_id = [...formatted_narrow_drafts, ...formatted_other_drafts][0]?.draft_id;
|
|
||||||
messages_overlay_ui.set_initial_element(first_element_id, keyboard_handling_context);
|
|
||||||
setup_event_handlers();
|
|
||||||
}
|
|
||||||
|
|
||||||
function update_bulk_delete_ui() {
|
|
||||||
const $unchecked_checkboxes = $(".draft-selection-checkbox").filter(function () {
|
|
||||||
return !is_checkbox_icon_checked($(this));
|
|
||||||
});
|
|
||||||
const $checked_checkboxes = $(".draft-selection-checkbox").filter(function () {
|
|
||||||
return is_checkbox_icon_checked($(this));
|
|
||||||
});
|
|
||||||
const $select_drafts_button = $(".select-drafts-button");
|
|
||||||
const $select_state_indicator = $(".select-drafts-button .select-state-indicator");
|
|
||||||
const $delete_selected_drafts_button = $(".delete-selected-drafts-button");
|
|
||||||
|
|
||||||
if ($checked_checkboxes.length > 0) {
|
|
||||||
$delete_selected_drafts_button.prop("disabled", false);
|
|
||||||
if ($unchecked_checkboxes.length === 0) {
|
|
||||||
toggle_checkbox_icon_state($select_state_indicator, true);
|
|
||||||
} else {
|
|
||||||
toggle_checkbox_icon_state($select_state_indicator, false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ($unchecked_checkboxes.length > 0) {
|
|
||||||
toggle_checkbox_icon_state($select_state_indicator, false);
|
|
||||||
$delete_selected_drafts_button.prop("disabled", true);
|
|
||||||
} else {
|
|
||||||
$select_drafts_button.hide();
|
|
||||||
$delete_selected_drafts_button.hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function open_overlay() {
|
|
||||||
sync_count();
|
|
||||||
overlays.open_overlay({
|
|
||||||
name: "drafts",
|
|
||||||
$overlay: $("#draft_overlay"),
|
|
||||||
on_close() {
|
|
||||||
browser_history.exit_overlay();
|
|
||||||
sync_count();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_checkbox_icon_checked($checkbox) {
|
|
||||||
return $checkbox.hasClass("fa-check-square");
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggle_checkbox_icon_state($checkbox, checked) {
|
|
||||||
$checkbox.parent().attr("aria-checked", checked);
|
|
||||||
if (checked) {
|
|
||||||
$checkbox.removeClass("fa-square-o").addClass("fa-check-square");
|
|
||||||
} else {
|
|
||||||
$checkbox.removeClass("fa-check-square").addClass("fa-square-o");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function initialize() {
|
export function initialize() {
|
||||||
remove_old_drafts();
|
remove_old_drafts();
|
||||||
|
|
||||||
|
@ -739,8 +373,4 @@ export function initialize() {
|
||||||
});
|
});
|
||||||
|
|
||||||
set_count(Object.keys(draft_model.get()).length);
|
set_count(Object.keys(draft_model.get()).length);
|
||||||
|
|
||||||
$("body").on("focus", "#drafts_table .overlay-message-info-box", (e) => {
|
|
||||||
messages_overlay_ui.activate_element(e.target, keyboard_handling_context);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,382 @@
|
||||||
|
import $ from "jquery";
|
||||||
|
import _ from "lodash";
|
||||||
|
|
||||||
|
import render_draft_table_body from "../templates/draft_table_body.hbs";
|
||||||
|
|
||||||
|
import * as browser_history from "./browser_history";
|
||||||
|
import * as compose_actions from "./compose_actions";
|
||||||
|
import * as compose_state from "./compose_state";
|
||||||
|
import * as drafts from "./drafts";
|
||||||
|
import {$t} from "./i18n";
|
||||||
|
import * as messages_overlay_ui from "./messages_overlay_ui";
|
||||||
|
import * as narrow from "./narrow";
|
||||||
|
import * as narrow_state from "./narrow_state";
|
||||||
|
import * as overlays from "./overlays";
|
||||||
|
import * as people from "./people";
|
||||||
|
import * as rendered_markdown from "./rendered_markdown";
|
||||||
|
import * as stream_data from "./stream_data";
|
||||||
|
import * as util from "./util";
|
||||||
|
|
||||||
|
function restore_draft(draft_id) {
|
||||||
|
const draft = drafts.draft_model.getDraft(draft_id);
|
||||||
|
if (!draft) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const compose_args = {...drafts.restore_message(draft), draft_id};
|
||||||
|
|
||||||
|
if (compose_args.type === "stream") {
|
||||||
|
if (draft.stream_id !== "" && draft.topic !== "") {
|
||||||
|
narrow.activate(
|
||||||
|
[
|
||||||
|
{operator: "stream", operand: compose_args.stream_name},
|
||||||
|
{operator: "topic", operand: compose_args.topic},
|
||||||
|
],
|
||||||
|
{trigger: "restore draft"},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (compose_args.private_message_recipient !== "") {
|
||||||
|
narrow.activate([{operator: "dm", operand: compose_args.private_message_recipient}], {
|
||||||
|
trigger: "restore draft",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
overlays.close_overlay("drafts");
|
||||||
|
compose_actions.start(compose_args.type, compose_args);
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove_draft($draft_row) {
|
||||||
|
// Deletes the draft and removes it from the list
|
||||||
|
const draft_id = $draft_row.data("draft-id");
|
||||||
|
|
||||||
|
drafts.draft_model.deleteDraft(draft_id);
|
||||||
|
|
||||||
|
$draft_row.remove();
|
||||||
|
|
||||||
|
if ($("#drafts_table .overlay-message-row").length === 0) {
|
||||||
|
$("#drafts_table .no-drafts").show();
|
||||||
|
}
|
||||||
|
update_rendered_drafts(
|
||||||
|
$("#drafts-from-conversation .overlay-message-row").length > 0,
|
||||||
|
$("#other-drafts .overlay-message-row").length > 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function update_rendered_drafts(has_drafts_from_conversation, has_other_drafts) {
|
||||||
|
if (has_drafts_from_conversation) {
|
||||||
|
$("#drafts-from-conversation").show();
|
||||||
|
} else {
|
||||||
|
// Since there are no relevant drafts from this conversation left, switch to the "all drafts" view and remove headers.
|
||||||
|
$("#drafts-from-conversation").hide();
|
||||||
|
$("#other-drafts-header").hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!has_other_drafts) {
|
||||||
|
$("#other-drafts").hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function current_recipient_data() {
|
||||||
|
// Prioritize recipients from the compose box first. If the compose
|
||||||
|
// box isn't open, just return data from the current narrow.
|
||||||
|
if (!compose_state.composing()) {
|
||||||
|
const stream_name = narrow_state.stream_name();
|
||||||
|
return {
|
||||||
|
stream_name,
|
||||||
|
topic: narrow_state.topic(),
|
||||||
|
private_recipients: narrow_state.pm_emails_string(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compose_state.get_message_type() === "stream") {
|
||||||
|
const stream_name = compose_state.stream_name();
|
||||||
|
return {
|
||||||
|
stream_name,
|
||||||
|
topic: compose_state.topic(),
|
||||||
|
private_recipients: undefined,
|
||||||
|
};
|
||||||
|
} else if (compose_state.get_message_type() === "private") {
|
||||||
|
return {
|
||||||
|
stream_name: undefined,
|
||||||
|
topic: undefined,
|
||||||
|
private_recipients: compose_state.private_message_recipient(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
stream_name: undefined,
|
||||||
|
topic: undefined,
|
||||||
|
private_recipients: undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function filter_drafts_by_compose_box_and_recipient(drafts) {
|
||||||
|
const {stream_name, topic, private_recipients} = current_recipient_data();
|
||||||
|
const stream_id = stream_name ? stream_data.get_stream_id(stream_name) : undefined;
|
||||||
|
const narrow_drafts_ids = [];
|
||||||
|
for (const [id, draft] of Object.entries(drafts)) {
|
||||||
|
// Match by stream and topic.
|
||||||
|
if (
|
||||||
|
stream_id &&
|
||||||
|
topic &&
|
||||||
|
draft.topic &&
|
||||||
|
util.same_recipient(draft, {type: "stream", stream_id, topic})
|
||||||
|
) {
|
||||||
|
narrow_drafts_ids.push(id);
|
||||||
|
}
|
||||||
|
// Match by only stream.
|
||||||
|
else if (draft.type === "stream" && stream_id && !topic && draft.stream_id === stream_id) {
|
||||||
|
narrow_drafts_ids.push(id);
|
||||||
|
}
|
||||||
|
// Match by direct message recipient.
|
||||||
|
else if (
|
||||||
|
draft.type === "private" &&
|
||||||
|
private_recipients &&
|
||||||
|
_.isEqual(
|
||||||
|
draft.private_message_recipient
|
||||||
|
.split(",")
|
||||||
|
.map((s) => s.trim())
|
||||||
|
.sort(),
|
||||||
|
private_recipients
|
||||||
|
.split(",")
|
||||||
|
.map((s) => s.trim())
|
||||||
|
.sort(),
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
narrow_drafts_ids.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _.pick(drafts, narrow_drafts_ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyboard_handling_context = {
|
||||||
|
get_items_ids() {
|
||||||
|
const draft_arrow = drafts.draft_model.get();
|
||||||
|
return Object.getOwnPropertyNames(draft_arrow);
|
||||||
|
},
|
||||||
|
on_enter() {
|
||||||
|
// This handles when pressing Enter while looking at drafts.
|
||||||
|
// It restores draft that is focused.
|
||||||
|
const draft_id_arrow = this.get_items_ids();
|
||||||
|
const focused_draft_id = messages_overlay_ui.get_focused_element_id(this);
|
||||||
|
if (Object.hasOwn(document.activeElement.parentElement.dataset, "draftId")) {
|
||||||
|
restore_draft(focused_draft_id);
|
||||||
|
} else {
|
||||||
|
const first_draft = draft_id_arrow.at(-1);
|
||||||
|
restore_draft(first_draft);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
on_delete() {
|
||||||
|
// Allows user to delete drafts with Backspace
|
||||||
|
const focused_element_id = messages_overlay_ui.get_focused_element_id(this);
|
||||||
|
if (focused_element_id === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const $focused_row = messages_overlay_ui.row_with_focus(this);
|
||||||
|
messages_overlay_ui.focus_on_sibling_element(this);
|
||||||
|
remove_draft($focused_row);
|
||||||
|
},
|
||||||
|
items_container_selector: "drafts-container",
|
||||||
|
items_list_selector: "drafts-list",
|
||||||
|
row_item_selector: "overlay-message-row",
|
||||||
|
box_item_selector: "overlay-message-info-box",
|
||||||
|
id_attribute_name: "data-draft-id",
|
||||||
|
};
|
||||||
|
|
||||||
|
export function handle_keyboard_events(event_key) {
|
||||||
|
messages_overlay_ui.modals_handle_events(event_key, keyboard_handling_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function launch() {
|
||||||
|
function format_drafts(data) {
|
||||||
|
for (const [id, draft] of Object.entries(data)) {
|
||||||
|
draft.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsorted_raw_drafts = Object.values(data);
|
||||||
|
|
||||||
|
const sorted_raw_drafts = unsorted_raw_drafts.sort(
|
||||||
|
(draft_a, draft_b) => draft_b.updatedAt - draft_a.updatedAt,
|
||||||
|
);
|
||||||
|
|
||||||
|
const sorted_formatted_drafts = sorted_raw_drafts
|
||||||
|
.map((draft_row) => drafts.format_draft(draft_row))
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
return sorted_formatted_drafts;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_header_for_narrow_drafts() {
|
||||||
|
const {stream_name, topic, private_recipients} = current_recipient_data();
|
||||||
|
if (private_recipients) {
|
||||||
|
return $t(
|
||||||
|
{defaultMessage: "Drafts from conversation with {recipient}"},
|
||||||
|
{
|
||||||
|
recipient: people.emails_to_full_names_string(private_recipients.split(",")),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const recipient = topic ? `#${stream_name} > ${topic}` : `#${stream_name}`;
|
||||||
|
return $t({defaultMessage: "Drafts from {recipient}"}, {recipient});
|
||||||
|
}
|
||||||
|
|
||||||
|
function render_widgets(narrow_drafts, other_drafts) {
|
||||||
|
$("#drafts_table").empty();
|
||||||
|
|
||||||
|
const narrow_drafts_header = get_header_for_narrow_drafts();
|
||||||
|
|
||||||
|
const rendered = render_draft_table_body({
|
||||||
|
narrow_drafts_header,
|
||||||
|
narrow_drafts,
|
||||||
|
other_drafts,
|
||||||
|
draft_lifetime: drafts.DRAFT_LIFETIME,
|
||||||
|
});
|
||||||
|
const $drafts_table = $("#drafts_table");
|
||||||
|
$drafts_table.append(rendered);
|
||||||
|
if ($("#drafts_table .overlay-message-row").length > 0) {
|
||||||
|
$("#drafts_table .no-drafts").hide();
|
||||||
|
// Update possible dynamic elements.
|
||||||
|
const $rendered_drafts = $drafts_table.find(
|
||||||
|
".message_content.rendered_markdown.restore-overlay-message",
|
||||||
|
);
|
||||||
|
$rendered_drafts.each(function () {
|
||||||
|
rendered_markdown.update_elements($(this));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
update_rendered_drafts(narrow_drafts.length > 0, other_drafts.length > 0);
|
||||||
|
update_bulk_delete_ui();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setup_event_handlers() {
|
||||||
|
$("#drafts_table .restore-overlay-message").on("click", function (e) {
|
||||||
|
if (document.getSelection().type === "Range") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
const $draft_row = $(this).closest(".overlay-message-row");
|
||||||
|
const $draft_id = $draft_row.data("draft-id");
|
||||||
|
restore_draft($draft_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#drafts_table .overlay_message_controls .delete-overlay-message").on(
|
||||||
|
"click",
|
||||||
|
function () {
|
||||||
|
const $draft_row = $(this).closest(".overlay-message-row");
|
||||||
|
|
||||||
|
remove_draft($draft_row);
|
||||||
|
update_bulk_delete_ui();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
$("#drafts_table .overlay_message_controls .draft-selection-checkbox").on("click", (e) => {
|
||||||
|
const is_checked = is_checkbox_icon_checked($(e.target));
|
||||||
|
toggle_checkbox_icon_state($(e.target), !is_checked);
|
||||||
|
update_bulk_delete_ui();
|
||||||
|
});
|
||||||
|
|
||||||
|
$(".select-drafts-button").on("click", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const $unchecked_checkboxes = $(".draft-selection-checkbox").filter(function () {
|
||||||
|
return !is_checkbox_icon_checked($(this));
|
||||||
|
});
|
||||||
|
const check_boxes = $unchecked_checkboxes.length > 0;
|
||||||
|
$(".draft-selection-checkbox").each(function () {
|
||||||
|
toggle_checkbox_icon_state($(this), check_boxes);
|
||||||
|
});
|
||||||
|
update_bulk_delete_ui();
|
||||||
|
});
|
||||||
|
|
||||||
|
$(".delete-selected-drafts-button").on("click", () => {
|
||||||
|
$(".drafts-list")
|
||||||
|
.find(".draft-selection-checkbox.fa-check-square")
|
||||||
|
.closest(".overlay-message-row")
|
||||||
|
.each(function () {
|
||||||
|
remove_draft($(this));
|
||||||
|
});
|
||||||
|
update_bulk_delete_ui();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const all_drafts = drafts.draft_model.get();
|
||||||
|
const narrow_drafts = filter_drafts_by_compose_box_and_recipient(all_drafts);
|
||||||
|
const other_drafts = _.pick(
|
||||||
|
all_drafts,
|
||||||
|
_.difference(Object.keys(all_drafts), Object.keys(narrow_drafts)),
|
||||||
|
);
|
||||||
|
const formatted_narrow_drafts = format_drafts(narrow_drafts);
|
||||||
|
const formatted_other_drafts = format_drafts(other_drafts);
|
||||||
|
|
||||||
|
render_widgets(formatted_narrow_drafts, formatted_other_drafts);
|
||||||
|
|
||||||
|
// We need to force a style calculation on the newly created
|
||||||
|
// element in order for the CSS transition to take effect.
|
||||||
|
$("#draft_overlay").css("opacity");
|
||||||
|
|
||||||
|
open_overlay();
|
||||||
|
const first_element_id = [...formatted_narrow_drafts, ...formatted_other_drafts][0]?.draft_id;
|
||||||
|
messages_overlay_ui.set_initial_element(first_element_id, keyboard_handling_context);
|
||||||
|
setup_event_handlers();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function update_bulk_delete_ui() {
|
||||||
|
const $unchecked_checkboxes = $(".draft-selection-checkbox").filter(function () {
|
||||||
|
return !is_checkbox_icon_checked($(this));
|
||||||
|
});
|
||||||
|
const $checked_checkboxes = $(".draft-selection-checkbox").filter(function () {
|
||||||
|
return is_checkbox_icon_checked($(this));
|
||||||
|
});
|
||||||
|
const $select_drafts_button = $(".select-drafts-button");
|
||||||
|
const $select_state_indicator = $(".select-drafts-button .select-state-indicator");
|
||||||
|
const $delete_selected_drafts_button = $(".delete-selected-drafts-button");
|
||||||
|
|
||||||
|
if ($checked_checkboxes.length > 0) {
|
||||||
|
$delete_selected_drafts_button.prop("disabled", false);
|
||||||
|
if ($unchecked_checkboxes.length === 0) {
|
||||||
|
toggle_checkbox_icon_state($select_state_indicator, true);
|
||||||
|
} else {
|
||||||
|
toggle_checkbox_icon_state($select_state_indicator, false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($unchecked_checkboxes.length > 0) {
|
||||||
|
toggle_checkbox_icon_state($select_state_indicator, false);
|
||||||
|
$delete_selected_drafts_button.prop("disabled", true);
|
||||||
|
} else {
|
||||||
|
$select_drafts_button.hide();
|
||||||
|
$delete_selected_drafts_button.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function open_overlay() {
|
||||||
|
drafts.sync_count();
|
||||||
|
overlays.open_overlay({
|
||||||
|
name: "drafts",
|
||||||
|
$overlay: $("#draft_overlay"),
|
||||||
|
on_close() {
|
||||||
|
browser_history.exit_overlay();
|
||||||
|
drafts.sync_count();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function is_checkbox_icon_checked($checkbox) {
|
||||||
|
return $checkbox.hasClass("fa-check-square");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toggle_checkbox_icon_state($checkbox, checked) {
|
||||||
|
$checkbox.parent().attr("aria-checked", checked);
|
||||||
|
if (checked) {
|
||||||
|
$checkbox.removeClass("fa-square-o").addClass("fa-check-square");
|
||||||
|
} else {
|
||||||
|
$checkbox.removeClass("fa-check-square").addClass("fa-square-o");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initialize() {
|
||||||
|
$("body").on("focus", "#drafts_table .overlay-message-info-box", (e) => {
|
||||||
|
messages_overlay_ui.activate_element(e.target, keyboard_handling_context);
|
||||||
|
});
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ import * as about_zulip from "./about_zulip";
|
||||||
import * as admin from "./admin";
|
import * as admin from "./admin";
|
||||||
import * as blueslip from "./blueslip";
|
import * as blueslip from "./blueslip";
|
||||||
import * as browser_history from "./browser_history";
|
import * as browser_history from "./browser_history";
|
||||||
import * as drafts from "./drafts";
|
import * as drafts_overlay_ui from "./drafts_overlay_ui";
|
||||||
import * as hash_parser from "./hash_parser";
|
import * as hash_parser from "./hash_parser";
|
||||||
import * as hash_util from "./hash_util";
|
import * as hash_util from "./hash_util";
|
||||||
import {$t_html} from "./i18n";
|
import {$t_html} from "./i18n";
|
||||||
|
@ -400,7 +400,7 @@ function do_hashchange_overlay(old_hash) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (base === "drafts") {
|
if (base === "drafts") {
|
||||||
drafts.launch();
|
drafts_overlay_ui.launch();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ import * as compose_textarea from "./compose_textarea";
|
||||||
import * as condense from "./condense";
|
import * as condense from "./condense";
|
||||||
import * as copy_and_paste from "./copy_and_paste";
|
import * as copy_and_paste from "./copy_and_paste";
|
||||||
import * as deprecated_feature_notice from "./deprecated_feature_notice";
|
import * as deprecated_feature_notice from "./deprecated_feature_notice";
|
||||||
import * as drafts from "./drafts";
|
import * as drafts_overlay_ui from "./drafts_overlay_ui";
|
||||||
import * as emoji from "./emoji";
|
import * as emoji from "./emoji";
|
||||||
import * as emoji_picker from "./emoji_picker";
|
import * as emoji_picker from "./emoji_picker";
|
||||||
import * as feedback_widget from "./feedback_widget";
|
import * as feedback_widget from "./feedback_widget";
|
||||||
|
@ -460,7 +460,7 @@ export function process_enter_key(e) {
|
||||||
// This handles when pressing Enter while looking at drafts.
|
// This handles when pressing Enter while looking at drafts.
|
||||||
// It restores draft that is focused.
|
// It restores draft that is focused.
|
||||||
if (overlays.drafts_open()) {
|
if (overlays.drafts_open()) {
|
||||||
drafts.handle_keyboard_events("enter");
|
drafts_overlay_ui.handle_keyboard_events("enter");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -705,7 +705,7 @@ export function process_hotkey(e, hotkey) {
|
||||||
case "backspace":
|
case "backspace":
|
||||||
case "delete":
|
case "delete":
|
||||||
if (overlays.drafts_open()) {
|
if (overlays.drafts_open()) {
|
||||||
drafts.handle_keyboard_events(event_name);
|
drafts_overlay_ui.handle_keyboard_events(event_name);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (overlays.scheduled_messages_open()) {
|
if (overlays.scheduled_messages_open()) {
|
||||||
|
|
|
@ -34,6 +34,7 @@ import * as condense from "./condense";
|
||||||
import * as copy_and_paste from "./copy_and_paste";
|
import * as copy_and_paste from "./copy_and_paste";
|
||||||
import * as dark_theme from "./dark_theme";
|
import * as dark_theme from "./dark_theme";
|
||||||
import * as drafts from "./drafts";
|
import * as drafts from "./drafts";
|
||||||
|
import * as drafts_overlay_ui from "./drafts_overlay_ui";
|
||||||
import * as echo from "./echo";
|
import * as echo from "./echo";
|
||||||
import * as emoji from "./emoji";
|
import * as emoji from "./emoji";
|
||||||
import * as emoji_picker from "./emoji_picker";
|
import * as emoji_picker from "./emoji_picker";
|
||||||
|
@ -697,6 +698,7 @@ export function initialize_everything() {
|
||||||
});
|
});
|
||||||
topic_zoom.initialize();
|
topic_zoom.initialize();
|
||||||
drafts.initialize();
|
drafts.initialize();
|
||||||
|
drafts_overlay_ui.initialize();
|
||||||
sent_messages.initialize();
|
sent_messages.initialize();
|
||||||
hotspots.initialize();
|
hotspots.initialize();
|
||||||
typing.initialize();
|
typing.initialize();
|
||||||
|
|
|
@ -63,6 +63,7 @@ user_settings.twenty_four_hour_time = false;
|
||||||
|
|
||||||
const {localstorage} = zrequire("localstorage");
|
const {localstorage} = zrequire("localstorage");
|
||||||
const drafts = zrequire("drafts");
|
const drafts = zrequire("drafts");
|
||||||
|
const drafts_overlay_ui = zrequire("drafts_overlay_ui");
|
||||||
const messages_overlay_ui = zrequire("messages_overlay_ui");
|
const messages_overlay_ui = zrequire("messages_overlay_ui");
|
||||||
const timerender = zrequire("timerender");
|
const timerender = zrequire("timerender");
|
||||||
|
|
||||||
|
@ -214,6 +215,7 @@ test("initialize", ({override_rewire}) => {
|
||||||
$(".top_left_drafts").set_find_results(".unread_count", $unread_count);
|
$(".top_left_drafts").set_find_results(".unread_count", $unread_count);
|
||||||
|
|
||||||
drafts.initialize();
|
drafts.initialize();
|
||||||
|
drafts_overlay_ui.initialize();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("remove_old_drafts", () => {
|
test("remove_old_drafts", () => {
|
||||||
|
@ -603,7 +605,7 @@ test("format_drafts", ({override_rewire, mock_template}) => {
|
||||||
|
|
||||||
$.create("#drafts_table .overlay-message-row", {children: []});
|
$.create("#drafts_table .overlay-message-row", {children: []});
|
||||||
$(".draft-selection-checkbox").filter = () => [];
|
$(".draft-selection-checkbox").filter = () => [];
|
||||||
drafts.launch();
|
drafts_overlay_ui.launch();
|
||||||
|
|
||||||
$.clear_all_elements();
|
$.clear_all_elements();
|
||||||
$.create("#drafts_table .overlay-message-row", {children: []});
|
$.create("#drafts_table .overlay-message-row", {children: []});
|
||||||
|
@ -622,7 +624,7 @@ test("format_drafts", ({override_rewire, mock_template}) => {
|
||||||
$(".top_left_drafts").set_find_results(".unread_count", $unread_count);
|
$(".top_left_drafts").set_find_results(".unread_count", $unread_count);
|
||||||
|
|
||||||
$(".draft-selection-checkbox").filter = () => [];
|
$(".draft-selection-checkbox").filter = () => [];
|
||||||
drafts.launch();
|
drafts_overlay_ui.launch();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("filter_drafts", ({override_rewire, mock_template}) => {
|
test("filter_drafts", ({override_rewire, mock_template}) => {
|
||||||
|
@ -770,5 +772,5 @@ test("filter_drafts", ({override_rewire, mock_template}) => {
|
||||||
|
|
||||||
$.create("#drafts_table .overlay-message-row", {children: []});
|
$.create("#drafts_table .overlay-message-row", {children: []});
|
||||||
$(".draft-selection-checkbox").filter = () => [];
|
$(".draft-selection-checkbox").filter = () => [];
|
||||||
drafts.launch();
|
drafts_overlay_ui.launch();
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,7 +15,7 @@ set_global("document", "document-stub");
|
||||||
const history = set_global("history", {});
|
const history = set_global("history", {});
|
||||||
|
|
||||||
const admin = mock_esm("../src/admin");
|
const admin = mock_esm("../src/admin");
|
||||||
const drafts = mock_esm("../src/drafts");
|
const drafts_overlay_ui = mock_esm("../src/drafts_overlay_ui");
|
||||||
const info_overlay = mock_esm("../src/info_overlay");
|
const info_overlay = mock_esm("../src/info_overlay");
|
||||||
const message_viewport = mock_esm("../src/message_viewport");
|
const message_viewport = mock_esm("../src/message_viewport");
|
||||||
const narrow = mock_esm("../src/narrow");
|
const narrow = mock_esm("../src/narrow");
|
||||||
|
@ -132,7 +132,7 @@ function test_helper({override, change_tab}) {
|
||||||
|
|
||||||
stub(admin, "launch");
|
stub(admin, "launch");
|
||||||
stub(admin, "build_page");
|
stub(admin, "build_page");
|
||||||
stub(drafts, "launch");
|
stub(drafts_overlay_ui, "launch");
|
||||||
stub(message_viewport, "stop_auto_scrolling");
|
stub(message_viewport, "stop_auto_scrolling");
|
||||||
stub(narrow, "deactivate");
|
stub(narrow, "deactivate");
|
||||||
stub(overlays, "close_for_hash_change");
|
stub(overlays, "close_for_hash_change");
|
||||||
|
@ -300,7 +300,7 @@ run_test("hash_interactions", ({override}) => {
|
||||||
$window_stub.trigger("hashchange");
|
$window_stub.trigger("hashchange");
|
||||||
helper.assert_events([
|
helper.assert_events([
|
||||||
[overlays, "close_for_hash_change"],
|
[overlays, "close_for_hash_change"],
|
||||||
[drafts, "launch"],
|
[drafts_overlay_ui, "launch"],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
window.location.hash = "#settings/alert-words";
|
window.location.hash = "#settings/alert-words";
|
||||||
|
|
|
@ -35,7 +35,7 @@ set_global("document", "document-stub");
|
||||||
const browser_history = mock_esm("../src/browser_history");
|
const browser_history = mock_esm("../src/browser_history");
|
||||||
const compose_actions = mock_esm("../src/compose_actions");
|
const compose_actions = mock_esm("../src/compose_actions");
|
||||||
const condense = mock_esm("../src/condense");
|
const condense = mock_esm("../src/condense");
|
||||||
const drafts = mock_esm("../src/drafts");
|
const drafts_overlay_ui = mock_esm("../src/drafts_overlay_ui");
|
||||||
const emoji_picker = mock_esm("../src/emoji_picker", {
|
const emoji_picker = mock_esm("../src/emoji_picker", {
|
||||||
is_open: () => false,
|
is_open: () => false,
|
||||||
toggle_emoji_popover() {},
|
toggle_emoji_popover() {},
|
||||||
|
@ -539,8 +539,8 @@ run_test("motion_keys", () => {
|
||||||
|
|
||||||
delete overlays.is_active;
|
delete overlays.is_active;
|
||||||
overlays.drafts_open = () => true;
|
overlays.drafts_open = () => true;
|
||||||
assert_mapping("up_arrow", drafts, "handle_keyboard_events");
|
assert_mapping("up_arrow", drafts_overlay_ui, "handle_keyboard_events");
|
||||||
assert_mapping("down_arrow", drafts, "handle_keyboard_events");
|
assert_mapping("down_arrow", drafts_overlay_ui, "handle_keyboard_events");
|
||||||
delete overlays.is_active;
|
delete overlays.is_active;
|
||||||
delete overlays.drafts_open;
|
delete overlays.drafts_open;
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue