257 lines
15 KiB
PHP
257 lines
15 KiB
PHP
@extends('layouts.nova')
|
|
|
|
@php
|
|
$discoverBreadcrumbs = collect([
|
|
(object) ['name' => 'Discover', 'url' => '/discover/trending'],
|
|
(object) ['name' => $page_title ?? 'Discover', 'url' => request()->path()],
|
|
]);
|
|
@endphp
|
|
|
|
@php
|
|
$followingActivity = collect($following_activity ?? []);
|
|
$networkTrending = collect($network_trending ?? []);
|
|
$suggestedUsers = collect($suggested_users ?? $fallback_creators ?? []);
|
|
$fallbackTrending = collect($fallback_trending ?? []);
|
|
@endphp
|
|
|
|
@section('content')
|
|
|
|
<x-nova-page-header
|
|
section="Discover"
|
|
:title="$page_title ?? 'Discover'"
|
|
:icon="$icon ?? 'fa-compass'"
|
|
:breadcrumbs="$discoverBreadcrumbs"
|
|
:description="$description ?? null"
|
|
headerClass="pb-6"
|
|
actionsClass="lg:pt-8"
|
|
>
|
|
<x-slot name="actions">
|
|
@include('web.discover._nav', ['section' => $section ?? ''])
|
|
</x-slot>
|
|
</x-nova-page-header>
|
|
|
|
@if (($section ?? null) === 'following')
|
|
<section class="px-6 pt-2 md:px-10">
|
|
@if (!empty($empty))
|
|
<div class="rounded-[32px] border border-white/10 bg-[linear-gradient(135deg,rgba(56,189,248,0.08),rgba(255,255,255,0.03),rgba(249,115,22,0.06))] p-6 shadow-[0_20px_60px_rgba(2,6,23,0.24)]">
|
|
<div class="flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between">
|
|
<div>
|
|
<p class="text-[11px] font-semibold uppercase tracking-[0.24em] text-sky-200/80">Personalized following feed</p>
|
|
<h2 class="mt-2 text-3xl font-semibold tracking-[-0.03em] text-white">Your network starts here</h2>
|
|
<p class="mt-3 max-w-2xl text-sm leading-relaxed text-slate-300">Follow a few creators to unlock a feed made of their newest art, social activity, and rising work from around your network.</p>
|
|
</div>
|
|
<div class="flex flex-wrap gap-2">
|
|
<a href="/discover/trending" class="inline-flex items-center gap-2 rounded-2xl border border-white/10 bg-white/[0.04] px-4 py-2.5 text-sm font-medium text-slate-200 transition-colors hover:bg-white/[0.08]">
|
|
<i class="fa-solid fa-fire fa-fw"></i>
|
|
Explore trending
|
|
</a>
|
|
<a href="/feed/following" class="inline-flex items-center gap-2 rounded-2xl border border-sky-400/20 bg-sky-500/10 px-4 py-2.5 text-sm font-medium text-sky-200 transition-colors hover:bg-sky-500/15">
|
|
<i class="fa-solid fa-newspaper fa-fw"></i>
|
|
Open post feed
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<div class="mt-6 grid gap-6 xl:grid-cols-[minmax(0,1.6fr)_minmax(0,1fr)]">
|
|
<div class="rounded-[28px] border border-white/10 bg-white/[0.03] p-5 shadow-[0_20px_50px_rgba(2,6,23,0.18)]">
|
|
<div class="mb-4 flex items-center justify-between gap-4">
|
|
<div>
|
|
<p class="text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-500">Activity from people you follow</p>
|
|
<h3 class="mt-1 text-xl font-semibold text-white">Network activity</h3>
|
|
</div>
|
|
<a href="/community/activity?filter=following" class="text-sm text-sky-300/80 transition-colors hover:text-sky-200">View all</a>
|
|
</div>
|
|
|
|
<div class="space-y-3">
|
|
@forelse ($followingActivity as $activity)
|
|
<div class="rounded-2xl border border-white/[0.06] bg-white/[0.02] px-4 py-3">
|
|
<div class="flex items-start gap-3">
|
|
<img src="{{ data_get($activity, 'user.avatar_url') ?: '/images/avatar_default.webp' }}" alt="{{ data_get($activity, 'user.username') }}" class="h-10 w-10 rounded-full object-cover ring-1 ring-white/10" loading="lazy" />
|
|
<div class="min-w-0 flex-1">
|
|
<p class="text-sm font-semibold text-white">{{ data_get($activity, 'user.username') ? '@' . data_get($activity, 'user.username') : data_get($activity, 'user.name', 'Creator') }}</p>
|
|
<p class="mt-1 text-sm text-slate-300">
|
|
@if (data_get($activity, 'type') === 'follow')
|
|
started following {{ data_get($activity, 'target_user.username') ? '@' . data_get($activity, 'target_user.username') : 'another creator' }}
|
|
@elseif (data_get($activity, 'type') === 'upload')
|
|
published <a href="{{ data_get($activity, 'artwork.url') }}" class="text-sky-300 hover:text-sky-200">{{ data_get($activity, 'artwork.title', 'a new artwork') }}</a>
|
|
@elseif (in_array(data_get($activity, 'type'), ['comment', 'reply'], true))
|
|
{{ data_get($activity, 'type') === 'reply' ? 'replied on' : 'commented on' }} <a href="{{ data_get($activity, 'artwork.url') }}" class="text-sky-300 hover:text-sky-200">{{ data_get($activity, 'artwork.title', 'an artwork') }}</a>
|
|
@else
|
|
{{ ucfirst(str_replace('_', ' ', (string) data_get($activity, 'type', 'activity'))) }}
|
|
@endif
|
|
</p>
|
|
<p class="mt-1 text-xs text-slate-500">{{ data_get($activity, 'time_ago') ?: data_get($activity, 'created_at') }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@empty
|
|
<div class="rounded-2xl border border-dashed border-white/10 bg-white/[0.02] px-4 py-8 text-center text-sm text-slate-400">Follow activity will appear here as your network starts moving.</div>
|
|
@endforelse
|
|
</div>
|
|
</div>
|
|
|
|
<div class="space-y-6">
|
|
<div class="rounded-[28px] border border-white/10 bg-white/[0.03] p-5 shadow-[0_20px_50px_rgba(2,6,23,0.18)]">
|
|
<div class="mb-4">
|
|
<p class="text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-500">Trending in your network</p>
|
|
<h3 class="mt-1 text-xl font-semibold text-white">Network highlights</h3>
|
|
</div>
|
|
|
|
<div class="space-y-3">
|
|
@foreach (($empty ?? false) ? $fallbackTrending->take(4) : $networkTrending->take(4) as $item)
|
|
@php
|
|
$itemId = (int) data_get($item, 'id', 0);
|
|
$itemSlug = (string) data_get($item, 'slug', '');
|
|
$itemUrl = $itemSlug !== '' && $itemId > 0
|
|
? route('art.show', ['id' => $itemId, 'slug' => $itemSlug])
|
|
: data_get($item, 'url', '#');
|
|
$itemThumb = data_get($item, 'thumb_url') ?: data_get($item, 'thumb') ?: data_get($item, 'thumbnail_url') ?: '/images/placeholder.jpg';
|
|
$itemTitle = data_get($item, 'title') ?: data_get($item, 'name', 'Artwork');
|
|
@endphp
|
|
<a href="{{ $itemUrl }}" class="flex items-center gap-3 rounded-2xl border border-white/[0.06] bg-white/[0.02] p-3 transition-colors hover:bg-white/[0.04]">
|
|
<img src="{{ $itemThumb }}" alt="{{ $itemTitle }}" class="h-16 w-16 rounded-xl object-cover" loading="lazy" />
|
|
<div class="min-w-0 flex-1">
|
|
<p class="truncate text-sm font-semibold text-white">{{ $itemTitle ?: 'Untitled artwork' }}</p>
|
|
<p class="truncate text-xs text-slate-400">{{ data_get($item, 'author.username') ? '@' . data_get($item, 'author.username') : data_get($item, 'username', data_get($item, 'uname')) }}</p>
|
|
@if (is_array($item) && isset($item['stats']))
|
|
<p class="mt-1 text-[11px] text-slate-500">{{ number_format((int) data_get($item, 'stats.favorites', 0)) }} favourites · {{ number_format((int) data_get($item, 'stats.views', 0)) }} views</p>
|
|
@endif
|
|
</div>
|
|
</a>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
|
|
<div class="rounded-[28px] border border-white/10 bg-white/[0.03] p-5 shadow-[0_20px_50px_rgba(2,6,23,0.18)]">
|
|
<div class="mb-4">
|
|
<p class="text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-500">Suggested creators</p>
|
|
<h3 class="mt-1 text-xl font-semibold text-white">Who to follow next</h3>
|
|
</div>
|
|
|
|
<div class="space-y-3">
|
|
@foreach ($suggestedUsers->take(4) as $userCard)
|
|
<a href="{{ $userCard['profile_url'] ?? ('/@' . strtolower((string) ($userCard['username'] ?? ''))) }}" class="flex items-start gap-3 rounded-2xl border border-white/[0.06] bg-white/[0.02] p-3 transition-colors hover:bg-white/[0.04]">
|
|
<img src="{{ $userCard['avatar_url'] ?? '/images/avatar_default.webp' }}" alt="{{ $userCard['username'] ?? 'creator' }}" class="h-10 w-10 rounded-full object-cover ring-1 ring-white/10" loading="lazy" />
|
|
<div class="min-w-0 flex-1">
|
|
<p class="truncate text-sm font-semibold text-white">{{ $userCard['name'] ?? $userCard['username'] ?? 'Creator' }}</p>
|
|
<p class="truncate text-xs text-slate-500">@{{ $userCard['username'] ?? 'creator' }}</p>
|
|
<p class="mt-1 text-xs text-slate-400">{{ data_get($userCard, 'context.follower_overlap.label') ?: data_get($userCard, 'context.shared_following.label') ?: ($userCard['reason'] ?? 'Recommended for you') }}</p>
|
|
</div>
|
|
</a>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
@endif
|
|
|
|
{{-- ── Artwork grid (React MasonryGallery) ── --}}
|
|
@php
|
|
$galleryItems = method_exists($artworks, 'items') ? $artworks->items() : (is_iterable($artworks) ? $artworks : []);
|
|
$galleryArtworks = collect($galleryItems)->map(fn ($art) => [
|
|
'id' => $art->id,
|
|
'name' => $art->name ?? null,
|
|
'thumb' => $art->thumb_url ?? $art->thumb ?? null,
|
|
'thumb_srcset' => $art->thumb_srcset ?? null,
|
|
'uname' => $art->uname ?? '',
|
|
'username' => $art->uname ?? '',
|
|
'avatar_url' => $art->avatar_url ?? null,
|
|
'category_name' => $art->category_name ?? '',
|
|
'category_slug' => $art->category_slug ?? '',
|
|
'slug' => $art->slug ?? '',
|
|
'width' => $art->width ?? null,
|
|
'height' => $art->height ?? null,
|
|
])->values();
|
|
$galleryNextPageUrl = method_exists($artworks, 'nextPageUrl') ? $artworks->nextPageUrl() : null;
|
|
@endphp
|
|
<section class="px-6 pt-8 md:px-10">
|
|
<div
|
|
data-react-masonry-gallery
|
|
data-artworks="{{ json_encode($galleryArtworks) }}"
|
|
data-gallery-type="{{ $section ?? 'discover' }}"
|
|
@if ($galleryNextPageUrl) data-next-page-url="{{ $galleryNextPageUrl }}" @endif
|
|
data-limit="24"
|
|
class="min-h-32"
|
|
></div>
|
|
</section>
|
|
|
|
@endsection
|
|
|
|
@push('styles')
|
|
<style>
|
|
[data-nova-gallery].is-enhanced [data-gallery-grid] {
|
|
display: grid;
|
|
grid-template-columns: repeat(1, minmax(0, 1fr));
|
|
grid-auto-rows: 8px;
|
|
gap: 1rem;
|
|
}
|
|
@media (min-width: 768px) {
|
|
[data-nova-gallery].is-enhanced [data-gallery-grid] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
}
|
|
@media (min-width: 1024px) {
|
|
[data-nova-gallery] [data-gallery-grid] { grid-template-columns: repeat(5, minmax(0, 1fr)); }
|
|
[data-nova-gallery].is-enhanced [data-gallery-grid] { grid-template-columns: repeat(5, minmax(0, 1fr)); }
|
|
[data-gallery-grid].force-5 { grid-template-columns: repeat(5, minmax(0, 1fr)) !important; }
|
|
}
|
|
@media (min-width: 1600px) {
|
|
[data-nova-gallery] [data-gallery-grid] { grid-template-columns: repeat(6, minmax(0, 1fr)); }
|
|
[data-nova-gallery].is-enhanced [data-gallery-grid] { grid-template-columns: repeat(6, minmax(0, 1fr)); }
|
|
[data-gallery-grid].force-5 { grid-template-columns: repeat(6, minmax(0, 1fr)) !important; }
|
|
}
|
|
@media (min-width: 2600px) {
|
|
[data-nova-gallery] [data-gallery-grid] { grid-template-columns: repeat(7, minmax(0, 1fr)); }
|
|
[data-nova-gallery].is-enhanced [data-gallery-grid] { grid-template-columns: repeat(7, minmax(0, 1fr)); }
|
|
[data-gallery-grid].force-5 { grid-template-columns: repeat(7, minmax(0, 1fr)) !important; }
|
|
}
|
|
[data-nova-gallery].is-enhanced [data-gallery-grid] > .nova-card { margin: 0 !important; }
|
|
[data-nova-gallery].is-enhanced [data-gallery-pagination] {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
margin-top: 1.5rem;
|
|
}
|
|
[data-nova-gallery].is-enhanced [data-gallery-pagination] ul {
|
|
display: inline-flex;
|
|
gap: 0.25rem;
|
|
align-items: center;
|
|
padding: 0;
|
|
margin: 0;
|
|
list-style: none;
|
|
}
|
|
[data-nova-gallery].is-enhanced [data-gallery-pagination] li a,
|
|
[data-nova-gallery].is-enhanced [data-gallery-pagination] li span {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-width: 2.25rem;
|
|
height: 2.25rem;
|
|
border-radius: 0.5rem;
|
|
padding: 0 0.5rem;
|
|
background: rgba(255,255,255,0.03);
|
|
color: #e6eef8;
|
|
border: 1px solid rgba(255,255,255,0.04);
|
|
text-decoration: none;
|
|
font-size: 0.875rem;
|
|
}
|
|
[data-gallery-skeleton].is-loading { display: grid !important; grid-template-columns: inherit; gap: 1rem; }
|
|
.nova-skeleton-card {
|
|
border-radius: 1rem;
|
|
min-height: 180px;
|
|
background: linear-gradient(110deg, rgba(255,255,255,.06) 8%, rgba(255,255,255,.12) 18%, rgba(255,255,255,.06) 33%);
|
|
background-size: 200% 100%;
|
|
animation: novaShimmer 1.2s linear infinite;
|
|
}
|
|
@keyframes novaShimmer {
|
|
to { background-position-x: -200%; }
|
|
}
|
|
</style>
|
|
@endpush
|
|
|
|
@push('scripts')
|
|
@vite('resources/js/entry-masonry-gallery.jsx')
|
|
@endpush
|