mirror of
https://github.com/ericasch6/scrum-poker-internal.git
synced 2026-10-13 15:52:09 +00:00
Implement instant websocket room updates
This commit is contained in:
@@ -1,72 +1,73 @@
|
||||
import { router } from '@inertiajs/vue3';
|
||||
import { onScopeDispose, watch } from 'vue';
|
||||
import { onScopeDispose, ref, shallowReadonly, watch } from 'vue';
|
||||
|
||||
import { getEcho } from '@/lib/echo';
|
||||
import type { RoomStateChangedPayload } from '@/types/room';
|
||||
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(
|
||||
roomId: () => string,
|
||||
stateVersion: () => number,
|
||||
): void {
|
||||
let reloadInProgress = false;
|
||||
let pendingVersion = 0;
|
||||
let disposed = false;
|
||||
|
||||
function reloadRoom(): void {
|
||||
if (reloadInProgress || disposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
reloadInProgress = true;
|
||||
|
||||
router.reload({
|
||||
only: ['room', 'currentPlayer'],
|
||||
onFinish: () => {
|
||||
reloadInProgress = false;
|
||||
|
||||
if (disposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pendingVersion > stateVersion()) {
|
||||
reloadRoom();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
pendingVersion = stateVersion();
|
||||
},
|
||||
});
|
||||
}
|
||||
initialRoom: () => PokerRoom,
|
||||
initialCurrentPlayer: () => CurrentRoomPlayer,
|
||||
) {
|
||||
const room = ref<PokerRoom>({ ...initialRoom() });
|
||||
const currentPlayer = ref<CurrentRoomPlayer>({ ...initialCurrentPlayer() });
|
||||
|
||||
function handleRoomChange(payload: RoomStateChangedPayload): void {
|
||||
if (
|
||||
typeof payload.version !== 'number' ||
|
||||
payload.version <= Math.max(stateVersion(), pendingVersion)
|
||||
payload.version <= room.value.state_version
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
pendingVersion = payload.version;
|
||||
|
||||
if (payload.change === 'room_deleted') {
|
||||
router.visit('/', { replace: true });
|
||||
router.visit(home().url, { replace: true });
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
reloadRoom();
|
||||
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(
|
||||
roomId,
|
||||
() => initialRoom().id,
|
||||
(nextRoomId, _previousRoomId, onCleanup) => {
|
||||
const echo = getEcho();
|
||||
const channelName = `rooms.${nextRoomId}`;
|
||||
|
||||
pendingVersion = stateVersion();
|
||||
echo.channel(channelName).listen(ROOM_EVENT, handleRoomChange);
|
||||
|
||||
onCleanup(() => {
|
||||
@@ -76,19 +77,30 @@ export function useRoomRealtime(
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
const stopWatchingVersion = watch(
|
||||
stateVersion,
|
||||
(version) => {
|
||||
if (!reloadInProgress) {
|
||||
pendingVersion = Math.max(pendingVersion, version);
|
||||
}
|
||||
const stopWatchingInitialState = watch(
|
||||
[initialRoom, initialCurrentPlayer],
|
||||
([nextRoom, nextCurrentPlayer]) => {
|
||||
room.value = { ...nextRoom };
|
||||
currentPlayer.value = { ...nextCurrentPlayer };
|
||||
},
|
||||
{ flush: 'sync' },
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
onScopeDispose(() => {
|
||||
disposed = true;
|
||||
stopWatchingRoom();
|
||||
stopWatchingVersion();
|
||||
stopWatchingInitialState();
|
||||
});
|
||||
|
||||
function setCurrentVote(value: string | null): void {
|
||||
currentPlayer.value = {
|
||||
...currentPlayer.value,
|
||||
current_vote: value,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
room: shallowReadonly(room),
|
||||
currentPlayer: shallowReadonly(currentPlayer),
|
||||
setCurrentVote,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user