Keep guest rooms moderated and restore owner moderation
CI/CD / test_and_check_app (push) Successful in 2m22s
CI/CD / Publish immutable image (push) Successful in 1m16s

This commit is contained in:
2026-09-24 15:02:05 +02:00
parent 4dfe79ce5c
commit 33df9e49ab
6 changed files with 267 additions and 11 deletions
+38 -3
View File
@@ -30,7 +30,7 @@ class RemoveAbsentPlayers implements ShouldQueue
->select(['id', 'room_id'])
->chunkById(100, function ($players) use ($cutoff, $realtimeState): void {
foreach ($players as $player) {
$room = DB::transaction(function () use ($player, $cutoff): ?Room {
$result = DB::transaction(function () use ($player, $cutoff): ?array {
$room = Room::withTrashed()
->lockForUpdate()
->find($player->room_id);
@@ -54,14 +54,49 @@ class RemoveAbsentPlayers implements ShouldQueue
}
$currentPlayer->delete();
$roomDeleted = false;
if ($room->owner_id === null && ! $room->trashed()) {
if (! $room->players()->exists()) {
$room->delete();
$roomDeleted = true;
} elseif (! $room->players()->where('is_moderator', true)->exists()) {
$successor = $room->players()
->where(function ($query) use ($cutoff): void {
$query->where('last_seen_at', '>', $cutoff)
->orWhere(function ($query) use ($cutoff): void {
$query->whereNull('last_seen_at')
->where('created_at', '>', $cutoff);
});
})
->orderBy('created_at')
->orderBy('id')
->first();
$successor?->update(['is_moderator' => true]);
}
}
$room->update([
'state_version' => $room->state_version + 1,
]);
return $room;
return [$room, $roomDeleted];
});
if ($room !== null && ! $room->trashed()) {
if ($result === null) {
continue;
}
[$room, $roomDeleted] = $result;
if ($roomDeleted) {
RoomStateChanged::dispatch(
$room->id,
$room->state_version,
RoomStateChange::RoomDeleted,
);
} elseif (! $room->trashed()) {
RoomStateChanged::dispatch(
$room->id,
$room->state_version,
@@ -69,17 +69,19 @@ class RoomParticipationService
$user,
$sessionPlayerIds[$room->id] ?? null,
);
$shouldModerate = $this->shouldModerate($room, $user);
if ($player === null) {
$player = $room->players()->create([
'user_id' => $user?->id,
'name' => $data['player_name'],
'is_moderator' => false,
'is_moderator' => $shouldModerate,
'last_seen_at' => now(),
]);
} else {
$player->update([
'last_seen_at' => now(),
'is_moderator' => $player->is_moderator || $shouldModerate,
]);
}
@@ -123,6 +125,7 @@ class RoomParticipationService
$user,
$sessionPlayerId,
);
$shouldModerate = $this->shouldModerate($lockedRoom, $user);
$nameIsTaken = $lockedRoom->players()
->where('name', $playerName)
@@ -144,18 +147,19 @@ class RoomParticipationService
$player = $lockedRoom->players()->create([
'user_id' => $user?->id,
'name' => $playerName,
'is_moderator' => false,
'is_moderator' => $shouldModerate,
'last_seen_at' => now(),
]);
$change = RoomStateChange::PlayerJoined;
} else {
if ($player->name !== $playerName) {
if ($player->name !== $playerName || ($shouldModerate && ! $player->is_moderator)) {
$change = RoomStateChange::PlayerUpdated;
}
$updates = [
'name' => $playerName,
'last_seen_at' => now(),
'is_moderator' => $player->is_moderator || $shouldModerate,
];
if (
@@ -196,6 +200,16 @@ class RoomParticipationService
return $player;
}
private function shouldModerate(Room $room, ?User $user): bool
{
if ($user !== null && $room->owner_id === $user->id) {
return true;
}
return $room->owner_id === null
&& ! $room->players()->where('is_moderator', true)->exists();
}
private function findExistingEmbeddedPlayer(
Room $room,
?User $user,
@@ -218,7 +232,10 @@ class RoomParticipationService
->lockForUpdate()
->first();
if ($sessionPlayer !== null) {
if (
$sessionPlayer !== null
&& ($user === null || $sessionPlayer->user_id === null || $sessionPlayer->user_id === $user->id)
) {
return $sessionPlayer;
}
}