mirror of
https://github.com/pixelfed/pixelfed.git
synced 2024-11-22 06:21:27 +00:00
Add infinite scroll to profiles, and support private profiles
This commit is contained in:
parent
b29ca1823d
commit
4ba9e2cd6c
6 changed files with 201 additions and 63 deletions
|
@ -18,6 +18,7 @@ class ProfileController extends Controller
|
|||
public function show(Request $request, $username)
|
||||
{
|
||||
$user = Profile::whereUsername($username)->firstOrFail();
|
||||
$settings = User::whereUsername($username)->firstOrFail()->settings;
|
||||
|
||||
$mimes = [
|
||||
'application/activity+json',
|
||||
|
@ -27,7 +28,12 @@ class ProfileController extends Controller
|
|||
if(in_array($request->header('accept'), $mimes) && config('pixelfed.activitypub_enabled')) {
|
||||
return $this->showActivityPub($request, $user);
|
||||
}
|
||||
|
||||
if($user->is_private == true) {
|
||||
$can_access = $this->privateProfileCheck($user);
|
||||
if($can_access !== true) {
|
||||
abort(403);
|
||||
}
|
||||
}
|
||||
// TODO: refactor this mess
|
||||
$owner = Auth::check() && Auth::id() === $user->user_id;
|
||||
$is_following = ($owner == false && Auth::check()) ? $user->followedBy(Auth::user()->profile) : false;
|
||||
|
@ -39,7 +45,22 @@ class ProfileController extends Controller
|
|||
->withCount(['comments', 'likes'])
|
||||
->simplePaginate(21);
|
||||
|
||||
return view('profile.show', compact('user', 'owner', 'is_following', 'is_admin', 'timeline'));
|
||||
return view('profile.show', compact('user', 'settings', 'owner', 'is_following', 'is_admin', 'timeline'));
|
||||
}
|
||||
|
||||
protected function privateProfileCheck(Profile $profile)
|
||||
{
|
||||
if(Auth::check() === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$follower_ids = (array) $profile->followers()->pluck('followers.profile_id');
|
||||
$pid = Auth::user()->profile->id;
|
||||
if(!in_array($pid, $follower_ids) && $pid !== $profile->id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function showActivityPub(Request $request, $user)
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\{Profile, User};
|
||||
use Auth;
|
||||
use App\{AccountLog, Profile, User};
|
||||
use Auth, DB;
|
||||
|
||||
class SettingsController extends Controller
|
||||
{
|
||||
|
@ -89,6 +89,34 @@ class SettingsController extends Controller
|
|||
return view('settings.avatar');
|
||||
}
|
||||
|
||||
public function accessibility()
|
||||
{
|
||||
$settings = Auth::user()->settings;
|
||||
return view('settings.accessibility', compact('settings'));
|
||||
}
|
||||
|
||||
public function accessibilityStore(Request $request)
|
||||
{
|
||||
$settings = Auth::user()->settings;
|
||||
$fields = [
|
||||
'compose_media_descriptions',
|
||||
'reduce_motion',
|
||||
'optimize_screen_reader',
|
||||
'high_contrast_mode',
|
||||
'video_autoplay'
|
||||
];
|
||||
foreach($fields as $field) {
|
||||
$form = $request->input($field);
|
||||
if($form == 'on') {
|
||||
$settings->{$field} = true;
|
||||
} else {
|
||||
$settings->{$field} = false;
|
||||
}
|
||||
$settings->save();
|
||||
}
|
||||
return redirect(route('settings.accessibility'))->with('status', 'Settings successfully updated!');
|
||||
}
|
||||
|
||||
public function notifications()
|
||||
{
|
||||
return view('settings.notifications');
|
||||
|
@ -96,12 +124,61 @@ class SettingsController extends Controller
|
|||
|
||||
public function privacy()
|
||||
{
|
||||
return view('settings.privacy');
|
||||
$settings = Auth::user()->settings;
|
||||
$is_private = Auth::user()->profile->is_private;
|
||||
$settings['is_private'] = (bool) $is_private;
|
||||
return view('settings.privacy', compact('settings'));
|
||||
}
|
||||
|
||||
public function privacyStore(Request $request)
|
||||
{
|
||||
$settings = Auth::user()->settings;
|
||||
$profile = Auth::user()->profile;
|
||||
$fields = [
|
||||
'is_private',
|
||||
'crawlable',
|
||||
];
|
||||
foreach($fields as $field) {
|
||||
$form = $request->input($field);
|
||||
if($field == 'is_private') {
|
||||
if($form == 'on') {
|
||||
$profile->{$field} = true;
|
||||
$settings->show_guests = false;
|
||||
$settings->show_discover = false;
|
||||
$profile->save();
|
||||
} else {
|
||||
$profile->{$field} = false;
|
||||
$profile->save();
|
||||
}
|
||||
} elseif($field == 'crawlable') {
|
||||
if($form == 'on') {
|
||||
$settings->{$field} = false;
|
||||
} else {
|
||||
$settings->{$field} = true;
|
||||
}
|
||||
} else {
|
||||
if($form == 'on') {
|
||||
$settings->{$field} = true;
|
||||
} else {
|
||||
$settings->{$field} = false;
|
||||
}
|
||||
}
|
||||
$settings->save();
|
||||
}
|
||||
return redirect(route('settings.privacy'))->with('status', 'Settings successfully updated!');
|
||||
}
|
||||
|
||||
public function security()
|
||||
{
|
||||
return view('settings.security');
|
||||
$sessions = DB::table('sessions')
|
||||
->whereUserId(Auth::id())
|
||||
->limit(20)
|
||||
->get();
|
||||
$activity = AccountLog::whereUserId(Auth::id())
|
||||
->orderBy('created_at','desc')
|
||||
->limit(50)
|
||||
->get();
|
||||
return view('settings.security', compact('sessions', 'activity'));
|
||||
}
|
||||
|
||||
public function applications()
|
||||
|
@ -121,7 +198,7 @@ class SettingsController extends Controller
|
|||
|
||||
public function dataImportInstagram()
|
||||
{
|
||||
return view('settings.import.ig');
|
||||
return view('settings.import.instagram.home');
|
||||
}
|
||||
|
||||
public function developers()
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
@include('profile.partial.user-info')
|
||||
|
||||
@if($owner == true)
|
||||
@if(true === $owner)
|
||||
<div>
|
||||
<ul class="nav nav-topbar d-flex justify-content-center border-0">
|
||||
<li class="nav-item">
|
||||
|
@ -16,51 +16,79 @@
|
|||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
<div class="container">
|
||||
<div class="profile-timeline mt-5 row">
|
||||
<div class="container mt-5">
|
||||
@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">
|
||||
<div class="row">
|
||||
@if($timeline->count() > 0)
|
||||
@foreach($timeline as $status)
|
||||
<div class="col-12 col-md-4 mb-4">
|
||||
<a class="card info-overlay" href="{{$status->url()}}">
|
||||
<div class="square {{$status->firstMedia()->filter_class}}">
|
||||
<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 class="pr-4">
|
||||
<span class="far fa-heart fa-lg pr-1"></span> {{$status->likes_count}}
|
||||
</span>
|
||||
<span>
|
||||
<span class="far fa-comment fa-lg pr-1"></span> {{$status->comments_count}}
|
||||
</span>
|
||||
</h5>
|
||||
<div class="col-12 col-md-4 mb-4">
|
||||
<a class="card info-overlay" href="{{$status->url()}}">
|
||||
<div class="square {{$status->firstMedia()->filter_class}}">
|
||||
<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 class="pr-4">
|
||||
<span class="far fa-heart fa-lg pr-1"></span> {{$status->likes_count}}
|
||||
</span>
|
||||
<span>
|
||||
<span class="far fa-comment fa-lg pr-1"></span> {{$status->comments_count}}
|
||||
</span>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</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">
|
||||
<p class="lead font-weight-bold">{{ __('profile.emptyTimeline') }}</p>
|
||||
<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">
|
||||
<p class="lead font-weight-bold">{{ __('profile.emptyTimeline') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
||||
@push('meta')
|
||||
<meta property="og:description" content="{{$user->bio}}">
|
||||
<meta property="og:image" content="{{$user->avatarUrl()}}">
|
||||
@push('meta')<meta property="og:description" content="{{$user->bio}}">
|
||||
<meta property="og:image" content="{{$user->avatarUrl()}}">
|
||||
@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',
|
||||
status: '.page-load-status',
|
||||
history: false,
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@endpush
|
||||
|
||||
|
|
|
@ -1,44 +1,44 @@
|
|||
<div class="col-12 col-md-3 py-3" style="border-right:1px solid #ccc;">
|
||||
<ul class="nav flex-column settings-nav">
|
||||
<li class="nav-item pl-3 {{request()->is('settings/home')?'active':''}}">
|
||||
<a class="nav-link lead text-muted" href="{{route('settings')}}">Profile</a>
|
||||
<a class="nav-link font-weight-light text-muted" href="{{route('settings')}}">Profile</a>
|
||||
</li>
|
||||
<li class="nav-item pl-3 {{request()->is('settings/avatar')?'active':''}}">
|
||||
<a class="nav-link lead text-muted" href="{{route('settings.avatar')}}">Avatar</a>
|
||||
<a class="nav-link font-weight-light text-muted" href="{{route('settings.avatar')}}">Avatar</a>
|
||||
</li>
|
||||
<li class="nav-item pl-3 {{request()->is('settings/password')?'active':''}}">
|
||||
<a class="nav-link lead text-muted" href="{{route('settings.password')}}">Password</a>
|
||||
<a class="nav-link font-weight-light text-muted" href="{{route('settings.password')}}">Password</a>
|
||||
</li>
|
||||
<li class="nav-item pl-3 {{request()->is('settings/email')?'active':''}}">
|
||||
<a class="nav-link lead text-muted" href="{{route('settings.email')}}">Email</a>
|
||||
<a class="nav-link font-weight-light text-muted" href="{{route('settings.email')}}">Email</a>
|
||||
</li>
|
||||
<li class="nav-item pl-3 {{request()->is('settings/notifications')?'active':''}}">
|
||||
<a class="nav-link lead text-muted" href="{{route('settings.notifications')}}">Notifications</a>
|
||||
<a class="nav-link font-weight-light text-muted" href="{{route('settings.notifications')}}">Notifications</a>
|
||||
</li>
|
||||
<li class="nav-item pl-3 {{request()->is('settings/privacy')?'active':''}}">
|
||||
<a class="nav-link lead text-muted" href="{{route('settings.privacy')}}">Privacy</a>
|
||||
<a class="nav-link font-weight-light text-muted" href="{{route('settings.privacy')}}">Privacy</a>
|
||||
</li>
|
||||
<li class="nav-item pl-3 {{request()->is('settings/security')?'active':''}}">
|
||||
<a class="nav-link lead text-muted" href="{{route('settings.security')}}">Security</a>
|
||||
<a class="nav-link font-weight-light text-muted" href="{{route('settings.security')}}">Security</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<hr>
|
||||
</li>
|
||||
<li class="nav-item pl-3 {{request()->is('settings/import*')?'active':''}}">
|
||||
<a class="nav-link lead text-muted" href="{{route('settings.import')}}">Import</a>
|
||||
<a class="nav-link font-weight-light text-muted" href="{{route('settings.import')}}">Import</a>
|
||||
</li>
|
||||
<li class="nav-item pl-3 {{request()->is('settings/data-export')?'active':''}}">
|
||||
<a class="nav-link lead text-muted" href="{{route('settings.dataexport')}}">Export</a>
|
||||
<a class="nav-link font-weight-light text-muted" href="{{route('settings.dataexport')}}">Export</a>
|
||||
</li>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<hr>
|
||||
</li>
|
||||
<li class="nav-item pl-3 {{request()->is('settings/applications')?'active':''}}">
|
||||
<a class="nav-link lead text-muted" href="{{route('settings.applications')}}">Applications</a>
|
||||
<a class="nav-link font-weight-light text-muted" href="{{route('settings.applications')}}">Applications</a>
|
||||
</li>
|
||||
<li class="nav-item pl-3 {{request()->is('settings/developers')?'active':''}}">
|
||||
<a class="nav-link lead text-muted" href="{{route('settings.developers')}}">Developers</a>
|
||||
<a class="nav-link font-weight-light text-muted" href="{{route('settings.developers')}}">Developers</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
|
@ -6,8 +6,31 @@
|
|||
<h3 class="font-weight-bold">Privacy Settings</h3>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="alert alert-danger">
|
||||
Coming Soon
|
||||
</div>
|
||||
<form method="post">
|
||||
@csrf
|
||||
<div class="form-check pb-3">
|
||||
<input class="form-check-input" type="checkbox" name="is_private" id="is_private" {{$settings->is_private ? 'checked=""':''}}>
|
||||
<label class="form-check-label font-weight-bold" for="is_private">
|
||||
{{__('Private Account')}}
|
||||
</label>
|
||||
<p class="text-muted small help-text">When your account is private, only people you approve can see your photos and videos on pixelfed. Your existing followers won't be affected.</p>
|
||||
</div>
|
||||
<div class="form-check pb-3">
|
||||
<input class="form-check-input" type="checkbox" name="crawlable" id="crawlable" {{!$settings->crawlable ? 'checked=""':''}} {{$settings->is_private ? 'disabled=""':''}}>
|
||||
<label class="form-check-label font-weight-bold" for="crawlable">
|
||||
{{__('Opt-out of search engine indexing')}}
|
||||
</label>
|
||||
<p class="text-muted small help-text">When your account is visible to search engines, your information can be crawled and stored by search engines.</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="form-group row mt-5 pt-5">
|
||||
<div class="col-12 text-right">
|
||||
<hr>
|
||||
<button type="submit" class="btn btn-primary font-weight-bold">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@endsection
|
|
@ -1,17 +1,5 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Web Routes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you can register web routes for your application. These
|
||||
| routes are loaded by the RouteServiceProvider within a group which
|
||||
| contains the "web" middleware group. Now create something great!
|
||||
|
|
||||
*/
|
||||
|
||||
|
||||
Route::domain(config('pixelfed.domain.admin'))->group(function() {
|
||||
Route::redirect('/', '/dashboard');
|
||||
Route::redirect('timeline', config('app.url').'/timeline');
|
||||
|
@ -91,6 +79,7 @@ Route::domain(config('pixelfed.domain.app'))->middleware('validemail')->group(fu
|
|||
Route::get('email', 'SettingsController@email')->name('settings.email');
|
||||
Route::get('notifications', 'SettingsController@notifications')->name('settings.notifications');
|
||||
Route::get('privacy', 'SettingsController@privacy')->name('settings.privacy');
|
||||
Route::post('privacy', 'SettingsController@privacyStore');
|
||||
Route::get('security', 'SettingsController@security')->name('settings.security');
|
||||
Route::get('applications', 'SettingsController@applications')->name('settings.applications');
|
||||
Route::get('data-export', 'SettingsController@dataExport')->name('settings.dataexport');
|
||||
|
@ -137,4 +126,4 @@ Route::domain(config('pixelfed.domain.app'))->middleware('validemail')->group(fu
|
|||
Route::get('{username}/following', 'ProfileController@following');
|
||||
Route::get('{username}', 'ProfileController@show');
|
||||
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue