aboutsummaryrefslogtreecommitdiff
path: root/client/js/controls
diff options
context:
space:
mode:
authorRuin0x112020-05-04 02:20:23 -0700
committerRuin0x112020-05-04 02:20:23 -0700
commit8795279a73e976a3aadbb4b9067209a46f138733 (patch)
tree8d0742d03bd5c2847c5fd813fb3105be961c3b88 /client/js/controls
parente6bf102bc03504d9abc584f15da70a5fed7fa950 (diff)
Add pool input box in post details
Diffstat (limited to 'client/js/controls')
-rw-r--r--client/js/controls/pool_input_control.js230
-rw-r--r--client/js/controls/post_edit_sidebar_control.js31
2 files changed, 260 insertions, 1 deletions
diff --git a/client/js/controls/pool_input_control.js b/client/js/controls/pool_input_control.js
new file mode 100644
index 0000000..09fcf63
--- /dev/null
+++ b/client/js/controls/pool_input_control.js
@@ -0,0 +1,230 @@
+'use strict';
+
+const api = require('../api.js');
+const pools = require('../pools.js');
+const misc = require('../util/misc.js');
+const uri = require('../util/uri.js');
+const Pool = require('../models/pool.js');
+const settings = require('../models/settings.js');
+const events = require('../events.js');
+const views = require('../util/views.js');
+const PoolAutoCompleteControl = require('./pool_auto_complete_control.js');
+
+const KEY_SPACE = 32;
+const KEY_RETURN = 13;
+
+const SOURCE_INIT = 'init';
+const SOURCE_IMPLICATION = 'implication';
+const SOURCE_USER_INPUT = 'user-input';
+const SOURCE_CLIPBOARD = 'clipboard';
+
+const template = views.getTemplate('pool-input');
+
+function _fadeOutListItemNodeStatus(listItemNode) {
+ if (listItemNode.classList.length) {
+ if (listItemNode.fadeTimeout) {
+ window.clearTimeout(listItemNode.fadeTimeout);
+ }
+ listItemNode.fadeTimeout = window.setTimeout(() => {
+ while (listItemNode.classList.length) {
+ listItemNode.classList.remove(
+ listItemNode.classList.item(0));
+ }
+ listItemNode.fadeTimeout = null;
+ }, 2500);
+ }
+}
+
+class PoolInputControl extends events.EventTarget {
+ constructor(hostNode, poolList) {
+ super();
+ this.pools = poolList;
+ this._hostNode = hostNode;
+ this._poolToListItemNode = new Map();
+
+ // dom
+ const editAreaNode = template();
+ this._editAreaNode = editAreaNode;
+ this._poolInputNode = editAreaNode.querySelector('input');
+ this._poolListNode = editAreaNode.querySelector('ul.compact-pools');
+
+ this._autoCompleteControl = new PoolAutoCompleteControl(
+ this._poolInputNode, {
+ getTextToFind: () => {
+ return this._poolInputNode.value;
+ },
+ confirm: pool => {
+ this._poolInputNode.value = '';
+ this.addPool(pool, SOURCE_USER_INPUT);
+ },
+ delete: pool => {
+ this._poolInputNode.value = '';
+ this.deletePool(pool);
+ },
+ verticalShift: -2
+ });
+
+ // dom events
+ this._poolInputNode.addEventListener(
+ 'keydown', e => this._evtInputKeyDown(e));
+
+ // show
+ this._hostNode.style.display = 'none';
+ this._hostNode.parentNode.insertBefore(
+ this._editAreaNode, hostNode.nextSibling);
+
+ // add existing pools
+ for (let pool of [...this.pools]) {
+ const listItemNode = this._createListItemNode(pool);
+ this._poolListNode.appendChild(listItemNode);
+ }
+ }
+
+ addPoolByText(text, source) {
+ for (let poolName of text.split(/\s+/).filter(word => word).reverse()) {
+ this.addPoolByName(poolName, source);
+ }
+ }
+
+ addPoolByName(name, source) {
+ name = name.trim();
+ if (!name) {
+ return;
+ }
+ return Pool.get(name).then(pool => {
+ return this.addPool(pool, source);
+ }, () => {
+ const pool = new Pool();
+ pool.names = [name];
+ pool.category = null;
+ return this.addPool(pool, source);
+ });
+ }
+
+ addPool(pool, source) {
+ if (source != SOURCE_INIT && this.pools.hasPoolId(pool.id)) {
+ return Promise.resolve();
+ }
+
+ this.pools.add(pool, false)
+
+ const listItemNode = this._createListItemNode(pool);
+ if (!pool.category) {
+ listItemNode.classList.add('new');
+ }
+ this._poolListNode.prependChild(listItemNode);
+ _fadeOutListItemNodeStatus(listItemNode);
+
+ this.dispatchEvent(new CustomEvent('add', {
+ detail: {pool: pool, source: source},
+ }));
+ this.dispatchEvent(new CustomEvent('change'));
+
+ return Promise.resolve();
+ }
+
+ deletePool(pool) {
+ if (!this.pools.hasPoolId(pool.id)) {
+ return;
+ }
+ this.pools.removeById(pool.id);
+ this._hideAutoComplete();
+
+ this._deleteListItemNode(pool);
+
+ this.dispatchEvent(new CustomEvent('remove', {
+ detail: {pool: pool},
+ }));
+ this.dispatchEvent(new CustomEvent('change'));
+ }
+
+ _evtAddPoolButtonClick(e) {
+ // TODO
+ // e.preventDefault();
+ // this.addPoolByName(this._poolInputNode.value, SOURCE_USER_INPUT);
+ // this._poolInputNode.value = '';
+ }
+
+ _evtInputKeyDown(e) {
+ // TODO
+ if (e.which == KEY_RETURN || e.which == KEY_SPACE) {
+ e.preventDefault();
+ // this._hideAutoComplete();
+ // this.addPoolByText(this._poolInputNode.value, SOURCE_USER_INPUT);
+ // this._poolInputNode.value = '';
+ }
+ }
+
+ _createListItemNode(pool) {
+ const className = pool.category ?
+ misc.makeCssName(pool.category, 'pool') :
+ null;
+
+ const poolLinkNode = document.createElement('a');
+ if (className) {
+ poolLinkNode.classList.add(className);
+ }
+ poolLinkNode.setAttribute(
+ 'href', uri.formatClientLink('pool', pool.names[0]));
+
+ const poolIconNode = document.createElement('i');
+ poolIconNode.classList.add('fa');
+ poolIconNode.classList.add('fa-pool');
+ poolLinkNode.appendChild(poolIconNode);
+
+ const searchLinkNode = document.createElement('a');
+ if (className) {
+ searchLinkNode.classList.add(className);
+ }
+ searchLinkNode.setAttribute(
+ 'href', uri.formatClientLink(
+ 'posts', {query: uri.escapeColons(pool.names[0])}));
+ searchLinkNode.textContent = pool.names[0] + ' ';
+ searchLinkNode.addEventListener('click', e => {
+ e.preventDefault();
+ });
+
+ const usagesNode = document.createElement('span');
+ usagesNode.classList.add('pool-usages');
+ usagesNode.setAttribute('data-pseudo-content', pool.postCount);
+
+ const removalLinkNode = document.createElement('a');
+ removalLinkNode.classList.add('remove-pool');
+ removalLinkNode.setAttribute('href', '');
+ removalLinkNode.setAttribute('data-pseudo-content', '×');
+ removalLinkNode.addEventListener('click', e => {
+ e.preventDefault();
+ this.deletePool(pool);
+ });
+
+ const listItemNode = document.createElement('li');
+ listItemNode.appendChild(removalLinkNode);
+ listItemNode.appendChild(poolLinkNode);
+ listItemNode.appendChild(searchLinkNode);
+ listItemNode.appendChild(usagesNode);
+ for (let name of pool.names) {
+ this._poolToListItemNode.set(name, listItemNode);
+ }
+ return listItemNode;
+ }
+
+ _deleteListItemNode(pool) {
+ const listItemNode = this._getListItemNode(pool);
+ if (listItemNode) {
+ listItemNode.parentNode.removeChild(listItemNode);
+ }
+ for (let name of pool.names) {
+ this._poolToListItemNode.delete(name);
+ }
+ }
+
+ _getListItemNode(pool) {
+ return this._poolToListItemNode.get(pool.names[0]);
+ }
+
+ _hideAutoComplete() {
+ this._autoCompleteControl.hide();
+ }
+}
+
+module.exports = PoolInputControl;
diff --git a/client/js/controls/post_edit_sidebar_control.js b/client/js/controls/post_edit_sidebar_control.js
index 030bb7a..c89f9de 100644
--- a/client/js/controls/post_edit_sidebar_control.js
+++ b/client/js/controls/post_edit_sidebar_control.js
@@ -7,6 +7,7 @@ const views = require('../util/views.js');
const Note = require('../models/note.js');
const Point = require('../models/point.js');
const TagInputControl = require('./tag_input_control.js');
+const PoolInputControl = require('./pool_input_control.js');
const ExpanderControl = require('../controls/expander_control.js');
const FileDropperControl = require('../controls/file_dropper_control.js');
@@ -37,7 +38,8 @@ class PostEditSidebarControl extends events.EventTarget {
canEditPostFlags: api.hasPrivilege('posts:edit:flags'),
canEditPostContent: api.hasPrivilege('posts:edit:content'),
canEditPostThumbnail: api.hasPrivilege('posts:edit:thumbnail'),
- canEditPostSource : api.hasPrivilege('posts:edit:source'),
+ canEditPostSource: api.hasPrivilege('posts:edit:source'),
+ canEditPoolPosts: api.hasPrivilege('pools:edit:posts'),
canCreateAnonymousPosts: api.hasPrivilege('posts:create:anonymous'),
canDeletePosts: api.hasPrivilege('posts:delete'),
canFeaturePosts: api.hasPrivilege('posts:feature'),
@@ -56,6 +58,10 @@ class PostEditSidebarControl extends events.EventTarget {
'post-notes',
'Notes',
this._hostNode.querySelectorAll('.notes'));
+ this._poolsExpander = new ExpanderControl(
+ 'post-pools',
+ `Pools (${this._post.pools.length})`,
+ this._hostNode.querySelectorAll('.pools'));
new ExpanderControl(
'post-content',
'Content',
@@ -76,6 +82,11 @@ class PostEditSidebarControl extends events.EventTarget {
this._tagInputNode, post.tags);
}
+ if (this._poolInputNode) {
+ this._poolControl = new PoolInputControl(
+ this._poolInputNode, post.pools);
+ }
+
if (this._contentInputNode) {
this._contentFileDropper = new FileDropperControl(
this._contentInputNode, {
@@ -170,6 +181,9 @@ class PostEditSidebarControl extends events.EventTarget {
this._post.notes.addEventListener(eventType, e => {
this._syncExpanderTitles();
});
+ this._post.pools.addEventListener(eventType, e => {
+ this._syncExpanderTitles();
+ });
}
this._tagControl.addEventListener(
@@ -182,11 +196,18 @@ class PostEditSidebarControl extends events.EventTarget {
this._noteTextareaNode.addEventListener(
'change', e => this._evtNoteTextChangeRequest(e));
}
+
+ this._poolControl.addEventListener(
+ 'change', e => {
+ this.dispatchEvent(new CustomEvent('change'));
+ this._syncExpanderTitles();
+ });
}
_syncExpanderTitles() {
this._notesExpander.title = `Notes (${this._post.notes.length})`;
this._tagsExpander.title = `Tags (${this._post.tags.length})`;
+ this._poolsExpander.title = `Pools (${this._post.pools.length})`;
}
_evtPostContentChange(e) {
@@ -338,6 +359,10 @@ class PostEditSidebarControl extends events.EventTarget {
misc.splitByWhitespace(this._tagInputNode.value) :
undefined,
+ pools: this._poolInputNode ?
+ misc.splitByWhitespace(this._poolInputNode.value) :
+ undefined,
+
relations: this._relationsInputNode ?
misc.splitByWhitespace(this._relationsInputNode.value)
.map(x => parseInt(x)) :
@@ -374,6 +399,10 @@ class PostEditSidebarControl extends events.EventTarget {
return this._formNode.querySelector('.tags input');
}
+ get _poolInputNode() {
+ return this._formNode.querySelector('.pools input');
+ }
+
get _loopVideoInputNode() {
return this._formNode.querySelector('.flags input[name=loop]');
}

© 2015 - 2026 Jakob L. Kreuze