1
0
mirror of https://github.com/BookStackApp/BookStack.git synced 2024-11-23 19:32:29 +01:00

Added loading icons, Added comment activity

This commit is contained in:
Dan Brown 2017-09-09 17:06:30 +01:00
parent 41f56e659d
commit 0275d2ad58
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
10 changed files with 116 additions and 69 deletions

View File

@ -1,5 +1,6 @@
<?php namespace BookStack\Http\Controllers;
use Activity;
use BookStack\Repos\CommentRepo;
use BookStack\Repos\EntityRepo;
use Illuminate\Database\Eloquent\ModelNotFoundException;
@ -51,7 +52,8 @@ class CommentController extends Controller
// Create a new comment.
$this->checkPermission('comment-create-all');
$comment = $this->commentRepo->create($page, $request->all());
$comment = $this->commentRepo->create($page, $request->only(['html', 'text', 'parent_id']));
Activity::add($page, 'commented_on', $page->book->id);
return view('comments/comment', ['comment' => $comment]);
}
@ -72,7 +74,7 @@ class CommentController extends Controller
$this->checkOwnablePermission('page-view', $comment->entity);
$this->checkOwnablePermission('comment-update', $comment);
$comment = $this->commentRepo->update($comment, $request->all());
$comment = $this->commentRepo->update($comment, $request->only(['html', 'text']));
return view('comments/comment', ['comment' => $comment]);
}

View File

