client/general: remove api calls from controls
Introduce some missing models along the way
This commit is contained in:
parent
54e3099c56
commit
a697aba1b0
|
@ -1,6 +1,4 @@
|
||||||
<div class='comments'>
|
<div class='comments'>
|
||||||
<% if (ctx.canListComments && ctx.comments.length) { %>
|
<ul>
|
||||||
<ul>
|
</ul>
|
||||||
</ul>
|
|
||||||
<% } %>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
const api = require('../api.js');
|
const api = require('../api.js');
|
||||||
const misc = require('../util/misc.js');
|
const misc = require('../util/misc.js');
|
||||||
|
const PostList = require('../models/post_list.js');
|
||||||
const topNavigation = require('../models/top_navigation.js');
|
const topNavigation = require('../models/top_navigation.js');
|
||||||
const PageController = require('../controllers/page_controller.js');
|
const PageController = require('../controllers/page_controller.js');
|
||||||
const CommentsPageView = require('../views/comments_page_view.js');
|
const CommentsPageView = require('../views/comments_page_view.js');
|
||||||
|
@ -10,25 +11,62 @@ class CommentsController {
|
||||||
constructor(ctx) {
|
constructor(ctx) {
|
||||||
topNavigation.activate('comments');
|
topNavigation.activate('comments');
|
||||||
|
|
||||||
|
const proxy = PageController.createHistoryCacheProxy(
|
||||||
|
ctx, page => {
|
||||||
|
const url =
|
||||||
|
'/posts/?query=sort:comment-date+comment-count-min:1' +
|
||||||
|
`&page=${page}&pageSize=10&fields=` +
|
||||||
|
'id,comments,commentCount,thumbnailUrl';
|
||||||
|
return api.get(url);
|
||||||
|
});
|
||||||
|
|
||||||
this._pageController = new PageController({
|
this._pageController = new PageController({
|
||||||
searchQuery: ctx.searchQuery,
|
searchQuery: ctx.searchQuery,
|
||||||
clientUrl: '/comments/' + misc.formatSearchQuery({page: '{page}'}),
|
clientUrl: '/comments/' + misc.formatSearchQuery({page: '{page}'}),
|
||||||
requestPage: PageController.createHistoryCacheProxy(
|
requestPage: page => {
|
||||||
ctx,
|
return proxy(page).then(response => {
|
||||||
page => {
|
return Promise.resolve(Object.assign(
|
||||||
return api.get(
|
{},
|
||||||
'/posts/?query=sort:comment-date+comment-count-min:1' +
|
response,
|
||||||
`&page=${page}&pageSize=10&fields=` +
|
{results: PostList.fromResponse(response.results)}));
|
||||||
'id,comments,commentCount,thumbnailUrl');
|
});
|
||||||
}),
|
},
|
||||||
pageRenderer: pageCtx => {
|
pageRenderer: pageCtx => {
|
||||||
Object.assign(pageCtx, {
|
Object.assign(pageCtx, {
|
||||||
canViewPosts: api.hasPrivilege('posts:view'),
|
canViewPosts: api.hasPrivilege('posts:view'),
|
||||||
});
|
});
|
||||||
return new CommentsPageView(pageCtx);
|
const view = new CommentsPageView(pageCtx);
|
||||||
|
view.addEventListener('change', e => this._evtChange(e));
|
||||||
|
view.addEventListener('score', e => this._evtScore(e));
|
||||||
|
view.addEventListener('delete', e => this._evtDelete(e));
|
||||||
|
return view;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_evtChange(e) {
|
||||||
|
// TODO: disable form
|
||||||
|
e.detail.comment.text = e.detail.text;
|
||||||
|
e.detail.comment.save()
|
||||||
|
.catch(errorMessage => {
|
||||||
|
e.detail.target.showError(errorMessage);
|
||||||
|
// TODO: enable form
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_evtScore(e) {
|
||||||
|
e.detail.comment.setScore(e.detail.score)
|
||||||
|
.catch(errorMessage => {
|
||||||
|
window.alert(errorMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_evtDelete(e) {
|
||||||
|
e.detail.comment.delete()
|
||||||
|
.catch(errorMessage => {
|
||||||
|
window.alert(errorMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = router => {
|
module.exports = router => {
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const api = require('../api.js');
|
const api = require('../api.js');
|
||||||
|
const misc = require('../util/misc.js');
|
||||||
const settings = require('../models/settings.js');
|
const settings = require('../models/settings.js');
|
||||||
|
const Comment = require('../models/comment.js');
|
||||||
const Post = require('../models/post.js');
|
const Post = require('../models/post.js');
|
||||||
const topNavigation = require('../models/top_navigation.js');
|
const topNavigation = require('../models/top_navigation.js');
|
||||||
const PostView = require('../views/post_view.js');
|
const PostView = require('../views/post_view.js');
|
||||||
|
@ -17,6 +19,7 @@ class PostController {
|
||||||
this._decorateSearchQuery('')),
|
this._decorateSearchQuery('')),
|
||||||
]).then(responses => {
|
]).then(responses => {
|
||||||
const [post, aroundResponse] = responses;
|
const [post, aroundResponse] = responses;
|
||||||
|
this._post = post;
|
||||||
this._view = new PostView({
|
this._view = new PostView({
|
||||||
post: post,
|
post: post,
|
||||||
editMode: editMode,
|
editMode: editMode,
|
||||||
|
@ -26,6 +29,28 @@ class PostController {
|
||||||
canListComments: api.hasPrivilege('comments:list'),
|
canListComments: api.hasPrivilege('comments:list'),
|
||||||
canCreateComments: api.hasPrivilege('comments:create'),
|
canCreateComments: api.hasPrivilege('comments:create'),
|
||||||
});
|
});
|
||||||
|
if (this._view.sidebarControl) {
|
||||||
|
this._view.sidebarControl.addEventListener(
|
||||||
|
'favorite', e => this._evtFavoritePost(e));
|
||||||
|
this._view.sidebarControl.addEventListener(
|
||||||
|
'unfavorite', e => this._evtUnfavoritePost(e));
|
||||||
|
this._view.sidebarControl.addEventListener(
|
||||||
|
'score', e => this._evtScorePost(e));
|
||||||
|
}
|
||||||
|
if (this._view.commentFormControl) {
|
||||||
|
this._view.commentFormControl.addEventListener(
|
||||||
|
'change', e => this._evtCommentChange(e));
|
||||||
|
this._view.commentFormControl.addEventListener(
|
||||||
|
'submit', e => this._evtCreateComment(e));
|
||||||
|
}
|
||||||
|
if (this._view.commentListControl) {
|
||||||
|
this._view.commentListControl.addEventListener(
|
||||||
|
'change', e => this._evtUpdateComment(e));
|
||||||
|
this._view.commentListControl.addEventListener(
|
||||||
|
'score', e => this._evtScoreComment(e));
|
||||||
|
this._view.commentListControl.addEventListener(
|
||||||
|
'delete', e => this._evtDeleteComment(e));
|
||||||
|
}
|
||||||
}, response => {
|
}, response => {
|
||||||
this._view = new EmptyView();
|
this._view = new EmptyView();
|
||||||
this._view.showError(response.description);
|
this._view.showError(response.description);
|
||||||
|
@ -45,6 +70,71 @@ class PostController {
|
||||||
}
|
}
|
||||||
return text.trim();
|
return text.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_evtCommentChange(e) {
|
||||||
|
misc.enableExitConfirmation();
|
||||||
|
}
|
||||||
|
|
||||||
|
_evtCreateComment(e) {
|
||||||
|
// TODO: disable form
|
||||||
|
const comment = Comment.create(this._post.id);
|
||||||
|
comment.text = e.detail.text;
|
||||||
|
comment.save()
|
||||||
|
.then(() => {
|
||||||
|
this._post.comments.add(comment);
|
||||||
|
this._view.commentFormControl.setText('');
|
||||||
|
// TODO: enable form
|
||||||
|
misc.disableExitConfirmation();
|
||||||
|
}, errorMessage => {
|
||||||
|
this._view.commentFormControl.showError(errorMessage);
|
||||||
|
// TODO: enable form
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_evtUpdateComment(e) {
|
||||||
|
// TODO: disable form
|
||||||
|
e.detail.comment.text = e.detail.text;
|
||||||
|
e.detail.comment.save()
|
||||||
|
.catch(errorMessage => {
|
||||||
|
e.detail.target.showError(errorMessage);
|
||||||
|
// TODO: enable form
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_evtScoreComment(e) {
|
||||||
|
e.detail.comment.setScore(e.detail.score)
|
||||||
|
.catch(errorMessage => {
|
||||||
|
window.alert(errorMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_evtDeleteComment(e) {
|
||||||
|
e.detail.comment.delete()
|
||||||
|
.catch(errorMessage => {
|
||||||
|
window.alert(errorMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_evtScorePost(e) {
|
||||||
|
e.detail.post.setScore(e.detail.score)
|
||||||
|
.catch(errorMessage => {
|
||||||
|
window.alert(errorMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_evtFavoritePost(e) {
|
||||||
|
e.detail.post.addToFavorites()
|
||||||
|
.catch(errorMessage => {
|
||||||
|
window.alert(errorMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_evtUnfavoritePost(e) {
|
||||||
|
e.detail.post.removeFromFavorites()
|
||||||
|
.catch(errorMessage => {
|
||||||
|
window.alert(errorMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = router => {
|
module.exports = router => {
|
||||||
|
|
|
@ -1,98 +1,86 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const api = require('../api.js');
|
const api = require('../api.js');
|
||||||
|
const events = require('../events.js');
|
||||||
const views = require('../util/views.js');
|
const views = require('../util/views.js');
|
||||||
const CommentFormControl = require('../controls/comment_form_control.js');
|
const CommentFormControl = require('../controls/comment_form_control.js');
|
||||||
|
|
||||||
class CommentControl {
|
const template = views.getTemplate('comment');
|
||||||
constructor(hostNode, comment, settings) {
|
const scoreTemplate = views.getTemplate('score');
|
||||||
|
|
||||||
|
class CommentControl extends events.EventTarget {
|
||||||
|
constructor(hostNode, comment) {
|
||||||
|
super();
|
||||||
this._hostNode = hostNode;
|
this._hostNode = hostNode;
|
||||||
this._comment = comment;
|
this._comment = comment;
|
||||||
this._template = views.getTemplate('comment');
|
|
||||||
this._scoreTemplate = views.getTemplate('score');
|
|
||||||
this._settings = settings;
|
|
||||||
|
|
||||||
this.install();
|
comment.addEventListener('change', e => this._evtChange(e));
|
||||||
}
|
comment.addEventListener('changeScore', e => this._evtChangeScore(e));
|
||||||
|
|
||||||
install() {
|
|
||||||
const isLoggedIn = api.isLoggedIn(this._comment.user);
|
const isLoggedIn = api.isLoggedIn(this._comment.user);
|
||||||
const infix = isLoggedIn ? 'own' : 'any';
|
const infix = isLoggedIn ? 'own' : 'any';
|
||||||
const sourceNode = this._template({
|
views.replaceContent(this._hostNode, template({
|
||||||
comment: this._comment,
|
comment: this._comment,
|
||||||
canViewUsers: api.hasPrivilege('users:view'),
|
canViewUsers: api.hasPrivilege('users:view'),
|
||||||
canEditComment: api.hasPrivilege(`comments:edit:${infix}`),
|
canEditComment: api.hasPrivilege(`comments:edit:${infix}`),
|
||||||
canDeleteComment: api.hasPrivilege(`comments:delete:${infix}`),
|
canDeleteComment: api.hasPrivilege(`comments:delete:${infix}`),
|
||||||
});
|
}));
|
||||||
|
|
||||||
|
if (this._editButtonNode) {
|
||||||
|
this._editButtonNode.addEventListener(
|
||||||
|
'click', e => this._evtEditClick(e));
|
||||||
|
}
|
||||||
|
if (this._deleteButtonNode) {
|
||||||
|
this._deleteButtonNode.addEventListener(
|
||||||
|
'click', e => this._evtDeleteClick(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
this._formControl = new CommentFormControl(
|
||||||
|
this._hostNode.querySelector('.comment-form-container'),
|
||||||
|
this._comment,
|
||||||
|
true);
|
||||||
|
events.proxyEvent(this._formControl, this, 'submit', 'change');
|
||||||
|
|
||||||
|
this._installScore();
|
||||||
|
}
|
||||||
|
|
||||||
|
get _scoreContainerNode() {
|
||||||
|
return this._hostNode.querySelector('.score-container');
|
||||||
|
}
|
||||||
|
|
||||||
|
get _editButtonNode() {
|
||||||
|
return this._hostNode.querySelector('.edit');
|
||||||
|
}
|
||||||
|
|
||||||
|
get _deleteButtonNode() {
|
||||||
|
return this._hostNode.querySelector('.delete');
|
||||||
|
}
|
||||||
|
|
||||||
|
get _upvoteButtonNode() {
|
||||||
|
return this._hostNode.querySelector('.upvote');
|
||||||
|
}
|
||||||
|
|
||||||
|
get _downvoteButtonNode() {
|
||||||
|
return this._hostNode.querySelector('.downvote');
|
||||||
|
}
|
||||||
|
|
||||||
|
_installScore() {
|
||||||
views.replaceContent(
|
views.replaceContent(
|
||||||
sourceNode.querySelector('.score-container'),
|
this._scoreContainerNode,
|
||||||
this._scoreTemplate({
|
scoreTemplate({
|
||||||
score: this._comment.score,
|
score: this._comment.score,
|
||||||
ownScore: this._comment.ownScore,
|
ownScore: this._comment.ownScore,
|
||||||
canScore: api.hasPrivilege('comments:score'),
|
canScore: api.hasPrivilege('comments:score'),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const editButton = sourceNode.querySelector('.edit');
|
if (this._upvoteButtonNode) {
|
||||||
const deleteButton = sourceNode.querySelector('.delete');
|
this._upvoteButtonNode.addEventListener(
|
||||||
const upvoteButton = sourceNode.querySelector('.upvote');
|
'click', e => this._evtScoreClick(e, 1));
|
||||||
const downvoteButton = sourceNode.querySelector('.downvote');
|
|
||||||
|
|
||||||
if (editButton) {
|
|
||||||
editButton.addEventListener(
|
|
||||||
'click', e => this._evtEditClick(e));
|
|
||||||
}
|
}
|
||||||
if (deleteButton) {
|
if (this._downvoteButtonNode) {
|
||||||
deleteButton.addEventListener(
|
this._downvoteButtonNode.addEventListener(
|
||||||
'click', e => this._evtDeleteClick(e));
|
'click', e => this._evtScoreClick(e, -1));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (upvoteButton) {
|
|
||||||
upvoteButton.addEventListener(
|
|
||||||
'click',
|
|
||||||
e => this._evtScoreClick(
|
|
||||||
e, () => this._comment.ownScore === 1 ? 0 : 1));
|
|
||||||
}
|
|
||||||
if (downvoteButton) {
|
|
||||||
downvoteButton.addEventListener(
|
|
||||||
'click',
|
|
||||||
e => this._evtScoreClick(
|
|
||||||
e, () => this._comment.ownScore === -1 ? 0 : -1));
|
|
||||||
}
|
|
||||||
|
|
||||||
this._formControl = new CommentFormControl(
|
|
||||||
sourceNode.querySelector('.comment-form-container'),
|
|
||||||
this._comment,
|
|
||||||
{
|
|
||||||
onSave: text => {
|
|
||||||
return api.put('/comment/' + this._comment.id, {
|
|
||||||
text: text,
|
|
||||||
}).then(response => {
|
|
||||||
this._comment = response;
|
|
||||||
this.install();
|
|
||||||
}, response => {
|
|
||||||
this._formControl.showError(response.description);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
canCancel: true
|
|
||||||
});
|
|
||||||
|
|
||||||
views.replaceContent(this._hostNode, sourceNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
_evtScoreClick(e, scoreGetter) {
|
|
||||||
e.preventDefault();
|
|
||||||
api.put(
|
|
||||||
'/comment/' + this._comment.id + '/score',
|
|
||||||
{score: scoreGetter()})
|
|
||||||
.then(
|
|
||||||
response => {
|
|
||||||
this._comment.score = parseInt(response.score);
|
|
||||||
this._comment.ownScore = parseInt(response.ownScore);
|
|
||||||
this.install();
|
|
||||||
}, response => {
|
|
||||||
window.alert(response.description);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_evtEditClick(e) {
|
_evtEditClick(e) {
|
||||||
|
@ -100,20 +88,34 @@ class CommentControl {
|
||||||
this._formControl.enterEditMode();
|
this._formControl.enterEditMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_evtScoreClick(e, score) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.dispatchEvent(new CustomEvent('score', {
|
||||||
|
detail: {
|
||||||
|
comment: this._comment,
|
||||||
|
score: this._comment.ownScore === score ? 0 : score,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
_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?')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
api.delete('/comment/' + this._comment.id)
|
this.dispatchEvent(new CustomEvent('delete', {
|
||||||
.then(response => {
|
detail: {
|
||||||
if (this._settings.onDelete) {
|
comment: this._comment,
|
||||||
this._settings.onDelete(this._comment);
|
},
|
||||||
}
|
}));
|
||||||
this._hostNode.parentNode.removeChild(this._hostNode);
|
}
|
||||||
}, response => {
|
|
||||||
window.alert(response.description);
|
_evtChange(e) {
|
||||||
});
|
this._formControl.exitEditMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
_evtChangeScore(e) {
|
||||||
|
this._installScore();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const events = require('../events.js');
|
||||||
const misc = require('../util/misc.js');
|
const misc = require('../util/misc.js');
|
||||||
const views = require('../util/views.js');
|
const views = require('../util/views.js');
|
||||||
|
|
||||||
class CommentFormControl {
|
const template = views.getTemplate('comment-form');
|
||||||
constructor(hostNode, comment, settings) {
|
|
||||||
|
class CommentFormControl extends events.EventTarget {
|
||||||
|
constructor(hostNode, comment, canCancel, minHeight) {
|
||||||
|
super();
|
||||||
this._hostNode = hostNode;
|
this._hostNode = hostNode;
|
||||||
this._comment = comment || {text: ''};
|
this._comment = comment || {text: ''};
|
||||||
this._template = views.getTemplate('comment-form');
|
this._canCancel = canCancel;
|
||||||
this._settings = settings;
|
this._minHeight = minHeight || 150;
|
||||||
this.install();
|
|
||||||
}
|
|
||||||
|
|
||||||
install() {
|
const sourceNode = template({
|
||||||
const sourceNode = this._template({
|
|
||||||
comment: this._comment,
|
comment: this._comment,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -30,7 +31,7 @@ class CommentFormControl {
|
||||||
|
|
||||||
formNode.addEventListener('submit', e => this._evtSaveClick(e));
|
formNode.addEventListener('submit', e => this._evtSaveClick(e));
|
||||||
|
|
||||||
if (this._settings.canCancel) {
|
if (this._canCancel) {
|
||||||
cancelButton
|
cancelButton
|
||||||
.addEventListener('click', e => this._evtCancelClick(e));
|
.addEventListener('click', e => this._evtCancelClick(e));
|
||||||
} else {
|
} else {
|
||||||
|
@ -43,7 +44,11 @@ class CommentFormControl {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
textareaNode.addEventListener('change', e => {
|
textareaNode.addEventListener('change', e => {
|
||||||
misc.enableExitConfirmation();
|
this.dispatchEvent(new CustomEvent('change', {
|
||||||
|
detail: {
|
||||||
|
target: this,
|
||||||
|
},
|
||||||
|
}));
|
||||||
this._growTextArea();
|
this._growTextArea();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -60,7 +65,6 @@ class CommentFormControl {
|
||||||
exitEditMode() {
|
exitEditMode() {
|
||||||
this._hostNode.classList.remove('editing');
|
this._hostNode.classList.remove('editing');
|
||||||
this._hostNode.querySelector('.tabs-wrapper').style.minHeight = null;
|
this._hostNode.querySelector('.tabs-wrapper').style.minHeight = null;
|
||||||
misc.disableExitConfirmation();
|
|
||||||
views.clearMessages(this._hostNode);
|
views.clearMessages(this._hostNode);
|
||||||
this.setText(this._comment.text);
|
this.setText(this._comment.text);
|
||||||
}
|
}
|
||||||
|
@ -97,11 +101,13 @@ class CommentFormControl {
|
||||||
|
|
||||||
_evtSaveClick(e) {
|
_evtSaveClick(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!this._settings.onSave) {
|
this.dispatchEvent(new CustomEvent('submit', {
|
||||||
throw 'No save handler';
|
detail: {
|
||||||
}
|
target: this,
|
||||||
this._settings.onSave(this._textareaNode.value)
|
comment: this._comment,
|
||||||
.then(() => { misc.disableExitConfirmation(); });
|
text: this._textareaNode.value,
|
||||||
|
},
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
_evtCancelClick(e) {
|
_evtCancelClick(e) {
|
||||||
|
@ -125,7 +131,7 @@ class CommentFormControl {
|
||||||
_growTextArea() {
|
_growTextArea() {
|
||||||
this._textareaNode.style.height =
|
this._textareaNode.style.height =
|
||||||
Math.max(
|
Math.max(
|
||||||
this._settings.minHeight || 0,
|
this._minHeight || 0,
|
||||||
this._textareaNode.scrollHeight) + 'px';
|
this._textareaNode.scrollHeight) + 'px';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,48 +1,58 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const api = require('../api.js');
|
const events = require('../events.js');
|
||||||
const views = require('../util/views.js');
|
const views = require('../util/views.js');
|
||||||
const CommentControl = require('../controls/comment_control.js');
|
const CommentControl = require('../controls/comment_control.js');
|
||||||
|
|
||||||
class CommentListControl {
|
const template = views.getTemplate('comment-list');
|
||||||
constructor(hostNode, comments) {
|
|
||||||
|
class CommentListControl extends events.EventTarget {
|
||||||
|
constructor(hostNode, comments, reversed) {
|
||||||
|
super();
|
||||||
this._hostNode = hostNode;
|
this._hostNode = hostNode;
|
||||||
this._comments = comments;
|
this._comments = comments;
|
||||||
this._template = views.getTemplate('comment-list');
|
this._commentIdToNode = {};
|
||||||
|
|
||||||
this.install();
|
comments.addEventListener('add', e => this._evtAdd(e));
|
||||||
|
comments.addEventListener('remove', e => this._evtRemove(e));
|
||||||
|
|
||||||
|
views.replaceContent(this._hostNode, template());
|
||||||
|
|
||||||
|
const commentList = Array.from(comments);
|
||||||
|
if (reversed) {
|
||||||
|
commentList.reverse();
|
||||||
|
}
|
||||||
|
for (let comment of commentList) {
|
||||||
|
this._installCommentNode(comment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
install() {
|
get _commentListNode() {
|
||||||
const sourceNode = this._template({
|
return this._hostNode.querySelector('ul');
|
||||||
comments: this._comments,
|
|
||||||
canListComments: api.hasPrivilege('comments:list'),
|
|
||||||
});
|
|
||||||
|
|
||||||
views.replaceContent(this._hostNode, sourceNode);
|
|
||||||
|
|
||||||
this._renderComments();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderComments() {
|
_installCommentNode(comment) {
|
||||||
if (!this._comments.length) {
|
const commentListItemNode = document.createElement('li');
|
||||||
return;
|
const commentControl = new CommentControl(
|
||||||
}
|
commentListItemNode, comment);
|
||||||
const commentList = new DocumentFragment();
|
events.proxyEvent(commentControl, this, 'change');
|
||||||
for (let comment of this._comments) {
|
events.proxyEvent(commentControl, this, 'score');
|
||||||
const commentListItemNode = document.createElement('li');
|
events.proxyEvent(commentControl, this, 'delete');
|
||||||
new CommentControl(commentListItemNode, comment, {
|
this._commentIdToNode[comment.id] = commentListItemNode;
|
||||||
onDelete: removedComment => {
|
this._commentListNode.appendChild(commentListItemNode);
|
||||||
for (let [index, comment] of this._comments.entries()) {
|
}
|
||||||
if (comment.id === removedComment.id) {
|
|
||||||
this._comments.splice(index, 1);
|
_uninstallCommentNode(comment) {
|
||||||
}
|
const commentListItemNode = this._commentIdToNode[comment.id];
|
||||||
}
|
commentListItemNode.parentNode.removeChild(commentListItemNode);
|
||||||
},
|
}
|
||||||
});
|
|
||||||
commentList.appendChild(commentListItemNode);
|
_evtAdd(e) {
|
||||||
}
|
this._installCommentNode(e.detail.comment);
|
||||||
views.replaceContent(this._hostNode.querySelector('ul'), commentList);
|
}
|
||||||
|
|
||||||
|
_evtRemove(e) {
|
||||||
|
this._uninstallCommentNode(e.detail.comment);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,20 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const events = require('../events.js');
|
||||||
const views = require('../util/views.js');
|
const views = require('../util/views.js');
|
||||||
|
|
||||||
class PostEditSidebarControl {
|
const template = views.getTemplate('post-edit-sidebar');
|
||||||
|
|
||||||
|
class PostEditSidebarControl extends events.EventTarget {
|
||||||
constructor(hostNode, post, postContentControl) {
|
constructor(hostNode, post, postContentControl) {
|
||||||
|
super();
|
||||||
this._hostNode = hostNode;
|
this._hostNode = hostNode;
|
||||||
this._post = post;
|
this._post = post;
|
||||||
this._postContentControl = postContentControl;
|
this._postContentControl = postContentControl;
|
||||||
this._template = views.getTemplate('post-edit-sidebar');
|
|
||||||
|
|
||||||
this.install();
|
views.replaceContent(this._hostNode, template({
|
||||||
}
|
|
||||||
|
|
||||||
install() {
|
|
||||||
const sourceNode = this._template({
|
|
||||||
post: this._post,
|
post: this._post,
|
||||||
});
|
}));
|
||||||
views.replaceContent(this._hostNode, sourceNode);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,93 +1,128 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const api = require('../api.js');
|
const api = require('../api.js');
|
||||||
|
const events = require('../events.js');
|
||||||
const tags = require('../tags.js');
|
const tags = require('../tags.js');
|
||||||
const views = require('../util/views.js');
|
const views = require('../util/views.js');
|
||||||
|
|
||||||
class PostReadonlySidebarControl {
|
const template = views.getTemplate('post-readonly-sidebar');
|
||||||
|
const scoreTemplate = views.getTemplate('score');
|
||||||
|
const favTemplate = views.getTemplate('fav');
|
||||||
|
|
||||||
|
class PostReadonlySidebarControl extends events.EventTarget {
|
||||||
constructor(hostNode, post, postContentControl) {
|
constructor(hostNode, post, postContentControl) {
|
||||||
|
super();
|
||||||
this._hostNode = hostNode;
|
this._hostNode = hostNode;
|
||||||
this._post = post;
|
this._post = post;
|
||||||
this._postContentControl = postContentControl;
|
this._postContentControl = postContentControl;
|
||||||
this._template = views.getTemplate('post-readonly-sidebar');
|
|
||||||
this._scoreTemplate = views.getTemplate('score');
|
|
||||||
this._favTemplate = views.getTemplate('fav');
|
|
||||||
|
|
||||||
this.install();
|
post.addEventListener('changeFavorite', e => this._evtChangeFav(e));
|
||||||
}
|
post.addEventListener('changeScore', e => this._evtChangeScore(e));
|
||||||
|
|
||||||
install() {
|
views.replaceContent(this._hostNode, template({
|
||||||
const sourceNode = this._template({
|
|
||||||
post: this._post,
|
post: this._post,
|
||||||
getTagCategory: this._getTagCategory,
|
getTagCategory: this._getTagCategory,
|
||||||
getTagUsages: this._getTagUsages,
|
getTagUsages: this._getTagUsages,
|
||||||
canListPosts: api.hasPrivilege('posts:list'),
|
canListPosts: api.hasPrivilege('posts:list'),
|
||||||
canViewTags: api.hasPrivilege('tags:view'),
|
canViewTags: api.hasPrivilege('tags:view'),
|
||||||
});
|
}));
|
||||||
|
|
||||||
views.replaceContent(
|
this._installFav();
|
||||||
sourceNode.querySelector('.score-container'),
|
this._installScore();
|
||||||
this._scoreTemplate({
|
this._installFitButtons();
|
||||||
score: this._post.score,
|
this._syncFitButton();
|
||||||
ownScore: this._post.ownScore,
|
}
|
||||||
canScore: api.hasPrivilege('posts:score'),
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
get _scoreContainerNode() {
|
||||||
|
return this._hostNode.querySelector('.score-container');
|
||||||
|
}
|
||||||
|
|
||||||
|
get _favContainerNode() {
|
||||||
|
return this._hostNode.querySelector('.fav-container');
|
||||||
|
}
|
||||||
|
|
||||||
|
get _upvoteButtonNode() {
|
||||||
|
return this._hostNode.querySelector('.upvote');
|
||||||
|
}
|
||||||
|
|
||||||
|
get _downvoteButtonNode() {
|
||||||
|
return this._hostNode.querySelector('.downvote');
|
||||||
|
}
|
||||||
|
|
||||||
|
get _addFavButtonNode() {
|
||||||
|
return this._hostNode.querySelector('.add-favorite');
|
||||||
|
}
|
||||||
|
|
||||||
|
get _remFavButtonNode() {
|
||||||
|
return this._hostNode.querySelector('.remove-favorite');
|
||||||
|
}
|
||||||
|
|
||||||
|
get _fitBothButtonNode() {
|
||||||
|
return this._hostNode.querySelector('.fit-both');
|
||||||
|
}
|
||||||
|
|
||||||
|
get _fitOriginalButtonNode() {
|
||||||
|
return this._hostNode.querySelector('.fit-original');
|
||||||
|
}
|
||||||
|
|
||||||
|
get _fitWidthButtonNode() {
|
||||||
|
return this._hostNode.querySelector('.fit-width');
|
||||||
|
}
|
||||||
|
|
||||||
|
get _fitHeightButtonNode() {
|
||||||
|
return this._hostNode.querySelector('.fit-height');
|
||||||
|
}
|
||||||
|
|
||||||
|
_installFitButtons() {
|
||||||
|
this._fitBothButtonNode.addEventListener(
|
||||||
|
'click', this._eventZoomProxy(
|
||||||
|
() => this._postContentControl.fitBoth()));
|
||||||
|
this._fitOriginalButtonNode.addEventListener(
|
||||||
|
'click', this._eventZoomProxy(
|
||||||
|
() => this._postContentControl.fitOriginal()));
|
||||||
|
this._fitWidthButtonNode.addEventListener(
|
||||||
|
'click', this._eventZoomProxy(
|
||||||
|
() => this._postContentControl.fitWidth()));
|
||||||
|
this._fitHeightButtonNode.addEventListener(
|
||||||
|
'click', this._eventZoomProxy(
|
||||||
|
() => this._postContentControl.fitHeight()));
|
||||||
|
}
|
||||||
|
|
||||||
|
_installFav() {
|
||||||
views.replaceContent(
|
views.replaceContent(
|
||||||
sourceNode.querySelector('.fav-container'),
|
this._favContainerNode,
|
||||||
this._favTemplate({
|
favTemplate({
|
||||||
favoriteCount: this._post.favoriteCount,
|
favoriteCount: this._post.favoriteCount,
|
||||||
ownFavorite: this._post.ownFavorite,
|
ownFavorite: this._post.ownFavorite,
|
||||||
canFavorite: api.hasPrivilege('posts:favorite'),
|
canFavorite: api.hasPrivilege('posts:favorite'),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const upvoteButton = sourceNode.querySelector('.upvote');
|
if (this._addFavButtonNode) {
|
||||||
const downvoteButton = sourceNode.querySelector('.downvote');
|
this._addFavButtonNode.addEventListener(
|
||||||
const addFavButton = sourceNode.querySelector('.add-favorite');
|
'click', e => this._evtAddToFavoritesClick(e));
|
||||||
const remFavButton = sourceNode.querySelector('.remove-favorite');
|
|
||||||
const fitBothButton = sourceNode.querySelector('.fit-both');
|
|
||||||
const fitOriginalButton = sourceNode.querySelector('.fit-original');
|
|
||||||
const fitWidthButton = sourceNode.querySelector('.fit-width');
|
|
||||||
const fitHeightButton = sourceNode.querySelector('.fit-height');
|
|
||||||
|
|
||||||
if (upvoteButton) {
|
|
||||||
upvoteButton.addEventListener(
|
|
||||||
'click', this._eventRequestProxy(
|
|
||||||
() => this._setScore(this._post.ownScore === 1 ? 0 : 1)));
|
|
||||||
}
|
}
|
||||||
if (downvoteButton) {
|
if (this._remFavButtonNode) {
|
||||||
downvoteButton.addEventListener(
|
this._remFavButtonNode.addEventListener(
|
||||||
'click', this._eventRequestProxy(
|
'click', e => this._evtRemoveFromFavoritesClick(e));
|
||||||
() => this._setScore(this._post.ownScore === -1 ? 0 : -1)));
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (addFavButton) {
|
_installScore() {
|
||||||
addFavButton.addEventListener(
|
views.replaceContent(
|
||||||
'click', this._eventRequestProxy(
|
this._scoreContainerNode,
|
||||||
() => this._addToFavorites()));
|
scoreTemplate({
|
||||||
|
score: this._post.score,
|
||||||
|
ownScore: this._post.ownScore,
|
||||||
|
canScore: api.hasPrivilege('posts:score'),
|
||||||
|
}));
|
||||||
|
if (this._upvoteButtonNode) {
|
||||||
|
this._upvoteButtonNode.addEventListener(
|
||||||
|
'click', e => this._evtScoreClick(e, 1));
|
||||||
}
|
}
|
||||||
if (remFavButton) {
|
if (this._downvoteButtonNode) {
|
||||||
remFavButton.addEventListener(
|
this._downvoteButtonNode.addEventListener(
|
||||||
'click', this._eventRequestProxy(
|
'click', e => this._evtScoreClick(e, -1));
|
||||||
() => this._removeFromFavorites()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fitBothButton.addEventListener(
|
|
||||||
'click', this._eventZoomProxy(
|
|
||||||
() => this._postContentControl.fitBoth()));
|
|
||||||
fitOriginalButton.addEventListener(
|
|
||||||
'click', this._eventZoomProxy(
|
|
||||||
() => this._postContentControl.fitOriginal()));
|
|
||||||
fitWidthButton.addEventListener(
|
|
||||||
'click', this._eventZoomProxy(
|
|
||||||
() => this._postContentControl.fitWidth()));
|
|
||||||
fitHeightButton.addEventListener(
|
|
||||||
'click', this._eventZoomProxy(
|
|
||||||
() => this._postContentControl.fitHeight()));
|
|
||||||
|
|
||||||
views.replaceContent(this._hostNode, sourceNode);
|
|
||||||
|
|
||||||
this._syncFitButton();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_eventZoomProxy(func) {
|
_eventZoomProxy(func) {
|
||||||
|
@ -99,15 +134,6 @@ class PostReadonlySidebarControl {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_eventRequestProxy(promise) {
|
|
||||||
return e => {
|
|
||||||
e.preventDefault();
|
|
||||||
promise().then(() => {
|
|
||||||
this.install();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_syncFitButton() {
|
_syncFitButton() {
|
||||||
const funcToClassName = {};
|
const funcToClassName = {};
|
||||||
funcToClassName[this._postContentControl.fitBoth] = 'fit-both';
|
funcToClassName[this._postContentControl.fitBoth] = 'fit-both';
|
||||||
|
@ -134,37 +160,40 @@ class PostReadonlySidebarControl {
|
||||||
return tag ? tag.category : 'unknown';
|
return tag ? tag.category : 'unknown';
|
||||||
}
|
}
|
||||||
|
|
||||||
_setScore(score) {
|
_evtAddToFavoritesClick(e) {
|
||||||
return this._requestAndRefresh(
|
e.preventDefault();
|
||||||
() => api.put('/post/' + this._post.id + '/score', {score: score}));
|
this.dispatchEvent(new CustomEvent('favorite', {
|
||||||
|
detail: {
|
||||||
|
post: this._post,
|
||||||
|
},
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
_addToFavorites() {
|
_evtRemoveFromFavoritesClick(e) {
|
||||||
return this._requestAndRefresh(
|
e.preventDefault();
|
||||||
() => api.post('/post/' + this._post.id + '/favorite'));
|
this.dispatchEvent(new CustomEvent('unfavorite', {
|
||||||
|
detail: {
|
||||||
|
post: this._post,
|
||||||
|
},
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
_removeFromFavorites() {
|
_evtScoreClick(e, score) {
|
||||||
return this._requestAndRefresh(
|
e.preventDefault();
|
||||||
() => api.delete('/post/' + this._post.id + '/favorite'));
|
this.dispatchEvent(new CustomEvent('score', {
|
||||||
|
detail: {
|
||||||
|
post: this._post,
|
||||||
|
score: this._post.ownScore === score ? 0 : score,
|
||||||
|
},
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
_requestAndRefresh(requestPromise) {
|
_evtChangeFav(e) {
|
||||||
return new Promise((resolve, reject) => {
|
this._installFav();
|
||||||
requestPromise()
|
}
|
||||||
.then(
|
|
||||||
response => { return api.get('/post/' + this._post.id); },
|
_evtChangeScore(e) {
|
||||||
response => { return Promise.reject(response); })
|
this._installScore();
|
||||||
.then(
|
|
||||||
response => {
|
|
||||||
this._post = response;
|
|
||||||
resolve();
|
|
||||||
},
|
|
||||||
response => {
|
|
||||||
reject();
|
|
||||||
window.alert(response.description);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -13,10 +13,22 @@ class EventTarget {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function proxyEvent(source, target, sourceEventType, targetEventType) {
|
||||||
|
if (!targetEventType) {
|
||||||
|
targetEventType = sourceEventType;
|
||||||
|
}
|
||||||
|
source.addEventListener(sourceEventType, e => {
|
||||||
|
target.dispatchEvent(new CustomEvent(targetEventType, {
|
||||||
|
detail: e.detail,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Success: 'success',
|
Success: 'success',
|
||||||
Error: 'error',
|
Error: 'error',
|
||||||
Info: 'info',
|
Info: 'info',
|
||||||
|
|
||||||
|
proxyEvent: proxyEvent,
|
||||||
EventTarget: EventTarget,
|
EventTarget: EventTarget,
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const api = require('../api.js');
|
||||||
|
const events = require('../events.js');
|
||||||
|
|
||||||
|
class Comment extends events.EventTarget {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.commentList = null;
|
||||||
|
|
||||||
|
this._id = null;
|
||||||
|
this._postId = null;
|
||||||
|
this._text = null;
|
||||||
|
this._user = null;
|
||||||
|
this._creationTime = null;
|
||||||
|
this._lastEditTime = null;
|
||||||
|
this._score = null;
|
||||||
|
this._ownScore = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static create(postId) {
|
||||||
|
const comment = new Comment();
|
||||||
|
comment._postId = postId;
|
||||||
|
return comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromResponse(response) {
|
||||||
|
const comment = new Comment();
|
||||||
|
comment._updateFromResponse(response);
|
||||||
|
return comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
get id() { return this._id; }
|
||||||
|
get postId() { return this._postId; }
|
||||||
|
get text() { return this._text; }
|
||||||
|
get user() { return this._user; }
|
||||||
|
get creationTime() { return this._creationTime; }
|
||||||
|
get lastEditTime() { return this._lastEditTime; }
|
||||||
|
get score() { return this._score; }
|
||||||
|
get ownScore() { return this._ownScore; }
|
||||||
|
|
||||||
|
set text(value) { this._text = value; }
|
||||||
|
|
||||||
|
save() {
|
||||||
|
let promise = null;
|
||||||
|
if (this._id) {
|
||||||
|
promise = api.put(
|
||||||
|
'/comment/' + this._id,
|
||||||
|
{
|
||||||
|
text: this._text,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
promise = api.post(
|
||||||
|
'/comments',
|
||||||
|
{
|
||||||
|
text: this._text,
|
||||||
|
postId: this._postId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return promise.then(response => {
|
||||||
|
this._updateFromResponse(response);
|
||||||
|
this.dispatchEvent(new CustomEvent('change', {
|
||||||
|
details: {
|
||||||
|
comment: this,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
return Promise.resolve();
|
||||||
|
}, response => {
|
||||||
|
return Promise.reject(response.description);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
delete() {
|
||||||
|
return api.delete('/comment/' + this._id)
|
||||||
|
.then(response => {
|
||||||
|
if (this.commentList) {
|
||||||
|
this.commentList.remove(this);
|
||||||
|
}
|
||||||
|
this.dispatchEvent(new CustomEvent('delete', {
|
||||||
|
details: {
|
||||||
|
comment: this,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
return Promise.resolve();
|
||||||
|
}, response => {
|
||||||
|
return Promise.reject(response.description);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setScore(score) {
|
||||||
|
return api.put('/comment/' + this._id + '/score', {score: score})
|
||||||
|
.then(response => {
|
||||||
|
this._updateFromResponse(response);
|
||||||
|
this.dispatchEvent(new CustomEvent('changeScore', {
|
||||||
|
details: {
|
||||||
|
comment: this,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
return Promise.resolve();
|
||||||
|
}, response => {
|
||||||
|
return Promise.reject(response.description);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateFromResponse(response) {
|
||||||
|
this._id = response.id;
|
||||||
|
this._postId = response.postId;
|
||||||
|
this._text = response.text;
|
||||||
|
this._user = response.user;
|
||||||
|
this._creationTime = response.creationTime;
|
||||||
|
this._lastEditTime = response.lastEditTime;
|
||||||
|
this._score = parseInt(response.score);
|
||||||
|
this._ownScore = parseInt(response.ownScore);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Comment;
|
|
@ -0,0 +1,59 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const events = require('../events.js');
|
||||||
|
const Comment = require('./comment.js');
|
||||||
|
|
||||||
|
class CommentList extends events.EventTarget {
|
||||||
|
constructor(comments) {
|
||||||
|
super();
|
||||||
|
this._list = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromResponse(commentsResponse) {
|
||||||
|
const commentList = new CommentList();
|
||||||
|
for (let commentResponse of commentsResponse) {
|
||||||
|
const comment = Comment.fromResponse(commentResponse);
|
||||||
|
comment.commentList = commentList;
|
||||||
|
commentList._list.push(comment);
|
||||||
|
}
|
||||||
|
return commentList;
|
||||||
|
}
|
||||||
|
|
||||||
|
get comments() {
|
||||||
|
return [...this._list];
|
||||||
|
}
|
||||||
|
|
||||||
|
add(comment) {
|
||||||
|
comment.commentList = this;
|
||||||
|
this._list.push(comment);
|
||||||
|
this.dispatchEvent(new CustomEvent('add', {
|
||||||
|
detail: {
|
||||||
|
comment: comment,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(commentToRemove) {
|
||||||
|
for (let [index, comment] of this._list.entries()) {
|
||||||
|
if (comment.id === commentToRemove.id) {
|
||||||
|
this._list.splice(index, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.dispatchEvent(new CustomEvent('remove', {
|
||||||
|
detail: {
|
||||||
|
comment: commentToRemove,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
get length() {
|
||||||
|
return this._list.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Symbol.iterator]() {
|
||||||
|
return this._list[Symbol.iterator]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CommentList;
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
const api = require('../api.js');
|
const api = require('../api.js');
|
||||||
const events = require('../events.js');
|
const events = require('../events.js');
|
||||||
|
const CommentList = require('./comment_list.js');
|
||||||
|
|
||||||
class Post extends events.EventTarget {
|
class Post extends events.EventTarget {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -29,7 +30,22 @@ class Post extends events.EventTarget {
|
||||||
this._ownFavorite = null;
|
this._ownFavorite = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// encapsulation - don't let set these casually
|
static fromResponse(response) {
|
||||||
|
const post = new Post();
|
||||||
|
post._updateFromResponse(response);
|
||||||
|
return post;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get(id) {
|
||||||
|
return api.get('/post/' + id)
|
||||||
|
.then(response => {
|
||||||
|
const post = Post.fromResponse(response);
|
||||||
|
return Promise.resolve(post);
|
||||||
|
}, response => {
|
||||||
|
return Promise.reject(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
get id() { return this._id; }
|
get id() { return this._id; }
|
||||||
get type() { return this._type; }
|
get type() { return this._type; }
|
||||||
get mimeType() { return this._mimeType; }
|
get mimeType() { return this._mimeType; }
|
||||||
|
@ -52,37 +68,97 @@ class Post extends events.EventTarget {
|
||||||
get ownFavorite() { return this._ownFavorite; }
|
get ownFavorite() { return this._ownFavorite; }
|
||||||
get ownScore() { return this._ownScore; }
|
get ownScore() { return this._ownScore; }
|
||||||
|
|
||||||
static get(id) {
|
setScore(score) {
|
||||||
return new Promise((resolve, reject) => {
|
return api.put('/post/' + this._id + '/score', {score: score})
|
||||||
api.get('/post/' + id)
|
.then(response => {
|
||||||
.then(response => {
|
const prevFavorite = this._ownFavorite;
|
||||||
const post = new Post();
|
this._updateFromResponse(response);
|
||||||
post._id = response.id;
|
if (this._ownFavorite !== prevFavorite) {
|
||||||
post._type = response.type;
|
this.dispatchEvent(new CustomEvent('changeFavorite', {
|
||||||
post._mimeType = response.mimeType;
|
details: {
|
||||||
post._creationTime = response.creationTime;
|
post: this,
|
||||||
post._user = response.user;
|
},
|
||||||
post._safety = response.safety;
|
}));
|
||||||
post._contentUrl = response.contentUrl;
|
}
|
||||||
post._thumbnailUrl = response.thumbnailUrl;
|
this.dispatchEvent(new CustomEvent('changeScore', {
|
||||||
post._canvasWidth = response.canvasWidth;
|
details: {
|
||||||
post._canvasHeight = response.canvasHeight;
|
post: this,
|
||||||
post._fileSize = response.fileSize;
|
},
|
||||||
|
}));
|
||||||
|
return Promise.resolve();
|
||||||
|
}, response => {
|
||||||
|
return Promise.reject(response.description);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
post._tags = response.tags;
|
addToFavorites() {
|
||||||
post._notes = response.notes;
|
return api.post('/post/' + this.id + '/favorite')
|
||||||
post._comments = response.comments;
|
.then(response => {
|
||||||
post._relations = response.relations;
|
const prevScore = this._ownScore;
|
||||||
|
this._updateFromResponse(response);
|
||||||
|
if (this._ownScore !== prevScore) {
|
||||||
|
this.dispatchEvent(new CustomEvent('changeScore', {
|
||||||
|
details: {
|
||||||
|
post: this,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
this.dispatchEvent(new CustomEvent('changeFavorite', {
|
||||||
|
details: {
|
||||||
|
post: this,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
return Promise.resolve();
|
||||||
|
}, response => {
|
||||||
|
return Promise.reject(response.description);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
post._score = response.score;
|
removeFromFavorites() {
|
||||||
post._favoriteCount = response.favoriteCount;
|
return api.delete('/post/' + this.id + '/favorite')
|
||||||
post._ownScore = response.ownScore;
|
.then(response => {
|
||||||
post._ownFavorite = response.ownFavorite;
|
const prevScore = this._ownScore;
|
||||||
resolve(post);
|
this._updateFromResponse(response);
|
||||||
}, response => {
|
if (this._ownScore !== prevScore) {
|
||||||
reject(response);
|
this.dispatchEvent(new CustomEvent('changeScore', {
|
||||||
});
|
details: {
|
||||||
});
|
post: this,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
this.dispatchEvent(new CustomEvent('changeFavorite', {
|
||||||
|
details: {
|
||||||
|
post: this,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
return Promise.resolve();
|
||||||
|
}, response => {
|
||||||
|
return Promise.reject(response.description);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateFromResponse(response) {
|
||||||
|
this._id = response.id;
|
||||||
|
this._type = response.type;
|
||||||
|
this._mimeType = response.mimeType;
|
||||||
|
this._creationTime = response.creationTime;
|
||||||
|
this._user = response.user;
|
||||||
|
this._safety = response.safety;
|
||||||
|
this._contentUrl = response.contentUrl;
|
||||||
|
this._thumbnailUrl = response.thumbnailUrl;
|
||||||
|
this._canvasWidth = response.canvasWidth;
|
||||||
|
this._canvasHeight = response.canvasHeight;
|
||||||
|
this._fileSize = response.fileSize;
|
||||||
|
|
||||||
|
this._tags = response.tags;
|
||||||
|
this._notes = response.notes;
|
||||||
|
this._comments = CommentList.fromResponse(response.comments);
|
||||||
|
this._relations = response.relations;
|
||||||
|
|
||||||
|
this._score = response.score;
|
||||||
|
this._favoriteCount = response.favoriteCount;
|
||||||
|
this._ownScore = response.ownScore;
|
||||||
|
this._ownFavorite = response.ownFavorite;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const events = require('../events.js');
|
||||||
|
const Post = require('./post.js');
|
||||||
|
|
||||||
|
class PostList extends events.EventTarget {
|
||||||
|
constructor(posts) {
|
||||||
|
super();
|
||||||
|
this._list = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromResponse(postsResponse) {
|
||||||
|
const postList = new PostList();
|
||||||
|
for (let postResponse of postsResponse) {
|
||||||
|
postList._list.push(Post.fromResponse(postResponse));
|
||||||
|
}
|
||||||
|
return postList;
|
||||||
|
}
|
||||||
|
|
||||||
|
get posts() {
|
||||||
|
return [...this._list];
|
||||||
|
}
|
||||||
|
|
||||||
|
get length() {
|
||||||
|
return this._list.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Symbol.iterator]() {
|
||||||
|
return this._list[Symbol.iterator]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = PostList;
|
|
@ -1,24 +1,27 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const events = require('../events.js');
|
||||||
const views = require('../util/views.js');
|
const views = require('../util/views.js');
|
||||||
const CommentListControl = require('../controls/comment_list_control.js');
|
const CommentListControl = require('../controls/comment_list_control.js');
|
||||||
|
|
||||||
const template = views.getTemplate('comments-page');
|
const template = views.getTemplate('comments-page');
|
||||||
|
|
||||||
class CommentsPageView {
|
class CommentsPageView extends events.EventTarget {
|
||||||
constructor(ctx) {
|
constructor(ctx) {
|
||||||
|
super();
|
||||||
this._hostNode = ctx.hostNode;
|
this._hostNode = ctx.hostNode;
|
||||||
this._controls = [];
|
|
||||||
|
|
||||||
const sourceNode = template(ctx);
|
const sourceNode = template(ctx);
|
||||||
|
|
||||||
for (let post of ctx.results) {
|
for (let post of ctx.results) {
|
||||||
post.comments.sort((a, b) => { return b.id - a.id; });
|
const commentListControl = new CommentListControl(
|
||||||
this._controls.push(
|
sourceNode.querySelector(
|
||||||
new CommentListControl(
|
`.comments-container[data-for="${post.id}"]`),
|
||||||
sourceNode.querySelector(
|
post.comments,
|
||||||
`.comments-container[data-for="${post.id}"]`),
|
true);
|
||||||
post.comments));
|
events.proxyEvent(commentListControl, this, 'change');
|
||||||
|
events.proxyEvent(commentListControl, this, 'score');
|
||||||
|
events.proxyEvent(commentListControl, this, 'delete');
|
||||||
}
|
}
|
||||||
|
|
||||||
views.replaceContent(this._hostNode, sourceNode);
|
views.replaceContent(this._hostNode, sourceNode);
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const api = require('../api.js');
|
|
||||||
const router = require('../router.js');
|
const router = require('../router.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');
|
||||||
|
@ -52,8 +51,8 @@ class PostView {
|
||||||
ctx.post);
|
ctx.post);
|
||||||
|
|
||||||
this._installSidebar(ctx);
|
this._installSidebar(ctx);
|
||||||
this._installCommentForm(ctx);
|
this._installCommentForm();
|
||||||
this._installComments(ctx);
|
this._installComments(ctx.post.comments);
|
||||||
|
|
||||||
keyboard.bind('e', () => {
|
keyboard.bind('e', () => {
|
||||||
if (ctx.editMode) {
|
if (ctx.editMode) {
|
||||||
|
@ -79,49 +78,35 @@ class PostView {
|
||||||
'#content-holder .sidebar-container');
|
'#content-holder .sidebar-container');
|
||||||
|
|
||||||
if (ctx.editMode) {
|
if (ctx.editMode) {
|
||||||
new PostEditSidebarControl(
|
this.sidebarControl = new PostEditSidebarControl(
|
||||||
sidebarContainerNode, ctx.post, this._postContentControl);
|
sidebarContainerNode, ctx.post, this._postContentControl);
|
||||||
} else {
|
} else {
|
||||||
new PostReadonlySidebarControl(
|
this.sidebarControl = new PostReadonlySidebarControl(
|
||||||
sidebarContainerNode, ctx.post, this._postContentControl);
|
sidebarContainerNode, ctx.post, this._postContentControl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_installCommentForm(ctx) {
|
_installCommentForm() {
|
||||||
const commentFormContainer = document.querySelector(
|
const commentFormContainer = document.querySelector(
|
||||||
'#content-holder .comment-form-container');
|
'#content-holder .comment-form-container');
|
||||||
if (!commentFormContainer) {
|
if (!commentFormContainer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._formControl = new CommentFormControl(
|
this.commentFormControl = new CommentFormControl(
|
||||||
commentFormContainer,
|
commentFormContainer, null, false, 150);
|
||||||
null,
|
this.commentFormControl.enterEditMode();
|
||||||
{
|
|
||||||
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) {
|
_installComments(comments) {
|
||||||
const commentsContainerNode = document.querySelector(
|
const commentsContainerNode = document.querySelector(
|
||||||
'#content-holder .comments-container');
|
'#content-holder .comments-container');
|
||||||
if (commentsContainerNode) {
|
if (!commentsContainerNode) {
|
||||||
new CommentListControl(commentsContainerNode, ctx.post.comments);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.commentListControl = new CommentListControl(
|
||||||
|
commentsContainerNode, comments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue