Update (view): redesign

This commit is contained in:
2023-10-05 00:29:23 +08:00
parent f1f02bb7b6
commit 7374d66a21
14 changed files with 104 additions and 49 deletions

View File

@@ -1,5 +1,6 @@
<?php <?php
use GrahamCampbell\Markdown\Facades\Markdown;
use Illuminate\Support\Str; use Illuminate\Support\Str;
if (! function_exists('epoch_now_timestamp')) { if (! function_exists('epoch_now_timestamp')) {
@@ -9,6 +10,27 @@ function epoch_now_timestamp()
} }
} }
if (! function_exists('read_duration')) {
function read_duration($text)
{
return Str::readDuration($text);
}
}
if (! function_exists('plain_text')) {
function plain_text($content)
{
return trim(preg_replace('/\s\s+/', ' ', strip_tags($content)));
}
}
if (! function_exists('markdown_min_read')) {
function markdown_min_read($markdown)
{
return read_duration(plain_text(Markdown::convert($markdown)->getContent()));
}
}
if (! function_exists('unslug')) { if (! function_exists('unslug')) {
function unslug($slug, $delimiter = '-') function unslug($slug, $delimiter = '-')
{ {

View File

@@ -22,7 +22,7 @@ public function redirect(Request $request, $slug)
return abort(404); return abort(404);
} }
return redirect()->route('front.post',['category_slug' => $post->category->slug, 'slug' => $post->slug]); return redirect()->route('front.post', ['category_slug' => $post->category->slug, 'slug' => $post->slug]);
} }
@@ -104,7 +104,7 @@ public function index(Request $request, $category_slug, $slug)
->addValue('dateCreated', $post->created_at->format('Y-m-d')) ->addValue('dateCreated', $post->created_at->format('Y-m-d'))
->addValue('dateModified', $post->updated_at->format('Y-m-d')) ->addValue('dateModified', $post->updated_at->format('Y-m-d'))
->addValue('description', $post_description) ->addValue('description', $post_description)
->addValue('articleBody', trim(preg_replace('/\s\s+/', ' ', strip_tags($content)))); ->addValue('articleBody', plain_text($content));
// breadcrumb json ld // breadcrumb json ld
$listItems = []; $listItems = [];
@@ -161,7 +161,7 @@ private function injectBootstrapClasses($content)
if (strpos($pNode->text(), 'Q:') === 0) { if (strpos($pNode->text(), 'Q:') === 0) {
$currentClasses = $pNode->attr('class'); $currentClasses = $pNode->attr('class');
$newClasses = trim($currentClasses.' fw-bold'); $newClasses = trim($currentClasses.' ');
$pNode->getNode(0)->setAttribute('class', $newClasses); $pNode->getNode(0)->setAttribute('class', $newClasses);
} }
}); });
@@ -234,7 +234,7 @@ private function injectPublishDateAndAuthor($post, $content)
$authorName = $post->author->name; $authorName = $post->author->name;
// Create the HTML structure for publish date and author // Create the HTML structure for publish date and author
$publishInfo = "<div class=\"mb-4\"><span class=\"text-secondary small\">Published on {$publishedAtFormatted} by {$authorName}</span></div>"; $publishInfo = "<div class=\"mb-4\"><span class=\"text-secondary small\">Published on {$publishedAtFormatted} by {$authorName}<i class=\"bi bi-dot\"></i>" . markdown_min_read($post->body) . "</span></div>";
// Inject the publish date and author information after the `h1` tag // Inject the publish date and author information after the `h1` tag
$content = preg_replace('/(<\/h1>)/', '$1'.$publishInfo, $content, 1); $content = preg_replace('/(<\/h1>)/', '$1'.$publishInfo, $content, 1);

View File

@@ -42,18 +42,14 @@ public static function handle(SerpUrl $serp_url)
$count = 0; $count = 0;
while ((!isset($ai_suggestion?->article_type)) || (!isset($ai_suggestion?->short_title)) || (!isset($ai_suggestion?->main_keyword)) || (!isset($ai_suggestion?->photo_keywords)) || (!isset($ai_suggestion?->description)) || (!isset($ai_suggestion?->title))) while ((! isset($ai_suggestion?->article_type)) || (! isset($ai_suggestion?->short_title)) || (! isset($ai_suggestion?->main_keyword)) || (! isset($ai_suggestion?->photo_keywords)) || (! isset($ai_suggestion?->description)) || (! isset($ai_suggestion?->title))) {
{ if ($count >= 3) {
if ($count >= 3) Log::error(serialize($ai_suggestion));
{ throw new Exception('Failed to generate ai_suggestion');
Log::error(serialize($ai_suggestion)); } else {
throw new Exception('Failed to generate ai_suggestion'); $count++;
} $ai_suggestion = OpenAI::createNewArticleTitle($serp_url->title, $serp_url->description);
else }
{
$count++;
$ai_suggestion = OpenAI::createNewArticleTitle($serp_url->title, $serp_url->description);
}
} }
$readability_content = ScrapeUrlBodyTask::handle($serp_url->url); $readability_content = ScrapeUrlBodyTask::handle($serp_url->url);

