@whereby.com/core 0.36.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +4267 -3903
- package/dist/index.d.cts +777 -4399
- package/dist/index.d.mts +777 -4399
- package/dist/index.d.ts +777 -4399
- package/dist/index.mjs +4342 -3736
- package/dist/legacy-esm.js +4342 -3736
- package/dist/redux/index.cjs +3286 -0
- package/dist/redux/index.d.cts +4691 -0
- package/dist/redux/index.d.mts +4691 -0
- package/dist/redux/index.d.ts +4691 -0
- package/dist/redux/index.js +3006 -0
- package/dist/redux/index.mjs +3006 -0
- package/package.json +24 -8
- package/redux/package.json +4 -0
|
@@ -0,0 +1,3006 @@
|
|
|
1
|
+
import { createAsyncThunk, createListenerMiddleware, addListener, createSlice, createAction, createSelector, isAnyOf, combineReducers, configureStore } from '@reduxjs/toolkit';
|
|
2
|
+
import { ServerSocket, getDeviceData, getStream, getUpdatedDevices, RtcManagerDispatcher, setClientProvider, subscribeIssues } from '@whereby.com/media';
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
4
|
+
|
|
5
|
+
function createAppAsyncThunk(typePrefix, payloadCreator) {
|
|
6
|
+
return createAsyncThunk(typePrefix, payloadCreator);
|
|
7
|
+
}
|
|
8
|
+
function createAppThunk(thunk) {
|
|
9
|
+
return thunk;
|
|
10
|
+
}
|
|
11
|
+
function createAppAuthorizedThunk(authorizationSelector, thunk) {
|
|
12
|
+
return createAppThunk((payload) => (dispatch, getState, extra) => {
|
|
13
|
+
const isAuthorized = authorizationSelector(getState());
|
|
14
|
+
if (!isAuthorized) {
|
|
15
|
+
console.warn("Not authorized to perform this action");
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return thunk(payload)(dispatch, getState, extra);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function createRoomConnectedThunk(thunk) {
|
|
22
|
+
return createAppThunk((payload) => (dispatch, getState, extra) => {
|
|
23
|
+
const connectionStatus = getState().roomConnection.status;
|
|
24
|
+
if (connectionStatus !== "connected") {
|
|
25
|
+
console.warn("Action cannot be performed outside of a connected room");
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
return thunk(payload)(dispatch, getState, extra);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function createAsyncRoomConnectedThunk(typePrefix, payloadCreator) {
|
|
32
|
+
return createAppAsyncThunk(typePrefix, (arg, thunkApi) => {
|
|
33
|
+
const { getState } = thunkApi;
|
|
34
|
+
const connectionStatus = getState().roomConnection.status;
|
|
35
|
+
if (connectionStatus !== "connected") {
|
|
36
|
+
console.warn("Action cannot be performed outside of a connected room");
|
|
37
|
+
return Promise.reject("Action cannot be performed outside of a connected room");
|
|
38
|
+
}
|
|
39
|
+
return payloadCreator(arg, thunkApi);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function createAuthorizedRoomConnectedThunk(authorizationSelector, thunk) {
|
|
43
|
+
return createAppThunk((payload) => (dispatch, getState, extra) => {
|
|
44
|
+
const connectionStatus = getState().roomConnection.status;
|
|
45
|
+
if (connectionStatus !== "connected") {
|
|
46
|
+
console.warn("Action cannot be performed outside of a connected room");
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
return createAppAuthorizedThunk(authorizationSelector, thunk)(payload)(dispatch, getState, extra);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const listenerMiddleware = createListenerMiddleware();
|
|
54
|
+
const startAppListening = listenerMiddleware.startListening;
|
|
55
|
+
const addAppListener = addListener;
|
|
56
|
+
const createReactor = (selectors, callback) => {
|
|
57
|
+
return startAppListening({
|
|
58
|
+
predicate: (_, currentState, previousState) => {
|
|
59
|
+
const previousValues = selectors.map((selector) => selector(previousState));
|
|
60
|
+
const currentValues = selectors.map((selector) => selector(currentState));
|
|
61
|
+
return previousValues.some((previousValue, index) => previousValue !== currentValues[index]);
|
|
62
|
+
},
|
|
63
|
+
effect: (action, { dispatch, getState, extra }) => {
|
|
64
|
+
const selectorResults = selectors.map((selector) => selector(getState()));
|
|
65
|
+
callback({
|
|
66
|
+
dispatch,
|
|
67
|
+
getState,
|
|
68
|
+
extra,
|
|
69
|
+
}, ...selectorResults);
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const coreVersion = "1.0.0";
|
|
75
|
+
|
|
76
|
+
const initialState = {
|
|
77
|
+
isNodeSdk: false,
|
|
78
|
+
isActive: false,
|
|
79
|
+
isDialIn: false,
|
|
80
|
+
ignoreBreakoutGroups: false,
|
|
81
|
+
roomName: null,
|
|
82
|
+
roomUrl: null,
|
|
83
|
+
displayName: null,
|
|
84
|
+
userAgent: `core:${coreVersion}`,
|
|
85
|
+
externalId: null,
|
|
86
|
+
};
|
|
87
|
+
const appSlice = createSlice({
|
|
88
|
+
name: "app",
|
|
89
|
+
initialState,
|
|
90
|
+
reducers: {
|
|
91
|
+
doAppStart: (state, action) => {
|
|
92
|
+
const url = new URL(action.payload.roomUrl);
|
|
93
|
+
return Object.assign(Object.assign(Object.assign({}, state), action.payload), { roomName: url.pathname, initialConfig: Object.assign({}, action.payload), isActive: true });
|
|
94
|
+
},
|
|
95
|
+
doAppStop: (state) => {
|
|
96
|
+
return Object.assign(Object.assign({}, state), { isActive: false });
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
const { doAppStop, doAppStart } = appSlice.actions;
|
|
101
|
+
const selectAppRaw = (state) => state.app;
|
|
102
|
+
const selectAppIsActive = (state) => state.app.isActive;
|
|
103
|
+
const selectAppIsDialIn = (state) => state.app.isDialIn;
|
|
104
|
+
const selectAppRoomName = (state) => state.app.roomName;
|
|
105
|
+
const selectAppRoomUrl = (state) => state.app.roomUrl;
|
|
106
|
+
const selectAppDisplayName = (state) => state.app.displayName;
|
|
107
|
+
const selectAppUserAgent = (state) => state.app.userAgent;
|
|
108
|
+
const selectAppExternalId = (state) => state.app.externalId;
|
|
109
|
+
const selectAppIsNodeSdk = (state) => state.app.isNodeSdk;
|
|
110
|
+
const selectAppInitialConfig = (state) => state.app.initialConfig;
|
|
111
|
+
const selectAppIgnoreBreakoutGroups = (state) => state.app.ignoreBreakoutGroups;
|
|
112
|
+
|
|
113
|
+
function createSignalEventAction(name) {
|
|
114
|
+
return createAction(`signalConnection/event/${name}`);
|
|
115
|
+
}
|
|
116
|
+
const signalEvents = {
|
|
117
|
+
audioEnabled: createSignalEventAction("audioEnabled"),
|
|
118
|
+
audioEnableRequested: createSignalEventAction("audioEnableRequested"),
|
|
119
|
+
breakoutGroupJoined: createSignalEventAction("breakoutGroupJoined"),
|
|
120
|
+
breakoutMoveToGroup: createSignalEventAction("breakoutMoveToGroup"),
|
|
121
|
+
breakoutMoveToMain: createSignalEventAction("breakoutMoveToMain"),
|
|
122
|
+
breakoutSessionUpdated: createSignalEventAction("breakoutSessionUpdated"),
|
|
123
|
+
chatMessage: createSignalEventAction("chatMessage"),
|
|
124
|
+
clientLeft: createSignalEventAction("clientLeft"),
|
|
125
|
+
clientKicked: createSignalEventAction("clientKicked"),
|
|
126
|
+
clientMetadataReceived: createSignalEventAction("clientMetadataReceived"),
|
|
127
|
+
clientUnableToJoin: createSignalEventAction("clientUnableToJoin"),
|
|
128
|
+
cloudRecordingStarted: createSignalEventAction("cloudRecordingStarted"),
|
|
129
|
+
cloudRecordingStopped: createSignalEventAction("cloudRecordingStopped"),
|
|
130
|
+
disconnect: createSignalEventAction("disconnect"),
|
|
131
|
+
knockerLeft: createSignalEventAction("knockerLeft"),
|
|
132
|
+
knockHandled: createSignalEventAction("knockHandled"),
|
|
133
|
+
newClient: createSignalEventAction("newClient"),
|
|
134
|
+
roomJoined: createSignalEventAction("roomJoined"),
|
|
135
|
+
roomKnocked: createSignalEventAction("roomKnocked"),
|
|
136
|
+
roomLeft: createSignalEventAction("roomLeft"),
|
|
137
|
+
roomLocked: createSignalEventAction("roomLocked"),
|
|
138
|
+
roomSessionEnded: createSignalEventAction("roomSessionEnded"),
|
|
139
|
+
screenshareStarted: createSignalEventAction("screenshareStarted"),
|
|
140
|
+
screenshareStopped: createSignalEventAction("screenshareStopped"),
|
|
141
|
+
spotlightAdded: createSignalEventAction("spotlightAdded"),
|
|
142
|
+
spotlightRemoved: createSignalEventAction("spotlightRemoved"),
|
|
143
|
+
streamingStopped: createSignalEventAction("streamingStopped"),
|
|
144
|
+
videoEnabled: createSignalEventAction("videoEnabled"),
|
|
145
|
+
videoEnableRequested: createSignalEventAction("videoEnableRequested"),
|
|
146
|
+
liveTranscriptionStarted: createSignalEventAction("liveTranscriptionStarted"),
|
|
147
|
+
liveTranscriptionStopped: createSignalEventAction("liveTranscriptionStopped"),
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const ROOM_ACTION_PERMISSIONS_BY_ROLE = {
|
|
151
|
+
canLockRoom: ["host"],
|
|
152
|
+
canRequestAudioEnable: ["host"],
|
|
153
|
+
canRequestVideoEnable: ["host"],
|
|
154
|
+
canKickClient: ["host"],
|
|
155
|
+
canEndMeeting: ["host"],
|
|
156
|
+
canAskToSpeak: ["host"],
|
|
157
|
+
canSpotlight: ["host"],
|
|
158
|
+
};
|
|
159
|
+
const authorizationSliceInitialState = {
|
|
160
|
+
roomKey: null,
|
|
161
|
+
roleName: "none",
|
|
162
|
+
};
|
|
163
|
+
const authorizationSlice = createSlice({
|
|
164
|
+
name: "authorization",
|
|
165
|
+
initialState: authorizationSliceInitialState,
|
|
166
|
+
reducers: {
|
|
167
|
+
setRoomKey: (state, action) => {
|
|
168
|
+
return Object.assign(Object.assign({}, state), { roomKey: action.payload });
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
extraReducers: (builder) => {
|
|
172
|
+
builder.addCase(doAppStart, (state, action) => {
|
|
173
|
+
return Object.assign(Object.assign({}, state), { roomKey: action.payload.roomKey });
|
|
174
|
+
});
|
|
175
|
+
builder.addCase(signalEvents.roomJoined, (state, action) => {
|
|
176
|
+
if ("error" in action.payload) {
|
|
177
|
+
return state;
|
|
178
|
+
}
|
|
179
|
+
const { room, selfId } = action.payload || {};
|
|
180
|
+
const client = room === null || room === void 0 ? void 0 : room.clients.find((c) => c.id === selfId);
|
|
181
|
+
if (client) {
|
|
182
|
+
return Object.assign(Object.assign({}, state), { roleName: (client === null || client === void 0 ? void 0 : client.role.roleName) || "none" });
|
|
183
|
+
}
|
|
184
|
+
return state;
|
|
185
|
+
});
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
const { setRoomKey } = authorizationSlice.actions;
|
|
189
|
+
const selectRoomKey = (state) => state.authorization.roomKey;
|
|
190
|
+
const selectAuthorizationRoleName = (state) => state.authorization.roleName;
|
|
191
|
+
const selectIsAuthorizedToLockRoom = createSelector(selectAuthorizationRoleName, (localParticipantRole) => ROOM_ACTION_PERMISSIONS_BY_ROLE.canLockRoom.includes(localParticipantRole));
|
|
192
|
+
const selectIsAuthorizedToRequestAudioEnable = createSelector(selectAuthorizationRoleName, (localParticipantRole) => ROOM_ACTION_PERMISSIONS_BY_ROLE.canRequestAudioEnable.includes(localParticipantRole));
|
|
193
|
+
const selectIsAuthorizedToRequestVideoEnable = createSelector(selectAuthorizationRoleName, (localParticipantRole) => ROOM_ACTION_PERMISSIONS_BY_ROLE.canRequestVideoEnable.includes(localParticipantRole));
|
|
194
|
+
const selectIsAuthorizedToKickClient = createSelector(selectAuthorizationRoleName, (localParticipantRole) => ROOM_ACTION_PERMISSIONS_BY_ROLE.canKickClient.includes(localParticipantRole));
|
|
195
|
+
const selectIsAuthorizedToEndMeeting = createSelector(selectAuthorizationRoleName, (localParticipantRole) => ROOM_ACTION_PERMISSIONS_BY_ROLE.canEndMeeting.includes(localParticipantRole));
|
|
196
|
+
const selectIsAuthorizedToAskToSpeak = createSelector(selectAuthorizationRoleName, (localParticipantRole) => ROOM_ACTION_PERMISSIONS_BY_ROLE.canAskToSpeak.includes(localParticipantRole));
|
|
197
|
+
const selectIsAuthorizedToSpotlight = createSelector(selectAuthorizationRoleName, (localParticipantRole) => ROOM_ACTION_PERMISSIONS_BY_ROLE.canSpotlight.includes(localParticipantRole));
|
|
198
|
+
|
|
199
|
+
/******************************************************************************
|
|
200
|
+
Copyright (c) Microsoft Corporation.
|
|
201
|
+
|
|
202
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
203
|
+
purpose with or without fee is hereby granted.
|
|
204
|
+
|
|
205
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
206
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
207
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
208
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
209
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
210
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
211
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
212
|
+
***************************************************************************** */
|
|
213
|
+
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
function __rest(s, e) {
|
|
217
|
+
var t = {};
|
|
218
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
219
|
+
t[p] = s[p];
|
|
220
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
221
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
222
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
223
|
+
t[p[i]] = s[p[i]];
|
|
224
|
+
}
|
|
225
|
+
return t;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
229
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
230
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
231
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
232
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
233
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
234
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
239
|
+
var e = new Error(message);
|
|
240
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const deviceCredentialsSliceInitialState = {
|
|
244
|
+
isFetching: false,
|
|
245
|
+
data: null,
|
|
246
|
+
};
|
|
247
|
+
const deviceCredentialsSlice = createSlice({
|
|
248
|
+
name: "deviceCredentials",
|
|
249
|
+
initialState: deviceCredentialsSliceInitialState,
|
|
250
|
+
reducers: {},
|
|
251
|
+
extraReducers: (builder) => {
|
|
252
|
+
builder.addCase(doGetDeviceCredentials.pending, (state) => {
|
|
253
|
+
return Object.assign(Object.assign({}, state), { isFetching: true });
|
|
254
|
+
});
|
|
255
|
+
builder.addCase(doGetDeviceCredentials.fulfilled, (state, action) => {
|
|
256
|
+
return Object.assign(Object.assign({}, state), { isFetching: false, data: action.payload });
|
|
257
|
+
});
|
|
258
|
+
builder.addCase(doGetDeviceCredentials.rejected, (state) => {
|
|
259
|
+
return Object.assign(Object.assign({}, state), { isFetching: true });
|
|
260
|
+
});
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
const doGetDeviceCredentials = createAppAsyncThunk("deviceCredentials/doGetDeviceCredentials", (payload_1, _a) => __awaiter(void 0, [payload_1, _a], void 0, function* (payload, { extra }) {
|
|
264
|
+
try {
|
|
265
|
+
const deviceCredentials = yield extra.services.credentialsService.getCredentials();
|
|
266
|
+
return deviceCredentials;
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
console.error(error);
|
|
270
|
+
}
|
|
271
|
+
}));
|
|
272
|
+
const selectDeviceCredentialsRaw = (state) => state.deviceCredentials;
|
|
273
|
+
const selectHasFetchedDeviceCredentials = (state) => { var _a; return !!((_a = state.deviceCredentials.data) === null || _a === void 0 ? void 0 : _a.credentials); };
|
|
274
|
+
const selectDeviceId = (state) => { var _a, _b; return (_b = (_a = state.deviceCredentials.data) === null || _a === void 0 ? void 0 : _a.credentials) === null || _b === void 0 ? void 0 : _b.uuid; };
|
|
275
|
+
const selectShouldFetchDeviceCredentials = createSelector(selectAppIsActive, selectDeviceCredentialsRaw, (appIsActive, deviceCredentials) => {
|
|
276
|
+
if (appIsActive && !deviceCredentials.isFetching && !deviceCredentials.data) {
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
return false;
|
|
280
|
+
});
|
|
281
|
+
createReactor([selectShouldFetchDeviceCredentials], ({ dispatch }, shouldFetchDeviceCredentials) => {
|
|
282
|
+
if (shouldFetchDeviceCredentials) {
|
|
283
|
+
dispatch(doGetDeviceCredentials());
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
function forwardSocketEvents(socket, dispatch) {
|
|
288
|
+
socket.on("room_joined", (payload) => dispatch(signalEvents.roomJoined(payload)));
|
|
289
|
+
socket.on("new_client", (payload) => dispatch(signalEvents.newClient(payload)));
|
|
290
|
+
socket.on("client_left", (payload) => dispatch(signalEvents.clientLeft(payload)));
|
|
291
|
+
socket.on("client_kicked", (payload) => dispatch(signalEvents.clientKicked(payload)));
|
|
292
|
+
socket.on("client_unable_to_join", (payload) => dispatch(signalEvents.clientUnableToJoin(payload)));
|
|
293
|
+
socket.on("audio_enabled", (payload) => dispatch(signalEvents.audioEnabled(payload)));
|
|
294
|
+
socket.on("video_enabled", (payload) => dispatch(signalEvents.videoEnabled(payload)));
|
|
295
|
+
socket.on("audio_enable_requested", (payload) => dispatch(signalEvents.audioEnableRequested(payload)));
|
|
296
|
+
socket.on("client_metadata_received", (payload) => dispatch(signalEvents.clientMetadataReceived(payload)));
|
|
297
|
+
socket.on("chat_message", (payload) => dispatch(signalEvents.chatMessage(payload)));
|
|
298
|
+
socket.on("disconnect", () => dispatch(signalEvents.disconnect()));
|
|
299
|
+
socket.on("room_knocked", (payload) => dispatch(signalEvents.roomKnocked(payload)));
|
|
300
|
+
socket.on("room_left", () => dispatch(signalEvents.roomLeft()));
|
|
301
|
+
socket.on("room_locked", (payload) => dispatch(signalEvents.roomLocked(payload)));
|
|
302
|
+
socket.on("room_session_ended", (payload) => dispatch(signalEvents.roomSessionEnded(payload)));
|
|
303
|
+
socket.on("knocker_left", (payload) => dispatch(signalEvents.knockerLeft(payload)));
|
|
304
|
+
socket.on("knock_handled", (payload) => dispatch(signalEvents.knockHandled(payload)));
|
|
305
|
+
socket.on("screenshare_started", (payload) => dispatch(signalEvents.screenshareStarted(payload)));
|
|
306
|
+
socket.on("screenshare_stopped", (payload) => dispatch(signalEvents.screenshareStopped(payload)));
|
|
307
|
+
socket.on("cloud_recording_started", (payload) => dispatch(signalEvents.cloudRecordingStarted(payload)));
|
|
308
|
+
socket.on("cloud_recording_stopped", () => dispatch(signalEvents.cloudRecordingStopped()));
|
|
309
|
+
socket.on("streaming_stopped", () => dispatch(signalEvents.streamingStopped()));
|
|
310
|
+
socket.on("spotlight_added", (payload) => dispatch(signalEvents.spotlightAdded(payload)));
|
|
311
|
+
socket.on("spotlight_removed", (payload) => dispatch(signalEvents.spotlightRemoved(payload)));
|
|
312
|
+
socket.on("live_transcription_started", (payload) => dispatch(signalEvents.liveTranscriptionStarted(payload)));
|
|
313
|
+
socket.on("live_transcription_stopped", (payload) => dispatch(signalEvents.liveTranscriptionStopped(payload)));
|
|
314
|
+
socket.on("video_enable_requested", (payload) => dispatch(signalEvents.videoEnableRequested(payload)));
|
|
315
|
+
socket.on("breakout_group_joined", (payload) => dispatch(signalEvents.breakoutGroupJoined(payload)));
|
|
316
|
+
socket.on("breakout_session_updated", (payload) => dispatch(signalEvents.breakoutSessionUpdated(payload)));
|
|
317
|
+
socket.on("breakout_move_to_group", () => dispatch(signalEvents.breakoutMoveToGroup()));
|
|
318
|
+
socket.on("breakout_move_to_main", () => dispatch(signalEvents.breakoutMoveToMain()));
|
|
319
|
+
}
|
|
320
|
+
const SIGNAL_BASE_URL = "wss://signal.appearin.net";
|
|
321
|
+
function createSocket() {
|
|
322
|
+
const parsedUrl = new URL(SIGNAL_BASE_URL);
|
|
323
|
+
const socketHost = parsedUrl.origin;
|
|
324
|
+
const socketOverrides = {
|
|
325
|
+
autoConnect: false,
|
|
326
|
+
};
|
|
327
|
+
return new ServerSocket(socketHost, socketOverrides);
|
|
328
|
+
}
|
|
329
|
+
const signalConnectionSliceInitialState = {
|
|
330
|
+
deviceIdentified: false,
|
|
331
|
+
isIdentifyingDevice: false,
|
|
332
|
+
status: "ready",
|
|
333
|
+
socket: null,
|
|
334
|
+
};
|
|
335
|
+
const signalConnectionSlice = createSlice({
|
|
336
|
+
name: "signalConnection",
|
|
337
|
+
initialState: signalConnectionSliceInitialState,
|
|
338
|
+
reducers: {
|
|
339
|
+
socketConnecting: (state) => {
|
|
340
|
+
return Object.assign(Object.assign({}, state), { status: "connecting" });
|
|
341
|
+
},
|
|
342
|
+
socketConnected: (state, action) => {
|
|
343
|
+
return Object.assign(Object.assign({}, state), { socket: action.payload, status: "connected" });
|
|
344
|
+
},
|
|
345
|
+
socketDisconnected: (state) => {
|
|
346
|
+
return Object.assign(Object.assign({}, state), { socket: null, deviceIdentified: false, status: "disconnected" });
|
|
347
|
+
},
|
|
348
|
+
socketReconnecting: (state) => {
|
|
349
|
+
return Object.assign(Object.assign({}, state), { status: "reconnecting" });
|
|
350
|
+
},
|
|
351
|
+
deviceIdentifying: (state) => {
|
|
352
|
+
return Object.assign(Object.assign({}, state), { isIdentifyingDevice: true });
|
|
353
|
+
},
|
|
354
|
+
deviceIdentified: (state) => {
|
|
355
|
+
return Object.assign(Object.assign({}, state), { deviceIdentified: true, isIdentifyingDevice: false });
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
extraReducers: (builder) => {
|
|
359
|
+
builder.addCase(signalEvents.disconnect, (state) => {
|
|
360
|
+
return Object.assign(Object.assign({}, state), { deviceIdentified: false, status: "disconnected" });
|
|
361
|
+
});
|
|
362
|
+
},
|
|
363
|
+
});
|
|
364
|
+
const { deviceIdentifying, deviceIdentified, socketConnected, socketConnecting, socketDisconnected, socketReconnecting, } = signalConnectionSlice.actions;
|
|
365
|
+
const doSignalConnect = createAppThunk(() => {
|
|
366
|
+
return (dispatch, getState) => {
|
|
367
|
+
if (selectSignalConnectionSocket(getState())) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
dispatch(socketConnecting());
|
|
371
|
+
const socket = createSocket();
|
|
372
|
+
socket.on("connect", () => dispatch(socketConnected(socket)));
|
|
373
|
+
socket.on("device_identified", () => dispatch(deviceIdentified()));
|
|
374
|
+
socket.getManager().on("reconnect", () => dispatch(socketReconnecting()));
|
|
375
|
+
forwardSocketEvents(socket, dispatch);
|
|
376
|
+
socket.connect();
|
|
377
|
+
};
|
|
378
|
+
});
|
|
379
|
+
const doSignalIdentifyDevice = createAppThunk(({ deviceCredentials }) => (dispatch, getState) => {
|
|
380
|
+
const state = getState();
|
|
381
|
+
const signalSocket = selectSignalConnectionSocket(state);
|
|
382
|
+
const deviceIdentified = selectSignalConnectionDeviceIdentified(state);
|
|
383
|
+
if (!signalSocket) {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
if (deviceIdentified) {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
signalSocket.emit("identify_device", { deviceCredentials });
|
|
390
|
+
dispatch(deviceIdentifying());
|
|
391
|
+
});
|
|
392
|
+
const doSignalDisconnect = createAppThunk(() => (dispatch, getState) => {
|
|
393
|
+
const state = getState();
|
|
394
|
+
const signalStatus = selectSignalStatus(state);
|
|
395
|
+
if (signalStatus === "connected") {
|
|
396
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
397
|
+
socket === null || socket === void 0 ? void 0 : socket.disconnect();
|
|
398
|
+
dispatch(socketDisconnected());
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
const selectSignalConnectionRaw = (state) => state.signalConnection;
|
|
402
|
+
const selectSignalIsIdentifyingDevice = (state) => state.signalConnection.isIdentifyingDevice;
|
|
403
|
+
const selectSignalConnectionDeviceIdentified = (state) => state.signalConnection.deviceIdentified;
|
|
404
|
+
const selectSignalStatus = (state) => state.signalConnection.status;
|
|
405
|
+
const selectSignalConnectionSocket = (state) => state.signalConnection.socket;
|
|
406
|
+
const selectShouldConnectSignal = createSelector(selectAppIsActive, selectSignalStatus, (appIsActive, signalStatus) => {
|
|
407
|
+
if (appIsActive && ["ready", "reconnecting"].includes(signalStatus)) {
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
return false;
|
|
411
|
+
});
|
|
412
|
+
createReactor([selectShouldConnectSignal], ({ dispatch }, shouldConnectSignal) => {
|
|
413
|
+
if (shouldConnectSignal) {
|
|
414
|
+
dispatch(doSignalConnect());
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
const selectShouldIdentifyDevice = createSelector(selectDeviceCredentialsRaw, selectSignalStatus, selectSignalConnectionDeviceIdentified, selectSignalIsIdentifyingDevice, (deviceCredentialsRaw, signalStatus, deviceIdentified, isIdentifyingDevice) => {
|
|
418
|
+
if (deviceCredentialsRaw.data && signalStatus === "connected" && !deviceIdentified && !isIdentifyingDevice) {
|
|
419
|
+
return true;
|
|
420
|
+
}
|
|
421
|
+
return false;
|
|
422
|
+
});
|
|
423
|
+
createReactor([selectShouldIdentifyDevice, selectDeviceCredentialsRaw], ({ dispatch }, shouldIdentifyDevice, deviceCredentialsRaw) => {
|
|
424
|
+
if (shouldIdentifyDevice && deviceCredentialsRaw.data) {
|
|
425
|
+
dispatch(doSignalIdentifyDevice({ deviceCredentials: deviceCredentialsRaw.data }));
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
startAppListening({
|
|
429
|
+
actionCreator: signalEvents.roomLeft,
|
|
430
|
+
effect: (_, { dispatch }) => {
|
|
431
|
+
dispatch(doSignalDisconnect());
|
|
432
|
+
},
|
|
433
|
+
});
|
|
434
|
+
startAppListening({
|
|
435
|
+
actionCreator: signalEvents.clientKicked,
|
|
436
|
+
effect: (_, { dispatch }) => {
|
|
437
|
+
dispatch(doSignalDisconnect());
|
|
438
|
+
},
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
function debounce(fn, { delay = 500, edges } = {}) {
|
|
442
|
+
let timeout;
|
|
443
|
+
let nCalls = 0;
|
|
444
|
+
return (...args) => {
|
|
445
|
+
nCalls += 1;
|
|
446
|
+
if (edges && nCalls === 1) {
|
|
447
|
+
fn(...args);
|
|
448
|
+
}
|
|
449
|
+
clearTimeout(timeout);
|
|
450
|
+
timeout = setTimeout(() => {
|
|
451
|
+
if (!edges || nCalls > 1) {
|
|
452
|
+
fn(...args);
|
|
453
|
+
}
|
|
454
|
+
timeout = undefined;
|
|
455
|
+
nCalls = 0;
|
|
456
|
+
}, delay);
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const initialLocalMediaState = {
|
|
461
|
+
busyDeviceIds: [],
|
|
462
|
+
cameraEnabled: false,
|
|
463
|
+
currentSpeakerDeviceId: "default",
|
|
464
|
+
devices: [],
|
|
465
|
+
isSettingCameraDevice: false,
|
|
466
|
+
isSettingMicrophoneDevice: false,
|
|
467
|
+
isSettingSpeakerDevice: false,
|
|
468
|
+
isTogglingCamera: false,
|
|
469
|
+
lowDataMode: false,
|
|
470
|
+
microphoneEnabled: false,
|
|
471
|
+
status: "inactive",
|
|
472
|
+
stream: undefined,
|
|
473
|
+
isSwitchingStream: false,
|
|
474
|
+
};
|
|
475
|
+
const localMediaSlice = createSlice({
|
|
476
|
+
name: "localMedia",
|
|
477
|
+
initialState: initialLocalMediaState,
|
|
478
|
+
reducers: {
|
|
479
|
+
deviceBusy(state, action) {
|
|
480
|
+
if (state.busyDeviceIds.includes(action.payload.deviceId)) {
|
|
481
|
+
return state;
|
|
482
|
+
}
|
|
483
|
+
return Object.assign(Object.assign({}, state), { busyDeviceIds: [...state.busyDeviceIds, action.payload.deviceId] });
|
|
484
|
+
},
|
|
485
|
+
toggleCameraEnabled(state, action) {
|
|
486
|
+
var _a;
|
|
487
|
+
return Object.assign(Object.assign({}, state), { cameraEnabled: (_a = action.payload.enabled) !== null && _a !== void 0 ? _a : !state.cameraEnabled });
|
|
488
|
+
},
|
|
489
|
+
setCurrentCameraDeviceId(state, action) {
|
|
490
|
+
return Object.assign(Object.assign({}, state), { currentCameraDeviceId: action.payload.deviceId });
|
|
491
|
+
},
|
|
492
|
+
toggleMicrophoneEnabled(state, action) {
|
|
493
|
+
var _a;
|
|
494
|
+
return Object.assign(Object.assign({}, state), { microphoneEnabled: (_a = action.payload.enabled) !== null && _a !== void 0 ? _a : !state.microphoneEnabled });
|
|
495
|
+
},
|
|
496
|
+
setCurrentMicrophoneDeviceId(state, action) {
|
|
497
|
+
return Object.assign(Object.assign({}, state), { currentMicrophoneDeviceId: action.payload.deviceId });
|
|
498
|
+
},
|
|
499
|
+
setCurrentSpeakerDeviceId(state, action) {
|
|
500
|
+
var _a;
|
|
501
|
+
return Object.assign(Object.assign({}, state), { currentSpeakerDeviceId: (_a = action.payload.deviceId) !== null && _a !== void 0 ? _a : "default" });
|
|
502
|
+
},
|
|
503
|
+
toggleLowDataModeEnabled(state, action) {
|
|
504
|
+
var _a;
|
|
505
|
+
return Object.assign(Object.assign({}, state), { lowDataMode: (_a = action.payload.enabled) !== null && _a !== void 0 ? _a : !state.lowDataMode });
|
|
506
|
+
},
|
|
507
|
+
setDevices(state, action) {
|
|
508
|
+
return Object.assign(Object.assign({}, state), { devices: action.payload.devices });
|
|
509
|
+
},
|
|
510
|
+
setLocalMediaStream(state, action) {
|
|
511
|
+
return Object.assign(Object.assign({}, state), { stream: action.payload.stream });
|
|
512
|
+
},
|
|
513
|
+
setLocalMediaOptions(state, action) {
|
|
514
|
+
return Object.assign(Object.assign({}, state), { options: action.payload.options });
|
|
515
|
+
},
|
|
516
|
+
localMediaStopped(state) {
|
|
517
|
+
return Object.assign(Object.assign({}, state), { status: "stopped", stream: undefined });
|
|
518
|
+
},
|
|
519
|
+
localStreamMetadataUpdated(state, action) {
|
|
520
|
+
const { audio, video } = action.payload;
|
|
521
|
+
return Object.assign(Object.assign({}, state), { currentCameraDeviceId: video.deviceId, currentMicrophoneDeviceId: audio.deviceId, busyDeviceIds: state.busyDeviceIds.filter((id) => id !== audio.deviceId && id !== video.deviceId) });
|
|
522
|
+
},
|
|
523
|
+
},
|
|
524
|
+
extraReducers: (builder) => {
|
|
525
|
+
builder.addCase(doAppStart, (state, action) => {
|
|
526
|
+
return Object.assign(Object.assign({}, state), { options: action.payload.localMediaOptions });
|
|
527
|
+
});
|
|
528
|
+
builder.addCase(doSetDevice.pending, (state, action) => {
|
|
529
|
+
const { audio, video } = action.meta.arg;
|
|
530
|
+
return Object.assign(Object.assign({}, state), { isSettingCameraDevice: video, isSettingMicrophoneDevice: audio });
|
|
531
|
+
});
|
|
532
|
+
builder.addCase(doSetDevice.fulfilled, (state, action) => {
|
|
533
|
+
const { audio, video } = action.meta.arg;
|
|
534
|
+
return Object.assign(Object.assign({}, state), { isSettingCameraDevice: video ? false : state.isSettingCameraDevice, isSettingMicrophoneDevice: audio ? false : state.isSettingMicrophoneDevice });
|
|
535
|
+
});
|
|
536
|
+
builder.addCase(doSetDevice.rejected, (state, action) => {
|
|
537
|
+
const { audio, video } = action.meta.arg;
|
|
538
|
+
return Object.assign(Object.assign({}, state), { isSettingCameraDevice: video ? false : state.isSettingCameraDevice, isSettingMicrophoneDevice: audio ? false : state.isSettingMicrophoneDevice, cameraDeviceError: video ? action.error : state.cameraDeviceError, microphoneDeviceError: audio ? action.error : state.microphoneDeviceError });
|
|
539
|
+
});
|
|
540
|
+
builder.addCase(doToggleCamera.pending, (state) => {
|
|
541
|
+
return Object.assign(Object.assign({}, state), { isTogglingCamera: true });
|
|
542
|
+
});
|
|
543
|
+
builder.addCase(doToggleCamera.fulfilled, (state) => {
|
|
544
|
+
return Object.assign(Object.assign({}, state), { isTogglingCamera: false });
|
|
545
|
+
});
|
|
546
|
+
builder.addCase(doUpdateDeviceList.fulfilled, (state, action) => {
|
|
547
|
+
return Object.assign(Object.assign({}, state), { devices: action.payload.devices });
|
|
548
|
+
});
|
|
549
|
+
builder.addCase(doStartLocalMedia.pending, (state) => {
|
|
550
|
+
return Object.assign(Object.assign({}, state), { status: "starting" });
|
|
551
|
+
});
|
|
552
|
+
builder.addCase(doStartLocalMedia.fulfilled, (state, { payload: { stream, onDeviceChange } }) => {
|
|
553
|
+
let cameraDeviceId = undefined;
|
|
554
|
+
let cameraEnabled = false;
|
|
555
|
+
let microphoneDeviceId = undefined;
|
|
556
|
+
let microphoneEnabled = false;
|
|
557
|
+
const audioTrack = stream.getAudioTracks()[0];
|
|
558
|
+
const videoTrack = stream.getVideoTracks()[0];
|
|
559
|
+
if (audioTrack) {
|
|
560
|
+
microphoneDeviceId = audioTrack.getSettings().deviceId;
|
|
561
|
+
microphoneEnabled = audioTrack.enabled;
|
|
562
|
+
}
|
|
563
|
+
if (videoTrack) {
|
|
564
|
+
cameraEnabled = videoTrack.enabled;
|
|
565
|
+
cameraDeviceId = videoTrack.getSettings().deviceId;
|
|
566
|
+
}
|
|
567
|
+
return Object.assign(Object.assign({}, state), { stream, status: "started", currentCameraDeviceId: cameraDeviceId, currentMicrophoneDeviceId: microphoneDeviceId, cameraEnabled,
|
|
568
|
+
microphoneEnabled,
|
|
569
|
+
onDeviceChange });
|
|
570
|
+
});
|
|
571
|
+
builder.addCase(doStartLocalMedia.rejected, (state, action) => {
|
|
572
|
+
return Object.assign(Object.assign({}, state), { status: "error", startError: action.error });
|
|
573
|
+
});
|
|
574
|
+
builder.addCase(doSwitchLocalStream.pending, (state) => {
|
|
575
|
+
return Object.assign(Object.assign({}, state), { isSwitchingStream: true });
|
|
576
|
+
});
|
|
577
|
+
builder.addCase(doSwitchLocalStream.fulfilled, (state) => {
|
|
578
|
+
return Object.assign(Object.assign({}, state), { isSwitchingStream: false });
|
|
579
|
+
});
|
|
580
|
+
builder.addCase(doSwitchLocalStream.rejected, (state) => {
|
|
581
|
+
return Object.assign(Object.assign({}, state), { isSwitchingStream: false });
|
|
582
|
+
});
|
|
583
|
+
},
|
|
584
|
+
});
|
|
585
|
+
const { deviceBusy, setCurrentCameraDeviceId, setCurrentMicrophoneDeviceId, setCurrentSpeakerDeviceId, toggleCameraEnabled, toggleMicrophoneEnabled, toggleLowDataModeEnabled, setLocalMediaOptions, setLocalMediaStream, localMediaStopped, localStreamMetadataUpdated, } = localMediaSlice.actions;
|
|
586
|
+
const doToggleCamera = createAppAsyncThunk("localMedia/doToggleCamera", (_1, _a) => __awaiter(void 0, [_1, _a], void 0, function* (_, { getState, rejectWithValue }) {
|
|
587
|
+
const state = getState();
|
|
588
|
+
const stream = selectLocalMediaStream(state);
|
|
589
|
+
if (!stream) {
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
let track = stream.getVideoTracks()[0];
|
|
593
|
+
const enabled = selectIsCameraEnabled(state);
|
|
594
|
+
try {
|
|
595
|
+
if (enabled) {
|
|
596
|
+
if (track) {
|
|
597
|
+
track.enabled = true;
|
|
598
|
+
}
|
|
599
|
+
else {
|
|
600
|
+
const constraintsOptions = selectLocalMediaConstraintsOptions(state);
|
|
601
|
+
const cameraDeviceId = selectCurrentCameraDeviceId(state);
|
|
602
|
+
yield getStream(Object.assign(Object.assign({}, constraintsOptions), { audioId: false, videoId: cameraDeviceId, type: "exact" }), { replaceStream: stream });
|
|
603
|
+
track = stream.getVideoTracks()[0];
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
else {
|
|
607
|
+
if (!track) {
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
track.enabled = false;
|
|
611
|
+
track.stop();
|
|
612
|
+
stream.removeTrack(track);
|
|
613
|
+
}
|
|
614
|
+
stream.dispatchEvent(new CustomEvent("stopresumevideo", { detail: { track, enable: enabled } }));
|
|
615
|
+
}
|
|
616
|
+
catch (error) {
|
|
617
|
+
return rejectWithValue(error);
|
|
618
|
+
}
|
|
619
|
+
}));
|
|
620
|
+
const doToggleMicrophone = createAppAsyncThunk("localMedia/doToggleMicrophone", (_, { getState }) => {
|
|
621
|
+
var _a;
|
|
622
|
+
const state = getState();
|
|
623
|
+
const stream = selectLocalMediaStream(state);
|
|
624
|
+
if (!stream) {
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
const enabled = selectIsMicrophoneEnabled(state);
|
|
628
|
+
const audioTrack = (_a = stream.getAudioTracks()) === null || _a === void 0 ? void 0 : _a[0];
|
|
629
|
+
if (!audioTrack) {
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
audioTrack.enabled = enabled;
|
|
633
|
+
});
|
|
634
|
+
const doToggleLowDataMode = createAppThunk(() => (dispatch, getState) => {
|
|
635
|
+
const state = getState();
|
|
636
|
+
const stream = selectLocalMediaStream(state);
|
|
637
|
+
if (!stream) {
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
const videoId = selectCurrentCameraDeviceId(state);
|
|
641
|
+
const audioId = selectCurrentMicrophoneDeviceId(state);
|
|
642
|
+
dispatch(doSwitchLocalStream({ audioId, videoId }));
|
|
643
|
+
});
|
|
644
|
+
const doSetDevice = createAppAsyncThunk("localMedia/reactSetDevice", (_a, _b) => __awaiter(void 0, [_a, _b], void 0, function* ({ audio, video }, { getState, rejectWithValue }) {
|
|
645
|
+
try {
|
|
646
|
+
const state = getState();
|
|
647
|
+
const stream = selectLocalMediaStream(state);
|
|
648
|
+
if (!stream) {
|
|
649
|
+
throw new Error("No stream");
|
|
650
|
+
}
|
|
651
|
+
const audioId = audio ? selectCurrentMicrophoneDeviceId(state) : false;
|
|
652
|
+
const videoId = video ? selectCurrentCameraDeviceId(state) : false;
|
|
653
|
+
const constraintsOptions = selectLocalMediaConstraintsOptions(state);
|
|
654
|
+
const { replacedTracks } = yield getStream(Object.assign(Object.assign({}, constraintsOptions), { audioId,
|
|
655
|
+
videoId, type: "exact" }), { replaceStream: stream });
|
|
656
|
+
const isAudioEnabled = selectIsMicrophoneEnabled(state);
|
|
657
|
+
stream.getAudioTracks().forEach((track) => (track.enabled = isAudioEnabled));
|
|
658
|
+
const isVideoEnabled = selectIsCameraEnabled(state);
|
|
659
|
+
stream.getVideoTracks().forEach((track) => (track.enabled = isVideoEnabled));
|
|
660
|
+
return { replacedTracks };
|
|
661
|
+
}
|
|
662
|
+
catch (error) {
|
|
663
|
+
return rejectWithValue(error);
|
|
664
|
+
}
|
|
665
|
+
}));
|
|
666
|
+
const doUpdateDeviceList = createAppAsyncThunk("localMedia/doUpdateDeviceList", (_1, _a) => __awaiter(void 0, [_1, _a], void 0, function* (_, { getState, dispatch, rejectWithValue }) {
|
|
667
|
+
var _b, _c, _d, _e;
|
|
668
|
+
const state = getState();
|
|
669
|
+
let newDevices = [];
|
|
670
|
+
let oldDevices = [];
|
|
671
|
+
const stream = selectLocalMediaStream(state);
|
|
672
|
+
try {
|
|
673
|
+
newDevices = yield navigator.mediaDevices.enumerateDevices();
|
|
674
|
+
oldDevices = selectLocalMediaDevices(state);
|
|
675
|
+
const shouldHandleDeviceUpdate = stream &&
|
|
676
|
+
!selectLocalMediaIsSwitchingStream(state) &&
|
|
677
|
+
newDevices &&
|
|
678
|
+
oldDevices &&
|
|
679
|
+
oldDevices.find((d) => d.deviceId);
|
|
680
|
+
if (!shouldHandleDeviceUpdate) {
|
|
681
|
+
return { devices: newDevices };
|
|
682
|
+
}
|
|
683
|
+
const { changedDevices, addedDevices } = getUpdatedDevices({
|
|
684
|
+
oldDevices,
|
|
685
|
+
newDevices,
|
|
686
|
+
});
|
|
687
|
+
let autoSwitchAudioId = (_b = changedDevices.audioinput) === null || _b === void 0 ? void 0 : _b.deviceId;
|
|
688
|
+
let autoSwitchVideoId = (_c = changedDevices.videoinput) === null || _c === void 0 ? void 0 : _c.deviceId;
|
|
689
|
+
if (autoSwitchAudioId === undefined) {
|
|
690
|
+
autoSwitchAudioId = (_d = addedDevices.audioinput) === null || _d === void 0 ? void 0 : _d.deviceId;
|
|
691
|
+
}
|
|
692
|
+
if (autoSwitchVideoId === undefined) {
|
|
693
|
+
autoSwitchVideoId = (_e = addedDevices.videoinput) === null || _e === void 0 ? void 0 : _e.deviceId;
|
|
694
|
+
}
|
|
695
|
+
if (autoSwitchAudioId !== undefined || autoSwitchVideoId !== undefined) {
|
|
696
|
+
dispatch(doSwitchLocalStream({ audioId: autoSwitchAudioId, videoId: autoSwitchVideoId }));
|
|
697
|
+
}
|
|
698
|
+
return { devices: newDevices };
|
|
699
|
+
}
|
|
700
|
+
catch (error) {
|
|
701
|
+
return rejectWithValue(error);
|
|
702
|
+
}
|
|
703
|
+
}));
|
|
704
|
+
const doSwitchLocalStream = createAppAsyncThunk("localMedia/doSwitchLocalStream", (_a, _b) => __awaiter(void 0, [_a, _b], void 0, function* ({ audioId, videoId }, { dispatch, getState, rejectWithValue }) {
|
|
705
|
+
const state = getState();
|
|
706
|
+
const replaceStream = selectLocalMediaStream(state);
|
|
707
|
+
const constraintsOptions = selectLocalMediaConstraintsOptions(state);
|
|
708
|
+
const onlySwitchingOne = !!(videoId && !audioId) || !!(!videoId && audioId);
|
|
709
|
+
if (!replaceStream) {
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
try {
|
|
713
|
+
const { replacedTracks } = yield getStream(Object.assign(Object.assign({}, constraintsOptions), { audioId: audioId === undefined ? false : audioId, videoId: videoId === undefined ? false : videoId, type: "exact" }), { replaceStream });
|
|
714
|
+
const deviceId = audioId || videoId;
|
|
715
|
+
if (onlySwitchingOne && deviceId) {
|
|
716
|
+
dispatch(deviceBusy({
|
|
717
|
+
deviceId,
|
|
718
|
+
}));
|
|
719
|
+
}
|
|
720
|
+
return { replacedTracks };
|
|
721
|
+
}
|
|
722
|
+
catch (error) {
|
|
723
|
+
console.error(error);
|
|
724
|
+
const deviceId = audioId || videoId;
|
|
725
|
+
if (onlySwitchingOne && deviceId) {
|
|
726
|
+
dispatch(deviceBusy({
|
|
727
|
+
deviceId,
|
|
728
|
+
}));
|
|
729
|
+
}
|
|
730
|
+
return rejectWithValue(error);
|
|
731
|
+
}
|
|
732
|
+
}));
|
|
733
|
+
const doStartLocalMedia = createAppAsyncThunk("localMedia/doStartLocalMedia", (payload_1, _a) => __awaiter(void 0, [payload_1, _a], void 0, function* (payload, { getState, dispatch, rejectWithValue }) {
|
|
734
|
+
const onDeviceChange = debounce(() => {
|
|
735
|
+
dispatch(doUpdateDeviceList());
|
|
736
|
+
}, { delay: 500 });
|
|
737
|
+
if (navigator.mediaDevices) {
|
|
738
|
+
navigator.mediaDevices.addEventListener("devicechange", onDeviceChange);
|
|
739
|
+
}
|
|
740
|
+
if ("getTracks" in payload) {
|
|
741
|
+
return Promise.resolve({ stream: payload, onDeviceChange });
|
|
742
|
+
}
|
|
743
|
+
if (!(payload.audio || payload.video)) {
|
|
744
|
+
return { stream: new MediaStream(), onDeviceChange };
|
|
745
|
+
}
|
|
746
|
+
else {
|
|
747
|
+
dispatch(setLocalMediaOptions({ options: payload }));
|
|
748
|
+
}
|
|
749
|
+
try {
|
|
750
|
+
yield dispatch(doUpdateDeviceList());
|
|
751
|
+
const state = getState();
|
|
752
|
+
const constraintsOptions = selectLocalMediaConstraintsOptions(state);
|
|
753
|
+
const { stream } = yield getStream(Object.assign(Object.assign({}, constraintsOptions), { audioId: payload.audio, videoId: payload.video }));
|
|
754
|
+
return { stream, onDeviceChange };
|
|
755
|
+
}
|
|
756
|
+
catch (error) {
|
|
757
|
+
return rejectWithValue(error);
|
|
758
|
+
}
|
|
759
|
+
}));
|
|
760
|
+
const doStopLocalMedia = createAppThunk(() => (dispatch, getState) => {
|
|
761
|
+
const stream = selectLocalMediaStream(getState());
|
|
762
|
+
const onDeviceChange = selectLocalMediaRaw(getState()).onDeviceChange;
|
|
763
|
+
stream === null || stream === void 0 ? void 0 : stream.getTracks().forEach((track) => {
|
|
764
|
+
track.stop();
|
|
765
|
+
});
|
|
766
|
+
if (navigator.mediaDevices && onDeviceChange) {
|
|
767
|
+
navigator.mediaDevices.removeEventListener("devicechange", onDeviceChange);
|
|
768
|
+
}
|
|
769
|
+
dispatch(localMediaStopped());
|
|
770
|
+
});
|
|
771
|
+
const selectBusyDeviceIds = (state) => state.localMedia.busyDeviceIds;
|
|
772
|
+
const selectCameraDeviceError = (state) => state.localMedia.cameraDeviceError;
|
|
773
|
+
const selectCurrentCameraDeviceId = (state) => state.localMedia.currentCameraDeviceId;
|
|
774
|
+
const selectCurrentMicrophoneDeviceId = (state) => state.localMedia.currentMicrophoneDeviceId;
|
|
775
|
+
const selectCurrentSpeakerDeviceId = (state) => state.localMedia.currentSpeakerDeviceId;
|
|
776
|
+
const selectIsCameraEnabled = (state) => state.localMedia.cameraEnabled;
|
|
777
|
+
const selectIsMicrophoneEnabled = (state) => state.localMedia.microphoneEnabled;
|
|
778
|
+
const selectIsLowDataModeEnabled = (state) => state.localMedia.lowDataMode;
|
|
779
|
+
const selectIsSettingCameraDevice = (state) => state.localMedia.isSettingCameraDevice;
|
|
780
|
+
const selectIsSettingMicrophoneDevice = (state) => state.localMedia.isSettingMicrophoneDevice;
|
|
781
|
+
const selectIsToggleCamera = (state) => state.localMedia.isTogglingCamera;
|
|
782
|
+
const selectLocalMediaDevices = (state) => state.localMedia.devices;
|
|
783
|
+
const selectLocalMediaOptions = (state) => state.localMedia.options;
|
|
784
|
+
const selectLocalMediaOwnsStream = createSelector(selectLocalMediaOptions, (options) => !!options);
|
|
785
|
+
const selectLocalMediaRaw = (state) => state.localMedia;
|
|
786
|
+
const selectLocalMediaStatus = (state) => state.localMedia.status;
|
|
787
|
+
const selectLocalMediaStream = (state) => state.localMedia.stream;
|
|
788
|
+
const selectMicrophoneDeviceError = (state) => state.localMedia.microphoneDeviceError;
|
|
789
|
+
const selectLocalMediaStartError = (state) => state.localMedia.startError;
|
|
790
|
+
const selectLocalMediaIsSwitchingStream = (state) => state.localMedia.isSwitchingStream;
|
|
791
|
+
const selectLocalMediaConstraintsOptions = createSelector(selectLocalMediaDevices, selectCurrentCameraDeviceId, selectCurrentMicrophoneDeviceId, selectIsLowDataModeEnabled, (devices, videoId, audioId, lowDataMode) => ({
|
|
792
|
+
devices,
|
|
793
|
+
videoId,
|
|
794
|
+
audioId,
|
|
795
|
+
options: {
|
|
796
|
+
disableAEC: false,
|
|
797
|
+
disableAGC: false,
|
|
798
|
+
hd: true,
|
|
799
|
+
lax: false,
|
|
800
|
+
lowDataMode,
|
|
801
|
+
simulcast: true,
|
|
802
|
+
widescreen: true,
|
|
803
|
+
},
|
|
804
|
+
}));
|
|
805
|
+
const selectIsLocalMediaStarting = createSelector(selectLocalMediaStatus, (status) => status === "starting");
|
|
806
|
+
const selectCameraDevices = createSelector(selectLocalMediaDevices, selectBusyDeviceIds, (devices, busyDeviceIds) => devices.filter((d) => d.kind === "videoinput").filter((d) => !busyDeviceIds.includes(d.deviceId)));
|
|
807
|
+
const selectMicrophoneDevices = createSelector(selectLocalMediaDevices, selectBusyDeviceIds, (devices, busyDeviceIds) => devices.filter((d) => d.kind === "audioinput").filter((d) => !busyDeviceIds.includes(d.deviceId)));
|
|
808
|
+
const selectSpeakerDevices = createSelector(selectLocalMediaDevices, (devices) => devices.filter((d) => d.kind === "audiooutput"));
|
|
809
|
+
const selectLocalMediaShouldStartWithOptions = createSelector(selectAppIsActive, selectLocalMediaStatus, selectLocalMediaOptions, (appIsActive, localMediaStatus, localMediaOptions) => {
|
|
810
|
+
if (appIsActive && ["inactive", "stopped"].includes(localMediaStatus) && localMediaOptions) {
|
|
811
|
+
return localMediaOptions;
|
|
812
|
+
}
|
|
813
|
+
});
|
|
814
|
+
createReactor([selectLocalMediaShouldStartWithOptions], ({ dispatch }, options) => {
|
|
815
|
+
if (options) {
|
|
816
|
+
dispatch(doStartLocalMedia(options));
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
const selectLocalMediaShouldStop = createSelector(selectAppIsActive, selectLocalMediaStatus, selectLocalMediaOptions, (appIsActive, localMediaStatus, localMediaOptions) => {
|
|
820
|
+
return !appIsActive && localMediaStatus !== "inactive" && !!localMediaOptions;
|
|
821
|
+
});
|
|
822
|
+
createReactor([selectLocalMediaShouldStop], ({ dispatch }, localMediaShouldStop) => {
|
|
823
|
+
if (localMediaShouldStop) {
|
|
824
|
+
dispatch(doStopLocalMedia());
|
|
825
|
+
}
|
|
826
|
+
});
|
|
827
|
+
startAppListening({
|
|
828
|
+
predicate: (_action, currentState, previousState) => {
|
|
829
|
+
const oldValue = selectIsMicrophoneEnabled(previousState);
|
|
830
|
+
const newValue = selectIsMicrophoneEnabled(currentState);
|
|
831
|
+
const isReady = selectLocalMediaStatus(previousState) === "started";
|
|
832
|
+
return isReady && oldValue !== newValue;
|
|
833
|
+
},
|
|
834
|
+
effect: (_, { dispatch }) => {
|
|
835
|
+
dispatch(doToggleMicrophone());
|
|
836
|
+
},
|
|
837
|
+
});
|
|
838
|
+
startAppListening({
|
|
839
|
+
predicate: (_action, currentState, previousState) => {
|
|
840
|
+
const oldValue = selectIsLowDataModeEnabled(previousState);
|
|
841
|
+
const newValue = selectIsLowDataModeEnabled(currentState);
|
|
842
|
+
const isReady = selectLocalMediaStatus(previousState) === "started";
|
|
843
|
+
return isReady && oldValue !== newValue;
|
|
844
|
+
},
|
|
845
|
+
effect: (_action, { dispatch }) => {
|
|
846
|
+
dispatch(doToggleLowDataMode());
|
|
847
|
+
},
|
|
848
|
+
});
|
|
849
|
+
startAppListening({
|
|
850
|
+
predicate: (_action, currentState, previousState) => {
|
|
851
|
+
const isToggling = selectIsToggleCamera(currentState);
|
|
852
|
+
if (isToggling) {
|
|
853
|
+
return false;
|
|
854
|
+
}
|
|
855
|
+
const oldValue = selectIsCameraEnabled(previousState);
|
|
856
|
+
const newValue = selectIsCameraEnabled(currentState);
|
|
857
|
+
const isReady = selectLocalMediaStatus(previousState) === "started";
|
|
858
|
+
return isReady && oldValue !== newValue;
|
|
859
|
+
},
|
|
860
|
+
effect: (_action, { dispatch }) => {
|
|
861
|
+
dispatch(doToggleCamera());
|
|
862
|
+
},
|
|
863
|
+
});
|
|
864
|
+
startAppListening({
|
|
865
|
+
predicate: (_action, currentState, previousState) => {
|
|
866
|
+
const oldValue = selectCurrentCameraDeviceId(previousState);
|
|
867
|
+
const newValue = selectCurrentCameraDeviceId(currentState);
|
|
868
|
+
const isReady = selectLocalMediaStatus(previousState) === "started";
|
|
869
|
+
return isReady && oldValue !== newValue;
|
|
870
|
+
},
|
|
871
|
+
effect: (_action, { dispatch }) => {
|
|
872
|
+
dispatch(doSetDevice({ audio: false, video: true }));
|
|
873
|
+
},
|
|
874
|
+
});
|
|
875
|
+
startAppListening({
|
|
876
|
+
predicate: (_action, currentState, previousState) => {
|
|
877
|
+
const oldValue = selectCurrentMicrophoneDeviceId(previousState);
|
|
878
|
+
const newValue = selectCurrentMicrophoneDeviceId(currentState);
|
|
879
|
+
const isReady = selectLocalMediaStatus(previousState) === "started";
|
|
880
|
+
return isReady && oldValue !== newValue;
|
|
881
|
+
},
|
|
882
|
+
effect: (_action, { dispatch }) => {
|
|
883
|
+
dispatch(doSetDevice({ audio: true, video: false }));
|
|
884
|
+
},
|
|
885
|
+
});
|
|
886
|
+
startAppListening({
|
|
887
|
+
matcher: isAnyOf(doStartLocalMedia.fulfilled, doUpdateDeviceList.fulfilled, doSwitchLocalStream.fulfilled, doSwitchLocalStream.rejected),
|
|
888
|
+
effect: (_action, { dispatch, getState }) => {
|
|
889
|
+
const state = getState();
|
|
890
|
+
const stream = selectLocalMediaStream(state);
|
|
891
|
+
const devices = selectLocalMediaDevices(state);
|
|
892
|
+
if (!stream)
|
|
893
|
+
return;
|
|
894
|
+
const deviceData = getDeviceData({
|
|
895
|
+
audioTrack: stream.getAudioTracks()[0],
|
|
896
|
+
videoTrack: stream.getVideoTracks()[0],
|
|
897
|
+
devices,
|
|
898
|
+
});
|
|
899
|
+
dispatch(localStreamMetadataUpdated(deviceData));
|
|
900
|
+
},
|
|
901
|
+
});
|
|
902
|
+
startAppListening({
|
|
903
|
+
actionCreator: signalEvents.audioEnableRequested,
|
|
904
|
+
effect: ({ payload }, { dispatch }) => {
|
|
905
|
+
const { enable } = payload;
|
|
906
|
+
if (!enable) {
|
|
907
|
+
dispatch(toggleMicrophoneEnabled({ enabled: false }));
|
|
908
|
+
}
|
|
909
|
+
},
|
|
910
|
+
});
|
|
911
|
+
startAppListening({
|
|
912
|
+
actionCreator: signalEvents.videoEnableRequested,
|
|
913
|
+
effect: ({ payload }, { dispatch }) => {
|
|
914
|
+
const { enable } = payload;
|
|
915
|
+
if (!enable) {
|
|
916
|
+
dispatch(toggleCameraEnabled({ enabled: false }));
|
|
917
|
+
}
|
|
918
|
+
},
|
|
919
|
+
});
|
|
920
|
+
startAppListening({
|
|
921
|
+
actionCreator: signalEvents.breakoutSessionUpdated,
|
|
922
|
+
effect: ({ payload }, { dispatch }) => {
|
|
923
|
+
var _a;
|
|
924
|
+
if (((_a = payload.initiatedBy) === null || _a === void 0 ? void 0 : _a.active) === false) {
|
|
925
|
+
dispatch(toggleMicrophoneEnabled({ enabled: false }));
|
|
926
|
+
}
|
|
927
|
+
},
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
const NON_PERSON_ROLES = ["recorder", "streamer"];
|
|
931
|
+
|
|
932
|
+
const selectLocalParticipantRaw = (state) => state.localParticipant;
|
|
933
|
+
const selectSelfId = (state) => state.localParticipant.id;
|
|
934
|
+
const selectLocalParticipantDisplayName = (state) => state.localParticipant.displayName;
|
|
935
|
+
const selectLocalParticipantClientClaim = (state) => state.localParticipant.clientClaim;
|
|
936
|
+
const selectLocalParticipantIsScreenSharing = (state) => state.localParticipant.isScreenSharing;
|
|
937
|
+
const selectLocalParticipantStickyReaction = (state) => state.localParticipant.stickyReaction;
|
|
938
|
+
const selectLocalParticipantBreakoutGroup = (state) => state.localParticipant.breakoutGroup;
|
|
939
|
+
const selectLocalParticipantBreakoutAssigned = (state) => state.localParticipant.breakoutGroupAssigned;
|
|
940
|
+
const selectLocalParticipantView = createSelector(selectLocalParticipantRaw, selectLocalMediaStream, (participant, localStream) => {
|
|
941
|
+
const clientView = {
|
|
942
|
+
id: participant.id,
|
|
943
|
+
clientId: participant.id,
|
|
944
|
+
breakoutGroup: participant.breakoutGroup,
|
|
945
|
+
displayName: participant.displayName,
|
|
946
|
+
stream: localStream,
|
|
947
|
+
isLocalClient: true,
|
|
948
|
+
isAudioEnabled: participant.isAudioEnabled,
|
|
949
|
+
isVideoEnabled: participant.isVideoEnabled,
|
|
950
|
+
};
|
|
951
|
+
if (NON_PERSON_ROLES.includes(participant.roleName)) {
|
|
952
|
+
return null;
|
|
953
|
+
}
|
|
954
|
+
return clientView;
|
|
955
|
+
});
|
|
956
|
+
|
|
957
|
+
function createBreakout({ assignments, groups, startedAt, initiatedBy, breakoutStartedAt, breakoutEndedAt, breakoutNotification, breakoutTimerDuration, autoMoveToGroup, moveToGroupGracePeriod, autoMoveToMain, moveToMainGracePeriod, enforceAssignment, breakoutTimerSetting, } = {}) {
|
|
958
|
+
return {
|
|
959
|
+
assignments: assignments || null,
|
|
960
|
+
groups: groups || null,
|
|
961
|
+
startedAt: startedAt ? new Date(startedAt) : null,
|
|
962
|
+
initiatedBy,
|
|
963
|
+
breakoutStartedAt: breakoutStartedAt || null,
|
|
964
|
+
breakoutEndedAt: breakoutEndedAt || null,
|
|
965
|
+
breakoutNotification: breakoutNotification || null,
|
|
966
|
+
breakoutTimerDuration: breakoutTimerDuration || 1800,
|
|
967
|
+
autoMoveToGroup: autoMoveToGroup || false,
|
|
968
|
+
moveToGroupGracePeriod: moveToGroupGracePeriod || 10,
|
|
969
|
+
autoMoveToMain: autoMoveToMain || false,
|
|
970
|
+
moveToMainGracePeriod: moveToMainGracePeriod || 30,
|
|
971
|
+
enforceAssignment: enforceAssignment || false,
|
|
972
|
+
breakoutTimerSetting: breakoutTimerSetting || false,
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
const breakoutSliceInitialState = Object.assign(Object.assign({}, createBreakout()), { groupId: null });
|
|
976
|
+
const breakoutSlice = createSlice({
|
|
977
|
+
name: "breakout",
|
|
978
|
+
initialState: breakoutSliceInitialState,
|
|
979
|
+
reducers: {},
|
|
980
|
+
extraReducers: (builder) => {
|
|
981
|
+
builder.addCase(signalEvents.roomJoined, (state, action) => {
|
|
982
|
+
if ("error" in action.payload) {
|
|
983
|
+
return state;
|
|
984
|
+
}
|
|
985
|
+
const { breakout } = action.payload || {};
|
|
986
|
+
if (breakout) {
|
|
987
|
+
return Object.assign(Object.assign({}, state), createBreakout(breakout));
|
|
988
|
+
}
|
|
989
|
+
return state;
|
|
990
|
+
});
|
|
991
|
+
builder.addCase(signalEvents.breakoutSessionUpdated, (state, action) => {
|
|
992
|
+
return Object.assign(Object.assign({}, state), createBreakout(action.payload));
|
|
993
|
+
});
|
|
994
|
+
builder.addCase(signalEvents.breakoutGroupJoined, (state, action) => {
|
|
995
|
+
var _a;
|
|
996
|
+
if (((_a = action.meta) === null || _a === void 0 ? void 0 : _a.localParticipantId) !== action.payload.clientId) {
|
|
997
|
+
return state;
|
|
998
|
+
}
|
|
999
|
+
return Object.assign(Object.assign({}, state), { groupId: action.payload.group });
|
|
1000
|
+
});
|
|
1001
|
+
},
|
|
1002
|
+
});
|
|
1003
|
+
const doBreakoutJoin = createAppThunk((payload) => (_, getState) => {
|
|
1004
|
+
const state = getState();
|
|
1005
|
+
const { socket } = selectSignalConnectionRaw(state);
|
|
1006
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("join_breakout_group", { group: payload.group });
|
|
1007
|
+
});
|
|
1008
|
+
const selectBreakoutRaw = (state) => state.breakout;
|
|
1009
|
+
const selectBreakoutInitiatedBy = (state) => state.breakout.initiatedBy;
|
|
1010
|
+
const selectBreakoutActive = (state) => !!state.breakout.startedAt;
|
|
1011
|
+
const selectBreakoutAssignments = (state) => state.breakout.assignments;
|
|
1012
|
+
const selectBreakoutGroups = (state) => state.breakout.groups;
|
|
1013
|
+
const selectBreakoutCurrentId = createSelector(selectBreakoutRaw, selectLocalParticipantBreakoutGroup, (raw, localParticipantBreakoutGroup) => {
|
|
1014
|
+
return raw.groupId || localParticipantBreakoutGroup || "";
|
|
1015
|
+
});
|
|
1016
|
+
const selectBreakoutCurrentGroup = createSelector(selectBreakoutRaw, selectBreakoutCurrentId, (raw, breakoutCurrentId) => {
|
|
1017
|
+
var _a;
|
|
1018
|
+
const name = (_a = raw.groups) === null || _a === void 0 ? void 0 : _a[breakoutCurrentId];
|
|
1019
|
+
if (!name)
|
|
1020
|
+
return null;
|
|
1021
|
+
return { id: breakoutCurrentId, name };
|
|
1022
|
+
});
|
|
1023
|
+
startAppListening({
|
|
1024
|
+
actionCreator: signalEvents.breakoutMoveToGroup,
|
|
1025
|
+
effect: (_, { dispatch, getState }) => {
|
|
1026
|
+
const state = getState();
|
|
1027
|
+
const localParticipant = selectLocalParticipantRaw(state);
|
|
1028
|
+
const breakoutGroupAssigned = localParticipant.breakoutGroupAssigned;
|
|
1029
|
+
if (breakoutGroupAssigned) {
|
|
1030
|
+
dispatch(doBreakoutJoin({ group: breakoutGroupAssigned }));
|
|
1031
|
+
}
|
|
1032
|
+
},
|
|
1033
|
+
});
|
|
1034
|
+
startAppListening({
|
|
1035
|
+
actionCreator: signalEvents.breakoutMoveToMain,
|
|
1036
|
+
effect: (_, { dispatch }) => {
|
|
1037
|
+
dispatch(doBreakoutJoin({ group: "" }));
|
|
1038
|
+
},
|
|
1039
|
+
});
|
|
1040
|
+
startAppListening({
|
|
1041
|
+
actionCreator: signalEvents.breakoutSessionUpdated,
|
|
1042
|
+
effect: ({ payload }, { dispatch, getState }) => {
|
|
1043
|
+
var _a;
|
|
1044
|
+
const state = getState();
|
|
1045
|
+
const autoMoveToMain = selectBreakoutRaw(state).autoMoveToMain;
|
|
1046
|
+
if (((_a = payload.initiatedBy) === null || _a === void 0 ? void 0 : _a.active) === false) {
|
|
1047
|
+
if (!autoMoveToMain) {
|
|
1048
|
+
dispatch(doBreakoutJoin({ group: "" }));
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
},
|
|
1052
|
+
});
|
|
1053
|
+
|
|
1054
|
+
const chatSliceInitialState = {
|
|
1055
|
+
chatMessages: [],
|
|
1056
|
+
};
|
|
1057
|
+
const chatSlice = createSlice({
|
|
1058
|
+
name: "chat",
|
|
1059
|
+
initialState: chatSliceInitialState,
|
|
1060
|
+
reducers: {},
|
|
1061
|
+
extraReducers(builder) {
|
|
1062
|
+
builder.addCase(signalEvents.chatMessage, (state, action) => {
|
|
1063
|
+
const message = {
|
|
1064
|
+
senderId: action.payload.senderId,
|
|
1065
|
+
timestamp: action.payload.timestamp,
|
|
1066
|
+
text: action.payload.text,
|
|
1067
|
+
};
|
|
1068
|
+
return Object.assign(Object.assign({}, state), { chatMessages: [...state.chatMessages, message] });
|
|
1069
|
+
});
|
|
1070
|
+
},
|
|
1071
|
+
});
|
|
1072
|
+
const doSendChatMessage = createRoomConnectedThunk((payload) => (_, getState) => {
|
|
1073
|
+
const state = getState();
|
|
1074
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
1075
|
+
const breakoutCurrentId = selectBreakoutCurrentId(state);
|
|
1076
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("chat_message", Object.assign(Object.assign({ text: payload.text }, (breakoutCurrentId && { breakoutGroup: breakoutCurrentId })), (payload.isBroadcast && { broadcast: true })));
|
|
1077
|
+
});
|
|
1078
|
+
const selectChatRaw = (state) => state.chat;
|
|
1079
|
+
const selectChatMessages = (state) => state.chat.chatMessages;
|
|
1080
|
+
|
|
1081
|
+
const initialCloudRecordingState = {
|
|
1082
|
+
isRecording: false,
|
|
1083
|
+
error: null,
|
|
1084
|
+
startedAt: undefined,
|
|
1085
|
+
};
|
|
1086
|
+
const cloudRecordingSlice = createSlice({
|
|
1087
|
+
name: "cloudRecording",
|
|
1088
|
+
initialState: initialCloudRecordingState,
|
|
1089
|
+
reducers: {
|
|
1090
|
+
recordingRequestStarted: (state) => {
|
|
1091
|
+
return Object.assign(Object.assign({}, state), { status: "requested" });
|
|
1092
|
+
},
|
|
1093
|
+
},
|
|
1094
|
+
extraReducers: (builder) => {
|
|
1095
|
+
builder.addCase(signalEvents.cloudRecordingStopped, (state) => {
|
|
1096
|
+
return Object.assign(Object.assign({}, state), { isRecording: false, status: undefined });
|
|
1097
|
+
});
|
|
1098
|
+
builder.addCase(signalEvents.cloudRecordingStarted, (state, action) => {
|
|
1099
|
+
const { payload } = action;
|
|
1100
|
+
if (!payload.error) {
|
|
1101
|
+
return state;
|
|
1102
|
+
}
|
|
1103
|
+
return Object.assign(Object.assign({}, state), { isRecording: false, status: "error", error: payload.error });
|
|
1104
|
+
});
|
|
1105
|
+
builder.addCase(signalEvents.newClient, (state, { payload }) => {
|
|
1106
|
+
var _a;
|
|
1107
|
+
const { client } = payload;
|
|
1108
|
+
if (((_a = client.role) === null || _a === void 0 ? void 0 : _a.roleName) === "recorder") {
|
|
1109
|
+
return Object.assign(Object.assign({}, state), { isRecording: true, status: "recording", startedAt: client.startedCloudRecordingAt
|
|
1110
|
+
? new Date(client.startedCloudRecordingAt).getTime()
|
|
1111
|
+
: new Date().getTime() });
|
|
1112
|
+
}
|
|
1113
|
+
return state;
|
|
1114
|
+
});
|
|
1115
|
+
},
|
|
1116
|
+
});
|
|
1117
|
+
const { recordingRequestStarted } = cloudRecordingSlice.actions;
|
|
1118
|
+
const doStartCloudRecording = createRoomConnectedThunk(() => (dispatch, getState) => {
|
|
1119
|
+
const state = getState();
|
|
1120
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
1121
|
+
const status = selectCloudRecordingStatus(state);
|
|
1122
|
+
if (status && ["recording", "requested"].includes(status)) {
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("start_recording", {
|
|
1126
|
+
recording: "cloud",
|
|
1127
|
+
});
|
|
1128
|
+
dispatch(recordingRequestStarted());
|
|
1129
|
+
});
|
|
1130
|
+
const doStopCloudRecording = createRoomConnectedThunk(() => (dispatch, getState) => {
|
|
1131
|
+
const state = getState();
|
|
1132
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
1133
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("stop_recording");
|
|
1134
|
+
});
|
|
1135
|
+
const selectCloudRecordingRaw = (state) => state.cloudRecording;
|
|
1136
|
+
const selectCloudRecordingStatus = (state) => state.cloudRecording.status;
|
|
1137
|
+
const selectCloudRecordingStartedAt = (state) => state.cloudRecording.startedAt;
|
|
1138
|
+
const selectCloudRecordingError = (state) => state.cloudRecording.error;
|
|
1139
|
+
const selectIsCloudRecording = (state) => state.cloudRecording.isRecording;
|
|
1140
|
+
|
|
1141
|
+
const selectRoomConnectionRaw = (state) => state.roomConnection;
|
|
1142
|
+
const selectRoomConnectionSession = (state) => state.roomConnection.session;
|
|
1143
|
+
const selectRoomConnectionSessionId = (state) => { var _a; return (_a = state.roomConnection.session) === null || _a === void 0 ? void 0 : _a.id; };
|
|
1144
|
+
const selectRoomConnectionStatus = (state) => state.roomConnection.status;
|
|
1145
|
+
const selectRoomConnectionError = (state) => state.roomConnection.error;
|
|
1146
|
+
|
|
1147
|
+
function createRtcEventAction(name) {
|
|
1148
|
+
return createAction(`rtcConnection/event/${name}`);
|
|
1149
|
+
}
|
|
1150
|
+
const rtcEvents = {
|
|
1151
|
+
rtcManagerCreated: createRtcEventAction("rtcManagerCreated"),
|
|
1152
|
+
rtcManagerDestroyed: createRtcEventAction("rtcManagerDestroyed"),
|
|
1153
|
+
streamAdded: createRtcEventAction("streamAdded"),
|
|
1154
|
+
clientConnectionStatusChanged: createRtcEventAction("clientConnectionStatusChanged"),
|
|
1155
|
+
};
|
|
1156
|
+
|
|
1157
|
+
function createRemoteParticipant(client, newJoiner = false) {
|
|
1158
|
+
const { streams, role, breakoutGroup } = client, rest = __rest(client, ["streams", "role", "breakoutGroup"]);
|
|
1159
|
+
return Object.assign(Object.assign({}, rest), { stream: null, streams: streams.map((streamId) => ({ id: streamId, state: newJoiner ? "new_accept" : "to_accept" })), isLocalParticipant: false, roleName: (role === null || role === void 0 ? void 0 : role.roleName) || "none", presentationStream: null, breakoutGroup: breakoutGroup || null, newJoiner });
|
|
1160
|
+
}
|
|
1161
|
+
function findParticipant(state, participantId) {
|
|
1162
|
+
const index = state.remoteParticipants.findIndex((c) => c.id === participantId);
|
|
1163
|
+
return { index, participant: state.remoteParticipants[index] };
|
|
1164
|
+
}
|
|
1165
|
+
function updateParticipant(state, participantId, updates) {
|
|
1166
|
+
const { participant, index } = findParticipant(state, participantId);
|
|
1167
|
+
if (!participant) {
|
|
1168
|
+
console.error(`Did not find client for update ${participantId}`);
|
|
1169
|
+
return state;
|
|
1170
|
+
}
|
|
1171
|
+
return Object.assign(Object.assign({}, state), { remoteParticipants: [
|
|
1172
|
+
...state.remoteParticipants.slice(0, index),
|
|
1173
|
+
Object.assign(Object.assign({}, participant), updates),
|
|
1174
|
+
...state.remoteParticipants.slice(index + 1),
|
|
1175
|
+
] });
|
|
1176
|
+
}
|
|
1177
|
+
function addParticipant(state, participant) {
|
|
1178
|
+
const { participant: foundParticipant } = findParticipant(state, participant.id);
|
|
1179
|
+
if (foundParticipant) {
|
|
1180
|
+
console.warn(`Client already existing ${participant.id}. Ignoring`);
|
|
1181
|
+
return state;
|
|
1182
|
+
}
|
|
1183
|
+
return Object.assign(Object.assign({}, state), { remoteParticipants: [...state.remoteParticipants, participant] });
|
|
1184
|
+
}
|
|
1185
|
+
function updateStreamState(state, participantId, streamId, state_) {
|
|
1186
|
+
const { participant } = findParticipant(state, participantId);
|
|
1187
|
+
if (!participant) {
|
|
1188
|
+
console.error(`No client ${participantId} found to update stream ${streamId} ${state_}`);
|
|
1189
|
+
return state;
|
|
1190
|
+
}
|
|
1191
|
+
const idIdx = participant.streams.findIndex((s) => s.id === streamId);
|
|
1192
|
+
const streams = [...participant.streams];
|
|
1193
|
+
streams[idIdx] = Object.assign(Object.assign({}, streams[idIdx]), { state: state_ });
|
|
1194
|
+
return updateParticipant(state, participantId, { streams });
|
|
1195
|
+
}
|
|
1196
|
+
function removeClient(state, participantId) {
|
|
1197
|
+
return Object.assign(Object.assign({}, state), { remoteParticipants: state.remoteParticipants.filter((c) => c.id !== participantId) });
|
|
1198
|
+
}
|
|
1199
|
+
function addStreamId(state, participantId, streamId) {
|
|
1200
|
+
const { participant } = findParticipant(state, participantId);
|
|
1201
|
+
if (!participant || participant.streams.find((s) => s.id === streamId)) {
|
|
1202
|
+
console.warn(`No participant ${participantId} or stream ${streamId} already exists`);
|
|
1203
|
+
return state;
|
|
1204
|
+
}
|
|
1205
|
+
return updateParticipant(state, participantId, {
|
|
1206
|
+
streams: [...participant.streams, { id: streamId, state: "to_accept" }],
|
|
1207
|
+
});
|
|
1208
|
+
}
|
|
1209
|
+
function removeStreamId(state, participantId, streamId) {
|
|
1210
|
+
var _a, _b;
|
|
1211
|
+
const { participant } = findParticipant(state, participantId);
|
|
1212
|
+
if (!participant) {
|
|
1213
|
+
console.error(`No participant ${participantId} found to remove stream ${streamId}`);
|
|
1214
|
+
return state;
|
|
1215
|
+
}
|
|
1216
|
+
const currentStreamId = participant.stream && participant.stream.id;
|
|
1217
|
+
const presentationId = ((_a = participant.presentationStream) === null || _a === void 0 ? void 0 : _a.inboundId) || ((_b = participant.presentationStream) === null || _b === void 0 ? void 0 : _b.id);
|
|
1218
|
+
const idIdx = participant.streams.findIndex((s) => s.id === streamId);
|
|
1219
|
+
return updateParticipant(state, participantId, Object.assign(Object.assign({ streams: participant.streams.filter((_, i) => i !== idIdx) }, (currentStreamId === streamId && { stream: null })), (presentationId === streamId && { presentationStream: null })));
|
|
1220
|
+
}
|
|
1221
|
+
function addStream(state, payload) {
|
|
1222
|
+
const { clientId, stream, streamType } = payload;
|
|
1223
|
+
let { streamId } = payload;
|
|
1224
|
+
const { participant } = findParticipant(state, clientId);
|
|
1225
|
+
if (!participant) {
|
|
1226
|
+
console.error(`Did not find client ${clientId} for adding stream`);
|
|
1227
|
+
return state;
|
|
1228
|
+
}
|
|
1229
|
+
const remoteParticipants = state.remoteParticipants;
|
|
1230
|
+
if (!streamId) {
|
|
1231
|
+
streamId = stream.id;
|
|
1232
|
+
}
|
|
1233
|
+
const remoteParticipant = remoteParticipants.find((p) => p.id === clientId);
|
|
1234
|
+
if (!remoteParticipant) {
|
|
1235
|
+
return state;
|
|
1236
|
+
}
|
|
1237
|
+
const remoteParticipantStream = remoteParticipant.streams.find((s) => s.id === streamId);
|
|
1238
|
+
if ((remoteParticipant.stream &&
|
|
1239
|
+
(remoteParticipant.stream.id === streamId || remoteParticipant.stream.inboundId === streamId)) ||
|
|
1240
|
+
(!remoteParticipant.stream && streamType === "webcam") ||
|
|
1241
|
+
(!remoteParticipant.stream && !streamType && !remoteParticipantStream)) {
|
|
1242
|
+
return updateParticipant(state, clientId, { stream });
|
|
1243
|
+
}
|
|
1244
|
+
return updateParticipant(state, clientId, {
|
|
1245
|
+
presentationStream: stream,
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
1248
|
+
const remoteParticipantsSliceInitialState = {
|
|
1249
|
+
remoteParticipants: [],
|
|
1250
|
+
};
|
|
1251
|
+
const remoteParticipantsSlice = createSlice({
|
|
1252
|
+
name: "remoteParticipants",
|
|
1253
|
+
initialState: remoteParticipantsSliceInitialState,
|
|
1254
|
+
reducers: {
|
|
1255
|
+
streamStatusUpdated: (state, action) => {
|
|
1256
|
+
let newState = state;
|
|
1257
|
+
for (const { clientId, streamId, state } of action.payload) {
|
|
1258
|
+
newState = updateStreamState(newState, clientId, streamId, state);
|
|
1259
|
+
}
|
|
1260
|
+
return newState;
|
|
1261
|
+
},
|
|
1262
|
+
participantStreamAdded: (state, action) => {
|
|
1263
|
+
const { clientId, stream } = action.payload;
|
|
1264
|
+
return updateParticipant(state, clientId, {
|
|
1265
|
+
stream,
|
|
1266
|
+
});
|
|
1267
|
+
},
|
|
1268
|
+
participantStreamIdAdded: (state, action) => {
|
|
1269
|
+
const { clientId, streamId } = action.payload;
|
|
1270
|
+
return addStreamId(state, clientId, streamId);
|
|
1271
|
+
},
|
|
1272
|
+
},
|
|
1273
|
+
extraReducers: (builder) => {
|
|
1274
|
+
builder.addCase(signalEvents.roomJoined, (state, action) => {
|
|
1275
|
+
if ("error" in action.payload) {
|
|
1276
|
+
return state;
|
|
1277
|
+
}
|
|
1278
|
+
const { room, selfId } = action.payload || {};
|
|
1279
|
+
if (room === null || room === void 0 ? void 0 : room.clients) {
|
|
1280
|
+
return Object.assign(Object.assign({}, state), { remoteParticipants: room.clients
|
|
1281
|
+
.filter((c) => c.id !== selfId)
|
|
1282
|
+
.map((c) => createRemoteParticipant(c)) });
|
|
1283
|
+
}
|
|
1284
|
+
return state;
|
|
1285
|
+
});
|
|
1286
|
+
builder.addCase(rtcEvents.streamAdded, (state, action) => {
|
|
1287
|
+
return addStream(state, action.payload);
|
|
1288
|
+
});
|
|
1289
|
+
builder.addCase(signalEvents.newClient, (state, action) => {
|
|
1290
|
+
const { client } = action.payload;
|
|
1291
|
+
return addParticipant(state, createRemoteParticipant(client, true));
|
|
1292
|
+
});
|
|
1293
|
+
builder.addCase(signalEvents.clientLeft, (state, action) => {
|
|
1294
|
+
const { clientId } = action.payload;
|
|
1295
|
+
return removeClient(state, clientId);
|
|
1296
|
+
});
|
|
1297
|
+
builder.addCase(signalEvents.audioEnabled, (state, action) => {
|
|
1298
|
+
const { clientId, isAudioEnabled } = action.payload;
|
|
1299
|
+
return updateParticipant(state, clientId, {
|
|
1300
|
+
isAudioEnabled,
|
|
1301
|
+
});
|
|
1302
|
+
});
|
|
1303
|
+
builder.addCase(signalEvents.videoEnabled, (state, action) => {
|
|
1304
|
+
const { clientId, isVideoEnabled } = action.payload;
|
|
1305
|
+
return updateParticipant(state, clientId, {
|
|
1306
|
+
isVideoEnabled,
|
|
1307
|
+
});
|
|
1308
|
+
});
|
|
1309
|
+
builder.addCase(signalEvents.clientMetadataReceived, (state, action) => {
|
|
1310
|
+
const { error, payload } = action.payload;
|
|
1311
|
+
if (error || !payload) {
|
|
1312
|
+
console.warn(error || "Client metadata error received");
|
|
1313
|
+
return state;
|
|
1314
|
+
}
|
|
1315
|
+
const { clientId, displayName, stickyReaction } = payload;
|
|
1316
|
+
return updateParticipant(state, clientId, {
|
|
1317
|
+
displayName,
|
|
1318
|
+
stickyReaction,
|
|
1319
|
+
});
|
|
1320
|
+
});
|
|
1321
|
+
builder.addCase(signalEvents.breakoutGroupJoined, (state, action) => {
|
|
1322
|
+
const { clientId, group } = action.payload;
|
|
1323
|
+
return updateParticipant(state, clientId, {
|
|
1324
|
+
breakoutGroup: group || null,
|
|
1325
|
+
});
|
|
1326
|
+
});
|
|
1327
|
+
builder.addCase(signalEvents.screenshareStarted, (state, action) => {
|
|
1328
|
+
const { clientId, streamId } = action.payload;
|
|
1329
|
+
return addStreamId(state, clientId, streamId);
|
|
1330
|
+
});
|
|
1331
|
+
builder.addCase(signalEvents.screenshareStopped, (state, action) => {
|
|
1332
|
+
const { clientId, streamId } = action.payload;
|
|
1333
|
+
return removeStreamId(state, clientId, streamId);
|
|
1334
|
+
});
|
|
1335
|
+
},
|
|
1336
|
+
});
|
|
1337
|
+
const { participantStreamAdded, participantStreamIdAdded, streamStatusUpdated } = remoteParticipantsSlice.actions;
|
|
1338
|
+
const doRequestAudioEnable = createAuthorizedRoomConnectedThunk((state) => selectIsAuthorizedToRequestAudioEnable(state), (payload) => (_, getState) => {
|
|
1339
|
+
const state = getState();
|
|
1340
|
+
const canEnableRemoteAudio = selectIsAuthorizedToAskToSpeak(state);
|
|
1341
|
+
if (payload.enable && !canEnableRemoteAudio) {
|
|
1342
|
+
console.warn("Not authorized to perform this action");
|
|
1343
|
+
return;
|
|
1344
|
+
}
|
|
1345
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
1346
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("request_audio_enable", payload);
|
|
1347
|
+
});
|
|
1348
|
+
const doRequestVideoEnable = createAuthorizedRoomConnectedThunk((state) => selectIsAuthorizedToRequestVideoEnable(state), (payload) => (_, getState) => {
|
|
1349
|
+
const state = getState();
|
|
1350
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
1351
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("request_video_enable", payload);
|
|
1352
|
+
});
|
|
1353
|
+
const selectRemoteParticipantsRaw = (state) => state.remoteParticipants;
|
|
1354
|
+
const selectRemoteClients = (state) => state.remoteParticipants.remoteParticipants;
|
|
1355
|
+
const selectRemoteParticipants = createSelector(selectRemoteClients, (clients) => clients.filter((c) => !NON_PERSON_ROLES.includes(c.roleName)));
|
|
1356
|
+
const selectNumClients = createSelector(selectRemoteClients, (clients) => clients.length + 1);
|
|
1357
|
+
const selectNumParticipants = createSelector(selectRemoteParticipants, selectLocalParticipantRaw, (clients, localParticipant) => {
|
|
1358
|
+
if (NON_PERSON_ROLES.includes(localParticipant.roleName)) {
|
|
1359
|
+
return clients.length;
|
|
1360
|
+
}
|
|
1361
|
+
return clients.length + 1;
|
|
1362
|
+
});
|
|
1363
|
+
|
|
1364
|
+
const localScreenshareSliceInitialState = {
|
|
1365
|
+
status: "inactive",
|
|
1366
|
+
stream: null,
|
|
1367
|
+
error: null,
|
|
1368
|
+
};
|
|
1369
|
+
const localScreenshareSlice = createSlice({
|
|
1370
|
+
name: "localScreenshare",
|
|
1371
|
+
initialState: localScreenshareSliceInitialState,
|
|
1372
|
+
reducers: {
|
|
1373
|
+
stopScreenshare(state, action) {
|
|
1374
|
+
return Object.assign(Object.assign({}, state), { status: "inactive", stream: null });
|
|
1375
|
+
},
|
|
1376
|
+
},
|
|
1377
|
+
extraReducers: (builder) => {
|
|
1378
|
+
builder.addCase(doStartScreenshare.pending, (state) => {
|
|
1379
|
+
return Object.assign(Object.assign({}, state), { status: "starting" });
|
|
1380
|
+
});
|
|
1381
|
+
builder.addCase(doStartScreenshare.fulfilled, (state, { payload: { stream } }) => {
|
|
1382
|
+
return Object.assign(Object.assign({}, state), { status: "active", stream });
|
|
1383
|
+
});
|
|
1384
|
+
builder.addCase(doStartScreenshare.rejected, (state, { payload }) => {
|
|
1385
|
+
return Object.assign(Object.assign({}, state), { error: payload, status: "inactive", stream: null });
|
|
1386
|
+
});
|
|
1387
|
+
},
|
|
1388
|
+
});
|
|
1389
|
+
const { stopScreenshare } = localScreenshareSlice.actions;
|
|
1390
|
+
const doStartScreenshare = createAsyncRoomConnectedThunk("localScreenshare/doStartScreenshare", (_1, _a) => __awaiter(void 0, [_1, _a], void 0, function* (_, { dispatch, getState, rejectWithValue }) {
|
|
1391
|
+
var _b;
|
|
1392
|
+
try {
|
|
1393
|
+
const state = getState();
|
|
1394
|
+
const screenshareStream = selectLocalScreenshareStream(state);
|
|
1395
|
+
if (screenshareStream) {
|
|
1396
|
+
return { stream: screenshareStream };
|
|
1397
|
+
}
|
|
1398
|
+
const stream = yield navigator.mediaDevices.getDisplayMedia();
|
|
1399
|
+
const onEnded = () => {
|
|
1400
|
+
dispatch(doStopScreenshare());
|
|
1401
|
+
};
|
|
1402
|
+
if ("oninactive" in stream) {
|
|
1403
|
+
stream.addEventListener("inactive", onEnded);
|
|
1404
|
+
}
|
|
1405
|
+
else {
|
|
1406
|
+
(_b = stream.getVideoTracks()[0]) === null || _b === void 0 ? void 0 : _b.addEventListener("ended", onEnded);
|
|
1407
|
+
}
|
|
1408
|
+
return { stream };
|
|
1409
|
+
}
|
|
1410
|
+
catch (error) {
|
|
1411
|
+
return rejectWithValue(error);
|
|
1412
|
+
}
|
|
1413
|
+
}));
|
|
1414
|
+
const doStopScreenshare = createRoomConnectedThunk(() => (dispatch, getState) => {
|
|
1415
|
+
const state = getState();
|
|
1416
|
+
const screenshareStream = selectLocalScreenshareStream(state);
|
|
1417
|
+
if (!screenshareStream) {
|
|
1418
|
+
return;
|
|
1419
|
+
}
|
|
1420
|
+
screenshareStream.getTracks().forEach((track) => track.stop());
|
|
1421
|
+
dispatch(stopScreenshare({ stream: screenshareStream }));
|
|
1422
|
+
});
|
|
1423
|
+
const selectLocalScreenshareRaw = (state) => state.localScreenshare;
|
|
1424
|
+
const selectLocalScreenshareStatus = (state) => state.localScreenshare.status;
|
|
1425
|
+
const selectLocalScreenshareStream = (state) => state.localScreenshare.stream;
|
|
1426
|
+
startAppListening({
|
|
1427
|
+
actionCreator: localMediaStopped,
|
|
1428
|
+
effect: (_, { getState }) => {
|
|
1429
|
+
const state = getState();
|
|
1430
|
+
const screenshareStream = selectLocalScreenshareStream(state);
|
|
1431
|
+
if (!screenshareStream) {
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
screenshareStream === null || screenshareStream === void 0 ? void 0 : screenshareStream.getTracks().forEach((track) => {
|
|
1435
|
+
track.stop();
|
|
1436
|
+
});
|
|
1437
|
+
},
|
|
1438
|
+
});
|
|
1439
|
+
|
|
1440
|
+
function isStreamerClient(client) {
|
|
1441
|
+
return client.roleName === "streamer";
|
|
1442
|
+
}
|
|
1443
|
+
function isRecorderClient(client) {
|
|
1444
|
+
return client.roleName === "recorder";
|
|
1445
|
+
}
|
|
1446
|
+
const roomSliceInitialState = {
|
|
1447
|
+
isLocked: false,
|
|
1448
|
+
};
|
|
1449
|
+
const roomSlice = createSlice({
|
|
1450
|
+
name: "room",
|
|
1451
|
+
initialState: roomSliceInitialState,
|
|
1452
|
+
reducers: {},
|
|
1453
|
+
extraReducers: (builder) => {
|
|
1454
|
+
builder.addCase(signalEvents.roomJoined, (state, action) => {
|
|
1455
|
+
if ("error" in action.payload) {
|
|
1456
|
+
if (action.payload.error === "room_locked") {
|
|
1457
|
+
return Object.assign(Object.assign({}, state), { isLocked: Boolean(action.payload.isLocked) });
|
|
1458
|
+
}
|
|
1459
|
+
return state;
|
|
1460
|
+
}
|
|
1461
|
+
const { room } = action.payload;
|
|
1462
|
+
return Object.assign(Object.assign({}, state), { isLocked: Boolean(room.isLocked) });
|
|
1463
|
+
});
|
|
1464
|
+
builder.addCase(signalEvents.roomLocked, (state, action) => {
|
|
1465
|
+
const { isLocked } = action.payload;
|
|
1466
|
+
return Object.assign(Object.assign({}, state), { isLocked: Boolean(isLocked) });
|
|
1467
|
+
});
|
|
1468
|
+
},
|
|
1469
|
+
});
|
|
1470
|
+
const doLockRoom = createAppAuthorizedThunk((state) => selectIsAuthorizedToLockRoom(state), (payload) => (_, getState) => {
|
|
1471
|
+
const state = getState();
|
|
1472
|
+
const { socket } = selectSignalConnectionRaw(state);
|
|
1473
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("set_lock", { locked: payload.locked });
|
|
1474
|
+
});
|
|
1475
|
+
const doKickParticipant = createAppAuthorizedThunk((state) => selectIsAuthorizedToKickClient(state), (payload) => (_, getState) => {
|
|
1476
|
+
const state = getState();
|
|
1477
|
+
const { socket } = selectSignalConnectionRaw(state);
|
|
1478
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("kick_client", { clientId: payload.clientId, reasonId: "kick" });
|
|
1479
|
+
});
|
|
1480
|
+
const doEndMeeting = createAppAuthorizedThunk((state) => selectIsAuthorizedToEndMeeting(state), (payload) => (dispatch, getState) => {
|
|
1481
|
+
const state = getState();
|
|
1482
|
+
const clientsToKick = selectRemoteClients(state).map((c) => c.id);
|
|
1483
|
+
if (clientsToKick.length) {
|
|
1484
|
+
const { socket } = selectSignalConnectionRaw(state);
|
|
1485
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("kick_client", { clientIds: clientsToKick, reasonId: "end-meeting" });
|
|
1486
|
+
}
|
|
1487
|
+
if (!payload.stayBehind) {
|
|
1488
|
+
dispatch(doAppStop());
|
|
1489
|
+
}
|
|
1490
|
+
});
|
|
1491
|
+
const selectRoomIsLocked = (state) => state.room.isLocked;
|
|
1492
|
+
const selectScreenshares = createSelector(selectLocalScreenshareStream, selectLocalParticipantRaw, selectRemoteParticipants, (localScreenshareStream, localParticipant, remoteParticipants) => {
|
|
1493
|
+
const screenshares = [];
|
|
1494
|
+
if (localScreenshareStream) {
|
|
1495
|
+
screenshares.push({
|
|
1496
|
+
id: localScreenshareStream.id || "local-screenshare",
|
|
1497
|
+
participantId: "local",
|
|
1498
|
+
hasAudioTrack: localScreenshareStream.getTracks().some((track) => track.kind === "audio"),
|
|
1499
|
+
breakoutGroup: localParticipant.breakoutGroup,
|
|
1500
|
+
stream: localScreenshareStream,
|
|
1501
|
+
isLocal: true,
|
|
1502
|
+
});
|
|
1503
|
+
}
|
|
1504
|
+
for (const participant of remoteParticipants) {
|
|
1505
|
+
if (participant.presentationStream) {
|
|
1506
|
+
screenshares.push({
|
|
1507
|
+
id: participant.presentationStream.id || `pres-${participant.id}`,
|
|
1508
|
+
participantId: participant.id,
|
|
1509
|
+
hasAudioTrack: participant.presentationStream.getTracks().some((track) => track.kind === "audio"),
|
|
1510
|
+
breakoutGroup: participant.breakoutGroup,
|
|
1511
|
+
stream: participant.presentationStream,
|
|
1512
|
+
isLocal: false,
|
|
1513
|
+
});
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
return screenshares;
|
|
1517
|
+
});
|
|
1518
|
+
const selectRemoteClientViews = createSelector(selectLocalScreenshareStream, selectLocalParticipantRaw, selectRemoteParticipants, selectBreakoutCurrentId, selectBreakoutAssignments, (localScreenshareStream, localParticipant, remoteParticipants, breakoutCurrentId, breakoutAssignments) => {
|
|
1519
|
+
const views = [];
|
|
1520
|
+
if (localScreenshareStream) {
|
|
1521
|
+
const isScreenshareAudioEnabled = !!localScreenshareStream.getAudioTracks().length;
|
|
1522
|
+
views.push({
|
|
1523
|
+
clientId: localParticipant.id,
|
|
1524
|
+
displayName: "Your screenshare",
|
|
1525
|
+
id: "local-screenshare",
|
|
1526
|
+
isAudioEnabled: isScreenshareAudioEnabled,
|
|
1527
|
+
isLocalClient: true,
|
|
1528
|
+
isPresentation: true,
|
|
1529
|
+
isVideoEnabled: true,
|
|
1530
|
+
stream: localScreenshareStream,
|
|
1531
|
+
breakoutGroup: breakoutCurrentId || "",
|
|
1532
|
+
});
|
|
1533
|
+
}
|
|
1534
|
+
for (const c of remoteParticipants) {
|
|
1535
|
+
if (isStreamerClient(c) || isRecorderClient(c)) {
|
|
1536
|
+
continue;
|
|
1537
|
+
}
|
|
1538
|
+
const { presentationStream } = c, clientView = __rest(c, ["presentationStream"]);
|
|
1539
|
+
const displayName = c.displayName || "Guest";
|
|
1540
|
+
const isPresentationActive = presentationStream && presentationStream.active;
|
|
1541
|
+
const presentationId = "pres-" + c.id;
|
|
1542
|
+
const isStreamActive = c.stream && c.stream.active;
|
|
1543
|
+
const isVideoEnabled = c.isVideoEnabled;
|
|
1544
|
+
views.push(Object.assign(Object.assign(Object.assign({}, clientView), { breakoutGroupAssigned: (breakoutAssignments === null || breakoutAssignments === void 0 ? void 0 : breakoutAssignments[c.deviceId]) || "", clientId: c.id, displayName, hasActivePresentation: !!isPresentationActive }), (c.isVideoEnabled ? { isVideoEnabled } : {})));
|
|
1545
|
+
if (isPresentationActive) {
|
|
1546
|
+
views.push(Object.assign(Object.assign(Object.assign({}, clientView), { clientId: c.id, stream: c.presentationStream, displayName: `Screenshare (${displayName})`, id: presentationId, isPresentation: true, isVideoEnabled: true }), (isStreamActive && { isRecording: null })));
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
return views;
|
|
1550
|
+
});
|
|
1551
|
+
const selectAllClientViews = createSelector(selectLocalParticipantView, selectRemoteClientViews, (localParticipant, remoteParticipants) => {
|
|
1552
|
+
return [...(localParticipant ? [localParticipant] : []), ...remoteParticipants];
|
|
1553
|
+
});
|
|
1554
|
+
const selectAllClientViewsInCurrentGroup = createSelector(selectAllClientViews, selectBreakoutActive, selectBreakoutCurrentId, (allClientViews, breakoutActive, breakoutCurrentId) => {
|
|
1555
|
+
if (!breakoutActive || !breakoutCurrentId) {
|
|
1556
|
+
return allClientViews;
|
|
1557
|
+
}
|
|
1558
|
+
return allClientViews.filter((client) => client.isLocalClient ||
|
|
1559
|
+
client.breakoutGroup === (breakoutCurrentId || "") ||
|
|
1560
|
+
(client.breakoutGroupAssigned && client.breakoutGroupAssigned === breakoutCurrentId));
|
|
1561
|
+
});
|
|
1562
|
+
const selectBreakoutGroupedParticipants = createSelector(selectAllClientViews, selectBreakoutActive, selectBreakoutGroups, (clientViews, breakoutActive, breakoutGroups) => {
|
|
1563
|
+
if (!breakoutActive || !breakoutGroups)
|
|
1564
|
+
return [];
|
|
1565
|
+
const clientsInMainRoom = clientViews.filter((client) => !client.breakoutGroup || client.breakoutGroup === "");
|
|
1566
|
+
const mainRoom = {
|
|
1567
|
+
clients: clientsInMainRoom,
|
|
1568
|
+
group: { id: "", name: "" },
|
|
1569
|
+
};
|
|
1570
|
+
const groups = Object.entries(breakoutGroups).map(([id, name]) => ({
|
|
1571
|
+
clients: clientViews.filter((client) => !client.isPresentation && client.breakoutGroup === id),
|
|
1572
|
+
group: { id, name },
|
|
1573
|
+
}));
|
|
1574
|
+
return [mainRoom, ...groups];
|
|
1575
|
+
});
|
|
1576
|
+
|
|
1577
|
+
function streamIdForClient({ isPresentation, stream }) {
|
|
1578
|
+
var _a, _b;
|
|
1579
|
+
return isPresentation ? (_b = (_a = stream === null || stream === void 0 ? void 0 : stream.outboundId) !== null && _a !== void 0 ? _a : stream === null || stream === void 0 ? void 0 : stream.inboundId) !== null && _b !== void 0 ? _b : stream === null || stream === void 0 ? void 0 : stream.id : "0";
|
|
1580
|
+
}
|
|
1581
|
+
function isClientSpotlighted({ spotlights, isPresentation, clientId, stream, }) {
|
|
1582
|
+
return !!spotlights.find((s) => {
|
|
1583
|
+
const streamId = streamIdForClient({ isPresentation, stream });
|
|
1584
|
+
return s.clientId === clientId && s.streamId === streamId;
|
|
1585
|
+
});
|
|
1586
|
+
}
|
|
1587
|
+
function mergeSpotlight(spotlights, spotlight) {
|
|
1588
|
+
const found = spotlights.find((s) => s.clientId === spotlight.clientId && s.streamId === spotlight.streamId);
|
|
1589
|
+
if (found) {
|
|
1590
|
+
return spotlights;
|
|
1591
|
+
}
|
|
1592
|
+
return spotlights.concat(spotlight);
|
|
1593
|
+
}
|
|
1594
|
+
function mapSpotlightsToClientViews(spotlights, clientViews) {
|
|
1595
|
+
return spotlights.reduce((acc, s) => {
|
|
1596
|
+
const clientView = clientViews.find((c) => s.clientId === c.clientId && s.streamId === streamIdForClient(c));
|
|
1597
|
+
if (clientView && !acc.includes(clientView)) {
|
|
1598
|
+
acc.push(clientView);
|
|
1599
|
+
}
|
|
1600
|
+
return acc;
|
|
1601
|
+
}, []);
|
|
1602
|
+
}
|
|
1603
|
+
const spotlightsSliceInitialState = {
|
|
1604
|
+
sorted: [],
|
|
1605
|
+
};
|
|
1606
|
+
const spotlightsSlice = createSlice({
|
|
1607
|
+
name: "spotlights",
|
|
1608
|
+
initialState: spotlightsSliceInitialState,
|
|
1609
|
+
reducers: {
|
|
1610
|
+
addSpotlight(state, action) {
|
|
1611
|
+
const { clientId, streamId } = action.payload;
|
|
1612
|
+
return Object.assign(Object.assign({}, state), { sorted: mergeSpotlight(state.sorted, { clientId, streamId }) });
|
|
1613
|
+
},
|
|
1614
|
+
removeSpotlight(state, action) {
|
|
1615
|
+
const { clientId, streamId } = action.payload;
|
|
1616
|
+
return Object.assign(Object.assign({}, state), { sorted: state.sorted.filter((s) => !(s.clientId === clientId && s.streamId === streamId)) });
|
|
1617
|
+
},
|
|
1618
|
+
},
|
|
1619
|
+
extraReducers: (builder) => {
|
|
1620
|
+
builder.addCase(signalEvents.roomJoined, (state, action) => {
|
|
1621
|
+
var _a;
|
|
1622
|
+
if ("error" in action.payload) {
|
|
1623
|
+
return state;
|
|
1624
|
+
}
|
|
1625
|
+
const { room } = action.payload || {};
|
|
1626
|
+
if (room) {
|
|
1627
|
+
return Object.assign(Object.assign({}, state), { sorted: (_a = room.spotlights) !== null && _a !== void 0 ? _a : state.sorted });
|
|
1628
|
+
}
|
|
1629
|
+
return state;
|
|
1630
|
+
});
|
|
1631
|
+
builder.addCase(signalEvents.spotlightAdded, (state, action) => {
|
|
1632
|
+
const { clientId, streamId } = action.payload;
|
|
1633
|
+
return Object.assign(Object.assign({}, state), { sorted: mergeSpotlight(state.sorted, { clientId, streamId }) });
|
|
1634
|
+
});
|
|
1635
|
+
builder.addCase(signalEvents.spotlightRemoved, (state, action) => {
|
|
1636
|
+
const { clientId, streamId } = action.payload;
|
|
1637
|
+
return Object.assign(Object.assign({}, state), { sorted: state.sorted.filter((s) => !(s.clientId === clientId && s.streamId === streamId)) });
|
|
1638
|
+
});
|
|
1639
|
+
builder.addMatcher(isAnyOf(signalEvents.clientKicked, signalEvents.clientLeft), (state, action) => {
|
|
1640
|
+
const { clientId } = action.payload;
|
|
1641
|
+
return Object.assign(Object.assign({}, state), { sorted: state.sorted.filter((s) => s.clientId !== clientId) });
|
|
1642
|
+
});
|
|
1643
|
+
},
|
|
1644
|
+
});
|
|
1645
|
+
const { addSpotlight, removeSpotlight } = spotlightsSlice.actions;
|
|
1646
|
+
const doSpotlightParticipant = createAppAuthorizedThunk((state) => selectIsAuthorizedToSpotlight(state), ({ id }) => (_, getState) => {
|
|
1647
|
+
const state = getState();
|
|
1648
|
+
const clientView = selectAllClientViews(state).find((c) => c.clientId === id);
|
|
1649
|
+
if (!clientView) {
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1652
|
+
const { socket } = selectSignalConnectionRaw(state);
|
|
1653
|
+
const streamId = streamIdForClient(clientView);
|
|
1654
|
+
const payload = { clientId: clientView.id, streamId: streamId !== null && streamId !== void 0 ? streamId : "0" };
|
|
1655
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("add_spotlight", payload);
|
|
1656
|
+
});
|
|
1657
|
+
const doRemoveSpotlight = createAppAuthorizedThunk((state) => selectIsAuthorizedToSpotlight(state), ({ id }) => (_, getState) => {
|
|
1658
|
+
const state = getState();
|
|
1659
|
+
const clientView = selectAllClientViews(state).find((c) => c.clientId === id);
|
|
1660
|
+
if (!clientView) {
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1663
|
+
const { socket } = selectSignalConnectionRaw(state);
|
|
1664
|
+
const streamId = streamIdForClient(clientView);
|
|
1665
|
+
const payload = { clientId: clientView.id, streamId: streamId !== null && streamId !== void 0 ? streamId : "0" };
|
|
1666
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("remove_spotlight", payload);
|
|
1667
|
+
});
|
|
1668
|
+
const selectSpotlightsRaw = (state) => state.spotlights;
|
|
1669
|
+
const selectSpotlights = (state) => state.spotlights.sorted;
|
|
1670
|
+
const selectIsLocalParticipantSpotlighted = createSelector(selectLocalParticipantRaw, selectSpotlights, (localParticipant, spotlights) => {
|
|
1671
|
+
return isClientSpotlighted({ clientId: localParticipant.id, stream: localParticipant.stream, spotlights });
|
|
1672
|
+
});
|
|
1673
|
+
const selectSpotlightedClientViews = createSelector(selectAllClientViews, selectSpotlights, (clientViews, spotlights) => {
|
|
1674
|
+
return mapSpotlightsToClientViews(spotlights, clientViews);
|
|
1675
|
+
});
|
|
1676
|
+
startAppListening({
|
|
1677
|
+
actionCreator: doStartScreenshare.fulfilled,
|
|
1678
|
+
effect: ({ payload }, { getState, dispatch }) => {
|
|
1679
|
+
const { stream } = payload;
|
|
1680
|
+
const state = getState();
|
|
1681
|
+
const localParticipant = selectLocalParticipantRaw(state);
|
|
1682
|
+
if (!localParticipant) {
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1685
|
+
dispatch(addSpotlight({ clientId: localParticipant.id, streamId: stream.id }));
|
|
1686
|
+
},
|
|
1687
|
+
});
|
|
1688
|
+
startAppListening({
|
|
1689
|
+
actionCreator: stopScreenshare,
|
|
1690
|
+
effect: ({ payload }, { getState, dispatch }) => {
|
|
1691
|
+
const { stream } = payload;
|
|
1692
|
+
const state = getState();
|
|
1693
|
+
const localParticipant = selectLocalParticipantRaw(state);
|
|
1694
|
+
if (!localParticipant) {
|
|
1695
|
+
return;
|
|
1696
|
+
}
|
|
1697
|
+
dispatch(removeSpotlight({ clientId: localParticipant.id, streamId: stream.id }));
|
|
1698
|
+
},
|
|
1699
|
+
});
|
|
1700
|
+
|
|
1701
|
+
function isDeferrable({ client, breakoutCurrentId }) {
|
|
1702
|
+
if (!client)
|
|
1703
|
+
return false;
|
|
1704
|
+
if (!breakoutCurrentId && client.breakoutGroup)
|
|
1705
|
+
return true;
|
|
1706
|
+
if (!client.isAudioEnabled && !client.isVideoEnabled)
|
|
1707
|
+
return true;
|
|
1708
|
+
return false;
|
|
1709
|
+
}
|
|
1710
|
+
const createWebRtcEmitter = (dispatch) => {
|
|
1711
|
+
return {
|
|
1712
|
+
emit: (eventName, data) => {
|
|
1713
|
+
if (eventName === "rtc_manager_created") {
|
|
1714
|
+
dispatch(doRtcManagerCreated(data));
|
|
1715
|
+
}
|
|
1716
|
+
else if (eventName === "stream_added") {
|
|
1717
|
+
dispatch(rtcEvents.streamAdded(data));
|
|
1718
|
+
}
|
|
1719
|
+
else if (eventName === "rtc_manager_destroyed") {
|
|
1720
|
+
dispatch(rtcManagerDestroyed());
|
|
1721
|
+
}
|
|
1722
|
+
else if (eventName === "client_connection_status_changed") {
|
|
1723
|
+
dispatch(rtcEvents.clientConnectionStatusChanged(data));
|
|
1724
|
+
}
|
|
1725
|
+
else ;
|
|
1726
|
+
},
|
|
1727
|
+
};
|
|
1728
|
+
};
|
|
1729
|
+
const rtcConnectionSliceInitialState = {
|
|
1730
|
+
dispatcherCreated: false,
|
|
1731
|
+
error: null,
|
|
1732
|
+
isCreatingDispatcher: false,
|
|
1733
|
+
reportedStreamResolutions: {},
|
|
1734
|
+
rtcManager: null,
|
|
1735
|
+
rtcManagerDispatcher: null,
|
|
1736
|
+
rtcManagerInitialized: false,
|
|
1737
|
+
status: "inactive",
|
|
1738
|
+
isAcceptingStreams: false,
|
|
1739
|
+
};
|
|
1740
|
+
const rtcConnectionSlice = createSlice({
|
|
1741
|
+
name: "rtcConnection",
|
|
1742
|
+
initialState: rtcConnectionSliceInitialState,
|
|
1743
|
+
reducers: {
|
|
1744
|
+
isAcceptingStreams: (state, action) => {
|
|
1745
|
+
return Object.assign(Object.assign({}, state), { isAcceptingStreams: action.payload });
|
|
1746
|
+
},
|
|
1747
|
+
resolutionReported: (state, action) => {
|
|
1748
|
+
const { streamId, width, height } = action.payload;
|
|
1749
|
+
return Object.assign(Object.assign({}, state), { reportedStreamResolutions: Object.assign(Object.assign({}, state.reportedStreamResolutions), { [streamId]: { width, height } }) });
|
|
1750
|
+
},
|
|
1751
|
+
rtcDisconnected: () => {
|
|
1752
|
+
return Object.assign({}, rtcConnectionSliceInitialState);
|
|
1753
|
+
},
|
|
1754
|
+
rtcDispatcherCreated: (state, action) => {
|
|
1755
|
+
return Object.assign(Object.assign({}, state), { dispatcherCreated: true, rtcManagerDispatcher: action.payload });
|
|
1756
|
+
},
|
|
1757
|
+
rtcManagerCreated: (state, action) => {
|
|
1758
|
+
return Object.assign(Object.assign({}, state), { rtcManager: action.payload, status: "ready" });
|
|
1759
|
+
},
|
|
1760
|
+
rtcManagerDestroyed: (state) => {
|
|
1761
|
+
return Object.assign(Object.assign({}, state), { rtcManager: null });
|
|
1762
|
+
},
|
|
1763
|
+
rtcManagerInitialized: (state) => {
|
|
1764
|
+
return Object.assign(Object.assign({}, state), { rtcManagerInitialized: true });
|
|
1765
|
+
},
|
|
1766
|
+
rtcClientConnectionStatusChanged: {
|
|
1767
|
+
reducer: (state) => {
|
|
1768
|
+
return state;
|
|
1769
|
+
},
|
|
1770
|
+
prepare: (payload) => {
|
|
1771
|
+
return {
|
|
1772
|
+
payload: {},
|
|
1773
|
+
meta: {
|
|
1774
|
+
localParticipantId: payload.localParticipantId,
|
|
1775
|
+
},
|
|
1776
|
+
};
|
|
1777
|
+
},
|
|
1778
|
+
},
|
|
1779
|
+
},
|
|
1780
|
+
extraReducers: (builder) => {
|
|
1781
|
+
builder.addCase(socketReconnecting, (state) => {
|
|
1782
|
+
return Object.assign(Object.assign({}, state), { status: "reconnecting" });
|
|
1783
|
+
});
|
|
1784
|
+
builder.addCase(signalEvents.roomJoined, (state, action) => {
|
|
1785
|
+
if ("error" in action.payload) {
|
|
1786
|
+
return state;
|
|
1787
|
+
}
|
|
1788
|
+
return Object.assign(Object.assign({}, state), { status: state.status === "reconnecting" ? "ready" : state.status });
|
|
1789
|
+
});
|
|
1790
|
+
},
|
|
1791
|
+
});
|
|
1792
|
+
const { resolutionReported, rtcDispatcherCreated, rtcDisconnected, rtcManagerCreated, rtcManagerDestroyed, rtcManagerInitialized, isAcceptingStreams, rtcClientConnectionStatusChanged, } = rtcConnectionSlice.actions;
|
|
1793
|
+
const doConnectRtc = createAppThunk(() => (dispatch, getState) => {
|
|
1794
|
+
const state = getState();
|
|
1795
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
1796
|
+
const dispatcher = selectRtcConnectionRaw(state).rtcManagerDispatcher;
|
|
1797
|
+
const isCameraEnabled = selectIsCameraEnabled(state);
|
|
1798
|
+
const isMicrophoneEnabled = selectIsMicrophoneEnabled(state);
|
|
1799
|
+
const isNodeSdk = selectAppIsNodeSdk(state);
|
|
1800
|
+
if (dispatcher || !socket) {
|
|
1801
|
+
return;
|
|
1802
|
+
}
|
|
1803
|
+
const webrtcProvider = {
|
|
1804
|
+
getMediaConstraints: () => ({
|
|
1805
|
+
audio: isMicrophoneEnabled,
|
|
1806
|
+
video: isCameraEnabled,
|
|
1807
|
+
}),
|
|
1808
|
+
deferrable(clientId) {
|
|
1809
|
+
const client = selectRemoteParticipants(getState()).find((p) => p.id === clientId);
|
|
1810
|
+
const breakoutCurrentId = selectBreakoutCurrentId(getState()) || "";
|
|
1811
|
+
return isDeferrable({ client, breakoutCurrentId });
|
|
1812
|
+
},
|
|
1813
|
+
};
|
|
1814
|
+
const rtcManagerDispatcher = new RtcManagerDispatcher({
|
|
1815
|
+
emitter: createWebRtcEmitter(dispatch),
|
|
1816
|
+
serverSocket: socket,
|
|
1817
|
+
webrtcProvider,
|
|
1818
|
+
features: {
|
|
1819
|
+
isNodeSdk,
|
|
1820
|
+
lowDataModeEnabled: false,
|
|
1821
|
+
sfuServerOverrideHost: undefined,
|
|
1822
|
+
turnServerOverrideHost: undefined,
|
|
1823
|
+
useOnlyTURN: undefined,
|
|
1824
|
+
sfuVp9On: false,
|
|
1825
|
+
h264On: false,
|
|
1826
|
+
simulcastScreenshareOn: false,
|
|
1827
|
+
},
|
|
1828
|
+
});
|
|
1829
|
+
dispatch(rtcDispatcherCreated(rtcManagerDispatcher));
|
|
1830
|
+
});
|
|
1831
|
+
const doDisconnectRtc = createAppThunk(() => (dispatch, getState) => {
|
|
1832
|
+
const { rtcManager } = selectRtcConnectionRaw(getState());
|
|
1833
|
+
if (rtcManager) {
|
|
1834
|
+
rtcManager.disconnectAll();
|
|
1835
|
+
}
|
|
1836
|
+
dispatch(rtcDisconnected());
|
|
1837
|
+
});
|
|
1838
|
+
const doHandleAcceptStreams = createAppThunk((payload) => (dispatch, getState) => {
|
|
1839
|
+
var _a;
|
|
1840
|
+
dispatch(isAcceptingStreams(true));
|
|
1841
|
+
const state = getState();
|
|
1842
|
+
const rtcManager = selectRtcConnectionRaw(state).rtcManager;
|
|
1843
|
+
const remoteClients = selectRemoteClients(state);
|
|
1844
|
+
if (!rtcManager) {
|
|
1845
|
+
throw new Error("No rtc manager");
|
|
1846
|
+
}
|
|
1847
|
+
const activeBreakout = selectBreakoutActive(state);
|
|
1848
|
+
const shouldAcceptNewClients = (_a = rtcManager.shouldAcceptStreamsFromBothSides) === null || _a === void 0 ? void 0 : _a.call(rtcManager);
|
|
1849
|
+
const updates = [];
|
|
1850
|
+
for (const { clientId, streamId, state } of payload) {
|
|
1851
|
+
const participant = remoteClients.find((p) => p.id === clientId);
|
|
1852
|
+
if (!participant)
|
|
1853
|
+
continue;
|
|
1854
|
+
if (state === "to_accept" ||
|
|
1855
|
+
(state === "new_accept" && shouldAcceptNewClients) ||
|
|
1856
|
+
(state === "old_accept" && !shouldAcceptNewClients)) {
|
|
1857
|
+
rtcManager.acceptNewStream({
|
|
1858
|
+
streamId: streamId === "0" ? clientId : streamId,
|
|
1859
|
+
clientId,
|
|
1860
|
+
shouldAddLocalVideo: streamId === "0",
|
|
1861
|
+
activeBreakout,
|
|
1862
|
+
});
|
|
1863
|
+
}
|
|
1864
|
+
else if (state === "new_accept" || state === "old_accept") ;
|
|
1865
|
+
else if (state === "to_unaccept") {
|
|
1866
|
+
rtcManager === null || rtcManager === void 0 ? void 0 : rtcManager.disconnect(streamId === "0" ? clientId : streamId, activeBreakout);
|
|
1867
|
+
}
|
|
1868
|
+
else if (state !== "done_accept") {
|
|
1869
|
+
continue;
|
|
1870
|
+
}
|
|
1871
|
+
else ;
|
|
1872
|
+
updates.push({ clientId, streamId, state: state.replace(/to_|new_|old_/, "done_") });
|
|
1873
|
+
}
|
|
1874
|
+
dispatch(streamStatusUpdated(updates));
|
|
1875
|
+
dispatch(isAcceptingStreams(false));
|
|
1876
|
+
});
|
|
1877
|
+
const doRtcReportStreamResolution = createAppThunk(({ streamId, width, height }) => (dispatch, getState) => {
|
|
1878
|
+
const { reportedStreamResolutions, rtcManager } = selectRtcConnectionRaw(getState());
|
|
1879
|
+
const localStream = selectLocalMediaStream(getState());
|
|
1880
|
+
if (!rtcManager || (localStream === null || localStream === void 0 ? void 0 : localStream.id) === streamId) {
|
|
1881
|
+
return;
|
|
1882
|
+
}
|
|
1883
|
+
const old = reportedStreamResolutions[streamId];
|
|
1884
|
+
if (!old || old.width !== width || old.height !== height) {
|
|
1885
|
+
rtcManager.updateStreamResolution(streamId, null, { width: width || 1, height: height || 1 });
|
|
1886
|
+
}
|
|
1887
|
+
dispatch(resolutionReported({ streamId, width, height }));
|
|
1888
|
+
});
|
|
1889
|
+
const doRtcManagerCreated = createAppThunk((payload) => (dispatch) => {
|
|
1890
|
+
const { rtcManager } = payload;
|
|
1891
|
+
dispatch(rtcManagerCreated(rtcManager));
|
|
1892
|
+
});
|
|
1893
|
+
const doRtcManagerInitialize = createAppThunk(() => (dispatch, getState) => {
|
|
1894
|
+
const localMediaStream = selectLocalMediaStream(getState());
|
|
1895
|
+
const rtcManager = selectRtcConnectionRaw(getState()).rtcManager;
|
|
1896
|
+
const isCameraEnabled = selectIsCameraEnabled(getState());
|
|
1897
|
+
const isMicrophoneEnabled = selectIsMicrophoneEnabled(getState());
|
|
1898
|
+
if (localMediaStream && rtcManager) {
|
|
1899
|
+
rtcManager.addNewStream("0", localMediaStream, !isMicrophoneEnabled, !isCameraEnabled);
|
|
1900
|
+
}
|
|
1901
|
+
dispatch(rtcManagerInitialized());
|
|
1902
|
+
});
|
|
1903
|
+
const selectRtcConnectionRaw = (state) => state.rtcConnection;
|
|
1904
|
+
const selectRtcManagerInitialized = (state) => state.rtcConnection.rtcManagerInitialized;
|
|
1905
|
+
const selectRtcManager = (state) => state.rtcConnection.rtcManager;
|
|
1906
|
+
const selectRtcDispatcherCreated = (state) => state.rtcConnection.dispatcherCreated;
|
|
1907
|
+
const selectRtcIsCreatingDispatcher = (state) => state.rtcConnection.isCreatingDispatcher;
|
|
1908
|
+
const selectRtcStatus = (state) => state.rtcConnection.status;
|
|
1909
|
+
const selectIsAcceptingStreams = (state) => state.rtcConnection.isAcceptingStreams;
|
|
1910
|
+
startAppListening({
|
|
1911
|
+
actionCreator: doSetDevice.fulfilled,
|
|
1912
|
+
effect: ({ payload }, { getState }) => {
|
|
1913
|
+
const { replacedTracks } = payload;
|
|
1914
|
+
const { rtcManager } = selectRtcConnectionRaw(getState());
|
|
1915
|
+
const stream = selectLocalMediaStream(getState());
|
|
1916
|
+
const replace = (kind, oldTrack) => {
|
|
1917
|
+
const track = stream === null || stream === void 0 ? void 0 : stream.getTracks().find((t) => t.kind === kind);
|
|
1918
|
+
return track && (rtcManager === null || rtcManager === void 0 ? void 0 : rtcManager.replaceTrack(oldTrack, track));
|
|
1919
|
+
};
|
|
1920
|
+
replacedTracks === null || replacedTracks === void 0 ? void 0 : replacedTracks.forEach((t) => {
|
|
1921
|
+
replace(t.kind, t);
|
|
1922
|
+
});
|
|
1923
|
+
},
|
|
1924
|
+
});
|
|
1925
|
+
startAppListening({
|
|
1926
|
+
actionCreator: doStartScreenshare.fulfilled,
|
|
1927
|
+
effect: ({ payload }, { getState }) => {
|
|
1928
|
+
const { stream } = payload;
|
|
1929
|
+
const { rtcManager } = selectRtcConnectionRaw(getState());
|
|
1930
|
+
rtcManager === null || rtcManager === void 0 ? void 0 : rtcManager.addNewStream(stream.id, stream, false, true);
|
|
1931
|
+
},
|
|
1932
|
+
});
|
|
1933
|
+
startAppListening({
|
|
1934
|
+
actionCreator: doAppStop,
|
|
1935
|
+
effect: (_, { getState }) => {
|
|
1936
|
+
const rtcManager = selectRtcManager(getState());
|
|
1937
|
+
rtcManager === null || rtcManager === void 0 ? void 0 : rtcManager.rtcStatsDisconnect();
|
|
1938
|
+
},
|
|
1939
|
+
});
|
|
1940
|
+
startAppListening({
|
|
1941
|
+
actionCreator: stopScreenshare,
|
|
1942
|
+
effect: ({ payload }, { getState }) => {
|
|
1943
|
+
const { stream } = payload;
|
|
1944
|
+
const { rtcManager } = selectRtcConnectionRaw(getState());
|
|
1945
|
+
rtcManager === null || rtcManager === void 0 ? void 0 : rtcManager.removeStream(stream.id, stream, null);
|
|
1946
|
+
},
|
|
1947
|
+
});
|
|
1948
|
+
startAppListening({
|
|
1949
|
+
actionCreator: doSwitchLocalStream.fulfilled,
|
|
1950
|
+
effect: ({ payload }, { getState }) => {
|
|
1951
|
+
var _a;
|
|
1952
|
+
const stream = selectLocalMediaStream(getState());
|
|
1953
|
+
const { rtcManager } = selectRtcConnectionRaw(getState());
|
|
1954
|
+
if (stream && rtcManager) {
|
|
1955
|
+
const replace = (kind, oldTrack) => {
|
|
1956
|
+
const track = stream.getTracks().find((t) => t.kind === kind);
|
|
1957
|
+
return track && rtcManager.replaceTrack(oldTrack, track);
|
|
1958
|
+
};
|
|
1959
|
+
(_a = payload === null || payload === void 0 ? void 0 : payload.replacedTracks) === null || _a === void 0 ? void 0 : _a.forEach((t) => {
|
|
1960
|
+
replace(t.kind, t);
|
|
1961
|
+
});
|
|
1962
|
+
}
|
|
1963
|
+
},
|
|
1964
|
+
});
|
|
1965
|
+
startAppListening({
|
|
1966
|
+
actionCreator: rtcEvents.clientConnectionStatusChanged,
|
|
1967
|
+
effect: (_, { dispatch, getState }) => {
|
|
1968
|
+
const localParticipant = selectLocalParticipantRaw(getState());
|
|
1969
|
+
dispatch(rtcClientConnectionStatusChanged({ localParticipantId: localParticipant.id }));
|
|
1970
|
+
},
|
|
1971
|
+
});
|
|
1972
|
+
const selectShouldConnectRtc = createSelector(selectRtcStatus, selectAppIsActive, selectRtcDispatcherCreated, selectRtcIsCreatingDispatcher, selectSignalConnectionSocket, (rtcStatus, appIsActive, dispatcherCreated, isCreatingDispatcher, signalSocket) => {
|
|
1973
|
+
if (appIsActive && rtcStatus === "inactive" && !dispatcherCreated && !isCreatingDispatcher && signalSocket) {
|
|
1974
|
+
return true;
|
|
1975
|
+
}
|
|
1976
|
+
return false;
|
|
1977
|
+
});
|
|
1978
|
+
createReactor([selectShouldConnectRtc], ({ dispatch }, shouldConnectRtc) => {
|
|
1979
|
+
if (shouldConnectRtc) {
|
|
1980
|
+
dispatch(doConnectRtc());
|
|
1981
|
+
}
|
|
1982
|
+
});
|
|
1983
|
+
const selectShouldInitializeRtc = createSelector(selectRtcManager, selectRtcManagerInitialized, selectLocalMediaStatus, (rtcManager, rtcManagerInitialized, localMediaStatus) => {
|
|
1984
|
+
if (localMediaStatus === "started" && rtcManager && !rtcManagerInitialized) {
|
|
1985
|
+
return true;
|
|
1986
|
+
}
|
|
1987
|
+
return false;
|
|
1988
|
+
});
|
|
1989
|
+
createReactor([selectShouldInitializeRtc], ({ dispatch }, shouldInitializeRtc) => {
|
|
1990
|
+
if (shouldInitializeRtc) {
|
|
1991
|
+
dispatch(doRtcManagerInitialize());
|
|
1992
|
+
}
|
|
1993
|
+
});
|
|
1994
|
+
const selectShouldDisconnectRtc = createSelector(selectRtcStatus, selectAppIsActive, (status, appIsActive) => {
|
|
1995
|
+
if (!appIsActive && !["inactive", "disconnected"].includes(status)) {
|
|
1996
|
+
return true;
|
|
1997
|
+
}
|
|
1998
|
+
return false;
|
|
1999
|
+
});
|
|
2000
|
+
createReactor([selectShouldDisconnectRtc], ({ dispatch }, shouldDisconnectRtc) => {
|
|
2001
|
+
if (shouldDisconnectRtc) {
|
|
2002
|
+
dispatch(doDisconnectRtc());
|
|
2003
|
+
}
|
|
2004
|
+
});
|
|
2005
|
+
const selectStreamsToAccept = createSelector(selectRtcStatus, selectRemoteClients, selectBreakoutCurrentId, selectSpotlights, selectAppIgnoreBreakoutGroups, (rtcStatus, remoteParticipants, breakoutCurrentId, spotlights, ignoreBreakoutGroups) => {
|
|
2006
|
+
if (rtcStatus !== "ready") {
|
|
2007
|
+
return [];
|
|
2008
|
+
}
|
|
2009
|
+
const upd = [];
|
|
2010
|
+
for (const client of remoteParticipants) {
|
|
2011
|
+
const { streams, id: clientId, newJoiner } = client;
|
|
2012
|
+
const clientSpotlight = spotlights.find((s) => s.clientId === client.id && s.streamId === "0");
|
|
2013
|
+
for (let i = 0; i < streams.length; i++) {
|
|
2014
|
+
let streamId = streams[i].id;
|
|
2015
|
+
let state = streams[i].state;
|
|
2016
|
+
if ((streams === null || streams === void 0 ? void 0 : streams.length) > 1 && streams[1].id === "0") {
|
|
2017
|
+
if (i === 0) {
|
|
2018
|
+
streamId = streams[1].id;
|
|
2019
|
+
state = streams[1].state;
|
|
2020
|
+
}
|
|
2021
|
+
else if (i === 1) {
|
|
2022
|
+
streamId = streams[0].id;
|
|
2023
|
+
state = streams[0].state;
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
if ((!client.breakoutGroup && !breakoutCurrentId) ||
|
|
2027
|
+
client.breakoutGroup === breakoutCurrentId ||
|
|
2028
|
+
("" === client.breakoutGroup && clientSpotlight) ||
|
|
2029
|
+
ignoreBreakoutGroups) {
|
|
2030
|
+
if (state === "done_accept")
|
|
2031
|
+
continue;
|
|
2032
|
+
upd.push({
|
|
2033
|
+
clientId,
|
|
2034
|
+
streamId,
|
|
2035
|
+
state: `${newJoiner && streamId === "0" ? "new" : "to"}_accept`,
|
|
2036
|
+
});
|
|
2037
|
+
}
|
|
2038
|
+
else {
|
|
2039
|
+
if (state === "done_unaccept")
|
|
2040
|
+
continue;
|
|
2041
|
+
upd.push({ clientId, streamId, state: "to_unaccept" });
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
return upd;
|
|
2046
|
+
});
|
|
2047
|
+
createReactor([selectStreamsToAccept, selectIsAcceptingStreams], ({ dispatch }, streamsToAccept, isAcceptingStreams) => {
|
|
2048
|
+
if (0 < streamsToAccept.length && !isAcceptingStreams) {
|
|
2049
|
+
dispatch(doHandleAcceptStreams(streamsToAccept));
|
|
2050
|
+
}
|
|
2051
|
+
});
|
|
2052
|
+
|
|
2053
|
+
const connectionMonitorSliceInitialState = {
|
|
2054
|
+
running: false,
|
|
2055
|
+
};
|
|
2056
|
+
const connectionMonitorSlice = createSlice({
|
|
2057
|
+
name: "connectionMonitor",
|
|
2058
|
+
initialState: connectionMonitorSliceInitialState,
|
|
2059
|
+
reducers: {
|
|
2060
|
+
connectionMonitorStarted: (state, action) => {
|
|
2061
|
+
return Object.assign(Object.assign({}, state), { running: true, stopCallbackFunction: action.payload.stopIssueSubscription });
|
|
2062
|
+
},
|
|
2063
|
+
connectionMonitorStopped: () => {
|
|
2064
|
+
return Object.assign({}, connectionMonitorSliceInitialState);
|
|
2065
|
+
},
|
|
2066
|
+
},
|
|
2067
|
+
});
|
|
2068
|
+
const { connectionMonitorStarted, connectionMonitorStopped } = connectionMonitorSlice.actions;
|
|
2069
|
+
const doStartConnectionMonitor = createAppThunk(() => (dispatch, getState) => {
|
|
2070
|
+
setClientProvider(() => {
|
|
2071
|
+
const state = getState();
|
|
2072
|
+
const clientViews = selectAllClientViews(state).map((clientView) => {
|
|
2073
|
+
var _a, _b;
|
|
2074
|
+
return ({
|
|
2075
|
+
id: clientView.id,
|
|
2076
|
+
clientId: clientView.clientId,
|
|
2077
|
+
isLocalClient: clientView.isLocalClient,
|
|
2078
|
+
audio: {
|
|
2079
|
+
enabled: clientView.isAudioEnabled,
|
|
2080
|
+
track: (_a = clientView.stream) === null || _a === void 0 ? void 0 : _a.getAudioTracks()[0],
|
|
2081
|
+
},
|
|
2082
|
+
video: {
|
|
2083
|
+
enabled: clientView.isVideoEnabled,
|
|
2084
|
+
track: (_b = clientView.stream) === null || _b === void 0 ? void 0 : _b.getVideoTracks()[0],
|
|
2085
|
+
},
|
|
2086
|
+
isPresentation: clientView.isPresentation,
|
|
2087
|
+
});
|
|
2088
|
+
});
|
|
2089
|
+
return clientViews;
|
|
2090
|
+
});
|
|
2091
|
+
const issueMonitorSubscription = subscribeIssues({
|
|
2092
|
+
onUpdatedIssues: (issuesAndMetricsByClients) => {
|
|
2093
|
+
var _a;
|
|
2094
|
+
const state = getState();
|
|
2095
|
+
const rtcManager = selectRtcManager(state);
|
|
2096
|
+
if (!rtcManager) {
|
|
2097
|
+
return;
|
|
2098
|
+
}
|
|
2099
|
+
let lossSend = 0;
|
|
2100
|
+
let lossReceive = 0;
|
|
2101
|
+
let bitrateSend = 0;
|
|
2102
|
+
let bitrateReceive = 0;
|
|
2103
|
+
Object.entries(issuesAndMetricsByClients.aggregated.metrics).forEach(([key, value]) => {
|
|
2104
|
+
if (/loc.*packetloss/.test(key))
|
|
2105
|
+
lossSend = Math.max(lossSend, value.curMax);
|
|
2106
|
+
if (/rem.*packetloss/.test(key))
|
|
2107
|
+
lossReceive = Math.max(lossReceive, value.curMax);
|
|
2108
|
+
if (/loc.*bitrate/.test(key))
|
|
2109
|
+
bitrateSend += value.curSum;
|
|
2110
|
+
if (/rem.*bitrate/.test(key))
|
|
2111
|
+
bitrateReceive += value.curSum;
|
|
2112
|
+
});
|
|
2113
|
+
rtcManager.sendStatsCustomEvent("insightsStats", {
|
|
2114
|
+
ls: Math.round(lossSend * 1000) / 1000,
|
|
2115
|
+
lr: Math.round(lossReceive * 1000) / 1000,
|
|
2116
|
+
bs: Math.round(bitrateSend),
|
|
2117
|
+
br: Math.round(bitrateReceive),
|
|
2118
|
+
cpu: (_a = issuesAndMetricsByClients.aggregated.metrics["global-cpu-pressure"]) === null || _a === void 0 ? void 0 : _a.curSum,
|
|
2119
|
+
_time: Date.now(),
|
|
2120
|
+
});
|
|
2121
|
+
},
|
|
2122
|
+
});
|
|
2123
|
+
dispatch(connectionMonitorStarted({ stopIssueSubscription: issueMonitorSubscription.stop }));
|
|
2124
|
+
});
|
|
2125
|
+
const doStopConnectionMonitor = createAppThunk(() => (dispatch, getState) => {
|
|
2126
|
+
const state = getState();
|
|
2127
|
+
const stopCallbackFn = selectStopCallbackFunction(state);
|
|
2128
|
+
if (stopCallbackFn) {
|
|
2129
|
+
stopCallbackFn();
|
|
2130
|
+
}
|
|
2131
|
+
dispatch(connectionMonitorStopped());
|
|
2132
|
+
});
|
|
2133
|
+
const selectConnectionMonitorIsRunning = (state) => state.connectionMonitor.running;
|
|
2134
|
+
const selectStopCallbackFunction = (state) => state.connectionMonitor.stopCallbackFunction;
|
|
2135
|
+
const selectShouldStartConnectionMonitor = createSelector(selectRoomConnectionStatus, selectConnectionMonitorIsRunning, (roomConnectionStatus, isRunning) => {
|
|
2136
|
+
if (!isRunning && roomConnectionStatus === "connected") {
|
|
2137
|
+
return true;
|
|
2138
|
+
}
|
|
2139
|
+
return false;
|
|
2140
|
+
});
|
|
2141
|
+
const selectShouldStopConnectionMonitor = createSelector(selectRoomConnectionStatus, selectConnectionMonitorIsRunning, (roomConnectionStatus, isRunning) => {
|
|
2142
|
+
if (isRunning && ["kicked", "left"].includes(roomConnectionStatus)) {
|
|
2143
|
+
return true;
|
|
2144
|
+
}
|
|
2145
|
+
return false;
|
|
2146
|
+
});
|
|
2147
|
+
createReactor([selectShouldStartConnectionMonitor], ({ dispatch }, shouldStartConnectionMonitor) => {
|
|
2148
|
+
if (shouldStartConnectionMonitor) {
|
|
2149
|
+
dispatch(doStartConnectionMonitor());
|
|
2150
|
+
}
|
|
2151
|
+
});
|
|
2152
|
+
createReactor([selectShouldStopConnectionMonitor], ({ dispatch }, shouldStartConnectionMonitor) => {
|
|
2153
|
+
if (shouldStartConnectionMonitor) {
|
|
2154
|
+
dispatch(doStopConnectionMonitor());
|
|
2155
|
+
}
|
|
2156
|
+
});
|
|
2157
|
+
|
|
2158
|
+
const localParticipantSliceInitialState = {
|
|
2159
|
+
displayName: "",
|
|
2160
|
+
id: "",
|
|
2161
|
+
breakoutGroup: null,
|
|
2162
|
+
breakoutGroupAssigned: "",
|
|
2163
|
+
isAudioEnabled: true,
|
|
2164
|
+
isVideoEnabled: true,
|
|
2165
|
+
isLocalParticipant: true,
|
|
2166
|
+
stream: undefined,
|
|
2167
|
+
isScreenSharing: false,
|
|
2168
|
+
roleName: "none",
|
|
2169
|
+
clientClaim: undefined,
|
|
2170
|
+
stickyReaction: undefined,
|
|
2171
|
+
isDialIn: false,
|
|
2172
|
+
};
|
|
2173
|
+
const localParticipantSlice = createSlice({
|
|
2174
|
+
name: "localParticipant",
|
|
2175
|
+
initialState: localParticipantSliceInitialState,
|
|
2176
|
+
reducers: {
|
|
2177
|
+
setDisplayName: (state, action) => {
|
|
2178
|
+
return Object.assign(Object.assign({}, state), { displayName: action.payload.displayName });
|
|
2179
|
+
},
|
|
2180
|
+
setBreakoutGroupAssigned: (state, action) => {
|
|
2181
|
+
return Object.assign(Object.assign({}, state), { breakoutGroupAssigned: action.payload.breakoutGroupAssigned });
|
|
2182
|
+
},
|
|
2183
|
+
},
|
|
2184
|
+
extraReducers: (builder) => {
|
|
2185
|
+
builder.addCase(doAppStart, (state, action) => {
|
|
2186
|
+
return Object.assign(Object.assign({}, state), { displayName: action.payload.displayName });
|
|
2187
|
+
});
|
|
2188
|
+
builder.addCase(doEnableAudio.fulfilled, (state, action) => {
|
|
2189
|
+
return Object.assign(Object.assign({}, state), { isAudioEnabled: action.payload });
|
|
2190
|
+
});
|
|
2191
|
+
builder.addCase(doEnableVideo.fulfilled, (state, action) => {
|
|
2192
|
+
return Object.assign(Object.assign({}, state), { isVideoEnabled: action.payload });
|
|
2193
|
+
});
|
|
2194
|
+
builder.addCase(doSetLocalStickyReaction.fulfilled, (state, action) => {
|
|
2195
|
+
return Object.assign(Object.assign({}, state), { stickyReaction: action.payload });
|
|
2196
|
+
});
|
|
2197
|
+
builder.addCase(signalEvents.roomJoined, (state, action) => {
|
|
2198
|
+
var _a;
|
|
2199
|
+
if ("error" in action.payload) {
|
|
2200
|
+
if (action.payload.error === "room_locked") {
|
|
2201
|
+
return Object.assign(Object.assign({}, state), { id: action.payload.selfId });
|
|
2202
|
+
}
|
|
2203
|
+
return state;
|
|
2204
|
+
}
|
|
2205
|
+
const { room, selfId, clientClaim } = action.payload || {};
|
|
2206
|
+
const client = room.clients.find((c) => c.id === selfId);
|
|
2207
|
+
return Object.assign(Object.assign({}, state), { id: selfId, roleName: ((_a = client === null || client === void 0 ? void 0 : client.role) === null || _a === void 0 ? void 0 : _a.roleName) || "none", clientClaim, breakoutGroup: (client === null || client === void 0 ? void 0 : client.breakoutGroup) || null });
|
|
2208
|
+
});
|
|
2209
|
+
builder.addCase(signalEvents.breakoutGroupJoined, (state, action) => {
|
|
2210
|
+
var _a, _b;
|
|
2211
|
+
if (((_a = action.payload) === null || _a === void 0 ? void 0 : _a.clientId) !== state.id) {
|
|
2212
|
+
return state;
|
|
2213
|
+
}
|
|
2214
|
+
return Object.assign(Object.assign({}, state), { breakoutGroup: (_b = action.payload) === null || _b === void 0 ? void 0 : _b.group });
|
|
2215
|
+
});
|
|
2216
|
+
},
|
|
2217
|
+
});
|
|
2218
|
+
const { setDisplayName, setBreakoutGroupAssigned } = localParticipantSlice.actions;
|
|
2219
|
+
const doSetDisplayName = createRoomConnectedThunk((payload) => (dispatch, getState) => {
|
|
2220
|
+
const state = getState();
|
|
2221
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
2222
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("send_client_metadata", {
|
|
2223
|
+
type: "UserData",
|
|
2224
|
+
payload: { displayName: payload.displayName },
|
|
2225
|
+
});
|
|
2226
|
+
dispatch(setDisplayName({ displayName: payload.displayName }));
|
|
2227
|
+
});
|
|
2228
|
+
const doEnableAudio = createAsyncRoomConnectedThunk("localParticipant/doEnableAudio", (payload_1, _a) => __awaiter(void 0, [payload_1, _a], void 0, function* (payload, { dispatch, getState }) {
|
|
2229
|
+
const state = getState();
|
|
2230
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
2231
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("enable_audio", { enabled: payload.enabled });
|
|
2232
|
+
if (payload.enabled) {
|
|
2233
|
+
dispatch(doSetLocalStickyReaction({ enabled: false }));
|
|
2234
|
+
}
|
|
2235
|
+
return payload.enabled;
|
|
2236
|
+
}));
|
|
2237
|
+
const doEnableVideo = createAsyncRoomConnectedThunk("localParticipant/doEnableVideo", (payload_1, _a) => __awaiter(void 0, [payload_1, _a], void 0, function* (payload, { getState }) {
|
|
2238
|
+
const state = getState();
|
|
2239
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
2240
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("enable_video", { enabled: payload.enabled });
|
|
2241
|
+
return payload.enabled;
|
|
2242
|
+
}));
|
|
2243
|
+
const doSetLocalStickyReaction = createAsyncRoomConnectedThunk("localParticipant/doSetLocalStickyReaction", (payload_1, _a) => __awaiter(void 0, [payload_1, _a], void 0, function* (payload, { getState, rejectWithValue }) {
|
|
2244
|
+
var _b;
|
|
2245
|
+
const state = getState();
|
|
2246
|
+
const currentStickyReaction = selectLocalParticipantStickyReaction(state);
|
|
2247
|
+
const stickyReactionCurrentlyEnabled = Boolean(currentStickyReaction);
|
|
2248
|
+
const enabled = (_b = payload.enabled) !== null && _b !== void 0 ? _b : !stickyReactionCurrentlyEnabled;
|
|
2249
|
+
if (enabled === stickyReactionCurrentlyEnabled) {
|
|
2250
|
+
return rejectWithValue(currentStickyReaction);
|
|
2251
|
+
}
|
|
2252
|
+
const stickyReaction = enabled ? { reaction: "✋", timestamp: new Date().toISOString() } : null;
|
|
2253
|
+
return stickyReaction;
|
|
2254
|
+
}));
|
|
2255
|
+
const doSendClientMetadata = createRoomConnectedThunk(() => (_, getState) => {
|
|
2256
|
+
const state = getState();
|
|
2257
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
2258
|
+
const payload = {
|
|
2259
|
+
displayName: selectLocalParticipantDisplayName(state),
|
|
2260
|
+
stickyReaction: selectLocalParticipantStickyReaction(state),
|
|
2261
|
+
};
|
|
2262
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("send_client_metadata", {
|
|
2263
|
+
type: "UserData",
|
|
2264
|
+
payload,
|
|
2265
|
+
});
|
|
2266
|
+
});
|
|
2267
|
+
startAppListening({
|
|
2268
|
+
actionCreator: toggleCameraEnabled,
|
|
2269
|
+
effect: ({ payload }, { dispatch, getState }) => {
|
|
2270
|
+
const { enabled } = payload;
|
|
2271
|
+
const { isVideoEnabled } = selectLocalParticipantRaw(getState());
|
|
2272
|
+
const roomConnectionStatus = selectRoomConnectionStatus(getState());
|
|
2273
|
+
if (roomConnectionStatus !== "connected") {
|
|
2274
|
+
return;
|
|
2275
|
+
}
|
|
2276
|
+
dispatch(doEnableVideo({ enabled: enabled || !isVideoEnabled }));
|
|
2277
|
+
},
|
|
2278
|
+
});
|
|
2279
|
+
startAppListening({
|
|
2280
|
+
actionCreator: toggleMicrophoneEnabled,
|
|
2281
|
+
effect: ({ payload }, { dispatch, getState }) => {
|
|
2282
|
+
const { enabled } = payload;
|
|
2283
|
+
const { isAudioEnabled } = selectLocalParticipantRaw(getState());
|
|
2284
|
+
const roomConnectionStatus = selectRoomConnectionStatus(getState());
|
|
2285
|
+
if (roomConnectionStatus !== "connected") {
|
|
2286
|
+
return;
|
|
2287
|
+
}
|
|
2288
|
+
dispatch(doEnableAudio({ enabled: enabled || !isAudioEnabled }));
|
|
2289
|
+
},
|
|
2290
|
+
});
|
|
2291
|
+
createReactor([selectLocalParticipantDisplayName, selectLocalParticipantStickyReaction, selectRoomConnectionStatus], ({ dispatch }, diplayName, stickyReaction, roomConnectionStatus) => {
|
|
2292
|
+
if (roomConnectionStatus === "connected") {
|
|
2293
|
+
dispatch(doSendClientMetadata());
|
|
2294
|
+
}
|
|
2295
|
+
});
|
|
2296
|
+
createReactor([selectBreakoutAssignments, selectDeviceId, selectLocalParticipantRaw], ({ dispatch }, breakoutAssignments, deviceId, localParticipant) => {
|
|
2297
|
+
const breakoutGroupAssigned = (breakoutAssignments === null || breakoutAssignments === void 0 ? void 0 : breakoutAssignments[deviceId || ""]) || "";
|
|
2298
|
+
if (localParticipant.breakoutGroupAssigned === breakoutGroupAssigned) {
|
|
2299
|
+
return;
|
|
2300
|
+
}
|
|
2301
|
+
dispatch(setBreakoutGroupAssigned({ breakoutGroupAssigned }));
|
|
2302
|
+
});
|
|
2303
|
+
|
|
2304
|
+
const emitter = new EventEmitter();
|
|
2305
|
+
function createNotificationEvent(payload) {
|
|
2306
|
+
const notificationEvent = Object.assign(Object.assign({}, payload), { timestamp: Date.now() });
|
|
2307
|
+
return notificationEvent;
|
|
2308
|
+
}
|
|
2309
|
+
const initialNotificationsState = {
|
|
2310
|
+
emitter,
|
|
2311
|
+
events: [],
|
|
2312
|
+
};
|
|
2313
|
+
const notificationsSlice = createSlice({
|
|
2314
|
+
name: "notifications",
|
|
2315
|
+
initialState: initialNotificationsState,
|
|
2316
|
+
reducers: {
|
|
2317
|
+
addNotification: (state, action) => {
|
|
2318
|
+
return Object.assign(Object.assign({}, state), { events: [...state.events, Object.assign({}, action.payload)] });
|
|
2319
|
+
},
|
|
2320
|
+
doClearNotifications: (state) => {
|
|
2321
|
+
return Object.assign(Object.assign({}, state), { events: [] });
|
|
2322
|
+
},
|
|
2323
|
+
},
|
|
2324
|
+
});
|
|
2325
|
+
const { doClearNotifications } = notificationsSlice.actions;
|
|
2326
|
+
const doSetNotification = createAppThunk((payload) => (dispatch, getState) => {
|
|
2327
|
+
dispatch(notificationsSlice.actions.addNotification(payload));
|
|
2328
|
+
const state = getState();
|
|
2329
|
+
const emitter = selectNotificationsEmitter(state);
|
|
2330
|
+
emitter.emit(payload.type, payload);
|
|
2331
|
+
emitter.emit("*", payload);
|
|
2332
|
+
});
|
|
2333
|
+
const selectNotificationsRaw = (state) => state.notifications;
|
|
2334
|
+
const selectNotificationsEvents = (state) => state.notifications.events;
|
|
2335
|
+
const selectNotificationsEmitter = (state) => state.notifications.emitter;
|
|
2336
|
+
startAppListening({
|
|
2337
|
+
actionCreator: signalEvents.chatMessage,
|
|
2338
|
+
effect: ({ payload }, { dispatch, getState }) => {
|
|
2339
|
+
const state = getState();
|
|
2340
|
+
const client = selectRemoteParticipants(state).find(({ id }) => id === payload.senderId);
|
|
2341
|
+
if (!client) {
|
|
2342
|
+
console.warn("Could not find remote client that sent chat message");
|
|
2343
|
+
return;
|
|
2344
|
+
}
|
|
2345
|
+
dispatch(doSetNotification(createNotificationEvent({
|
|
2346
|
+
type: "chatMessageReceived",
|
|
2347
|
+
message: `${client.displayName} says: ${payload.text}`,
|
|
2348
|
+
props: {
|
|
2349
|
+
client,
|
|
2350
|
+
chatMessage: {
|
|
2351
|
+
senderId: payload.senderId,
|
|
2352
|
+
timestamp: payload.timestamp,
|
|
2353
|
+
text: payload.text,
|
|
2354
|
+
},
|
|
2355
|
+
},
|
|
2356
|
+
})));
|
|
2357
|
+
},
|
|
2358
|
+
});
|
|
2359
|
+
startAppListening({
|
|
2360
|
+
actionCreator: signalEvents.audioEnableRequested,
|
|
2361
|
+
effect: ({ payload }, { dispatch, getState }) => {
|
|
2362
|
+
const { enable, requestedByClientId } = payload;
|
|
2363
|
+
const state = getState();
|
|
2364
|
+
const client = selectRemoteParticipants(state).find(({ id }) => id === requestedByClientId);
|
|
2365
|
+
if (!client) {
|
|
2366
|
+
console.warn("Could not find remote client that requested a local audio change");
|
|
2367
|
+
return;
|
|
2368
|
+
}
|
|
2369
|
+
dispatch(doSetNotification(createNotificationEvent({
|
|
2370
|
+
type: enable ? "requestAudioEnable" : "requestAudioDisable",
|
|
2371
|
+
message: enable
|
|
2372
|
+
? `${client.displayName} has requested for you to speak`
|
|
2373
|
+
: `${client.displayName} has muted your microphone`,
|
|
2374
|
+
props: {
|
|
2375
|
+
client,
|
|
2376
|
+
enable,
|
|
2377
|
+
},
|
|
2378
|
+
})));
|
|
2379
|
+
},
|
|
2380
|
+
});
|
|
2381
|
+
startAppListening({
|
|
2382
|
+
actionCreator: signalEvents.videoEnableRequested,
|
|
2383
|
+
effect: ({ payload }, { dispatch, getState }) => {
|
|
2384
|
+
const { enable, requestedByClientId } = payload;
|
|
2385
|
+
const state = getState();
|
|
2386
|
+
const client = selectRemoteParticipants(state).find(({ id }) => id === requestedByClientId);
|
|
2387
|
+
if (!client) {
|
|
2388
|
+
console.warn("Could not find remote client that requested a local video change");
|
|
2389
|
+
return;
|
|
2390
|
+
}
|
|
2391
|
+
dispatch(doSetNotification(createNotificationEvent({
|
|
2392
|
+
type: enable ? "requestVideoEnable" : "requestVideoDisable",
|
|
2393
|
+
message: enable
|
|
2394
|
+
? `${client.displayName} has requested for you to start video`
|
|
2395
|
+
: `${client.displayName} has stopped your video`,
|
|
2396
|
+
props: {
|
|
2397
|
+
client,
|
|
2398
|
+
enable,
|
|
2399
|
+
},
|
|
2400
|
+
})));
|
|
2401
|
+
},
|
|
2402
|
+
});
|
|
2403
|
+
startAppListening({
|
|
2404
|
+
actionCreator: signalEvents.clientMetadataReceived,
|
|
2405
|
+
effect: (action, { dispatch, getOriginalState, getState }) => {
|
|
2406
|
+
var _a;
|
|
2407
|
+
const { error, payload } = action.payload;
|
|
2408
|
+
if (error || !payload) {
|
|
2409
|
+
return;
|
|
2410
|
+
}
|
|
2411
|
+
const { clientId, stickyReaction } = payload;
|
|
2412
|
+
const state = getState();
|
|
2413
|
+
const canAskToSpeak = selectIsAuthorizedToAskToSpeak(state);
|
|
2414
|
+
if (!canAskToSpeak) {
|
|
2415
|
+
return;
|
|
2416
|
+
}
|
|
2417
|
+
const client = selectRemoteParticipants(state).find(({ id }) => id === clientId);
|
|
2418
|
+
if (!client) {
|
|
2419
|
+
console.warn("Could not find remote client that provided updated metadata");
|
|
2420
|
+
return;
|
|
2421
|
+
}
|
|
2422
|
+
const previousState = getOriginalState();
|
|
2423
|
+
const previousClient = selectRemoteParticipants(previousState).find(({ id }) => id === clientId);
|
|
2424
|
+
if ((!stickyReaction && !(previousClient === null || previousClient === void 0 ? void 0 : previousClient.stickyReaction)) ||
|
|
2425
|
+
(stickyReaction === null || stickyReaction === void 0 ? void 0 : stickyReaction.timestamp) === ((_a = previousClient === null || previousClient === void 0 ? void 0 : previousClient.stickyReaction) === null || _a === void 0 ? void 0 : _a.timestamp)) {
|
|
2426
|
+
return;
|
|
2427
|
+
}
|
|
2428
|
+
dispatch(doSetNotification(createNotificationEvent({
|
|
2429
|
+
type: stickyReaction ? "remoteHandRaised" : "remoteHandLowered",
|
|
2430
|
+
message: `${client.displayName} ${stickyReaction ? "raised" : "lowered"} their hand`,
|
|
2431
|
+
props: {
|
|
2432
|
+
client,
|
|
2433
|
+
stickyReaction,
|
|
2434
|
+
},
|
|
2435
|
+
})));
|
|
2436
|
+
},
|
|
2437
|
+
});
|
|
2438
|
+
createReactor([selectSignalStatus], ({ dispatch, getState }, signalStatus) => {
|
|
2439
|
+
const state = getState();
|
|
2440
|
+
const roomConnectionStatus = selectRoomConnectionStatus(state);
|
|
2441
|
+
if (["left", "kicked"].includes(roomConnectionStatus)) {
|
|
2442
|
+
return;
|
|
2443
|
+
}
|
|
2444
|
+
if (signalStatus === "disconnected") {
|
|
2445
|
+
dispatch(doSetNotification(createNotificationEvent({
|
|
2446
|
+
type: "signalTrouble",
|
|
2447
|
+
message: `Network connection lost. Trying to reconnect you...`,
|
|
2448
|
+
props: {},
|
|
2449
|
+
})));
|
|
2450
|
+
}
|
|
2451
|
+
else if (signalStatus === "connected") {
|
|
2452
|
+
dispatch(doSetNotification(createNotificationEvent({
|
|
2453
|
+
type: "signalOk",
|
|
2454
|
+
message: `Network connection available`,
|
|
2455
|
+
props: {},
|
|
2456
|
+
})));
|
|
2457
|
+
}
|
|
2458
|
+
});
|
|
2459
|
+
startAppListening({
|
|
2460
|
+
actionCreator: signalEvents.clientUnableToJoin,
|
|
2461
|
+
effect: (action, { dispatch }) => {
|
|
2462
|
+
var _a;
|
|
2463
|
+
if (((_a = action.payload) === null || _a === void 0 ? void 0 : _a.error) === "room_full") {
|
|
2464
|
+
dispatch(doSetNotification(createNotificationEvent({
|
|
2465
|
+
type: "clientUnableToJoinFullRoom",
|
|
2466
|
+
message: "Someone tried to join but the room is full and at capacity.",
|
|
2467
|
+
props: {},
|
|
2468
|
+
})));
|
|
2469
|
+
}
|
|
2470
|
+
},
|
|
2471
|
+
});
|
|
2472
|
+
|
|
2473
|
+
const organizationSliceInitialState = {
|
|
2474
|
+
data: null,
|
|
2475
|
+
isFetching: false,
|
|
2476
|
+
error: null,
|
|
2477
|
+
};
|
|
2478
|
+
const organizationSlice = createSlice({
|
|
2479
|
+
name: "organization",
|
|
2480
|
+
initialState: organizationSliceInitialState,
|
|
2481
|
+
reducers: {},
|
|
2482
|
+
extraReducers: (builder) => {
|
|
2483
|
+
builder.addCase(doOrganizationFetch.pending, (state) => {
|
|
2484
|
+
return Object.assign(Object.assign({}, state), { isFetching: true });
|
|
2485
|
+
});
|
|
2486
|
+
builder.addCase(doOrganizationFetch.fulfilled, (state, action) => {
|
|
2487
|
+
if (!action.payload)
|
|
2488
|
+
return Object.assign(Object.assign({}, state), { isFetching: true });
|
|
2489
|
+
return Object.assign(Object.assign({}, state), { isFetching: false, data: action.payload });
|
|
2490
|
+
});
|
|
2491
|
+
builder.addCase(doOrganizationFetch.rejected, (state) => {
|
|
2492
|
+
return Object.assign(Object.assign({}, state), { isFetching: false, error: true });
|
|
2493
|
+
});
|
|
2494
|
+
},
|
|
2495
|
+
});
|
|
2496
|
+
const doOrganizationFetch = createAppAsyncThunk("organization/doOrganizationFetch", (_1, _a) => __awaiter(void 0, [_1, _a], void 0, function* (_, { extra, getState }) {
|
|
2497
|
+
try {
|
|
2498
|
+
const roomUrl = selectAppRoomUrl(getState());
|
|
2499
|
+
const organization = yield extra.services.fetchOrganizationFromRoomUrl(roomUrl || "");
|
|
2500
|
+
if (!organization) {
|
|
2501
|
+
throw new Error("Invalid room url");
|
|
2502
|
+
}
|
|
2503
|
+
return organization;
|
|
2504
|
+
}
|
|
2505
|
+
catch (error) {
|
|
2506
|
+
console.error(error);
|
|
2507
|
+
}
|
|
2508
|
+
}));
|
|
2509
|
+
const selectOrganizationRaw = (state) => state.organization;
|
|
2510
|
+
const selectOrganizationId = (state) => { var _a; return (_a = state.organization.data) === null || _a === void 0 ? void 0 : _a.organizationId; };
|
|
2511
|
+
const selectOrganizationPreferences = (state) => { var _a; return (_a = state.organization.data) === null || _a === void 0 ? void 0 : _a.preferences; };
|
|
2512
|
+
const selectShouldFetchOrganization = createSelector(selectAppIsActive, selectOrganizationRaw, selectDeviceCredentialsRaw, (appIsActive, organization, deviceCredentials) => {
|
|
2513
|
+
if (appIsActive &&
|
|
2514
|
+
!organization.data &&
|
|
2515
|
+
!organization.isFetching &&
|
|
2516
|
+
!organization.error &&
|
|
2517
|
+
!deviceCredentials.isFetching) {
|
|
2518
|
+
return true;
|
|
2519
|
+
}
|
|
2520
|
+
return false;
|
|
2521
|
+
});
|
|
2522
|
+
createReactor([selectShouldFetchOrganization], ({ dispatch }, shouldFetchOrganization) => {
|
|
2523
|
+
if (shouldFetchOrganization) {
|
|
2524
|
+
dispatch(doOrganizationFetch());
|
|
2525
|
+
}
|
|
2526
|
+
});
|
|
2527
|
+
|
|
2528
|
+
const roomConnectionSliceInitialState = {
|
|
2529
|
+
session: null,
|
|
2530
|
+
status: "ready",
|
|
2531
|
+
error: null,
|
|
2532
|
+
};
|
|
2533
|
+
const roomConnectionSlice = createSlice({
|
|
2534
|
+
name: "roomConnection",
|
|
2535
|
+
initialState: roomConnectionSliceInitialState,
|
|
2536
|
+
reducers: {
|
|
2537
|
+
connectionStatusChanged: (state, action) => {
|
|
2538
|
+
return Object.assign(Object.assign({}, state), { status: action.payload });
|
|
2539
|
+
},
|
|
2540
|
+
},
|
|
2541
|
+
extraReducers: (builder) => {
|
|
2542
|
+
builder.addCase(signalEvents.roomJoined, (state, action) => {
|
|
2543
|
+
var _a;
|
|
2544
|
+
if ("error" in action.payload) {
|
|
2545
|
+
if (action.payload.error === "room_locked" && action.payload.isLocked) {
|
|
2546
|
+
return Object.assign(Object.assign({}, state), { status: "room_locked" });
|
|
2547
|
+
}
|
|
2548
|
+
return Object.assign(Object.assign({}, state), { status: "disconnected", error: action.payload.error });
|
|
2549
|
+
}
|
|
2550
|
+
const { room } = action.payload;
|
|
2551
|
+
return Object.assign(Object.assign({}, state), { status: "connected", session: (_a = room.session) !== null && _a !== void 0 ? _a : null });
|
|
2552
|
+
});
|
|
2553
|
+
builder.addCase(signalEvents.disconnect, (state) => {
|
|
2554
|
+
if (["kicked", "left"].includes(state.status)) {
|
|
2555
|
+
return Object.assign({}, state);
|
|
2556
|
+
}
|
|
2557
|
+
return Object.assign(Object.assign({}, state), { status: "disconnected" });
|
|
2558
|
+
});
|
|
2559
|
+
builder.addCase(signalEvents.newClient, (state, action) => {
|
|
2560
|
+
var _a, _b;
|
|
2561
|
+
return Object.assign(Object.assign({}, state), { session: (_b = (_a = action.payload.room) === null || _a === void 0 ? void 0 : _a.session) !== null && _b !== void 0 ? _b : null });
|
|
2562
|
+
});
|
|
2563
|
+
builder.addCase(signalEvents.roomSessionEnded, (state, action) => {
|
|
2564
|
+
var _a;
|
|
2565
|
+
if (((_a = state.session) === null || _a === void 0 ? void 0 : _a.id) !== action.payload.roomSessionId) {
|
|
2566
|
+
return state;
|
|
2567
|
+
}
|
|
2568
|
+
return Object.assign(Object.assign({}, state), { session: null });
|
|
2569
|
+
});
|
|
2570
|
+
builder.addCase(signalEvents.clientKicked, (state) => {
|
|
2571
|
+
return Object.assign(Object.assign({}, state), { status: "kicked" });
|
|
2572
|
+
});
|
|
2573
|
+
builder.addCase(signalEvents.roomLeft, (state) => {
|
|
2574
|
+
return Object.assign(Object.assign({}, state), { status: "left" });
|
|
2575
|
+
});
|
|
2576
|
+
builder.addCase(socketReconnecting, (state) => {
|
|
2577
|
+
return Object.assign(Object.assign({}, state), { status: "reconnecting" });
|
|
2578
|
+
});
|
|
2579
|
+
},
|
|
2580
|
+
});
|
|
2581
|
+
const { connectionStatusChanged } = roomConnectionSlice.actions;
|
|
2582
|
+
const doKnockRoom = createAppThunk(() => (dispatch, getState) => {
|
|
2583
|
+
const state = getState();
|
|
2584
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
2585
|
+
const roomName = selectAppRoomName(state);
|
|
2586
|
+
const roomKey = selectRoomKey(state);
|
|
2587
|
+
const displayName = selectAppDisplayName(state);
|
|
2588
|
+
const isDialIn = selectAppIsDialIn(state);
|
|
2589
|
+
const userAgent = selectAppUserAgent(state);
|
|
2590
|
+
const externalId = selectAppExternalId(state);
|
|
2591
|
+
const organizationId = selectOrganizationId(state);
|
|
2592
|
+
const connectionStatus = selectRoomConnectionStatus(state);
|
|
2593
|
+
if (connectionStatus !== "room_locked") {
|
|
2594
|
+
console.warn("Room is not locked, knock aborted");
|
|
2595
|
+
return;
|
|
2596
|
+
}
|
|
2597
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("knock_room", {
|
|
2598
|
+
avatarUrl: null,
|
|
2599
|
+
config: {
|
|
2600
|
+
isAudioEnabled: true,
|
|
2601
|
+
isVideoEnabled: true,
|
|
2602
|
+
},
|
|
2603
|
+
deviceCapabilities: { canScreenshare: true },
|
|
2604
|
+
displayName,
|
|
2605
|
+
isCoLocated: false,
|
|
2606
|
+
isDialIn,
|
|
2607
|
+
isDevicePermissionDenied: false,
|
|
2608
|
+
kickFromOtherRooms: false,
|
|
2609
|
+
organizationId,
|
|
2610
|
+
roomKey,
|
|
2611
|
+
roomName,
|
|
2612
|
+
userAgent,
|
|
2613
|
+
externalId,
|
|
2614
|
+
});
|
|
2615
|
+
dispatch(connectionStatusChanged("knocking"));
|
|
2616
|
+
});
|
|
2617
|
+
const doConnectRoom = createAppThunk(() => (dispatch, getState) => {
|
|
2618
|
+
const state = getState();
|
|
2619
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
2620
|
+
const roomName = selectAppRoomName(state);
|
|
2621
|
+
const roomKey = selectRoomKey(state);
|
|
2622
|
+
const displayName = selectAppDisplayName(state);
|
|
2623
|
+
const userAgent = selectAppUserAgent(state);
|
|
2624
|
+
const externalId = selectAppExternalId(state);
|
|
2625
|
+
const isDialIn = selectAppIsDialIn(state);
|
|
2626
|
+
const organizationId = selectOrganizationId(state);
|
|
2627
|
+
const isCameraEnabled = selectIsCameraEnabled(getState());
|
|
2628
|
+
const isMicrophoneEnabled = selectIsMicrophoneEnabled(getState());
|
|
2629
|
+
const clientClaim = selectLocalParticipantClientClaim(getState());
|
|
2630
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("join_room", Object.assign({ avatarUrl: null, config: {
|
|
2631
|
+
isAudioEnabled: isMicrophoneEnabled,
|
|
2632
|
+
isVideoEnabled: isCameraEnabled,
|
|
2633
|
+
}, deviceCapabilities: { canScreenshare: true }, displayName, isCoLocated: false, isDialIn, isDevicePermissionDenied: false, kickFromOtherRooms: false, organizationId,
|
|
2634
|
+
roomKey,
|
|
2635
|
+
roomName,
|
|
2636
|
+
userAgent,
|
|
2637
|
+
externalId }, (clientClaim && { clientClaim })));
|
|
2638
|
+
dispatch(connectionStatusChanged("connecting"));
|
|
2639
|
+
});
|
|
2640
|
+
const selectShouldConnectRoom = createSelector([
|
|
2641
|
+
selectAppIsActive,
|
|
2642
|
+
selectOrganizationId,
|
|
2643
|
+
selectRoomConnectionStatus,
|
|
2644
|
+
selectSignalConnectionDeviceIdentified,
|
|
2645
|
+
selectLocalMediaStatus,
|
|
2646
|
+
selectRoomConnectionError,
|
|
2647
|
+
], (appIsActive, hasOrganizationIdFetched, roomConnectionStatus, signalConnectionDeviceIdentified, localMediaStatus, roomConnectionError) => {
|
|
2648
|
+
if (appIsActive &&
|
|
2649
|
+
localMediaStatus === "started" &&
|
|
2650
|
+
signalConnectionDeviceIdentified &&
|
|
2651
|
+
!!hasOrganizationIdFetched &&
|
|
2652
|
+
["ready", "reconnecting", "disconnected"].includes(roomConnectionStatus) &&
|
|
2653
|
+
!roomConnectionError) {
|
|
2654
|
+
return true;
|
|
2655
|
+
}
|
|
2656
|
+
return false;
|
|
2657
|
+
});
|
|
2658
|
+
createReactor([selectShouldConnectRoom], ({ dispatch }, shouldConnectRoom) => {
|
|
2659
|
+
if (shouldConnectRoom) {
|
|
2660
|
+
dispatch(doConnectRoom());
|
|
2661
|
+
}
|
|
2662
|
+
});
|
|
2663
|
+
startAppListening({
|
|
2664
|
+
actionCreator: signalEvents.knockHandled,
|
|
2665
|
+
effect: ({ payload }, { dispatch, getState }) => {
|
|
2666
|
+
const { clientId, resolution } = payload;
|
|
2667
|
+
const state = getState();
|
|
2668
|
+
const selfId = selectSelfId(state);
|
|
2669
|
+
if (clientId !== selfId) {
|
|
2670
|
+
return;
|
|
2671
|
+
}
|
|
2672
|
+
if (resolution === "accepted") {
|
|
2673
|
+
dispatch(setRoomKey(payload.metadata.roomKey));
|
|
2674
|
+
dispatch(doConnectRoom());
|
|
2675
|
+
}
|
|
2676
|
+
else if (resolution === "rejected") {
|
|
2677
|
+
dispatch(connectionStatusChanged("knock_rejected"));
|
|
2678
|
+
}
|
|
2679
|
+
},
|
|
2680
|
+
});
|
|
2681
|
+
startAppListening({
|
|
2682
|
+
actionCreator: doAppStop,
|
|
2683
|
+
effect: (_, { dispatch, getState }) => {
|
|
2684
|
+
const state = getState();
|
|
2685
|
+
const roomConnectionStatus = selectRoomConnectionStatus(state);
|
|
2686
|
+
if (roomConnectionStatus === "connected") {
|
|
2687
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
2688
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("leave_room");
|
|
2689
|
+
dispatch(connectionStatusChanged("leaving"));
|
|
2690
|
+
}
|
|
2691
|
+
else {
|
|
2692
|
+
doSignalDisconnect();
|
|
2693
|
+
}
|
|
2694
|
+
},
|
|
2695
|
+
});
|
|
2696
|
+
|
|
2697
|
+
const rtcAnalyticsCustomEvents = {
|
|
2698
|
+
audioEnabled: {
|
|
2699
|
+
actions: [doEnableAudio.fulfilled],
|
|
2700
|
+
rtcEventName: "audioEnabled",
|
|
2701
|
+
getValue: (state) => selectIsMicrophoneEnabled(state),
|
|
2702
|
+
getOutput: (value) => ({ enabled: value }),
|
|
2703
|
+
},
|
|
2704
|
+
videoEnabled: {
|
|
2705
|
+
actions: [doEnableVideo.fulfilled],
|
|
2706
|
+
rtcEventName: "videoEnabled",
|
|
2707
|
+
getValue: (state) => selectIsCameraEnabled(state),
|
|
2708
|
+
getOutput: (value) => ({ enabled: value }),
|
|
2709
|
+
},
|
|
2710
|
+
localStream: {
|
|
2711
|
+
actions: [doSetDevice.fulfilled],
|
|
2712
|
+
rtcEventName: "localStream",
|
|
2713
|
+
getValue: (state) => {
|
|
2714
|
+
var _a;
|
|
2715
|
+
return (_a = selectLocalMediaStream(state)) === null || _a === void 0 ? void 0 : _a.getTracks().map((track) => ({ id: track.id, kind: track.kind, label: track.label }));
|
|
2716
|
+
},
|
|
2717
|
+
getOutput: (value) => ({ stream: value }),
|
|
2718
|
+
},
|
|
2719
|
+
localScreenshareStream: {
|
|
2720
|
+
actions: [doStartScreenshare.fulfilled],
|
|
2721
|
+
rtcEventName: "localScreenshareStream",
|
|
2722
|
+
getValue: (state) => {
|
|
2723
|
+
var _a;
|
|
2724
|
+
return (_a = selectLocalScreenshareStream(state)) === null || _a === void 0 ? void 0 : _a.getTracks().map((track) => ({ id: track.id, kind: track.kind, label: track.label }));
|
|
2725
|
+
},
|
|
2726
|
+
getOutput: (value) => ({ tracks: value }),
|
|
2727
|
+
},
|
|
2728
|
+
localScreenshareStreamStopped: {
|
|
2729
|
+
actions: [stopScreenshare],
|
|
2730
|
+
rtcEventName: "localScreenshareStream",
|
|
2731
|
+
getValue: () => () => null,
|
|
2732
|
+
getOutput: () => ({}),
|
|
2733
|
+
},
|
|
2734
|
+
displayName: {
|
|
2735
|
+
actions: [setDisplayName],
|
|
2736
|
+
rtcEventName: "displayName",
|
|
2737
|
+
getValue: (state) => {
|
|
2738
|
+
const displayName = selectLocalParticipantDisplayName(state);
|
|
2739
|
+
const prefs = selectOrganizationPreferences(state);
|
|
2740
|
+
return (prefs === null || prefs === void 0 ? void 0 : prefs.hideInsightsDisplayNames) ? "[[redacted]]" : displayName;
|
|
2741
|
+
},
|
|
2742
|
+
getOutput: (value) => ({ displayName: value }),
|
|
2743
|
+
},
|
|
2744
|
+
clientId: {
|
|
2745
|
+
actions: null,
|
|
2746
|
+
rtcEventName: "clientId",
|
|
2747
|
+
getValue: (state) => selectSelfId(state),
|
|
2748
|
+
getOutput: (value) => ({ clientId: value }),
|
|
2749
|
+
},
|
|
2750
|
+
deviceId: {
|
|
2751
|
+
actions: null,
|
|
2752
|
+
rtcEventName: "deviceId",
|
|
2753
|
+
getValue: (state) => selectDeviceId(state),
|
|
2754
|
+
getOutput: (value) => ({ deviceId: value }),
|
|
2755
|
+
},
|
|
2756
|
+
externalId: {
|
|
2757
|
+
actions: null,
|
|
2758
|
+
rtcEventName: "externalId",
|
|
2759
|
+
getValue: (state) => selectAppExternalId(state),
|
|
2760
|
+
getOutput: (value) => ({ externalId: value }),
|
|
2761
|
+
},
|
|
2762
|
+
organizationId: {
|
|
2763
|
+
actions: null,
|
|
2764
|
+
rtcEventName: "organizationId",
|
|
2765
|
+
getValue: (state) => selectOrganizationId(state),
|
|
2766
|
+
getOutput: (value) => ({ organizationId: value }),
|
|
2767
|
+
},
|
|
2768
|
+
signalConnectionStatus: {
|
|
2769
|
+
actions: null,
|
|
2770
|
+
rtcEventName: "signalConnectionStatus",
|
|
2771
|
+
getValue: (state) => selectSignalStatus(state),
|
|
2772
|
+
getOutput: (value) => ({ status: value }),
|
|
2773
|
+
},
|
|
2774
|
+
roomSessionId: {
|
|
2775
|
+
actions: [
|
|
2776
|
+
signalEvents.newClient,
|
|
2777
|
+
signalEvents.roomJoined,
|
|
2778
|
+
signalEvents.roomSessionEnded,
|
|
2779
|
+
signalEvents.clientLeft,
|
|
2780
|
+
],
|
|
2781
|
+
rtcEventName: "roomSessionId",
|
|
2782
|
+
getValue: (state) => selectRoomConnectionSessionId(state),
|
|
2783
|
+
getOutput: (value) => ({ roomSessionId: value }),
|
|
2784
|
+
},
|
|
2785
|
+
rtcConnectionStatus: {
|
|
2786
|
+
actions: null,
|
|
2787
|
+
rtcEventName: "rtcConnectionStatus",
|
|
2788
|
+
getValue: (state) => selectRtcStatus(state),
|
|
2789
|
+
getOutput: (value) => ({ status: value }),
|
|
2790
|
+
},
|
|
2791
|
+
userRole: {
|
|
2792
|
+
actions: null,
|
|
2793
|
+
rtcEventName: "userRole",
|
|
2794
|
+
getValue: (state) => selectAuthorizationRoleName(state),
|
|
2795
|
+
getOutput: (value) => ({ userRole: value }),
|
|
2796
|
+
},
|
|
2797
|
+
};
|
|
2798
|
+
const rtcCustomEventActions = Object.values(rtcAnalyticsCustomEvents)
|
|
2799
|
+
.flatMap(({ actions }) => { var _a; return (_a = actions === null || actions === void 0 ? void 0 : actions.map((action) => action)) !== null && _a !== void 0 ? _a : null; })
|
|
2800
|
+
.filter((action) => action !== null);
|
|
2801
|
+
const makeComparable = (value) => {
|
|
2802
|
+
if (typeof value === "object")
|
|
2803
|
+
return JSON.stringify(value);
|
|
2804
|
+
return value;
|
|
2805
|
+
};
|
|
2806
|
+
const rtcAnalyticsSliceInitialState = {
|
|
2807
|
+
reportedValues: {},
|
|
2808
|
+
};
|
|
2809
|
+
const rtcAnalyticsSlice = createSlice({
|
|
2810
|
+
name: "rtcAnalytics",
|
|
2811
|
+
initialState: rtcAnalyticsSliceInitialState,
|
|
2812
|
+
reducers: {
|
|
2813
|
+
updateReportedValues(state, action) {
|
|
2814
|
+
return Object.assign(Object.assign({}, state), { reportedValues: Object.assign(Object.assign({}, state.reportedValues), { [action.payload.rtcEventName]: action.payload.value }) });
|
|
2815
|
+
},
|
|
2816
|
+
},
|
|
2817
|
+
});
|
|
2818
|
+
const doRtcAnalyticsCustomEventsInitialize = createAppThunk(() => (dispatch, getState) => {
|
|
2819
|
+
const state = getState();
|
|
2820
|
+
const rtcManager = selectRtcConnectionRaw(state).rtcManager;
|
|
2821
|
+
if (!rtcManager)
|
|
2822
|
+
return;
|
|
2823
|
+
rtcManager.sendStatsCustomEvent("insightsStats", {
|
|
2824
|
+
_time: Date.now(),
|
|
2825
|
+
ls: 0,
|
|
2826
|
+
lr: 0,
|
|
2827
|
+
bs: 0,
|
|
2828
|
+
br: 0,
|
|
2829
|
+
cpu: 0,
|
|
2830
|
+
});
|
|
2831
|
+
Object.values(rtcAnalyticsCustomEvents).forEach(({ rtcEventName, getValue, getOutput }) => {
|
|
2832
|
+
var _a;
|
|
2833
|
+
const value = getValue(state);
|
|
2834
|
+
const output = Object.assign(Object.assign({}, getOutput(value)), { _time: Date.now() });
|
|
2835
|
+
const comparableValue = makeComparable(value);
|
|
2836
|
+
if (((_a = state.rtcAnalytics.reportedValues) === null || _a === void 0 ? void 0 : _a[rtcEventName]) !== comparableValue) {
|
|
2837
|
+
rtcManager.sendStatsCustomEvent(rtcEventName, output);
|
|
2838
|
+
dispatch(updateReportedValues({ rtcEventName, value }));
|
|
2839
|
+
}
|
|
2840
|
+
});
|
|
2841
|
+
});
|
|
2842
|
+
const { updateReportedValues } = rtcAnalyticsSlice.actions;
|
|
2843
|
+
startAppListening({
|
|
2844
|
+
matcher: isAnyOf(...rtcCustomEventActions),
|
|
2845
|
+
effect: ({ type }, { getState, dispatch }) => {
|
|
2846
|
+
var _a;
|
|
2847
|
+
const state = getState();
|
|
2848
|
+
const rtcManager = selectRtcConnectionRaw(state).rtcManager;
|
|
2849
|
+
if (!rtcManager)
|
|
2850
|
+
return;
|
|
2851
|
+
const rtcCustomEvent = Object.values(rtcAnalyticsCustomEvents).find(({ actions }) => actions === null || actions === void 0 ? void 0 : actions.map((a) => a.type).includes(type));
|
|
2852
|
+
if (!rtcCustomEvent)
|
|
2853
|
+
return;
|
|
2854
|
+
const { getValue, getOutput, rtcEventName } = rtcCustomEvent;
|
|
2855
|
+
const value = getValue(state);
|
|
2856
|
+
const comparableValue = makeComparable(value);
|
|
2857
|
+
const output = Object.assign(Object.assign({}, getOutput(value)), { _time: Date.now() });
|
|
2858
|
+
if (((_a = state.rtcAnalytics.reportedValues) === null || _a === void 0 ? void 0 : _a[rtcEventName]) !== comparableValue) {
|
|
2859
|
+
rtcManager.sendStatsCustomEvent(rtcEventName, output);
|
|
2860
|
+
dispatch(updateReportedValues({ rtcEventName, value }));
|
|
2861
|
+
}
|
|
2862
|
+
},
|
|
2863
|
+
});
|
|
2864
|
+
createReactor([selectRtcManagerInitialized], ({ dispatch }, selectRtcManagerInitialized) => {
|
|
2865
|
+
if (selectRtcManagerInitialized) {
|
|
2866
|
+
dispatch(doRtcAnalyticsCustomEventsInitialize());
|
|
2867
|
+
}
|
|
2868
|
+
});
|
|
2869
|
+
|
|
2870
|
+
const streamingSliceInitialState = {
|
|
2871
|
+
isStreaming: false,
|
|
2872
|
+
error: null,
|
|
2873
|
+
startedAt: undefined,
|
|
2874
|
+
};
|
|
2875
|
+
const streamingSlice = createSlice({
|
|
2876
|
+
name: "streaming",
|
|
2877
|
+
initialState: streamingSliceInitialState,
|
|
2878
|
+
reducers: {
|
|
2879
|
+
doHandleStreamingStarted: (state) => {
|
|
2880
|
+
return Object.assign(Object.assign({}, state), { isStreaming: true, error: null, startedAt: new Date().getTime() });
|
|
2881
|
+
},
|
|
2882
|
+
doHandleStreamingStopped: (state) => {
|
|
2883
|
+
return Object.assign(Object.assign({}, state), { isStreaming: false });
|
|
2884
|
+
},
|
|
2885
|
+
},
|
|
2886
|
+
});
|
|
2887
|
+
const { doHandleStreamingStarted, doHandleStreamingStopped } = streamingSlice.actions;
|
|
2888
|
+
const selectStreamingRaw = (state) => state.streaming;
|
|
2889
|
+
|
|
2890
|
+
const waitingParticipantsSliceInitialState = {
|
|
2891
|
+
waitingParticipants: [],
|
|
2892
|
+
};
|
|
2893
|
+
const waitingParticipantsSlice = createSlice({
|
|
2894
|
+
name: "waitingParticipants",
|
|
2895
|
+
initialState: waitingParticipantsSliceInitialState,
|
|
2896
|
+
reducers: {},
|
|
2897
|
+
extraReducers: (builder) => {
|
|
2898
|
+
builder.addCase(signalEvents.roomJoined, (state, action) => {
|
|
2899
|
+
if ("error" in action.payload) {
|
|
2900
|
+
return state;
|
|
2901
|
+
}
|
|
2902
|
+
const { room } = action.payload;
|
|
2903
|
+
if (room.knockers.length) {
|
|
2904
|
+
return Object.assign(Object.assign({}, state), { waitingParticipants: room.knockers.map((knocker) => ({
|
|
2905
|
+
id: knocker.clientId,
|
|
2906
|
+
displayName: knocker.displayName,
|
|
2907
|
+
})) });
|
|
2908
|
+
}
|
|
2909
|
+
return state;
|
|
2910
|
+
});
|
|
2911
|
+
builder.addCase(signalEvents.roomKnocked, (state, action) => {
|
|
2912
|
+
const { clientId, displayName } = action.payload;
|
|
2913
|
+
return Object.assign(Object.assign({}, state), { waitingParticipants: [...state.waitingParticipants, { id: clientId, displayName }] });
|
|
2914
|
+
});
|
|
2915
|
+
builder.addCase(signalEvents.knockerLeft, (state, action) => {
|
|
2916
|
+
const { clientId } = action.payload;
|
|
2917
|
+
return Object.assign(Object.assign({}, state), { waitingParticipants: state.waitingParticipants.filter((p) => p.id !== clientId) });
|
|
2918
|
+
});
|
|
2919
|
+
},
|
|
2920
|
+
});
|
|
2921
|
+
const doAcceptWaitingParticipant = createRoomConnectedThunk((payload) => (dispatch, getState) => {
|
|
2922
|
+
const { participantId } = payload;
|
|
2923
|
+
const state = getState();
|
|
2924
|
+
const socket = selectSignalConnectionSocket(state);
|
|
2925
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("handle_knock", {
|
|
2926
|
+
action: "accept",
|
|
2927
|
+
clientId: participantId,
|
|
2928
|
+
response: {},
|
|
2929
|
+
});
|
|
2930
|
+
});
|
|
2931
|
+
const doRejectWaitingParticipant = createRoomConnectedThunk((payload) => (dispatch, getState) => {
|
|
2932
|
+
const { participantId } = payload;
|
|
2933
|
+
const state = getState();
|
|
2934
|
+
const socket = selectSignalConnectionSocket(state);
|
|
2935
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("handle_knock", {
|
|
2936
|
+
action: "reject",
|
|
2937
|
+
clientId: participantId,
|
|
2938
|
+
response: {},
|
|
2939
|
+
});
|
|
2940
|
+
});
|
|
2941
|
+
const selectWaitingParticipantsRaw = (state) => state.waitingParticipants;
|
|
2942
|
+
const selectWaitingParticipants = (state) => state.waitingParticipants.waitingParticipants;
|
|
2943
|
+
|
|
2944
|
+
const IS_DEV = undefined === "true";
|
|
2945
|
+
const appReducer = combineReducers({
|
|
2946
|
+
app: appSlice.reducer,
|
|
2947
|
+
authorization: authorizationSlice.reducer,
|
|
2948
|
+
breakout: breakoutSlice.reducer,
|
|
2949
|
+
chat: chatSlice.reducer,
|
|
2950
|
+
cloudRecording: cloudRecordingSlice.reducer,
|
|
2951
|
+
connectionMonitor: connectionMonitorSlice.reducer,
|
|
2952
|
+
deviceCredentials: deviceCredentialsSlice.reducer,
|
|
2953
|
+
localMedia: localMediaSlice.reducer,
|
|
2954
|
+
localParticipant: localParticipantSlice.reducer,
|
|
2955
|
+
localScreenshare: localScreenshareSlice.reducer,
|
|
2956
|
+
notifications: notificationsSlice.reducer,
|
|
2957
|
+
organization: organizationSlice.reducer,
|
|
2958
|
+
remoteParticipants: remoteParticipantsSlice.reducer,
|
|
2959
|
+
room: roomSlice.reducer,
|
|
2960
|
+
roomConnection: roomConnectionSlice.reducer,
|
|
2961
|
+
rtcAnalytics: rtcAnalyticsSlice.reducer,
|
|
2962
|
+
rtcConnection: rtcConnectionSlice.reducer,
|
|
2963
|
+
signalConnection: signalConnectionSlice.reducer,
|
|
2964
|
+
spotlights: spotlightsSlice.reducer,
|
|
2965
|
+
streaming: streamingSlice.reducer,
|
|
2966
|
+
waitingParticipants: waitingParticipantsSlice.reducer,
|
|
2967
|
+
});
|
|
2968
|
+
const rootReducer = (state, action) => {
|
|
2969
|
+
var _a;
|
|
2970
|
+
if (doAppStart.match(action)) {
|
|
2971
|
+
const resetState = {
|
|
2972
|
+
app: Object.assign(Object.assign({}, appSlice.getInitialState()), { initialConfig: (_a = state === null || state === void 0 ? void 0 : state.app) === null || _a === void 0 ? void 0 : _a.initialConfig }),
|
|
2973
|
+
localMedia: Object.assign(Object.assign({}, localMediaSlice.getInitialState()), state === null || state === void 0 ? void 0 : state.localMedia),
|
|
2974
|
+
};
|
|
2975
|
+
return appReducer(resetState, action);
|
|
2976
|
+
}
|
|
2977
|
+
return appReducer(state, action);
|
|
2978
|
+
};
|
|
2979
|
+
const createStore = ({ preloadedState, injectServices, }) => {
|
|
2980
|
+
return configureStore({
|
|
2981
|
+
devTools: IS_DEV,
|
|
2982
|
+
reducer: rootReducer,
|
|
2983
|
+
middleware: (getDefaultMiddleware) => getDefaultMiddleware({
|
|
2984
|
+
thunk: {
|
|
2985
|
+
extraArgument: { services: injectServices },
|
|
2986
|
+
},
|
|
2987
|
+
serializableCheck: false,
|
|
2988
|
+
}).prepend(listenerMiddleware.middleware),
|
|
2989
|
+
preloadedState,
|
|
2990
|
+
});
|
|
2991
|
+
};
|
|
2992
|
+
const observeStore = (store, select, onChange) => {
|
|
2993
|
+
let currentState;
|
|
2994
|
+
function handleChange() {
|
|
2995
|
+
const nextState = select(store.getState());
|
|
2996
|
+
if (nextState !== currentState) {
|
|
2997
|
+
currentState = nextState;
|
|
2998
|
+
onChange(currentState);
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3001
|
+
const unsubscribe = store.subscribe(handleChange);
|
|
3002
|
+
handleChange();
|
|
3003
|
+
return unsubscribe;
|
|
3004
|
+
};
|
|
3005
|
+
|
|
3006
|
+
export { addAppListener, addSpotlight, appSlice, authorizationSlice, authorizationSliceInitialState, breakoutSlice, breakoutSliceInitialState, chatSlice, chatSliceInitialState, cloudRecordingSlice, connectionMonitorSlice, connectionMonitorSliceInitialState, connectionMonitorStarted, connectionMonitorStopped, createAppAsyncThunk, createAppAuthorizedThunk, createAppThunk, createAsyncRoomConnectedThunk, createAuthorizedRoomConnectedThunk, createReactor, createRemoteParticipant, createRoomConnectedThunk, createStore, createWebRtcEmitter, deviceBusy, deviceCredentialsSlice, deviceCredentialsSliceInitialState, deviceIdentified, deviceIdentifying, doAcceptWaitingParticipant, doAppStart, doAppStop, doBreakoutJoin, doClearNotifications, doConnectRoom, doConnectRtc, doDisconnectRtc, doEnableAudio, doEnableVideo, doEndMeeting, doGetDeviceCredentials, doHandleAcceptStreams, doHandleStreamingStarted, doHandleStreamingStopped, doKickParticipant, doKnockRoom, doLockRoom, doOrganizationFetch, doRejectWaitingParticipant, doRemoveSpotlight, doRequestAudioEnable, doRequestVideoEnable, doRtcAnalyticsCustomEventsInitialize, doRtcManagerCreated, doRtcManagerInitialize, doRtcReportStreamResolution, doSendChatMessage, doSendClientMetadata, doSetDevice, doSetDisplayName, doSetLocalStickyReaction, doSetNotification, doSignalConnect, doSignalDisconnect, doSignalIdentifyDevice, doSpotlightParticipant, doStartCloudRecording, doStartConnectionMonitor, doStartLocalMedia, doStartScreenshare, doStopCloudRecording, doStopConnectionMonitor, doStopLocalMedia, doStopScreenshare, doSwitchLocalStream, doToggleCamera, doToggleLowDataMode, doUpdateDeviceList, initialCloudRecordingState, initialLocalMediaState, initialNotificationsState, initialState, isAcceptingStreams, isClientSpotlighted, listenerMiddleware, localMediaSlice, localMediaStopped, localParticipantSlice, localParticipantSliceInitialState, localScreenshareSlice, localScreenshareSliceInitialState, localStreamMetadataUpdated, notificationsSlice, observeStore, organizationSlice, organizationSliceInitialState, participantStreamAdded, participantStreamIdAdded, recordingRequestStarted, remoteParticipantsSlice, remoteParticipantsSliceInitialState, removeSpotlight, resolutionReported, roomConnectionSlice, roomConnectionSliceInitialState, roomSlice, roomSliceInitialState, rootReducer, rtcAnalyticsCustomEvents, rtcAnalyticsSlice, rtcAnalyticsSliceInitialState, rtcClientConnectionStatusChanged, rtcConnectionSlice, rtcConnectionSliceInitialState, rtcDisconnected, rtcDispatcherCreated, rtcEvents, rtcManagerCreated, rtcManagerDestroyed, rtcManagerInitialized, selectAllClientViews, selectAllClientViewsInCurrentGroup, selectAppDisplayName, selectAppExternalId, selectAppIgnoreBreakoutGroups, selectAppInitialConfig, selectAppIsActive, selectAppIsDialIn, selectAppIsNodeSdk, selectAppRaw, selectAppRoomName, selectAppRoomUrl, selectAppUserAgent, selectAuthorizationRoleName, selectBreakoutActive, selectBreakoutAssignments, selectBreakoutCurrentGroup, selectBreakoutCurrentId, selectBreakoutGroupedParticipants, selectBreakoutGroups, selectBreakoutInitiatedBy, selectBreakoutRaw, selectBusyDeviceIds, selectCameraDeviceError, selectCameraDevices, selectChatMessages, selectChatRaw, selectCloudRecordingError, selectCloudRecordingRaw, selectCloudRecordingStartedAt, selectCloudRecordingStatus, selectConnectionMonitorIsRunning, selectCurrentCameraDeviceId, selectCurrentMicrophoneDeviceId, selectCurrentSpeakerDeviceId, selectDeviceCredentialsRaw, selectDeviceId, selectHasFetchedDeviceCredentials, selectIsAcceptingStreams, selectIsAuthorizedToAskToSpeak, selectIsAuthorizedToEndMeeting, selectIsAuthorizedToKickClient, selectIsAuthorizedToLockRoom, selectIsAuthorizedToRequestAudioEnable, selectIsAuthorizedToRequestVideoEnable, selectIsAuthorizedToSpotlight, selectIsCameraEnabled, selectIsCloudRecording, selectIsLocalMediaStarting, selectIsLocalParticipantSpotlighted, selectIsLowDataModeEnabled, selectIsMicrophoneEnabled, selectIsSettingCameraDevice, selectIsSettingMicrophoneDevice, selectIsToggleCamera, selectLocalMediaConstraintsOptions, selectLocalMediaDevices, selectLocalMediaIsSwitchingStream, selectLocalMediaOptions, selectLocalMediaOwnsStream, selectLocalMediaRaw, selectLocalMediaShouldStartWithOptions, selectLocalMediaShouldStop, selectLocalMediaStartError, selectLocalMediaStatus, selectLocalMediaStream, selectLocalParticipantBreakoutAssigned, selectLocalParticipantBreakoutGroup, selectLocalParticipantClientClaim, selectLocalParticipantDisplayName, selectLocalParticipantIsScreenSharing, selectLocalParticipantRaw, selectLocalParticipantStickyReaction, selectLocalParticipantView, selectLocalScreenshareRaw, selectLocalScreenshareStatus, selectLocalScreenshareStream, selectMicrophoneDeviceError, selectMicrophoneDevices, selectNotificationsEmitter, selectNotificationsEvents, selectNotificationsRaw, selectNumClients, selectNumParticipants, selectOrganizationId, selectOrganizationPreferences, selectOrganizationRaw, selectRemoteClientViews, selectRemoteClients, selectRemoteParticipants, selectRemoteParticipantsRaw, selectRoomConnectionError, selectRoomConnectionRaw, selectRoomConnectionSession, selectRoomConnectionSessionId, selectRoomConnectionStatus, selectRoomIsLocked, selectRoomKey, selectRtcConnectionRaw, selectRtcDispatcherCreated, selectRtcIsCreatingDispatcher, selectRtcManager, selectRtcManagerInitialized, selectRtcStatus, selectScreenshares, selectSelfId, selectShouldConnectRoom, selectShouldConnectRtc, selectShouldConnectSignal, selectShouldDisconnectRtc, selectShouldFetchDeviceCredentials, selectShouldFetchOrganization, selectShouldIdentifyDevice, selectShouldInitializeRtc, selectShouldStartConnectionMonitor, selectShouldStopConnectionMonitor, selectSignalConnectionDeviceIdentified, selectSignalConnectionRaw, selectSignalConnectionSocket, selectSignalIsIdentifyingDevice, selectSignalStatus, selectSpeakerDevices, selectSpotlightedClientViews, selectSpotlights, selectSpotlightsRaw, selectStopCallbackFunction, selectStreamingRaw, selectStreamsToAccept, selectWaitingParticipants, selectWaitingParticipantsRaw, setBreakoutGroupAssigned, setCurrentCameraDeviceId, setCurrentMicrophoneDeviceId, setCurrentSpeakerDeviceId, setDisplayName, setLocalMediaOptions, setLocalMediaStream, setRoomKey, signalConnectionSlice, signalConnectionSliceInitialState, signalEvents, socketConnected, socketConnecting, socketDisconnected, socketReconnecting, spotlightsSlice, spotlightsSliceInitialState, startAppListening, stopScreenshare, streamIdForClient, streamStatusUpdated, streamingSlice, streamingSliceInitialState, toggleCameraEnabled, toggleLowDataModeEnabled, toggleMicrophoneEnabled, updateReportedValues, waitingParticipantsSlice, waitingParticipantsSliceInitialState };
|