messages implemented
This commit is contained in:
178
app/Http/Controllers/Api/ArtworkCommentController.php
Normal file
178
app/Http/Controllers/Api/ArtworkCommentController.php
Normal file
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Artwork;
|
||||
use App\Models\ArtworkComment;
|
||||
use App\Services\ContentSanitizer;
|
||||
use App\Services\LegacySmileyMapper;
|
||||
use App\Support\AvatarUrl;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
/**
|
||||
* Artwork comment CRUD.
|
||||
*
|
||||
* POST /api/artworks/{artworkId}/comments → store
|
||||
* PUT /api/artworks/{artworkId}/comments/{id} → update (own comment)
|
||||
* DELETE /api/artworks/{artworkId}/comments/{id} → delete (own or admin)
|
||||
* GET /api/artworks/{artworkId}/comments → list (paginated)
|
||||
*/
|
||||
class ArtworkCommentController extends Controller
|
||||
{
|
||||
private const MAX_LENGTH = 10_000;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// List
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
public function index(Request $request, int $artworkId): JsonResponse
|
||||
{
|
||||
$artwork = Artwork::public()->published()->findOrFail($artworkId);
|
||||
|
||||
$page = max(1, (int) $request->query('page', 1));
|
||||
$perPage = 20;
|
||||
|
||||
$comments = ArtworkComment::with(['user', 'user.profile'])
|
||||
->where('artwork_id', $artwork->id)
|
||||
->where('is_approved', true)
|
||||
->orderByDesc('created_at')
|
||||
->paginate($perPage, ['*'], 'page', $page);
|
||||
|
||||
$userId = $request->user()?->id;
|
||||
$items = $comments->getCollection()->map(fn ($c) => $this->formatComment($c, $userId));
|
||||
|
||||
return response()->json([
|
||||
'data' => $items,
|
||||
'meta' => [
|
||||
'current_page' => $comments->currentPage(),
|
||||
'last_page' => $comments->lastPage(),
|
||||
'total' => $comments->total(),
|
||||
'per_page' => $comments->perPage(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Store
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
public function store(Request $request, int $artworkId): JsonResponse
|
||||
{
|
||||
$artwork = Artwork::public()->published()->findOrFail($artworkId);
|
||||
|
||||
$request->validate([
|
||||
'content' => ['required', 'string', 'min:1', 'max:' . self::MAX_LENGTH],
|
||||
]);
|
||||
|
||||
$raw = $request->input('content');
|
||||
|
||||
// Validate markdown-lite content
|
||||
$errors = ContentSanitizer::validate($raw);
|
||||
if ($errors) {
|
||||
return response()->json(['errors' => ['content' => $errors]], 422);
|
||||
}
|
||||
|
||||
$rendered = ContentSanitizer::render($raw);
|
||||
|
||||
$comment = ArtworkComment::create([
|
||||
'artwork_id' => $artwork->id,
|
||||
'user_id' => $request->user()->id,
|
||||
'content' => $raw, // legacy column (plain text fallback)
|
||||
'raw_content' => $raw,
|
||||
'rendered_content' => $rendered,
|
||||
'is_approved' => true, // auto-approve; extend with moderation as needed
|
||||
]);
|
||||
|
||||
// Bust the comments cache for this user's 'all' feed
|
||||
Cache::forget('comments.latest.all.page1');
|
||||
|
||||
$comment->load(['user', 'user.profile']);
|
||||
|
||||
return response()->json(['data' => $this->formatComment($comment, $request->user()->id)], 201);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Update
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
public function update(Request $request, int $artworkId, int $commentId): JsonResponse
|
||||
{
|
||||
$comment = ArtworkComment::where('artwork_id', $artworkId)
|
||||
->findOrFail($commentId);
|
||||
|
||||
Gate::authorize('update', $comment);
|
||||
|
||||
$request->validate([
|
||||
'content' => ['required', 'string', 'min:1', 'max:' . self::MAX_LENGTH],
|
||||
]);
|
||||
|
||||
$raw = $request->input('content');
|
||||
$errors = ContentSanitizer::validate($raw);
|
||||
if ($errors) {
|
||||
return response()->json(['errors' => ['content' => $errors]], 422);
|
||||
}
|
||||
|
||||
$rendered = ContentSanitizer::render($raw);
|
||||
|
||||
$comment->update([
|
||||
'content' => $raw,
|
||||
'raw_content' => $raw,
|
||||
'rendered_content' => $rendered,
|
||||
]);
|
||||
|
||||
Cache::forget('comments.latest.all.page1');
|
||||
|
||||
$comment->load(['user', 'user.profile']);
|
||||
|
||||
return response()->json(['data' => $this->formatComment($comment, $request->user()->id)]);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Delete
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
public function destroy(Request $request, int $artworkId, int $commentId): JsonResponse
|
||||
{
|
||||
$comment = ArtworkComment::where('artwork_id', $artworkId)->findOrFail($commentId);
|
||||
|
||||
Gate::authorize('delete', $comment);
|
||||
|
||||
$comment->delete();
|
||||
Cache::forget('comments.latest.all.page1');
|
||||
|
||||
return response()->json(['message' => 'Comment deleted.'], 200);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Helpers
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
private function formatComment(ArtworkComment $c, ?int $currentUserId): array
|
||||
{
|
||||
$user = $c->user;
|
||||
$userId = (int) ($c->user_id ?? 0);
|
||||
$avatarHash = $user?->profile?->avatar_hash ?? null;
|
||||
|
||||
return [
|
||||
'id' => $c->id,
|
||||
'raw_content' => $c->raw_content ?? $c->content,
|
||||
'rendered_content' => $c->rendered_content ?? e(strip_tags($c->content ?? '')),
|
||||
'created_at' => $c->created_at?->toIso8601String(),
|
||||
'time_ago' => $c->created_at ? Carbon::parse($c->created_at)->diffForHumans() : null,
|
||||
'can_edit' => $currentUserId === $userId,
|
||||
'can_delete' => $currentUserId === $userId,
|
||||
'user' => [
|
||||
'id' => $userId,
|
||||
'username' => $user?->username,
|
||||
'display' => $user?->username ?? $user?->name ?? 'User',
|
||||
'profile_url' => $user?->username ? '/@' . $user->username : '/profile/' . $userId,
|
||||
'avatar_url' => AvatarUrl::forUser($userId, $avatarHash, 64),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user