aboutsummaryrefslogtreecommitdiff
path: root/client/js
diff options
context:
space:
mode:
authorNeo2023-01-19 18:44:31 +0100
committerGitHub2023-01-19 18:44:31 +0100
commite3062b1c77b52ff9fad23a279844b946d6491417 (patch)
treea037b90b2a6a2f4fb5da20a339efdd8642c7871a /client/js
parent8088ff3bbe101907c2cea3341bc4437285b3d12d (diff)
client: add bulk delete feature (#459)
This introduces a new privilege 'posts:bulk-edit:delete' which by default is given to power users.
Diffstat (limited to 'client/js')
-rw-r--r--client/js/controllers/post_list_controller.js46
-rw-r--r--client/js/views/posts_header_view.js45
-rw-r--r--client/js/views/posts_page_view.js35
3 files changed, 126 insertions, 0 deletions
diff --git a/client/js/controllers/post_list_controller.js b/client/js/controllers/post_list_controller.js
index 526d8f5..ec3e13c 100644
--- a/client/js/controllers/post_list_controller.js
+++ b/client/js/controllers/post_list_controller.js
@@ -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;
},
});
diff --git a/client/js/views/posts_header_view.js b/client/js/views/posts_header_view.js
index f64060d..38a4aa9 100644
--- a/client/js/views/posts_header_view.js
+++ b/client/js/views/posts_header_view.js
@@ -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 } })
);
diff --git a/client/js/views/posts_page_view.js b/client/js/views/posts_page_view.js
index ba07a63..c4b1988 100644
--- a/client/js/views/posts_page_view.js
+++ b/client/js/views/posts_page_view.js
@@ -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
+ )
+ );
+ }
}
}
}

© 2015 - 2026 Jakob L. Kreuze