import { handleActiveSpeakerChange, handleDisconnect, handleLocalTrackUnpublished, handleTrackSubscribed, handleTrackUnsubscribed, } from "helpers/liveKitEventHandler"; import { ConnectionQuality, Room, RoomEvent } from "livekit-client"; import { create } from "zustand"; let interval: NodeJS.Timeout; type TalkState = { isTalking: boolean; state: "connecting" | "connected" | "disconnected"; connectionQuality: ConnectionQuality; remoteParticipants: number; toggleTalking: () => void; connect: (roomName: string) => void; disconnect: () => void; room: Room | null; }; const getToken = async (roomName: string) => { const response = await fetch( `${process.env.NEXT_PUBLIC_DISPATCH_SERVER_URL}/livekit/token?roomName=${roomName}`, ); const data = await response.json(); return data.token; }; export const useAudioStore = create((set, get) => ({ isTalking: false, state: "disconnected", remoteParticipants: 0, connectionQuality: ConnectionQuality.Unknown, room: null, toggleTalking: () => set((state) => ({ isTalking: !state.isTalking })), connect: async (roomName) => { const connectedRoom = get().room; if (interval) clearInterval(interval); if (connectedRoom) { connectedRoom.disconnect(); connectedRoom.removeAllListeners(); } set({ state: "connecting" }); const url = process.env.NEXT_PUBLIC_LIVEKIT_URL; if (!url) return console.error("NEXT_PUBLIC_LIVEKIT_URL not set"); const token = await getToken(roomName); const room = new Room(); await room.prepareConnection(url, token); room // Connection events .on(RoomEvent.Connected, () => { set({ state: "connected", room }); }) .on(RoomEvent.Disconnected, () => { set({ state: "disconnected" }); handleDisconnect(); }) .on(RoomEvent.ConnectionQualityChanged, (connectionQuality) => set({ connectionQuality }), ) // Track events .on(RoomEvent.TrackSubscribed, handleTrackSubscribed) .on(RoomEvent.TrackUnsubscribed, handleTrackUnsubscribed) .on(RoomEvent.ActiveSpeakersChanged, handleActiveSpeakerChange) .on(RoomEvent.LocalTrackUnpublished, handleLocalTrackUnpublished); await room.connect(url, token); interval = setInterval(() => { set({ remoteParticipants: room.numParticipants === 0 ? 0 : room.numParticipants - 1, // Unreliable and delayed }); }, 500); }, disconnect: () => { get().room?.disconnect(); }, }));