Update legacy Profile component to use new cursor pagination for following/follower modals

This commit is contained in:
Daniel Supernault 2023-03-04 22:21:38 -07:00
parent 713aa5fd7d
commit 7a1495e6f6
No known key found for this signature in database
GPG key ID: 0DEF1C662C9033F7

View file

@ -133,10 +133,10 @@
</a> </a>
</div> </div>
</div> </div>
<p class="d-flex align-items-center mb-1"> <div class="d-md-flex align-items-center mb-1 text-break">
<span class="font-weight-bold mr-1">{{profile.display_name}}</span> <div class="font-weight-bold mr-1">{{profile.display_name}}</div>
<span v-if="profile.pronouns" class="text-muted small">{{profile.pronouns.join('/')}}</span> <div v-if="profile.pronouns" class="text-muted small">{{profile.pronouns.join('/')}}</div>
</p> </div>
<p v-if="profile.note" class="mb-0" v-html="profile.note"></p> <p v-if="profile.note" class="mb-0" v-html="profile.note"></p>
<p v-if="profile.website"><a :href="profile.website" class="profile-website small" rel="me external nofollow noopener" target="_blank">{{formatWebsite(profile.website)}}</a></p> <p v-if="profile.website"><a :href="profile.website" class="profile-website small" rel="me external nofollow noopener" target="_blank">{{formatWebsite(profile.website)}}</a></p>
<p class="d-flex small text-muted align-items-center"> <p class="d-flex small text-muted align-items-center">
@ -154,8 +154,8 @@
</div> </div>
</div> </div>
</div> </div>
<div class="d-block d-md-none my-0 pt-3 border-bottom"> <div v-if="user && user.hasOwnProperty('id')" class="d-block d-md-none my-0 pt-3 border-bottom">
<p v-if="user && user.hasOwnProperty('id')" class="pt-3"> <p class="pt-3">
<button v-if="owner" class="btn btn-outline-secondary bg-white btn-sm py-1 btn-block text-center font-weight-bold text-dark border border-lighter" @click.prevent="redirect('/settings/home')">Edit Profile</button> <button v-if="owner" class="btn btn-outline-secondary bg-white btn-sm py-1 btn-block text-center font-weight-bold text-dark border border-lighter" @click.prevent="redirect('/settings/home')">Edit Profile</button>
<button v-if="!owner && relationship.following" class="btn btn-outline-secondary bg-white btn-sm py-1 px-5 font-weight-bold text-dark border border-lighter" @click="followProfile">&nbsp;&nbsp; Unfollow &nbsp;&nbsp;</button> <button v-if="!owner && relationship.following" class="btn btn-outline-secondary bg-white btn-sm py-1 px-5 font-weight-bold text-dark border border-lighter" @click="followProfile">&nbsp;&nbsp; Unfollow &nbsp;&nbsp;</button>
<button v-if="!owner && !relationship.following" class="btn btn-primary btn-sm py-1 px-5 font-weight-bold" @click="followProfile">{{relationship.followed_by ? 'Follow Back' : '&nbsp;&nbsp;&nbsp;&nbsp; Follow &nbsp;&nbsp;&nbsp;&nbsp;'}}</button> <button v-if="!owner && !relationship.following" class="btn btn-primary btn-sm py-1 px-5 font-weight-bold" @click="followProfile">{{relationship.followed_by ? 'Follow Back' : '&nbsp;&nbsp;&nbsp;&nbsp; Follow &nbsp;&nbsp;&nbsp;&nbsp;'}}</button>
@ -339,16 +339,10 @@
<span class="text-dark">{{profileUsername}}</span> is not following yet</p> <span class="text-dark">{{profileUsername}}</span> is not following yet</p>
</div> </div>
<div v-else> <div v-else>
<div v-if="owner == true" class="list-group-item border-0 pt-0 px-0 mt-n2 mb-3">
<span class="d-flex px-4 pb-0 align-items-center">
<i class="fas fa-search text-lighter"></i>
<input type="text" class="form-control border-0 shadow-0 no-focus" placeholder="Search Following..." v-model="followingModalSearch" v-on:keyup="followingModalSearchHandler">
</span>
</div>
<div class="list-group-item border-0 py-1 mb-1" v-for="(user, index) in following" :key="'following_'+index"> <div class="list-group-item border-0 py-1 mb-1" v-for="(user, index) in following" :key="'following_'+index">
<div class="media"> <div class="media">
<a :href="profileUrlRedirect(user)"> <a :href="profileUrlRedirect(user)">
<img class="mr-3 rounded-circle box-shadow" :src="user.avatar" :alt="user.username + 's avatar'" width="30px" loading="lazy" onerror="this.onerror=null;this.src='/storage/avatars/default.png?v=0'"> <img class="mr-3 rounded-circle box-shadow" :src="user.avatar" :alt="user.username + 's avatar'" width="30px" loading="lazy" onerror="this.src='/storage/avatars/default.jpg?v=0';this.onerror=null">
</a> </a>
<div class="media-body text-truncate"> <div class="media-body text-truncate">
<p class="mb-0" style="font-size: 14px"> <p class="mb-0" style="font-size: 14px">
@ -368,7 +362,7 @@
</div> </div>
</div> </div>
</div> </div>
<div v-if="followingModalSearch && following.length == 0" class="list-group-item border-0"> <div v-if="!followingLoading && following.length == 0" class="list-group-item border-0">
<div class="list-group-item border-0 pt-5"> <div class="list-group-item border-0 pt-5">
<p class="p-3 text-center mb-0 lead">No Results Found</p> <p class="p-3 text-center mb-0 lead">No Results Found</p>
</div> </div>
@ -394,7 +388,7 @@
dialog-class="follow-modal" dialog-class="follow-modal"
> >
<div v-if="!followerLoading" class="list-group" style="max-height: 60vh;"> <div v-if="!followerLoading" class="list-group" style="max-height: 60vh;">
<div v-if="!followers.length" class="list-group-item border-0"> <div v-if="!followerLoading && !followers.length" class="list-group-item border-0">
<p class="text-center mb-0 font-weight-bold text-muted py-5"> <p class="text-center mb-0 font-weight-bold text-muted py-5">
<span class="text-dark">{{profileUsername}}</span> has no followers yet</p> <span class="text-dark">{{profileUsername}}</span> has no followers yet</p>
</div> </div>
@ -403,7 +397,7 @@
<div class="list-group-item border-0 py-1 mb-1" v-for="(user, index) in followers" :key="'follower_'+index"> <div class="list-group-item border-0 py-1 mb-1" v-for="(user, index) in followers" :key="'follower_'+index">
<div class="media mb-0"> <div class="media mb-0">
<a :href="profileUrlRedirect(user)"> <a :href="profileUrlRedirect(user)">
<img class="mr-3 rounded-circle box-shadow" :src="user.avatar" :alt="user.username + 's avatar'" width="30px" height="30px" loading="lazy" onerror="this.onerror=null;this.src='/storage/avatars/default.png?v=0'"> <img class="mr-3 rounded-circle box-shadow" :src="user.avatar" :alt="user.username + 's avatar'" width="30px" height="30px" loading="lazy" onerror="this.src='/storage/avatars/default.jpg?v=0';this.onerror=null">
</a> </a>
<div class="media-body mb-0"> <div class="media-body mb-0">
<p class="mb-0" style="font-size: 14px"> <p class="mb-0" style="font-size: 14px">
@ -593,6 +587,7 @@
<script type="text/javascript"> <script type="text/javascript">
import VueMasonry from 'vue-masonry-css' import VueMasonry from 'vue-masonry-css'
import StatusCard from './partials/StatusCard.vue'; import StatusCard from './partials/StatusCard.vue';
import { parseLinkHeader } from '@web3-storage/parse-link-header';
export default { export default {
props: [ props: [
@ -623,11 +618,11 @@
modalStatus: false, modalStatus: false,
relationship: {}, relationship: {},
followers: [], followers: [],
followerCursor: 1, followerCursor: null,
followerMore: true, followerMore: true,
followerLoading: true, followerLoading: true,
following: [], following: [],
followingCursor: 1, followingCursor: null,
followingMore: true, followingMore: true,
followingLoading: true, followingLoading: true,
warning: false, warning: false,
@ -641,8 +636,6 @@
ctxEmbedPayload: null, ctxEmbedPayload: null,
copiedEmbed: false, copiedEmbed: false,
hasStory: null, hasStory: null,
followingModalSearch: null,
followingModalSearchCache: null,
followingModalTab: 'following', followingModalTab: 'following',
bookmarksLoading: true, bookmarksLoading: true,
archives: [], archives: [],
@ -1084,23 +1077,33 @@
if(this.profileSettings.following.list == false) { if(this.profileSettings.following.list == false) {
return; return;
} }
if(this.followingCursor > 1) { if(this.followingCursor) {
this.$refs.followingModal.show(); this.$refs.followingModal.show();
return; return;
} else { } else {
axios.get('/api/pixelfed/v1/accounts/'+this.profileId+'/following', { axios.get('/api/v1/accounts/'+this.profileId+'/following', {
params: { params: {
page: this.followingCursor cursor: this.followingCursor,
limit: 40
} }
}) })
.then(res => { .then(res => {
this.following = res.data; this.following = res.data;
this.followingModalSearchCache = res.data;
this.followingCursor++; if(res.headers && res.headers.link) {
if(res.data.length < 10) { const links = parseLinkHeader(res.headers.link);
if(links.prev) {
this.followingCursor = links.prev.cursor;
this.followingMore = true;
} else {
this.followingMore = false;
}
} else {
this.followingMore = false; this.followingMore = false;
} }
this.followingLoading = false; })
.then(() => {
setTimeout(() => { this.followingLoading = false }, 1000);
}); });
this.$refs.followingModal.show(); this.$refs.followingModal.show();
return; return;
@ -1119,19 +1122,29 @@
this.$refs.followerModal.show(); this.$refs.followerModal.show();
return; return;
} else { } else {
axios.get('/api/pixelfed/v1/accounts/'+this.profileId+'/followers', { axios.get('/api/v1/accounts/'+this.profileId+'/followers', {
params: { params: {
page: this.followerCursor cursor: this.followerCursor,
limit: 40
} }
}) })
.then(res => { .then(res => {
this.followers.push(...res.data); this.followers.push(...res.data);
this.followerCursor++; if(res.headers && res.headers.link) {
if(res.data.length < 10) { const links = parseLinkHeader(res.headers.link);
if(links.prev) {
this.followerCursor = links.prev.cursor;
this.followerMore = true;
} else {
this.followerMore = false;
}
} else {
this.followerMore = false; this.followerMore = false;
} }
this.followerLoading = false;
}) })
.then(() => {
setTimeout(() => { this.followerLoading = false }, 1000);
});
this.$refs.followerModal.show(); this.$refs.followerModal.show();
return; return;
} }
@ -1142,20 +1155,26 @@
window.location.href = encodeURI('/login?next=/' + this.profile.username + '/'); window.location.href = encodeURI('/login?next=/' + this.profile.username + '/');
return; return;
} }
axios.get('/api/pixelfed/v1/accounts/'+this.profile.id+'/following', { axios.get('/api/v1/accounts/'+this.profile.id+'/following', {
params: { params: {
page: this.followingCursor, cursor: this.followingCursor,
fbu: this.followingModalSearch limit: 40
} }
}) })
.then(res => { .then(res => {
if(res.data.length > 0) { if(res.data.length > 0) {
this.following.push(...res.data); this.following.push(...res.data);
this.followingCursor++;
this.followingModalSearchCache = this.following;
} }
if(res.data.length < 10) {
this.followingModalSearchCache = this.following; if(res.headers && res.headers.link) {
const links = parseLinkHeader(res.headers.link);
if(links.prev) {
this.followingCursor = links.prev.cursor;
this.followingMore = true;
} else {
this.followingMore = false;
}
} else {
this.followingMore = false; this.followingMore = false;
} }
}); });
@ -1165,17 +1184,26 @@
if($('body').hasClass('loggedIn') == false) { if($('body').hasClass('loggedIn') == false) {
return; return;
} }
axios.get('/api/pixelfed/v1/accounts/'+this.profile.id+'/followers', { axios.get('/api/v1/accounts/'+this.profile.id+'/followers', {
params: { params: {
page: this.followerCursor cursor: this.followerCursor,
limit: 40
} }
}) })
.then(res => { .then(res => {
if(res.data.length > 0) { if(res.data.length > 0) {
this.followers.push(...res.data); this.followers.push(...res.data);
this.followerCursor++;
} }
if(res.data.length < 10) {
if(res.headers && res.headers.link) {
const links = parseLinkHeader(res.headers.link);
if(links.prev) {
this.followerCursor = links.prev.cursor;
this.followerMore = true;
} else {
this.followerMore = false;
}
} else {
this.followerMore = false; this.followerMore = false;
} }
}); });
@ -1284,28 +1312,6 @@
window.location.href = '/stories/' + this.profileUsername + '?t=4'; window.location.href = '/stories/' + this.profileUsername + '?t=4';
}, },
followingModalSearchHandler() {
let self = this;
let q = this.followingModalSearch;
if(q.length == 0) {
this.following = this.followingModalSearchCache;
this.followingModalSearch = null;
}
if(q.length > 0) {
let url = '/api/pixelfed/v1/accounts/' +
self.profileId + '/following?page=1&fbu=' +
q;
axios.get(url).then(res => {
this.following = res.data;
}).catch(err => {
self.following = self.followingModalSearchCache;
self.followingModalSearch = null;
});
}
},
truncate(str, len) { truncate(str, len) {
return _.truncate(str, { return _.truncate(str, {
length: len length: len