mirror of
https://github.com/pixelfed/pixelfed.git
synced 2024-11-18 12:31:27 +00:00
Merge pull request #516 from pixelfed/frontend-ui-refactor
Frontend ui refactor
This commit is contained in:
commit
7263363028
12 changed files with 286 additions and 11 deletions
10
app/Collection.php
Normal file
10
app/Collection.php
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class Collection extends Model
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
10
app/CollectionItem.php
Normal file
10
app/CollectionItem.php
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class CollectionItem extends Model
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
29
app/DirectMessage.php
Normal file
29
app/DirectMessage.php
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class DirectMessage extends Model
|
||||||
|
{
|
||||||
|
public function status()
|
||||||
|
{
|
||||||
|
return $this->hasOne(Status::class, 'id', 'status_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function url()
|
||||||
|
{
|
||||||
|
return url('/i/message/' . $this->to_id . '/' . $this->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function author()
|
||||||
|
{
|
||||||
|
return $this->hasOne(Profile::class, 'id', 'from_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function me()
|
||||||
|
{
|
||||||
|
return Auth::user()->profile->id === $this->from_id;
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,8 +6,11 @@ use App\Avatar;
|
||||||
use App\Http\Controllers\AvatarController;
|
use App\Http\Controllers\AvatarController;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Jobs\AvatarPipeline\AvatarOptimize;
|
use App\Jobs\AvatarPipeline\AvatarOptimize;
|
||||||
|
use App\Jobs\ImageOptimizePipeline\ImageOptimize;
|
||||||
|
use App\Media;
|
||||||
use App\Profile;
|
use App\Profile;
|
||||||
use App\Transformer\Api\AccountTransformer;
|
use App\Transformer\Api\AccountTransformer;
|
||||||
|
use App\Transformer\Api\MediaTransformer;
|
||||||
use App\Transformer\Api\StatusTransformer;
|
use App\Transformer\Api\StatusTransformer;
|
||||||
use Auth;
|
use Auth;
|
||||||
use Cache;
|
use Cache;
|
||||||
|
@ -115,4 +118,44 @@ class BaseApiController extends Controller
|
||||||
'msg' => 'Avatar successfully updated',
|
'msg' => 'Avatar successfully updated',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function uploadMedia(Request $request)
|
||||||
|
{
|
||||||
|
$this->validate($request, [
|
||||||
|
'file.*' => function() {
|
||||||
|
return [
|
||||||
|
'required',
|
||||||
|
'mimes:' . config('pixelfed.media_types'),
|
||||||
|
'max:' . config('pixelfed.max_photo_size'),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
$user = Auth::user();
|
||||||
|
$profile = $user->profile;
|
||||||
|
$monthHash = hash('sha1', date('Y').date('m'));
|
||||||
|
$userHash = hash('sha1', $user->id.(string) $user->created_at);
|
||||||
|
$photo = $request->file('file');
|
||||||
|
|
||||||
|
$storagePath = "public/m/{$monthHash}/{$userHash}";
|
||||||
|
$path = $photo->store($storagePath);
|
||||||
|
$hash = \hash_file('sha256', $photo);
|
||||||
|
|
||||||
|
$media = new Media();
|
||||||
|
$media->status_id = null;
|
||||||
|
$media->profile_id = $profile->id;
|
||||||
|
$media->user_id = $user->id;
|
||||||
|
$media->media_path = $path;
|
||||||
|
$media->original_sha256 = $hash;
|
||||||
|
$media->size = $photo->getClientSize();
|
||||||
|
$media->mime = $photo->getClientMimeType();
|
||||||
|
$media->filter_class = null;
|
||||||
|
$media->filter_name = null;
|
||||||
|
$media->save();
|
||||||
|
|
||||||
|
ImageOptimize::dispatch($media);
|
||||||
|
$resource = new Fractal\Resource\Item($media, new MediaTransformer());
|
||||||
|
$res = $this->fractal->createData($resource)->toArray();
|
||||||
|
|
||||||
|
return response()->json($res);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,7 +110,10 @@ class StatusController extends Controller
|
||||||
$status->profile_id = $profile->id;
|
$status->profile_id = $profile->id;
|
||||||
$status->caption = strip_tags($request->caption);
|
$status->caption = strip_tags($request->caption);
|
||||||
$status->is_nsfw = $cw;
|
$status->is_nsfw = $cw;
|
||||||
|
|
||||||
|
// TODO: remove deprecated visibility in favor of scope
|
||||||
$status->visibility = $visibility;
|
$status->visibility = $visibility;
|
||||||
|
$status->scope = $visibility;
|
||||||
|
|
||||||
$status->save();
|
$status->save();
|
||||||
|
|
||||||
|
|
|
@ -39,9 +39,10 @@ class ProfileTransformer extends Fractal\TransformerAbstract
|
||||||
'owner' => $profile->permalink(),
|
'owner' => $profile->permalink(),
|
||||||
'publicKeyPem' => $profile->public_key,
|
'publicKeyPem' => $profile->public_key,
|
||||||
],
|
],
|
||||||
'endpoints' => [
|
// remove shared inbox support until proper support
|
||||||
'sharedInbox' => config('routes.api.sharedInbox'),
|
// 'endpoints' => [
|
||||||
],
|
// 'sharedInbox' => config('routes.api.sharedInbox'),
|
||||||
|
// ],
|
||||||
'icon' => [
|
'icon' => [
|
||||||
'type' => 'Image',
|
'type' => 'Image',
|
||||||
'mediaType' => 'image/jpeg',
|
'mediaType' => 'image/jpeg',
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
|
class UpdateFollowerTableAddRemoteFlags extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('followers', function (Blueprint $table) {
|
||||||
|
$table->boolean('local_profile')->default(true)->index()->after('following_id');
|
||||||
|
$table->boolean('local_following')->default(true)->index()->after('local_profile');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('followers', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('local_profile');
|
||||||
|
$table->dropColumn('local_following');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
147
resources/assets/js/components/PostComments.vue
Normal file
147
resources/assets/js/components/PostComments.vue
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
<style type="text/css">
|
||||||
|
.b-dropdown > button {
|
||||||
|
padding:0 !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style scoped>
|
||||||
|
span {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.comment-text {
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
.b-dropdown {
|
||||||
|
padding:0 !important;
|
||||||
|
}
|
||||||
|
.b-dropdown < button {
|
||||||
|
}
|
||||||
|
.lds-ring {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
}
|
||||||
|
.lds-ring div {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
width: 51px;
|
||||||
|
height: 51px;
|
||||||
|
margin: 6px;
|
||||||
|
border: 6px solid #6c757d;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
|
||||||
|
border-color: #6c757d transparent transparent transparent;
|
||||||
|
}
|
||||||
|
.lds-ring div:nth-child(1) {
|
||||||
|
animation-delay: -0.45s;
|
||||||
|
}
|
||||||
|
.lds-ring div:nth-child(2) {
|
||||||
|
animation-delay: -0.3s;
|
||||||
|
}
|
||||||
|
.lds-ring div:nth-child(3) {
|
||||||
|
animation-delay: -0.15s;
|
||||||
|
}
|
||||||
|
@keyframes lds-ring {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="lwrapper text-center">
|
||||||
|
<div class="lds-ring"><div></div><div></div><div></div><div></div></div>
|
||||||
|
</div>
|
||||||
|
<div class="cwrapper d-none">
|
||||||
|
<p class="mb-1 text-center load-more-link"><a href="#" class="text-muted" v-on:click="loadMore">Load more comments</a></p>
|
||||||
|
<div class="comments" data-min-id="0" data-max-id="0">
|
||||||
|
<p class="mb-2 d-flex justify-content-between align-items-center" v-for="(comment, index) in results" :data-id="comment.id" v-bind:key="comment.id">
|
||||||
|
<span class="pr-3">
|
||||||
|
<span class="font-weight-bold pr-1"><bdi><a class="text-dark" :href="comment.account.url">{{comment.account.username}}</a></bdi></span>
|
||||||
|
<span class="comment-text" v-html="comment.content"></span>
|
||||||
|
</span>
|
||||||
|
<b-dropdown :id="comment.uri" variant="link" no-caret class="float-right">
|
||||||
|
<template slot="button-content">
|
||||||
|
<i class="fas fa-ellipsis-v text-muted"></i><span class="sr-only">Options</span>
|
||||||
|
</template>
|
||||||
|
<b-dropdown-item class="font-weight-bold" v-on:click="reply(comment)">Reply</b-dropdown-item>
|
||||||
|
<b-dropdown-item class="font-weight-bold" :href="comment.url">Permalink</b-dropdown-item>
|
||||||
|
<b-dropdown-item class="font-weight-bold" v-on:click="embed(comment)">Embed</b-dropdown-item>
|
||||||
|
<b-dropdown-item class="font-weight-bold" :href="comment.account.url">Profile</b-dropdown-item>
|
||||||
|
<b-dropdown-divider></b-dropdown-divider>
|
||||||
|
<b-dropdown-item class="font-weight-bold" :href="'/i/report?type=post&id='+comment.id">Report</b-dropdown-item>
|
||||||
|
</b-dropdown>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['post-id', 'post-username'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
results: {},
|
||||||
|
pagination: {},
|
||||||
|
min_id: 0,
|
||||||
|
max_id: 0,
|
||||||
|
reply_to_profile_id: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchData();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
embed(e) {
|
||||||
|
pixelfed.embed.build(e);
|
||||||
|
},
|
||||||
|
reply(e) {
|
||||||
|
this.reply_to_profile_id = e.account.id;
|
||||||
|
$('.comment-form input[name=comment]').val('@'+e.account.username+' ');
|
||||||
|
$('.comment-form input[name=comment]').focus();
|
||||||
|
},
|
||||||
|
fetchData() {
|
||||||
|
let url = '/api/v2/comments/'+this.postUsername+'/status/'+this.postId;
|
||||||
|
axios.get(url)
|
||||||
|
.then(response => {
|
||||||
|
let self = this;
|
||||||
|
this.results = response.data.data;
|
||||||
|
this.pagination = response.data.meta.pagination;
|
||||||
|
$('.lwrapper').addClass('d-none');
|
||||||
|
$('.cwrapper').removeClass('d-none');
|
||||||
|
}).catch(error => {
|
||||||
|
$('.lds-ring').attr('style','width:100%').addClass('pt-4 font-weight-bold text-muted').text('An error occured, cannot fetch comments. Please try again later.');
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
loadMore(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if(this.pagination.total_pages == 1 || this.pagination.current_page == this.pagination.total_pages) {
|
||||||
|
$('.load-more-link').addClass('d-none');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$('.cwrapper').addClass('d-none');
|
||||||
|
$('.lwrapper').removeClass('d-none');
|
||||||
|
let next = this.pagination.links.next;
|
||||||
|
axios.get(next)
|
||||||
|
.then(response => {
|
||||||
|
let self = this;
|
||||||
|
let res = response.data.data;
|
||||||
|
$('.lwrapper').addClass('d-none');
|
||||||
|
for(let i=0; i < res.length; i++) {
|
||||||
|
this.results.unshift(res[i]);
|
||||||
|
}
|
||||||
|
$('.cwrapper').removeClass('d-none');
|
||||||
|
this.pagination = response.data.meta.pagination;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -1,6 +1,5 @@
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
$(document).on('submit', '.bookmark-form', function(e) {
|
||||||
$('.bookmark-form').submit(function(e) {
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var el = $(this);
|
var el = $(this);
|
||||||
var id = el.data('id');
|
var id = el.data('id');
|
||||||
|
|
|
@ -35,9 +35,9 @@ $(document).ready(function() {
|
||||||
var comments = el.parents().eq(1).find('.comments');
|
var comments = el.parents().eq(1).find('.comments');
|
||||||
}
|
}
|
||||||
|
|
||||||
var comment = '<p class="mb-0"><span class="font-weight-bold pr-1"><bdi><a class="text-dark" href="' + profile + '">' + username + '</a></bdi></span><span class="comment-text">'+ reply + '</span><span class="float-right"><a href="' + permalink + '" class="text-dark small font-weight-bold">1s</a></span></p>';
|
var comment = '<p class="mb-2"><span class="font-weight-bold pr-1"><bdi><a class="text-dark" href="' + profile + '">' + username + '</a></bdi></span><span class="comment-text">'+ reply + '</span></p>';
|
||||||
|
|
||||||
comments.append(comment);
|
comments.prepend(comment);
|
||||||
|
|
||||||
commentform.val('');
|
commentform.val('');
|
||||||
commentform.blur();
|
commentform.blur();
|
||||||
|
|
|
@ -7,11 +7,8 @@
|
||||||
<a href="{{route('site.terms')}}" class="text-primary pr-3">Terms</a>
|
<a href="{{route('site.terms')}}" class="text-primary pr-3">Terms</a>
|
||||||
<a href="{{route('site.privacy')}}" class="text-primary pr-3">Privacy</a>
|
<a href="{{route('site.privacy')}}" class="text-primary pr-3">Privacy</a>
|
||||||
<a href="{{route('site.platform')}}" class="text-primary pr-3">API</a>
|
<a href="{{route('site.platform')}}" class="text-primary pr-3">API</a>
|
||||||
<a href="#" class="text-primary pr-3">Directory</a>
|
|
||||||
<a href="#" class="text-primary pr-3">Profiles</a>
|
|
||||||
<a href="#" class="text-primary pr-3">Hashtags</a>
|
|
||||||
<a href="{{route('site.language')}}" class="text-primary pr-3">Language</a>
|
<a href="{{route('site.language')}}" class="text-primary pr-3">Language</a>
|
||||||
<a href="http://pixelfed.org" class="text-muted float-right" rel="noopener">Powered by PixelFed</a>
|
<a href="https://pixelfed.org" class="text-muted float-right" rel="noopener">Powered by PixelFed</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
2
storage/purify/.gitignore
vendored
Normal file
2
storage/purify/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
Loading…
Reference in a new issue