messages implemented

This commit is contained in:
2026-02-26 21:12:32 +01:00
parent d0aefc5ddc
commit 15b7b77d20
168 changed files with 14728 additions and 6786 deletions

View File

@@ -0,0 +1,192 @@
<?php
namespace App\Http\Controllers\Api;
use App\Enums\ReactionType;
use App\Http\Controllers\Controller;
use App\Models\ArtworkComment;
use App\Models\ArtworkReaction;
use App\Models\CommentReaction;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
/**
* Handles reaction toggling for artworks and comments.
*
* POST /api/artworks/{id}/reactions toggle artwork reaction
* POST /api/comments/{id}/reactions toggle comment reaction
* GET /api/artworks/{id}/reactions list artwork reactions
* GET /api/comments/{id}/reactions list comment reactions
*/
class ReactionController extends Controller
{
// ─────────────────────────────────────────────────────────────────────────
// Artwork reactions
// ─────────────────────────────────────────────────────────────────────────
public function artworkReactions(Request $request, int $artworkId): JsonResponse
{
return $this->listReactions('artwork', $artworkId, $request->user()?->id);
}
public function toggleArtworkReaction(Request $request, int $artworkId): JsonResponse
{
$this->validateExists('artworks', $artworkId);
$slug = $this->validateReactionSlug($request);
return $this->toggle(
model: new ArtworkReaction(),
where: ['artwork_id' => $artworkId, 'user_id' => $request->user()->id, 'reaction' => $slug],
countWhere: ['artwork_id' => $artworkId],
entityId: $artworkId,
entityType: 'artwork',
userId: $request->user()->id,
slug: $slug,
);
}
// ─────────────────────────────────────────────────────────────────────────
// Comment reactions
// ─────────────────────────────────────────────────────────────────────────
public function commentReactions(Request $request, int $commentId): JsonResponse
{
return $this->listReactions('comment', $commentId, $request->user()?->id);
}
public function toggleCommentReaction(Request $request, int $commentId): JsonResponse
{
// Make sure comment exists and belongs to a public artwork
$comment = ArtworkComment::with('artwork')
->where('id', $commentId)
->whereHas('artwork', fn ($q) => $q->public()->published())
->firstOrFail();
$slug = $this->validateReactionSlug($request);
return $this->toggle(
model: new CommentReaction(),
where: ['comment_id' => $commentId, 'user_id' => $request->user()->id, 'reaction' => $slug],
countWhere: ['comment_id' => $commentId],
entityId: $commentId,
entityType: 'comment',
userId: $request->user()->id,
slug: $slug,
);
}
// ─────────────────────────────────────────────────────────────────────────
// Shared internals
// ─────────────────────────────────────────────────────────────────────────
private function toggle(
\Illuminate\Database\Eloquent\Model $model,
array $where,
array $countWhere,
int $entityId,
string $entityType,
int $userId,
string $slug,
): JsonResponse {
$table = $model->getTable();
$existing = DB::table($table)->where($where)->first();
if ($existing) {
// Toggle off
DB::table($table)->where($where)->delete();
$active = false;
} else {
// Toggle on
DB::table($table)->insertOrIgnore(array_merge($where, [
'created_at' => now(),
]));
$active = true;
}
// Return fresh totals per reaction type
$totals = $this->getTotals($table, $countWhere, $userId);
return response()->json([
'entity_type' => $entityType,
'entity_id' => $entityId,
'reaction' => $slug,
'active' => $active,
'totals' => $totals,
]);
}
private function listReactions(string $entityType, int $entityId, ?int $userId): JsonResponse
{
if ($entityType === 'artwork') {
$table = 'artwork_reactions';
$where = ['artwork_id' => $entityId];
} else {
$table = 'comment_reactions';
$where = ['comment_id' => $entityId];
}
$totals = $this->getTotals($table, $where, $userId);
return response()->json([
'entity_type' => $entityType,
'entity_id' => $entityId,
'totals' => $totals,
]);
}
/**
* Return per-slug totals and whether the current user has each reaction.
*/
private function getTotals(string $table, array $where, ?int $userId): array
{
$rows = DB::table($table)
->where($where)
->selectRaw('reaction, COUNT(*) as total')
->groupBy('reaction')
->get()
->keyBy('reaction');
$totals = [];
foreach (ReactionType::cases() as $type) {
$slug = $type->value;
$count = (int) ($rows[$slug]->total ?? 0);
// Check if current user has this reaction
$mine = false;
if ($userId && $count > 0) {
$mine = DB::table($table)
->where($where)
->where('reaction', $slug)
->where('user_id', $userId)
->exists();
}
$totals[$slug] = [
'emoji' => $type->emoji(),
'label' => $type->label(),
'count' => $count,
'mine' => $mine,
];
}
return $totals;
}
private function validateReactionSlug(Request $request): string
{
$request->validate([
'reaction' => ['required', 'string', 'in:' . implode(',', ReactionType::values())],
]);
return $request->input('reaction');
}
private function validateExists(string $table, int $id): void
{
if (! DB::table($table)->where('id', $id)->exists()) {
throw new ModelNotFoundException("No [{$table}] record found with id [{$id}].");
}
}
}