Save workspace changes
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\EarlyGrowth;
|
||||
|
||||
use App\Models\Artwork;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* ActivityLayer (§8 — Optional)
|
||||
*
|
||||
* Surfaces real site-activity signals as human-readable summaries.
|
||||
* All data is genuine — no fabrication, no fake counts.
|
||||
*
|
||||
* Examples:
|
||||
* "🔥 Trending this week: 24 artworks"
|
||||
* "📈 Rising in Wallpapers"
|
||||
* "🌟 5 new creators joined this month"
|
||||
* "🎨 38 artworks published recently"
|
||||
*
|
||||
* Only active when EarlyGrowth::activityLayerEnabled() returns true.
|
||||
*/
|
||||
final class ActivityLayer
|
||||
{
|
||||
/**
|
||||
* Return an array of activity signal strings for use in UI badges/widgets.
|
||||
* Empty array when ActivityLayer is disabled.
|
||||
*
|
||||
* @return array<int, array{icon: string, text: string, type: string}>
|
||||
*/
|
||||
public function getSignals(): array
|
||||
{
|
||||
if (! EarlyGrowth::activityLayerEnabled()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$ttl = (int) config('early_growth.cache_ttl.activity', 1800);
|
||||
|
||||
return Cache::remember('egs.activity_signals', $ttl, fn (): array => $this->buildSignals());
|
||||
}
|
||||
|
||||
// ─── Signal builders ─────────────────────────────────────────────────────
|
||||
|
||||
private function buildSignals(): array
|
||||
{
|
||||
$signals = [];
|
||||
|
||||
// §8: "X artworks published recently"
|
||||
$recentCount = $this->recentArtworkCount(7);
|
||||
if ($recentCount > 0) {
|
||||
$signals[] = [
|
||||
'icon' => '🎨',
|
||||
'text' => "{$recentCount} artwork" . ($recentCount !== 1 ? 's' : '') . ' published this week',
|
||||
'type' => 'uploads',
|
||||
];
|
||||
}
|
||||
|
||||
// §8: "X new creators joined this month"
|
||||
$newCreators = $this->newCreatorsThisMonth();
|
||||
if ($newCreators > 0) {
|
||||
$signals[] = [
|
||||
'icon' => '🌟',
|
||||
'text' => "{$newCreators} new creator" . ($newCreators !== 1 ? 's' : '') . ' joined this month',
|
||||
'type' => 'creators',
|
||||
];
|
||||
}
|
||||
|
||||
// §8: "Trending this week"
|
||||
$trendingCount = $this->recentArtworkCount(7);
|
||||
if ($trendingCount > 0) {
|
||||
$signals[] = [
|
||||
'icon' => '🔥',
|
||||
'text' => 'Trending this week',
|
||||
'type' => 'trending',
|
||||
];
|
||||
}
|
||||
|
||||
// §8: "Rising in Wallpapers" (first content type with recent uploads)
|
||||
$risingType = $this->getRisingContentType();
|
||||
if ($risingType !== null) {
|
||||
$signals[] = [
|
||||
'icon' => '📈',
|
||||
'text' => "Rising in {$risingType}",
|
||||
'type' => 'rising',
|
||||
];
|
||||
}
|
||||
|
||||
return array_values($signals);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count approved public artworks published in the last N days.
|
||||
*/
|
||||
private function recentArtworkCount(int $days): int
|
||||
{
|
||||
try {
|
||||
return Artwork::query()
|
||||
->where('is_public', true)
|
||||
->where('is_approved', true)
|
||||
->whereNull('deleted_at')
|
||||
->where('published_at', '>=', now()->subDays($days))
|
||||
->count();
|
||||
} catch (\Throwable) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Count users who registered (email_verified_at set) this calendar month.
|
||||
*/
|
||||
private function newCreatorsThisMonth(): int
|
||||
{
|
||||
try {
|
||||
return User::query()
|
||||
->whereNotNull('email_verified_at')
|
||||
->where('email_verified_at', '>=', now()->startOfMonth())
|
||||
->count();
|
||||
} catch (\Throwable) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the content type with the most uploads in the last 30 days,
|
||||
* or null if the content_types table isn't available.
|
||||
*/
|
||||
private function getRisingContentType(): ?string
|
||||
{
|
||||
try {
|
||||
$row = DB::table('artworks')
|
||||
->join('content_types', 'content_types.id', '=', 'artworks.content_type_id')
|
||||
->where('artworks.is_public', true)
|
||||
->where('artworks.is_approved', true)
|
||||
->whereNull('artworks.deleted_at')
|
||||
->where('artworks.published_at', '>=', now()->subDays(30))
|
||||
->selectRaw('content_types.name, COUNT(*) as cnt')
|
||||
->groupBy('content_types.id', 'content_types.name')
|
||||
->orderByDesc('cnt')
|
||||
->first();
|
||||
|
||||
return $row ? (string) $row->name : null;
|
||||
} catch (\Throwable) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user