client/posts: add bulk safety editing (#122)
This commit is contained in:
		
							parent
							
								
									0dc7a4058e
								
							
						
					
					
						commit
						1caf76b1b2
					
				@ -84,6 +84,37 @@
 | 
			
		||||
                    &[data-disabled]
 | 
			
		||||
                        background: rgba(200, 200, 200, 0.7)
 | 
			
		||||
 | 
			
		||||
                .safety-flipper a
 | 
			
		||||
                    display: inline-block
 | 
			
		||||
                    margin: 0.1em
 | 
			
		||||
                    box-sizing: border-box
 | 
			
		||||
                    border: 0
 | 
			
		||||
                    display: inline-block
 | 
			
		||||
                    width: 1.2em
 | 
			
		||||
                    height: 1.2em
 | 
			
		||||
                    text-align: center
 | 
			
		||||
                    line-height: 1em
 | 
			
		||||
                    font-size: 20pt
 | 
			
		||||
                    border: 3px solid
 | 
			
		||||
                    &.safety-safe
 | 
			
		||||
                        background-color: darken($safety-safe, 5%)
 | 
			
		||||
                        border-color: @background-color
 | 
			
		||||
                        &:not(.active)
 | 
			
		||||
                            background-color: alpha(@background-color, 0.3)
 | 
			
		||||
                    &.safety-sketchy
 | 
			
		||||
                        background-color: $safety-sketchy
 | 
			
		||||
                        border-color: @background-color
 | 
			
		||||
                        &:not(.active)
 | 
			
		||||
                            background-color: alpha(@background-color, 0.3)
 | 
			
		||||
                    &.safety-unsafe
 | 
			
		||||
                        background-color: $safety-unsafe
 | 
			
		||||
                        border-color: @background-color
 | 
			
		||||
                        &:not(.active)
 | 
			
		||||
                            background-color: alpha(@background-color, 0.3)
 | 
			
		||||
                    &[data-disabled]
 | 
			
		||||
                        background: rgba(200, 200, 200, 0.7)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            .thumbnail
 | 
			
		||||
                background-position: 50% 30%
 | 
			
		||||
                width: 100%
 | 
			
		||||
@ -122,20 +153,25 @@
 | 
			
		||||
    .append
 | 
			
		||||
        font-size: 0.95em
 | 
			
		||||
        color: $inactive-link-color
 | 
			
		||||
    .bulk-edit-tags
 | 
			
		||||
    .bulk-edit
 | 
			
		||||
        &:not(.opened)
 | 
			
		||||
            [type=text],
 | 
			
		||||
            .start,
 | 
			
		||||
            .close
 | 
			
		||||
                display: none
 | 
			
		||||
            .hint
 | 
			
		||||
                display: none
 | 
			
		||||
        &.opened
 | 
			
		||||
            .open
 | 
			
		||||
                display: none
 | 
			
		||||
        &.hidden
 | 
			
		||||
            display: none
 | 
			
		||||
    .bulk-edit-tags
 | 
			
		||||
        &:not(.opened)
 | 
			
		||||
            [type=text],
 | 
			
		||||
            .start
 | 
			
		||||
                display: none
 | 
			
		||||
            .hint
 | 
			
		||||
                display: none
 | 
			
		||||
        input[name=tag]
 | 
			
		||||
            width: 12em
 | 
			
		||||
        .hint, .open
 | 
			
		||||
        .hint
 | 
			
		||||
            margin-right: 1em
 | 
			
		||||
 | 
			
		||||
    .safety
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,7 @@
 | 
			
		||||
        %><a class='mousetrap button append' href='<%- ctx.formatClientLink('help', 'search', 'posts') %>'>Syntax help</a><%
 | 
			
		||||
    %></form><%
 | 
			
		||||
    %><% if (ctx.canBulkEditTags) { %><%
 | 
			
		||||
        %><form class='horizontal bulk-edit-tags'><%
 | 
			
		||||
        %><form class='horizontal bulk-edit bulk-edit-tags'><%
 | 
			
		||||
            %><span class='append hint'>Tagging with:</span><%
 | 
			
		||||
            %><a href class='mousetrap button append open'>Mass tag</a><%
 | 
			
		||||
            %><wbr/><%
 | 
			
		||||
@ -20,4 +20,10 @@
 | 
			
		||||
            %><a href class='mousetrap button append close'>Stop tagging</a><%
 | 
			
		||||
        %></form><%
 | 
			
		||||
    %><% } %><%
 | 
			
		||||
    %><% if (ctx.canBulkEditSafety) { %><%
 | 
			
		||||
        %><form class='horizontal bulk-edit bulk-edit-safety'><%
 | 
			
		||||
            %><a href class='mousetrap button append open'>Mass edit safety</a><%
 | 
			
		||||
            %><a href class='mousetrap button append close'>Stop editing safety</a><%
 | 
			
		||||
        %></form><%
 | 
			
		||||
    %><% } %><%
 | 
			
		||||
%></div>
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@
 | 
			
		||||
    <% if (ctx.response.results.length) { %>
 | 
			
		||||
        <ul>
 | 
			
		||||
            <% for (let post of ctx.response.results) { %>
 | 
			
		||||
                <li>
 | 
			
		||||
                <li data-post-id='<%= post.id %>'>
 | 
			
		||||
                    <a class='thumbnail-wrapper <%= post.tags.length > 0 ? "tags" : "no-tags" %>'
 | 
			
		||||
                            title='@<%- post.id %> (<%- post.type %>)

Tags: <%- post.tags.map(tag => '#' + tag).join(' ') || 'none' %>'
 | 
			
		||||
                            href='<%= ctx.canViewPosts ? ctx.getPostUrl(post.id, ctx.parameters) : '' %>'>
 | 
			
		||||
@ -35,9 +35,17 @@
 | 
			
		||||
                    </a>
 | 
			
		||||
                    <span class='edit-overlay'>
 | 
			
		||||
                        <% if (ctx.canBulkEditTags && ctx.parameters && ctx.parameters.tag) { %>
 | 
			
		||||
                            <a href data-post-id='<%= post.id %>' class='tag-flipper'>
 | 
			
		||||
                            <a href class='tag-flipper'>
 | 
			
		||||
                            </a>
 | 
			
		||||
                        <% } %>
 | 
			
		||||
                        <% if (ctx.canBulkEditSafety && ctx.parameters && ctx.parameters.safety) { %>
 | 
			
		||||
                            <span class='safety-flipper'>
 | 
			
		||||
                                <% for (let safety of ['safe', 'sketchy', 'unsafe']) { %>
 | 
			
		||||
                                    <a href data-safety='<%- safety %>' class='safety-<%- safety %><%- post.safety === safety ? ' active' : '' %>'>
 | 
			
		||||
                                    </a>
 | 
			
		||||
                                <% } %>
 | 
			
		||||
                            </span>
 | 
			
		||||
                        <% } %>
 | 
			
		||||
                    </span>
 | 
			
		||||
                </li>
 | 
			
		||||
            <% } %>
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
@ -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,18 +135,38 @@ 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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -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}}));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -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 _tagFlipperNodes() {
 | 
			
		||||
        return this._hostNode.querySelectorAll('.tag-flipper');
 | 
			
		||||
    get _listItemNodes() {
 | 
			
		||||
        return this._hostNode.querySelectorAll('li');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -90,6 +90,7 @@ privileges:
 | 
			
		||||
    'posts:merge':                  moderator
 | 
			
		||||
    'posts:favorite':               regular
 | 
			
		||||
    'posts:bulk-edit:tags':         power
 | 
			
		||||
    'posts:bulk-edit:safety':       power
 | 
			
		||||
 | 
			
		||||
    'tags:create':                  regular
 | 
			
		||||
    'tags:edit:names':              power
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user