import { serverApi } from "helpers/axios"; 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" | "error"; message: string | null; connectionQuality: ConnectionQuality; remoteParticipants: number; toggleTalking: () => void; connect: (roomName: string) => void; disconnect: () => void; room: Room | null; }; const getToken = async (roomName: string) => { const response = await serverApi.get(`/livekit/token?roomName=${roomName}`); const data = response.data; return data.token; }; export const useAudioStore = create((set, get) => ({ isTalking: false, message: null, state: "disconnected", remoteParticipants: 0, connectionQuality: ConnectionQuality.Unknown, room: null, toggleTalking: () => set((state) => ({ isTalking: !state.isTalking })), connect: async (roomName) => { set({ state: "connecting" }); console.log("Connecting to room: ", roomName); try { // Clean old room const connectedRoom = get().room; if (interval) clearInterval(interval); if (connectedRoom) { connectedRoom.disconnect(); connectedRoom.removeAllListeners(); } const url = process.env.NEXT_PUBLIC_LIVEKIT_URL; if (!url) return console.error("NEXT_PUBLIC_LIVEKIT_URL not set"); const token = await getToken(roomName); if (!token) throw new Error("Fehlende Berechtigung"); const room = new Room({}); await room.prepareConnection(url, token); room // Connection events .on(RoomEvent.Connected, () => { set({ state: "connected", room, message: null }); }) .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); console.log(room); set({ room }); interval = setInterval(() => { set({ remoteParticipants: room.numParticipants === 0 ? 0 : room.numParticipants - 1, // Unreliable and delayed }); }, 500); } catch (error: Error | unknown) { console.error("Error occured: ", error); if (error instanceof Error) { set({ state: "error", message: error.message }); } else { set({ state: "error", message: "Unknown error" }); } } }, disconnect: () => { get().room?.disconnect(); }, }));