diff --git a/app/Http/Controllers/Articles/ArticlesController.php b/app/Http/Controllers/Articles/ArticlesController.php index 579c8414f..3f6d99107 100644 --- a/app/Http/Controllers/Articles/ArticlesController.php +++ b/app/Http/Controllers/Articles/ArticlesController.php @@ -24,6 +24,7 @@ public function __construct() public function index() { + return view('articles.index'); } public function show(Article $article) diff --git a/app/Http/Livewire/ShowArticles.php b/app/Http/Livewire/ShowArticles.php new file mode 100644 index 000000000..1b24dfb74 --- /dev/null +++ b/app/Http/Livewire/ShowArticles.php @@ -0,0 +1,70 @@ + ['except' => ''], + 'sortBy' => ['except' => 'recent'], + ]; + + public function mount(): void + { + $this->toggleTag(request()->query('tag', $this->tag)); + $this->sortBy(request()->query('sortBy') ?: $this->sortBy); + } + + public function render(): View + { + $articles = Article::published(); + + if ($this->tag) { + $articles->forTag($this->tag); + } + + $articles->{$this->sortBy}(); + + return view('livewire.show-articles', [ + 'articles' => $articles->paginate(10), + 'selectedTag' => $this->tag, + 'selectedSortBy' => $this->sortBy, + ]); + } + + public function toggleTag($tag): void + { + $this->tag = $this->tag !== $tag && $this->tagExists($tag) ? $tag : null; + } + + public function sortBy($sort): void + { + $this->sortBy = $this->validSort($sort) ? $sort : 'recent'; + } + + public function tagExists($tag): bool + { + return Tag::where('slug', $tag)->exists(); + } + + public function validSort($sort): bool + { + return in_array($sort, [ + 'recent', + 'popular', + 'trending', + ]); + } +} diff --git a/app/Models/Article.php b/app/Models/Article.php index faa9f339e..091ab6608 100644 --- a/app/Models/Article.php +++ b/app/Models/Article.php @@ -10,6 +10,7 @@ use Carbon\Carbon; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Str; final class Article extends Model { @@ -48,6 +49,11 @@ public function body(): string return $this->body; } + public function excerpt(int $limit = 100): string + { + return Str::limit(strip_tags(md_to_html($this->body())), $limit); + } + public function originalUrl(): ?string { return $this->original_url; @@ -103,6 +109,13 @@ public function isNotPublished(): bool return is_null($this->published_at); } + public function readTime() + { + $minutes = round(str_word_count($this->body()) / 200); + + return $minutes == 0 ? 1 : $minutes; + } + public function scopePublished(Builder $query): Builder { return $query->whereNotNull('published_at'); @@ -112,4 +125,32 @@ public function scopeNotPublished(Builder $query): Builder { return $query->whereNull('published_at'); } + + public function scopeForTag(Builder $query, string $tag): Builder + { + return $query->whereHas('tagsRelation', function ($query) use ($tag) { + $query->where('tags.slug', $tag); + }); + } + + public function scopeRecent(Builder $query): Builder + { + return $query->orderBy('published_at', 'desc'); + } + + public function scopePopular(Builder $query): Builder + { + return $query->withCount('likes') + ->orderBy('likes_count', 'desc') + ->orderBy('published_at', 'desc'); + } + + public function scopeTrending(Builder $query): Builder + { + return $query->withCount(['likes' => function ($query) { + $query->where('created_at', '>=', now()->subWeek()); + }]) + ->orderBy('likes_count', 'desc') + ->orderBy('published_at', 'desc'); + } } diff --git a/app/Models/Tag.php b/app/Models/Tag.php index 37f683165..19506cd08 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -5,6 +5,7 @@ use App\Helpers\HasSlug; use App\Helpers\ModelHelpers; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\MorphToMany; final class Tag extends Model { @@ -30,4 +31,14 @@ public function name(): string { return $this->name; } + + public function slug(): string + { + return $this->slug; + } + + public function articles(): MorphToMany + { + return $this->morphedByMany(Article::class, 'taggable'); + } } diff --git a/resources/css/tags.css b/resources/css/tags.css index 2c49d479e..7af929f9b 100644 --- a/resources/css/tags.css +++ b/resources/css/tags.css @@ -6,6 +6,14 @@ ul.tags li.active { @apply bg-gray-100 rounded font-bold; } +ul.tags li.active button { + @apply font-bold; +} + +ul.tags li button:focus { + @apply outline-none; +} + .thread-info-tags a:hover { @apply no-underline; } \ No newline at end of file diff --git a/resources/views/articles/index.blade.php b/resources/views/articles/index.blade.php index c5933b6c2..1da12f003 100644 --- a/resources/views/articles/index.blade.php +++ b/resources/views/articles/index.blade.php @@ -1,24 +1,7 @@ +@title('Community Articles') + @extends('layouts.default') @section('content') -
+ {{ $article->excerpt() }} +
+ + ++ + {{ $article->author()->name() }} + +
+