@ -1,31 +1,31 @@
const MarkdownIt = require("markdown-it");
const md = new MarkdownIt({ html: true });
const md = new MarkdownIt({ html: false });
class PageComments {
constructor(elem) {
this.elem = elem;
this.pageId = Number(elem.getAttribute('page-id'));
this.formContainer = elem.querySelector('[comment-form-container]');
this.form = this.formContainer.querySelector('form');
this.formInput = this.form.querySelector('textarea');
this.container = elem.querySelector('[comment-container]');
// TODO - Handle elem usage when no permissions
this.form.addEventListener('submit', this.saveComment.bind(this));
this.elem.addEventListener('click', this.handleAction.bind(this));
this.elem.addEventListener('submit', this.updateComment.bind(this));
this.editingComment = null;
this.parentId = null;
this.container = elem.querySelector('[comment-container]');
this.formContainer = elem.querySelector('[comment-form-container]');
if (this.formContainer) {
this.form = this.formContainer.querySelector('form');
this.formInput = this.form.querySelector('textarea');
this.form.addEventListener('submit', this.saveComment.bind(this));
}
this.elem.addEventListener('click', this.handleAction.bind(this));
this.elem.addEventListener('submit', this.updateComment.bind(this));
}
handleAction(event) {
let actionElem = event.target.closest('[action]');
if (event.target.matches('a[href^="#"]')) {
let id = event.target.href.split('#')[1];
console.log(document.querySelector('#' + id));
window.scrollAndHighlight(document.querySelector('#' + id));
}
if (actionElem === null) return;
@ -52,6 +52,9 @@ class PageComments {
if (this.editingComment) this.closeUpdateForm();
commentElem.querySelector('[comment-content]').style.display = 'none';
commentElem.querySelector('[comment-edit-container]').style.display = 'block';
let textArea = commentElem.querySelector('[comment-edit-container] textarea');
let lineCount = textArea.value.split('\n').length;
textArea.style.height = (lineCount * 20) + 'px';
this.editingComment = commentElem;
}
@ -64,7 +67,7 @@ class PageComments {
html: md.render(text),
parent_id: this.parentId || null,
};
// TODO - Loading indicator
this.showLoading(form);
let commentId = this.editingComment.getAttribute('comment');
window.$http.put(window.baseUrl(`/ajax/comment/${commentId}`), reqData).then(resp => {
let newComment = document.createElement('div');
@ -74,12 +77,13 @@ class PageComments {
window.components.init(this.editingComment);
this.closeUpdateForm();
this.editingComment = null;
this.hideLoading(form);
});
}
deleteComment(commentElem) {
let id = commentElem.getAttribute('comment');
// TODO - Loading indicator
this.showLoading(commentElem.querySelector('[comment-content]'));
window.$http.delete(window.baseUrl(`/ajax/comment/${id}`)).then(resp => {
commentElem.parentNode.removeChild(commentElem);
window.$events.emit('success', window.trans('entities.comment_deleted_success'));
@ -96,7 +100,7 @@ class PageComments {
html: md.render(text),
parent_id: this.parentId || null,
};
// TODO - Loading indicator
this.showLoading(this.form);
window.$http.post(window.baseUrl(`/ajax/page/${this.pageId}/comment`), reqData).then(resp => {
let newComment = document.createElement('div');
newComment.innerHTML = resp.data;
@ -119,6 +123,7 @@ class PageComments {
this.formContainer.appendChild(this.form);
this.hideForm();
this.removeReplyTo();
this.hideLoading(this.form);
}
showForm() {
@ -149,9 +154,22 @@ class PageComments {
this.elem.querySelector('[comment-form-reply-to]').style.display = 'none';
}
showLoading(formElem) {
let groups = formElem.querySelectorAll('.form-group');
for (let i = 0, len = groups.length; i < len; i++) {
groups[i].style.display = 'none';
}
formElem.querySelector('.form-group.loading').style.display = 'block';
}
hideLoading(formElem) {
let groups = formElem.querySelectorAll('.form-group');
for (let i = 0, len = groups.length; i < len; i++) {
groups[i].style.display = 'block';
}
formElem.querySelector('.form-group.loading').style.display = 'none';
}
}
// TODO - Go to comment if url param set
module.exports = PageComments;

View File

@ -1,38 +0,0 @@
.comment-box {
border: 1px solid #DDD;
margin-bottom: $-s;
border-radius: 3px;
.content {
padding: $-s;
}
.content p {
margin-bottom: 1em;
}
.reply-row {
padding: $-xs $-s;
}
}
.comment-box .header {
padding: $-xs $-s;
background-color: #f8f8f8;
border-bottom: 1px solid #DDD;
.meta {
img, a, span {
display: inline-block;
vertical-align: top;
}
a, span {
padding: $-xxs 0 $-xxs 0;
line-height: 1.6;
}
a { color: #666; }
span {
color: #888;
padding-left: $-xxs;
}
}
.text-muted {
color: #999;
}
}

View File

@ -540,4 +540,45 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
margin-right: $-xs;
text-decoration: underline;
}
}
.comment-box {
border: 1px solid #DDD;
margin-bottom: $-s;
border-radius: 3px;
.content {
padding: $-s;
font-size: 0.666em;
}
.content p {
font-size: $fs-m;
margin: .5em 0;
}
.reply-row {
padding: $-xs $-s;
}
}
.comment-box .header {
padding: $-xs $-s;
background-color: #f8f8f8;
border-bottom: 1px solid #DDD;
.meta {
img, a, span {
display: inline-block;
vertical-align: top;
}
a, span {
padding: $-xxs 0 $-xxs 0;
line-height: 1.6;
}
a { color: #666; }
span {
color: #888;
padding-left: $-xxs;
}
}
.text-muted {
color: #999;
}
}

View File

@ -15,7 +15,6 @@
@import "header";
@import "lists";
@import "pages";
@import "comments";
[v-cloak] {
display: none; opacity: 0;
@ -69,7 +68,6 @@ $loadingSize: 10px;
.loading-container {
position: relative;
display: block;
height: $loadingSize;
margin: $-xl auto;
> div {
width: $loadingSize;
@ -77,7 +75,8 @@ $loadingSize: 10px;
border-radius: $loadingSize;
display: inline-block;
vertical-align: top;
transform: translate3d(0, 0, 0);
transform: translate3d(-10px, 0, 0);
margin-top: $-xs;
animation-name: loadingBob;
animation-duration: 1.4s;
animation-iteration-count: infinite;
@ -91,11 +90,17 @@ $loadingSize: 10px;
background-color: $color-book;
animation-delay: 0s;
}
> div:last-child {
> div:last-of-type {
left: $loadingSize+$-xs;
background-color: $color-chapter;
animation-delay: 0.6s;
}
> span {
margin-left: $-s;
font-style: italic;
color: #888;
vertical-align: top;
}
}

View File

@ -37,4 +37,6 @@ return [
'book_sort' => 'sorted book',
'book_sort_notification' => 'Book Successfully Re-sorted',
// Other
'commented_on' => 'commented on',
];

View File

@ -245,6 +245,8 @@ return [
'comment_placeholder' => 'Leave a comment here',
'comment_count' => '{0} No Comments|{1} 1 Comment|[2,*] :count Comments',
'comment_save' => 'Save Comment',
'comment_saving' => 'Saving comment...',
'comment_deleting' => 'Deleting comment...',
'comment_new' => 'New Comment',
'comment_created' => 'commented :createDiff',
'comment_updated' => 'Updated :updateDiff by :username',

View File

@ -1,4 +1,4 @@
<div class="comment-box" comment="{{ $comment->id }}" local-id="{{$comment->local_id}}" parent-id="{{$comment->parent_id || ''}}" id="comment{{$comment->local_id}}">
<div class="comment-box" comment="{{ $comment->id }}" local-id="{{$comment->local_id}}" parent-id="{{$comment->parent_id}}" id="comment{{$comment->local_id}}">
<div class="header">
<div class="float right actions">
@ -23,17 +23,20 @@
<div class="meta">
<a href="#comment{{$comment->local_id}}" class="text-muted">#{{$comment->local_id}}</a>
&nbsp;&nbsp;
<img width="50" src="{{ $comment->createdBy->getAvatar(50) }}" class="avatar" alt="{{ $comment->createdBy->name }}">
&nbsp;
<a href="{{ $comment->createdBy->getProfileUrl() }}">{{ $comment->createdBy->name }}</a>
{{--TODO - Account for deleted user--}}
@if ($comment->createdBy)
<img width="50" src="{{ $comment->createdBy->getAvatar(50) }}" class="avatar" alt="{{ $comment->createdBy->name }}">
&nbsp;
<a href="{{ $comment->createdBy->getProfileUrl() }}">{{ $comment->createdBy->name }}</a>
@else
<span>{{ trans('common.deleted_user') }}</span>
@endif
<span title="{{ $comment->created_at }}">
{{ trans('entities.comment_created', ['createDiff' => $comment->created]) }}
</span>
@if($comment->isUpdated())
<span title="{{ $comment->updated_at }}">
&bull;&nbsp;
{{ trans('entities.comment_updated', ['updateDiff' => $comment->updated, 'username' => $comment->updatedBy->name]) }}
{{ trans('entities.comment_updated', ['updateDiff' => $comment->updated, 'username' => $comment->updatedBy? $comment->updatedBy->name : trans('common.deleted_user')]) }}
</span>
@endif
</div>
@ -47,6 +50,9 @@
@endif
<div comment-content class="content">
<div class="form-group loading" style="display: none;">
@include('partials.loading-icon', ['text' => trans('entities.comment_deleting')])
</div>
{!! $comment->html !!}
</div>
@ -60,6 +66,9 @@
<button type="button" class="button outline" action="closeUpdateForm">{{ trans('common.cancel') }}</button>
<button type="submit" class="button pos">{{ trans('entities.comment_save') }}</button>
</div>
<div class="form-group loading" style="display: none;">
@include('partials.loading-icon', ['text' => trans('entities.comment_saving')])
</div>
</form>
</div>
@endif

View File

@ -25,6 +25,9 @@
<button type="button" class="button outline" action="hideForm">{{ trans('common.cancel') }}</button>
<button type="submit" class="button pos">{{ trans('entities.comment_save') }}</button>
</div>
<div class="form-group loading" style="display: none;">
@include('partials.loading-icon', ['text' => trans('entities.comment_saving')])
</div>
</form>
</div>
</div>

View File

@ -2,4 +2,7 @@
<div></div>
<div></div>
<div></div>
@if(isset($text))
<span>{{$text}}</span>
@endif
</div>