mirror of
https://github.com/zulip/zulip.git
synced 2025-11-12 01:47:41 +00:00
compose: Restore the last draft when compose box is opened.
Now when the user opens a narrow that has a draft saved for that particular conversation, the draft will automatically be restored in the compose box. This will make it easy to return to a draft after clicking away, and also will make it less confusing when people close the compose box by accident. Note that this only restores the draft when there is a full recipient specified (stream and topic, or at least one PM recipient). Fixes part of #18555. Fixes #11218 and #17396.
This commit is contained in:
@@ -43,6 +43,23 @@ async function create_stream_message_draft(page: Page): Promise<void> {
|
||||
await page.click("#compose_close");
|
||||
}
|
||||
|
||||
async function test_restore_stream_message_draft_by_opening_compose_box(page: Page): Promise<void> {
|
||||
await page.click(".search_icon");
|
||||
await page.waitForSelector("#search_query", {visible: true});
|
||||
await common.select_item_via_typeahead(page, "#search_query", "stream:Denmark topic:tests", "");
|
||||
|
||||
await page.click("#left_bar_compose_reply_button_big");
|
||||
await page.waitForSelector("#send_message_form", {visible: true});
|
||||
|
||||
await common.check_compose_state(page, {
|
||||
stream: "Denmark",
|
||||
topic: "tests",
|
||||
content: "Test stream message.",
|
||||
});
|
||||
await page.click("#compose_close");
|
||||
await page.waitForSelector("#send_message_form", {visible: false});
|
||||
}
|
||||
|
||||
async function create_private_message_draft(page: Page): Promise<void> {
|
||||
console.log("Creating direct message draft");
|
||||
await page.keyboard.press("KeyX");
|
||||
@@ -50,7 +67,17 @@ async function create_private_message_draft(page: Page): Promise<void> {
|
||||
await common.fill_form(page, "form#send_message_form", {content: "Test direct message."});
|
||||
await common.pm_recipient.set(page, "cordelia@zulip.com");
|
||||
await common.pm_recipient.set(page, "hamlet@zulip.com");
|
||||
}
|
||||
|
||||
async function test_restore_private_message_draft_by_opening_composebox(page: Page): Promise<void> {
|
||||
await page.click("#left_bar_compose_reply_button_big");
|
||||
await page.waitForSelector("#private_message_recipient", {visible: true});
|
||||
|
||||
await common.check_form_contents(page, "form#send_message_form", {
|
||||
content: "Test direct message.",
|
||||
});
|
||||
await page.click("#compose_close");
|
||||
await page.waitForSelector("#private_message_recipient", {visible: false});
|
||||
}
|
||||
|
||||
async function open_compose_markdown_preview(page: Page): Promise<void> {
|
||||
@@ -99,7 +126,7 @@ async function test_previously_created_drafts_rendered(page: Page): Promise<void
|
||||
page,
|
||||
"#drafts_table .overlay-message-row .message_header_private_message .stream_label",
|
||||
),
|
||||
"You and Cordelia, Lear's daughter, King Hamlet",
|
||||
"You and King Hamlet, Cordelia, Lear's daughter",
|
||||
);
|
||||
assert.strictEqual(
|
||||
await common.get_text_from_selector(
|
||||
@@ -174,7 +201,7 @@ async function test_restore_private_message_draft_via_draft_overlay(page: Page):
|
||||
});
|
||||
const cordelia_internal_email = await common.get_internal_email_from_name(page, "cordelia");
|
||||
const hamlet_internal_email = await common.get_internal_email_from_name(page, "hamlet");
|
||||
await common.pm_recipient.expect(page, `${cordelia_internal_email},${hamlet_internal_email}`);
|
||||
await common.pm_recipient.expect(page, `${hamlet_internal_email},${cordelia_internal_email}`);
|
||||
assert.strictEqual(
|
||||
await common.get_text_from_selector(page, "title"),
|
||||
"Cordelia, Lear's daughter, King Hamlet - Zulip Dev - Zulip",
|
||||
@@ -259,7 +286,22 @@ async function drafts_test(page: Page): Promise<void> {
|
||||
await test_empty_drafts(page);
|
||||
|
||||
await create_stream_message_draft(page);
|
||||
await test_restore_stream_message_draft_by_opening_compose_box(page);
|
||||
|
||||
// Send a private message so that the draft we create is
|
||||
// for an existing conversation.
|
||||
await common.send_message(page, "private", {
|
||||
recipient: "cordelia@zulip.com, hamlet@zulip.com",
|
||||
content: "howdy doo",
|
||||
outside_view: true,
|
||||
});
|
||||
await create_private_message_draft(page);
|
||||
// Narrow to the conversation so that the compose box will restore it,
|
||||
// then close and try restoring it by opening the composebox again.
|
||||
await page.click("#compose .narrow_to_compose_recipients");
|
||||
await page.click("#compose_close");
|
||||
await test_restore_private_message_draft_by_opening_composebox(page);
|
||||
|
||||
await open_drafts_after_markdown_preview(page);
|
||||
await test_previously_created_drafts_rendered(page);
|
||||
|
||||
|
||||
@@ -302,6 +302,20 @@ export function start(raw_opts: ComposeActionsStartOpts): void {
|
||||
opts.private_message_recipient.replaceAll(/,\s*/g, ", "),
|
||||
);
|
||||
|
||||
// If we're not explicitly opening a different draft, restore the last
|
||||
// saved draft (if it exists).
|
||||
if (
|
||||
!opts.content &&
|
||||
opts.draft_id === undefined &&
|
||||
compose_state.message_content().length === 0
|
||||
) {
|
||||
const possible_last_draft = drafts.get_last_draft_based_on_compose_state();
|
||||
if (possible_last_draft !== undefined) {
|
||||
opts.draft_id = possible_last_draft.id;
|
||||
opts.content = possible_last_draft.content;
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.content !== undefined) {
|
||||
compose_ui.insert_and_scroll_into_view(opts.content, $("textarea#compose-textarea"), true);
|
||||
$(".compose_control_button_container:has(.add-poll)").addClass("disabled-on-hover");
|
||||
|
||||
@@ -493,6 +493,20 @@ export function filter_drafts_by_compose_box_and_recipient(
|
||||
return _.pick(drafts, narrow_drafts_ids);
|
||||
}
|
||||
|
||||
export function get_last_draft_based_on_compose_state(): LocalStorageDraftWithId | undefined {
|
||||
const current_drafts = draft_model.get();
|
||||
const drafts_map_for_compose_state = filter_drafts_by_compose_box_and_recipient(current_drafts);
|
||||
const drafts_for_compose_state = Object.entries(drafts_map_for_compose_state).map(
|
||||
([draft_id, draft]) => ({
|
||||
...draft,
|
||||
id: draft_id,
|
||||
}),
|
||||
);
|
||||
return drafts_for_compose_state
|
||||
.sort((draft_a, draft_b) => draft_a.updatedAt - draft_b.updatedAt)
|
||||
.pop();
|
||||
}
|
||||
|
||||
export function remove_old_drafts(): void {
|
||||
const old_date = subDays(new Date(), DRAFT_LIFETIME).getTime();
|
||||
const drafts = draft_model.get();
|
||||
|
||||
@@ -45,6 +45,7 @@ mock_esm("../src/reload_state", {
|
||||
mock_esm("../src/drafts", {
|
||||
update_draft: noop,
|
||||
update_compose_draft_count: noop,
|
||||
get_last_draft_based_on_compose_state: noop,
|
||||
});
|
||||
mock_esm("../src/unread_ops", {
|
||||
notify_server_message_read: noop,
|
||||
|
||||
@@ -65,9 +65,10 @@ const drafts_overlay_ui = zrequire("drafts_overlay_ui");
|
||||
const timerender = zrequire("timerender");
|
||||
|
||||
const mock_current_timestamp = 1234;
|
||||
const stream_id = 30;
|
||||
|
||||
const draft_1 = {
|
||||
stream_id: 30,
|
||||
stream_id,
|
||||
topic: "topic",
|
||||
type: "stream",
|
||||
content: "Test stream message",
|
||||
@@ -81,7 +82,7 @@ const draft_2 = {
|
||||
updatedAt: mock_current_timestamp,
|
||||
};
|
||||
const short_msg = {
|
||||
stream_id: 30,
|
||||
stream_id,
|
||||
topic: "topic",
|
||||
type: "stream",
|
||||
content: "a",
|
||||
|
||||
Reference in New Issue
Block a user