301 lines
12 KiB
PHP
301 lines
12 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Anime;
|
|
use App\Models\AnimeRating;
|
|
use App\Models\AnimeFollow;
|
|
use App\Models\ContinueWatching;
|
|
use App\Models\EpisodeVote;
|
|
use App\Models\EpisodeNote;
|
|
use App\Models\UserNotification;
|
|
use App\Models\UserAchievement;
|
|
use App\Models\Achievement;
|
|
use App\Models\Watchlist;
|
|
use App\Models\AnimeRequest;
|
|
use App\Models\AnimeRequestVote;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class UserApiController extends Controller
|
|
{
|
|
// ── Watchlist ──────────────────────────────────────────────────────────────
|
|
|
|
public function watchlist(Request $request)
|
|
{
|
|
$status = $request->input('status'); // watching|completed|plan_to_watch|dropped
|
|
|
|
$query = Watchlist::where('user_id', $request->user()->id)
|
|
->with('anime:id,title,slug,cover_image,rating,episode_count,status,release_year');
|
|
|
|
if ($status) $query->where('status', $status);
|
|
|
|
$items = $query->orderByDesc('updated_at')->paginate(24);
|
|
|
|
return response()->json([
|
|
'data' => collect($items->items())->map(fn($w) => [
|
|
'id' => $w->id,
|
|
'status' => $w->status,
|
|
'anime' => $w->anime ? [
|
|
'id' => $w->anime->id,
|
|
'title' => $w->anime->title,
|
|
'slug' => $w->anime->slug,
|
|
'cover_url' => \App\Support\MediaUrl::fromStoragePath($w->anime->cover_image),
|
|
'rating' => $w->anime->rating,
|
|
'episode_count' => $w->anime->episode_count,
|
|
'status' => $w->anime->status,
|
|
'release_year' => $w->anime->release_year,
|
|
] : null,
|
|
]),
|
|
'total' => $items->total(),
|
|
'last_page'=> $items->lastPage(),
|
|
]);
|
|
}
|
|
|
|
public function watchlistToggle(Request $request, Anime $anime)
|
|
{
|
|
$user = $request->user();
|
|
$status = $request->input('status', 'plan_to_watch');
|
|
|
|
$existing = Watchlist::where('user_id', $user->id)->where('anime_id', $anime->id)->first();
|
|
|
|
if ($existing) {
|
|
if ($existing->status === $status) {
|
|
$existing->delete();
|
|
return response()->json(['in_watchlist' => false, 'status' => null]);
|
|
}
|
|
$existing->update(['status' => $status]);
|
|
return response()->json(['in_watchlist' => true, 'status' => $status]);
|
|
}
|
|
|
|
Watchlist::create(['user_id' => $user->id, 'anime_id' => $anime->id, 'status' => $status]);
|
|
return response()->json(['in_watchlist' => true, 'status' => $status]);
|
|
}
|
|
|
|
// ── Continue Watching ──────────────────────────────────────────────────────
|
|
|
|
public function continueWatchingUpdate(Request $request)
|
|
{
|
|
$data = $request->validate([
|
|
'anime_id' => 'required|exists:animes,id',
|
|
'season_number' => 'required|integer|min:1',
|
|
'episode_number' => 'required|integer|min:1',
|
|
'percent_complete' => 'required|numeric|min:0|max:100',
|
|
]);
|
|
|
|
ContinueWatching::updateOrCreate(
|
|
['user_id' => $request->user()->id, 'anime_id' => $data['anime_id']],
|
|
[
|
|
'season_number' => $data['season_number'],
|
|
'episode_number' => $data['episode_number'],
|
|
'percent_complete' => $data['percent_complete'],
|
|
]
|
|
);
|
|
|
|
return response()->json(['ok' => true]);
|
|
}
|
|
|
|
// ── Anime Rate ─────────────────────────────────────────────────────────────
|
|
|
|
public function animeRate(Request $request, Anime $anime)
|
|
{
|
|
$data = $request->validate(['rating' => 'required|numeric|min:1|max:10']);
|
|
|
|
AnimeRating::updateOrCreate(
|
|
['user_id' => $request->user()->id, 'anime_id' => $anime->id],
|
|
['rating' => $data['rating']]
|
|
);
|
|
|
|
$avg = AnimeRating::where('anime_id', $anime->id)->avg('rating');
|
|
$anime->update(['rating' => round($avg, 1)]);
|
|
|
|
return response()->json(['rating' => $data['rating'], 'avg' => round($avg, 1)]);
|
|
}
|
|
|
|
// ── Follow ─────────────────────────────────────────────────────────────────
|
|
|
|
public function followToggle(Request $request, Anime $anime)
|
|
{
|
|
$user = $request->user();
|
|
$existing = AnimeFollow::where('user_id', $user->id)->where('anime_id', $anime->id)->first();
|
|
|
|
if ($existing) {
|
|
$existing->delete();
|
|
return response()->json(['following' => false]);
|
|
}
|
|
|
|
AnimeFollow::create(['user_id' => $user->id, 'anime_id' => $anime->id]);
|
|
return response()->json(['following' => true]);
|
|
}
|
|
|
|
// ── Notifications ──────────────────────────────────────────────────────────
|
|
|
|
public function notifications(Request $request)
|
|
{
|
|
$items = UserNotification::where('user_id', $request->user()->id)
|
|
->orderByDesc('created_at')->paginate(20);
|
|
|
|
// Mark all as read
|
|
UserNotification::where('user_id', $request->user()->id)->whereNull('read_at')->update(['read_at' => now()]);
|
|
|
|
return response()->json([
|
|
'data' => collect($items->items())->map(fn($n) => [
|
|
'id' => $n->id,
|
|
'type' => $n->type,
|
|
'data' => $n->data ?? [],
|
|
'is_read' => $n->is_read,
|
|
'created_at' => $n->created_at?->diffForHumans(),
|
|
]),
|
|
'total' => $items->total(),
|
|
'last_page'=> $items->lastPage(),
|
|
]);
|
|
}
|
|
|
|
public function notificationsCount(Request $request)
|
|
{
|
|
if (!$request->user()) return response()->json(['count' => 0]);
|
|
$count = UserNotification::where('user_id', $request->user()->id)->whereNull('read_at')->count();
|
|
return response()->json(['count' => $count]);
|
|
}
|
|
|
|
// ── Achievements ───────────────────────────────────────────────────────────
|
|
|
|
public function achievements(Request $request)
|
|
{
|
|
$all = Achievement::orderBy('points')->get();
|
|
$earned = UserAchievement::where('user_id', $request->user()->id)->pluck('achievement_id')->toArray();
|
|
$total = UserAchievement::where('user_id', $request->user()->id)->join('achievements','achievements.id','=','user_achievements.achievement_id')->sum('achievements.points');
|
|
|
|
return response()->json([
|
|
'total_points' => (int)$total,
|
|
'data' => $all->map(fn($a) => [
|
|
'id' => $a->id,
|
|
'name' => $a->name,
|
|
'description' => $a->description,
|
|
'icon' => $a->icon,
|
|
'points' => $a->points,
|
|
'earned' => in_array($a->id, $earned),
|
|
'earned_at' => in_array($a->id, $earned)
|
|
? UserAchievement::where('user_id', $request->user()->id)->where('achievement_id', $a->id)->value('created_at')?->toISOString()
|
|
: null,
|
|
]),
|
|
]);
|
|
}
|
|
|
|
// ── Episode Notes ──────────────────────────────────────────────────────────
|
|
|
|
public function noteStore(Request $request, $episodeId)
|
|
{
|
|
$data = $request->validate(['note' => 'required|string|max:1000']);
|
|
|
|
$note = EpisodeNote::create([
|
|
'user_id' => $request->user()->id,
|
|
'episode_id' => $episodeId,
|
|
'note' => $data['note'],
|
|
]);
|
|
|
|
return response()->json(['id' => $note->id, 'note' => $note->note, 'created_at' => $note->created_at?->toISOString()], 201);
|
|
}
|
|
|
|
public function noteDelete(Request $request, $noteId)
|
|
{
|
|
$note = EpisodeNote::where('id', $noteId)->where('user_id', $request->user()->id)->firstOrFail();
|
|
$note->delete();
|
|
return response()->json(['ok' => true]);
|
|
}
|
|
|
|
public function episodeNotesList(Request $request, $episodeId)
|
|
{
|
|
$notes = EpisodeNote::where('user_id', $request->user()->id)
|
|
->where('episode_id', $episodeId)
|
|
->orderByDesc('created_at')->get();
|
|
|
|
return response()->json($notes->map(fn($n) => [
|
|
'id' => $n->id,
|
|
'note' => $n->note,
|
|
'created_at' => $n->created_at?->toISOString(),
|
|
]));
|
|
}
|
|
|
|
// ── Anime Requests ─────────────────────────────────────────────────────────
|
|
|
|
public function requestIndex(Request $request)
|
|
{
|
|
$items = AnimeRequest::withCount('votes')
|
|
->orderByDesc('votes_count')->orderByDesc('created_at')->paginate(20);
|
|
|
|
return response()->json([
|
|
'data' => collect($items->items())->map(fn($r) => [
|
|
'id' => $r->id,
|
|
'title' => $r->title,
|
|
'note' => $r->note,
|
|
'status' => $r->status,
|
|
'votes_count' => $r->votes_count,
|
|
'created_at' => $r->created_at?->diffForHumans(),
|
|
'user_voted' => $request->user()
|
|
? AnimeRequestVote::where('user_id', $request->user()->id)->where('anime_request_id', $r->id)->exists()
|
|
: false,
|
|
]),
|
|
'total' => $items->total(),
|
|
'last_page'=> $items->lastPage(),
|
|
]);
|
|
}
|
|
|
|
public function requestStore(Request $request)
|
|
{
|
|
$data = $request->validate([
|
|
'title' => 'required|string|max:200',
|
|
'note' => 'nullable|string|max:500',
|
|
]);
|
|
|
|
$req = AnimeRequest::create([
|
|
'user_id' => $request->user()->id,
|
|
'title' => $data['title'],
|
|
'note' => $data['note'] ?? null,
|
|
'status' => 'pending',
|
|
]);
|
|
|
|
return response()->json(['id' => $req->id, 'title' => $req->title], 201);
|
|
}
|
|
|
|
public function requestVote(Request $request, AnimeRequest $animeRequest)
|
|
{
|
|
$user = $request->user();
|
|
$existing = AnimeRequestVote::where('user_id', $user->id)->where('anime_request_id', $animeRequest->id)->first();
|
|
|
|
if ($existing) {
|
|
$existing->delete();
|
|
return response()->json(['voted' => false, 'votes' => $animeRequest->votes()->count()]);
|
|
}
|
|
|
|
AnimeRequestVote::create(['user_id' => $user->id, 'anime_request_id' => $animeRequest->id]);
|
|
return response()->json(['voted' => true, 'votes' => $animeRequest->votes()->count()]);
|
|
}
|
|
|
|
// ── Profile Stats ──────────────────────────────────────────────────────────
|
|
|
|
public function profileStats(Request $request)
|
|
{
|
|
$userId = $request->user()->id;
|
|
|
|
$watchlistCount = Watchlist::where('user_id', $userId)->count();
|
|
$completedCount = Watchlist::where('user_id', $userId)->where('status', 'completed')->count();
|
|
$notifCount = UserNotification::where('user_id', $userId)->where('is_read', false)->count();
|
|
$achPoints = UserAchievement::where('user_id', $userId)
|
|
->join('achievements','achievements.id','=','user_achievements.achievement_id')
|
|
->sum('achievements.points');
|
|
$achCount = UserAchievement::where('user_id', $userId)->count();
|
|
$commentCount = \App\Models\Comment::where('user_id', $userId)->count();
|
|
|
|
return response()->json([
|
|
'watchlist_count' => $watchlistCount,
|
|
'completed_count' => $completedCount,
|
|
'notif_count' => (int)$notifCount,
|
|
'achievement_points'=> (int)$achPoints,
|
|
'achievement_count' => $achCount,
|
|
'comment_count' => $commentCount,
|
|
]);
|
|
}
|
|
}
|