<template> <div class="post-timeline-component web-wrapper"> <div v-if="isLoaded" class="container-fluid mt-3"> <div class="row"> <div class="col-md-4 col-lg-3 d-md-block"> <sidebar :user="user" /> </div> <div class="col-md-8 col-lg-6"> <div v-if="isReply" class="p-3 rounded-top mb-n3" style="background-color: var(--card-header-accent)"> <p> <i class="fal fa-reply mr-1"></i> In reply to <a :href="'/i/web/profile/' + reply.account.id" class="font-weight-bold primary" @click.prevent="goToProfile(reply.account)"> @{{ reply.account.acct }} </a> <button @click.prevent="goToPost(reply)" class="btn btn-primary font-weight-bold btn-sm px-3 float-right rounded-pill"> View Post </button> </p> </div> <status :key="post.id + ':fui:' + forceUpdateIdx" :status="post" :profile="user" v-on:menu="openContextMenu()" v-on:like="likeStatus()" v-on:unlike="unlikeStatus()" v-on:likes-modal="openLikesModal()" v-on:shares-modal="openSharesModal()" v-on:bookmark="handleBookmark()" v-on:share="shareStatus()" v-on:unshare="unshareStatus()" v-on:follow="follow()" v-on:unfollow="unfollow()" v-on:counter-change="counterChange" /> </div> <div class="d-none d-lg-block col-lg-3"> <rightbar /> </div> </div> </div> <div v-if="postStateError" class="container-fluid mt-3"> <div class="row"> <div class="col-md-4 col-lg-3 d-md-block"> <sidebar :user="user" /> </div> <div class="col-md-8 col-lg-6"> <div class="card card-body shadow-none border"> <div class="d-flex align-self-center flex-column" style="max-width: 500px;"> <p class="text-center"> <i class="far fa-exclamation-triangle fa-3x text-lighter"></i> </p> <p class="text-center lead font-weight-bold">Error displaying post</p> <p class="mb-0">This can happen for a few reasons:</p> <ul class="text-lighter"> <li>The url is invalid or has a typo</li> <li>The page has been flagged for review by our automated abuse detection systems</li> <li>The content may have been deleted</li> <li>You do not have permission to view this content</li> </ul> </div> </div> </div> <div class="d-none d-lg-block col-lg-3"> <rightbar /> </div> </div> </div> <context-menu v-if="isLoaded" ref="contextMenu" :status="post" :profile="user" @report-modal="handleReport()" @delete="deletePost()" v-on:edit="handleEdit" /> <likes-modal v-if="showLikesModal" ref="likesModal" :status="post" :profile="user" /> <shares-modal v-if="showSharesModal" ref="sharesModal" :status="post" :profile="profile" /> <report-modal v-if="post" ref="reportModal" :status="post" /> <post-edit-modal ref="editModal" v-on:update="mergeUpdatedPost" /> <drawer /> </div> </template> <script type="text/javascript"> import Drawer from './partials/drawer.vue'; import Rightbar from './partials/rightbar.vue'; import Sidebar from './partials/sidebar.vue'; import Status from './partials/TimelineStatus.vue'; import ContextMenu from './partials/post/ContextMenu.vue'; import MediaContainer from './partials/post/MediaContainer.vue'; import LikesModal from './partials/post/LikeModal.vue'; import SharesModal from './partials/post/ShareModal.vue'; import ReportModal from './partials/modal/ReportPost.vue'; import PostEditModal from './partials/post/PostEditModal.vue'; export default { props: { cachedStatus: { type: Object }, cachedProfile: { type: Object } }, components: { "drawer": Drawer, "sidebar": Sidebar, "status": Status, "context-menu": ContextMenu, "media-container": MediaContainer, "likes-modal": LikesModal, "shares-modal": SharesModal, "rightbar": Rightbar, "report-modal": ReportModal, "post-edit-modal": PostEditModal }, data() { return { isLoaded: false, user: undefined, profile: undefined, post: undefined, relationship: {}, media: undefined, mediaIndex: 0, showLikesModal: false, isReply: false, reply: {}, showSharesModal: false, postStateError: false, forceUpdateIdx: 0 } }, created() { this.init(); }, watch: { '$route': 'init' }, methods: { init() { this.fetchSelf(); }, fetchSelf() { this.user = window._sharedData.user; this.fetchPost(); }, fetchPost() { axios.get('/api/pixelfed/v1/statuses/'+this.$route.params.id) .then(res => { if(!res.data || !res.data.hasOwnProperty('id')) { this.$router.push('/i/web/404'); } if(!res.data.hasOwnProperty('account') || !res.data.account) { this.postStateError = true; return; } this.post = res.data; this.media = this.post.media_attachments; this.profile = this.post.account; if(this.post.in_reply_to_id) { this.fetchReply(); } else { this.fetchRelationship(); } }).catch(err => { switch(err.response.status) { case 403: case 404: this.$router.push('/i/web/404'); break; } }) }, fetchReply() { axios.get('/api/pixelfed/v1/statuses/' + this.post.in_reply_to_id) .then(res => { this.reply = res.data; this.isReply = true; this.fetchRelationship(); }) .catch(err => { this.fetchRelationship(); }) }, fetchRelationship() { if(this.profile.id == this.user.id) { this.relationship = {}; this.fetchState(); return; } axios.get('/api/pixelfed/v1/accounts/relationships', { params: { 'id[]': this.profile.id } }).then(res => { this.relationship = res.data[0]; this.fetchState(); }); }, fetchState() { axios.get('/api/v2/statuses/'+this.post.id+'/state') .then(res => { this.post.favourited = res.data.liked; this.post.reblogged = res.data.shared; this.post.bookmarked = res.data.bookmarked; if(!this.post.favourites_count && this.post.favourited) { this.post.favourites_count = 1; } this.isLoaded = true; }).catch(err => { this.isLoaded = false; this.postStateError = true; }) }, goBack() { this.$router.push('/i/web'); }, likeStatus() { let count = this.post.favourites_count; this.post.favourites_count = count + 1; this.post.favourited = !this.post.favourited; axios.post('/api/v1/statuses/' + this.post.id + '/favourite') .then(res => { // }).catch(err => { this.post.favourites_count = count; this.post.favourited = false; }) }, unlikeStatus() { let count = this.post.favourites_count; this.post.favourites_count = count - 1; this.post.favourited = !this.post.favourited; axios.post('/api/v1/statuses/' + this.post.id + '/unfavourite') .then(res => { // }).catch(err => { this.post.favourites_count = count; this.post.favourited = false; }) }, shareStatus() { let count = this.post.reblogs_count; this.post.reblogs_count = count + 1; this.post.reblogged = !this.post.reblogged; axios.post('/api/v1/statuses/' + this.post.id + '/reblog') .then(res => { // }).catch(err => { this.post.reblogs_count = count; this.post.reblogged = false; }) }, unshareStatus() { let count = this.post.reblogs_count; this.post.reblogs_count = count - 1; this.post.reblogged = !this.post.reblogged; axios.post('/api/v1/statuses/' + this.post.id + '/unreblog') .then(res => { // }).catch(err => { this.post.reblogs_count = count; this.post.reblogged = false; }) }, follow() { axios.post('/api/v1/accounts/' + this.post.account.id + '/follow') .then(res => { this.$store.commit('updateRelationship', [res.data]); this.user.following_count++; this.post.account.followers_count++; }).catch(err => { swal('Oops!', 'An error occurred when attempting to follow this account.', 'error'); this.post.relationship.following = false; }); }, unfollow() { axios.post('/api/v1/accounts/' + this.post.account.id + '/unfollow') .then(res => { this.$store.commit('updateRelationship', [res.data]); this.user.following_count--; this.post.account.followers_count--; }).catch(err => { swal('Oops!', 'An error occurred when attempting to unfollow this account.', 'error'); this.post.relationship.following = true; }); }, openContextMenu() { this.$nextTick(() => { this.$refs.contextMenu.open(); }); }, openLikesModal() { this.showLikesModal = true; this.$nextTick(() => { this.$refs.likesModal.open(); }); }, openSharesModal() { this.showSharesModal = true; this.$nextTick(() => { this.$refs.sharesModal.open(); }); }, deletePost() { this.$router.push('/i/web'); }, goToPost(post) { this.$router.push({ name: 'post', path: `/i/web/post/${post.id}`, params: { id: post.id, cachedStatus: post, cachedProfile: this.user } }) }, goToProfile(account) { this.$router.push({ name: 'profile', path: `/i/web/profile/${account.id}`, params: { id: account.id, cachedProfile: account, cachedUser: this.user } }) }, handleBookmark() { axios.post('/i/bookmark', { item: this.post.id }) .then(res => { this.post.bookmarked = !this.post.bookmarked; }) .catch(err => { this.$bvToast.toast('Cannot bookmark post at this time.', { title: 'Bookmark Error', variant: 'danger', autoHideDelay: 5000 }); }); }, handleReport() { this.$nextTick(() => { this.$refs.reportModal.open(); }); }, counterChange(type) { switch(type) { case 'comment-increment': this.post.reply_count = this.post.reply_count + 1; break; case 'comment-decrement': this.post.reply_count = this.post.reply_count - 1; break; } }, handleEdit(status) { this.$refs.editModal.show(status); }, mergeUpdatedPost(post) { this.post = post; this.$nextTick(() => { this.forceUpdateIdx++; }); } } } </script>