mirror of https://github.com/pixelfed/pixelfed
Merge branch 'dev' of https://github.com/dansup/pixelfed into dev
commit
16dc76db17
@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Auth, Cache;
|
||||
use App\{
|
||||
Avatar,
|
||||
Like,
|
||||
Profile,
|
||||
Status
|
||||
};
|
||||
use League\Fractal;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Controllers\AvatarController;
|
||||
use App\Util\Webfinger\Webfinger;
|
||||
use App\Transformer\Api\{
|
||||
AccountTransformer,
|
||||
StatusTransformer
|
||||
};
|
||||
use App\Jobs\AvatarPipeline\AvatarOptimize;
|
||||
use League\Fractal\Serializer\ArraySerializer;
|
||||
|
||||
class BaseApiController extends Controller
|
||||
{
|
||||
protected $fractal;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
$this->fractal = new Fractal\Manager();
|
||||
$this->fractal->setSerializer(new ArraySerializer());
|
||||
}
|
||||
|
||||
public function accounts(Request $request, $id)
|
||||
{
|
||||
$profile = Profile::findOrFail($id);
|
||||
$resource = new Fractal\Resource\Item($profile, new AccountTransformer);
|
||||
$res = $this->fractal->createData($resource)->toArray();
|
||||
return response()->json($res, 200, [], JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
public function accountFollowers(Request $request, $id)
|
||||
{
|
||||
$profile = Profile::findOrFail($id);
|
||||
$followers = $profile->followers;
|
||||
$resource = new Fractal\Resource\Collection($followers, new AccountTransformer);
|
||||
$res = $this->fractal->createData($resource)->toArray();
|
||||
return response()->json($res, 200, [], JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
public function accountFollowing(Request $request, $id)
|
||||
{
|
||||
$profile = Profile::findOrFail($id);
|
||||
$following = $profile->following;
|
||||
$resource = new Fractal\Resource\Collection($following, new AccountTransformer);
|
||||
$res = $this->fractal->createData($resource)->toArray();
|
||||
return response()->json($res, 200, [], JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
public function accountStatuses(Request $request, $id)
|
||||
{
|
||||
$profile = Profile::findOrFail($id);
|
||||
$statuses = $profile->statuses()->orderBy('id', 'desc')->paginate(20);
|
||||
$resource = new Fractal\Resource\Collection($statuses, new StatusTransformer);
|
||||
$res = $this->fractal->createData($resource)->toArray();
|
||||
return response()->json($res, 200, [], JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
|
||||
public function followSuggestions(Request $request)
|
||||
{
|
||||
$followers = Auth::user()->profile->recommendFollowers();
|
||||
$resource = new Fractal\Resource\Collection($followers, new AccountTransformer);
|
||||
$res = $this->fractal->createData($resource)->toArray();
|
||||
return response()->json($res);
|
||||
}
|
||||
|
||||
public function avatarUpdate(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'upload' => 'required|mimes:jpeg,png,gif|max:2000',
|
||||
]);
|
||||
try {
|
||||
$user = Auth::user();
|
||||
$profile = $user->profile;
|
||||
$file = $request->file('upload');
|
||||
$path = (new AvatarController())->getPath($user, $file);
|
||||
$dir = $path['root'];
|
||||
$name = $path['name'];
|
||||
$public = $path['storage'];
|
||||
$currentAvatar = storage_path('app/'.$profile->avatar->media_path);
|
||||
$loc = $request->file('upload')->storeAs($public, $name);
|
||||
|
||||
$avatar = Avatar::whereProfileId($profile->id)->firstOrFail();
|
||||
$opath = $avatar->media_path;
|
||||
$avatar->media_path = "$public/$name";
|
||||
$avatar->thumb_path = null;
|
||||
$avatar->change_count = ++$avatar->change_count;
|
||||
$avatar->last_processed_at = null;
|
||||
$avatar->save();
|
||||
|
||||
Cache::forget("avatar:{$profile->id}");
|
||||
AvatarOptimize::dispatch($user->profile, $currentAvatar);
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'code' => 200,
|
||||
'msg' => 'Avatar successfully updated'
|
||||
]);
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs\AvatarPipeline;
|
||||
|
||||
use \Carbon\Carbon;
|
||||
use Image as Intervention;
|
||||
use App\{Avatar, Profile};
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
|
||||
class AvatarOptimize implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
protected $profile;
|
||||
protected $current;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Profile $profile, $current)
|
||||
{
|
||||
$this->profile = $profile;
|
||||
$this->current = $current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$avatar = $this->profile->avatar;
|
||||
$file = storage_path("app/$avatar->media_path");
|
||||
|
||||
try {
|
||||
$img = Intervention::make($file)->orientate();
|
||||
$img->fit(200, 200, function ($constraint) {
|
||||
$constraint->upsize();
|
||||
});
|
||||
$quality = config('pixelfed.image_quality');
|
||||
$img->save($file, $quality);
|
||||
|
||||
$avatar = Avatar::whereProfileId($this->profile->id)->firstOrFail();
|
||||
$avatar->thumb_path = $avatar->media_path;
|
||||
$avatar->change_count = ++$avatar->change_count;
|
||||
$avatar->last_processed_at = Carbon::now();
|
||||
$avatar->save();
|
||||
$this->deleteOldAvatar($avatar->media_path, $this->current);
|
||||
} catch (Exception $e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected function deleteOldAvatar($new, $current)
|
||||
{
|
||||
if(storage_path('app/' . $new) == $current) {
|
||||
return;
|
||||
}
|
||||
if(is_file($current)) {
|
||||
@unlink($current);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Report extends Model
|
||||
{
|
||||
public function url()
|
||||
{
|
||||
return url('/i/admin/reports/show/' . $this->id);
|
||||
}
|
||||
|
||||
public function reporter()
|
||||
{
|
||||
return $this->belongsTo(Profile::class, 'profile_id');
|
||||
}
|
||||
|
||||
public function reported()
|
||||
{
|
||||
$class = $this->object_type;
|
||||
switch ($class) {
|
||||
case 'App\Status':
|
||||
$column = 'id';
|
||||
break;
|
||||
|
||||
default:
|
||||
$column = 'id';
|
||||
break;
|
||||
}
|
||||
return (new $class())->where($column, $this->object_id)->firstOrFail();
|
||||
}
|
||||
|
||||
public function reportedUser()
|
||||
{
|
||||
return $this->belongsTo(Profile::class, 'reported_profile_id', 'id');
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class UpdateStatusTableChangeCaptionToText extends Migration
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
DB::getDoctrineSchemaManager()
|
||||
->getDatabasePlatform()
|
||||
->registerDoctrineTypeMapping('enum', 'string');
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('statuses', function ($table) {
|
||||
$table->text('caption')->change();
|
||||
$table->text('rendered')->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
!function(e){var n={};function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="/",t(t.s=2)}({2:function(e,n,t){e.exports=t("uOOV")},uOOV:function(e,n){$(document).ready(function(){$(".pagination").hide();var e=document.querySelector(".timeline-feed");new InfiniteScroll(e,{path:".pagination__next",append:".timeline-feed",status:".page-load-status",history:!1}).on("append",function(e,n,t){pixelfed.hydrateLikes()})})}});
|
||||
!function(e){var n={};function t(o){if(n[o])return n[o].exports;var i=n[o]={i:o,l:!1,exports:{}};return e[o].call(i.exports,i,i.exports,t),i.l=!0,i.exports}t.m=e,t.c=n,t.d=function(e,n,o){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:o})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="/",t(t.s=2)}({2:function(e,n,t){e.exports=t("uOOV")},uOOV:function(e,n){$(document).ready(function(){$(".pagination").hide(),$(".container.timeline-container").removeClass("d-none");var e=document.querySelector(".timeline-feed");pixelfed.fetchLikes(),new InfiniteScroll(e,{path:".pagination__next",append:".timeline-feed",status:".page-load-status",history:!1}).on("append",function(e,n,t){pixelfed.hydrateLikes(),$(".status-card > .card-footer").each(function(){var e=$(this);e.hasClass("d-none")||e.find('input[name="comment"]').val()||$(this).addClass("d-none")})})}),$(document).on("DOMContentLoaded",function(){var e=!1,n=function(){if(!1===e){e=!0;var n=[].slice.call(document.querySelectorAll("img.lazy"));n.forEach(function(e){e.getBoundingClientRect().top<=window.innerHeight&&e.getBoundingClientRect().bottom>=0&&"none"!==getComputedStyle(e).display&&(e.src=e.dataset.src,e.srcset=e.dataset.srcset,e.classList.remove("lazy"),n=n.filter(function(n){return n!==e}))}),e=!1}};document.addEventListener("scroll",n),window.addEventListener("resize",n),window.addEventListener("orientationchange",n)})}});
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"/js/app.js": "/js/app.js?id=4c2440700c647b915b2e",
|
||||
"/css/app.css": "/css/app.css?id=a7c64d139bb04ef8e290",
|
||||
"/js/timeline.js": "/js/timeline.js?id=d9a3145c0cd21ca09172",
|
||||
"/js/app.js": "/js/app.js?id=670b2543dcb79503ba92",
|
||||
"/css/app.css": "/css/app.css?id=909c2fa80940ca721877",
|
||||
"/js/timeline.js": "/js/timeline.js?id=74c2181f0fcd6fe6933c",
|
||||
"/js/activity.js": "/js/activity.js?id=723dfb98bbbc96a9d39f"
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-white">
|
||||
<span class="font-weight-bold h5">Who to follow</span>
|
||||
<span class="small float-right font-weight-bold">
|
||||
<a href="javascript:void(0);" class="pr-2" v-on:click="fetchData">Refresh</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div v-if="results.length == 0">
|
||||
<p class="mb-0 font-weight-bold">You are not following anyone yet, try the <a href="/discover">discover</a> feature to find users to follow.</p>
|
||||
</div>
|
||||
<div v-for="(user, index) in results">
|
||||
<div class="media " style="width:100%">
|
||||
<img class="mr-3" :src="user.avatar" width="40px">
|
||||
<div class="media-body" style="width:70%">
|
||||
<p class="my-0 font-weight-bold text-truncate" style="text-overflow: hidden">{{user.acct}} <span class="text-muted font-weight-normal">@{{user.username}}</span></p>
|
||||
<a class="btn btn-outline-primary px-3 py-0" :href="user.url" style="border-radius:20px;">Follow</a>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="index != results.length - 1"><hr></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
results: {},
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.fetchData();
|
||||
},
|
||||
methods: {
|
||||
fetchData() {
|
||||
axios.get('/api/local/i/follow-suggestions')
|
||||
.then(response => {
|
||||
this.results = response.data;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,13 +1,54 @@
|
||||
$(document).ready(function() {
|
||||
$('.pagination').hide();
|
||||
$('.container.timeline-container').removeClass('d-none');
|
||||
let elem = document.querySelector('.timeline-feed');
|
||||
pixelfed.fetchLikes();
|
||||
|
||||
let infScroll = new InfiniteScroll( elem, {
|
||||
path: '.pagination__next',
|
||||
append: '.timeline-feed',
|
||||
status: '.page-load-status',
|
||||
history: false,
|
||||
});
|
||||
|
||||
infScroll.on( 'append', function( response, path, items ) {
|
||||
pixelfed.hydrateLikes();
|
||||
$('.status-card > .card-footer').each(function() {
|
||||
var el = $(this);
|
||||
if(!el.hasClass('d-none') && !el.find('input[name="comment"]').val()) {
|
||||
$(this).addClass('d-none');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
$(document).on("DOMContentLoaded", function() {
|
||||
|
||||
var active = false;
|
||||
|
||||
var lazyLoad = function() {
|
||||
if (active === false) {
|
||||
active = true;
|
||||
|
||||
var lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));
|
||||
lazyImages.forEach(function(lazyImage) {
|
||||
if ((lazyImage.getBoundingClientRect().top <= window.innerHeight && lazyImage.getBoundingClientRect().bottom >= 0) && getComputedStyle(lazyImage).display !== "none") {
|
||||
lazyImage.src = lazyImage.dataset.src;
|
||||
lazyImage.srcset = lazyImage.dataset.srcset;
|
||||
lazyImage.classList.remove("lazy");
|
||||
|
||||
lazyImages = lazyImages.filter(function(image) {
|
||||
return image !== lazyImage;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
active = false;
|
||||
};
|
||||
}
|
||||
document.addEventListener("scroll", lazyLoad);
|
||||
window.addEventListener("resize", lazyLoad);
|
||||
window.addEventListener("orientationchange", lazyLoad);
|
||||
});
|
||||
|
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
return [
|
||||
'viewMyProfile' => 'Voir mon profil',
|
||||
'myTimeline' => 'Ma chronologie',
|
||||
'publicTimeline' => 'Chronologie publique',
|
||||
'remoteFollow' => 'Suivre à distance',
|
||||
'settings' => 'Paramètres',
|
||||
'admin' => 'Admin',
|
||||
'logout' => ' Se déconnecter',
|
||||
];
|
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'viewMyProfile' => 'Ver perfil',
|
||||
'myTimeline' => 'A miña liña temporal',
|
||||
'publicTimeline' => 'Liña temporal pública',
|
||||
'remoteFollow' => 'Seguimento remoto',
|
||||
'settings' => 'Axustes',
|
||||
'admin' => 'Admin',
|
||||
'logout' => 'Saír',
|
||||
|
||||
];
|
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'viewMyProfile' => 'Veire mon perfil',
|
||||
'myTimeline' => 'Ma cronologia',
|
||||
'publicTimeline' => 'Cronologia publica',
|
||||
'remoteFollow' => 'Seguir a distància',
|
||||
'settings' => 'Paramètres',
|
||||
'admin' => 'Admin',
|
||||
'logout' => 'Desconnexion',
|
||||
|
||||
];
|
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'emptyPersonalTimeline' => 'Vòstre cronologia es voida.'
|
||||
|
||||
];
|
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'viewMyProfile' => 'Pokaż mój profil',
|
||||
'myTimeline' => 'Moja oś czasu',
|
||||
'publicTimeline' => 'Publiczna oś czasu',
|
||||
'remoteFollow' => 'Zdalne śledzenie',
|
||||
'settings' => 'Ustawienia',
|
||||
'admin' => 'Administrator',
|
||||
'logout' => 'Wyloguj się',
|
||||
|
||||
];
|
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'exception_message' => 'Mensaxe da exepción: :message',
|
||||
'exception_trace' => 'Traza da excepción: :trace',
|
||||
'exception_message_title' => 'Mensaxe da excepción',
|
||||
'exception_trace_title' => 'Traza da excepción',
|
||||
|
||||
'backup_failed_subject' => 'Erro no respaldo de :application_name',
|
||||
'backup_failed_body' => 'Importante: Algo fallou ao respaldar :application_name',
|
||||
|
||||
'backup_successful_subject' => 'Respaldo realizado correctamente :application_name',
|
||||
'backup_successful_subject_title' => 'Novo respaldo correcto!',
|
||||
'backup_successful_body' => 'Parabéns, un novo respaldo de :application_name foi realizado correctamente no disco con nome :disk_name.',
|
||||
|
||||
'cleanup_failed_subject' => 'Limpando os respaldos de :application_name failed.',
|
||||
'cleanup_failed_body' => 'Algo fallou mentras se limpaban os respaldos de :application_name',
|
||||
|
||||
'cleanup_successful_subject' => 'Limpeza correcta nos respaldos de :application_name',
|
||||
'cleanup_successful_subject_title' => 'Limpeza dos respaldos correcta!',
|
||||
'cleanup_successful_body' => 'Realizouse correctamente a limpeza dos respaldos de :application_name no disco con nome :disk_name.',
|
||||
|
||||
'healthy_backup_found_subject' => 'Os respaldos de :application_name no disco :disk_name están en bo estado',
|
||||
'healthy_backup_found_subject_title' => 'Os respaldos de :application_name están ben!',
|
||||
'healthy_backup_found_body' => 'Os respaldos de :application_name están en bo estado. Bo traballo!',
|
||||
|
||||
'unhealthy_backup_found_subject' => 'Importante: Os respaldos de :application_name non están en bo estado',
|
||||
'unhealthy_backup_found_subject_title' => 'Importante: Os respaldos de :application_name non están ben. :problem',
|
||||
'unhealthy_backup_found_body' => 'Os respaldos para :application_name no disco :disk_name non están ben.',
|
||||
'unhealthy_backup_found_not_reachable' => 'Non se puido alcanzar o disco de destino. :error',
|
||||
'unhealthy_backup_found_empty' => 'Non existen copias de respaldo para esta aplicación.',
|
||||
'unhealthy_backup_found_old' => 'O último respaldo realizouse en :date e considerase demasiado antigo.',
|
||||
'unhealthy_backup_found_unknown' => 'Lamentámolo, non se puido determinar unha causa concreta.',
|
||||
'unhealthy_backup_found_full' => 'Os respaldos están a utilizar demasiado espazo. A utilización actual de :disk_usage é maior que o límite establecido de :disk_limit.',
|
||||
];
|
@ -0,0 +1,96 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container notification-page" style="min-height: 60vh;">
|
||||
<div class="col-12 col-md-8 offset-md-2">
|
||||
<div class="card mt-3">
|
||||
<div class="card-body p-0">
|
||||
<ul class="nav nav-tabs d-flex text-center">
|
||||
<li class="nav-item flex-fill">
|
||||
<a class="nav-link font-weight-bold text-uppercase active" href="{{route('notifications.following')}}">Following</a>
|
||||
</li>
|
||||
<li class="nav-item flex-fill">
|
||||
<a class="nav-link font-weight-bold text-uppercase" href="{{route('notifications')}}">My Notifications</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="">
|
||||
{{-- <div class="card-header bg-white">
|
||||
<span class="font-weight-bold lead">Notifications</span>
|
||||
<span class="small float-right font-weight-bold">
|
||||
<a href="?a=comment" class="pr-4 text-muted" title="Commented on your post"><i class="fas fa-comment fa-2x"></i></a>
|
||||
<a href="?a=follow" class="pr-4 text-muted" title="Followed you"><i class="fas fa-user-plus fa-2x"></i></a>
|
||||
<a href="?a=mention" class="pr-4 text-muted" title="Mentioned you"><i class="fas fa-comment-dots fa-2x"></i></a>
|
||||
<a href="{{route('notifications')}}" class="font-weight-bold text-dark">View All</a>
|
||||
</span>
|
||||
</div> --}}
|
||||
</div>
|
||||
<ul class="list-group">
|
||||
|
||||
@if($notifications->count() > 0)
|
||||
@foreach($notifications as $notification)
|
||||
@php
|
||||
if(!in_array($notification->action, ['like', 'follow'])) {
|
||||
continue;
|
||||
}
|
||||
@endphp
|
||||
<li class="list-group-item notification border-0">
|
||||
@switch($notification->action)
|
||||
|
||||
@case('like')
|
||||
<span class="notification-icon pr-3">
|
||||
<img src="{{optional($notification->actor, function($actor) {
|
||||
return $actor->avatarUrl(); }) }}" width="32px" class="rounded-circle">
|
||||
</span>
|
||||
<span class="notification-text">
|
||||
<a class="font-weight-bold text-dark" href="{{$notification->actor->url()}}">{{$notification->actor->username}}</a>
|
||||
|
||||
{{__('liked a post by')}}
|
||||
|
||||
<a class="font-weight-bold text-dark" href="{{$notification->item->profile->url()}}">{{$notification->item->profile->username}}</a>
|
||||
|
||||
<span class="text-muted notification-timestamp pl-1">{{$notification->created_at->diffForHumans(null, true, true, true)}}</span>
|
||||
</span>
|
||||
<span class="float-right notification-action">
|
||||
@if($notification->item_id && $notification->item_type == 'App\Status')
|
||||
<a href="{{$notification->status->url()}}"><img src="{{$notification->status->thumb()}}" width="32px" height="32px"></a>
|
||||
@endif
|
||||
</span>
|
||||
@break
|
||||
|
||||
@case('follow')
|
||||
<span class="notification-icon pr-3">
|
||||
<img src="{{$notification->actor->avatarUrl()}}" width="32px" class="rounded-circle">
|
||||
</span>
|
||||
<span class="notification-text">
|
||||
<a class="font-weight-bold text-dark" href="{{$notification->actor->url()}}">{{$notification->actor->username}}</a>
|
||||
|
||||
{{__('started following')}}
|
||||
|
||||
<a class="font-weight-bold text-dark" href="{{$notification->item->url()}}">{{$notification->item->username}}</a>
|
||||
|
||||
<span class="text-muted notification-timestamp pl-1">{{$notification->created_at->diffForHumans(null, true, true, true)}}</span>
|
||||
</span>
|
||||
@break
|
||||
|
||||
@endswitch
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
|
||||
<div class="d-flex justify-content-center my-4">
|
||||
{{$notifications->links()}}
|
||||
</div>
|
||||
@else
|
||||
<div class="mt-4">
|
||||
<div class="alert alert-info font-weight-bold">No unread notifications found.</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script type="text/javascript" src="{{mix('js/activity.js')}}"></script>
|
||||
@endpush
|
@ -0,0 +1,37 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="container px-0 mt-md-4">
|
||||
<div class="col-12 col-md-8 offset-md-2">
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<p class="mb-0">
|
||||
<img class="img-thumbnail mr-2" src="{{$user->avatarUrl()}}" width="24px" height="24px" style="border-radius:12px;">
|
||||
<span class="font-weight-bold pr-1"><bdi><a class="text-dark" href="{{$status->profile->url()}}">{{ str_limit($status->profile->username, 15)}}</a></bdi></span>
|
||||
<span class="comment-text">{!! $status->rendered ?? e($status->caption) !!} <a href="{{$status->url()}}" class="text-dark small font-weight-bold float-right pl-2">{{$status->created_at->diffForHumans(null, true, true ,true)}}</a></span>
|
||||
</p>
|
||||
<hr>
|
||||
<div class="comments">
|
||||
@foreach($replies as $item)
|
||||
<p class="mb-2">
|
||||
<span class="font-weight-bold pr-1">
|
||||
<img class="img-thumbnail mr-2" src="{{$item->profile->avatarUrl()}}" width="24px" height="24px" style="border-radius:12px;">
|
||||
<bdi><a class="text-dark" href="{{$item->profile->url()}}">{{ str_limit($item->profile->username, 15)}}</a></bdi>
|
||||
</span>
|
||||
<span class="comment-text">
|
||||
{!! $item->rendered ?? e($item->caption) !!}
|
||||
<a href="{{$item->url()}}" class="text-dark small font-weight-bold float-right pl-2">
|
||||
{{$item->created_at->diffForHumans(null, true, true ,true)}}
|
||||
</a>
|
||||
</span>
|
||||
</p>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endsection
|
@ -1,111 +1,102 @@
|
||||
<div class="card my-4 status-card card-md-rounded-0">
|
||||
<div class="card-header d-inline-flex align-items-center bg-white">
|
||||
<img src="{{$item->profile->avatarUrl()}}" width="32px" height="32px" style="border-radius: 32px;">
|
||||
<a class="username font-weight-bold pl-2 text-dark" href="{{$item->profile->url()}}">
|
||||
{{$item->profile->username}}
|
||||
</a>
|
||||
<div class="text-right" style="flex-grow:1;">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-link text-dark no-caret dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="Post options">
|
||||
<span class="fas fa-ellipsis-v fa-lg text-muted"></span>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
|
||||
<a class="dropdown-item" href="{{$item->url()}}">Go to post</a>
|
||||
<a class="dropdown-item" href="{{route('report.form')}}?type=post&id={{$item->id}}">Report Inappropriate</a>
|
||||
<a class="dropdown-item" href="#">Embed</a>
|
||||
@if(Auth::check())
|
||||
@if(Auth::user()->profile->id === $item->profile->id || Auth::user()->is_admin == true)
|
||||
<a class="dropdown-item" href="{{$item->editUrl()}}">Edit</a>
|
||||
<form method="post" action="/i/delete">
|
||||
@csrf
|
||||
<input type="hidden" name="type" value="post">
|
||||
<input type="hidden" name="item" value="{{$item->id}}">
|
||||
<button type="submit" class="dropdown-item btn btn-link">Delete</button>
|
||||
</form>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if($item->is_nsfw)
|
||||
<details class="details-animated">
|
||||
<p>
|
||||
<summary>NSFW / Hidden Image</summary>
|
||||
<a class="max-hide-overflow {{$item->firstMedia()->filter_class}}" href="{{$item->url()}}">
|
||||
<img class="card-img-top" src="{{$item->mediaUrl()}}">
|
||||
</a>
|
||||
</p>
|
||||
</details>
|
||||
@else
|
||||
<a class="max-hide-overflow {{$item->firstMedia()->filter_class}}" href="{{$item->url()}}">
|
||||
<img class="card-img-top" src="{{$item->mediaUrl()}}">
|
||||
</a>
|
||||
@endif
|
||||
<div class="card-body">
|
||||
<div class="reactions my-1">
|
||||
<form class="d-inline-flex like-form pr-3" method="post" action="/i/like" style="display: inline;" data-id="{{$item->id}}" data-action="like" data-count="{{$item->likes_count}}">
|
||||
@csrf
|
||||
<input type="hidden" name="item" value="{{$item->id}}">
|
||||
<button class="btn btn-link text-dark p-0" type="submit" title=""Like!>
|
||||
<h3 class="far fa-heart status-heart m-0"></h3>
|
||||
</button>
|
||||
</form>
|
||||
<h3 class="far fa-comment status-comment-focus" title="Comment"></h3>
|
||||
<span class="float-right">
|
||||
<form class="d-inline-flex bookmark-form" method="post" action="/i/bookmark" style="display: inline;" data-id="{{$item->id}}" data-action="bookmark">
|
||||
@csrf
|
||||
<input type="hidden" name="item" value="{{$item->id}}">
|
||||
<button class="btn btn-link text-dark p-0 border-0" type="submit" title="Save">
|
||||
<h3 class="far fa-bookmark m-0"></h3>
|
||||
</button>
|
||||
</form>
|
||||
</span>
|
||||
</div>
|
||||
<div class="likes font-weight-bold">
|
||||
<span class="like-count">{{$item->likes_count}}</span> likes
|
||||
</div>
|
||||
<div class="caption">
|
||||
<p class="mb-1">
|
||||
<span class="username font-weight-bold">
|
||||
<bdi><a class="text-dark" href="{{$item->profile->url()}}">{{$item->profile->username}}</a></bdi>
|
||||
</span>
|
||||
<span>{!! $item->rendered ?? e($item->caption) !!}</span>
|
||||
</p>
|
||||
</div>
|
||||
@if($item->comments()->count() > 3)
|
||||
<div class="more-comments">
|
||||
<a class="text-muted" href="{{$item->url()}}">Load more comments</a>
|
||||
</div>
|
||||
@endif
|
||||
<div class="comments">
|
||||
@if(isset($showSingleComment) && $showSingleComment === true)
|
||||
<p class="mb-0">
|
||||
<span class="font-weight-bold pr-1">
|
||||
<bdi>
|
||||
<a class="text-dark" href="{{$status->profile->url()}}">{{$status->profile->username}}</a>
|
||||
</bdi>
|
||||
</span>
|
||||
<span class="comment-text">{!! $item->rendered ?? e($item->caption) !!}</span>
|
||||
<span class="float-right">
|
||||
<a href="{{$status->url()}}" class="text-dark small font-weight-bold">
|
||||
{{$status->created_at->diffForHumans(null, true, true, true)}}
|
||||
</a>
|
||||
</span>
|
||||
</p>
|
||||
@else
|
||||
@endif
|
||||
</div>
|
||||
<div class="timestamp pt-1">
|
||||
<p class="small text-uppercase mb-0"><a href="{{$item->url()}}" class="text-muted">{{$item->created_at->diffForHumans()}}</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer bg-white">
|
||||
<form class="comment-form" method="post" action="/i/comment" data-id="{{$item->id}}" data-truncate="true">
|
||||
<div class="card mb-4 status-card card-md-rounded-0" data-id="{{$item->id}}" data-comment-max-id="0">
|
||||
<div class="card-header d-inline-flex align-items-center bg-white">
|
||||
<img src="{{$item->profile->avatarUrl()}}" width="32px" height="32px" style="border-radius: 32px;">
|
||||
<a class="username font-weight-bold pl-2 text-dark" href="{{$item->profile->url()}}">
|
||||
{{$item->profile->username}}
|
||||
</a>
|
||||
<div class="text-right" style="flex-grow:1;">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-link text-dark no-caret dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="Post options">
|
||||
<span class="fas fa-ellipsis-v fa-lg text-muted"></span>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
|
||||
<a class="dropdown-item font-weight-bold" href="{{$item->url()}}">Go to post</a>
|
||||
<a class="dropdown-item font-weight-bold" href="{{route('report.form')}}?type=post&id={{$item->id}}">Report</a>
|
||||
<a class="dropdown-item font-weight-bold" href="#">Embed</a>
|
||||
@if(Auth::check())
|
||||
@if(Auth::user()->profile->id === $item->profile->id || Auth::user()->is_admin == true)
|
||||
<a class="dropdown-item font-weight-bold" href="{{$item->editUrl()}}">Edit</a>
|
||||
<form method="post" action="/i/delete">
|
||||
@csrf
|
||||
<input type="hidden" name="type" value="post">
|
||||
<input type="hidden" name="item" value="{{$item->id}}">
|
||||
<input class="form-control status-reply-input" name="comment" placeholder="Add a comment…">
|
||||
<button type="submit" class="dropdown-item btn btn-link text-danger font-weight-bold">Delete</button>
|
||||
</form>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if($item->is_nsfw)
|
||||
<details class="details-animated">
|
||||
<summary>
|
||||
<p class="mb-0 px-3 lead font-weight-bold">Content Warning: This may contain potentially sensitive content.</p>
|
||||
<p class="font-weight-light">(click to show)</p>
|
||||
</summary>
|
||||
<a class="max-hide-overflow {{$item->firstMedia()->filter_class}}" href="{{$item->url()}}">
|
||||
<img class="card-img-top lazy" src="data:image/gif;base64,R0lGODlhAQABAIAAAMLCwgAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" data-src="{{$item->mediaUrl()}}" data-srcset="{{$item->mediaUrl()}} 1x">
|
||||
</a>
|
||||
</details>
|
||||
@else
|
||||
<a class="max-hide-overflow {{$item->firstMedia()->filter_class}}" href="{{$item->url()}}">
|
||||
@if($loop->index < 2)
|
||||
<img class="card-img-top" src="{{$item->mediaUrl()}}" data-srcset="{{$item->mediaUrl()}} 1x">
|
||||
@else
|
||||
<img class="card-img-top lazy" src="data:image/gif;base64,R0lGODlhAQABAIAAAMLCwgAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" data-src="{{$item->mediaUrl()}}" data-srcset="{{$item->mediaUrl()}} 1x">
|
||||
@endif
|
||||
</a>
|
||||
@endif
|
||||
<div class="card-body">
|
||||
<div class="reactions my-1">
|
||||
<form class="d-inline-flex like-form pr-3" method="post" action="/i/like" style="display: inline;" data-id="{{$item->id}}" data-action="like" data-count="{{$item->likes_count}}">
|
||||
@csrf
|
||||
<input type="hidden" name="item" value="{{$item->id}}">
|
||||
<button class="btn btn-link text-dark p-0" type="submit" title="Like!">
|
||||
<h3 class="far fa-heart status-heart m-0"></h3>
|
||||
</button>
|
||||
</form>
|
||||
<h3 class="far fa-comment pr-3 status-comment-focus" title="Comment"></h3>
|
||||
<form class="d-inline-flex share-form pr-3" method="post" action="/i/share" style="display: inline;" data-id="{{$item->id}}" data-action="share" data-count="{{$item->shares_count}}">
|
||||
@csrf
|
||||
<input type="hidden" name="item" value="{{$item->id}}">
|
||||
<button class="btn btn-link text-dark p-0" type="submit" title="Share">
|
||||
<h3 class="far fa-share-square m-0"></h3>
|
||||
</button>
|
||||
</form>
|
||||
<span class="float-right">
|
||||
<form class="d-inline-flex bookmark-form" method="post" action="/i/bookmark" style="display: inline;" data-id="{{$item->id}}" data-action="bookmark">
|
||||
@csrf
|
||||
<input type="hidden" name="item" value="{{$item->id}}">
|
||||
<button class="btn btn-link text-dark p-0 border-0" type="submit" title="Save">
|
||||
<h3 class="far fa-bookmark m-0"></h3>
|
||||
</button>
|
||||
</form>
|
||||
</span>
|
||||
</div>
|
||||
<div class="likes font-weight-bold">
|
||||
<span class="like-count">{{$item->likes_count}}</span> likes
|
||||
</div>
|
||||
<div class="caption">
|
||||
<p class="mb-1">
|
||||
<span class="username font-weight-bold">
|
||||
<bdi><a class="text-dark" href="{{$item->profile->url()}}">{{$item->profile->username}}</a></bdi>
|
||||
</span>
|
||||
<span>{!! $item->rendered ?? e($item->caption) !!}</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="comments">
|
||||
</div>
|
||||
<div class="timestamp pt-1">
|
||||
<p class="small text-uppercase mb-0"><a href="{{$item->url()}}" class="text-muted">{{$item->created_at->diffForHumans()}}</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer bg-white">
|
||||
<form class="comment-form" method="post" action="/i/comment" data-id="{{$item->id}}" data-truncate="true">
|
||||
@csrf
|
||||
<input type="hidden" name="item" value="{{$item->id}}">
|
||||
<input class="form-control status-reply-input" name="comment" placeholder="Add a comment…" autocomplete="off">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -0,0 +1,115 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
|
||||
<noscript>
|
||||
<div class="container">
|
||||
<div class="card border-left-primary mt-5">
|
||||
<div class="card-body">
|
||||
<p class="mb-0 font-weight-bold">Javascript is required for an optimized experience, please enable it or use the <a href="#">lite</a> version.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</noscript>
|
||||
|
||||
<div class="container p-0 d-none timeline-container">
|
||||
<div class="row">
|
||||
<div class="col-md-10 col-lg-8 mx-auto pt-4 px-0 my-3 pr-2">
|
||||
@if (session('status'))
|
||||
<div class="alert alert-success">
|
||||
<span class="font-weight-bold">{!! session('status') !!}</span>
|
||||
</div>
|
||||
@endif
|
||||
@if (session('error'))
|
||||
<div class="alert alert-danger">
|
||||
<span class="font-weight-bold">{!! session('error') !!}</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
|
||||
<div class="timeline-feed" data-timeline="{{$type}}">
|
||||
|
||||
@foreach($timeline as $item)
|
||||
@if(is_null($item->in_reply_to_id))
|
||||
@include('status.template')
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
@if($timeline->count() == 0)
|
||||
<div class="card card-md-rounded-0">
|
||||
<div class="card-body py-5">
|
||||
<div class="d-flex justify-content-center align-items-center">
|
||||
<p class="lead font-weight-bold mb-0">{{ __('timeline.emptyPersonalTimeline') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="page-load-status" style="display: none;">
|
||||
<div class="infinite-scroll-request" style="display: none;">
|
||||
<div class="fixed-top loading-page"></div>
|
||||
</div>
|
||||
<div class="infinite-scroll-last" style="display: none;">
|
||||
<h3>No more content</h3>
|
||||
<p class="text-muted">
|
||||
Maybe you could try
|
||||
<a href="{{route('discover')}}">discovering</a>
|
||||
more people you can follow.
|
||||
</p>
|
||||
</div>
|
||||
<div class="infinite-scroll-error" style="display: none;">
|
||||
<h3>Whoops, an error</h3>
|
||||
<p class="text-muted">
|
||||
Try reloading the page
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-center">
|
||||
{{$timeline->links()}}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-md-2 col-lg-4 pt-4 my-3">
|
||||
|
||||
<div class="media d-flex align-items-center mb-4">
|
||||
<a href="{{Auth::user()->profile->url()}}">
|
||||
<img class="mr-3 rounded-circle box-shadow" src="{{Auth::user()->profile->avatarUrl()}}" alt="{{Auth::user()->username}}'s avatar" width="64px">
|
||||
</a>
|
||||
<div class="media-body">
|
||||
<p class="mb-0 px-0 font-weight-bold"><a href="{{Auth::user()->profile->url()}}">@{{Auth::user()->username}}</a></p>
|
||||
<p class="mb-0 small text-muted">{{Auth::user()->name}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<follow-suggestions></follow-suggestions>
|
||||
|
||||
<footer>
|
||||
<div class="container pb-5">
|
||||
<p class="mb-0 text-uppercase font-weight-bold text-muted small">
|
||||
<a href="{{route('site.about')}}" class="text-dark pr-2">About Us</a>
|
||||
<a href="{{route('site.help')}}" class="text-dark pr-2">Support</a>
|
||||
<a href="{{route('site.opensource')}}" class="text-dark pr-2">Open Source</a>
|
||||
<a href="{{route('site.language')}}" class="text-dark pr-2">Language</a>
|
||||
<a href="{{route('site.terms')}}" class="text-dark pr-2">Terms</a>
|
||||
<a href="{{route('site.privacy')}}" class="text-dark pr-2">Privacy</a>
|
||||
<a href="{{route('site.platform')}}" class="text-dark pr-2">API</a>
|
||||
<a href="#" class="text-dark pr-2">Directory</a>
|
||||
<a href="#" class="text-dark pr-2">Profiles</a>
|
||||
<a href="#" class="text-dark">Hashtags</a>
|
||||
</p>
|
||||
<p class="mb-0 text-uppercase font-weight-bold text-muted small">
|
||||
<a href="http://pixelfed.org" class="text-muted" rel="noopener">Powered by PixelFed</a>
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script type="text/javascript" src="{{mix('js/timeline.js')}}"></script>
|
||||
@endpush
|
Loading…
Reference in New Issue