feature/reconnect #14
6 changed files with 67 additions and 7 deletions
|
|
@ -324,6 +324,22 @@ export function registerRoomHandlers(
|
|||
broadcastState(room)
|
||||
})
|
||||
|
||||
socket.on("room:leave", () => {
|
||||
const code = socket.data.roomCode
|
||||
const playerId = socket.data.playerId
|
||||
if (!code || !playerId) {
|
||||
return
|
||||
}
|
||||
socket.leave(code)
|
||||
socket.data.roomCode = undefined
|
||||
socket.data.playerId = undefined
|
||||
const room = rooms.leave(code, playerId)
|
||||
if (room) {
|
||||
io.to(room.code).emit("player:left", { playerId })
|
||||
broadcastState(room)
|
||||
}
|
||||
})
|
||||
|
||||
socket.on("disconnect", () => {
|
||||
const affected = rooms.handleDisconnect(socket.id)
|
||||
if (!affected) {
|
||||
|
|
|
|||
|
|
@ -94,6 +94,28 @@ export class RoomManager {
|
|||
return this.rooms.get(code)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retire un joueur d'une room (départ volontaire). Réassigne l'hôte si besoin,
|
||||
* supprime la room si plus personne. Renvoie la room (ou null si supprimée).
|
||||
*/
|
||||
leave(code: string, playerId: string): ServerRoom | null {
|
||||
const room = this.rooms.get(code)
|
||||
if (!room) {
|
||||
return null
|
||||
}
|
||||
room.players.delete(playerId)
|
||||
room.scores.delete(playerId)
|
||||
if (room.players.size === 0) {
|
||||
this.rooms.delete(code)
|
||||
return null
|
||||
}
|
||||
if (room.hostId === playerId) {
|
||||
const named = [...room.players.values()].find((p) => p.name !== "")
|
||||
room.hostId = (named ?? [...room.players.values()][0]).id
|
||||
}
|
||||
return room
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque le joueur d'un socket comme déconnecté.
|
||||
* Retourne la room affectée et le playerId, ou null si non rattaché.
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ export function GameEndView({ snapshot }: { snapshot: RoomSnapshot }) {
|
|||
const finalScores = useRoomStore((s) => s.finalScores)
|
||||
const gameTracks = useRoomStore((s) => s.gameTracks)
|
||||
const gameRecap = useRoomStore((s) => s.gameRecap)
|
||||
const reset = useRoomStore((s) => s.reset)
|
||||
const leaveRoom = useRoomStore((s) => s.leaveRoom)
|
||||
const returnToLobby = useRoomStore((s) => s.returnToLobby)
|
||||
const isHost = snapshot.hostId === playerId
|
||||
|
||||
|
|
@ -284,7 +284,7 @@ export function GameEndView({ snapshot }: { snapshot: RoomSnapshot }) {
|
|||
</p>
|
||||
)}
|
||||
<Link href="/">
|
||||
<Button variant="secondary" className="w-full" onClick={reset}>
|
||||
<Button variant="secondary" className="w-full" onClick={leaveRoom}>
|
||||
Quitter
|
||||
</Button>
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Link } from "wouter"
|
||||
import { Link, useLocation } from "wouter"
|
||||
import { AnimatePresence, motion } from "framer-motion"
|
||||
import { LogOut } from "lucide-react"
|
||||
import { Button } from "@workspace/ui/components/button"
|
||||
import { useRoomStore } from "@/store/room"
|
||||
import { LobbyView } from "@/components/lobby-view"
|
||||
|
|
@ -13,8 +14,10 @@ import { RoundTransition } from "@/components/round-transition"
|
|||
import { PseudoScreen } from "@/components/pseudo-screen"
|
||||
|
||||
export function RoomPage({ code }: { code: string }) {
|
||||
const [, navigate] = useLocation()
|
||||
const snapshot = useRoomStore((s) => s.snapshot)
|
||||
const roomCode = useRoomStore((s) => s.roomCode)
|
||||
const leaveRoom = useRoomStore((s) => s.leaveRoom)
|
||||
const playerId = useRoomStore((s) => s.playerId)
|
||||
const connected = useRoomStore((s) => s.connected)
|
||||
const pseudoSet = useRoomStore((s) => s.pseudoSet)
|
||||
|
|
@ -80,10 +83,22 @@ export function RoomPage({ code }: { code: string }) {
|
|||
>
|
||||
<header className="flex items-center justify-between">
|
||||
<RoomCode code={snapshot.code} />
|
||||
<div className="flex items-center gap-3">
|
||||
<span
|
||||
className={`size-2.5 rounded-full ${connected ? "bg-green-500" : "bg-red-500"}`}
|
||||
title={connected ? "Connecté" : "Déconnecté"}
|
||||
/>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
leaveRoom()
|
||||
navigate("/")
|
||||
}}
|
||||
>
|
||||
<LogOut /> Quitter
|
||||
</Button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<AnimatePresence mode="wait">
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ interface RoomState {
|
|||
submitTrack: (youtubeUrl: string) => Promise<SubmitOkPayload>
|
||||
removeTrack: (trackId: string) => Promise<boolean>
|
||||
returnToLobby: () => void
|
||||
leaveRoom: () => void
|
||||
vote: (choiceIndex: number) => void
|
||||
voteText: (text: string) => void
|
||||
voteBlindtest: (answer: BlindtestAnswer) => void
|
||||
|
|
@ -192,6 +193,11 @@ export const useRoomStore = create<RoomState>((set, get) => ({
|
|||
socket.emit("lobby:return")
|
||||
},
|
||||
|
||||
leaveRoom: () => {
|
||||
socket.emit("room:leave")
|
||||
get().reset()
|
||||
},
|
||||
|
||||
vote: (choiceIndex) => {
|
||||
if (get().hasVoted) {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@ export interface ClientToServerEvents {
|
|||
"room:join": (payload: RoomJoinPayload, ack: Ack<RoomCreatedPayload>) => void
|
||||
"room:rejoin": (payload: RejoinPayload, ack: Ack<RoomCreatedPayload>) => void
|
||||
"player:setName": (payload: SetNamePayload) => void
|
||||
"room:leave": () => void
|
||||
"lobby:updateSettings": (payload: UpdateSettingsPayload) => void
|
||||
"lobby:return": () => void
|
||||
"game:start": (ack: Ack<null>) => void
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue