Update legacy components to properly handle follow/unfollowing

This commit is contained in:
Daniel Supernault 2023-03-05 02:45:15 -07:00
parent 87dfc55b43
commit 60fbf0e7d5
No known key found for this signature in database
GPG key ID: 0DEF1C662C9033F7
9 changed files with 80 additions and 436 deletions

View file

@ -484,8 +484,6 @@
size="sm"
body-class="list-group-flush p-0 rounded">
<div class="list-group text-center">
<!-- <div v-if="user && user.id != status.account.id && relationship && relationship.following" class="list-group-item rounded cursor-pointer font-weight-bold text-danger" @click="ctxMenuUnfollow()">Unfollow</div>
<div v-if="user && user.id != status.account.id && relationship && !relationship.following" class="list-group-item rounded cursor-pointer font-weight-bold text-primary" @click="ctxMenuFollow()">Follow</div> -->
<div v-if="status && status.local == true" class="list-group-item rounded cursor-pointer" @click="showEmbedPostModal()">Embed</div>
<div class="list-group-item rounded cursor-pointer" @click="ctxMenuCopyLink()">Copy Link</div>
<div v-if="status && user.id == status.account.id" class="list-group-item rounded cursor-pointer" @click="toggleCommentVisibility">{{ showComments ? 'Disable' : 'Enable'}} Comments</div>
@ -1605,34 +1603,6 @@ export default {
return;
},
ctxMenuFollow() {
let id = this.status.account.id;
axios.post('/i/follow', {
item: id
}).then(res => {
let username = this.status.account.acct;
this.relationship.following = true;
this.$refs.ctxModal.hide();
setTimeout(function() {
swal('Follow successful!', 'You are now following ' + username, 'success');
}, 500);
});
},
ctxMenuUnfollow() {
let id = this.status.account.id;
axios.post('/i/follow', {
item: id
}).then(res => {
let username = this.status.account.acct;
this.relationship.following = false;
this.$refs.ctxModal.hide();
setTimeout(function() {
swal('Unfollow successful!', 'You are no longer following ' + username, 'success');
}, 500);
});
},
archivePost(status) {
if(window.confirm('Are you sure you want to archive this post?') == false) {
return;

View file

@ -342,7 +342,7 @@
<div class="list-group-item border-0 py-1 mb-1" v-for="(user, index) in following" :key="'following_'+index">
<div class="media">
<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.src='/storage/avatars/default.jpg?v=0';this.onerror=null">
<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;" v-once>
</a>
<div class="media-body text-truncate">
<p class="mb-0" style="font-size: 14px">
@ -397,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="media mb-0">
<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.src='/storage/avatars/default.jpg?v=0';this.onerror=null">
<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;" v-once>
</a>
<div class="media-body mb-0">
<p class="mb-0" style="font-size: 14px">
@ -1049,19 +1049,22 @@
if($('body').hasClass('loggedIn') == false) {
return;
}
axios.post('/i/follow', {
item: this.profileId
}).then(res => {
this.$refs.visitorContextMenu.hide();
if(this.relationship.following) {
this.$refs.visitorContextMenu.hide();
const curState = this.relationship.following;
const apiUrl = curState ?
'/api/v1/accounts/' + this.profileId + '/unfollow' :
'/api/v1/accounts/' + this.profileId + '/follow';
axios.post(apiUrl)
.then(res => {
if(curState) {
this.profile.followers_count--;
if(this.profile.locked == true) {
window.location.href = '/';
if(this.profile.locked) {
location.reload();
}
} else {
this.profile.followers_count++;
}
this.relationship.following = !this.relationship.following;
this.relationship = res.data;
}).catch(err => {
if(err.response.data.message) {
swal('Error', err.response.data.message, 'error');
@ -1218,9 +1221,11 @@
},
followModalAction(id, index, type = 'following') {
axios.post('/i/follow', {
item: id
}).then(res => {
const apiUrl = type === 'following' ?
'/api/v1/accounts/' + id + '/unfollow' :
'/api/v1/accounts/' + id + '/follow';
axios.post(apiUrl)
.then(res => {
if(type == 'following') {
this.following.splice(index, 1);
this.profile.following_count--;

View file

@ -379,26 +379,6 @@ export default {
this.searchContext(this.analysis);
},
followProfile(profile, index) {
this.loading = true;
axios.post('/i/follow', {
item: profile.entity.id
}).then(res => {
if(profile.entity.local == true) {
this.fetchSearchResults();
return;
} else {
this.loading = false;
this.results.profiles[index].entity.follow_request = true;
return;
}
}).catch(err => {
if(err.response.data.message) {
swal('Error', err.response.data.message, 'error');
}
});
},
searchLexer() {
let q = this.query;

View file

@ -283,21 +283,21 @@
</div> -->
</div>
<div class="card-footer bg-transparent border-0 pt-0 pb-1">
<div class="d-flex justify-content-between text-center">
<span class="cursor-pointer" @click="redirect(profile.url)">
<p class="mb-0 font-weight-bold">{{formatCount(profile.statuses_count)}}</p>
<p class="mb-0 small text-muted">Posts</p>
</span>
<span class="cursor-pointer" @click="redirect(profile.url+'?md=followers')">
<p class="mb-0 font-weight-bold">{{formatCount(profile.followers_count)}}</p>
<p class="mb-0 small text-muted">Followers</p>
</span>
<span class="cursor-pointer" @click="redirect(profile.url+'?md=following')">
<p class="mb-0 font-weight-bold">{{formatCount(profile.following_count)}}</p>
<p class="mb-0 small text-muted">Following</p>
</span>
</div>
</div>
<div class="d-flex justify-content-between text-center">
<span class="cursor-pointer" @click="redirect(profile.url)">
<p class="mb-0 font-weight-bold">{{formatCount(profile.statuses_count)}}</p>
<p class="mb-0 small text-muted">Posts</p>
</span>
<span class="cursor-pointer" @click="redirect(profile.url+'?md=followers')">
<p class="mb-0 font-weight-bold">{{formatCount(profile.followers_count)}}</p>
<p class="mb-0 small text-muted">Followers</p>
</span>
<span class="cursor-pointer" @click="redirect(profile.url+'?md=following')">
<p class="mb-0 font-weight-bold">{{formatCount(profile.following_count)}}</p>
<p class="mb-0 small text-muted">Following</p>
</span>
</div>
</div>
</div>
<div v-show="modes.notify == true && !loading" class="mb-4">
@ -324,7 +324,6 @@
</p>
<p class="mb-0 small text-muted">{{rec.message}}</p>
</div>
<a class="font-weight-bold small" href="#" @click.prevent="expRecFollow(rec.id, index)">Follow</a>
</div>
</div>
</div>
@ -761,24 +760,6 @@
})
},
expRecFollow(id, index) {
return;
if(this.config.ab.rec == false) {
return;
}
axios.post('/i/follow', {
item: id
}).then(res => {
this.suggestions.splice(index, 1);
}).catch(err => {
if(err.response.data.message) {
swal('Error', err.response.data.message, 'error');
}
});
},
owner(status) {
return this.profile.id === status.account.id;
},
@ -865,34 +846,34 @@
},
commentFocus(status, $event) {
if(status.comments_disabled) {
return;
}
if(status.comments_disabled) {
return;
}
// if(this.status && this.status.id == status.id) {
// this.$refs.replyModal.show();
// return;
// }
// if(this.status && this.status.id == status.id) {
// this.$refs.replyModal.show();
// return;
// }
this.status = status;
this.replies = {};
this.replyStatus = {};
this.replyText = '';
this.replyId = status.id;
this.replyStatus = status;
// this.$refs.replyModal.show();
this.fetchStatusComments(status, '');
this.status = status;
this.replies = {};
this.replyStatus = {};
this.replyText = '';
this.replyId = status.id;
this.replyStatus = status;
// this.$refs.replyModal.show();
this.fetchStatusComments(status, '');
$('nav').hide();
$('footer').hide();
$('.mobile-footer-spacer').attr('style', 'display:none !important');
$('.mobile-footer').attr('style', 'display:none !important');
this.currentLayout = 'comments';
window.history.pushState({}, '', this.statusUrl(status));
return;
},
$('nav').hide();
$('footer').hide();
$('.mobile-footer-spacer').attr('style', 'display:none !important');
$('.mobile-footer').attr('style', 'display:none !important');
this.currentLayout = 'comments';
window.history.pushState({}, '', this.statusUrl(status));
return;
},
fetchStatusComments(status, card) {
fetchStatusComments(status, card) {
let url = '/api/v2/comments/'+status.account.id+'/status/'+status.id;
axios.get(url)
.then(response => {

View file

@ -9,8 +9,6 @@
size="sm"
body-class="list-group-flush p-0 rounded">
<div class="list-group text-center">
<!-- <div v-if="status && status.account.id != profile.id && ctxMenuRelationship && ctxMenuRelationship.following" class="list-group-item rounded cursor-pointer font-weight-bold text-danger" @click="ctxMenuUnfollow()">Unfollow</div>
<div v-if="status && status.account.id != profile.id && ctxMenuRelationship && !ctxMenuRelationship.following" class="list-group-item rounded cursor-pointer font-weight-bold text-primary" @click="ctxMenuFollow()">Follow</div> -->
<div v-if="status.visibility !== 'archived'" class="list-group-item rounded cursor-pointer" @click="ctxMenuGoToPost()">View Post</div>
<div v-if="status.visibility !== 'archived'" class="list-group-item rounded cursor-pointer" @click="ctxMenuGoToProfile()">View Profile</div>
<!-- <div v-if="status && status.local == true && !status.in_reply_to_id" class="list-group-item rounded cursor-pointer" @click="ctxMenuEmbed()">Embed</div>
@ -289,37 +287,6 @@
return;
},
ctxMenuFollow() {
let id = this.ctxMenuStatus.account.id;
axios.post('/i/follow', {
item: id
}).then(res => {
let username = this.ctxMenuStatus.account.acct;
this.closeCtxMenu();
setTimeout(function() {
swal('Follow successful!', 'You are now following ' + username, 'success');
}, 500);
});
},
ctxMenuUnfollow() {
let id = this.ctxMenuStatus.account.id;
axios.post('/i/follow', {
item: id
}).then(res => {
let username = this.ctxMenuStatus.account.acct;
if(this.scope == 'home') {
this.feed = this.feed.filter(s => {
return s.account.id != this.ctxMenuStatus.account.id;
});
}
this.closeCtxMenu();
setTimeout(function() {
swal('Unfollow successful!', 'You are no longer following ' + username, 'success');
}, 500);
});
},
ctxMenuReportPost() {
this.$refs.ctxModal.hide();
this.$refs.ctxReport.show();

View file

@ -71,14 +71,7 @@
<a v-if="status.place" class="small text-decoration-none text-muted" :href="'/discover/places/'+status.place.id+'/'+status.place.slug" title="Location" data-toggle="tooltip"><i class="fas fa-map-marked-alt"></i> {{status.place.name}}, {{status.place.country}}</a>
</div>
</div>
<div v-if="canFollow(status)">
<span class="px-2"></span>
<button class="btn btn-primary btn-sm font-weight-bold py-1 px-3 rounded-lg" @click="follow(status.account.id)"><i class="far fa-user-plus mr-1"></i> Follow</button>
</div>
<div v-if="status.hasOwnProperty('relationship') && status.relationship.hasOwnProperty('following') && status.relationship.following">
<span class="px-2"></span>
<button class="btn btn-outline-primary btn-sm font-weight-bold py-1 px-3 rounded-lg" @click="unfollow(status.account.id)"><i class="far fa-user-check mr-1"></i> Following</button>
</div>
<div class="text-right" style="flex-grow:1;">
<button class="btn btn-link text-dark py-0" type="button" @click="ctxMenu()">
<span class="fas fa-ellipsis-h text-lighter"></span>
@ -397,52 +390,6 @@
statusDeleted(status) {
this.$emit('status-delete', status);
},
canFollow(status) {
if(!status.hasOwnProperty('relationship')) {
return false;
}
if(!status.hasOwnProperty('account') || !status.account.hasOwnProperty('id')) {
return false;
}
if(status.account.id == this.profile.id) {
return false;
}
return !status.relationship.following;
},
follow(id) {
event.currentTarget.blur();
axios.post('/i/follow', {
item: id
}).then(res => {
this.status.relationship.following = true;
this.$emit('followed', id);
}).catch(err => {
if(err.response.data.message) {
swal('Error', err.response.data.message, 'error');
}
});
},
unfollow(id) {
event.currentTarget.blur();
axios.post('/i/follow', {
item: id
}).then(res => {
this.status.relationship.following = false;
this.$emit('unfollowed', id);
}).catch(err => {
if(err.response.data.message) {
swal('Error', err.response.data.message, 'error');
}
});
}
}
}

View file

@ -1,121 +0,0 @@
@extends('layouts.app',['title' => $user->username . " on " . config('app.name')])
@section('content')
@if (session('error'))
<div class="alert alert-danger text-center font-weight-bold mb-0">
{{ session('error') }}
</div>
@endif
@include('profile.partial.user-info')
@if(true === $owner)
<div>
<ul class="nav nav-topbar d-flex justify-content-center border-0">
<li class="nav-item">
<a class="nav-link {{request()->is($user->username) ? 'active': ''}} font-weight-bold text-uppercase" href="{{$user->url()}}">Posts</a>
</li>
{{-- <li class="nav-item">
<a class="nav-link {{request()->is('*/collections') ? 'active': ''}} font-weight-bold text-uppercase" href="{{$user->url()}}/collections">Collections</a>
</li> --}}
<li class="nav-item">
<a class="nav-link {{request()->is('*/saved') ? 'active':''}} font-weight-bold text-uppercase" href="{{$user->url('/saved')}}">Saved</a>
</li>
</ul>
</div>
@endif
<div class="container">
@if($owner && request()->is('*/saved'))
<div class="col-12">
<p class="text-muted font-weight-bold small">{{__('profile.savedWarning')}}</p>
</div>
@endif
<div class="profile-timeline mt-2 mt-md-4">
<div class="row">
@if($timeline->count() > 0)
@foreach($timeline as $status)
<div class="col-4 p-0 p-sm-2 p-md-3">
<a class="card info-overlay card-md-border-0" href="{{$status->url()}}">
<div class="square {{$status->firstMedia()->filter_class}}">
@switch($status->viewType())
@case('album')
@case('photo:album')
<span class="float-right mr-3" style="color:#fff;position:relative;margin-top:10px;z-index: 999999;opacity:0.6;text-shadow: 3px 3px 16px #272634;"><i class="fas fa-images fa-2x"></i></span>
@break
@case('video')
<span class="float-right mr-3" style="color:#fff;position:relative;margin-top:10px;z-index: 999999;opacity:0.6;text-shadow: 3px 3px 16px #272634;"><i class="fas fa-video fa-2x"></i></span>
@break
@case('video-album')
<span class="float-right mr-3" style="color:#fff;position:relative;margin-top:10px;z-index: 999999;opacity:0.6;text-shadow: 3px 3px 16px #272634;"><i class="fas fa-film fa-2x"></i></span>
@break
@endswitch
<div class="square-content" style="background-image: url('{{$status->thumb()}}')">
</div>
<div class="info-overlay-text">
<h5 class="text-white m-auto font-weight-bold">
<span>
<span class="far fa-heart fa-lg p-2 d-flex-inline"></span>
<span class="d-flex-inline">{{App\Util\Lexer\PrettyNumber::convert($status->likes_count)}}</span>
</span>
<span>
<span class="far fa-comment fa-lg p-2 d-flex-inline"></span>
<span class="d-flex-inline">{{App\Util\Lexer\PrettyNumber::convert($status->comments_count)}}</span>
</span>
</h5>
</div>
</div>
</a>
</div>
@endforeach
</div>
</div>
<div class="pagination-container">
<div class="d-flex justify-content-center">
{{$timeline->links()}}
</div>
</div>
@else
<div class="col-12">
<div class="card">
<div class="card-body py-5 my-5">
<div class="d-flex my-5 py-5 justify-content-center align-items-center">
@if($owner && request()->is('*/saved'))
<p class="lead font-weight-bold">{{ __('profile.emptySaved') }}</p>
@else
<p class="lead font-weight-bold">{{ __('profile.emptyTimeline') }}</p>
@endif
</div>
</div>
</div>
</div>
</div>
@endif
</div>
@endsection
@push('meta')<meta property="og:description" content="{{$user->bio}}">
<meta property="og:image" content="{{$user->avatarUrl()}}">
<link href="{{$user->permalink('.atom')}}" rel="alternate" title="{{$user->username}} on Pixelfed" type="application/atom+xml">
@if(false == $settings->crawlable || $user->remote_url)
<meta name="robots" content="noindex, nofollow">
@endif
@endpush
@push('scripts')
<script type="text/javascript">
$(document).ready(function() {
$('.pagination-container').hide();
$('.pagination').hide();
let elem = document.querySelector('.profile-timeline');
let infScroll = new InfiniteScroll( elem, {
path: '.pagination__next',
append: '.profile-timeline .row',
status: '.page-load-status',
history: false,
});
});
</script>
@endpush

View file

@ -10,28 +10,20 @@
<div class="profile-details">
<div class="username-bar pb-2 d-flex align-items-center">
<span class="font-weight-ultralight h3">{{$user->username}}</span>
@if(Auth::check() && $is_following == true)
@auth
@if($is_following == true)
<span class="pl-4">
<form class="follow-form" method="post" action="/i/follow" style="display: inline;" data-id="{{$user->id}}" data-action="unfollow">
@csrf
<input type="hidden" name="item" value="{{$user->id}}">
<button class="btn btn-outline-secondary font-weight-bold px-4 py-0" type="submit">Unfollow</button>
</form>
<button class="btn btn-outline-secondary font-weight-bold px-4 py-0" type="button" onclick="unfollowProfile()">Unfollow</button>
</span>
@elseif(Auth::check() && $requested == true)
@elseif($requested == true)
<span class="pl-4">
<button class="btn btn-outline-secondary font-weight-bold px-4 py-0 disabled" disabled type="button">Follow Requested</button>
<button class="btn btn-outline-secondary font-weight-bold px-4 py-0" type="button" onclick="unfollowProfile()">Follow Requested</button>
</span>
@elseif(Auth::check() && $is_following == false)
@elseif($is_following == false)
<span class="pl-4">
<form class="follow-form" method="post" action="/i/follow" style="display: inline;" data-id="{{$user->id}}" data-action="follow">
@csrf
<input type="hidden" name="item" value="{{$user->id}}">
<button class="btn btn-primary font-weight-bold px-4 py-0" type="submit">Follow</button>
</form>
<button class="btn btn-primary font-weight-bold px-4 py-0" type="button" onclick="followProfile()">Follow</button>
</span>
@endif
@auth
<span class="pl-4">
<i class="fas fa-cog fa-lg text-muted cursor-pointer" data-toggle="modal" data-target="#ctxProfileMenu"></i>
<div class="modal" tabindex="-1" role="dialog" id="ctxProfileMenu">
@ -81,6 +73,7 @@
swal('Muted Profile', 'You have successfully muted this profile.', 'success');
});
}
function blockProfile() {
axios.post('/i/block', {
type: 'user',
@ -92,6 +85,20 @@
});
}
function followProfile() {
axios.post('/api/v1/accounts/{{$user->id}}/follow')
.then(res => {
location.reload();
})
}
function unfollowProfile() {
axios.post('/api/v1/accounts/{{$user->id}}/unfollow')
.then(res => {
location.reload();
})
}
</script>
@endauth
@endpush

View file

@ -1,92 +0,0 @@
<div class="bg-white py-5 border-bottom">
<div class="container">
<div class="row">
<div class="col-12 col-md-4 d-flex">
<div class="profile-avatar mx-auto">
<img class="rounded-circle box-shadow" src="{{$user->avatarUrl()}}" width="172px" height="172px">
</div>
</div>
<div class="col-12 col-md-8 d-flex align-items-center">
<div class="profile-details">
<div class="username-bar pb-2 d-flex align-items-center">
<span class="font-weight-ultralight h1">{{$user->username}}</span>
@if($is_admin == true)
<span class="pl-4">
<span class="btn btn-outline-danger font-weight-bold py-0">ADMIN</span>
</span>
@endif
@if($owner == true)
<span class="pl-4">
<a class="fas fa-cog fa-lg text-muted" href="{{route('settings')}}"></a>
</span>
@elseif (Auth::check() && $is_following == true)
<span class="pl-4">
<form class="follow-form" method="post" action="/i/follow" style="display: inline;" data-id="{{$user->id}}" data-action="unfollow">
@csrf
<input type="hidden" name="item" value="{{$user->id}}">
<button class="btn btn-outline-secondary font-weight-bold px-4 py-0" type="submit">Unfollow</button>
</form>
</span>
@elseif (Auth::check() && $is_following == false)
<span class="pl-4">
<form class="follow-form" method="post" action="/i/follow" style="display: inline;" data-id="{{$user->id}}" data-action="follow">
@csrf
<input type="hidden" name="item" value="{{$user->id}}">
<button class="btn btn-primary font-weight-bold px-4 py-0" type="submit">Follow</button>
</form>
</span>
@endif
{{-- <span class="pl-4">
<div class="dropdown">
<button class="btn btn-link text-muted dropdown-toggle py-0" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" style="text-decoration: none;">
<i class="fas fa-ellipsis-v"></i>
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item font-weight-bold" href="#">Report User</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item font-weight-bold" href="#">Mute User</a>
<a class="dropdown-item font-weight-bold" href="#">Block User</a>
<a class="dropdown-item font-weight-bold mute-users" href="#">Mute User & User Followers</a>
<a class="dropdown-item font-weight-bold" href="#">Block User & User Followers</a>
</div>
</div>
</span>
--}}
</div>
<div class="profile-stats pb-3 d-inline-flex lead">
<div class="font-weight-light pr-5">
<a class="text-dark" href="{{$user->url()}}">
<span class="font-weight-bold">{{$user->statusCount()}}</span>
Posts
</a>
</div>
@if($settings->show_profile_follower_count)
<div class="font-weight-light pr-5">
<a class="text-dark" href="{{$user->url('/followers')}}">
<span class="font-weight-bold">{{$user->followerCount(true)}}</span>
Followers
</a>
</div>
@endif
@if($settings->show_profile_following_count)
<div class="font-weight-light pr-5">
<a class="text-dark" href="{{$user->url('/following')}}">
<span class="font-weight-bold">{{$user->followingCount(true)}}</span>
Following
</a>
</div>
@endif
</div>
<p class="lead mb-0 d-flex align-items-center">
<span class="font-weight-bold pr-3">{{$user->name}}</span>
@if($user->remote_url)
<span class="btn btn-outline-secondary btn-sm py-0">REMOTE PROFILE</span>
@endif
</p>
<div class="mb-0 lead" v-pre>{!!str_limit($user->bio, 127)!!}</div>
<p class="mb-0"><a href="{{$user->website}}" class="font-weight-bold" rel="me external nofollow noopener" target="_blank">{{str_limit($user->website, 30)}}</a></p>
</div>
</div>
</div>
</div>
</div>