import { BehaviorSubject, Observable } from 'rxjs'; import type { Patch } from './rxUtils'; import * as RxUtils from './rxUtils'; import { Call } from '../Call'; import { CallingState } from './CallingState'; import type { OwnUserResponse } from '../gen/coordinator'; import { videoLoggerSystem } from '../logger'; export class StreamVideoWriteableStateStore { /** * A store keeping data of a successfully connected user over WS to the coordinator server. */ connectedUserSubject = new BehaviorSubject( undefined, ); /** * A list of {@link Call} objects created/tracked by this client. */ callsSubject = new BehaviorSubject([]); constructor() { this.connectedUserSubject.subscribe(async (user) => { // leave all calls when the user disconnects. if (!user) { const logger = videoLoggerSystem.getLogger('client-state'); for (const call of this.calls) { if (call.state.callingState === CallingState.LEFT) continue; logger.info(`User disconnected, leaving call: ${call.cid}`); await call .leave({ message: 'client.disconnectUser() called' }) .catch((err) => { logger.error(`Error leaving call: ${call.cid}`, err); }); } } }); } /** * The currently connected user. */ get connectedUser(): OwnUserResponse | undefined { return this.connectedUserSubject.getValue(); } /** * Sets the currently connected user. * * @internal * @param user the user to set as connected. */ setConnectedUser = (user: Patch) => { return RxUtils.setCurrentValue(this.connectedUserSubject, user); }; /** * A list of {@link Call} objects created/tracked by this client. */ get calls(): Call[] { return RxUtils.getCurrentValue(this.callsSubject); } /** * Sets the list of {@link Call} objects created/tracked by this client. * @param calls */ setCalls = (calls: Patch) => { return RxUtils.setCurrentValue(this.callsSubject, calls); }; /** * Adds a {@link Call} object to the list of {@link Call} objects created/tracked by this client. * * @param call the call to add. */ registerCall = (call: Call) => { if (!this.calls.find((c) => c.cid === call.cid)) { this.setCalls((calls) => [...calls, call]); } }; /** * Registers a {@link Call} object if it doesn't exist, otherwise updates it. * * @param call the call to register or update. */ registerOrUpdateCall = (call: Call) => { if (this.calls.find((c) => c.cid === call.cid)) { return this.setCalls((calls) => calls.map((c) => (c.cid === call.cid ? call : c)), ); } else { return this.registerCall(call); } }; /** * Removes a {@link Call} object from the list of {@link Call} objects created/tracked by this client. * * @param call the call to remove */ unregisterCall = (call: Call) => { const logger = videoLoggerSystem.getLogger('client-state'); logger.trace(`Unregistering call: ${call.cid}`); return this.setCalls((calls) => calls.filter((c) => c !== call)); }; /** * Finds a {@link Call} object in the list of {@link Call} objects created/tracked by this client. * * @param type the type of call to find. * @param id the id of the call to find. */ findCall = (type: string, id: string) => { return this.calls.find((c) => c.type === type && c.id === id); }; } /** * A reactive store that exposes state variables in a reactive manner. * You can subscribe to changes of the different state variables. * This central store contains all the state variables related to [`StreamVideoClient`](./StreamVideClient.md) and [`Call`](./Call.md). */ export class StreamVideoReadOnlyStateStore { /** * Data describing a user successfully connected over WS to coordinator server. */ connectedUser$: Observable; /** * A list of {@link Call} objects created/tracked by this client. */ calls$: Observable; constructor(store: StreamVideoWriteableStateStore) { // convert and expose subjects as observables this.connectedUser$ = store.connectedUserSubject.asObservable(); this.calls$ = store.callsSubject.asObservable(); } /** * The current user connected over WS to the backend. */ get connectedUser(): OwnUserResponse | undefined { return RxUtils.getCurrentValue(this.connectedUser$); } /** * A list of {@link Call} objects created/tracked by this client. */ get calls(): Call[] { return RxUtils.getCurrentValue(this.calls$); } }