Add infinite scroll to profiles, and support private profiles

This commit is contained in:
Daniel Supernault 2018-07-24 22:33:55 -06:00
parent b29ca1823d
commit 4ba9e2cd6c
6 changed files with 201 additions and 63 deletions

View File

@ -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)

View File

@ -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()

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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');
});
});