client/comments: add comment adding
This commit is contained in:
parent
b9fc626ace
commit
a28b4bdd3e
9 changed files with 333 additions and 193 deletions
|
@ -5,18 +5,15 @@
|
||||||
margin: 0 0 2em 0
|
margin: 0 0 2em 0
|
||||||
padding: 0
|
padding: 0
|
||||||
|
|
||||||
.comment
|
|
||||||
margin: 0 0 1em 0
|
|
||||||
padding: 0
|
|
||||||
display: -webkit-flex
|
|
||||||
display: flex
|
|
||||||
|
|
||||||
|
|
||||||
|
.comment-form-container
|
||||||
&:not(.editing)
|
&:not(.editing)
|
||||||
.tabs nav
|
.tabs nav
|
||||||
display: none
|
display: none
|
||||||
.tabs .edit.tab
|
.tabs .edit.tab
|
||||||
display: none
|
display: none
|
||||||
.content
|
.comment-content
|
||||||
margin-left: 0.5em
|
margin-left: 0.5em
|
||||||
&.editing
|
&.editing
|
||||||
.tab:not(.active)
|
.tab:not(.active)
|
||||||
|
@ -25,10 +22,10 @@
|
||||||
background: $active-tab-background-color
|
background: $active-tab-background-color
|
||||||
.tab
|
.tab
|
||||||
padding: 1em
|
padding: 1em
|
||||||
.content-wrapper
|
.comment-content-wrapper
|
||||||
background: $window-color
|
background: $window-color
|
||||||
overflow: hidden
|
overflow: hidden
|
||||||
.content
|
.comment-content
|
||||||
margin: 1em
|
margin: 1em
|
||||||
textarea
|
textarea
|
||||||
resize: vertical
|
resize: vertical
|
||||||
|
@ -36,6 +33,27 @@
|
||||||
max-height: 80vh
|
max-height: 80vh
|
||||||
box-sizing: padding-box
|
box-sizing: padding-box
|
||||||
|
|
||||||
|
form
|
||||||
|
width: auto
|
||||||
|
margin: 0
|
||||||
|
|
||||||
|
nav
|
||||||
|
vertical-align: middle !important
|
||||||
|
margin: 0 0.3em 0.5em 0 !important
|
||||||
|
&.buttons
|
||||||
|
float: left
|
||||||
|
&.actions
|
||||||
|
float: left
|
||||||
|
margin-top: 0.3em !important
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.comment
|
||||||
|
margin: 0 0 1em 0
|
||||||
|
padding: 0
|
||||||
|
display: -webkit-flex
|
||||||
|
display: flex
|
||||||
|
|
||||||
.avatar
|
.avatar
|
||||||
margin-right: 1em
|
margin-right: 1em
|
||||||
-webkit-flex-shrink: 0
|
-webkit-flex-shrink: 0
|
||||||
|
@ -80,64 +98,55 @@
|
||||||
display: inline-block
|
display: inline-block
|
||||||
width: 2em
|
width: 2em
|
||||||
|
|
||||||
form
|
|
||||||
width: auto
|
|
||||||
margin: 0
|
|
||||||
|
|
||||||
nav
|
|
||||||
vertical-align: middle
|
|
||||||
margin: 0 0.8em 0.5em 0
|
|
||||||
&.buttons
|
|
||||||
float: left
|
|
||||||
&.actions
|
|
||||||
float: left
|
|
||||||
margin-top: 0.3em
|
|
||||||
|
|
||||||
.messages
|
.messages
|
||||||
margin: 1em 0
|
margin: 1em 0
|
||||||
|
|
||||||
.content
|
|
||||||
ul
|
|
||||||
list-style-position: inside
|
|
||||||
margin: 1em 0
|
|
||||||
padding: 0
|
|
||||||
|
|
||||||
.sjis
|
|
||||||
font-family: 'MS PGothic', 'MS Pゴシック', 'IPAMonaPGothic', 'Trebuchet MS', Verdana, Futura, Arial, Helvetica, sans-serif
|
|
||||||
background: #fbfbfb
|
|
||||||
color: #111
|
|
||||||
font-size: 12pt
|
|
||||||
line-height: 1
|
|
||||||
margin: 0
|
|
||||||
padding: 4px
|
|
||||||
overflow: auto
|
|
||||||
white-space: pre
|
|
||||||
word-wrap: normal
|
|
||||||
|
|
||||||
p:first-child
|
.comment-content
|
||||||
margin-top: 0
|
ul
|
||||||
|
list-style-position: inside
|
||||||
|
margin: 1em 0
|
||||||
|
padding: 0
|
||||||
|
|
||||||
.spoiler
|
.sjis
|
||||||
background: #eee
|
font-family: 'MS PGothic', 'MS Pゴシック', 'IPAMonaPGothic', 'Trebuchet MS', Verdana, Futura, Arial, Helvetica, sans-serif
|
||||||
color: #eee
|
background: #fbfbfb
|
||||||
&:hover
|
color: #111
|
||||||
color: dimgray
|
font-size: 12pt
|
||||||
&:before
|
line-height: 1
|
||||||
content: '['
|
margin: 0
|
||||||
color: #000
|
padding: 4px
|
||||||
&:after
|
overflow: auto
|
||||||
content: ']'
|
white-space: pre
|
||||||
color: #000
|
word-wrap: normal
|
||||||
|
|
||||||
|
p:first-child
|
||||||
|
margin-top: 0
|
||||||
|
|
||||||
|
.spoiler
|
||||||
|
background: #eee
|
||||||
|
color: #eee
|
||||||
|
&:hover
|
||||||
|
color: dimgray
|
||||||
|
&:before
|
||||||
|
content: '['
|
||||||
|
color: #000
|
||||||
|
&:after
|
||||||
|
content: ']'
|
||||||
|
color: #000
|
||||||
|
|
||||||
|
blockquote
|
||||||
|
border-left: 3px solid #eee
|
||||||
|
margin-left: 0
|
||||||
|
padding: 0.3em 0.3em 0.3em 0.7em
|
||||||
|
background: #fafafa
|
||||||
|
color: #444
|
||||||
|
|
||||||
|
blockquote :last-child
|
||||||
|
margin-bottom: 0
|
||||||
|
|
||||||
blockquote
|
|
||||||
border-left: 3px solid #eee
|
|
||||||
margin-left: 0
|
|
||||||
padding: 0.3em 0.3em 0.3em 0.7em
|
|
||||||
background: #fafafa
|
|
||||||
color: #444
|
|
||||||
|
|
||||||
blockquote :last-child
|
|
||||||
margin-bottom: 0
|
|
||||||
|
|
||||||
.global-comment-list
|
.global-comment-list
|
||||||
text-align: left
|
text-align: left
|
||||||
|
|
|
@ -44,32 +44,6 @@
|
||||||
--><% } %><!--
|
--><% } %><!--
|
||||||
--></header>
|
--></header>
|
||||||
|
|
||||||
<div class='tabs'>
|
<div class='comment-form-container'></div>
|
||||||
<form>
|
|
||||||
<div class='tabs-wrapper'>
|
|
||||||
<div class='preview tab'>
|
|
||||||
<div class='content-wrapper'><div class='content'><%= ctx.makeMarkdown(ctx.comment.text) %></div></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class='edit tab'>
|
|
||||||
<textarea required minlength=1><%= ctx.comment.text %></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<nav class='buttons'>
|
|
||||||
<ul>
|
|
||||||
<li class='preview'><a href='#'>Preview</a></li>
|
|
||||||
<li class='edit'><a href='#'>Edit</a></li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<nav class='actions'>
|
|
||||||
<input type='submit' class='save' value='Save'/>
|
|
||||||
<input type='button' class='cancel discourage' value='Cancel'/>
|
|
||||||
</nav>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div class='messages'></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
31
client/html/comment_form.tpl
Normal file
31
client/html/comment_form.tpl
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<div class='tabs'>
|
||||||
|
<form>
|
||||||
|
<div class='tabs-wrapper'><!--
|
||||||
|
--><div class='preview tab'><!--
|
||||||
|
--><div class='comment-content-wrapper'><!--
|
||||||
|
--><div class='comment-content'><!--
|
||||||
|
--><%= ctx.makeMarkdown(ctx.comment.text) %><!--
|
||||||
|
--></div><!--
|
||||||
|
--></div><!--
|
||||||
|
--></div><!--
|
||||||
|
|
||||||
|
--><div class='edit tab'><!--
|
||||||
|
--><textarea required minlength=1><%= ctx.comment.text %></textarea><!--
|
||||||
|
--></div><!--
|
||||||
|
--></div>
|
||||||
|
|
||||||
|
<nav class='buttons'>
|
||||||
|
<ul>
|
||||||
|
<li class='preview'><a href='#'>Preview</a></li>
|
||||||
|
<li class='edit'><a href='#'>Edit</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<nav class='actions'>
|
||||||
|
<input type='submit' class='save' value='Save'/>
|
||||||
|
<input type='button' class='cancel discourage' value='Cancel'/>
|
||||||
|
</nav>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class='messages'></div>
|
||||||
|
</div>
|
|
@ -46,6 +46,13 @@
|
||||||
<div class='content'>
|
<div class='content'>
|
||||||
<div class='post-container'></div>
|
<div class='post-container'></div>
|
||||||
|
|
||||||
<div class='comments-container'></div>
|
<% if (ctx.canListComments) { %>
|
||||||
|
<div class='comments-container'></div>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (ctx.canCreateComments) { %>
|
||||||
|
<h2>Add comment</h2>
|
||||||
|
<div class='comment-form-container'></div>
|
||||||
|
<% } %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -74,6 +74,8 @@ class PostsController {
|
||||||
nextPostId: aroundResponse.next ? aroundResponse.next.id : null,
|
nextPostId: aroundResponse.next ? aroundResponse.next.id : null,
|
||||||
prevPostId: aroundResponse.prev ? aroundResponse.prev.id : null,
|
prevPostId: aroundResponse.prev ? aroundResponse.prev.id : null,
|
||||||
canEditPosts: api.hasPrivilege('posts:edit'),
|
canEditPosts: api.hasPrivilege('posts:edit'),
|
||||||
|
canListComments: api.hasPrivilege('comments:list'),
|
||||||
|
canCreateComments: api.hasPrivilege('comments:create'),
|
||||||
});
|
});
|
||||||
}, response => {
|
}, response => {
|
||||||
this._emptyView.render();
|
this._emptyView.render();
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const api = require('../api.js');
|
const api = require('../api.js');
|
||||||
const misc = require('../util/misc.js');
|
|
||||||
const views = require('../util/views.js');
|
const views = require('../util/views.js');
|
||||||
|
const CommentFormControl = require('../controls/comment_form_control.js');
|
||||||
|
|
||||||
class CommentControl {
|
class CommentControl {
|
||||||
constructor(hostNode, comment) {
|
constructor(hostNode, comment, settings) {
|
||||||
this._hostNode = hostNode;
|
this._hostNode = hostNode;
|
||||||
this._comment = comment;
|
this._comment = comment;
|
||||||
this._template = views.getTemplate('comment');
|
this._template = views.getTemplate('comment');
|
||||||
this._scoreTemplate = views.getTemplate('score');
|
this._scoreTemplate = views.getTemplate('score');
|
||||||
|
this._settings = settings;
|
||||||
|
|
||||||
this.install();
|
this.install();
|
||||||
}
|
}
|
||||||
|
@ -36,11 +37,6 @@ class CommentControl {
|
||||||
const deleteButton = sourceNode.querySelector('.delete');
|
const deleteButton = sourceNode.querySelector('.delete');
|
||||||
const upvoteButton = sourceNode.querySelector('.upvote');
|
const upvoteButton = sourceNode.querySelector('.upvote');
|
||||||
const downvoteButton = sourceNode.querySelector('.downvote');
|
const downvoteButton = sourceNode.querySelector('.downvote');
|
||||||
const previewTabButton = sourceNode.querySelector('.buttons .preview');
|
|
||||||
const editTabButton = sourceNode.querySelector('.buttons .edit');
|
|
||||||
const formNode = sourceNode.querySelector('form');
|
|
||||||
const cancelButton = sourceNode.querySelector('.cancel');
|
|
||||||
const textareaNode = sourceNode.querySelector('form textarea');
|
|
||||||
|
|
||||||
if (editButton) {
|
if (editButton) {
|
||||||
editButton.addEventListener(
|
editButton.addEventListener(
|
||||||
|
@ -64,20 +60,22 @@ class CommentControl {
|
||||||
e, () => this._comment.ownScore === -1 ? 0 : -1));
|
e, () => this._comment.ownScore === -1 ? 0 : -1));
|
||||||
}
|
}
|
||||||
|
|
||||||
previewTabButton.addEventListener(
|
this._formControl = new CommentFormControl(
|
||||||
'click', e => this._evtPreviewClick(e));
|
sourceNode.querySelector('.comment-form-container'),
|
||||||
editTabButton.addEventListener(
|
this._comment,
|
||||||
'click', e => this._evtEditClick(e));
|
{
|
||||||
|
onSave: text => {
|
||||||
formNode.addEventListener('submit', e => this._evtSaveClick(e));
|
return api.put('/comment/' + this._comment.id, {
|
||||||
cancelButton.addEventListener('click', e => this._evtCancelClick(e));
|
text: text,
|
||||||
|
}).then(response => {
|
||||||
for (let event of ['cut', 'paste', 'drop', 'keydown']) {
|
this._comment = response;
|
||||||
textareaNode.addEventListener(event, e => {
|
this.install();
|
||||||
window.setTimeout(() => this._growTextArea(), 0);
|
}, response => {
|
||||||
|
this._formControl.showError(response.description);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
canCancel: true
|
||||||
});
|
});
|
||||||
}
|
|
||||||
textareaNode.addEventListener('change', e => { this._growTextArea(); });
|
|
||||||
|
|
||||||
views.showView(this._hostNode, sourceNode);
|
views.showView(this._hostNode, sourceNode);
|
||||||
}
|
}
|
||||||
|
@ -97,6 +95,11 @@ class CommentControl {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_evtEditClick(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
this._formControl.enterEditMode();
|
||||||
|
}
|
||||||
|
|
||||||
_evtDeleteClick(e) {
|
_evtDeleteClick(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!window.confirm('Are you sure you want to delete this comment?')) {
|
if (!window.confirm('Are you sure you want to delete this comment?')) {
|
||||||
|
@ -104,82 +107,14 @@ class CommentControl {
|
||||||
}
|
}
|
||||||
api.delete('/comment/' + this._comment.id)
|
api.delete('/comment/' + this._comment.id)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
|
if (this._settings.onDelete) {
|
||||||
|
this._settings.onDelete(this._comment);
|
||||||
|
}
|
||||||
this._hostNode.parentNode.removeChild(this._hostNode);
|
this._hostNode.parentNode.removeChild(this._hostNode);
|
||||||
}, response => {
|
}, response => {
|
||||||
window.alert(response.description);
|
window.alert(response.description);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_evtSaveClick(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
api.put('/comment/' + this._comment.id, {
|
|
||||||
text: this._hostNode.querySelector('.edit.tab textarea').value,
|
|
||||||
}).then(response => {
|
|
||||||
this._comment = response;
|
|
||||||
this.install();
|
|
||||||
}, response => {
|
|
||||||
this._showError(response.description);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_evtPreviewClick(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
this._hostNode.querySelector('.preview.tab .content').innerHTML
|
|
||||||
= misc.formatMarkdown(
|
|
||||||
this._hostNode.querySelector('.edit.tab textarea').value);
|
|
||||||
this._freezeTabHeights();
|
|
||||||
this._selectTab('preview');
|
|
||||||
}
|
|
||||||
|
|
||||||
_evtEditClick(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
this._freezeTabHeights();
|
|
||||||
this._enterEditMode();
|
|
||||||
this._selectTab('edit');
|
|
||||||
this._growTextArea();
|
|
||||||
}
|
|
||||||
|
|
||||||
_evtCancelClick(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
this._exitEditMode();
|
|
||||||
this._hostNode.querySelector('.edit.tab textarea').value
|
|
||||||
= this._comment.text;
|
|
||||||
}
|
|
||||||
|
|
||||||
_enterEditMode() {
|
|
||||||
this._hostNode.querySelector('.comment').classList.add('editing');
|
|
||||||
misc.enableExitConfirmation();
|
|
||||||
}
|
|
||||||
|
|
||||||
_exitEditMode() {
|
|
||||||
this._hostNode.querySelector('.comment').classList.remove('editing');
|
|
||||||
this._hostNode.querySelector('.tabs-wrapper').style.minHeight = null;
|
|
||||||
misc.disableExitConfirmation();
|
|
||||||
views.clearMessages(this._hostNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
_selectTab(tabName) {
|
|
||||||
this._freezeTabHeights();
|
|
||||||
for (let tab of this._hostNode.querySelectorAll('.tab, .buttons li')) {
|
|
||||||
tab.classList.toggle('active', tab.classList.contains(tabName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_freezeTabHeights() {
|
|
||||||
const tabsNode = this._hostNode.querySelector('.tabs-wrapper');
|
|
||||||
const tabsHeight = tabsNode.getBoundingClientRect().height;
|
|
||||||
tabsNode.style.minHeight = tabsHeight + 'px';
|
|
||||||
}
|
|
||||||
|
|
||||||
_growTextArea() {
|
|
||||||
const previewNode = this._hostNode.querySelector('.content');
|
|
||||||
const textareaNode = this._hostNode.querySelector('textarea');
|
|
||||||
textareaNode.style.height = textareaNode.scrollHeight + 'px';
|
|
||||||
}
|
|
||||||
|
|
||||||
_showError(message) {
|
|
||||||
views.showError(this._hostNode, message);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = CommentControl;
|
module.exports = CommentControl;
|
||||||
|
|
134
client/js/controls/comment_form_control.js
Normal file
134
client/js/controls/comment_form_control.js
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const misc = require('../util/misc.js');
|
||||||
|
const views = require('../util/views.js');
|
||||||
|
|
||||||
|
class CommentFormControl {
|
||||||
|
constructor(hostNode, comment, settings) {
|
||||||
|
this._hostNode = hostNode;
|
||||||
|
this._comment = comment || {text: ''};
|
||||||
|
this._template = views.getTemplate('comment-form');
|
||||||
|
this._settings = settings;
|
||||||
|
this.install();
|
||||||
|
}
|
||||||
|
|
||||||
|
install() {
|
||||||
|
const sourceNode = this._template({
|
||||||
|
comment: this._comment,
|
||||||
|
});
|
||||||
|
|
||||||
|
const previewTabButton = sourceNode.querySelector('.buttons .preview');
|
||||||
|
const editTabButton = sourceNode.querySelector('.buttons .edit');
|
||||||
|
const formNode = sourceNode.querySelector('form');
|
||||||
|
const cancelButton = sourceNode.querySelector('.cancel');
|
||||||
|
const textareaNode = sourceNode.querySelector('form textarea');
|
||||||
|
|
||||||
|
previewTabButton.addEventListener(
|
||||||
|
'click', e => this._evtPreviewClick(e));
|
||||||
|
editTabButton.addEventListener(
|
||||||
|
'click', e => this._evtEditClick(e));
|
||||||
|
|
||||||
|
formNode.addEventListener('submit', e => this._evtSaveClick(e));
|
||||||
|
|
||||||
|
if (this._settings.canCancel) {
|
||||||
|
cancelButton
|
||||||
|
.addEventListener('click', e => this._evtCancelClick(e));
|
||||||
|
} else {
|
||||||
|
cancelButton.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let event of ['cut', 'paste', 'drop', 'keydown']) {
|
||||||
|
textareaNode.addEventListener(event, e => {
|
||||||
|
window.setTimeout(() => this._growTextArea(), 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
textareaNode.addEventListener('change', e => {
|
||||||
|
misc.enableExitConfirmation();
|
||||||
|
this._growTextArea();
|
||||||
|
});
|
||||||
|
|
||||||
|
views.showView(this._hostNode, sourceNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
enterEditMode() {
|
||||||
|
this._freezeTabHeights();
|
||||||
|
this._hostNode.classList.add('editing');
|
||||||
|
this._selectTab('edit');
|
||||||
|
this._growTextArea();
|
||||||
|
}
|
||||||
|
|
||||||
|
exitEditMode() {
|
||||||
|
this._hostNode.classList.remove('editing');
|
||||||
|
this._hostNode.querySelector('.tabs-wrapper').style.minHeight = null;
|
||||||
|
misc.disableExitConfirmation();
|
||||||
|
views.clearMessages(this._hostNode);
|
||||||
|
this._hostNode.querySelector('.edit.tab textarea').value
|
||||||
|
= this._comment.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
get _textareaNode() {
|
||||||
|
return this._hostNode.querySelector('.edit.tab textarea');
|
||||||
|
}
|
||||||
|
|
||||||
|
get _contentNode() {
|
||||||
|
return this._hostNode.querySelector('.preview.tab .comment-content');
|
||||||
|
}
|
||||||
|
|
||||||
|
setText(text) {
|
||||||
|
this._textareaNode.value = text;
|
||||||
|
this._contentNode.innerHTML = misc.formatMarkdown(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
showError(message) {
|
||||||
|
views.showError(this._hostNode, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
_evtPreviewClick(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
this._contentNode.innerHTML
|
||||||
|
= misc.formatMarkdown(this._textareaNode.value);
|
||||||
|
this._freezeTabHeights();
|
||||||
|
this._selectTab('preview');
|
||||||
|
}
|
||||||
|
|
||||||
|
_evtEditClick(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.enterEditMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
_evtSaveClick(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!this._settings.onSave) {
|
||||||
|
throw 'No save handler';
|
||||||
|
}
|
||||||
|
this._settings.onSave(this._textareaNode.value)
|
||||||
|
.then(() => { misc.disableExitConfirmation(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
_evtCancelClick(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.exitEditMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
_selectTab(tabName) {
|
||||||
|
this._freezeTabHeights();
|
||||||
|
for (let tab of this._hostNode.querySelectorAll('.tab, .buttons li')) {
|
||||||
|
tab.classList.toggle('active', tab.classList.contains(tabName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_freezeTabHeights() {
|
||||||
|
const tabsNode = this._hostNode.querySelector('.tabs-wrapper');
|
||||||
|
const tabsHeight = tabsNode.getBoundingClientRect().height;
|
||||||
|
tabsNode.style.minHeight = tabsHeight + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
_growTextArea() {
|
||||||
|
this._textareaNode.style.height
|
||||||
|
= Math.max(
|
||||||
|
this._settings.minHeight || 0,
|
||||||
|
this._textareaNode.scrollHeight) + 'px';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = CommentFormControl;
|
|
@ -31,7 +31,15 @@ class CommentListControl {
|
||||||
const commentList = new DocumentFragment();
|
const commentList = new DocumentFragment();
|
||||||
for (let comment of this._comments) {
|
for (let comment of this._comments) {
|
||||||
const commentListItemNode = document.createElement('li');
|
const commentListItemNode = document.createElement('li');
|
||||||
new CommentControl(commentListItemNode, comment);
|
new CommentControl(commentListItemNode, comment, {
|
||||||
|
onDelete: removedComment => {
|
||||||
|
for (let [index, comment] of this._comments.entries()) {
|
||||||
|
if (comment.id === removedComment.id) {
|
||||||
|
this._comments.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
commentList.appendChild(commentListItemNode);
|
commentList.appendChild(commentListItemNode);
|
||||||
}
|
}
|
||||||
views.showView(this._hostNode.querySelector('ul'), commentList);
|
views.showView(this._hostNode.querySelector('ul'), commentList);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const api = require('../api.js');
|
||||||
const views = require('../util/views.js');
|
const views = require('../util/views.js');
|
||||||
const keyboard = require('../util/keyboard.js');
|
const keyboard = require('../util/keyboard.js');
|
||||||
const page = require('page');
|
const page = require('page');
|
||||||
|
@ -11,6 +12,7 @@ const PostReadonlySidebarControl
|
||||||
const PostEditSidebarControl
|
const PostEditSidebarControl
|
||||||
= require('../controls/post_edit_sidebar_control.js');
|
= require('../controls/post_edit_sidebar_control.js');
|
||||||
const CommentListControl = require('../controls/comment_list_control.js');
|
const CommentListControl = require('../controls/comment_list_control.js');
|
||||||
|
const CommentFormControl = require('../controls/comment_form_control.js');
|
||||||
|
|
||||||
class PostView {
|
class PostView {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -52,21 +54,9 @@ class PostView {
|
||||||
postContainerNode.querySelector('.post-overlay'),
|
postContainerNode.querySelector('.post-overlay'),
|
||||||
ctx.post);
|
ctx.post);
|
||||||
|
|
||||||
if (ctx.editMode) {
|
this._installSidebar(ctx);
|
||||||
new PostEditSidebarControl(
|
this._installCommentForm(ctx);
|
||||||
postViewNode.querySelector('.sidebar-container'),
|
this._installComments(ctx);
|
||||||
ctx.post,
|
|
||||||
this._postContentControl);
|
|
||||||
} else {
|
|
||||||
new PostReadonlySidebarControl(
|
|
||||||
postViewNode.querySelector('.sidebar-container'),
|
|
||||||
ctx.post,
|
|
||||||
this._postContentControl);
|
|
||||||
}
|
|
||||||
|
|
||||||
new CommentListControl(
|
|
||||||
postViewNode.querySelector('.comments-container'),
|
|
||||||
ctx.post.comments);
|
|
||||||
|
|
||||||
keyboard.bind('e', () => {
|
keyboard.bind('e', () => {
|
||||||
if (ctx.editMode) {
|
if (ctx.editMode) {
|
||||||
|
@ -86,6 +76,56 @@ class PostView {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_installSidebar(ctx) {
|
||||||
|
const sidebarContainerNode = document.querySelector(
|
||||||
|
'#content-holder .sidebar-container');
|
||||||
|
|
||||||
|
if (ctx.editMode) {
|
||||||
|
new PostEditSidebarControl(
|
||||||
|
sidebarContainerNode, ctx.post, this._postContentControl);
|
||||||
|
} else {
|
||||||
|
new PostReadonlySidebarControl(
|
||||||
|
sidebarContainerNode, ctx.post, this._postContentControl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_installCommentForm(ctx) {
|
||||||
|
const commentFormContainer = document.querySelector(
|
||||||
|
'#content-holder .comment-form-container');
|
||||||
|
if (!commentFormContainer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._formControl = new CommentFormControl(
|
||||||
|
commentFormContainer,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
onSave: text => {
|
||||||
|
return api.post('/comments', {
|
||||||
|
postId: ctx.post.id,
|
||||||
|
text: text,
|
||||||
|
}).then(response => {
|
||||||
|
ctx.post.comments.push(response);
|
||||||
|
this._formControl.setText('');
|
||||||
|
this._installComments(ctx);
|
||||||
|
}, response => {
|
||||||
|
this._formControl.showError(response.description);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
canCancel: false,
|
||||||
|
minHeight: 150,
|
||||||
|
});
|
||||||
|
this._formControl.enterEditMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
_installComments(ctx) {
|
||||||
|
const commentsContainerNode = document.querySelector(
|
||||||
|
'#content-holder .comments-container');
|
||||||
|
if (commentsContainerNode) {
|
||||||
|
new CommentListControl(commentsContainerNode, ctx.post.comments);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = PostView;
|
module.exports = PostView;
|
||||||
|
|
Loading…
Reference in a new issue