drafts: Add functionality to bulk delete drafts.

Fixes #19360.

Co-authored-by: Satyam Bansal <sbansal1999@gmail.com>
This commit is contained in:
Julia Bichler 2023-01-24 12:49:04 +01:00 committed by Tim Abbott
parent e4048de0a9
commit fed866449d
7 changed files with 201 additions and 9 deletions

View File

@ -617,6 +617,7 @@ export function launch() {
}); });
} }
update_rendered_drafts(narrow_drafts.length > 0, other_drafts.length > 0); update_rendered_drafts(narrow_drafts.length > 0, other_drafts.length > 0);
update_bulk_delete_ui();
} }
function setup_event_handlers() { function setup_event_handlers() {
@ -638,8 +639,37 @@ export function launch() {
const $draft_row = $(this).closest(".overlay-message-row"); const $draft_row = $(this).closest(".overlay-message-row");
remove_draft($draft_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 drafts = draft_model.get();
@ -663,6 +693,35 @@ export function launch() {
setup_event_handlers(); 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() { function open_overlay() {
sync_count(); sync_count();
overlays.open_overlay({ overlays.open_overlay({
@ -675,6 +734,19 @@ function open_overlay() {
}); });
} }
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();

View File

@ -143,13 +143,46 @@ export function initialize() {
// below specify the target directly, elements using those should // below specify the target directly, elements using those should
// not have the tippy-zulip-tooltip class. // not have the tippy-zulip-tooltip class.
$("body").on("blur", ".message_control_button", (e) => { delegate("body", {
// Remove tooltip when user is trying to tab through all the icons. target: ".draft-selection-tooltip",
// If user tabs slowly, tooltips are displayed otherwise they are delay: LONG_HOVER_DELAY,
// destroyed before they can be displayed. appendTo: () => document.body,
e.currentTarget?._tippy?.destroy(); onShow(instance) {
let content = $t({defaultMessage: "Select draft"});
const $elem = $(instance.reference);
if ($($elem).parent().find(".draft-selection-checkbox").hasClass("fa-check-square")) {
content = $t({defaultMessage: "Deselect draft"});
}
instance.setContent(content);
return true;
},
}); });
delegate("body", {
target: ".delete-selected-drafts-button-container",
appendTo: () => document.body,
onShow(instance) {
let content = $t({defaultMessage: "Delete all selected drafts"});
const $elem = $(instance.reference);
if ($($elem).find(".delete-selected-drafts-button").is(":disabled")) {
content = $t({defaultMessage: "No drafts selected"});
}
instance.setContent(content);
return true;
},
});
$("body").on(
"blur",
".message_control_button, .delete-selected-drafts-button-container",
(e) => {
// Remove tooltip when user is trying to tab through all the icons.
// If user tabs slowly, tooltips are displayed otherwise they are
// destroyed before they can be displayed.
e.currentTarget?._tippy?.destroy();
},
);
delegate("body", { delegate("body", {
target: [ target: [
"#streams_header .sidebar-title", "#streams_header .sidebar-title",

View File

@ -879,6 +879,10 @@
} }
} }
.drafts-container .header-body .delete-drafts-group > *:focus {
background-color: hsl(228deg 11% 17%);
}
& thead, & thead,
.drafts-container .drafts-header, .drafts-container .drafts-header,
.nav > li > a:focus, .nav > li > a:focus,

View File

@ -1,4 +1,60 @@
.drafts-container { .drafts-container {
.header-body {
display: flex;
align-items: center;
flex-direction: row;
justify-content: space-between;
gap: 5px;
.removed-drafts {
text-align: left;
margin-left: 25px;
@media (width < $lg_min) {
text-align: center;
margin-left: 0;
}
}
.delete-drafts-group {
display: flex;
justify-content: flex-end;
gap: 10px;
.delete-selected-drafts-button {
&:focus {
background-color: hsl(0deg 0% 93%);
}
}
.select-drafts-button {
display: flex;
align-items: center;
gap: 5px;
margin-right: 25px;
padding-left: 15px;
padding-right: 15px;
&:focus {
background-color: hsl(0deg 0% 93%);
}
}
.select-state-indicator {
width: 15px;
}
@media (width < $lg_min) {
margin-top: 5px;
width: 100%;
}
}
@media (width < $lg_min) {
display: block;
}
}
.drafts-list { .drafts-list {
& h2 { & h2 {
font-size: 1.1em; font-size: 1.1em;
@ -6,4 +62,12 @@
margin-bottom: 5px; margin-bottom: 5px;
} }
} }
.draft-selection-checkbox {
margin-top: 5px;
/* Required to make sure that the checkbox icon stays inside
the grid. Any value greater than 13px (original width of
the checkbox icon) will work. */
width: 15px;
}
} }

View File

@ -37,6 +37,9 @@
<div class="overlay_message_controls"> <div class="overlay_message_controls">
<i class="fa fa-pencil fa-lg restore-overlay-message tippy-zulip-tooltip" aria-hidden="true" data-tooltip-template-id="restore-draft-tooltip-template"></i> <i class="fa fa-pencil fa-lg restore-overlay-message tippy-zulip-tooltip" aria-hidden="true" data-tooltip-template-id="restore-draft-tooltip-template"></i>
<i class="fa fa-trash-o fa-lg delete-overlay-message tippy-zulip-tooltip" aria-hidden="true" data-tooltip-template-id="delete-draft-tooltip-template"></i> <i class="fa fa-trash-o fa-lg delete-overlay-message tippy-zulip-tooltip" aria-hidden="true" data-tooltip-template-id="delete-draft-tooltip-template"></i>
<div class="draft-selection-tooltip">
<i class="fa fa-square-o fa-lg draft-selection-checkbox" aria-hidden="true"></i>
</div>
</div> </div>
</div> </div>
<div class="message_content rendered_markdown restore-overlay-message tippy-zulip-delayed-tooltip" data-tooltip-template-id="restore-draft-tooltip-template">{{rendered_markdown content}}</div> <div class="message_content rendered_markdown restore-overlay-message tippy-zulip-delayed-tooltip" data-tooltip-template-id="restore-draft-tooltip-template">{{rendered_markdown content}}</div>

View File

@ -6,10 +6,23 @@
<div class="exit"> <div class="exit">
<span class="exit-sign">&times;</span> <span class="exit-sign">&times;</span>
</div> </div>
<div class="removed-drafts"> <div class="header-body">
{{t "Drafts are not synced to other devices and browsers." }} <div class="removed-drafts">
<br /> {{t "Drafts are not synced to other devices and browsers." }}
{{#tr}}Drafts older than <strong>{draft_lifetime}</strong> days are automatically removed.{{/tr}} <br />
{{#tr}}Drafts older than <strong>{draft_lifetime}</strong> days are automatically removed.{{/tr}}
</div>
<div class="delete-drafts-group">
<div class="delete-selected-drafts-button-container">
<button class="button small rounded delete-selected-drafts-button" type="button" disabled>
<i class="fa fa-trash-o fa-lg" aria-hidden="true"></i>
</button>
</div>
<button class="button small rounded select-drafts-button" role="checkbox" aria-checked="false">
<span>{{t "Select all drafts" }}</span>
<i class="fa fa-square-o fa-lg select-state-indicator" aria-hidden="true"></i>
</button>
</div>
</div> </div>
</div> </div>
<div class="drafts-list overlay-messages-list"> <div class="drafts-list overlay-messages-list">

View File

@ -610,6 +610,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);
$.create("#drafts_table .overlay-message-row", {children: []}); $.create("#drafts_table .overlay-message-row", {children: []});
$(".draft-selection-checkbox").filter = () => [];
drafts.launch(); drafts.launch();
$.clear_all_elements(); $.clear_all_elements();
@ -625,6 +626,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 = () => [];
drafts.launch(); drafts.launch();
}); });
@ -770,5 +772,6 @@ test("filter_drafts", ({override_rewire, mock_template}) => {
compose_state.private_message_recipient(aaron.email); compose_state.private_message_recipient(aaron.email);
$.create("#drafts_table .overlay-message-row", {children: []}); $.create("#drafts_table .overlay-message-row", {children: []});
$(".draft-selection-checkbox").filter = () => [];
drafts.launch(); drafts.launch();
}); });