Files
SkinbaseNova/app/Http/Controllers/Api/Messaging/TypingController.php

106 lines
3.3 KiB
PHP

<?php
namespace App\Http\Controllers\Api\Messaging;
use App\Events\TypingStarted;
use App\Events\TypingStopped;
use App\Http\Controllers\Controller;
use App\Models\Conversation;
use App\Models\ConversationParticipant;
use Illuminate\Cache\Repository;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
class TypingController extends Controller
{
public function start(Request $request, int $conversationId): JsonResponse
{
$this->findConversationOrFail($conversationId);
$ttl = max(5, (int) config('messaging.typing.ttl_seconds', 8));
$this->store()->put($this->key($conversationId, (int) $request->user()->id), 1, now()->addSeconds($ttl));
if ((bool) config('messaging.realtime', false)) {
event(new TypingStarted($conversationId, $request->user()));
}
return response()->json(['ok' => true]);
}
public function stop(Request $request, int $conversationId): JsonResponse
{
$this->findConversationOrFail($conversationId);
$this->store()->forget($this->key($conversationId, (int) $request->user()->id));
if ((bool) config('messaging.realtime', false)) {
event(new TypingStopped($conversationId, $request->user()));
}
return response()->json(['ok' => true]);
}
public function index(Request $request, int $conversationId): JsonResponse
{
$this->findConversationOrFail($conversationId);
$userId = (int) $request->user()->id;
$participants = ConversationParticipant::query()
->where('conversation_id', $conversationId)
->whereNull('left_at')
->where('user_id', '!=', $userId)
->with('user:id,username')
->get();
$typing = $participants
->filter(fn ($p) => $this->store()->has($this->key($conversationId, (int) $p->user_id)))
->map(fn ($p) => [
'user_id' => (int) $p->user_id,
'username' => (string) ($p->user->username ?? ''),
])
->values();
return response()->json(['typing' => $typing]);
}
private function assertParticipant(Request $request, int $conversationId): void
{
abort_unless(
ConversationParticipant::query()
->where('conversation_id', $conversationId)
->where('user_id', $request->user()->id)
->whereNull('left_at')
->exists(),
403,
'You are not a participant of this conversation.'
);
}
private function key(int $conversationId, int $userId): string
{
return "typing:{$conversationId}:{$userId}";
}
private function store(): Repository
{
$store = (string) config('messaging.typing.cache_store', 'redis');
if ($store === 'redis' && ! class_exists('Redis')) {
return Cache::store();
}
try {
return Cache::store($store);
} catch (\Throwable) {
return Cache::store();
}
}
private function findConversationOrFail(int $conversationId): Conversation
{
$conversation = Conversation::query()->findOrFail($conversationId);
$this->authorize('view', $conversation);
return $conversation;
}
}