Pinned posts

This commit is contained in:
MarceauKa 2019-10-22 20:29:59 +02:00
parent 3fc6ad5706
commit a394fb01fe
25 changed files with 132 additions and 12 deletions

View File

@ -27,6 +27,7 @@ class ChestController extends Controller
])->toArray());
$post = new Post(['is_private' => true, 'user_id' => $request->user()->id]);
$post->is_pinned = $data;
$post->postable()->associate($chest)->save();
if ($data['tags']) {
@ -47,6 +48,7 @@ class ChestController extends Controller
$data = collect($request->validated());
$chest->fill($data->only('title', 'content')->toArray());
$chest->post->is_pinned = $data->get('is_pinned', $chest->post->is_pinned);
if ($data['tags']) {
$chest->post->syncTags($data['tags']);

View File

@ -58,7 +58,8 @@ class LinkController extends Controller
$link->updatePreview();
$post = new Post();
$post->is_private = $data->get('is_private', 0);
$post->is_pinned = $data;
$post->is_private = $data;
$post->user_id = $request->user()->id;
$post->postable()->associate($link)->save();
@ -83,6 +84,7 @@ class LinkController extends Controller
$link->fill($data->only('title', 'content', 'url')->toArray());
$link->updatePreview();
$link->post->is_pinned = $data->get('is_pinned', $link->post->is_pinned);
$link->post->is_private = $data->get('is_private', $link->post->is_private);
$link->post->save();

View File

@ -28,7 +28,8 @@ class StoryController extends Controller
])->toArray());
$post = new Post();
$post->is_private = $data->get('is_private', 0);
$post->is_pinned = $data;
$post->is_private = $data;
$post->user_id = $request->user()->id;
$post->postable()->associate($story)->save();
@ -50,6 +51,7 @@ class StoryController extends Controller
$data = collect($request->validated());
$story->fill($data->only('title', 'slug', 'content')->toArray());
$story->post->is_pinned = $data->get('is_pinned', $story->post->is_pinned);
$story->post->is_private = $data->get('is_private', $story->post->is_private);
if ($data['tags']) {

View File

@ -22,6 +22,7 @@ class BrowseController extends Controller
}
$posts = $posts->withPrivate($request)
->pinnedFirst()
->latest()
->paginate(20);
@ -87,6 +88,7 @@ class BrowseController extends Controller
$tag = Tag::named($tag)->firstOrFail();
$posts = Post::withPrivate($request)
->pinnedFirst()
->with('postable', 'tags')
->withAllTags($tag)
->paginate(20);

View File

@ -22,6 +22,9 @@ class StoreChestRequest extends FormRequest
'content' => [
'array',
],
'is_pinned' => [
'nullable',
],
'tags' => [
'nullable',
'array',

View File

@ -30,6 +30,9 @@ class StoreLinkRequest extends FormRequest
'is_private' => [
'nullable',
],
'is_pinned' => [
'nullable',
],
'tags' => [
'nullable',
'array',

View File

@ -37,6 +37,9 @@ class StoreStoryRequest extends FormRequest
'is_private' => [
'nullable',
],
'is_pinned' => [
'nullable',
],
'tags' => [
'nullable',
'array',

View File

@ -15,6 +15,7 @@ class ChestResource extends JsonResource
'url' => $this->permalink,
'permalink' => $this->permalink,
'is_private' => true,
'is_pinned' => $this->post->is_pinned,
'date_formated' => $this->created_at->diffForHumans(),
'tags' => $this->post->tags->pluck('name')->toArray(),
$this->mergeWhen(Auth::check(), [

View File

@ -15,6 +15,7 @@ class LinkResource extends JsonResource
'url' => $this->url,
'permalink' => $this->permalink,
'is_private' => $this->post->is_private,
'is_pinned' => $this->post->is_pinned,
'preview' => $this->preview,
'date_formated' => $this->created_at->diffForHumans(),
'tags' => $this->post->tags->pluck('name')->toArray(),

View File

@ -39,6 +39,7 @@ class PostResource extends JsonResource
'postable_id' => $this->postable->id,
'tags' => $this->tags->pluck('name')->toArray(),
'is_private' => $this->is_private,
'is_pinned' => $this->is_pinned,
'created_at' => $this->created_at,
]);
}

View File

@ -15,6 +15,7 @@ class StoryResource extends JsonResource
'content' => $this->content,
'url' => $this->url,
'is_private' => $this->post->is_private,
'is_pinned' => $this->post->is_pinned,
'date_formated' => $this->created_at->diffForHumans(),
'tags' => $this->post->tags->pluck('name')->toArray(),
$this->mergeWhen(Auth::check(), [

View File

@ -9,9 +9,11 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Laravel\Scout\Searchable;
/**
* @method Builder|Post pinnedFirst()
* @method Builder|Post withPrivate(bool|User|Request $private)
* @method Builder|Post withoutChests()
*/
@ -24,10 +26,12 @@ class Post extends Model
'postable_type',
'postable_id',
'is_private',
'is_pinned',
'user_id',
'created_at',
];
protected $casts = [
'is_pinned' => 'bool',
'is_private' => 'bool',
];
@ -51,6 +55,24 @@ class Post extends Model
return $this->created_at->diffForHumans();
}
public function setIsPinnedAttribute($value): void
{
if ($value instanceof Collection) {
$value = (bool)$value->get('is_pinned', false);
}
$this->attributes['is_pinned'] = $value;
}
public function setIsPrivateAttribute($value): void
{
if ($value instanceof Collection) {
$value = (bool)$value->get('is_private', false);
}
$this->attributes['is_private'] = $value;
}
public function scopeWithPrivate(Builder $query, $user = null): Builder
{
if ($user instanceof Request) {
@ -74,6 +96,11 @@ class Post extends Model
return $query;
}
public function scopePinnedFirst(Builder $query): Builder
{
return $query->orderByDesc('is_pinned');
}
public function scopeWithoutChests(Builder $query): Builder
{
return $query->where('postable_type', '!=', Chest::class);

View File

@ -1,3 +1,11 @@
# Unreleased
⚠️ Run migrations when updating
## Added
- Pinned posts
# 1.2.20
⚠️ Run migrations when updating

View File

@ -10,6 +10,7 @@ use Faker\Generator as Faker;
$factory->define(Post::class, function (Faker $faker) {
return [
'is_private' => 0,
'is_pinned' => 0,
'user_id' => 1,
];
});

View File

@ -0,0 +1,22 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddIsPinnedToPostsTable extends Migration
{
public function up()
{
Schema::table('posts', function (Blueprint $table) {
$table->boolean('is_pinned')->index()->default(false)->after('is_private');
});
}
public function down()
{
Schema::table('posts', function (Blueprint $table) {
$table->dropColumn('is_pinned');
});
}
}

2
public/js/app.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
{
"/js/app.js": "/js/app.js?id=e7b948d4deb0a01e7e9e",
"/js/app.js": "/js/app.js?id=a9868a4cd1f7c234776c",
"/css/app.css": "/css/app.css?id=076c4b4fab9ab74f3edc",
"/js/manifest.js": "/js/manifest.js?id=3c768977c2574a34506e",
"/js/vendor.js": "/js/vendor.js?id=24c264fe7e2a39bcb572"

View File

@ -2,6 +2,7 @@
<div class="card card--chest mb-4" :class="{'card-single': single, 'card-index': !single}">
<div class="card-body">
<h5 class="card-title">
<i class="fas fa-thumbtack fa-sm pr-1" v-if="chest.is_pinned && !single"></i>
<span>{{ __('Chest') }}</span> &mdash; <a :href="chest.permalink">{{ chest.title }}</a>
</h5>

View File

@ -12,6 +12,13 @@
<chest-lines v-model="form.content"></chest-lines>
</div>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="is_pinned" v-model="form.is_pinned" :disabled="loading">
<label class="custom-control-label" for="is_pinned" dusk="chest-form-pinned">{{ __('Is pinned?') }}</label>
</div>
</div>
<div class="form-group">
<label>{{ __('Tags') }}</label>
<tags v-model="form.tags"></tags>
@ -33,6 +40,7 @@ let defaultChest = function () {
return {
title: '',
content: [],
is_pinned: false,
tags: [],
};
};

View File

@ -2,6 +2,7 @@
<div class="card card--link mb-4" :class="{'card-single': single, 'card-index': !single}">
<div class="card-body">
<h5 class="card-title">
<i class="fas fa-thumbtack fa-sm pr-1" v-if="link.is_pinned && !single"></i>
<span>{{ __('Link') }}</span> &mdash; <a :href="link.permalink">{{ link.title }}</a><br>
<a :href="link.url" class="small text-muted">{{ displayUrl }}</a>
</h5>

View File

@ -20,10 +20,23 @@
<span class="invalid-feedback" v-if="hasFormError('content')">{{ firstFormError('content') }}</span>
</div>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="is_private" v-model="form.is_private" :disabled="loading">
<label class="custom-control-label" for="is_private" dusk="link-form-private">{{ __('Private link?') }}</label>
<div class="row">
<div class="col-12 col-md-6">
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="is_private" v-model="form.is_private" :disabled="loading">
<label class="custom-control-label" for="is_private" dusk="link-form-private">{{ __('Private link?') }}</label>
</div>
</div>
</div>
<div class="col-12 col-md-6">
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="is_pinned" v-model="form.is_pinned" :disabled="loading">
<label class="custom-control-label" for="is_pinned" dusk="link-form-pinned">{{ __('Is pinned?') }}</label>
</div>
</div>
</div>
</div>
@ -58,6 +71,7 @@ let defaultLink = function () {
title: null,
content: null,
is_private: false,
is_pinned: false,
tags: []
};
};

View File

@ -2,6 +2,7 @@
<div class="card card--story mb-4" :class="{'card-single': single, 'card-index': !single}">
<div class="card-body">
<h5 class="card-title">
<i class="fas fa-thumbtack fa-sm pr-1" v-if="story.is_pinned && !single"></i>
<span>{{ __('Story') }}</span> &mdash; <a :href="story.url">{{ story.title }}</a>
</h5>

View File

@ -17,10 +17,23 @@
></editor>
</div>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="is_private" v-model="form.is_private" :disabled="loading">
<label class="custom-control-label" for="is_private">{{ __('Private story?') }}</label>
<div class="row">
<div class="col-12 col-md-6">
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="is_private" v-model="form.is_private" :disabled="loading">
<label class="custom-control-label" for="is_private" dusk="link-form-private">{{ __('Private link?') }}</label>
</div>
</div>
</div>
<div class="col-12 col-md-6">
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="is_pinned" v-model="form.is_pinned" :disabled="loading">
<label class="custom-control-label" for="is_pinned" dusk="link-form-pinned">{{ __('Is pinned?') }}</label>
</div>
</div>
</div>
</div>
@ -47,6 +60,7 @@ let defaultStory = function () {
slug: '',
content: '',
is_private: false,
is_pinned: false,
tags: [],
};
};

View File

@ -90,6 +90,7 @@
"Reset": "Zurücksetzen",
"Generate": "Generate",
"Loading": "Laden",
"Is pinned?": "Is pinned?",
"Type / to search": "Tippe zur Suche",

View File

@ -90,6 +90,7 @@
"Reset": "Réinitialiser",
"Generate": "Générer",
"Loading": "Chargement",
"Is pinned?": "Épinglé ?",
"Type / to search": "Tapez / pour chercher",