more fixes
This commit is contained in:
284
app/Http/Controllers/DashboardController.php
Normal file
284
app/Http/Controllers/DashboardController.php
Normal file
@@ -0,0 +1,284 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Artwork;
|
||||
use App\Models\Story;
|
||||
use App\Models\User;
|
||||
use App\Support\AvatarUrl;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
final class DashboardController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
return view('dashboard', [
|
||||
'page_title' => 'Dashboard',
|
||||
'dashboard_user_name' => $user?->username ?: $user?->name ?: 'Creator',
|
||||
'dashboard_is_creator' => Artwork::query()->where('user_id', $user->id)->exists(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function activity(Request $request): JsonResponse
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
$notificationItems = $user->notifications()
|
||||
->latest()
|
||||
->limit(12)
|
||||
->get()
|
||||
->map(function ($notification): array {
|
||||
return [
|
||||
'id' => (string) $notification->id,
|
||||
'type' => 'notification',
|
||||
'message' => $this->notificationMessage((array) $notification->data),
|
||||
'reference_id' => (string) ($notification->id ?? ''),
|
||||
'created_at' => $notification->created_at?->toIso8601String(),
|
||||
'is_unread' => $notification->read_at === null,
|
||||
'actor' => null,
|
||||
];
|
||||
});
|
||||
|
||||
$followItems = DB::table('user_followers as uf')
|
||||
->join('users as follower', 'follower.id', '=', 'uf.follower_id')
|
||||
->leftJoin('user_profiles as fp', 'fp.user_id', '=', 'follower.id')
|
||||
->where('uf.user_id', $user->id)
|
||||
->select([
|
||||
'uf.follower_id as actor_id',
|
||||
'follower.username as actor_username',
|
||||
'follower.name as actor_name',
|
||||
'fp.avatar_hash as actor_avatar_hash',
|
||||
'uf.created_at',
|
||||
])
|
||||
->orderByDesc('uf.created_at')
|
||||
->limit(10)
|
||||
->get()
|
||||
->map(function ($row): array {
|
||||
return [
|
||||
'id' => 'follow-' . (string) $row->actor_id . '-' . Carbon::parse((string) $row->created_at)->timestamp,
|
||||
'type' => 'new_follower',
|
||||
'message' => 'started following you',
|
||||
'reference_id' => (string) $row->actor_id,
|
||||
'created_at' => Carbon::parse((string) $row->created_at)->toIso8601String(),
|
||||
'is_unread' => false,
|
||||
'actor' => [
|
||||
'id' => (int) $row->actor_id,
|
||||
'name' => $row->actor_name,
|
||||
'username' => $row->actor_username,
|
||||
'avatar' => AvatarUrl::forUser((int) $row->actor_id, $row->actor_avatar_hash, 64),
|
||||
],
|
||||
];
|
||||
});
|
||||
|
||||
$commentItems = DB::table('artwork_comments as c')
|
||||
->join('artworks as a', 'a.id', '=', 'c.artwork_id')
|
||||
->join('users as commenter', 'commenter.id', '=', 'c.user_id')
|
||||
->leftJoin('user_profiles as cp', 'cp.user_id', '=', 'commenter.id')
|
||||
->where('a.user_id', $user->id)
|
||||
->where('c.user_id', '!=', $user->id)
|
||||
->where('c.is_approved', true)
|
||||
->whereNull('c.deleted_at')
|
||||
->select([
|
||||
'c.id as comment_id',
|
||||
'c.created_at',
|
||||
'a.id as artwork_id',
|
||||
'a.slug as artwork_slug',
|
||||
'a.title as artwork_title',
|
||||
'commenter.id as actor_id',
|
||||
'commenter.username as actor_username',
|
||||
'commenter.name as actor_name',
|
||||
'cp.avatar_hash as actor_avatar_hash',
|
||||
])
|
||||
->orderByDesc('c.created_at')
|
||||
->limit(10)
|
||||
->get()
|
||||
->map(function ($row): array {
|
||||
return [
|
||||
'id' => 'comment-' . (string) $row->comment_id,
|
||||
'type' => 'comment',
|
||||
'message' => 'commented on your artwork',
|
||||
'reference_id' => (string) $row->artwork_id,
|
||||
'created_at' => Carbon::parse((string) $row->created_at)->toIso8601String(),
|
||||
'is_unread' => false,
|
||||
'actor' => [
|
||||
'id' => (int) $row->actor_id,
|
||||
'name' => $row->actor_name,
|
||||
'username' => $row->actor_username,
|
||||
'avatar' => AvatarUrl::forUser((int) $row->actor_id, $row->actor_avatar_hash, 64),
|
||||
],
|
||||
'context' => [
|
||||
'artwork_id' => (int) $row->artwork_id,
|
||||
'artwork_title' => $row->artwork_title,
|
||||
'artwork_url' => '/art/' . $row->artwork_id . '/' . $row->artwork_slug,
|
||||
],
|
||||
];
|
||||
});
|
||||
|
||||
$items = collect()
|
||||
->concat($notificationItems)
|
||||
->concat($followItems)
|
||||
->concat($commentItems)
|
||||
->sortByDesc(fn (array $item) => (string) ($item['created_at'] ?? ''))
|
||||
->take(20)
|
||||
->values();
|
||||
|
||||
return response()->json([
|
||||
'data' => $items,
|
||||
]);
|
||||
}
|
||||
|
||||
public function analytics(Request $request): JsonResponse
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
$artworksCount = Artwork::query()->where('user_id', $user->id)->count();
|
||||
|
||||
$storyAggregate = Story::query()
|
||||
->where('creator_id', $user->id)
|
||||
->selectRaw('COUNT(*) as total_stories, COALESCE(SUM(views),0) as total_story_views, COALESCE(SUM(likes_count),0) as total_story_likes')
|
||||
->first();
|
||||
|
||||
$stats = $user->statistics;
|
||||
|
||||
$followersCount = (int) ($stats?->followers_count ?? $user->followers()->count());
|
||||
$artworkLikes = (int) ($stats?->favorites_received_count ?? 0);
|
||||
$storyLikes = (int) ($storyAggregate?->total_story_likes ?? 0);
|
||||
|
||||
return response()->json([
|
||||
'data' => [
|
||||
'is_creator' => $artworksCount > 0,
|
||||
'total_artworks' => $artworksCount,
|
||||
'total_stories' => (int) ($storyAggregate?->total_stories ?? 0),
|
||||
'total_story_views' => (int) ($storyAggregate?->total_story_views ?? 0),
|
||||
'total_followers' => $followersCount,
|
||||
'total_likes' => $artworkLikes + $storyLikes,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function trendingArtworks(): JsonResponse
|
||||
{
|
||||
$cacheKey = 'dashboard:trending-artworks:v1';
|
||||
|
||||
$data = Cache::remember($cacheKey, 300, function (): array {
|
||||
return Artwork::query()
|
||||
->select('artworks.*')
|
||||
->leftJoin('artwork_stats as s', 's.artwork_id', '=', 'artworks.id')
|
||||
->public()
|
||||
->with(['user.profile', 'stats'])
|
||||
->orderByRaw('COALESCE(s.ranking_score, 0) DESC')
|
||||
->orderByRaw('COALESCE(s.heat_score, 0) DESC')
|
||||
->orderByRaw('COALESCE(s.favorites, 0) DESC')
|
||||
->orderByRaw('COALESCE(s.views, 0) DESC')
|
||||
->orderByRaw('COALESCE(s.comments_count, 0) DESC')
|
||||
->limit(8)
|
||||
->get()
|
||||
->map(function (Artwork $artwork): array {
|
||||
return [
|
||||
'id' => $artwork->id,
|
||||
'title' => $artwork->title,
|
||||
'url' => '/art/' . $artwork->id . '/' . $artwork->slug,
|
||||
'thumbnail' => $artwork->thumbUrl('md') ?? $artwork->thumbnail_url,
|
||||
'likes' => (int) ($artwork->stats?->favorites ?? 0),
|
||||
'views' => (int) ($artwork->stats?->views ?? 0),
|
||||
'comments' => (int) ($artwork->stats?->comments_count ?? 0),
|
||||
'creator' => [
|
||||
'id' => (int) $artwork->user_id,
|
||||
'username' => $artwork->user?->username,
|
||||
'name' => $artwork->user?->name,
|
||||
'url' => $artwork->user?->username ? '/@' . $artwork->user->username : null,
|
||||
],
|
||||
];
|
||||
})
|
||||
->values()
|
||||
->all();
|
||||
});
|
||||
|
||||
return response()->json(['data' => $data]);
|
||||
}
|
||||
|
||||
public function recommendedCreators(Request $request): JsonResponse
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
$cacheKey = 'dashboard:recommended-creators:' . $user->id . ':v1';
|
||||
|
||||
$data = Cache::remember($cacheKey, 600, function () use ($user): array {
|
||||
$followingIds = DB::table('user_followers')
|
||||
->where('follower_id', $user->id)
|
||||
->pluck('user_id')
|
||||
->map(fn ($id) => (int) $id)
|
||||
->all();
|
||||
|
||||
$excludeIds = array_values(array_unique(array_merge([$user->id], $followingIds)));
|
||||
|
||||
return User::query()
|
||||
->from('users')
|
||||
->leftJoin('user_profiles as up', 'up.user_id', '=', 'users.id')
|
||||
->leftJoin('user_statistics as us', 'us.user_id', '=', 'users.id')
|
||||
->where('users.is_active', true)
|
||||
->whereNotIn('users.id', $excludeIds)
|
||||
->whereExists(function ($q): void {
|
||||
$q->select(DB::raw(1))
|
||||
->from('artworks')
|
||||
->whereColumn('artworks.user_id', 'users.id')
|
||||
->where('artworks.is_public', true)
|
||||
->where('artworks.is_approved', true)
|
||||
->whereNull('artworks.deleted_at');
|
||||
})
|
||||
->select([
|
||||
'users.id',
|
||||
'users.username',
|
||||
'users.name',
|
||||
'up.avatar_hash',
|
||||
DB::raw('COALESCE(us.followers_count, 0) as followers_count'),
|
||||
DB::raw('COALESCE(us.uploads_count, 0) as uploads_count'),
|
||||
])
|
||||
->orderByDesc('followers_count')
|
||||
->orderByDesc('uploads_count')
|
||||
->limit(6)
|
||||
->get()
|
||||
->map(function ($row): array {
|
||||
$username = (string) ($row->username ?? '');
|
||||
|
||||
return [
|
||||
'id' => (int) $row->id,
|
||||
'username' => $username,
|
||||
'name' => $row->name,
|
||||
'url' => $username !== '' ? '/@' . $username : null,
|
||||
'avatar' => AvatarUrl::forUser((int) $row->id, $row->avatar_hash, 64),
|
||||
'followers_count' => (int) $row->followers_count,
|
||||
'uploads_count' => (int) $row->uploads_count,
|
||||
];
|
||||
})
|
||||
->values()
|
||||
->all();
|
||||
});
|
||||
|
||||
return response()->json(['data' => $data]);
|
||||
}
|
||||
|
||||
private function notificationMessage(array $payload): string
|
||||
{
|
||||
$title = trim((string) ($payload['title'] ?? ''));
|
||||
if ($title !== '') {
|
||||
return $title;
|
||||
}
|
||||
|
||||
$message = trim((string) ($payload['message'] ?? ''));
|
||||
if ($message !== '') {
|
||||
return $message;
|
||||
}
|
||||
|
||||
$type = trim((string) ($payload['type'] ?? 'Notification'));
|
||||
return $type !== '' ? $type : 'New notification';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user