aboutsummaryrefslogtreecommitdiff
path: root/client/js
diff options
context:
space:
mode:
authorrr-2017-02-11 21:58:18 +0100
committerrr-2017-02-11 22:03:38 +0100
commit1caf76b1b2e2aaef507b1936a2c5a66c2df140f5 (patch)
tree4fa544825fb1dc785c2d1eec4876aeb3cc86845f /client/js
parent0dc7a4058e8e00b1dd878343f89b8c7a10cfcdaf (diff)
client/posts: add bulk safety editing (#122)
Diffstat (limited to 'client/js')
-rw-r--r--client/js/controllers/post_list_controller.js12
-rw-r--r--client/js/views/posts_header_view.js120
-rw-r--r--client/js/views/posts_page_view.js88
3 files changed, 182 insertions, 38 deletions
diff --git a/client/js/controllers/post_list_controller.js b/client/js/controllers/post_list_controller.js
index a0973e1..12b354f 100644
--- a/client/js/controllers/post_list_controller.js
+++ b/client/js/controllers/post_list_controller.js
@@ -11,7 +11,7 @@ const PostsPageView = require('../views/posts_page_view.js');
const EmptyView = require('../views/empty_view.js');
const fields = [
- 'id', 'thumbnailUrl', 'type',
+ 'id', 'thumbnailUrl', 'type', 'safety',
'score', 'favoriteCount', 'commentCount', 'tags', 'version'];
class PostListController {
@@ -32,6 +32,7 @@ class PostListController {
hostNode: this._pageController.view.pageHeaderHolderNode,
parameters: ctx.parameters,
canBulkEditTags: api.hasPrivilege('posts:bulkEdit:tags'),
+ canBulkEditSafety: api.hasPrivilege('posts:bulkEdit:safety'),
bulkEdit: {
tags: this._bulkEditTags
},
@@ -73,6 +74,11 @@ class PostListController {
e.detail.post.save().catch(error => window.alert(error.message));
}
+ _evtChangeSafety(e) {
+ e.detail.post.safety = e.detail.safety;
+ e.detail.post.save().catch(error => window.alert(error.message));
+ }
+
_decorateSearchQuery(text) {
const browsingSettings = settings.get();
let disabledSafety = [];
@@ -106,6 +112,8 @@ class PostListController {
Object.assign(pageCtx, {
canViewPosts: api.hasPrivilege('posts:view'),
canBulkEditTags: api.hasPrivilege('posts:bulkEdit:tags'),
+ canBulkEditSafety:
+ api.hasPrivilege('posts:bulkEdit:safety'),
bulkEdit: {
tags: this._bulkEditTags,
},
@@ -113,6 +121,8 @@ class PostListController {
const view = new PostsPageView(pageCtx);
view.addEventListener('tag', e => this._evtTag(e));
view.addEventListener('untag', e => this._evtUntag(e));
+ view.addEventListener(
+ 'changeSafety', e => this._evtChangeSafety(e));
return view;
},
});
diff --git a/client/js/views/posts_header_view.js b/client/js/views/posts_header_view.js
index 0bc1dce..efa3d02 100644
--- a/client/js/views/posts_header_view.js
+++ b/client/js/views/posts_header_view.js
@@ -11,26 +11,19 @@ const TagAutoCompleteControl =
const template = views.getTemplate('posts-header');
-class BulkTagEditor extends events.EventTarget {
+class BulkEditor extends events.EventTarget {
constructor(hostNode) {
super();
this._hostNode = hostNode;
-
- this._autoCompleteControl = new TagAutoCompleteControl(
- this._inputNode, {addSpace: false});
this._openLinkNode.addEventListener(
'click', e => this._evtOpenLinkClick(e));
this._closeLinkNode.addEventListener(
'click', e => this._evtCloseLinkClick(e));
- this._hostNode.addEventListener('submit', e => this._evtFormSubmit(e));
- }
-
- get value() {
- return this._inputNode.value;
}
get opened() {
- return this._hostNode.classList.contains('opened');
+ return this._hostNode.classList.contains('opened') &&
+ !this._hostNode.classList.contains('hidden');
}
get _openLinkNode() {
@@ -41,6 +34,53 @@ class BulkTagEditor extends events.EventTarget {
return this._hostNode.querySelector('.close');
}
+ toggleOpen(state) {
+ this._hostNode.classList.toggle('opened', state);
+ }
+
+ toggleHide(state) {
+ this._hostNode.classList.toggle('hidden', state);
+ }
+
+ _evtOpenLinkClick(e) {
+ throw new Error('Not implemented');
+ }
+
+ _evtCloseLinkClick(e) {
+ throw new Error('Not implemented');
+ }
+}
+
+class BulkSafetyEditor extends BulkEditor {
+ constructor(hostNode) {
+ super(hostNode);
+ }
+
+ _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 BulkTagEditor extends BulkEditor {
+ constructor(hostNode) {
+ super(hostNode);
+ this._autoCompleteControl = new TagAutoCompleteControl(
+ this._inputNode, {addSpace: false});
+ this._hostNode.addEventListener('submit', e => this._evtFormSubmit(e));
+ }
+
+ get value() {
+ return this._inputNode.value;
+ }
+
get _inputNode() {
return this._hostNode.querySelector('input[name=tag]');
}
@@ -54,10 +94,6 @@ class BulkTagEditor extends events.EventTarget {
this._inputNode.blur();
}
- toggleOpen(state) {
- this._hostNode.classList.toggle('opened', state);
- }
-
_evtFormSubmit(e) {
e.preventDefault();
this.dispatchEvent(new CustomEvent('submit', {detail: {}}));
@@ -99,19 +135,39 @@ class PostsHeaderView extends events.EventTarget {
safetyButtonNode.addEventListener(
'click', e => this._evtSafetyButtonClick(e));
}
- this._formNode.addEventListener(
- 'submit', e => this._evtFormSubmit(e));
+ this._formNode.addEventListener('submit', e => this._evtFormSubmit(e));
+ this._bulkEditors = [];
if (this._bulkEditTagsNode) {
this._bulkTagEditor = new BulkTagEditor(this._bulkEditTagsNode);
- this._bulkTagEditor.toggleOpen(!!ctx.parameters.tag);
+ this._bulkEditors.push(this._bulkTagEditor);
+ }
+
+ if (this._bulkEditSafetyNode) {
+ this._bulkSafetyEditor = new BulkSafetyEditor(
+ this._bulkEditSafetyNode);
+ this._bulkEditors.push(this._bulkSafetyEditor);
+ }
+
+ for (let editor of this._bulkEditors) {
this._bulkTagEditor.addEventListener('submit', e => {
this._navigate();
});
- this._bulkTagEditor.addEventListener('close', e => {
+ editor.addEventListener('open', e => {
+ this._hideBulkEditorsExcept(editor);
+ this._navigate();
+ });
+ editor.addEventListener('close', e => {
+ this._closeAndShowAllBulkEditors();
this._navigate();
});
}
+
+ if (ctx.parameters.tag && this._bulkTagEditor) {
+ this._openBulkEditor(this._bulkTagEditor);
+ } else if (ctx.parameters.safety && this._bulkSafetyEditor) {
+ this._openBulkEditor(this._bulkSafetyEditor);
+ }
}
get _formNode() {
@@ -130,6 +186,31 @@ class PostsHeaderView extends events.EventTarget {
return this._hostNode.querySelector('.bulk-edit-tags');
}
+ get _bulkEditSafetyNode() {
+ return this._hostNode.querySelector('.bulk-edit-safety');
+ }
+
+ _openBulkEditor(editor) {
+ editor.toggleOpen(true);
+ this._hideBulkEditorsExcept(editor);
+ }
+
+ _hideBulkEditorsExcept(editor) {
+ for (let otherEditor of this._bulkEditors) {
+ if (otherEditor !== editor) {
+ otherEditor.toggleOpen(false);
+ otherEditor.toggleHide(true);
+ }
+ }
+ }
+
+ _closeAndShowAllBulkEditors() {
+ for (let otherEditor of this._bulkEditors) {
+ otherEditor.toggleOpen(false);
+ otherEditor.toggleHide(false);
+ }
+ }
+
_evtSafetyButtonClick(e, url) {
e.preventDefault();
e.target.classList.toggle('disabled');
@@ -164,6 +245,9 @@ class PostsHeaderView extends events.EventTarget {
} else {
parameters.tag = null;
}
+ parameters.safety = (
+ this._bulkSafetyEditor &&
+ this._bulkSafetyEditor.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 c2dbe90..2610325 100644
--- a/client/js/views/posts_page_view.js
+++ b/client/js/views/posts_page_view.js
@@ -18,26 +18,48 @@ class PostsPageView extends events.EventTarget {
post.addEventListener('change', e => this._evtPostChange(e));
}
- this._postIdToLinkNode = {};
- for (let linkNode of this._tagFlipperNodes) {
- const postId = linkNode.getAttribute('data-post-id');
+ this._postIdToListItemNode = {};
+ for (let listItemNode of this._listItemNodes) {
+ const postId = listItemNode.getAttribute('data-post-id');
const post = this._postIdToPost[postId];
- this._postIdToLinkNode[postId] = linkNode;
- linkNode.addEventListener(
- 'click', e => this._evtBulkEditTagsClick(e, post));
+ this._postIdToListItemNode[postId] = listItemNode;
+
+ const tagFlipperNode = this._getTagFlipperNode(listItemNode);
+ if (tagFlipperNode) {
+ tagFlipperNode.addEventListener(
+ 'click', e => this._evtBulkEditTagsClick(e, post));
+ }
+
+ const safetyFlipperNode = this._getSafetyFlipperNode(listItemNode);
+ if (safetyFlipperNode) {
+ for (let linkNode of safetyFlipperNode.querySelectorAll('a')) {
+ linkNode.addEventListener(
+ 'click', e => this._evtBulkEditSafetyClick(e, post));
+ }
+ }
}
- this._syncTagFlippersHighlights();
+ this._syncBulkEditorsHighlights();
+ }
+
+ get _listItemNodes() {
+ return this._hostNode.querySelectorAll('li');
}
- get _tagFlipperNodes() {
- return this._hostNode.querySelectorAll('.tag-flipper');
+ _getTagFlipperNode(listItemNode) {
+ return listItemNode.querySelector('.tag-flipper');
+ }
+
+ _getSafetyFlipperNode(listItemNode) {
+ return listItemNode.querySelector('.safety-flipper');
}
_evtPostChange(e) {
- const linkNode = this._postIdToLinkNode[e.detail.post.id];
- linkNode.removeAttribute('data-disabled');
- this._syncTagFlippersHighlights();
+ const listItemNode = this._postIdToListItemNode[e.detail.post.id];
+ for (let node of listItemNode.querySelectorAll('[data-disabled]')) {
+ node.removeAttribute('data-disabled');
+ }
+ this._syncBulkEditorsHighlights();
}
_evtBulkEditTagsClick(e, post) {
@@ -53,15 +75,43 @@ class PostsPageView extends events.EventTarget {
{detail: {post: post}}));
}
- _syncTagFlippersHighlights() {
- for (let linkNode of this._tagFlipperNodes) {
- const postId = linkNode.getAttribute('data-post-id');
+ _evtBulkEditSafetyClick(e, post) {
+ e.preventDefault();
+ const linkNode = e.target;
+ if (linkNode.getAttribute('data-disabled')) {
+ return;
+ }
+ const newSafety = linkNode.getAttribute('data-safety');
+ if (post.safety === newSafety) {
+ return;
+ }
+ linkNode.setAttribute('data-disabled', true);
+ this.dispatchEvent(
+ new CustomEvent(
+ 'changeSafety', {detail: {post: post, safety: newSafety}}));
+ }
+
+ _syncBulkEditorsHighlights() {
+ for (let listItemNode of this._listItemNodes) {
+ const postId = listItemNode.getAttribute('data-post-id');
const post = this._postIdToPost[postId];
- let tagged = true;
- for (let tag of this._ctx.bulkEdit.tags) {
- tagged = tagged & post.isTaggedWith(tag);
+
+ const tagFlipperNode = this._getTagFlipperNode(listItemNode);
+ if (tagFlipperNode) {
+ let tagged = true;
+ for (let tag of this._ctx.bulkEdit.tags) {
+ tagged = tagged & post.isTaggedWith(tag);
+ }
+ tagFlipperNode.classList.toggle('tagged', tagged);
+ }
+
+ const safetyFlipperNode = this._getSafetyFlipperNode(listItemNode);
+ if (safetyFlipperNode) {
+ for (let linkNode of safetyFlipperNode.querySelectorAll('a')) {
+ const safety = linkNode.getAttribute('data-safety');
+ linkNode.classList.toggle('active', post.safety == safety);
+ }
}
- linkNode.classList.toggle('tagged', tagged);
}
}
}

© 2015 - 2026 Jakob L. Kreuze