56 lines
2.0 KiB
TypeScript
56 lines
2.0 KiB
TypeScript
import { useEffect } from 'react';
|
|
import { useQueryClient } from '@tanstack/react-query';
|
|
import { io, type Socket } from 'socket.io-client';
|
|
import { API_URL } from '../api/client';
|
|
import { useAuth } from '../auth/AuthContext';
|
|
|
|
let sharedSocket: Socket | null = null;
|
|
|
|
function getSocket(token: string): Socket {
|
|
if (!sharedSocket) {
|
|
sharedSocket = io(API_URL, { auth: { token }, transports: ['websocket', 'polling'] });
|
|
}
|
|
return sharedSocket;
|
|
}
|
|
|
|
// Joins the Socket.io room for a campaign and keeps React Query's cache in
|
|
// sync with server-pushed events (HP/condition changes, encounter rounds,
|
|
// new characters) so every open tab in the campaign updates live.
|
|
export function useCampaignSocket(campaignId: string | undefined) {
|
|
const { token } = useAuth();
|
|
const qc = useQueryClient();
|
|
|
|
useEffect(() => {
|
|
if (!campaignId || !token) return;
|
|
const socket = getSocket(token);
|
|
socket.emit('campaign:join', campaignId);
|
|
|
|
const onCharacterUpdated = () => {
|
|
qc.invalidateQueries({ queryKey: ['campaign', campaignId, 'characters'] });
|
|
qc.invalidateQueries({ queryKey: ['character'] });
|
|
};
|
|
const onCharacterCreated = () => {
|
|
qc.invalidateQueries({ queryKey: ['campaign', campaignId, 'characters'] });
|
|
};
|
|
const onParticipantUpdated = () => {
|
|
qc.invalidateQueries({ queryKey: ['encounter'] });
|
|
};
|
|
const onRoundAdvanced = () => {
|
|
qc.invalidateQueries({ queryKey: ['encounter'] });
|
|
};
|
|
|
|
socket.on('character:updated', onCharacterUpdated);
|
|
socket.on('character:created', onCharacterCreated);
|
|
socket.on('encounter:participant-updated', onParticipantUpdated);
|
|
socket.on('encounter:round-advanced', onRoundAdvanced);
|
|
|
|
return () => {
|
|
socket.emit('campaign:leave', campaignId);
|
|
socket.off('character:updated', onCharacterUpdated);
|
|
socket.off('character:created', onCharacterCreated);
|
|
socket.off('encounter:participant-updated', onParticipantUpdated);
|
|
socket.off('encounter:round-advanced', onRoundAdvanced);
|
|
};
|
|
}, [campaignId, token, qc]);
|
|
}
|