Add like & share model partial components

This commit is contained in:
Daniel Supernault 2023-02-08 04:08:33 -07:00
parent 937e6d070e
commit b565ead693
No known key found for this signature in database
GPG Key ID: 0DEF1C662C9033F7
2 changed files with 478 additions and 0 deletions

View File

@ -0,0 +1,239 @@
<template>
<div>
<b-modal
ref="likesModal"
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="$t('common.likes')">
<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">{{ $t('post.noLikes') }}</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">&commat;{{ account.acct }}</p>
</div>
<div>
<button
v-if="account.follows == null || 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: [],
page: 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.page = undefined;
},
fetchLikes() {
axios.get('/api/v1/statuses/'+this.status.id+'/favourited_by', {
params: {
limit: 40
}
})
.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.next) {
this.page = links.next.cursor;
this.canLoadMore = true;
} else {
this.canLoadMore = false;
}
}
this.isLoading = false;
});
},
open() {
if(this.page) {
this.clear();
}
this.isOpen = true;
this.fetchLikes();
this.$refs.likesModal.show();
},
enterIntersect() {
if(this.isFetchingMore) {
return;
}
this.isFetchingMore = true;
axios.get('/api/v1/statuses/'+this.status.id+'/favourited_by', {
params: {
limit: 10,
cursor: this.page
}
}).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.next) {
this.page = links.next.cursor;
} 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>

View File

@ -0,0 +1,239 @@
<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">&commat;{{ 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: [],
page: 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.page = undefined;
},
fetchShares() {
axios.get('/api/v1/statuses/'+this.status.id+'/reblogged_by', {
params: {
limit: 40
}
})
.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.next) {
this.page = links.next.cursor;
this.canLoadMore = true;
} else {
this.canLoadMore = false;
}
}
this.isLoading = false;
});
},
open() {
if(this.page) {
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.page
}
}).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.next) {
this.page = links.next.cursor;
} 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>