@connectycube/use-calls
Version:
A React hook for state management in ConnectyCube-powered audio/video calls solutions
1 lines • 64.3 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","sources":["../node_modules/zustand/esm/vanilla.mjs","../node_modules/zustand/esm/react.mjs","../node_modules/zustand/esm/middleware.mjs","../src/hooks/useCallsStore.ts","../node_modules/zustand/esm/vanilla/shallow.mjs","../node_modules/zustand/esm/react/shallow.mjs","../src/helpers.ts","../src/hooks/useCallsStoreRef.ts","../src/hooks/useCallsMediaManager.ts","../src/components/local-stream.tsx","../src/components/remote-stream.tsx","../src/hooks/useCalls.ts","../src/hooks/useCallsListeners.ts"],"sourcesContent":["const createStoreImpl = (createState) => {\n let state;\n const listeners = /* @__PURE__ */ new Set();\n const setState = (partial, replace) => {\n const nextState = typeof partial === \"function\" ? partial(state) : partial;\n if (!Object.is(nextState, state)) {\n const previousState = state;\n state = (replace != null ? replace : typeof nextState !== \"object\" || nextState === null) ? nextState : Object.assign({}, state, nextState);\n listeners.forEach((listener) => listener(state, previousState));\n }\n };\n const getState = () => state;\n const getInitialState = () => initialState;\n const subscribe = (listener) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n };\n const api = { setState, getState, getInitialState, subscribe };\n const initialState = state = createState(setState, getState, api);\n return api;\n};\nconst createStore = (createState) => createState ? createStoreImpl(createState) : createStoreImpl;\n\nexport { createStore };\n","import React from 'react';\nimport { createStore } from 'zustand/vanilla';\n\nconst identity = (arg) => arg;\nfunction useStore(api, selector = identity) {\n const slice = React.useSyncExternalStore(\n api.subscribe,\n () => selector(api.getState()),\n () => selector(api.getInitialState())\n );\n React.useDebugValue(slice);\n return slice;\n}\nconst createImpl = (createState) => {\n const api = createStore(createState);\n const useBoundStore = (selector) => useStore(api, selector);\n Object.assign(useBoundStore, api);\n return useBoundStore;\n};\nconst create = (createState) => createState ? createImpl(createState) : createImpl;\n\nexport { create, useStore };\n","const reduxImpl = (reducer, initial) => (set, _get, api) => {\n api.dispatch = (action) => {\n set((state) => reducer(state, action), false, action);\n return action;\n };\n api.dispatchFromDevtools = true;\n return { dispatch: (...args) => api.dispatch(...args), ...initial };\n};\nconst redux = reduxImpl;\n\nconst trackedConnections = /* @__PURE__ */ new Map();\nconst getTrackedConnectionState = (name) => {\n const api = trackedConnections.get(name);\n if (!api) return {};\n return Object.fromEntries(\n Object.entries(api.stores).map(([key, api2]) => [key, api2.getState()])\n );\n};\nconst extractConnectionInformation = (store, extensionConnector, options) => {\n if (store === void 0) {\n return {\n type: \"untracked\",\n connection: extensionConnector.connect(options)\n };\n }\n const existingConnection = trackedConnections.get(options.name);\n if (existingConnection) {\n return { type: \"tracked\", store, ...existingConnection };\n }\n const newConnection = {\n connection: extensionConnector.connect(options),\n stores: {}\n };\n trackedConnections.set(options.name, newConnection);\n return { type: \"tracked\", store, ...newConnection };\n};\nconst removeStoreFromTrackedConnections = (name, store) => {\n if (store === void 0) return;\n const connectionInfo = trackedConnections.get(name);\n if (!connectionInfo) return;\n delete connectionInfo.stores[store];\n if (Object.keys(connectionInfo.stores).length === 0) {\n trackedConnections.delete(name);\n }\n};\nconst findCallerName = (stack) => {\n var _a, _b;\n if (!stack) return void 0;\n const traceLines = stack.split(\"\\n\");\n const apiSetStateLineIndex = traceLines.findIndex(\n (traceLine) => traceLine.includes(\"api.setState\")\n );\n if (apiSetStateLineIndex < 0) return void 0;\n const callerLine = ((_a = traceLines[apiSetStateLineIndex + 1]) == null ? void 0 : _a.trim()) || \"\";\n return (_b = /.+ (.+) .+/.exec(callerLine)) == null ? void 0 : _b[1];\n};\nconst devtoolsImpl = (fn, devtoolsOptions = {}) => (set, get, api) => {\n const { enabled, anonymousActionType, store, ...options } = devtoolsOptions;\n let extensionConnector;\n try {\n extensionConnector = (enabled != null ? enabled : (import.meta.env ? import.meta.env.MODE : void 0) !== \"production\") && window.__REDUX_DEVTOOLS_EXTENSION__;\n } catch (e) {\n }\n if (!extensionConnector) {\n return fn(set, get, api);\n }\n const { connection, ...connectionInformation } = extractConnectionInformation(store, extensionConnector, options);\n let isRecording = true;\n api.setState = (state, replace, nameOrAction) => {\n const r = set(state, replace);\n if (!isRecording) return r;\n const action = nameOrAction === void 0 ? {\n type: anonymousActionType || findCallerName(new Error().stack) || \"anonymous\"\n } : typeof nameOrAction === \"string\" ? { type: nameOrAction } : nameOrAction;\n if (store === void 0) {\n connection == null ? void 0 : connection.send(action, get());\n return r;\n }\n connection == null ? void 0 : connection.send(\n {\n ...action,\n type: `${store}/${action.type}`\n },\n {\n ...getTrackedConnectionState(options.name),\n [store]: api.getState()\n }\n );\n return r;\n };\n api.devtools = {\n cleanup: () => {\n if (connection && typeof connection.unsubscribe === \"function\") {\n connection.unsubscribe();\n }\n removeStoreFromTrackedConnections(options.name, store);\n }\n };\n const setStateFromDevtools = (...a) => {\n const originalIsRecording = isRecording;\n isRecording = false;\n set(...a);\n isRecording = originalIsRecording;\n };\n const initialState = fn(api.setState, get, api);\n if (connectionInformation.type === \"untracked\") {\n connection == null ? void 0 : connection.init(initialState);\n } else {\n connectionInformation.stores[connectionInformation.store] = api;\n connection == null ? void 0 : connection.init(\n Object.fromEntries(\n Object.entries(connectionInformation.stores).map(([key, store2]) => [\n key,\n key === connectionInformation.store ? initialState : store2.getState()\n ])\n )\n );\n }\n if (api.dispatchFromDevtools && typeof api.dispatch === \"function\") {\n let didWarnAboutReservedActionType = false;\n const originalDispatch = api.dispatch;\n api.dispatch = (...args) => {\n if ((import.meta.env ? import.meta.env.MODE : void 0) !== \"production\" && args[0].type === \"__setState\" && !didWarnAboutReservedActionType) {\n console.warn(\n '[zustand devtools middleware] \"__setState\" action type is reserved to set state from the devtools. Avoid using it.'\n );\n didWarnAboutReservedActionType = true;\n }\n originalDispatch(...args);\n };\n }\n connection.subscribe((message) => {\n var _a;\n switch (message.type) {\n case \"ACTION\":\n if (typeof message.payload !== \"string\") {\n console.error(\n \"[zustand devtools middleware] Unsupported action format\"\n );\n return;\n }\n return parseJsonThen(\n message.payload,\n (action) => {\n if (action.type === \"__setState\") {\n if (store === void 0) {\n setStateFromDevtools(action.state);\n return;\n }\n if (Object.keys(action.state).length !== 1) {\n console.error(\n `\n [zustand devtools middleware] Unsupported __setState action format.\n When using 'store' option in devtools(), the 'state' should have only one key, which is a value of 'store' that was passed in devtools(),\n and value of this only key should be a state object. Example: { \"type\": \"__setState\", \"state\": { \"abc123Store\": { \"foo\": \"bar\" } } }\n `\n );\n }\n const stateFromDevtools = action.state[store];\n if (stateFromDevtools === void 0 || stateFromDevtools === null) {\n return;\n }\n if (JSON.stringify(api.getState()) !== JSON.stringify(stateFromDevtools)) {\n setStateFromDevtools(stateFromDevtools);\n }\n return;\n }\n if (!api.dispatchFromDevtools) return;\n if (typeof api.dispatch !== \"function\") return;\n api.dispatch(action);\n }\n );\n case \"DISPATCH\":\n switch (message.payload.type) {\n case \"RESET\":\n setStateFromDevtools(initialState);\n if (store === void 0) {\n return connection == null ? void 0 : connection.init(api.getState());\n }\n return connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name));\n case \"COMMIT\":\n if (store === void 0) {\n connection == null ? void 0 : connection.init(api.getState());\n return;\n }\n return connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name));\n case \"ROLLBACK\":\n return parseJsonThen(message.state, (state) => {\n if (store === void 0) {\n setStateFromDevtools(state);\n connection == null ? void 0 : connection.init(api.getState());\n return;\n }\n setStateFromDevtools(state[store]);\n connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name));\n });\n case \"JUMP_TO_STATE\":\n case \"JUMP_TO_ACTION\":\n return parseJsonThen(message.state, (state) => {\n if (store === void 0) {\n setStateFromDevtools(state);\n return;\n }\n if (JSON.stringify(api.getState()) !== JSON.stringify(state[store])) {\n setStateFromDevtools(state[store]);\n }\n });\n case \"IMPORT_STATE\": {\n const { nextLiftedState } = message.payload;\n const lastComputedState = (_a = nextLiftedState.computedStates.slice(-1)[0]) == null ? void 0 : _a.state;\n if (!lastComputedState) return;\n if (store === void 0) {\n setStateFromDevtools(lastComputedState);\n } else {\n setStateFromDevtools(lastComputedState[store]);\n }\n connection == null ? void 0 : connection.send(\n null,\n // FIXME no-any\n nextLiftedState\n );\n return;\n }\n case \"PAUSE_RECORDING\":\n return isRecording = !isRecording;\n }\n return;\n }\n });\n return initialState;\n};\nconst devtools = devtoolsImpl;\nconst parseJsonThen = (stringified, fn) => {\n let parsed;\n try {\n parsed = JSON.parse(stringified);\n } catch (e) {\n console.error(\n \"[zustand devtools middleware] Could not parse the received json\",\n e\n );\n }\n if (parsed !== void 0) fn(parsed);\n};\n\nconst subscribeWithSelectorImpl = (fn) => (set, get, api) => {\n const origSubscribe = api.subscribe;\n api.subscribe = (selector, optListener, options) => {\n let listener = selector;\n if (optListener) {\n const equalityFn = (options == null ? void 0 : options.equalityFn) || Object.is;\n let currentSlice = selector(api.getState());\n listener = (state) => {\n const nextSlice = selector(state);\n if (!equalityFn(currentSlice, nextSlice)) {\n const previousSlice = currentSlice;\n optListener(currentSlice = nextSlice, previousSlice);\n }\n };\n if (options == null ? void 0 : options.fireImmediately) {\n optListener(currentSlice, currentSlice);\n }\n }\n return origSubscribe(listener);\n };\n const initialState = fn(set, get, api);\n return initialState;\n};\nconst subscribeWithSelector = subscribeWithSelectorImpl;\n\nfunction combine(initialState, create) {\n return (...args) => Object.assign({}, initialState, create(...args));\n}\n\nfunction createJSONStorage(getStorage, options) {\n let storage;\n try {\n storage = getStorage();\n } catch (e) {\n return;\n }\n const persistStorage = {\n getItem: (name) => {\n var _a;\n const parse = (str2) => {\n if (str2 === null) {\n return null;\n }\n return JSON.parse(str2, options == null ? void 0 : options.reviver);\n };\n const str = (_a = storage.getItem(name)) != null ? _a : null;\n if (str instanceof Promise) {\n return str.then(parse);\n }\n return parse(str);\n },\n setItem: (name, newValue) => storage.setItem(name, JSON.stringify(newValue, options == null ? void 0 : options.replacer)),\n removeItem: (name) => storage.removeItem(name)\n };\n return persistStorage;\n}\nconst toThenable = (fn) => (input) => {\n try {\n const result = fn(input);\n if (result instanceof Promise) {\n return result;\n }\n return {\n then(onFulfilled) {\n return toThenable(onFulfilled)(result);\n },\n catch(_onRejected) {\n return this;\n }\n };\n } catch (e) {\n return {\n then(_onFulfilled) {\n return this;\n },\n catch(onRejected) {\n return toThenable(onRejected)(e);\n }\n };\n }\n};\nconst persistImpl = (config, baseOptions) => (set, get, api) => {\n let options = {\n storage: createJSONStorage(() => localStorage),\n partialize: (state) => state,\n version: 0,\n merge: (persistedState, currentState) => ({\n ...currentState,\n ...persistedState\n }),\n ...baseOptions\n };\n let hasHydrated = false;\n const hydrationListeners = /* @__PURE__ */ new Set();\n const finishHydrationListeners = /* @__PURE__ */ new Set();\n let storage = options.storage;\n if (!storage) {\n return config(\n (...args) => {\n console.warn(\n `[zustand persist middleware] Unable to update item '${options.name}', the given storage is currently unavailable.`\n );\n set(...args);\n },\n get,\n api\n );\n }\n const setItem = () => {\n const state = options.partialize({ ...get() });\n return storage.setItem(options.name, {\n state,\n version: options.version\n });\n };\n const savedSetState = api.setState;\n api.setState = (state, replace) => {\n savedSetState(state, replace);\n void setItem();\n };\n const configResult = config(\n (...args) => {\n set(...args);\n void setItem();\n },\n get,\n api\n );\n api.getInitialState = () => configResult;\n let stateFromStorage;\n const hydrate = () => {\n var _a, _b;\n if (!storage) return;\n hasHydrated = false;\n hydrationListeners.forEach((cb) => {\n var _a2;\n return cb((_a2 = get()) != null ? _a2 : configResult);\n });\n const postRehydrationCallback = ((_b = options.onRehydrateStorage) == null ? void 0 : _b.call(options, (_a = get()) != null ? _a : configResult)) || void 0;\n return toThenable(storage.getItem.bind(storage))(options.name).then((deserializedStorageValue) => {\n if (deserializedStorageValue) {\n if (typeof deserializedStorageValue.version === \"number\" && deserializedStorageValue.version !== options.version) {\n if (options.migrate) {\n const migration = options.migrate(\n deserializedStorageValue.state,\n deserializedStorageValue.version\n );\n if (migration instanceof Promise) {\n return migration.then((result) => [true, result]);\n }\n return [true, migration];\n }\n console.error(\n `State loaded from storage couldn't be migrated since no migrate function was provided`\n );\n } else {\n return [false, deserializedStorageValue.state];\n }\n }\n return [false, void 0];\n }).then((migrationResult) => {\n var _a2;\n const [migrated, migratedState] = migrationResult;\n stateFromStorage = options.merge(\n migratedState,\n (_a2 = get()) != null ? _a2 : configResult\n );\n set(stateFromStorage, true);\n if (migrated) {\n return setItem();\n }\n }).then(() => {\n postRehydrationCallback == null ? void 0 : postRehydrationCallback(stateFromStorage, void 0);\n stateFromStorage = get();\n hasHydrated = true;\n finishHydrationListeners.forEach((cb) => cb(stateFromStorage));\n }).catch((e) => {\n postRehydrationCallback == null ? void 0 : postRehydrationCallback(void 0, e);\n });\n };\n api.persist = {\n setOptions: (newOptions) => {\n options = {\n ...options,\n ...newOptions\n };\n if (newOptions.storage) {\n storage = newOptions.storage;\n }\n },\n clearStorage: () => {\n storage == null ? void 0 : storage.removeItem(options.name);\n },\n getOptions: () => options,\n rehydrate: () => hydrate(),\n hasHydrated: () => hasHydrated,\n onHydrate: (cb) => {\n hydrationListeners.add(cb);\n return () => {\n hydrationListeners.delete(cb);\n };\n },\n onFinishHydration: (cb) => {\n finishHydrationListeners.add(cb);\n return () => {\n finishHydrationListeners.delete(cb);\n };\n }\n };\n if (!options.skipHydration) {\n hydrate();\n }\n return stateFromStorage || configResult;\n};\nconst persist = persistImpl;\n\nexport { combine, createJSONStorage, devtools, persist, redux, subscribeWithSelector };\n","import { create } from \"zustand\";\nimport { subscribeWithSelector } from \"zustand/middleware\";\nimport { Calls, CallSessionConnectionState, Media } from \"connectycube/types\";\n\nexport enum ActiveMedia {\n USER = 0,\n DISPLAY = 1,\n}\n\nexport enum CallDataType {\n ON_CALL = \"onCall\",\n ON_STOP_CALLING = \"onStopCalling\",\n ON_ACCEPT = \"onAccept\",\n ON_REJECT = \"onReject\",\n ON_STOP = \"onStop\",\n ON_NOT_ANSWER = \"onNotAnswer\",\n ON_CONNECTION_STATE = \"onConnectionState\",\n}\n\nexport type CallData = {\n userID?: number;\n userInfo?: Calls.UserInfo;\n connectionState?: CallSessionConnectionState;\n};\n\nexport interface CallsStoreSessionState {\n activeCall: Calls.Session | null;\n incomingCall: Calls.Session | null;\n}\nexport interface CallsStoreSessionDataState {\n deviceChangeEvent: Event | null;\n incomingCallCallData: CallData | null;\n incomingCallStopData: CallData | null;\n activeCallAcceptData: CallData | null;\n activeCallRejectData: CallData | null;\n activeCallStopData: CallData | null;\n activeCallNotAnswerData: CallData | null;\n activeCallConnectionStateData: CallData | null;\n}\nexport interface CallsStoreMediaState {\n localStream: MediaStream | null;\n remoteStreams: { [key: number]: MediaStream };\n userMediaParams: Media.UserParams;\n displayMediaParams: Media.DisplayParams;\n activeMedia: ActiveMedia;\n}\nexport interface CallsStoreState extends CallsStoreSessionState, CallsStoreSessionDataState, CallsStoreMediaState {\n participantsIDs: Set<number>;\n hasActiveListeners: boolean;\n}\n\nexport interface CallsStoreSessionActions {\n setActiveCall: (session: Calls.Session) => void;\n resetActiveCall: () => void;\n setIncomingCall: (session: Calls.Session) => void;\n resetIncomingCall: () => void;\n}\nexport interface CallsStoreSessionDataActions {\n setDeviceChangeEvent: (deviceChangeEvent: Event) => void;\n upsertSessionData: (type: CallDataType, data: CallData) => void;\n resetSessionData: (type?: CallDataType) => void;\n}\nexport interface CallsStoreMediaActions {\n setLocalStream: (stream: MediaStream) => void;\n resetLocalStream: () => void;\n addRemoteStream: (key: number, stream: MediaStream) => void;\n removeRemoteStream: (key: number) => void;\n resetRemoteStreams: () => void;\n saveUserMediaParams: (params: Media.UserParams) => void;\n resetUserMediaParams: () => void;\n saveDisplayMediaParams: (params: Media.DisplayParams) => void;\n resetDisplayMediaParams: () => void;\n saveActiveMedia: (activeMedia: ActiveMedia) => void;\n}\nexport interface CallsStoreActions\n extends CallsStoreSessionActions,\n CallsStoreSessionDataActions,\n CallsStoreMediaActions {\n setParticipantsIDs: (participantsIDs: number[]) => void;\n participantDidEnter: (id: number) => void;\n participantDidLeave: (id: number) => void;\n activateCallsListeners: () => void;\n resetStore: () => void;\n}\n\ninterface CallsStore extends CallsStoreState, CallsStoreActions {}\n\nconst initialSessionState = {\n activeCall: null,\n incomingCall: null,\n};\nconst initialSessionDataState = {\n incomingCallCallData: null,\n incomingCallStopData: null,\n activeCallAcceptData: null,\n activeCallRejectData: null,\n activeCallStopData: null,\n activeCallNotAnswerData: null,\n activeCallConnectionStateData: null,\n deviceChangeEvent: null,\n};\nconst initialMediaState = {\n localStream: null,\n remoteStreams: {},\n userMediaParams: { video: true, audio: true },\n displayMediaParams: { video: true, audio: false },\n activeMedia: ActiveMedia.USER,\n};\nconst initialState: CallsStoreState = {\n ...initialSessionState,\n ...initialSessionDataState,\n ...initialMediaState,\n participantsIDs: new Set(),\n hasActiveListeners: false,\n};\n\nconst useCallsStore = create<CallsStore>()(\n subscribeWithSelector((set, get) => ({\n ...initialState,\n // active call session\n setActiveCall: (activeCall?: Calls.Session) => set({ activeCall }),\n resetActiveCall: () => set({ activeCall: initialState.activeCall }),\n // incoming call session (waiting)\n setIncomingCall: (incomingCall?: Calls.Session) => set({ incomingCall }),\n resetIncomingCall: () => set({ incomingCall: initialState.incomingCall }),\n // data from listeners\n setDeviceChangeEvent: (deviceChangeEvent: Event) => set({ deviceChangeEvent }),\n upsertSessionData: (type: CallDataType, data: CallData) => {\n switch (type) {\n case CallDataType.ON_CALL:\n set({ incomingCallCallData: data });\n break;\n case CallDataType.ON_STOP_CALLING:\n set({ incomingCallStopData: data });\n break;\n case CallDataType.ON_ACCEPT:\n set({ activeCallAcceptData: data });\n break;\n case CallDataType.ON_REJECT:\n set({ activeCallRejectData: data });\n break;\n case CallDataType.ON_STOP:\n set({ activeCallStopData: data });\n break;\n case CallDataType.ON_NOT_ANSWER:\n set({ activeCallNotAnswerData: data });\n break;\n case CallDataType.ON_CONNECTION_STATE:\n set({ activeCallConnectionStateData: data });\n break;\n default:\n break;\n }\n },\n resetSessionData: (type?: CallDataType) => {\n switch (type) {\n case CallDataType.ON_CALL:\n set({ incomingCallCallData: initialSessionDataState.incomingCallCallData });\n break;\n case CallDataType.ON_STOP_CALLING:\n set({ incomingCallStopData: initialSessionDataState.incomingCallStopData });\n break;\n case CallDataType.ON_ACCEPT:\n set({ activeCallAcceptData: initialSessionDataState.activeCallAcceptData });\n break;\n case CallDataType.ON_REJECT:\n set({ activeCallRejectData: initialSessionDataState.activeCallRejectData });\n break;\n case CallDataType.ON_STOP:\n set({ activeCallStopData: initialSessionDataState.activeCallStopData });\n break;\n case CallDataType.ON_NOT_ANSWER:\n set({ activeCallNotAnswerData: initialSessionDataState.activeCallNotAnswerData });\n break;\n case CallDataType.ON_CONNECTION_STATE:\n set({ activeCallConnectionStateData: initialSessionDataState.activeCallConnectionStateData });\n break;\n default:\n set(initialSessionDataState);\n break;\n }\n },\n // local stream\n setLocalStream: (localStream: MediaStream) => set({ localStream }),\n resetLocalStream: () => set({ localStream: initialState.localStream }),\n // remote stream\n addRemoteStream: (key: number, stream: MediaStream) =>\n set({ remoteStreams: { ...get().remoteStreams, [key]: stream } }),\n removeRemoteStream: (key: number) => {\n const remoteStreams = { ...get().remoteStreams };\n delete remoteStreams[key];\n set({ remoteStreams });\n },\n resetRemoteStreams: () => set({ remoteStreams: initialState.remoteStreams }),\n // getUserMedia/getDisplayMedia params controls\n saveUserMediaParams: (userMediaParams: Media.UserParams) => set({ userMediaParams }),\n resetUserMediaParams: () => set({ userMediaParams: initialState.userMediaParams }),\n saveDisplayMediaParams: (displayMediaParams: Media.DisplayParams) => set({ displayMediaParams }),\n resetDisplayMediaParams: () => set({ displayMediaParams: initialState.displayMediaParams }),\n saveActiveMedia: (activeMedia: ActiveMedia) => set({ activeMedia }),\n // pending/active call users ids\n setParticipantsIDs: (participantsIDs: number[]) => set({ participantsIDs: new Set(participantsIDs) }),\n participantDidEnter: (id: number) => {\n const participantsIDs = new Set([...get().participantsIDs]);\n participantsIDs.add(id);\n set({ participantsIDs });\n },\n participantDidLeave: (id: number) => {\n const participantsIDs = new Set([...get().participantsIDs]);\n participantsIDs.delete(id);\n set({ participantsIDs });\n },\n // setup calls listeners\n activateCallsListeners: () => set({ hasActiveListeners: true }),\n // clean store\n resetStore: () => set({ ...initialState, hasActiveListeners: get().hasActiveListeners }),\n })),\n);\n\nexport default useCallsStore;\n","const isIterable = (obj) => Symbol.iterator in obj;\nconst hasIterableEntries = (value) => (\n // HACK: avoid checking entries type\n \"entries\" in value\n);\nconst compareEntries = (valueA, valueB) => {\n const mapA = valueA instanceof Map ? valueA : new Map(valueA.entries());\n const mapB = valueB instanceof Map ? valueB : new Map(valueB.entries());\n if (mapA.size !== mapB.size) {\n return false;\n }\n for (const [key, value] of mapA) {\n if (!Object.is(value, mapB.get(key))) {\n return false;\n }\n }\n return true;\n};\nconst compareIterables = (valueA, valueB) => {\n const iteratorA = valueA[Symbol.iterator]();\n const iteratorB = valueB[Symbol.iterator]();\n let nextA = iteratorA.next();\n let nextB = iteratorB.next();\n while (!nextA.done && !nextB.done) {\n if (!Object.is(nextA.value, nextB.value)) {\n return false;\n }\n nextA = iteratorA.next();\n nextB = iteratorB.next();\n }\n return !!nextA.done && !!nextB.done;\n};\nfunction shallow(valueA, valueB) {\n if (Object.is(valueA, valueB)) {\n return true;\n }\n if (typeof valueA !== \"object\" || valueA === null || typeof valueB !== \"object\" || valueB === null) {\n return false;\n }\n if (Object.getPrototypeOf(valueA) !== Object.getPrototypeOf(valueB)) {\n return false;\n }\n if (isIterable(valueA) && isIterable(valueB)) {\n if (hasIterableEntries(valueA) && hasIterableEntries(valueB)) {\n return compareEntries(valueA, valueB);\n }\n return compareIterables(valueA, valueB);\n }\n return compareEntries(\n { entries: () => Object.entries(valueA) },\n { entries: () => Object.entries(valueB) }\n );\n}\n\nexport { shallow };\n","import React from 'react';\nimport { shallow } from 'zustand/vanilla/shallow';\n\nfunction useShallow(selector) {\n const prev = React.useRef(void 0);\n return (state) => {\n const next = selector(state);\n return shallow(prev.current, next) ? prev.current : prev.current = next;\n };\n}\n\nexport { useShallow };\n","type Logger = (name: string, ...args: unknown[]) => void;\n\nconst USE_CALLS_NAME = \"useCalls\";\n\nexport const CallsLogger = {\n log: ((name: string, ...args: unknown[]) => {\n console.log(`[${USE_CALLS_NAME}][${name}][Log]:`, ...args);\n }) as Logger,\n info: ((name: string, ...args: unknown[]) => {\n console.info(`[${USE_CALLS_NAME}][${name}][Info]:`, ...args);\n }) as Logger,\n warn: ((name: string, ...args: unknown[]) => {\n console.warn(`[${USE_CALLS_NAME}][${name}][Warn]:`, ...args);\n }) as Logger,\n error: ((name: string, ...args: unknown[]) => {\n console.error(`[${USE_CALLS_NAME}][${name}][Error]:`, ...args);\n }) as Logger,\n};\n","import { useEffect, useRef, RefObject } from 'react';\nimport useCallsStore, { CallsStoreState } from './useCallsStore';\n\nconst useCallsStoreRef = <K extends keyof CallsStoreState>(key: K): RefObject<CallsStoreState[K]> => {\n const ref = useRef<CallsStoreState[K]>(useCallsStore.getState()[key]);\n\n useEffect(() => {\n const unsubscribe = useCallsStore.subscribe(\n (state) => state[key],\n (value) => {\n ref.current = value;\n },\n );\n return () => unsubscribe();\n }, [key]);\n\n return ref;\n};\n\nexport default useCallsStoreRef;\n","import { useCallback, useMemo, useState } from \"react\";\nimport ConnectyCube from \"connectycube\";\nimport { Media, MediaType } from \"connectycube/types\";\nimport { useShallow } from \"zustand/react/shallow\";\nimport useCallsStore, { ActiveMedia, CallsStoreMediaState } from \"./useCallsStore\";\nimport { CallsLogger } from \"../helpers\";\n\nenum MediaAction {\n MUTE_AUDIO = \"audio-mute\",\n UNMUTE_AUDIO = \"audio-unmute\",\n MUTE_VIDEO = \"video-mute\",\n UNMUTE_VIDEO = \"video-unmute\",\n}\n\nexport interface CallsMediaManager extends CallsStoreMediaState {\n isLocalAudioMuted: boolean;\n isLocalVideoMuted: boolean;\n muteAudio: () => void;\n muteVideo: () => void;\n unmuteAudio: () => void;\n unmuteVideo: () => void;\n getUserMedia: (params?: Media.UserParams) => Promise<MediaStream | void>;\n getDisplayMedia: (params?: Media.DisplayParams) => Promise<MediaStream | void>;\n getMediaDevices: (kind?: MediaDeviceKind) => Promise<MediaDeviceInfo[] | void>;\n switchMediaTracks: (options?: Media.TrackConstraintsOrDeviceIds) => Promise<MediaStream | void>;\n setUserMediaParams: (userParams?: Media.UserParams) => void;\n setDisplayMediaParams: (displayParams?: Media.DisplayParams) => void;\n setActiveMedia: (activeMedia?: ActiveMedia) => void;\n}\n\nconst useCallsMediaManager = (): CallsMediaManager => {\n const [updateKey, setUpdateKey] = useState<number>(0);\n const [\n activeCall,\n localStream,\n remoteStreams,\n userMediaParams,\n saveUserMediaParams,\n displayMediaParams,\n saveDisplayMediaParams,\n activeMedia,\n saveActiveMedia,\n ] = useCallsStore(\n useShallow((store) => [\n store.activeCall,\n store.localStream,\n store.remoteStreams,\n store.userMediaParams,\n store.saveUserMediaParams,\n store.displayMediaParams,\n store.saveDisplayMediaParams,\n store.activeMedia,\n store.saveActiveMedia,\n ]),\n );\n\n const isLocalAudioMuted = useMemo(() => {\n const audioTracks = localStream?.getAudioTracks();\n return audioTracks ? audioTracks.length === 0 || audioTracks.every((track) => !track.enabled) : true;\n }, [localStream, updateKey]);\n\n const isLocalVideoMuted = useMemo(() => {\n const videoTracks = localStream?.getVideoTracks();\n return videoTracks ? videoTracks.length === 0 || videoTracks.every((track) => !track.enabled) : true;\n }, [localStream, updateKey]);\n\n const muteAudio = () => {\n _toggleLocalStreamTracksMuteState(MediaAction.MUTE_AUDIO);\n };\n\n const muteVideo = () => {\n _toggleLocalStreamTracksMuteState(MediaAction.MUTE_VIDEO);\n };\n\n const unmuteAudio = () => {\n _toggleLocalStreamTracksMuteState(MediaAction.UNMUTE_AUDIO);\n };\n\n const unmuteVideo = () => {\n _toggleLocalStreamTracksMuteState(MediaAction.UNMUTE_VIDEO);\n };\n\n const getUserMedia = async (params?: Media.UserParams) => {\n try {\n const stream = await activeCall?.getUserMedia(params ?? userMediaParams);\n\n saveActiveMedia(ActiveMedia.USER);\n return stream;\n } catch (error) {\n CallsLogger.error(\"getUserMedia\", { error });\n }\n };\n\n const getDisplayMedia = async (params?: Media.DisplayParams) => {\n try {\n const stream = await activeCall?.getDisplayMedia(params ?? displayMediaParams);\n\n saveActiveMedia(ActiveMedia.DISPLAY);\n return stream;\n } catch (error) {\n CallsLogger.error(\"getDisplayMedia\", { error });\n }\n };\n\n const getMediaDevices = async (kind?: MediaDeviceKind) => {\n try {\n const mediaDevices = await ConnectyCube.videochat.getMediaDevices(kind);\n\n return mediaDevices;\n } catch (error) {\n CallsLogger.error(\"getMediaDevices\", { error });\n }\n };\n\n const switchMediaTracks = async (options?: Media.TrackConstraintsOrDeviceIds) => {\n try {\n const stream = await activeCall?.switchMediaTracks(options);\n\n return stream;\n } catch (error) {\n CallsLogger.error(\"switchMediaTracks\", { error });\n }\n };\n\n const setUserMediaParams = async (params?: Media.UserParams) => {\n if (params) {\n saveUserMediaParams(params);\n }\n };\n\n const setDisplayMediaParams = async (params?: Media.DisplayParams) => {\n if (params) {\n saveDisplayMediaParams(params);\n }\n };\n\n const setActiveMedia = async (activeMedia?: ActiveMedia) => {\n if (activeMedia) {\n saveActiveMedia(activeMedia);\n }\n };\n\n const _toggleLocalStreamTracksMuteState = useCallback(\n async (action: MediaAction) => {\n if (activeCall) {\n switch (action) {\n case MediaAction.MUTE_AUDIO:\n activeCall.mute(MediaType.AUDIO);\n break;\n case MediaAction.MUTE_VIDEO:\n activeCall.mute(MediaType.VIDEO);\n break;\n case MediaAction.UNMUTE_AUDIO:\n activeCall.unmute(MediaType.AUDIO);\n break;\n case MediaAction.UNMUTE_VIDEO:\n activeCall.unmute(MediaType.VIDEO);\n break;\n }\n\n setUpdateKey((prevUpdateKey) => prevUpdateKey + 1);\n }\n },\n [activeCall],\n );\n\n return {\n isLocalAudioMuted,\n isLocalVideoMuted,\n muteAudio,\n muteVideo,\n unmuteAudio,\n unmuteVideo,\n localStream,\n remoteStreams,\n getUserMedia,\n getDisplayMedia,\n getMediaDevices,\n switchMediaTracks,\n userMediaParams,\n setUserMediaParams,\n displayMediaParams,\n setDisplayMediaParams,\n activeMedia,\n setActiveMedia,\n };\n};\n\nexport default useCallsMediaManager;\n","import { useEffect, useRef } from \"react\";\nimport useCallsStore, { ActiveMedia } from \"../hooks/useCallsStore\";\nimport { useShallow } from \"zustand/shallow\";\nimport { CallsLogger } from \"../helpers\";\n\ntype LocalStreamViewProps = { mirror?: boolean } & React.VideoHTMLAttributes<HTMLVideoElement>;\n\nconst LocalStreamView: React.FC<LocalStreamViewProps> = ({ muted, mirror, style = {}, ...props }) => {\n const [localStream, userMediaParams, displayMediaParams, activeMedia] = useCallsStore(\n useShallow((store) => [store.localStream, store.userMediaParams, store.displayMediaParams, store.activeMedia]),\n );\n const videoRef = useRef<HTMLVideoElement>(null);\n const isCamera = activeMedia === ActiveMedia.USER;\n const activeMediaParams = isCamera ? userMediaParams : displayMediaParams;\n const elementId = activeMediaParams.elementId ?? \"ConnectyCubeMediaStream-local\";\n const options = activeMediaParams.options ?? {};\n const isMuted = typeof muted === \"boolean\" ? muted : typeof options.muted === \"boolean\" ? options.muted : true;\n const isMirror =\n typeof mirror === \"boolean\" ? mirror : typeof options.mirror === \"boolean\" ? options.mirror : isCamera;\n const cssProps: React.CSSProperties = {\n transform: isMirror ? \"scaleX(-1)\" : \"none\",\n objectFit: \"contain\",\n height: \"100%\",\n width: \"100%\",\n ...style,\n };\n\n useEffect(() => {\n if (videoRef.current && localStream) {\n videoRef.current.srcObject = localStream;\n videoRef.current.onloadedmetadata = () => {\n try {\n videoRef.current?.play();\n } catch (error) {\n CallsLogger.error(\"<LocalStreamView/> play()\", error);\n }\n };\n }\n }, [localStream]);\n\n return localStream ? (\n <video autoPlay playsInline ref={videoRef} id={elementId} muted={isMuted} style={cssProps} {...props} />\n ) : null;\n};\n\nexport default LocalStreamView;\n","import { useEffect, useRef } from \"react\";\nimport useCallsStore from \"../hooks/useCallsStore\";\nimport { CallsLogger } from \"../helpers\";\n\ntype RemoteStreamViewProps = { userID?: number; mirror?: boolean } & React.VideoHTMLAttributes<HTMLVideoElement>;\n\nconst RemoteStreamView: React.FC<RemoteStreamViewProps> = ({ userID, mirror = false, style = {}, ...props }) => {\n const remoteStreams = useCallsStore((store) => store.remoteStreams);\n const remoteStream = userID && remoteStreams[userID];\n const videoRef = useRef<HTMLVideoElement>(null);\n const elementId = `ConnectyCubeMediaStream-${userID}`;\n const cssProps: React.CSSProperties = {\n transform: mirror ? \"scaleX(-1)\" : \"none\",\n objectFit: \"contain\",\n height: \"100%\",\n width: \"100%\",\n ...style,\n };\n\n useEffect(() => {\n if (videoRef.current && remoteStream) {\n videoRef.current.srcObject = remoteStream;\n videoRef.current.onloadedmetadata = () => {\n try {\n videoRef.current?.play();\n } catch (error) {\n CallsLogger.error(\"<RemoteStreamView/> play()\", error);\n }\n };\n }\n }, [remoteStream]);\n\n return remoteStream ? <video autoPlay playsInline ref={videoRef} id={elementId} style={cssProps} {...props} /> : null;\n};\n\nexport default RemoteStreamView;\n","import ConnectyCube from \"connectycube\";\nimport { Calls, CallType } from \"connectycube/types\";\nimport { useShallow } from \"zustand/react/shallow\";\nimport useCallsListeners from \"./useCallsListeners\";\nimport useCallsMediaManager, { CallsMediaManager } from \"./useCallsMediaManager\";\nimport useCallsStore, {\n ActiveMedia,\n CallsStoreSessionDataState,\n CallsStoreSessionState,\n CallDataType,\n} from \"./useCallsStore\";\nimport { useCallback, useEffect } from \"react\";\nimport useCallsStoreRef from \"./useCallsStoreRef\";\nimport { CallsLogger } from \"../helpers\";\n\nexport interface CallsContext extends CallsStoreSessionState, CallsStoreSessionDataState, CallsMediaManager {\n startCall: (userID: number | number[], callType: CallType, extension?: Calls.UserInfo) => void;\n acceptCall: (extension?: Calls.UserInfo) => void;\n rejectCall: (extension?: Calls.UserInfo) => void;\n stopCall: (extension?: Calls.UserInfo) => void;\n resetSessionData: (type?: CallDataType) => void;\n participantsIds: number[];\n}\n\nconst useCalls = (): CallsContext => {\n const mediaManager = useCallsMediaManager();\n const [\n activeCall,\n setActiveCall,\n incomingCall,\n resetIncomingCall,\n setLocalStream,\n userMediaParams,\n displayMediaParams,\n activeMedia,\n deviceChangeEvent,\n incomingCallCallData,\n incomingCallStopData,\n activeCallAcceptData,\n activeCallRejectData,\n activeCallStopData,\n activeCallNotAnswerData,\n activeCallConnectionStateData,\n resetSessionData,\n participantsIDs,\n setParticipantsIDs,\n resetStore,\n ] = useCallsStore(\n useShallow((store) => [\n store.activeCall,\n store.setActiveCall,\n store.incomingCall,\n store.resetIncomingCall,\n store.setLocalStream,\n store.userMediaParams,\n store.displayMediaParams,\n store.activeMedia,\n store.deviceChangeEvent,\n store.incomingCallCallData,\n store.incomingCallStopData,\n store.activeCallAcceptData,\n store.activeCallRejectData,\n store.activeCallStopData,\n store.activeCallNotAnswerData,\n store.activeCallConnectionStateData,\n store.resetSessionData,\n store.participantsIDs,\n store.setParticipantsIDs,\n store.resetStore,\n ]),\n );\n const hasActiveListenersRef = useCallsStoreRef(\"hasActiveListeners\");\n const { setupListeners } = useCallsListeners();\n\n const startCall = async (\n userID: number | number[] = [],\n callType: CallType = CallType.VIDEO,\n extension?: Calls.UserInfo,\n ): Promise<void> => {\n const usersIDs = Array.isArray(userID) ? userID : [userID];\n const session = ConnectyCube.videochat.createNewSession(usersIDs, callType);\n\n try {\n resetSessionData();\n setParticipantsIDs(usersIDs);\n await _setLocalStreamAndStoreSession(session);\n session.call(extension);\n } catch (error) {\n CallsLogger.error(\"startCall\", error);\n setParticipantsIDs([]);\n }\n };\n\n const acceptCall = useCallback(\n async (extension?: Calls.UserInfo) => {\n if (incomingCall) {\n const { currentUserID, initiatorID, opponentsIDs } = incomingCall;\n const participantsIds = [...opponentsIDs, initiatorID].filter((id) => id !== currentUserID);\n\n resetSessionData();\n setParticipantsIDs(participantsIds);\n\n try {\n await _setLocalStreamAndStoreSession(incomingCall);\n incomingCall.accept(extension);\n resetIncomingCall();\n } catch (error) {\n CallsLogger.error(\"acceptCall\", error);\n incomingCall.stop(extension);\n } finally {\n resetIncomingCall();\n }\n }\n },\n [incomingCall],\n );\n\n const rejectCall = useCallback(\n (extension?: Calls.UserInfo) => {\n incomingCall?.reject(extension);\n resetIncomingCall();\n },\n [incomingCall],\n );\n\n const stopCall = useCallback(\n (extension?: Calls.UserInfo) => {\n resetStore();\n activeCall?.stop(extension);\n },\n [activeCall],\n );\n\n const _setLocalStreamAndStoreSession = useCallback(\n async (session: Calls.Session) => {\n const stream =\n activeMedia === ActiveMedia.DISPLAY\n ? await session.getDisplayMedia(displayMediaParams)\n : await session.getUserMedia(userMediaParams);\n\n if (session.callType === CallType.AUDIO) {\n stream.getVideoTracks().forEach((track) => (track.enabled = false));\n }\n\n setLocalStream(stream);\n setActiveCall(session);\n },\n [activeMedia, userMediaParams, displayMediaParams],\n );\n\n useEffect(() => {\n if (activeCall && participantsIDs.size === 0) {\n stopCall();\n }\n }, [activeCall, participantsIDs]);\n\n useEffect(() => {\n if (!hasActiveListenersRef.current) {\n setupListeners();\n }\n }, []);\n\n return {\n startCall,\n acceptCall,\n rejectCall,\n stopCall,\n activeCall,\n incomingCall,\n deviceChangeEvent,\n incomingCallCallData,\n incomingCallStopData,\n activeCallAcceptData,\n activeCallRejectData,\n activeCallStopData,\n activeCallNotAnswerData,\n activeCallConnectionStateData,\n resetSessionData,\n participantsIds: [...participantsIDs],\n ...mediaManager,\n };\n};\n\nexport default useCalls;\n","import ConnectyCube from \"connectycube\";\nimport { CallEvent, Calls, CallSessionConnectionState } from \"connectycube/types\";\nimport { useShallow } from \"zustand/react/shallow\";\nimport { CallsLogger } from \"../helpers\";\nimport useCallsStore, { CallDataType } from \"./useCallsStore\";\nimport useCallsStoreRef from \"./useCallsStoreRef\";\n\nexport interface CallsListeners {\n setupListeners: () => void;\n}\n\nconst useCallsListeners = (): CallsListeners => {\n const [\n setIncomingCall,\n resetIncomingCall,\n addRemoteStream,\n removeRemoteStream,\n setDeviceChangeEvent,\n upsertSessionData,\n participantDidEnter,\n participantDidLeave,\n activateCallsListeners,\n ] = useCallsStore(\n useShallow((store) => [\n store.setIncomingCall,\n store.resetIncomingCall,\n store.addRemoteStream,\n store.removeRemoteStream,\n store.setDeviceChangeEvent,\n store.upsertSessionData,\n store.participantDidEnter,\n store.participantDidLeave,\n store.activateCallsListeners,\n ]),\n );\n const activeCallRef = useCallsStoreRef(\"activeCall\");\n const incomingCallRef = useCallsStoreRef(\"incomingCall\");\n\n const handleOnCall = (session: Calls.Session, userInfo: Calls.UserInfo) => {\n if (session.initiatorID === session.currentUserID) {\n return;\n }\n\n setIncomingCall(session);\n upsertSessionData(CallDataType.ON_CALL, { userInfo });\n CallsLogger.log(\"onCall\", { session, ext: userInfo });\n };\n\n const handleOnAccept = (session: Calls.Session, userID: number, userInfo: Calls.UserInfo) => {\n participantDidEnter(userID);\n upsertSessionData(CallDataType.ON_ACCEPT, { userID, userInfo });\n CallsLogger.log(\"onAccept\", { session, userID, ext: userInfo });\n };\n\n const handleOnReject = (session: Calls.Session, userID: number, userInfo: Calls.UserInfo) => {\n participantDidLeave(userID);\n upsertSessionData(CallDataType.ON_REJECT, { userID, userInfo });\n CallsLogger.log(\"onReject\", { session, userID, ext: userInfo });\n };\n\n const handleOnStop = (session: Calls.Session, userID: number, userInfo: Calls.UserInfo) => {\n if (activeCallRef.current?.ID === session.ID) {\n participantDidLeave(userID);\n removeRemoteStream(userID);\n upsertSessionData(CallDataType.ON_STOP, { userID, userInfo });\n }\n\n if (incomingCallRef.current?.ID === session.ID) {\n upsertSessionData(CallDataType.ON_STOP_CALLING, { userID, userInfo });\n resetIncomingCall();\n }\n\n CallsLogger.log(\"onStop\", { session, userID, ext: userInfo });\n };\n\n const handleOnNotAnswer = (session: Calls.Session, userID: number) => {\n participantDidLeave(userID);\n upsertSessionData(CallDataType.ON_NOT_ANSWER, { userID });\n CallsLogger.log(\"onNotAnswer\", { session, userID });\n };\n\n const handleOnRemoteStream = (session: Calls.Session, userID: number, stream: MediaStream) => {\n addRemoteStream(userID, stream);\n CallsLogger.log(\"onRemoteStream\", { session, userID, stream });\n };\n\n const handleOnConnectionState = (\n session: Calls.Session,\n userID: number,\n connectionState: CallSessionConnectionState,\n ) => {\n upsertSessionData(CallDataType.ON_CONNECTION_STATE, { userID, connectionState });\n CallsLogger.log(\"onConnectionState\", { session, userID, connectionState });\n };\n\n const handleOnDevicesChange = (event: Event) => {\n setDeviceChangeEvent(event);\n CallsLogger.log(\"onDevicesChange\", { event });\n };\n\n const setupListeners = () => {\n ConnectyCube.videochat.addListener(CallEvent.CALL, handleOnCall);\n ConnectyCube.videochat.addListener(CallEvent.ACCEPT, handleOnAccept);\n ConnectyCube.videochat.addListener(CallEvent.REJECT, handleOnReject);\n ConnectyCube.videochat.addListener(CallEvent.STOP, handleOnStop);\n ConnectyCube.videochat.addListener(CallEvent.NOT_ANSWER, handleOnNotAnswer);\n ConnectyCube.videochat.addListener(CallEvent.REMOTE_STREAM, handleOnRemoteStream);\n ConnectyCube.videochat.addListener(CallEvent.CONNECTION_STATE, handleOnConnectionState);\n ConnectyCube.videochat.addListener(CallEvent.DEVICES, handleOnDevicesChange);\n activateCallsListeners();\n };\n\n return {\n setupListeners,\n };\n};\n\nexport default useCallsListeners;\n"],"names":["createStoreImpl","createState","state","listeners","Set","setState","partial","replace","nextState","Object","is","previousState","assign","forEach","listener","getState","api","getInitialState","initialState","subscribe","add","delete","identity","arg","createImpl","createStore","useBoundStore","selector","slice","React","useSyncExternalStore","useDebugValue","useStore","subscribeWithSelector","fn","set","get","origSubscribe","optListener","options","equalityFn","currentSlice","nextSlice","previousSlice","fireImmediately","ActiveMedia","CallDataType","initialSessionDataState","incomingCallCallData","incomingCallStopData","activeCallAcceptData","activeCallRejectData","activeCallStopData","activeCallNotAnswerData","activeCallConnectionStateData","deviceChangeEvent","initialMediaState","localStream","remoteStreams","userMediaParams","video","audio","displayMediaParams","activeMedia","USER","activeCall","incomingCall","participantsIDs","hasActiveListeners","useCallsStore","setActiveCall","resetActiveCall","setIncomingCall","resetIncomingCall","setDeviceChangeEvent","upsertSessionData","type","data","ON_CALL","ON_STOP_CALLING","ON_ACCEPT","ON_REJECT","ON_STOP","ON_NOT_ANSWER","ON_CONNECTION_STATE","resetSessionData","setLocalStream","resetLocalStream","addRemoteStream","key","stream","removeRemoteStream","resetRemoteStreams","saveUserMediaParams","resetUserMediaParams","saveDisplayMediaParams","rese