<template> <div> <b-modal ref="sharesModal" centered size="md" :scrollable="true" hide-footer header-class="py-2" body-class="p-0" title-class="w-100 text-center pl-4 font-weight-bold" title-tag="p" title="Shared By"> <div v-if="isLoading" class="likes-loader list-group border-top-0" style="max-height: 500px;"> <like-placeholder /> </div> <div v-else> <div v-if="!likes.length" class="d-flex justify-content-center align-items-center" style="height: 140px;"> <p class="font-weight-bold mb-0">Nobody has shared this yet!</p> </div> <div v-else class="list-group" style="max-height: 500px;"> <div v-for="(account, index) in likes" class="list-group-item border-left-0 border-right-0 px-3" :class="[ index === 0 ? 'border-top-0' : '']"> <div class="media align-items-center"> <img :src="account.avatar" width="40" height="40" style="border-radius: 8px;" class="mr-3 shadow-sm" onerror="this.src='/storage/avatars/default.jpg?v=0';this.onerror=null;"> <div class="media-body"> <p class="mb-0 text-truncate"><a :href="account.url" class="text-dark font-weight-bold text-decoration-none" @click.prevent="goToProfile(account)">{{ getUsername(account) }}</a></p> <p class="mb-0 mt-n1 text-dark font-weight-bold small text-break">@{{ account.acct }}</p> </div> <div> <button v-if="account.id == user.id" class="btn btn-outline-muted rounded-pill btn-sm font-weight-bold" @click="goToProfile(profile)" style="width:110px;"> View Profile </button> <button v-else-if="account.follows" class="btn btn-outline-muted rounded-pill btn-sm font-weight-bold" :disabled="isUpdatingFollowState" @click="handleUnfollow(index)" style="width:110px;"> <span v-if="isUpdatingFollowState && followStateIndex === index"> <b-spinner small /> </span> <span v-else>Following</span> </button> <button v-else-if="!account.follows" class="btn btn-primary rounded-pill btn-sm font-weight-bold" :disabled="isUpdatingFollowState" @click="handleFollow(index)" style="width:110px;"> <span v-if="isUpdatingFollowState && followStateIndex === index"> <b-spinner small /> </span> <span v-else>Follow</span> </button> </div> </div> </div> <div v-if="canLoadMore"> <intersect @enter="enterIntersect"> <like-placeholder class="border-top-0" /> </intersect> <like-placeholder /> </div> </div> </div> </b-modal> </div> </template> <script type="text/javascript"> import Intersect from 'vue-intersect' import LikePlaceholder from './LikeListPlaceholder.vue'; import { parseLinkHeader } from '@web3-storage/parse-link-header'; export default { props: { status: { type: Object }, profile: { type: Object } }, components: { "intersect": Intersect, "like-placeholder": LikePlaceholder }, data() { return { isOpen: false, isLoading: true, canLoadMore: false, isFetchingMore: false, likes: [], ids: [], cursor: undefined, isUpdatingFollowState: false, followStateIndex: undefined, user: window._sharedData.user } }, methods: { clear() { this.isOpen = false; this.isLoading = true; this.canLoadMore = false; this.isFetchingMore = false; this.likes = []; this.ids = []; this.cursor = undefined; }, fetchShares() { axios.get('/api/v1/statuses/'+this.status.id+'/reblogged_by', { params: { limit: 40, '_pe': 1 } }) .then(res => { this.ids = res.data.map(a => a.id); this.likes = res.data; if(res.headers && res.headers.link) { const links = parseLinkHeader(res.headers.link); if(links.prev) { this.cursor = links.prev.cursor; this.canLoadMore = true; } else { this.canLoadMore = false; } } else { this.canLoadMore = false; } this.isLoading = false; }); }, open() { if(this.cursor) { this.clear(); } this.isOpen = true; this.fetchShares(); this.$refs.sharesModal.show(); }, enterIntersect() { if(this.isFetchingMore) { return; } this.isFetchingMore = true; axios.get('/api/v1/statuses/'+this.status.id+'/reblogged_by', { params: { limit: 10, cursor: this.cursor, '_pe': 1 } }).then(res => { if(!res.data || !res.data.length) { this.canLoadMore = false; this.isFetchingMore = false; return; } res.data.forEach(user => { if(this.ids.indexOf(user.id) == -1) { this.ids.push(user.id); this.likes.push(user); } }) if(res.headers && res.headers.link) { const links = parseLinkHeader(res.headers.link); if(links.prev) { this.cursor = links.prev.cursor; } else { this.canLoadMore = false; } } else { this.canLoadMore = false; } this.isFetchingMore = false; }) }, getUsername(account) { return account.display_name ? account.display_name : account.username; }, goToProfile(account) { this.$router.push({ name: 'profile', path: `/i/web/profile/${account.id}`, params: { id: account.id, cachedProfile: account, cachedUser: this.profile } }) }, handleFollow(index) { event.currentTarget.blur(); this.followStateIndex = index; this.isUpdatingFollowState = true; let account = this.likes[index]; axios.post('/api/v1/accounts/' + account.id + '/follow') .then(res => { this.likes[index].follows = true; this.followStateIndex = undefined; this.isUpdatingFollowState = false; }); }, handleUnfollow(index) { event.currentTarget.blur(); this.followStateIndex = index; this.isUpdatingFollowState = true; let account = this.likes[index]; axios.post('/api/v1/accounts/' + account.id + '/unfollow') .then(res => { this.likes[index].follows = false; this.followStateIndex = undefined; this.isUpdatingFollowState = false; }); } } } </script>