View File

@@ -3,6 +3,7 @@
namespace App\Providers; namespace App\Providers;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;
class AppServiceProvider extends ServiceProvider class AppServiceProvider extends ServiceProvider
{ {
@@ -19,6 +20,11 @@ public function register(): void
*/ */
public function boot(): void public function boot(): void
{ {
// Str::macro('readDuration', function (...$text) {
$totalWords = str_word_count(implode(' ', $text));
$minutesToRead = round($totalWords / 200);
return (int) max(1, $minutesToRead).' min read';
});
} }
} }

View File

@@ -1,15 +1,12 @@
.nav-scroller { .nav-scroller {
position: relative; position: relative;
z-index: 2; z-index: 2;
height: 2.75rem;
overflow-y: hidden; overflow-y: hidden;
} }
.nav-scroller .nav { .nav-scroller .nav {
display: flex; display: flex;
flex-wrap: nowrap; flex-wrap: nowrap;
padding-bottom: 1rem;
margin-top: -1px;
overflow-x: auto; overflow-x: auto;
text-align: center; text-align: center;
white-space: nowrap; white-space: nowrap;
@@ -35,3 +32,11 @@ img.lqip-blur {
} }
/* lqip end */ /* lqip end */
a.link-body-emphasis {
text-decoration: none;
&:hover {
text-decoration: none;
}
}

View File

@@ -4,6 +4,7 @@
// consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs. // consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs.
// Color system // Color system
$body-bg: #f0f2f5;
// scss-docs-start gray-color-variables // scss-docs-start gray-color-variables
$white: #fff; $white: #fff;

View File

@@ -1,4 +1,4 @@
<div class="container"> <div class="w-full shadow-sm bg-white">
<header class="border-bottom lh-1 py-3"> <header class="border-bottom lh-1 py-3">
<div class="row flex-nowrap justify-content-center align-items-center"> <div class="row flex-nowrap justify-content-center align-items-center">
@@ -10,8 +10,9 @@
</div> </div>
</header> </header>
<div class="nav-scroller py-1 mb-3 border-bottom"> <div class="nav-scroller py-2 mb-3 border-bottom">
<nav class="nav nav-underline justify-content-between">
<nav class="container nav nav-underline justify-content-between">
@foreach ($parent_categories as $category) @foreach ($parent_categories as $category)
<a class="fw-bold nav-item nav-link link-body-emphasis {{ active(route('front.category', ['category_slug' => $category->slug])) }}" <a class="fw-bold nav-item nav-link link-body-emphasis {{ active(route('front.category', ['category_slug' => $category->slug])) }}"
href="{{ route('front.category', ['category_slug' => $category->slug]) }}">{{ $category->short_name }}</a> href="{{ route('front.category', ['category_slug' => $category->slug]) }}">{{ $category->short_name }}</a>

View File

@@ -1,7 +1,6 @@
<div class="p-4 mb-3 bg-body-tertiary rounded"> <div class="p-4 mb-3 bg-white rounded-3 shadow-sm">
<h4 class="fst-italic">About EchoScoop</h4> <h4 class="fst-italic">About EchoScoop</h4>
<p class="mb-0"> <p class="mb-0">
EchoScoop is a streamlined news platform delivering concise global updates. Our goal is to keep you promptly EchoScoop is a streamlined news platform delivering concise global updates. We keep our news to be bite-sized within 1 to 5 minutes read. Our goal is to keep you promptly informed with each scoop.
informed with each scoop.
</p> </p>
</div> </div>

View File

@@ -1,18 +1,29 @@
<div class="row g-0 border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative"> <div class="row g-0 border-0 rounded-3 overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative bg-white">
<div class="col p-4 d-flex flex-column position-static"> <div class="col p-4 d-flex flex-column position-static">
@if((isset($category)) && ($category->id != $post->category->id)) @if (isset($category) && $category->id != $post->category->id)
<span class="d-inline-block mb-2 text-success-emphasis"><i>Under</i> <strong class="ms-1">{{ $category->name }} <i class="bi bi-chevron-right small"></i> {{ $post->category->name }}</strong></span> <span class="d-inline-block mb-2 text-success-emphasis"><i>Under</i> <strong
class="ms-1">{{ $category->name }} <i class="bi bi-chevron-right small"></i>
{{ $post->category->name }}</strong></span>
@else @else
<span class="d-inline-block mb-2 text-success-emphasis"><i>Under</i> <strong class="ms-1">{{ $post->category->name }}</strong></span> <span class="d-inline-block mb-2 text-success-emphasis"><i>Under</i> <strong
@endif class="ms-1">{{ $post->category->name }}</strong></span>
<h3 class="mb-0 h4">{{ $post->title }}</h3>
@if (!is_empty($post->published_at))
<div class="mb-1 text-body-secondary">{{ $post->published_at->format('M j') }}</div>
@endif @endif
<h3 class="mb-1 h4">{{ $post->title }}</h3>
<div class="mb-3 text-body-secondary">
@if (!is_empty($post->published_at))
{{ $post->published_at->format('M j') }}
@endif
<i class="bi bi-dot"></i>
{{ markdown_min_read($post->body) }}
</div>
<p class="mb-3">{{ $post->excerpt }}</p> <p class="mb-3">{{ $post->excerpt }}</p>
<a href="{{ route('front.post', ['slug' => $post->slug, 'category_slug' => $post->category->slug]) }}" <a href="{{ route('front.post', ['slug' => $post->slug, 'category_slug' => $post->category->slug]) }}"
class="icon-link gap-1 icon-link-hover stretched-link"> class="icon-link gap-1 icon-link-hover stretched-link">
Continue reading Continue reading...
<svg class="bi"> <svg class="bi">
<use xlink:href="#chevron-right"></use> <use xlink:href="#chevron-right"></use>
</svg> </svg>

View File

@@ -4,7 +4,7 @@
@include('front.partials.breadcrumbs') @include('front.partials.breadcrumbs')
<div class="row g-5"> <div class="row g-4">
<div class="col-md-8"> <div class="col-md-8">
<h1 class="pb-2 fw-normal h2"> <h1 class="pb-2 fw-normal h2">
@if (isset($category) && !is_null($category)) @if (isset($category) && !is_null($category))

View File

@@ -4,10 +4,11 @@
@include('front.partials.breadcrumbs') @include('front.partials.breadcrumbs')
<div class="row g-5"> <div class="row g-4">
<div class="col-md-8"> <div class="col-md-8">
<article class="blog-post"> <article class="blog-post bg-white rounded-3 shadow-sm px-5 py-4
">
{!! $content !!} {!! $content !!}
</article> </article>

View File

@@ -2,18 +2,32 @@
@section('content') @section('content')
<main class="container"> <main class="container">
@if (!is_null($featured_post)) @if (!is_null($featured_post))
<div class="p-4 p-md-5 mb-4 rounded text-body-emphasis bg-body-secondary"> <div class="p-4 p-md-5 mb-4 rounded-3 text-body-emphasis bg-white shadow-sm">
<div class="col-lg-12 px-0"> <div class="col-lg-12 px-0">
<h1 class="display-4 fst-italic">{{ $featured_post->title }}</h1> <h1 class="display-6 fw-semibold mb-1">{{ $featured_post->title }}</h1>
<p class="lead my-3">{{ $featured_post->excerpt }}</p>
<p class="lead mb-0"><a href="{{ route('front.post', ['slug' => $featured_post->slug, 'category_slug' => $featured_post->category->slug]) }}" <div class="mb-1 text-body-secondary">
@if (!is_empty($featured_post->published_at))
{{ $featured_post->published_at->format('M j') }}
@endif
<i class="bi bi-dot"></i>
{{ markdown_min_read($featured_post->body) }}
</div>
<p class="lead my-3 mb-3">{{ $featured_post->excerpt }}</p>
<p class="lead mb-0"><a
href="{{ route('front.post', ['slug' => $featured_post->slug, 'category_slug' => $featured_post->category->slug]) }}"
class=" fw-bold">Continue reading...</a></p> class=" fw-bold">Continue reading...</a></p>
</div> </div>
</div> </div>
@endif @endif
<div class="row g-5"> <div class="row g-4">
<div class="col-md-8"> <div class="col-md-8">
@if ($latest_posts->count() > 0) @if ($latest_posts->count() > 0)
@@ -22,7 +36,7 @@ class=" fw-bold">Continue reading...</a></p>
@endforeach @endforeach
<div class="w-100 d-flex justify-content-center"> <div class="w-100 d-flex justify-content-center">
<a class="btn btn-outline-primary rounded-pill px-3 text-decoration-none" <a class="btn btn-primary shadow rounded-pill px-3 text-decoration-none"
href="{{ route('front.all') }}">View Latest News</a> href="{{ route('front.all') }}">View Latest News</a>
</div> </div>
@else @else

View File

@@ -4,11 +4,11 @@
@if ($paginator->onFirstPage()) @if ($paginator->onFirstPage())
<div class="mx-1 page-item disabled" aria-disabled="true"> <div class="mx-1 page-item disabled" aria-disabled="true">
<span <span
class="text-decoration-none btn btn-light disabled border border-primary rounded-pill px-3">{!! __('pagination.previous') !!}</span> class="shadow-sm text-decoration-none btn btn-light disabled border border-light rounded-pill px-3">{!! __('pagination.previous') !!}</span>
</div> </div>
@else @else
<div class="mx-1 page-item"> <div class="mx-1 page-item">
<a class="text-decoration-none btn btn-outline-primary rounded-pill px-3" <a class="shadow-sm text-decoration-none btn btn-primary rounded-pill px-3"
href="{{ $paginator->previousPageUrl() }}" rel="prev"> href="{{ $paginator->previousPageUrl() }}" rel="prev">
{!! __('pagination.previous') !!} {!! __('pagination.previous') !!}
</a> </a>
@@ -18,13 +18,13 @@ class="text-decoration-none btn btn-light disabled border border-primary rounded
{{-- Next Page Link --}} {{-- Next Page Link --}}
@if ($paginator->hasMorePages()) @if ($paginator->hasMorePages())
<div class="mx-1 page-item"> <div class="mx-1 page-item">
<a class="text-decoration-none btn btn-outline-primary rounded-pill px-3" <a class="shadow-sm text-decoration-none btn btn-primary rounded-pill px-3"
href="{{ $paginator->nextPageUrl() }}" rel="next">{!! __('pagination.next') !!}</a> href="{{ $paginator->nextPageUrl() }}" rel="next">{!! __('pagination.next') !!}</a>
</div> </div>
@else @else
<div class="mx-1 page-item disabled" aria-disabled="true"> <div class="mx-1 page-item disabled" aria-disabled="true">
<span <span
class="text-decoration-none btn btn-light disabled border border-primary rounded-pill px-3">{!! __('pagination.next') !!}</span> class="shadow-sm text-decoration-none btn btn-light disabled border border-light rounded-pill px-3">{!! __('pagination.next') !!}</span>
</div> </div>
@endif @endif
</nav> </nav>

View File

@@ -29,7 +29,6 @@
Route::get('/{category_slug}/{slug}', [App\Http\Controllers\Front\FrontPostController::class, 'index'])->name('front.post'); Route::get('/{category_slug}/{slug}', [App\Http\Controllers\Front\FrontPostController::class, 'index'])->name('front.post');
Route::get('/{category_slug}', [App\Http\Controllers\Front\FrontListController::class, 'category']) Route::get('/{category_slug}', [App\Http\Controllers\Front\FrontListController::class, 'category'])
->where('category_slug', '^(automotive|business|trading|information-technology|marketing|office|telecommunications|food-drink|collectibles|pets|photography|hobbies-gifts|hunting-fishing|law|politics|home-garden|shopping|fashion-clothing|real-estate|family|wedding|immigration|society|education|languages|health|beauty|psychology|wellness|religion-spirituality|tips-tricks|how-to|holiday|world-festivals|travel|outdoors|computer|phones|gadgets|technology|social-networks|ai)$') ->where('category_slug', '^(automotive|business|trading|information-technology|marketing|office|telecommunications|food-drink|collectibles|pets|photography|hobbies-gifts|hunting-fishing|law|politics|home-garden|shopping|fashion-clothing|real-estate|family|wedding|immigration|society|education|languages|health|beauty|psychology|wellness|religion-spirituality|tips-tricks|how-to|holiday|world-festivals|travel|outdoors|computer|phones|gadgets|technology|social-networks|ai)$')
->name('front.category'); ->name('front.category');