Update PostComponent

This commit is contained in:
Daniel Supernault 2019-04-02 23:13:45 -06:00
parent 2549049ab2
commit 08b4a934ec
No known key found for this signature in database
GPG key ID: 0DEF1C662C9033F7

View file

@ -1,18 +1,3 @@
<style scoped>
.status-comments,
.reactions,
.col-md-4 {
background: #fff;
}
.postPresenterContainer {
background: #fff;
}
@media(min-width: 720px) {
.postPresenterContainer {
min-height: 600px;
}
}
</style>
<template> <template>
<div class="postComponent d-none"> <div class="postComponent d-none">
<div class="container px-0"> <div class="container px-0">
@ -40,7 +25,7 @@
<a class="dropdown-item font-weight-bold" v-on:click="blockProfile()">Block Profile</a> <a class="dropdown-item font-weight-bold" v-on:click="blockProfile()">Block Profile</a>
</div> </div>
<div v-if="ownerOrAdmin()"> <div v-if="ownerOrAdmin()">
<!-- <a class="dropdown-item font-weight-bold" :href="editUrl()">Disable Comments</a> --> <a class="dropdown-item font-weight-bold" href="#" v-on:click.prevent="toggleCommentVisibility">{{ showComments ? 'Disable' : 'Enable'}} Comments</a>
<a class="dropdown-item font-weight-bold" :href="editUrl()">Edit</a> <a class="dropdown-item font-weight-bold" :href="editUrl()">Edit</a>
<a class="dropdown-item font-weight-bold text-danger" v-on:click="deletePost(status)">Delete</a> <a class="dropdown-item font-weight-bold text-danger" v-on:click="deletePost(status)">Delete</a>
</div> </div>
@ -92,19 +77,18 @@
</a> </a>
<div class="float-right"> <div class="float-right">
<div class="post-actions"> <div class="post-actions">
<div class="dropdown"> <div v-if="user != false" class="dropdown">
<button class="btn btn-link text-dark no-caret dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="Post options"> <button class="btn btn-link text-dark no-caret dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="Post options">
<span class="fas fa-ellipsis-v text-muted"></span> <span class="fas fa-ellipsis-v text-muted"></span>
</button> </button>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton"> <div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
<span class="menu-user d-none"> <span v-if="!owner()">
<a class="dropdown-item font-weight-bold" :href="reportUrl()">Report</a> <a class="dropdown-item font-weight-bold" :href="reportUrl()">Report</a>
<a class="dropdown-item font-weight-bold" v-on:click="muteProfile">Mute Profile</a> <a class="dropdown-item font-weight-bold" v-on:click="muteProfile">Mute Profile</a>
<a class="dropdown-item font-weight-bold" v-on:click="blockProfile">Block Profile</a> <a class="dropdown-item font-weight-bold" v-on:click="blockProfile">Block Profile</a>
</span> </span>
<span class="menu-author d-none"> <span v-if="ownerOrAdmin()">
<!-- <a class="dropdown-item font-weight-bold" :href="editUrl()">Mute Comments</a> <a class="dropdown-item font-weight-bold" href="#" v-on:click.prevent="toggleCommentVisibility">{{ showComments ? 'Disable' : 'Enable'}} Comments</a>
<a class="dropdown-item font-weight-bold" :href="editUrl()">Disable Comments</a> -->
<a class="dropdown-item font-weight-bold" :href="editUrl()">Edit</a> <a class="dropdown-item font-weight-bold" :href="editUrl()">Edit</a>
<a class="dropdown-item font-weight-bold text-danger" v-on:click="deletePost">Delete</a> <a class="dropdown-item font-weight-bold text-danger" v-on:click="deletePost">Delete</a>
</span> </span>
@ -114,19 +98,49 @@
</div> </div>
</div> </div>
<div class="d-flex flex-md-column flex-column-reverse h-100"> <div class="d-flex flex-md-column flex-column-reverse h-100">
<div class="card-body status-comments"> <div class="card-body status-comments pb-5">
<div class="status-comment"> <div class="status-comment">
<p class="mb-1 read-more" style="overflow: hidden;"> <p class="mb-1 read-more" style="overflow: hidden;">
<span class="font-weight-bold pr-1">{{statusUsername}}</span> <span class="font-weight-bold pr-1">{{statusUsername}}</span>
<span class="comment-text" :id="status.id + '-status-readmore'" v-html="status.content"></span> <span class="comment-text" :id="status.id + '-status-readmore'" v-html="status.content"></span>
</p> </p>
<post-comments :user="this.user" :post-id="statusId" :post-username="statusUsername"></post-comments>
<div v-if="showComments">
<div class="postCommentsLoader text-center">
<div class="spinner-border" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
<div class="postCommentsContainer d-none pt-3">
<p class="mb-1 text-center load-more-link d-none"><a href="#" class="text-muted" v-on:click="loadMore">Load more comments</a></p>
<div class="comments" data-min-id="0" data-max-id="0">
<div v-for="(reply, index) in results" class="pb-3">
<p class="d-flex justify-content-between align-items-top read-more" style="overflow-y: hidden;">
<span>
<a class="text-dark font-weight-bold mr-1" :href="reply.account.url" v-bind:title="reply.account.username">{{truncate(reply.account.username,15)}}</a>
<span class="text-break" v-html="reply.content"></span>
</span>
<span class="pl-2" style="min-width:38px">
<span v-on:click="likeReply(reply, $event)"><i v-bind:class="[reply.favourited ? 'fas fa-heart fa-sm text-danger':'far fa-heart fa-sm text-lighter']"></i></span>
<post-menu :status="reply" :profile="user" :size="'sm'" :modal="'true'" class="d-inline-block pl-2" v-on:deletePost="deleteComment(reply.id, index)"></post-menu>
</span>
</p>
<p class="">
<span class="text-muted mr-3" style="width: 20px;" v-text="timeAgo(reply.created_at)"></span>
<span v-if="reply.favourites_count" class="text-muted comment-reaction font-weight-bold mr-3">{{reply.favourites_count == 1 ? '1 like' : reply.favourites_count + ' likes'}}</span>
<span class="text-muted comment-reaction font-weight-bold cursor-pointer" v-on:click="replyFocus(reply)">Reply</span>
</p>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
<div class="card-body flex-grow-0 py-1"> <div class="card-body flex-grow-0 py-1">
<div class="reactions my-1"> <div class="reactions my-1">
<h3 v-bind:class="[reactions.liked ? 'fas fa-heart text-danger pr-3 m-0 cursor-pointer' : 'far fa-heart pr-3 m-0 like-btn cursor-pointer']" title="Like" v-on:click="likeStatus"></h3> <h3 v-bind:class="[reactions.liked ? 'fas fa-heart text-danger pr-3 m-0 cursor-pointer' : 'far fa-heart pr-3 m-0 like-btn cursor-pointer']" title="Like" v-on:click="likeStatus"></h3>
<h3 class="far fa-comment pr-3 m-0 cursor-pointer" title="Comment" v-on:click="commentFocus"></h3> <h3 class="far fa-comment pr-3 m-0 cursor-pointer" title="Comment" v-on:click="replyFocus(status)"></h3>
<h3 v-bind:class="[reactions.shared ? 'far fa-share-square pr-3 m-0 text-primary cursor-pointer' : 'far fa-share-square pr-3 m-0 share-btn cursor-pointer']" title="Share" v-on:click="shareStatus"></h3> <h3 v-bind:class="[reactions.shared ? 'far fa-share-square pr-3 m-0 text-primary cursor-pointer' : 'far fa-share-square pr-3 m-0 share-btn cursor-pointer']" title="Share" v-on:click="shareStatus"></h3>
<h3 v-bind:class="[reactions.bookmarked ? 'fas fa-bookmark text-warning m-0 float-right cursor-pointer' : 'far fa-bookmark m-0 float-right cursor-pointer']" title="Bookmark" v-on:click="bookmarkStatus"></h3> <h3 v-bind:class="[reactions.bookmarked ? 'fas fa-bookmark text-warning m-0 float-right cursor-pointer' : 'far fa-bookmark m-0 float-right cursor-pointer']" title="Bookmark" v-on:click="bookmarkStatus"></h3>
</div> </div>
@ -145,15 +159,29 @@
</div> </div>
</div> </div>
</div> </div>
<div class="card-footer bg-white sticky-md-bottom"> <div v-if="showComments && user.length !== 0" class="card-footer bg-white px-2 py-0">
<div class="comment-form-guest"> <ul class="nav align-items-center emoji-reactions" style="overflow-x: scroll;flex-wrap: unset;">
<li class="nav-item" v-on:click="emojiReaction">😂</li>
<li class="nav-item" v-on:click="emojiReaction">💯</li>
<li class="nav-item" v-on:click="emojiReaction"></li>
<li class="nav-item" v-on:click="emojiReaction">🙌</li>
<li class="nav-item" v-on:click="emojiReaction">👏</li>
<li class="nav-item" v-on:click="emojiReaction">😍</li>
<li class="nav-item" v-on:click="emojiReaction">😯</li>
<li class="nav-item" v-on:click="emojiReaction">😢</li>
<li class="nav-item" v-on:click="emojiReaction">😅</li>
<li class="nav-item" v-on:click="emojiReaction">😁</li>
<li class="nav-item" v-on:click="emojiReaction">🙂</li>
<li class="nav-item" v-on:click="emojiReaction">😎</li>
</ul>
</div>
<div v-if="showComments" class="card-footer bg-white sticky-md-bottom p-0">
<div v-if="user.length == 0" class="comment-form-guest p-3">
<a href="/login">Login</a> to like or comment. <a href="/login">Login</a> to like or comment.
</div> </div>
<form class="comment-form d-none" method="post" action="/i/comment" :data-id="statusId" data-truncate="false"> <form v-else class="border-0 rounded-0 align-middle" method="post" action="/i/comment" :data-id="statusId" data-truncate="false">
<input type="hidden" name="_token" value=""> <textarea class="form-control border-0 rounded-0" name="comment" placeholder="Add a comment…" autocomplete="off" autocorrect="off" style="height:56px;line-height: 18px;max-height:80px;resize: none; padding-right:4.2rem;" v-model="replyText"></textarea>
<input type="hidden" name="item" :value="statusId"> <input type="button" value="Post" class="d-inline-block btn btn-link font-weight-bold reply-btn text-decoration-none" v-on:click.prevent="postReply"/>
<input class="form-control" name="comment" placeholder="Add a comment…" autocomplete="off">
<input type="submit" value="Send" class="btn btn-primary comment-submit" />
</form> </form>
</div> </div>
</div> </div>
@ -243,6 +271,68 @@
</div> </div>
</template> </template>
<style type="text/css" scoped>
.status-comments,
.reactions,
.col-md-4 {
background: #fff;
}
.postPresenterContainer {
background: #fff;
}
@media(min-width: 720px) {
.postPresenterContainer {
min-height: 600px;
}
}
::-webkit-scrollbar {
width: 0px;
background: transparent;
}
.reply-btn {
position: absolute;
bottom: 12px;
right: 20px;
width: 60px;
text-align: center;
border-radius: 0 3px 3px 0;
}
.text-lighter {
color:#B8C2CC !important;
}
.text-break {
overflow-wrap: break-word;
}
.comments p {
margin-bottom: 0;
}
.comment-reaction {
font-size: 80%;
}
.show-reply-bar {
display: inline-block;
border-bottom: 1px solid #999;
height: 0;
margin-right: 16px;
vertical-align: middle;
width: 24px;
}
.comment-thread {
margin: 4px 0 0 40px;
width: calc(100% - 40px);
}
.emoji-reactions .nav-item {
font-size: 1.2rem;
padding: 7px;
cursor: pointer;
}
.emoji-reactions::-webkit-scrollbar {
width: 0px;
height: 0px;
background: transparent;
}
</style>
<script> <script>
pixelfed.postComponent = {}; pixelfed.postComponent = {};
@ -262,7 +352,16 @@ export default {
likesPage: 1, likesPage: 1,
shares: [], shares: [],
sharesPage: 1, sharesPage: 1,
lightboxMedia: false lightboxMedia: false,
replyText: '',
results: [],
pagination: {},
min_id: 0,
max_id: 0,
reply_to_profile_id: 0,
thread: false,
showComments: false
} }
}, },
@ -279,6 +378,8 @@ export default {
updated() { updated() {
$('.carousel').carousel(); $('.carousel').carousel();
pixelfed.readmore();
if(this.reactions) { if(this.reactions) {
if(this.reactions.bookmarked == true) { if(this.reactions.bookmarked == true) {
$('.postComponent .far.fa-bookmark').removeClass('far').addClass('fas text-warning'); $('.postComponent .far.fa-bookmark').removeClass('far').addClass('fas text-warning');
@ -345,6 +446,10 @@ export default {
this.showMuteBlock(); this.showMuteBlock();
loader.hide(); loader.hide();
pixelfed.readmore(); pixelfed.readmore();
if(self.status.comments_disabled == false) {
self.showComments = true;
this.fetchComments();
}
$('.postComponent').removeClass('d-none'); $('.postComponent').removeClass('d-none');
$('.postPresenterLoader').addClass('d-none'); $('.postPresenterLoader').addClass('d-none');
$('.postPresenterContainer').removeClass('d-none'); $('.postPresenterContainer').removeClass('d-none');
@ -369,10 +474,6 @@ export default {
}); });
}, },
commentFocus() {
$('.comment-form input[name="comment"]').focus();
},
likesModal() { likesModal() {
if(this.status.favourites_count == 0 || $('body').hasClass('loggedIn') == false) { if(this.status.favourites_count == 0 || $('body').hasClass('loggedIn') == false) {
return; return;
@ -422,6 +523,7 @@ export default {
likeStatus(event) { likeStatus(event) {
if($('body').hasClass('loggedIn') == false) { if($('body').hasClass('loggedIn') == false) {
window.location.href = '/login?next=' + encodeURIComponent(window.location.pathname);
return; return;
} }
@ -448,6 +550,7 @@ export default {
shareStatus() { shareStatus() {
if($('body').hasClass('loggedIn') == false) { if($('body').hasClass('loggedIn') == false) {
window.location.href = '/login?next=' + encodeURIComponent(window.location.pathname);
return; return;
} }
@ -474,6 +577,7 @@ export default {
bookmarkStatus() { bookmarkStatus() {
if($('body').hasClass('loggedIn') == false) { if($('body').hasClass('loggedIn') == false) {
window.location.href = '/login?next=' + encodeURIComponent(window.location.pathname);
return; return;
} }
@ -521,6 +625,9 @@ export default {
}, },
deletePost(status) { deletePost(status) {
if(!this.ownerOrAdmin()) {
return;
}
var result = confirm('Are you sure you want to delete this post?'); var result = confirm('Are you sure you want to delete this post?');
if (result) { if (result) {
if($('body').hasClass('loggedIn') == false) { if($('body').hasClass('loggedIn') == false) {
@ -553,6 +660,198 @@ export default {
lightbox(src) { lightbox(src) {
this.lightboxMedia = src; this.lightboxMedia = src;
this.$refs.lightboxModal.show(); this.$refs.lightboxModal.show();
},
postReply() {
let self = this;
if(this.replyText.length == 0 ||
this.replyText.trim() == '@'+this.status.account.acct) {
self.replyText = null;
$('textarea[name="comment"]').blur();
return;
}
let data = {
item: this.statusId,
comment: this.replyText
}
axios.post('/i/comment', data)
.then(function(res) {
let entity = res.data.entity;
self.results.push(entity);
self.replyText = '';
let elem = $('.status-comments')[0];
elem.scrollTop = elem.clientHeight;
});
},
deleteComment(id, i) {
axios.post('/i/delete', {
type: 'comment',
item: id
}).then(res => {
this.results.splice(i, 1);
}).catch(err => {
swal('Something went wrong!', 'Please try again later', 'error');
});
},
l(e) {
let len = e.length;
if(len < 10) { return e; }
return e.substr(0, 10)+'...';
},
replyFocus(e) {
this.reply_to_profile_id = e.account.id;
this.replyText = '@' + e.account.username + ' ';
$('textarea[name="comment"]').focus();
},
fetchComments() {
let url = '/api/v2/comments/'+this.statusUsername+'/status/'+this.statusId;
axios.get(url)
.then(response => {
let self = this;
this.results = _.reverse(response.data.data);
this.pagination = response.data.meta.pagination;
if(this.results.length > 0) {
$('.load-more-link').removeClass('d-none');
}
$('.postCommentsLoader').addClass('d-none');
$('.postCommentsContainer').removeClass('d-none');
}).catch(error => {
if(!error.response) {
$('.postCommentsLoader .lds-ring')
.attr('style','width:100%')
.addClass('pt-4 font-weight-bold text-muted')
.text('An error occurred, cannot fetch comments. Please try again later.');
} else {
switch(error.response.status) {
case 401:
$('.postCommentsLoader .lds-ring')
.attr('style','width:100%')
.addClass('pt-4 font-weight-bold text-muted')
.text('Please login to view.');
break;
default:
$('.postCommentsLoader .lds-ring')
.attr('style','width:100%')
.addClass('pt-4 font-weight-bold text-muted')
.text('An error occurred, cannot fetch comments. Please try again later.');
break;
}
}
});
},
loadMore(e) {
e.preventDefault();
if(this.pagination.total_pages == 1 || this.pagination.current_page == this.pagination.total_pages) {
$('.load-more-link').addClass('d-none');
return;
}
$('.postCommentsLoader').removeClass('d-none');
let next = this.pagination.links.next;
axios.get(next)
.then(response => {
let self = this;
let res = response.data.data;
$('.postCommentsLoader').addClass('d-none');
for(let i=0; i < res.length; i++) {
this.results.unshift(res[i]);
}
this.pagination = response.data.meta.pagination;
});
},
likeReply(status, $event) {
if($('body').hasClass('loggedIn') == false) {
return;
}
axios.post('/i/like', {
item: status.id
}).then(res => {
status.favourites_count = res.data.count;
if(status.favourited == true) {
status.favourited = false;
} else {
status.favourited = true;
}
}).catch(err => {
swal('Error', 'Something went wrong, please try again later.', 'error');
});
},
truncate(str,lim) {
return _.truncate(str,{
length: lim
});
},
timeAgo(ts) {
let date = Date.parse(ts);
let seconds = Math.floor((new Date() - date) / 1000);
let interval = Math.floor(seconds / 31536000);
if (interval >= 1) {
return interval + "y";
}
interval = Math.floor(seconds / 604800);
if (interval >= 1) {
return interval + "w";
}
interval = Math.floor(seconds / 86400);
if (interval >= 1) {
return interval + "d";
}
interval = Math.floor(seconds / 3600);
if (interval >= 1) {
return interval + "h";
}
interval = Math.floor(seconds / 60);
if (interval >= 1) {
return interval + "m";
}
return Math.floor(seconds) + "s";
},
emojiReaction() {
let em = event.target.innerText;
if(this.replyText.length == 0) {
this.reply_to_profile_id = this.status.account.id;
this.replyText = '@' + this.status.account.username + ' ' + em;
$('textarea[name="comment"]').focus();
} else {
this.reply_to_profile_id = this.status.account.id;
this.replyText += em;
$('textarea[name="comment"]').focus();
}
},
toggleCommentVisibility() {
if(this.ownerOrAdmin() == false) {
return;
}
let state = this.status.comments_disabled;
let self = this;
if(state == true) {
// re-enable comments
axios.post('/i/visibility', {
item: self.status.id,
disableComments: false
}).then(function(res) {
window.location.href = self.status.url;
}).catch(function(err) {
return;
});
} else {
// disable comments
axios.post('/i/visibility', {
item: self.status.id,
disableComments: true
}).then(function(res) {
self.status.comments_disabled = false;
self.showComments = false;
}).catch(function(err) {
return;
});
}
} }
}, },