client: add bulk delete feature (#459)
This introduces a new privilege 'posts:bulk-edit:delete' which by default is given to power users.
This commit is contained in:
parent
8088ff3bbe
commit
e3062b1c77
7 changed files with 174 additions and 1 deletions
|
@ -114,6 +114,29 @@
|
|||
&[data-disabled]
|
||||
background: rgba(200, 200, 200, 0.7)
|
||||
|
||||
.delete-flipper
|
||||
display: inline-block
|
||||
padding: 0.5em
|
||||
box-sizing: border-box
|
||||
border: 0
|
||||
&:after
|
||||
display: inline-block
|
||||
width: 1em
|
||||
height: 1em
|
||||
text-align: center
|
||||
line-height: 1em
|
||||
font-size: 2.2em
|
||||
&.delete
|
||||
background: rgba(255, 0, 0, 0.7)
|
||||
&:after
|
||||
color: white
|
||||
font-family: FontAwesome;
|
||||
content: "\f1f8"; // fa-trash
|
||||
&:not(.delete)
|
||||
background: rgba(200, 200, 200, 0.7)
|
||||
&:after
|
||||
color: white
|
||||
content: '-'
|
||||
|
||||
.thumbnail
|
||||
width: 100%
|
||||
|
@ -215,7 +238,19 @@
|
|||
.append
|
||||
@media (max-width: 1000px)
|
||||
margin-left: 0
|
||||
|
||||
.bulk-edit-delete
|
||||
&.opened
|
||||
.start
|
||||
@media (max-width: 1000px)
|
||||
margin-left: 0
|
||||
&:not(.opened)
|
||||
.start
|
||||
display: none
|
||||
.append.open
|
||||
@media (max-width: 1000px)
|
||||
margin-left: 0
|
||||
.start
|
||||
margin-left: 1em
|
||||
.safety
|
||||
margin-right: 0.25em
|
||||
&.safety-safe
|
||||
|
|
|
@ -28,4 +28,11 @@
|
|||
%><a href class='mousetrap button append close'>Stop editing safety</a><%
|
||||
%></form><%
|
||||
%><% } %><%
|
||||
%><% if (ctx.canBulkDelete) { %><%
|
||||
%><form class='horizontal bulk-edit bulk-edit-delete'><%
|
||||
%><a href class='mousetrap button append open'>Mass delete</a><%
|
||||
%><input class='mousetrap start' type='submit' value='Delete selected posts'/><%
|
||||
%><a href class='mousetrap button append close'>Stop deleting</a><%
|
||||
%></form><%
|
||||
%><% } %><%
|
||||
%></div>
|
||||
|
|
|
@ -50,6 +50,10 @@
|
|||
<% } %>
|
||||
</span>
|
||||
<% } %>
|
||||
<% if (ctx.canBulkDelete && ctx.parameters && ctx.parameters.delete) { %>
|
||||
<a href class='delete-flipper'>
|
||||
</a>
|
||||
<% } %>
|
||||
</span>
|
||||
</li>
|
||||
<% } %>
|
||||
|
|
|
@ -44,6 +44,7 @@ class PostListController {
|
|||
enableSafety: api.safetyEnabled(),
|
||||
canBulkEditTags: api.hasPrivilege("posts:bulk-edit:tags"),
|
||||
canBulkEditSafety: api.hasPrivilege("posts:bulk-edit:safety"),
|
||||
canBulkDelete: api.hasPrivilege("posts:bulk-edit:delete"),
|
||||
bulkEdit: {
|
||||
tags: this._bulkEditTags,
|
||||
},
|
||||
|
@ -52,6 +53,14 @@ class PostListController {
|
|||
this._evtNavigate(e)
|
||||
);
|
||||
|
||||
this._headerView._bulkDeleteEditor.addEventListener(
|
||||
"deleteSelectedPosts",
|
||||
(e) => {
|
||||
this._evtDeleteSelectedPosts(e);
|
||||
}
|
||||
);
|
||||
|
||||
this._postsMarkedForDeletion = [];
|
||||
this._syncPageController();
|
||||
}
|
||||
|
||||
|
@ -91,6 +100,38 @@ class PostListController {
|
|||
e.detail.post.save().catch((error) => window.alert(error.message));
|
||||
}
|
||||
|
||||
_evtMarkForDeletion(e) {
|
||||
const postId = e.detail;
|
||||
|
||||
// Add or remove post from delete list
|
||||
if (e.detail.delete) {
|
||||
this._postsMarkedForDeletion.push(e.detail.post);
|
||||
} else {
|
||||
this._postsMarkedForDeletion = this._postsMarkedForDeletion.filter(
|
||||
(x) => x.id != e.detail.post.id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_evtDeleteSelectedPosts(e) {
|
||||
if (this._postsMarkedForDeletion.length == 0) return;
|
||||
|
||||
if (
|
||||
confirm(
|
||||
`Are you sure you want to delete ${this._postsMarkedForDeletion.length} posts?`
|
||||
)
|
||||
) {
|
||||
Promise.all(
|
||||
this._postsMarkedForDeletion.map((post) => post.delete())
|
||||
)
|
||||
.catch((error) => window.alert(error.message))
|
||||
.then(() => {
|
||||
this._postsMarkedForDeletion = [];
|
||||
this._headerView._navigate();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_syncPageController() {
|
||||
this._pageController.run({
|
||||
parameters: this._ctx.parameters,
|
||||
|
@ -117,8 +158,10 @@ class PostListController {
|
|||
canBulkEditSafety: api.hasPrivilege(
|
||||
"posts:bulk-edit:safety"
|
||||
),
|
||||
canBulkDelete: api.hasPrivilege("posts:bulk-edit:delete"),
|
||||
bulkEdit: {
|
||||
tags: this._bulkEditTags,
|
||||
markedForDeletion: this._postsMarkedForDeletion,
|
||||
},
|
||||
postFlow: settings.get().postFlow,
|
||||
});
|
||||
|
@ -128,6 +171,9 @@ class PostListController {
|
|||
view.addEventListener("changeSafety", (e) =>
|
||||
this._evtChangeSafety(e)
|
||||
);
|
||||
view.addEventListener("markForDeletion", (e) =>
|
||||
this._evtMarkForDeletion(e)
|
||||
);
|
||||
return view;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -141,6 +141,34 @@ class BulkTagEditor extends BulkEditor {
|
|||
}
|
||||
}
|
||||
|
||||
class BulkDeleteEditor extends BulkEditor {
|
||||
constructor(hostNode) {
|
||||
super(hostNode);
|
||||
this._hostNode.addEventListener("submit", (e) =>
|
||||
this._evtFormSubmit(e)
|
||||
);
|
||||
}
|
||||
|
||||
_evtFormSubmit(e) {
|
||||
e.preventDefault();
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("deleteSelectedPosts", { detail: {} })
|
||||
);
|
||||
}
|
||||
|
||||
_evtOpenLinkClick(e) {
|
||||
e.preventDefault();
|
||||
this.toggleOpen(true);
|
||||
this.dispatchEvent(new CustomEvent("open", { detail: {} }));
|
||||
}
|
||||
|
||||
_evtCloseLinkClick(e) {
|
||||
e.preventDefault();
|
||||
this.toggleOpen(false);
|
||||
this.dispatchEvent(new CustomEvent("close", { detail: {} }));
|
||||
}
|
||||
}
|
||||
|
||||
class PostsHeaderView extends events.EventTarget {
|
||||
constructor(ctx) {
|
||||
super();
|
||||
|
@ -186,6 +214,13 @@ class PostsHeaderView extends events.EventTarget {
|
|||
this._bulkEditors.push(this._bulkSafetyEditor);
|
||||
}
|
||||
|
||||
if (this._bulkEditDeleteNode) {
|
||||
this._bulkDeleteEditor = new BulkDeleteEditor(
|
||||
this._bulkEditDeleteNode
|
||||
);
|
||||
this._bulkEditors.push(this._bulkDeleteEditor);
|
||||
}
|
||||
|
||||
for (let editor of this._bulkEditors) {
|
||||
editor.addEventListener("submit", (e) => {
|
||||
this._navigate();
|
||||
|
@ -204,6 +239,8 @@ class PostsHeaderView extends events.EventTarget {
|
|||
this._openBulkEditor(this._bulkTagEditor);
|
||||
} else if (ctx.parameters.safety && this._bulkSafetyEditor) {
|
||||
this._openBulkEditor(this._bulkSafetyEditor);
|
||||
} else if (ctx.parameters.delete && this._bulkDeleteEditor) {
|
||||
this._openBulkEditor(this._bulkDeleteEditor);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -227,6 +264,10 @@ class PostsHeaderView extends events.EventTarget {
|
|||
return this._hostNode.querySelector(".bulk-edit-safety");
|
||||
}
|
||||
|
||||
get _bulkEditDeleteNode() {
|
||||
return this._hostNode.querySelector(".bulk-edit-delete");
|
||||
}
|
||||
|
||||
_openBulkEditor(editor) {
|
||||
editor.toggleOpen(true);
|
||||
this._hideBulkEditorsExcept(editor);
|
||||
|
@ -293,6 +334,10 @@ class PostsHeaderView extends events.EventTarget {
|
|||
this._bulkSafetyEditor && this._bulkSafetyEditor.opened
|
||||
? "1"
|
||||
: null;
|
||||
parameters.delete =
|
||||
this._bulkDeleteEditor && this._bulkDeleteEditor.opened
|
||||
? "1"
|
||||
: null;
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("navigate", { detail: { parameters: parameters } })
|
||||
);
|
||||
|
|
|
@ -39,6 +39,13 @@ class PostsPageView extends events.EventTarget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
const deleteFlipperNode = this._getDeleteFlipperNode(listItemNode);
|
||||
if (deleteFlipperNode) {
|
||||
deleteFlipperNode.addEventListener("click", (e) =>
|
||||
this._evtBulkToggleDeleteClick(e, post)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this._syncBulkEditorsHighlights();
|
||||
|
@ -56,6 +63,10 @@ class PostsPageView extends events.EventTarget {
|
|||
return listItemNode.querySelector(".safety-flipper");
|
||||
}
|
||||
|
||||
_getDeleteFlipperNode(listItemNode) {
|
||||
return listItemNode.querySelector(".delete-flipper");
|
||||
}
|
||||
|
||||
_evtPostChange(e) {
|
||||
const listItemNode = this._postIdToListItemNode[e.detail.post.id];
|
||||
for (let node of listItemNode.querySelectorAll("[data-disabled]")) {
|
||||
|
@ -99,6 +110,20 @@ class PostsPageView extends events.EventTarget {
|
|||
);
|
||||
}
|
||||
|
||||
_evtBulkToggleDeleteClick(e, post) {
|
||||
e.preventDefault();
|
||||
const linkNode = e.target;
|
||||
linkNode.classList.toggle("delete");
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("markForDeletion", {
|
||||
detail: {
|
||||
post,
|
||||
delete: linkNode.classList.contains("delete"),
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
_syncBulkEditorsHighlights() {
|
||||
for (let listItemNode of this._listItemNodes) {
|
||||
const postId = listItemNode.getAttribute("data-post-id");
|
||||
|
@ -123,6 +148,16 @@ class PostsPageView extends events.EventTarget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
const deleteFlipperNode = this._getDeleteFlipperNode(listItemNode);
|
||||
if (deleteFlipperNode) {
|
||||
deleteFlipperNode.classList.toggle(
|
||||
"delete",
|
||||
this._ctx.bulkEdit.markedForDeletion.some(
|
||||
(x) => x.id == postId
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,6 +115,7 @@ privileges:
|
|||
'posts:favorite': regular
|
||||
'posts:bulk-edit:tags': power
|
||||
'posts:bulk-edit:safety': power
|
||||
'posts:bulk-edit:delete': power
|
||||
|
||||
'tags:create': regular
|
||||
'tags:edit:names': power
|
||||
|
|
Loading…
Reference in a new issue