Merge pull request #2844 from pixelfed/staging

Staging
This commit is contained in:
daniel 2021-07-07 01:12:10 -06:00 committed by GitHub
commit 941a5d0d2d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 356 additions and 401 deletions

View file

@ -45,6 +45,11 @@
- Updated ApiV1Controller, use PublicTimelineService. ([f67c67bc](https://github.com/pixelfed/pixelfed/commit/f67c67bc))
- Updated ApiV1Controller, use ProfileService for verify_credentials. ([352aa573](https://github.com/pixelfed/pixelfed/commit/352aa573))
- Updated RemotePost.vue, fix content warning button. ([7647e724](https://github.com/pixelfed/pixelfed/commit/7647e724))
- Updated AdminMediaController, improve perf and use simple pagination. ([f2686cac](https://github.com/pixelfed/pixelfed/commit/f2686cac))
- Updated PostComponent, fix MomentUI like counter. ([42c6121a](https://github.com/pixelfed/pixelfed/commit/42c6121a))
- Updated status views, remove like counts from status embed. ([1a2e41b1](https://github.com/pixelfed/pixelfed/commit/1a2e41b1))
- Updated Profile, fix unauthenticated private profiles. ([9017f7c4](https://github.com/pixelfed/pixelfed/commit/9017f7c4))
- Updated PublicApiController, impr home timeline perf. ([4fe42e5b](https://github.com/pixelfed/pixelfed/commit/4fe42e5b))
- ([](https://github.com/pixelfed/pixelfed/commit/))
## [v0.11.0 (2021-06-01)](https://github.com/pixelfed/pixelfed/compare/v0.10.10...v0.11.0)

View file

@ -27,6 +27,7 @@ trait AdminMediaController
],
'search' => 'nullable|string|min:1|max:20'
]);
if($request->filled('search')) {
$profiles = Profile::where('username', 'like', '%'.$request->input('search').'%')->pluck('id')->toArray();
$media = Media::whereHas('status')
@ -42,7 +43,8 @@ trait AdminMediaController
$media = MediaBlocklist::latest()->paginate(12);
return view('admin.media.home', compact('media'));
}
$media = Media::whereHas('status')->with('status')->orderby('id', 'desc')->paginate(12);
$media = Media::whereNull('remote_url')->orderby('id', 'desc')->simplePaginate(12);
return view('admin.media.home', compact('media'));
}

View file

@ -47,7 +47,8 @@ class ProfileController extends Controller
});
if ($user->is_private == true) {
abort(404);
$profile = null;
return view('profile.private', compact('user'));
}
$owner = false;

View file

@ -290,69 +290,32 @@ class PublicApiController extends Controller
$max = $request->input('max_id');
$limit = $request->input('limit') ?? 3;
$user = $request->user();
$amin = SnowflakeService::byDate(now()->subDays(90));
$key = 'user:last_active_at:id:'.$user->id;
$ttl = now()->addMinutes(5);
Cache::remember($key, $ttl, function() use($user) {
$user->last_active_at = now();
$user->save();
return;
});
$filtered = UserFilter::whereUserId($user->profile_id)
->whereFilterableType('App\Profile')
->whereIn('filter_type', ['mute', 'block'])
->pluck('filterable_id')->toArray();
if($min || $max) {
$dir = $min ? '>' : '<';
$id = $min ?? $max;
$timeline = Status::select(
'id',
'profile_id',
'type',
'scope',
'local'
)->where('id', $dir, $id)
->where('id', '>', $amin)
->whereIn('type', ['text', 'photo', 'photo:album', 'video', 'video:album', 'photo:video:album'])
->whereNotIn('profile_id', $filtered)
->whereLocal(true)
->whereScope('public')
->orderBy('id', 'desc')
->limit($limit)
->get()
->map(function($s) use ($user) {
$status = StatusService::get($s->id);
$status['favourited'] = (bool) LikeService::liked($user->profile_id, $s->id);
return $status;
});
$res = $timeline->toArray();
} else {
$timeline = Status::select(
'id',
'profile_id',
'type',
'scope',
'local'
)->whereIn('type', ['text', 'photo', 'photo:album', 'video', 'video:album', 'photo:video:album'])
->where('id', '>', $amin)
->whereNotIn('profile_id', $filtered)
->with('profile', 'hashtags', 'mentions')
->whereLocal(true)
->whereScope('public')
->orderBy('id', 'desc')
->limit($limit)
->get()
->map(function($s) use ($user) {
$status = StatusService::get($s->id);
$status['favourited'] = (bool) LikeService::liked($user->profile_id, $s->id);
return $status;
});
$res = $timeline->toArray();
if(PublicTimelineService::count() == 0) {
PublicTimelineService::warmCache(true, 400);
}
if ($max) {
$feed = PublicTimelineService::getRankedMaxId($max, $limit);
} else if ($min) {
$feed = PublicTimelineService::getRankedMinId($min, $limit);
} else {
$feed = PublicTimelineService::get(0, $limit);
}
$res = collect($feed)
->map(function($k) use($user) {
$status = StatusService::get($k);
$status['favourited'] = (bool) LikeService::liked($user->profile_id, $k);
return $status;
})
->toArray();
return response()->json($res);
}

BIN
public/js/profile.js vendored

Binary file not shown.

BIN
public/js/status.js vendored

Binary file not shown.

Binary file not shown.

View file

@ -323,7 +323,7 @@
</div>
<div class="like-count font-weight-bold mt-2 rounded border" style="cursor:pointer;" v-on:click="likesModal">
{{ownerOrAdmin() ? (status.liked_by.total_count + 1) : 0}}
{{ownerOrAdmin == true ? (status.liked_by.total_count + 1) : 0}}
</div>
</div>
<div class="text-center">
@ -898,6 +898,10 @@ export default {
return;
}
if(u.has('ui') && u.get('ui') == 'moment' && this.layout != 'moment') {
this.layout = 'moment';
}
if(forceMetro == true || u.has('ui') && u.get('ui') == 'metro' && this.layout != 'metro') {
this.layout = 'metro';
}

View file

@ -703,6 +703,11 @@
this.fetchProfile();
let u = new URLSearchParams(window.location.search);
let forceMetro = localStorage.getItem('pf_metro_ui.exp.forceMetro') == 'true';
if(u.has('ui') && u.get('ui') == 'moment' && this.layout != 'moment') {
this.layout = 'moment';
}
if(forceMetro == true || u.has('ui') && u.get('ui') == 'metro' && this.layout != 'metro') {
this.layout = 'metro';
}
@ -739,10 +744,6 @@
}
});
}
if(window.outerWidth < 576) {
$('nav.navbar').hide();
this.isMobile = true;
}
},
updated() {

View file

@ -15,8 +15,8 @@ return [
'timelines' => 'خط زمانی',
'embed' => 'توکار',
'communityGuidelines' => 'دستورالعمل‌های انجمن',
'whatIsTheFediverse' => 'نامتمرکز یعنی چی؟',
'communityGuidelines' => 'دستورالعمل‌های اجتماع',
'whatIsTheFediverse' => 'فدیورس چیست؟',
'controllingVisibility' => 'کنترل نمایش',
'blockingAccounts' => 'حساب‌های مسدودشده',
'safetyTips' => 'نکات امنیتی',

View file

@ -5,15 +5,15 @@ return [
'home' => 'خانه',
'local' => 'محلی',
'network' => 'شبکه',
'discover' => 'کشف‌کردن',
'discover' => 'کشف ‌کردن',
'viewMyProfile' => 'مشاهده نمایه من',
'myProfile' => 'نمایه من',
'myTimeline' => 'جدول زمانی من',
'publicTimeline' => 'جدول زمانی عمومی',
'remoteFollow' => 'از راه دور دنبال کنید',
'myTimeline' => 'خط‌زمانی من',
'publicTimeline' => 'خط‌زمانی عمومی',
'remoteFollow' => 'پیگیری راه دور',
'settings' => 'تنظیمات',
'admin' => 'مدیر',
'logout' => 'خروج',
'directMessages' => 'پیام خصوصی',
'composePost' => 'نوشتن پست',
'directMessages' => 'پیام مستقیم',
'composePost' => 'ایجاد فرسته',
];

View file

@ -2,11 +2,11 @@
return [
'likedPhoto' => 'پست شما را پسندید.',
'likedPhoto' => 'فرستهٔ شما را پسندید.',
'likedComment' => 'دیدگاه شما را پسندید.',
'startedFollowingYou' => 'شما را دنبال می‌کند.',
'commented' => 'دیدگاهی روی پست شما نوشت.',
'commented' => 'دیدگاهی روی فرستهٔ شما نوشت.',
'mentionedYou' => 'شما را صدا کرد.',
'shared' => 'پستی منتشر کرد.',
'shared' => 'فرسته‌ای منتشر کرد.',
];

View file

@ -13,10 +13,10 @@ return [
|
*/
'password' => 'رمز عبور شما باید حداقل شامل ۶ کاراکتر باشد و همچنین با تکرار آن مطابق باشد.',
'password' => 'رمز عبور شما باید حداقل شامل ۶ حرف بوده و همچنین با تکرار آن مطابق باشد.',
'reset' => 'رمز عبور شما بازنشانی شد!',
'sent' => 'اگر ایمیل شما در پایگاه‌داده ما موجود باشد، شما ایمیل شامل یک لینک بازنشانی رمز عبور در چند دقیقه آینده دریافت خواهید کرد. در صورتی که ایمیلی دریافت نکردید، لطفا صندوق هرزنامه خود را نیز بررسی کنید.',
'token' => 'این لینک نامعتبر است.',
'user' => 'اگر ایمیل شما در پایگاه‌داده ما موجود باشد، شما ایمیل شامل یک لینک بازنشانی رمز عبور در چند دقیقه آینده دریافت خواهید کرد. در صورتی که ایمیلی دریافت نکردید، لطفا صندوق هرزنامه خود را نیز بررسی کنید.',
'sent' => 'اگر ایمیل‌تان در پایگاه‌داده ما موجود باشد، در چند دقیقه آینده ایمیلی حاوی یک پیوند بازنشانی رمز عبور دریافت خواهید کرد. در صورتی که ایمیلی دریافت نکردید، لطفا صندوق هرزنامه خود را نیز بررسی کنید.',
'token' => 'این پیوند نامعتبر است.',
'user' => 'اگر ایمیل‌تان در پایگاه‌داده ما موجود باشد، در چند دقیقه آینده ایمیلی حاوی یک پیوند بازنشانی رمز عبور دریافت خواهید کرد. در صورتی که ایمیلی دریافت نکردید، لطفا صندوق هرزنامه خود را نیز بررسی کنید.',
];

View file

@ -1,10 +1,10 @@
<?php
return [
'emptyTimeline' => 'این کاربر هنوز پستی ارسال نکرده!',
'emptyTimeline' => 'این کاربر هنوز چیزی منتشر نکرده است!',
'emptyFollowers' => 'این کاربر هنوز دنبال‌کننده‌ای ندارد!',
'emptyFollowing' => 'این کاربر هنوز کسی را دنبال نمی‌کند!',
'emptySaved' => 'شما هنوز هیچ پستی ذخیره نکرده‌اید!',
'emptySaved' => 'شما هنوز هیچ فرسته‌ای را ذخیره نکرده‌اید!',
'savedWarning' => 'فقط شما می‌توانید آنچه که ذخیره کرده‌اید را ببینید.',
'privateProfileWarning' => 'این حساب خصوصی است.',
'alreadyFollow' => 'الان :username را دنبال می‌کنید؟',

View file

@ -5,8 +5,8 @@ return [
'about' => 'درباره',
'help' => 'راهنما',
'language' => 'زبان‌ها',
'fediverse' => 'نامتمرکز',
'opensource' => 'متن باز',
'fediverse' => 'فدیورس',
'opensource' => 'آزاد/متن‌باز',
'terms' => 'قوانین',
'privacy' => 'حریم خصوصی',
'l10nWip' => 'ما هنوز روی محلی‌سازی کار می‌کنیم.',

View file

@ -2,6 +2,6 @@
return [
'emptyPersonalTimeline' => 'جدول زمانی شما خالی است.',
'emptyPersonalTimeline' => 'خط‌زمانی شما خالی است.',
];

View file

@ -14,7 +14,7 @@ return [
*/
'accepted' => ':attribute باید پذیرفته شده باشد.',
'active_url' => 'آدرس :attribute معتبر نیست',
'active_url' => 'نشانی :attribute معتبر نیست',
'after' => ':attribute باید تاریخی بعد از :date باشد.',
'after_or_equal' => ':attribute باید تاریخی بعد از :date، یا مطابق با آن باشد.',
'alpha' => ':attribute باید فقط حروف الفبا باشد.',
@ -26,8 +26,8 @@ return [
'between' => [
'numeric' => ':attribute باید بین :min و :max باشد.',
'file' => ':attribute باید بین :min و :max کیلوبایت باشد.',
'string' => ':attribute باید بین :min و :max کاراکتر باشد.',
'array' => ':attribute باید بین :min و :max آیتم باشد.',
'string' => ':attribute باید بین :min و :max حرف باشد.',
'array' => ':attribute باید بین :min و :max مورد باشد.',
],
'boolean' => 'فیلد :attribute فقط می‌تواند صحیح و یا غلط باشد',
'confirmed' => ':attribute با فیلد تکرار مطابقت ندارد.',
@ -38,39 +38,39 @@ return [
'digits_between' => ':attribute باید بین :min و :max رقم باشد.',
'dimensions' => 'ابعاد تصویر :attribute قابل قبول نیست.',
'distinct' => 'فیلد :attribute تکراری است.',
'email' => ':attribute باید یک ایمیل معتبر باشد',
'exists' => ':attribute انتخاب شده، معتبر نیست.',
'file' => ':attribute باید یک فایل باشد',
'email' => ':attribute باید ایمیلی معتبر باشد',
'exists' => ':attribute انتخاب شده معتبر نیست.',
'file' => ':attribute باید یک پرونده باشد',
'filled' => 'فیلد :attribute الزامی است',
'image' => ':attribute باید تصویر باشد.',
'in' => ':attribute انتخاب شده، معتبر نیست.',
'in' => ':attribute انتخاب شده معتبر نیست.',
'in_array' => 'فیلد :attribute در :other وجود ندارد.',
'integer' => ':attribute باید عدد صحیح باشد.',
'integer' => ':attribute باید عددی صحیح باشد.',
'ip' => ':attribute باید IP معتبر باشد.',
'ipv4' => ':attribute باید یک آدرس معتبر از نوع IPv4 باشد.',
'ipv6' => ':attribute باید یک آدرس معتبر از نوع IPv6 باشد.',
'ipv4' => ':attribute باید یک نشانی معتبر از نوع IPv4 باشد.',
'ipv6' => ':attribute باید یک نشانی معتبر از نوع IPv6 باشد.',
'json' => 'فیلد :attribute باید یک رشته از نوع JSON باشد.',
'max' => [
'numeric' => ':attribute نباید بزرگتر از :max باشد.',
'file' => ':attribute نباید بزرگتر از :max کیلوبایت باشد.',
'string' => ':attribute نباید بیشتر از :max کاراکتر باشد.',
'array' => ':attribute نباید بیشتر از :max آیتم باشد.',
'string' => ':attribute نباید بیشتر از :max حرف باشد.',
'array' => ':attribute نباید بیشتر از :max مورد باشد.',
],
'mimes' => ':attribute باید یکی از فرمت های :values باشد.',
'mimetypes' => ':attribute باید یکی از فرمت های :values باشد.',
'mimes' => ':attribute باید یکی از قالب‌های :values باشد.',
'mimetypes' => ':attribute باید یکی از قالب‌های :values باشد.',
'min' => [
'numeric' => ':attribute نباید کوچکتر از :min باشد.',
'file' => ':attribute نباید کوچکتر از :min کیلوبایت باشد.',
'string' => ':attribute نباید کمتر از :min کاراکتر باشد.',
'array' => ':attribute نباید کمتر از :min آیتم باشد.',
'string' => ':attribute نباید کمتر از :min حرف باشد.',
'array' => ':attribute نباید کمتر از :min مورد باشد.',
],
'not_in' => ':attribute انتخاب شده، معتبر نیست.',
'not_in' => ':attribute انتخاب شده معتبر نیست.',
'not_regex' => ':attribute نامعتبر است.',
'numeric' => ':attribute باید عدد باشد.',
'present' => 'فیلد :attribute باید در پارامترهای ارسالی وجود داشته باشد.',
'regex' => 'فرمت :attribute معتبر نیست',
'regex' => 'قالب :attribute معتبر نیست',
'required' => 'فیلد :attribute الزامی است',
'required_if' => 'هنگامی که :other برابر با :value است، فیلد :attribute الزامی است.',
'required_if' => 'هنگامی که :other برابر با :value باشد، فیلد :attribute الزامی است.',
'required_unless' => 'فیلد :attribute ضروری است، مگر آنکه :other در :values موجود باشد.',
'required_with' => 'در صورت وجود فیلد :values، فیلد :attribute الزامی است.',
'required_with_all' => 'در صورت وجود فیلدهای :values، فیلد :attribute الزامی است.',
@ -80,14 +80,14 @@ return [
'size' => [
'numeric' => ':attribute باید برابر با :size باشد.',
'file' => ':attribute باید برابر با :size کیلوبایت باشد.',
'string' => ':attribute باید برابر با :size کاراکتر باشد.',
'array' => ':attribute باسد شامل :size آیتم باشد.',
'string' => ':attribute باید برابر با :size حرف باشد.',
'array' => ':attribute باید شامل :size مورد باشد.',
],
'string' => 'فیلد :attribute باید متن باشد.',
'timezone' => 'فیلد :attribute باید یک منطقه زمانی قابل قبول باشد.',
'unique' => ':attribute قبلا انتخاب شده است.',
'uploaded' => 'آپلود فایل :attribute موفقیت آمیز نبود.',
'url' => 'فرمت آدرس :attribute اشتباه است.',
'uploaded' => 'بارگذاری پرونده :attribute موفقیت آمیز نبود.',
'url' => 'قالب نشانی :attribute اشتباه است.',
/*
|--------------------------------------------------------------------------
@ -131,7 +131,7 @@ return [
'phone' => 'تلفن',
'mobile' => 'تلفن همراه',
'age' => 'سن',
'sex' => 'جنسیت',
'sex' => 'جنس',
'gender' => 'جنسیت',
'day' => 'روز',
'month' => 'ماه',

View file

@ -1,107 +1,97 @@
<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 h3">{{$user->username}}</span>
@if(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() && $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>
</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">
<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">
<div class="modal-dialog modal-dialog-centered modal-sm">
<div class="modal-content">
<div class="modal-body p-0">
<div class="list-group-item cursor-pointer text-center rounded text-dark" onclick="window.App.util.clipboard('{{$user->url()}}');$('#ctxProfileMenu').modal('hide')">
Copy Link
</div>
@auth
<div class="list-group-item cursor-pointer text-center rounded text-dark" onclick="muteProfile()">
Mute
</div>
<a class="list-group-item cursor-pointer text-center rounded text-dark text-decoration-none" href="i/report?type=user&id={{$user->id}}">
Report User
</a>
<div class="list-group-item cursor-pointer text-center rounded text-dark" onclick="blockProfile()">
Block
</div>
@endauth
<div class="list-group-item cursor-pointer text-center rounded text-muted" onclick="$('#ctxProfileMenu').modal('hide')">
Close
</div>
</div>
</div>
</div>
</div>
</span>
</div>
<div class="profile-stats pb-3 d-inline-flex lead">
<div class="font-weight-light pr-5">
<span class="font-weight-bold">{{$user->statuses()->whereNull('reblog_of_id')->whereNull('in_reply_to_id')->count()}}</span>
Posts
</div>
</div>
<p class="lead mb-0">
<span class="font-weight-bold">{{$user->name}}</span>
@if($user->remote_url)
<span class="badge badge-info">REMOTE PROFILE</span>
@endif
</p>
<p class="mb-0 lead">{{$user->bio}}</p>
<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 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 h3">{{$user->username}}</span>
@if(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() && $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>
</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
@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">
<div class="modal-dialog modal-dialog-centered modal-sm">
<div class="modal-content">
<div class="modal-body p-0">
<div class="list-group-item cursor-pointer text-center rounded text-dark" onclick="window.App.util.clipboard('{{$user->url()}}');$('#ctxProfileMenu').modal('hide')">
Copy Link
</div>
@auth
<div class="list-group-item cursor-pointer text-center rounded text-dark" onclick="muteProfile()">
Mute
</div>
<a class="list-group-item cursor-pointer text-center rounded text-dark text-decoration-none" href="i/report?type=user&id={{$user->id}}">
Report User
</a>
<div class="list-group-item cursor-pointer text-center rounded text-dark" onclick="blockProfile()">
Block
</div>
@endauth
<div class="list-group-item cursor-pointer text-center rounded text-muted" onclick="$('#ctxProfileMenu').modal('hide')">
Close
</div>
</div>
</div>
</div>
</div>
</span>
@endauth
</div>
</div>
</div>
</div>
</div>
</div>
@push('scripts')
@auth
<script type="text/javascript">
function muteProfile() {
axios.post('/i/mute', {
type: 'user',
item: '{{$user->id}}'
}).then(res => {
$('#ctxProfileMenu').modal('hide');
$('#ctxProfileMenu').hide();
swal('Muted Profile', 'You have successfully muted this profile.', 'success');
});
}
function blockProfile() {
axios.post('/i/block', {
type: 'user',
item: '{{$user->id}}'
}).then(res => {
$('#ctxProfileMenu').modal('hide');
$('#ctxProfileMenu').hide();
swal('Blocked Profile', 'You have successfully blocked this profile.', 'success');
});
}
function muteProfile() {
axios.post('/i/mute', {
type: 'user',
item: '{{$user->id}}'
}).then(res => {
$('#ctxProfileMenu').modal('hide');
$('#ctxProfileMenu').hide();
swal('Muted Profile', 'You have successfully muted this profile.', 'success');
});
}
function blockProfile() {
axios.post('/i/block', {
type: 'user',
item: '{{$user->id}}'
}).then(res => {
$('#ctxProfileMenu').modal('hide');
$('#ctxProfileMenu').hide();
swal('Blocked Profile', 'You have successfully blocked this profile.', 'success');
});
}
</script>
@endauth
@endpush

View file

@ -2,28 +2,28 @@
@section('content')
@if (session('error'))
<div class="alert alert-danger text-center font-weight-bold mb-0">
{{ session('error') }}
</div>
<div class="alert alert-danger text-center font-weight-bold mb-0">
{{ session('error') }}
</div>
@endif
@include('profile.partial.private-info')
<div class="container">
<div class="profile-timeline mt-2 mt-md-4">
<div class="card">
<div class="card-body py-5">
<p class="text-center lead font-weight-bold mb-0">
{{__('profile.privateProfileWarning')}}
</p>
<div class="profile-timeline mt-2 mt-md-4">
<div class="">
<div class="py-5">
<p class="text-center lead font-weight-bold">
{{__('profile.privateProfileWarning')}}
</p>
@if(!Auth::check())
<p class="text-center mb-0">{{ __('profile.alreadyFollow', ['username'=>$user->username])}}</p>
<p class="text-center mb-0"><a href="{{route('login')}}">{{__('Log in')}}</a></p>
<p class="text-center mb-0">{{__('profile.loginToSeeProfile')}}</p>
@endif
</div>
</div>
</div>
@if(!Auth::check())
<p class="text-center mb-0">{{ __('profile.alreadyFollow', ['username'=>$user->username])}}</p>
<p class="text-center mb-0"><a href="{{route('login')}}">{{__('Log in')}}</a></p>
<p class="text-center mb-0">{{__('profile.loginToSeeProfile')}}</p>
@endif
</div>
</div>
</div>
</div>
@endsection

View file

@ -2,176 +2,171 @@
<html lang="{{ app()->getLocale() }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="mobile-web-app-capable" content="yes">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="mobile-web-app-capable" content="yes">
<title>{{ $title ?? config('app.name', 'Pixelfed') }}</title>
<title>{{ $title ?? config('app.name', 'Pixelfed') }}</title>
<meta property="og:site_name" content="{{ config('app.name', 'pixelfed') }}">
<meta property="og:title" content="{{ $title ?? config('app.name', 'pixelfed') }}">
<meta property="og:type" content="article">
<meta property="og:url" content="{{$status->url()}}">
<meta name="medium" content="image">
<meta name="theme-color" content="#10c5f8">
<meta name="apple-mobile-web-app-capable" content="yes">
<link rel="shortcut icon" type="image/png" href="/img/favicon.png?v=2">
<link rel="apple-touch-icon" type="image/png" href="/img/favicon.png?v=2">
<link href="{{ mix('css/app.css') }}" rel="stylesheet">
<style type="text/css">
body.embed-card {
background: #fff !important;
margin: 0;
padding-bottom: 0;
}
.status-card-embed {
box-shadow: none;
border-radius: 4px;
overflow: hidden;
}
</style>
<meta property="og:site_name" content="{{ config('app.name', 'pixelfed') }}">
<meta property="og:title" content="{{ $title ?? config('app.name', 'pixelfed') }}">
<meta property="og:type" content="article">
<meta property="og:url" content="{{$status->url()}}">
<meta name="medium" content="image">
<meta name="theme-color" content="#10c5f8">
<meta name="apple-mobile-web-app-capable" content="yes">
<link rel="shortcut icon" type="image/png" href="/img/favicon.png?v=2">
<link rel="apple-touch-icon" type="image/png" href="/img/favicon.png?v=2">
<link href="{{ mix('css/app.css') }}" rel="stylesheet">
<style type="text/css">
body.embed-card {
background: #fff !important;
margin: 0;
padding-bottom: 0;
}
.status-card-embed {
box-shadow: none;
border-radius: 4px;
overflow: hidden;
}
</style>
</head>
<body class="bg-white">
<div class="embed-card">
@php($item = $status)
<div class="card status-card-embed card-md-rounded-0 border">
<div class="card-header d-inline-flex align-items-center bg-white">
<img src="{{$item->profile->avatarUrl()}}" width="32px" height="32px" target="_blank" style="border-radius: 32px;">
<a class="username font-weight-bold pl-2 text-dark" target="_blank" href="{{$item->profile->url()}}">
{{$item->profile->username}}
</a>
</div>
<a href="{{$status->url()}}" target="_blank">
@php($status = $item)
@switch($status->viewType())
@case('photo')
@case('image')
@if($status->is_nsfw)
<details class="details-animated">
<summary>
<p class="mb-0 lead font-weight-bold">CW / NSFW / Hidden Media</p>
<p class="font-weight-light">(click to show)</p>
</summary>
<a class="max-hide-overflow {{$status->firstMedia()->filter_class}}" href="{{$status->url()}}" target="_blank">
<img class="card-img-top" src="{{$status->mediaUrl()}}">
</a>
</details>
@else
<div class="{{$status->firstMedia()->filter_class}}">
<img src="{{$status->mediaUrl()}}" width="100%">
</div>
@endif
@break
@case('photo:album')
<div id="photo-carousel-wrapper-{{$status->id}}" class="carousel slide carousel-fade mb-n3 " data-ride="carousel">
<ol class="carousel-indicators">
@for($i = 0; $i < $status->media_count; $i++)
<li data-target="#photo-carousel-wrapper-{{$status->id}}" data-slide-to="{{$i}}" class="{{$i == 0 ? 'active' : ''}}"></li>
@endfor
</ol>
<div class="carousel-inner">
@foreach($status->media()->orderBy('order')->get() as $media)
<div class="carousel-item {{$loop->iteration == 1 ? 'active' : ''}}">
<figure class="{{$media->filter_class}}">
<div class="float-right mr-3 badge badge-dark border border-secondary rounded-pill p-2" style="position:absolute;top:8px;right:0;margin-bottom:-20px;">{{$loop->iteration}}/{{$loop->count}}</div>
<img class="d-block w-100" src="{{$media->url()}}" alt="{{$status->caption}}">
</figure>
</div>
@endforeach
</div>
<a class="carousel-control-prev" href="#photo-carousel-wrapper-{{$status->id}}" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="carousel-control-next" href="#photo-carousel-wrapper-{{$status->id}}" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>
@break
@case('video')
@if($status->is_nsfw)
<details class="details-animated">
<summary>
<p class="mb-0 lead font-weight-bold">CW / NSFW / Hidden Media</p>
<p class="font-weight-light">(click to show)</p>
</summary>
<div class="embed-responsive embed-responsive-16by9">
<video class="video" preload="none" controls loop>
<source src="{{$status->firstMedia()->url()}}" type="{{$status->firstMedia()->mime}}">
</video>
</div>
</details>
@else
<div class="embed-responsive embed-responsive-16by9">
<video class="video" preload="none" controls loop>
<source src="{{$status->firstMedia()->url()}}" type="{{$status->firstMedia()->mime}}">
</video>
</div>
@endif
@break
@case('video-album')
@if($status->is_nsfw)
<details class="details-animated">
<summary>
<p class="mb-0 lead font-weight-bold">CW / NSFW / Hidden Media</p>
<p class="font-weight-light">(click to show)</p>
</summary>
<div class="embed-responsive embed-responsive-16by9">
<video class="video" preload="none" controls loop>
<source src="{{$status->firstMedia()->url()}}" type="{{$status->firstMedia()->mime}}">
</video>
</div>
</details>
@else
<div class="embed-responsive embed-responsive-16by9">
<video class="video" preload="none" controls loop>
<source src="{{$status->firstMedia()->url()}}" type="{{$status->firstMedia()->mime}}">
</video>
</div>
@endif
@break
@endswitch
</a>
@if($layout != 'compact')
<div class="card-body">
<div class="view-more mb-2">
<a class="font-weight-bold" href="{{$status->url()}}" target="_blank">View More on Pixelfed</a>
</div>
<hr>
@if($showLikes)
<div class="likes font-weight-bold pb-2">
<span class="like-count">{{$item->likes_count}}</span> likes
</div>
@endif
<div class="caption">
<p class="my-0">
<span class="username font-weight-bold">
<bdi><a class="text-dark" href="{{$item->profile->url()}}" target="_blank">{{$item->profile->username}}</a></bdi>
</span>
@if($showCaption)
<span class="caption-container">{!! $item->rendered ?? e($item->caption) !!}</span>
@endif
</p>
</div>
</div>
@endif
<div class="card-footer bg-white d-inline-flex justify-content-between align-items-center">
<div class="timestamp">
<p class="small text-uppercase mb-0"><a href="{{$item->url()}}" class="text-muted" target="_blank">{{$item->created_at->diffForHumans()}}</a></p>
</div>
<div>
<a class="small font-weight-bold text-muted pr-1" href="{{config('app.url')}}" target="_blank">{{config('pixelfed.domain.app')}}</a>
<img src="/img/pixelfed-icon-color.svg" width="26px">
</div>
</div>
</div>
</div>
<script type="text/javascript">window.addEventListener("message",e=>{const t=e.data||{};window.parent&&"setHeight"===t.type&&window.parent.postMessage({type:"setHeight",id:t.id,height:document.getElementsByTagName("html")[0].scrollHeight},"*")});</script>
<script type="text/javascript">document.querySelectorAll('.caption-container a').forEach(function(i) {i.setAttribute('target', '_blank');});</script>
<script type="text/javascript" src="{{ mix('js/manifest.js') }}"></script>
<script type="text/javascript" src="{{ mix('js/vendor.js') }}"></script>
<script type="text/javascript" src="{{ mix('js/app.js') }}"></script>
<div class="embed-card">
@php($item = $status)
<div class="card status-card-embed card-md-rounded-0 border">
<div class="card-header d-inline-flex align-items-center bg-white">
<img src="{{$item->profile->avatarUrl()}}" width="32px" height="32px" target="_blank" style="border-radius: 32px;">
<a class="username font-weight-bold pl-2 text-dark" target="_blank" href="{{$item->profile->url()}}">
{{$item->profile->username}}
</a>
</div>
<a href="{{$status->url()}}" target="_blank">
@php($status = $item)
@switch($status->viewType())
@case('photo')
@case('image')
@if($status->is_nsfw)
<details class="details-animated">
<summary>
<p class="mb-0 lead font-weight-bold">CW / NSFW / Hidden Media</p>
<p class="font-weight-light">(click to show)</p>
</summary>
<a class="max-hide-overflow {{$status->firstMedia()->filter_class}}" href="{{$status->url()}}" target="_blank">
<img class="card-img-top" src="{{$status->mediaUrl()}}">
</a>
</details>
@else
<div class="{{$status->firstMedia()->filter_class}}">
<img src="{{$status->mediaUrl()}}" width="100%">
</div>
@endif
@break
@case('photo:album')
<div id="photo-carousel-wrapper-{{$status->id}}" class="carousel slide carousel-fade mb-n3 " data-ride="carousel">
<ol class="carousel-indicators">
@for($i = 0; $i < $status->media_count; $i++)
<li data-target="#photo-carousel-wrapper-{{$status->id}}" data-slide-to="{{$i}}" class="{{$i == 0 ? 'active' : ''}}"></li>
@endfor
</ol>
<div class="carousel-inner">
@foreach($status->media()->orderBy('order')->get() as $media)
<div class="carousel-item {{$loop->iteration == 1 ? 'active' : ''}}">
<figure class="{{$media->filter_class}}">
<div class="float-right mr-3 badge badge-dark border border-secondary rounded-pill p-2" style="position:absolute;top:8px;right:0;margin-bottom:-20px;">{{$loop->iteration}}/{{$loop->count}}</div>
<img class="d-block w-100" src="{{$media->url()}}" alt="{{$status->caption}}">
</figure>
</div>
@endforeach
</div>
<a class="carousel-control-prev" href="#photo-carousel-wrapper-{{$status->id}}" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="carousel-control-next" href="#photo-carousel-wrapper-{{$status->id}}" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>
@break
@case('video')
@if($status->is_nsfw)
<details class="details-animated">
<summary>
<p class="mb-0 lead font-weight-bold">CW / NSFW / Hidden Media</p>
<p class="font-weight-light">(click to show)</p>
</summary>
<div class="embed-responsive embed-responsive-16by9">
<video class="video" preload="none" controls loop>
<source src="{{$status->firstMedia()->url()}}" type="{{$status->firstMedia()->mime}}">
</video>
</div>
</details>
@else
<div class="embed-responsive embed-responsive-16by9">
<video class="video" preload="none" controls loop>
<source src="{{$status->firstMedia()->url()}}" type="{{$status->firstMedia()->mime}}">
</video>
</div>
@endif
@break
@case('video-album')
@if($status->is_nsfw)
<details class="details-animated">
<summary>
<p class="mb-0 lead font-weight-bold">CW / NSFW / Hidden Media</p>
<p class="font-weight-light">(click to show)</p>
</summary>
<div class="embed-responsive embed-responsive-16by9">
<video class="video" preload="none" controls loop>
<source src="{{$status->firstMedia()->url()}}" type="{{$status->firstMedia()->mime}}">
</video>
</div>
</details>
@else
<div class="embed-responsive embed-responsive-16by9">
<video class="video" preload="none" controls loop>
<source src="{{$status->firstMedia()->url()}}" type="{{$status->firstMedia()->mime}}">
</video>
</div>
@endif
@break
@endswitch
</a>
@if($layout != 'compact')
<div class="card-body">
<div class="view-more mb-2">
<a class="font-weight-bold" href="{{$status->url()}}" target="_blank">View More on Pixelfed</a>
</div>
<hr>
<div class="caption">
<p class="my-0">
<span class="username font-weight-bold">
<bdi><a class="text-dark" href="{{$item->profile->url()}}" target="_blank">{{$item->profile->username}}</a></bdi>
</span>
@if($showCaption)
<span class="caption-container">{!! $item->rendered ?? e($item->caption) !!}</span>
@endif
</p>
</div>
</div>
@endif
<div class="card-footer bg-white d-inline-flex justify-content-between align-items-center">
<div class="timestamp">
<p class="small text-uppercase mb-0"><a href="{{$item->url()}}" class="text-muted" target="_blank">{{$item->created_at->diffForHumans()}}</a></p>
</div>
<div>
<a class="small font-weight-bold text-muted pr-1" href="{{config('app.url')}}" target="_blank">{{config('pixelfed.domain.app')}}</a>
<img src="/img/pixelfed-icon-color.svg" width="26px">
</div>
</div>
</div>
</div>
<script type="text/javascript">window.addEventListener("message",e=>{const t=e.data||{};window.parent&&"setHeight"===t.type&&window.parent.postMessage({type:"setHeight",id:t.id,height:document.getElementsByTagName("html")[0].scrollHeight},"*")});</script>
<script type="text/javascript">document.querySelectorAll('.caption-container a').forEach(function(i) {i.setAttribute('target', '_blank');});</script>
<script type="text/javascript" src="{{ mix('js/manifest.js') }}"></script>
<script type="text/javascript" src="{{ mix('js/vendor.js') }}"></script>
<script type="text/javascript" src="{{ mix('js/app.js') }}"></script>
</body>
</html>

View file

@ -25,11 +25,5 @@
@push('scripts')
<script type="text/javascript" src="{{ mix('js/status.js') }}"></script>
<script type="text/javascript">
$(document).ready(function() {
new Vue({
el: '#content'
});
});
</script>
<script type="text/javascript">App.boot();</script>
@endpush

View file

@ -72,7 +72,7 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
Route::get('timelines/home', 'Api\ApiV1Controller@timelineHome')->middleware($middleware);
Route::get('timelines/public', 'Api\ApiV1Controller@timelinePublic');
Route::get('timelines/public', 'Api\ApiV1Controller@timelinePublic')->middleware($middleware);
Route::get('timelines/tag/{hashtag}', 'Api\ApiV1Controller@timelineHashtag');
});
Route::group(['prefix' => 'stories'], function () use($middleware) {