pixelfed/resources/assets/js/components/PostComponent.vue

860 lines
32 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="postComponent d-none">
<div class="container px-0">
<div class="card card-md-rounded-0 status-container orientation-unknown">
<div class="row px-0 mx-0">
<div class="d-flex d-md-none align-items-center justify-content-between card-header bg-white w-100">
<a :href="statusProfileUrl" class="d-flex align-items-center status-username text-truncate" data-toggle="tooltip" data-placement="bottom" :title="statusUsername">
<div class="status-avatar mr-2">
<img :src="statusAvatar" width="24px" height="24px" style="border-radius:12px;">
</div>
<div class="username">
<span class="username-link font-weight-bold text-dark">{{ statusUsername }}</span>
</div>
</a>
<div v-if="user != false" class="float-right">
<div class="post-actions">
<div 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">
<span class="fas fa-ellipsis-v text-muted"></span>
</button>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
<div v-if="!owner()">
<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="blockProfile()">Block Profile</a>
</div>
<div v-if="ownerOrAdmin()">
<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 text-danger" v-on:click="deletePost(status)">Delete</a>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-12 col-md-8 px-0 mx-0">
<div class="postPresenterLoader text-center">
<div class="lds-ring"><div></div><div></div><div></div><div></div></div>
</div>
<div class="postPresenterContainer d-none d-flex justify-content-center align-items-center">
<div v-if="status.pf_type === 'photo'" class="w-100">
<photo-presenter :status="status" v-on:lightbox="lightbox"></photo-presenter>
</div>
<div v-else-if="status.pf_type === 'video'" class="w-100">
<video-presenter :status="status"></video-presenter>
</div>
<div v-else-if="status.pf_type === 'photo:album'" class="w-100">
<photo-album-presenter :status="status" v-on:lightbox="lightbox"></photo-album-presenter>
</div>
<div v-else-if="status.pf_type === 'video:album'" class="w-100">
<video-album-presenter :status="status"></video-album-presenter>
</div>
<div v-else-if="status.pf_type === 'photo:video:album'" class="w-100">
<mixed-album-presenter :status="status" v-on:lightbox="lightbox"></mixed-album-presenter>
</div>
<div v-else class="w-100">
<p class="text-center p-0 font-weight-bold text-white">Error: Problem rendering preview.</p>
</div>
</div>
</div>
<div class="col-12 col-md-4 px-0 d-flex flex-column border-left border-md-left-0">
<div class="d-md-flex d-none align-items-center justify-content-between card-header py-3 bg-white">
<a :href="statusProfileUrl" class="d-flex align-items-center status-username text-truncate" data-toggle="tooltip" data-placement="bottom" :title="statusUsername">
<div class="status-avatar mr-2">
<img :src="statusAvatar" width="24px" height="24px" style="border-radius:12px;">
</div>
<div class="username">
<span class="username-link font-weight-bold text-dark">{{ statusUsername }}</span>
</div>
</a>
<div class="float-right">
<div class="post-actions">
<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">
<span class="fas fa-ellipsis-v text-muted"></span>
</button>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
<span v-if="!owner()">
<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="blockProfile">Block Profile</a>
</span>
<span v-if="ownerOrAdmin()">
<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 text-danger" v-on:click="deletePost">Delete</a>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="d-flex flex-md-column flex-column-reverse h-100">
<div class="card-body status-comments pb-5">
<div class="status-comment">
<p class="mb-1 read-more" style="overflow: hidden;">
<span class="font-weight-bold pr-1">{{statusUsername}}</span>
<span class="comment-text" :id="status.id + '-status-readmore'" v-html="status.content"></span>
</p>
<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 class="card-body flex-grow-0 py-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-if="!status.comments_disabled" 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.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 class="reaction-counts font-weight-bold mb-0">
<span style="cursor:pointer;" v-on:click="likesModal">
<span class="like-count">{{status.favourites_count || 0}}</span> likes
</span>
<span class="float-right" style="cursor:pointer;" v-on:click="sharesModal">
<span class="share-count pl-4">{{status.reblogs_count || 0}}</span> shares
</span>
</div>
<div class="timestamp">
<a v-bind:href="statusUrl" class="small text-muted">
{{timestampFormat()}}
</a>
</div>
</div>
</div>
<div v-if="showComments && user.length !== 0" class="card-footer bg-white px-2 py-0">
<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.
</div>
<form v-else class="border-0 rounded-0 align-middle" method="post" action="/i/comment" :data-id="statusId" data-truncate="false">
<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="button" value="Post" class="d-inline-block btn btn-link font-weight-bold reply-btn text-decoration-none" v-on:click.prevent="postReply"/>
</form>
</div>
</div>
</div>
</div>
</div>
<b-modal ref="likesModal"
id="l-modal"
hide-footer
centered
title="Likes"
body-class="list-group-flush p-0">
<div class="list-group">
<div class="list-group-item border-0" v-for="(user, index) in likes" :key="'modal_likes_'+index">
<div class="media">
<a :href="user.url">
<img class="mr-3 rounded-circle box-shadow" :src="user.avatar" :alt="user.username + 's avatar'" width="30px">
</a>
<div class="media-body">
<p class="mb-0" style="font-size: 14px">
<a :href="user.url" class="font-weight-bold text-dark">
{{user.username}}
</a>
</p>
<p class="text-muted mb-0" style="font-size: 14px">
{{user.display_name}}
</a>
</p>
</div>
</div>
</div>
<infinite-loading @infinite="infiniteLikesHandler" spinner="spiral">
<div slot="no-more"></div>
<div slot="no-results"></div>
</infinite-loading>
</div>
</b-modal>
<b-modal ref="sharesModal"
id="s-modal"
hide-footer
centered
title="Shares"
body-class="list-group-flush p-0">
<div class="list-group">
<div class="list-group-item border-0" v-for="(user, index) in shares" :key="'modal_shares_'+index">
<div class="media">
<a :href="user.url">
<img class="mr-3 rounded-circle box-shadow" :src="user.avatar" :alt="user.username + 's avatar'" width="30px">
</a>
<div class="media-body">
<div class="d-inline-block">
<p class="mb-0" style="font-size: 14px">
<a :href="user.url" class="font-weight-bold text-dark">
{{user.username}}
</a>
</p>
<p class="text-muted mb-0" style="font-size: 14px">
{{user.display_name}}
</a>
</p>
</div>
<p class="float-right"><!-- <a class="btn btn-primary font-weight-bold py-1" href="#">Follow</a> --></p>
</div>
</div>
</div>
<infinite-loading @infinite="infiniteSharesHandler" spinner="spiral">
<div slot="no-more"></div>
<div slot="no-results"></div>
</infinite-loading>
</div>
</b-modal>
<b-modal
id="lightbox"
ref="lightboxModal"
:hide-header="true"
:hide-footer="true"
centered
size="lg"
body-class="p-0"
>
<div v-if="lightboxMedia" :class="lightboxMedia.filter_class">
<img :src="lightboxMedia.url" class="img-fluid" style="min-height: 100%; min-width: 100%">
</div>
</b-modal>
</div>
</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>
pixelfed.postComponent = {};
export default {
props: ['status-id', 'status-username', 'status-template', 'status-url', 'status-profile-url', 'status-avatar'],
data() {
return {
status: false,
media: {},
user: false,
reactions: {
liked: false,
shared: false
},
likes: [],
likesPage: 1,
shares: [],
sharesPage: 1,
lightboxMedia: false,
replyText: '',
results: [],
pagination: {},
min_id: 0,
max_id: 0,
reply_to_profile_id: 0,
thread: false,
showComments: false
}
},
mounted() {
this.fetchData();
this.authCheck();
let token = $('meta[name="csrf-token"]').attr('content');
$('input[name="_token"]').each(function(k, v) {
let el = $(v);
el.val(token);
});
},
updated() {
$('.carousel').carousel();
pixelfed.readmore();
if(this.reactions) {
if(this.reactions.bookmarked == true) {
$('.postComponent .far.fa-bookmark').removeClass('far').addClass('fas text-warning');
}
if(this.reactions.shared == true) {
$('.postComponent .far.fa-share-square').addClass('text-primary');
}
if(this.reactions.liked == true) {
$('.postComponent .far.fa-heart').removeClass('far text-dark').addClass('fas text-danger');
}
}
},
methods: {
authCheck() {
let authed = $('body').hasClass('loggedIn');
if(authed == true) {
$('.comment-form-guest').addClass('d-none');
$('.comment-form').removeClass('d-none');
}
},
showMuteBlock() {
let sid = this.status.account.id;
let uid = this.user.id;
if(sid == uid) {
$('.post-actions .menu-author').removeClass('d-none');
} else {
$('.post-actions .menu-user').removeClass('d-none');
}
},
reportUrl() {
return '/i/report?type=post&id=' + this.status.id;
},
editUrl() {
return this.status.url + '/edit';
},
timestampFormat() {
let ts = new Date(this.status.created_at);
return ts.toDateString() + ' ' + ts.toLocaleTimeString();
},
fetchData() {
let loader = this.$loading.show({
'opacity': 0,
'background-color': '#f5f8fa'
});
axios.get('/api/v2/profile/'+this.statusUsername+'/status/'+this.statusId)
.then(response => {
let self = this;
self.status = response.data.status;
self.user = response.data.user;
self.media = self.status.media_attachments;
self.reactions = response.data.reactions;
self.likes = response.data.likes;
self.shares = response.data.shares;
self.likesPage = 2;
self.sharesPage = 2;
//this.buildPresenter();
this.showMuteBlock();
loader.hide();
pixelfed.readmore();
if(self.status.comments_disabled == false) {
self.showComments = true;
this.fetchComments();
}
$('.postComponent').removeClass('d-none');
$('.postPresenterLoader').addClass('d-none');
$('.postPresenterContainer').removeClass('d-none');
$('head title').text(this.status.account.username + ' posted a photo: ' + this.status.favourites_count + ' likes');
}).catch(error => {
if(!error.response) {
$('.postPresenterLoader .lds-ring').attr('style','width:100%').addClass('pt-4 font-weight-bold text-muted').text('An error occurred, cannot fetch media. Please try again later.');
} else {
switch(error.response.status) {
case 401:
$('.postPresenterLoader .lds-ring')
.attr('style','width:100%')
.addClass('pt-4 font-weight-bold text-muted')
.text('Please login to view.');
break;
default:
$('.postPresenterLoader .lds-ring').attr('style','width:100%').addClass('pt-4 font-weight-bold text-muted').text('An error occurred, cannot fetch media. Please try again later.');
break;
}
}
});
},
likesModal() {
if(this.status.favourites_count == 0 || $('body').hasClass('loggedIn') == false) {
return;
}
this.$refs.likesModal.show();
},
sharesModal() {
if(this.status.reblogs_count == 0 || $('body').hasClass('loggedIn') == false) {
return;
}
this.$refs.sharesModal.show();
},
infiniteLikesHandler($state) {
let api = '/api/v2/likes/profile/'+this.statusUsername+'/status/'+this.statusId;
axios.get(api, {
params: {
page: this.likesPage,
},
}).then(({ data }) => {
if (data.data.length > 0) {
this.likes.push(...data.data);
this.likesPage++;
$state.loaded();
} else {
$state.complete();
}
});
},
infiniteSharesHandler($state) {
axios.get('/api/v2/shares/profile/'+this.statusUsername+'/status/'+this.statusId, {
params: {
page: this.sharesPage,
},
}).then(({ data }) => {
if (data.data.length > 0) {
this.shares.push(...data.data);
this.sharesPage++;
$state.loaded();
} else {
$state.complete();
}
});
},
likeStatus(event) {
if($('body').hasClass('loggedIn') == false) {
window.location.href = '/login?next=' + encodeURIComponent(window.location.pathname);
return;
}
axios.post('/i/like', {
item: this.status.id
}).then(res => {
this.status.favourites_count = res.data.count;
if(this.reactions.liked == true) {
this.reactions.liked = false;
let user = this.user.id;
this.likes = this.likes.filter(function(like) {
return like.id !== user;
});
} else {
this.reactions.liked = true;
let user = this.user;
this.likes.push(user);
}
}).catch(err => {
console.error(err);
swal('Error', 'Something went wrong, please try again later.', 'error');
});
},
shareStatus() {
if($('body').hasClass('loggedIn') == false) {
window.location.href = '/login?next=' + encodeURIComponent(window.location.pathname);
return;
}
axios.post('/i/share', {
item: this.status.id
}).then(res => {
this.status.reblogs_count = res.data.count;
if(this.reactions.shared == true) {
this.reactions.shared = false;
let user = this.user.id;
this.shares = this.shares.filter(function(reaction) {
return reaction.id !== user;
});
} else {
this.reactions.shared = true;
let user = this.user;
this.shares.push(user);
}
}).catch(err => {
console.error(err);
swal('Error', 'Something went wrong, please try again later.', 'error');
});
},
bookmarkStatus() {
if($('body').hasClass('loggedIn') == false) {
window.location.href = '/login?next=' + encodeURIComponent(window.location.pathname);
return;
}
axios.post('/i/bookmark', {
item: this.status.id
}).then(res => {
if(this.reactions.bookmarked == true) {
this.reactions.bookmarked = false;
} else {
this.reactions.bookmarked = true;
}
}).catch(err => {
swal('Error', 'Something went wrong, please try again later.', 'error');
});
},
muteProfile() {
if($('body').hasClass('loggedIn') == false) {
return;
}
axios.post('/i/mute', {
type: 'user',
item: this.status.account.id
}).then(res => {
swal('Success', 'You have successfully muted ' + this.status.account.acct, 'success');
}).catch(err => {
swal('Error', 'Something went wrong. Please try again later.', 'error');
});
},
blockProfile() {
if($('body').hasClass('loggedIn') == false) {
return;
}
axios.post('/i/block', {
type: 'user',
item: this.status.account.id
}).then(res => {
swal('Success', 'You have successfully blocked ' + this.status.account.acct, 'success');
}).catch(err => {
swal('Error', 'Something went wrong. Please try again later.', 'error');
});
},
deletePost(status) {
if(!this.ownerOrAdmin()) {
return;
}
var result = confirm('Are you sure you want to delete this post?');
if (result) {
if($('body').hasClass('loggedIn') == false) {
return;
}
axios.post('/i/delete', {
type: 'status',
item: this.status.id
}).then(res => {
swal('Success', 'You have successfully deleted this post', 'success');
window.location.href = '/';
}).catch(err => {
swal('Error', 'Something went wrong. Please try again later.', 'error');
});
}
},
owner() {
return this.user.id === this.status.account.id;
},
admin() {
return this.user.is_admin == true;
},
ownerOrAdmin() {
return this.owner() || this.admin();
},
lightbox(src) {
this.lightboxMedia = src;
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;
});
}
}
},
}
</script>