import { router } from '@inertiajs/vue3'; import { onScopeDispose, ref, shallowReadonly, watch } from 'vue'; import { getEcho } from '@/lib/echo'; import { home } from '@/routes'; import { join } from '@/routes/room'; import type { CurrentRoomPlayer, PokerRoom, RoomStateChangedPayload, } from '@/types/room'; const ROOM_EVENT = '.room.state.changed'; export function useRoomRealtime( initialRoom: () => PokerRoom, initialCurrentPlayer: () => CurrentRoomPlayer, ) { const room = ref({ ...initialRoom() }); const currentPlayer = ref({ ...initialCurrentPlayer() }); function handleRoomChange(payload: RoomStateChangedPayload): void { if ( typeof payload.version !== 'number' || payload.version <= room.value.state_version ) { return; } if (payload.change === 'room_deleted') { router.visit(home().url, { replace: true }); return; } if ( payload.change === 'player_kicked' && payload.affected_player_id === currentPlayer.value.id ) { router.visit(home().url, { replace: true }); return; } if (payload.change === 'room_closed') { router.visit( join({ query: { join_code: room.value.join_code } }).url, { replace: true }, ); return; } if (payload.state === null || payload.state === undefined) { return; } room.value = { ...room.value, ...payload.state, }; if ( payload.change === 'votes_reset' || payload.change === 'next_round' ) { currentPlayer.value = { ...currentPlayer.value, current_vote: null, }; } } const stopWatchingRoom = watch( () => initialRoom().id, (nextRoomId, _previousRoomId, onCleanup) => { const echo = getEcho(); const channelName = `rooms.${nextRoomId}`; echo.channel(channelName).listen(ROOM_EVENT, handleRoomChange); onCleanup(() => { echo.leaveChannel(channelName); }); }, { immediate: true }, ); const stopWatchingInitialState = watch( [initialRoom, initialCurrentPlayer], ([nextRoom, nextCurrentPlayer]) => { room.value = { ...nextRoom }; currentPlayer.value = { ...nextCurrentPlayer }; }, { deep: true }, ); onScopeDispose(() => { stopWatchingRoom(); stopWatchingInitialState(); }); function setCurrentVote(value: string | null): void { currentPlayer.value = { ...currentPlayer.value, current_vote: value, }; } return { room: shallowReadonly(room), currentPlayer: shallowReadonly(currentPlayer), setCurrentVote, }; }