@whereby.com/core 0.29.0 → 0.29.2
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 +1 -1
- package/dist/index.mjs +1 -1
- package/dist/legacy-esm.js +3888 -0
- package/package.json +3 -2
|
@@ -0,0 +1,3888 @@
|
|
|
1
|
+
import { createAsyncThunk, createListenerMiddleware, addListener, createSlice, createAction, createSelector, isAnyOf, combineReducers, configureStore } from '@reduxjs/toolkit';
|
|
2
|
+
import { ServerSocket, getDeviceData, getStream, getUpdatedDevices, RtcManagerDispatcher, setClientProvider, subscribeIssues, assert, fromLocation } from '@whereby.com/media';
|
|
3
|
+
import { Safari12 } from 'mediasoup-client/lib/handlers/Safari12.js';
|
|
4
|
+
import { EventEmitter } from 'events';
|
|
5
|
+
import nodeBtoa from 'btoa';
|
|
6
|
+
import axios from 'axios';
|
|
7
|
+
|
|
8
|
+
function createAppAsyncThunk(typePrefix, payloadCreator) {
|
|
9
|
+
return createAsyncThunk(typePrefix, payloadCreator);
|
|
10
|
+
}
|
|
11
|
+
function createAppThunk(thunk) {
|
|
12
|
+
return thunk;
|
|
13
|
+
}
|
|
14
|
+
function createAppAuthorizedThunk(authorizationSelector, thunk) {
|
|
15
|
+
return createAppThunk((payload) => (dispatch, getState, extra) => {
|
|
16
|
+
const isAuthorized = authorizationSelector(getState());
|
|
17
|
+
if (!isAuthorized) {
|
|
18
|
+
console.warn("Not authorized to perform this action");
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
return thunk(payload)(dispatch, getState, extra);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function createRoomConnectedThunk(thunk) {
|
|
25
|
+
return createAppThunk((payload) => (dispatch, getState, extra) => {
|
|
26
|
+
const connectionStatus = getState().roomConnection.status;
|
|
27
|
+
if (connectionStatus !== "connected") {
|
|
28
|
+
console.warn("Action cannot be performed outside of a connected room");
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return thunk(payload)(dispatch, getState, extra);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function createAsyncRoomConnectedThunk(typePrefix, payloadCreator) {
|
|
35
|
+
return createAppAsyncThunk(typePrefix, (arg, thunkApi) => {
|
|
36
|
+
const { getState } = thunkApi;
|
|
37
|
+
const connectionStatus = getState().roomConnection.status;
|
|
38
|
+
if (connectionStatus !== "connected") {
|
|
39
|
+
console.warn("Action cannot be performed outside of a connected room");
|
|
40
|
+
return Promise.reject("Action cannot be performed outside of a connected room");
|
|
41
|
+
}
|
|
42
|
+
return payloadCreator(arg, thunkApi);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function createAuthorizedRoomConnectedThunk(authorizationSelector, thunk) {
|
|
46
|
+
return createAppThunk((payload) => (dispatch, getState, extra) => {
|
|
47
|
+
const connectionStatus = getState().roomConnection.status;
|
|
48
|
+
if (connectionStatus !== "connected") {
|
|
49
|
+
console.warn("Action cannot be performed outside of a connected room");
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return createAppAuthorizedThunk(authorizationSelector, thunk)(payload)(dispatch, getState, extra);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const listenerMiddleware = createListenerMiddleware();
|
|
57
|
+
const startAppListening = listenerMiddleware.startListening;
|
|
58
|
+
const addAppListener = addListener;
|
|
59
|
+
const createReactor = (selectors, callback) => {
|
|
60
|
+
return startAppListening({
|
|
61
|
+
predicate: (_, currentState, previousState) => {
|
|
62
|
+
const previousValues = selectors.map((selector) => selector(previousState));
|
|
63
|
+
const currentValues = selectors.map((selector) => selector(currentState));
|
|
64
|
+
return previousValues.some((previousValue, index) => previousValue !== currentValues[index]);
|
|
65
|
+
},
|
|
66
|
+
effect: (action, { dispatch, getState, extra }) => {
|
|
67
|
+
const selectorResults = selectors.map((selector) => selector(getState()));
|
|
68
|
+
callback({
|
|
69
|
+
dispatch,
|
|
70
|
+
getState,
|
|
71
|
+
extra,
|
|
72
|
+
}, ...selectorResults);
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const coreVersion = "0.29.2";
|
|
78
|
+
|
|
79
|
+
const initialState$g = {
|
|
80
|
+
isNodeSdk: false,
|
|
81
|
+
isActive: false,
|
|
82
|
+
isDialIn: false,
|
|
83
|
+
roomName: null,
|
|
84
|
+
roomUrl: null,
|
|
85
|
+
displayName: null,
|
|
86
|
+
userAgent: `core:${coreVersion}`,
|
|
87
|
+
externalId: null,
|
|
88
|
+
};
|
|
89
|
+
const appSlice = createSlice({
|
|
90
|
+
name: "app",
|
|
91
|
+
initialState: initialState$g,
|
|
92
|
+
reducers: {
|
|
93
|
+
doAppStart: (state, action) => {
|
|
94
|
+
const url = new URL(action.payload.roomUrl);
|
|
95
|
+
return Object.assign(Object.assign(Object.assign({}, state), action.payload), { roomName: url.pathname, initialConfig: Object.assign({}, action.payload), isActive: true });
|
|
96
|
+
},
|
|
97
|
+
doAppStop: (state) => {
|
|
98
|
+
return Object.assign(Object.assign({}, state), { isActive: false });
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
const { doAppStop, doAppStart } = appSlice.actions;
|
|
103
|
+
const selectAppRaw = (state) => state.app;
|
|
104
|
+
const selectAppIsActive = (state) => state.app.isActive;
|
|
105
|
+
const selectAppIsDialIn = (state) => state.app.isDialIn;
|
|
106
|
+
const selectAppRoomName = (state) => state.app.roomName;
|
|
107
|
+
const selectAppRoomUrl = (state) => state.app.roomUrl;
|
|
108
|
+
const selectAppDisplayName = (state) => state.app.displayName;
|
|
109
|
+
const selectAppUserAgent = (state) => state.app.userAgent;
|
|
110
|
+
const selectAppExternalId = (state) => state.app.externalId;
|
|
111
|
+
const selectAppIsNodeSdk = (state) => state.app.isNodeSdk;
|
|
112
|
+
const selectAppInitialConfig = (state) => state.app.initialConfig;
|
|
113
|
+
|
|
114
|
+
function createSignalEventAction(name) {
|
|
115
|
+
return createAction(`signalConnection/event/${name}`);
|
|
116
|
+
}
|
|
117
|
+
const signalEvents = {
|
|
118
|
+
audioEnabled: createSignalEventAction("audioEnabled"),
|
|
119
|
+
audioEnableRequested: createSignalEventAction("audioEnableRequested"),
|
|
120
|
+
breakoutGroupJoined: createSignalEventAction("breakoutGroupJoined"),
|
|
121
|
+
chatMessage: createSignalEventAction("chatMessage"),
|
|
122
|
+
clientLeft: createSignalEventAction("clientLeft"),
|
|
123
|
+
clientKicked: createSignalEventAction("clientKicked"),
|
|
124
|
+
clientMetadataReceived: createSignalEventAction("clientMetadataReceived"),
|
|
125
|
+
clientUnableToJoin: createSignalEventAction("clientUnableToJoin"),
|
|
126
|
+
cloudRecordingStarted: createSignalEventAction("cloudRecordingStarted"),
|
|
127
|
+
cloudRecordingStopped: createSignalEventAction("cloudRecordingStopped"),
|
|
128
|
+
disconnect: createSignalEventAction("disconnect"),
|
|
129
|
+
knockerLeft: createSignalEventAction("knockerLeft"),
|
|
130
|
+
knockHandled: createSignalEventAction("knockHandled"),
|
|
131
|
+
newClient: createSignalEventAction("newClient"),
|
|
132
|
+
roomJoined: createSignalEventAction("roomJoined"),
|
|
133
|
+
roomKnocked: createSignalEventAction("roomKnocked"),
|
|
134
|
+
roomLeft: createSignalEventAction("roomLeft"),
|
|
135
|
+
roomLocked: createSignalEventAction("roomLocked"),
|
|
136
|
+
roomSessionEnded: createSignalEventAction("roomSessionEnded"),
|
|
137
|
+
screenshareStarted: createSignalEventAction("screenshareStarted"),
|
|
138
|
+
screenshareStopped: createSignalEventAction("screenshareStopped"),
|
|
139
|
+
spotlightAdded: createSignalEventAction("spotlightAdded"),
|
|
140
|
+
spotlightRemoved: createSignalEventAction("spotlightRemoved"),
|
|
141
|
+
streamingStopped: createSignalEventAction("streamingStopped"),
|
|
142
|
+
videoEnabled: createSignalEventAction("videoEnabled"),
|
|
143
|
+
videoEnableRequested: createSignalEventAction("videoEnableRequested"),
|
|
144
|
+
liveTranscriptionStarted: createSignalEventAction("liveTranscriptionStarted"),
|
|
145
|
+
liveTranscriptionStopped: createSignalEventAction("liveTranscriptionStopped"),
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const ROOM_ACTION_PERMISSIONS_BY_ROLE = {
|
|
149
|
+
canLockRoom: ["host"],
|
|
150
|
+
canRequestAudioEnable: ["host"],
|
|
151
|
+
canRequestVideoEnable: ["host"],
|
|
152
|
+
canKickClient: ["host"],
|
|
153
|
+
canEndMeeting: ["host"],
|
|
154
|
+
canAskToSpeak: ["host"],
|
|
155
|
+
canSpotlight: ["host"],
|
|
156
|
+
};
|
|
157
|
+
const initialState$f = {
|
|
158
|
+
roomKey: null,
|
|
159
|
+
roleName: "none",
|
|
160
|
+
};
|
|
161
|
+
const authorizationSlice = createSlice({
|
|
162
|
+
name: "authorization",
|
|
163
|
+
initialState: initialState$f,
|
|
164
|
+
reducers: {
|
|
165
|
+
setRoomKey: (state, action) => {
|
|
166
|
+
return Object.assign(Object.assign({}, state), { roomKey: action.payload });
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
extraReducers: (builder) => {
|
|
170
|
+
builder.addCase(doAppStart, (state, action) => {
|
|
171
|
+
return Object.assign(Object.assign({}, state), { roomKey: action.payload.roomKey });
|
|
172
|
+
});
|
|
173
|
+
builder.addCase(signalEvents.roomJoined, (state, action) => {
|
|
174
|
+
var _a, _b;
|
|
175
|
+
const client = (_b = (_a = action.payload) === null || _a === void 0 ? void 0 : _a.room) === null || _b === void 0 ? void 0 : _b.clients.find((c) => { var _a; return c.id === ((_a = action.payload) === null || _a === void 0 ? void 0 : _a.selfId); });
|
|
176
|
+
return Object.assign(Object.assign({}, state), { roleName: (client === null || client === void 0 ? void 0 : client.role.roleName) || "none" });
|
|
177
|
+
});
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
const { setRoomKey } = authorizationSlice.actions;
|
|
181
|
+
const selectRoomKey = (state) => state.authorization.roomKey;
|
|
182
|
+
const selectAuthorizationRoleName = (state) => state.authorization.roleName;
|
|
183
|
+
const selectIsAuthorizedToLockRoom = createSelector(selectAuthorizationRoleName, (localParticipantRole) => ROOM_ACTION_PERMISSIONS_BY_ROLE.canLockRoom.includes(localParticipantRole));
|
|
184
|
+
const selectIsAuthorizedToRequestAudioEnable = createSelector(selectAuthorizationRoleName, (localParticipantRole) => ROOM_ACTION_PERMISSIONS_BY_ROLE.canRequestAudioEnable.includes(localParticipantRole));
|
|
185
|
+
const selectIsAuthorizedToRequestVideoEnable = createSelector(selectAuthorizationRoleName, (localParticipantRole) => ROOM_ACTION_PERMISSIONS_BY_ROLE.canRequestVideoEnable.includes(localParticipantRole));
|
|
186
|
+
const selectIsAuthorizedToKickClient = createSelector(selectAuthorizationRoleName, (localParticipantRole) => ROOM_ACTION_PERMISSIONS_BY_ROLE.canKickClient.includes(localParticipantRole));
|
|
187
|
+
const selectIsAuthorizedToEndMeeting = createSelector(selectAuthorizationRoleName, (localParticipantRole) => ROOM_ACTION_PERMISSIONS_BY_ROLE.canEndMeeting.includes(localParticipantRole));
|
|
188
|
+
const selectIsAuthorizedToAskToSpeak = createSelector(selectAuthorizationRoleName, (localParticipantRole) => ROOM_ACTION_PERMISSIONS_BY_ROLE.canAskToSpeak.includes(localParticipantRole));
|
|
189
|
+
const selectIsAuthorizedToSpotlight = createSelector(selectAuthorizationRoleName, (localParticipantRole) => ROOM_ACTION_PERMISSIONS_BY_ROLE.canSpotlight.includes(localParticipantRole));
|
|
190
|
+
|
|
191
|
+
/******************************************************************************
|
|
192
|
+
Copyright (c) Microsoft Corporation.
|
|
193
|
+
|
|
194
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
195
|
+
purpose with or without fee is hereby granted.
|
|
196
|
+
|
|
197
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
198
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
199
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
200
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
201
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
202
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
203
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
204
|
+
***************************************************************************** */
|
|
205
|
+
/* global Reflect, Promise, SuppressedError, Symbol */
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
function __rest(s, e) {
|
|
209
|
+
var t = {};
|
|
210
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
211
|
+
t[p] = s[p];
|
|
212
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
213
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
214
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
215
|
+
t[p[i]] = s[p[i]];
|
|
216
|
+
}
|
|
217
|
+
return t;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
221
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
222
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
223
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
224
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
225
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
226
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
231
|
+
var e = new Error(message);
|
|
232
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const initialState$e = {
|
|
236
|
+
isFetching: false,
|
|
237
|
+
data: null,
|
|
238
|
+
};
|
|
239
|
+
const deviceCredentialsSlice = createSlice({
|
|
240
|
+
name: "deviceCredentials",
|
|
241
|
+
initialState: initialState$e,
|
|
242
|
+
reducers: {},
|
|
243
|
+
extraReducers: (builder) => {
|
|
244
|
+
builder.addCase(doGetDeviceCredentials.pending, (state) => {
|
|
245
|
+
return Object.assign(Object.assign({}, state), { isFetching: true });
|
|
246
|
+
});
|
|
247
|
+
builder.addCase(doGetDeviceCredentials.fulfilled, (state, action) => {
|
|
248
|
+
return Object.assign(Object.assign({}, state), { isFetching: false, data: action.payload });
|
|
249
|
+
});
|
|
250
|
+
builder.addCase(doGetDeviceCredentials.rejected, (state) => {
|
|
251
|
+
return Object.assign(Object.assign({}, state), { isFetching: true });
|
|
252
|
+
});
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
const doGetDeviceCredentials = createAppAsyncThunk("deviceCredentials/doGetDeviceCredentials", (payload, { extra }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
256
|
+
try {
|
|
257
|
+
const deviceCredentials = yield extra.services.credentialsService.getCredentials();
|
|
258
|
+
return deviceCredentials;
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
console.error(error);
|
|
262
|
+
}
|
|
263
|
+
}));
|
|
264
|
+
const selectDeviceCredentialsRaw = (state) => state.deviceCredentials;
|
|
265
|
+
const selectHasFetchedDeviceCredentials = (state) => { var _a; return !!((_a = state.deviceCredentials.data) === null || _a === void 0 ? void 0 : _a.credentials); };
|
|
266
|
+
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; };
|
|
267
|
+
const selectShouldFetchDeviceCredentials = createSelector(selectAppIsActive, selectDeviceCredentialsRaw, (appIsActive, deviceCredentials) => {
|
|
268
|
+
if (appIsActive && !deviceCredentials.isFetching && !deviceCredentials.data) {
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
return false;
|
|
272
|
+
});
|
|
273
|
+
createReactor([selectShouldFetchDeviceCredentials], ({ dispatch }, shouldFetchDeviceCredentials) => {
|
|
274
|
+
if (shouldFetchDeviceCredentials) {
|
|
275
|
+
dispatch(doGetDeviceCredentials());
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
function forwardSocketEvents(socket, dispatch) {
|
|
280
|
+
socket.on("room_joined", (payload) => dispatch(signalEvents.roomJoined(payload)));
|
|
281
|
+
socket.on("new_client", (payload) => dispatch(signalEvents.newClient(payload)));
|
|
282
|
+
socket.on("client_left", (payload) => dispatch(signalEvents.clientLeft(payload)));
|
|
283
|
+
socket.on("client_kicked", (payload) => dispatch(signalEvents.clientKicked(payload)));
|
|
284
|
+
socket.on("client_unable_to_join", (payload) => dispatch(signalEvents.clientUnableToJoin(payload)));
|
|
285
|
+
socket.on("audio_enabled", (payload) => dispatch(signalEvents.audioEnabled(payload)));
|
|
286
|
+
socket.on("video_enabled", (payload) => dispatch(signalEvents.videoEnabled(payload)));
|
|
287
|
+
socket.on("audio_enable_requested", (payload) => dispatch(signalEvents.audioEnableRequested(payload)));
|
|
288
|
+
socket.on("client_metadata_received", (payload) => dispatch(signalEvents.clientMetadataReceived(payload)));
|
|
289
|
+
socket.on("chat_message", (payload) => dispatch(signalEvents.chatMessage(payload)));
|
|
290
|
+
socket.on("disconnect", () => dispatch(signalEvents.disconnect()));
|
|
291
|
+
socket.on("room_knocked", (payload) => dispatch(signalEvents.roomKnocked(payload)));
|
|
292
|
+
socket.on("room_left", () => dispatch(signalEvents.roomLeft()));
|
|
293
|
+
socket.on("room_locked", (payload) => dispatch(signalEvents.roomLocked(payload)));
|
|
294
|
+
socket.on("room_session_ended", (payload) => dispatch(signalEvents.roomSessionEnded(payload)));
|
|
295
|
+
socket.on("knocker_left", (payload) => dispatch(signalEvents.knockerLeft(payload)));
|
|
296
|
+
socket.on("knock_handled", (payload) => dispatch(signalEvents.knockHandled(payload)));
|
|
297
|
+
socket.on("screenshare_started", (payload) => dispatch(signalEvents.screenshareStarted(payload)));
|
|
298
|
+
socket.on("screenshare_stopped", (payload) => dispatch(signalEvents.screenshareStopped(payload)));
|
|
299
|
+
socket.on("cloud_recording_started", (payload) => dispatch(signalEvents.cloudRecordingStarted(payload)));
|
|
300
|
+
socket.on("cloud_recording_stopped", () => dispatch(signalEvents.cloudRecordingStopped()));
|
|
301
|
+
socket.on("streaming_stopped", () => dispatch(signalEvents.streamingStopped()));
|
|
302
|
+
socket.on("spotlight_added", (payload) => dispatch(signalEvents.spotlightAdded(payload)));
|
|
303
|
+
socket.on("spotlight_removed", (payload) => dispatch(signalEvents.spotlightRemoved(payload)));
|
|
304
|
+
socket.on("live_transcription_started", (payload) => dispatch(signalEvents.liveTranscriptionStarted(payload)));
|
|
305
|
+
socket.on("live_transcription_stopped", (payload) => dispatch(signalEvents.liveTranscriptionStopped(payload)));
|
|
306
|
+
socket.on("video_enable_requested", (payload) => dispatch(signalEvents.videoEnableRequested(payload)));
|
|
307
|
+
socket.on("breakout_group_joined", (payload) => dispatch(signalEvents.breakoutGroupJoined(payload)));
|
|
308
|
+
}
|
|
309
|
+
const SIGNAL_BASE_URL = "wss://signal.appearin.net" ;
|
|
310
|
+
function createSocket() {
|
|
311
|
+
const parsedUrl = new URL(SIGNAL_BASE_URL);
|
|
312
|
+
const socketHost = parsedUrl.origin;
|
|
313
|
+
const socketOverrides = {
|
|
314
|
+
autoConnect: false,
|
|
315
|
+
};
|
|
316
|
+
return new ServerSocket(socketHost, socketOverrides);
|
|
317
|
+
}
|
|
318
|
+
const initialState$d = {
|
|
319
|
+
deviceIdentified: false,
|
|
320
|
+
isIdentifyingDevice: false,
|
|
321
|
+
status: "ready",
|
|
322
|
+
socket: null,
|
|
323
|
+
};
|
|
324
|
+
const signalConnectionSlice = createSlice({
|
|
325
|
+
name: "signalConnection",
|
|
326
|
+
initialState: initialState$d,
|
|
327
|
+
reducers: {
|
|
328
|
+
socketConnecting: (state) => {
|
|
329
|
+
return Object.assign(Object.assign({}, state), { status: "connecting" });
|
|
330
|
+
},
|
|
331
|
+
socketConnected: (state, action) => {
|
|
332
|
+
return Object.assign(Object.assign({}, state), { socket: action.payload, status: "connected" });
|
|
333
|
+
},
|
|
334
|
+
socketDisconnected: (state) => {
|
|
335
|
+
return Object.assign(Object.assign({}, state), { socket: null, deviceIdentified: false, status: "disconnected" });
|
|
336
|
+
},
|
|
337
|
+
socketReconnecting: (state) => {
|
|
338
|
+
return Object.assign(Object.assign({}, state), { status: "reconnecting" });
|
|
339
|
+
},
|
|
340
|
+
deviceIdentifying: (state) => {
|
|
341
|
+
return Object.assign(Object.assign({}, state), { isIdentifyingDevice: true });
|
|
342
|
+
},
|
|
343
|
+
deviceIdentified: (state) => {
|
|
344
|
+
return Object.assign(Object.assign({}, state), { deviceIdentified: true, isIdentifyingDevice: false });
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
extraReducers: (builder) => {
|
|
348
|
+
builder.addCase(signalEvents.disconnect, (state) => {
|
|
349
|
+
return Object.assign(Object.assign({}, state), { deviceIdentified: false, status: "disconnected" });
|
|
350
|
+
});
|
|
351
|
+
},
|
|
352
|
+
});
|
|
353
|
+
const { deviceIdentifying, deviceIdentified, socketConnected, socketConnecting, socketDisconnected, socketReconnecting, } = signalConnectionSlice.actions;
|
|
354
|
+
const doSignalConnect = createAppThunk(() => {
|
|
355
|
+
return (dispatch, getState) => {
|
|
356
|
+
if (selectSignalConnectionSocket(getState())) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
dispatch(socketConnecting());
|
|
360
|
+
const socket = createSocket();
|
|
361
|
+
socket.on("connect", () => dispatch(socketConnected(socket)));
|
|
362
|
+
socket.on("device_identified", () => dispatch(deviceIdentified()));
|
|
363
|
+
socket.getManager().on("reconnect", () => dispatch(socketReconnecting()));
|
|
364
|
+
forwardSocketEvents(socket, dispatch);
|
|
365
|
+
socket.connect();
|
|
366
|
+
};
|
|
367
|
+
});
|
|
368
|
+
const doSignalIdentifyDevice = createAppThunk(({ deviceCredentials }) => (dispatch, getState) => {
|
|
369
|
+
const state = getState();
|
|
370
|
+
const signalSocket = selectSignalConnectionSocket(state);
|
|
371
|
+
const deviceIdentified = selectSignalConnectionDeviceIdentified(state);
|
|
372
|
+
if (!signalSocket) {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
if (deviceIdentified) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
signalSocket.emit("identify_device", { deviceCredentials });
|
|
379
|
+
dispatch(deviceIdentifying());
|
|
380
|
+
});
|
|
381
|
+
const doSignalDisconnect = createAppThunk(() => (dispatch, getState) => {
|
|
382
|
+
const state = getState();
|
|
383
|
+
const signalStatus = selectSignalStatus(state);
|
|
384
|
+
if (signalStatus === "connected") {
|
|
385
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
386
|
+
socket === null || socket === void 0 ? void 0 : socket.disconnect();
|
|
387
|
+
dispatch(socketDisconnected());
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
const selectSignalConnectionRaw = (state) => state.signalConnection;
|
|
391
|
+
const selectSignalIsIdentifyingDevice = (state) => state.signalConnection.isIdentifyingDevice;
|
|
392
|
+
const selectSignalConnectionDeviceIdentified = (state) => state.signalConnection.deviceIdentified;
|
|
393
|
+
const selectSignalStatus = (state) => state.signalConnection.status;
|
|
394
|
+
const selectSignalConnectionSocket = (state) => state.signalConnection.socket;
|
|
395
|
+
const selectShouldConnectSignal = createSelector(selectAppIsActive, selectSignalStatus, (appIsActive, signalStatus) => {
|
|
396
|
+
if (appIsActive && ["ready", "reconnecting"].includes(signalStatus)) {
|
|
397
|
+
return true;
|
|
398
|
+
}
|
|
399
|
+
return false;
|
|
400
|
+
});
|
|
401
|
+
createReactor([selectShouldConnectSignal], ({ dispatch }, shouldConnectSignal) => {
|
|
402
|
+
if (shouldConnectSignal) {
|
|
403
|
+
dispatch(doSignalConnect());
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
const selectShouldIdentifyDevice = createSelector(selectDeviceCredentialsRaw, selectSignalStatus, selectSignalConnectionDeviceIdentified, selectSignalIsIdentifyingDevice, (deviceCredentialsRaw, signalStatus, deviceIdentified, isIdentifyingDevice) => {
|
|
407
|
+
if (deviceCredentialsRaw.data && signalStatus === "connected" && !deviceIdentified && !isIdentifyingDevice) {
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
return false;
|
|
411
|
+
});
|
|
412
|
+
createReactor([selectShouldIdentifyDevice, selectDeviceCredentialsRaw], ({ dispatch }, shouldIdentifyDevice, deviceCredentialsRaw) => {
|
|
413
|
+
if (shouldIdentifyDevice && deviceCredentialsRaw.data) {
|
|
414
|
+
dispatch(doSignalIdentifyDevice({ deviceCredentials: deviceCredentialsRaw.data }));
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
startAppListening({
|
|
418
|
+
actionCreator: signalEvents.roomLeft,
|
|
419
|
+
effect: (_, { dispatch }) => {
|
|
420
|
+
dispatch(doSignalDisconnect());
|
|
421
|
+
},
|
|
422
|
+
});
|
|
423
|
+
startAppListening({
|
|
424
|
+
actionCreator: signalEvents.clientKicked,
|
|
425
|
+
effect: (_, { dispatch }) => {
|
|
426
|
+
dispatch(doSignalDisconnect());
|
|
427
|
+
},
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
const initialState$c = {
|
|
431
|
+
chatMessages: [],
|
|
432
|
+
};
|
|
433
|
+
const chatSlice = createSlice({
|
|
434
|
+
name: "chat",
|
|
435
|
+
initialState: initialState$c,
|
|
436
|
+
reducers: {},
|
|
437
|
+
extraReducers(builder) {
|
|
438
|
+
builder.addCase(signalEvents.chatMessage, (state, action) => {
|
|
439
|
+
const message = {
|
|
440
|
+
senderId: action.payload.senderId,
|
|
441
|
+
timestamp: action.payload.timestamp,
|
|
442
|
+
text: action.payload.text,
|
|
443
|
+
};
|
|
444
|
+
return Object.assign(Object.assign({}, state), { chatMessages: [...state.chatMessages, message] });
|
|
445
|
+
});
|
|
446
|
+
},
|
|
447
|
+
});
|
|
448
|
+
const doSendChatMessage = createRoomConnectedThunk((payload) => (_, getState) => {
|
|
449
|
+
const state = getState();
|
|
450
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
451
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("chat_message", { text: payload.text });
|
|
452
|
+
});
|
|
453
|
+
const selectChatRaw = (state) => state.chat;
|
|
454
|
+
const selectChatMessages = (state) => state.chat.chatMessages;
|
|
455
|
+
|
|
456
|
+
const initialCloudRecordingState = {
|
|
457
|
+
isRecording: false,
|
|
458
|
+
error: null,
|
|
459
|
+
startedAt: undefined,
|
|
460
|
+
};
|
|
461
|
+
const cloudRecordingSlice = createSlice({
|
|
462
|
+
name: "cloudRecording",
|
|
463
|
+
initialState: initialCloudRecordingState,
|
|
464
|
+
reducers: {
|
|
465
|
+
recordingRequestStarted: (state) => {
|
|
466
|
+
return Object.assign(Object.assign({}, state), { status: "requested" });
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
extraReducers: (builder) => {
|
|
470
|
+
builder.addCase(signalEvents.cloudRecordingStopped, (state) => {
|
|
471
|
+
return Object.assign(Object.assign({}, state), { isRecording: false, status: undefined });
|
|
472
|
+
});
|
|
473
|
+
builder.addCase(signalEvents.cloudRecordingStarted, (state, action) => {
|
|
474
|
+
const { payload } = action;
|
|
475
|
+
if (!payload.error) {
|
|
476
|
+
return state;
|
|
477
|
+
}
|
|
478
|
+
return Object.assign(Object.assign({}, state), { isRecording: false, status: "error", error: payload.error });
|
|
479
|
+
});
|
|
480
|
+
builder.addCase(signalEvents.newClient, (state, { payload }) => {
|
|
481
|
+
var _a;
|
|
482
|
+
const { client } = payload;
|
|
483
|
+
if (((_a = client.role) === null || _a === void 0 ? void 0 : _a.roleName) === "recorder") {
|
|
484
|
+
return Object.assign(Object.assign({}, state), { isRecording: true, status: "recording", startedAt: client.startedCloudRecordingAt
|
|
485
|
+
? new Date(client.startedCloudRecordingAt).getTime()
|
|
486
|
+
: new Date().getTime() });
|
|
487
|
+
}
|
|
488
|
+
return state;
|
|
489
|
+
});
|
|
490
|
+
},
|
|
491
|
+
});
|
|
492
|
+
const { recordingRequestStarted } = cloudRecordingSlice.actions;
|
|
493
|
+
const doStartCloudRecording = createRoomConnectedThunk(() => (dispatch, getState) => {
|
|
494
|
+
const state = getState();
|
|
495
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
496
|
+
const status = selectCloudRecordingStatus(state);
|
|
497
|
+
if (status && ["recording", "requested"].includes(status)) {
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("start_recording", {
|
|
501
|
+
recording: "cloud",
|
|
502
|
+
});
|
|
503
|
+
dispatch(recordingRequestStarted());
|
|
504
|
+
});
|
|
505
|
+
const doStopCloudRecording = createRoomConnectedThunk(() => (dispatch, getState) => {
|
|
506
|
+
const state = getState();
|
|
507
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
508
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("stop_recording");
|
|
509
|
+
});
|
|
510
|
+
const selectCloudRecordingRaw = (state) => state.cloudRecording;
|
|
511
|
+
const selectCloudRecordingStatus = (state) => state.cloudRecording.status;
|
|
512
|
+
const selectCloudRecordingStartedAt = (state) => state.cloudRecording.startedAt;
|
|
513
|
+
const selectCloudRecordingError = (state) => state.cloudRecording.error;
|
|
514
|
+
const selectIsCloudRecording = (state) => state.cloudRecording.isRecording;
|
|
515
|
+
|
|
516
|
+
const initialState$b = {
|
|
517
|
+
data: null,
|
|
518
|
+
isFetching: false,
|
|
519
|
+
error: null,
|
|
520
|
+
};
|
|
521
|
+
const organizationSlice = createSlice({
|
|
522
|
+
initialState: initialState$b,
|
|
523
|
+
name: "organization",
|
|
524
|
+
reducers: {},
|
|
525
|
+
extraReducers: (builder) => {
|
|
526
|
+
builder.addCase(doOrganizationFetch.pending, (state) => {
|
|
527
|
+
return Object.assign(Object.assign({}, state), { isFetching: true });
|
|
528
|
+
});
|
|
529
|
+
builder.addCase(doOrganizationFetch.fulfilled, (state, action) => {
|
|
530
|
+
if (!action.payload)
|
|
531
|
+
return Object.assign(Object.assign({}, state), { isFetching: true });
|
|
532
|
+
return Object.assign(Object.assign({}, state), { isFetching: false, data: action.payload });
|
|
533
|
+
});
|
|
534
|
+
builder.addCase(doOrganizationFetch.rejected, (state) => {
|
|
535
|
+
return Object.assign(Object.assign({}, state), { isFetching: false, error: true });
|
|
536
|
+
});
|
|
537
|
+
},
|
|
538
|
+
});
|
|
539
|
+
const doOrganizationFetch = createAppAsyncThunk("organization/doOrganizationFetch", (_, { extra, getState }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
540
|
+
try {
|
|
541
|
+
const roomUrl = selectAppRoomUrl(getState());
|
|
542
|
+
const organization = yield extra.services.fetchOrganizationFromRoomUrl(roomUrl || "");
|
|
543
|
+
if (!organization) {
|
|
544
|
+
throw new Error("Invalid room url");
|
|
545
|
+
}
|
|
546
|
+
return organization;
|
|
547
|
+
}
|
|
548
|
+
catch (error) {
|
|
549
|
+
console.error(error);
|
|
550
|
+
}
|
|
551
|
+
}));
|
|
552
|
+
const selectOrganizationRaw = (state) => state.organization;
|
|
553
|
+
const selectOrganizationId = (state) => { var _a; return (_a = state.organization.data) === null || _a === void 0 ? void 0 : _a.organizationId; };
|
|
554
|
+
const selectShouldFetchOrganization = createSelector(selectAppIsActive, selectOrganizationRaw, selectDeviceCredentialsRaw, (appIsActive, organization, deviceCredentials) => {
|
|
555
|
+
if (appIsActive &&
|
|
556
|
+
!organization.data &&
|
|
557
|
+
!organization.isFetching &&
|
|
558
|
+
!organization.error &&
|
|
559
|
+
!deviceCredentials.isFetching) {
|
|
560
|
+
return true;
|
|
561
|
+
}
|
|
562
|
+
return false;
|
|
563
|
+
});
|
|
564
|
+
createReactor([selectShouldFetchOrganization], ({ dispatch }, shouldFetchOrganization) => {
|
|
565
|
+
if (shouldFetchOrganization) {
|
|
566
|
+
dispatch(doOrganizationFetch());
|
|
567
|
+
}
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
function fakeAudioStream() {
|
|
571
|
+
const audioCtx = new AudioContext();
|
|
572
|
+
const oscillator = audioCtx.createOscillator();
|
|
573
|
+
const destination = audioCtx.createMediaStreamDestination();
|
|
574
|
+
oscillator.connect(destination);
|
|
575
|
+
oscillator.start();
|
|
576
|
+
return destination.stream;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
let rotationAngle = 0;
|
|
580
|
+
function drawWebcamFrame(canvas) {
|
|
581
|
+
const context = canvas.getContext("2d");
|
|
582
|
+
if (!context) {
|
|
583
|
+
console.error("Canvas context not available");
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
const wheelRadius = 100;
|
|
587
|
+
const wheelCenterX = canvas.width / 2;
|
|
588
|
+
const wheelCenterY = canvas.height / 2;
|
|
589
|
+
context.fillStyle = "darkgreen";
|
|
590
|
+
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
591
|
+
context.save();
|
|
592
|
+
context.translate(wheelCenterX, wheelCenterY);
|
|
593
|
+
context.rotate(rotationAngle);
|
|
594
|
+
const numSlices = 12;
|
|
595
|
+
const sliceAngle = (2 * Math.PI) / numSlices;
|
|
596
|
+
const colors = ["red", "orange", "yellow", "green", "blue", "purple"];
|
|
597
|
+
for (let i = 0; i < numSlices; i++) {
|
|
598
|
+
context.beginPath();
|
|
599
|
+
context.moveTo(0, 0);
|
|
600
|
+
context.arc(0, 0, wheelRadius, i * sliceAngle, (i + 1) * sliceAngle);
|
|
601
|
+
context.fillStyle = colors[i % colors.length];
|
|
602
|
+
context.fill();
|
|
603
|
+
context.closePath();
|
|
604
|
+
}
|
|
605
|
+
context.restore();
|
|
606
|
+
context.fillStyle = "white";
|
|
607
|
+
context.font = "42px Arial";
|
|
608
|
+
const topText = "Whereby Media Stream";
|
|
609
|
+
const topTextWidth = context.measureText(topText).width;
|
|
610
|
+
context.fillText(topText, canvas.width / 2 - topTextWidth / 2, 50);
|
|
611
|
+
context.font = "32px Arial";
|
|
612
|
+
const now = new Date();
|
|
613
|
+
const timeText = `time: ${now.getHours().toString().padStart(2, "0")}:${now
|
|
614
|
+
.getMinutes()
|
|
615
|
+
.toString()
|
|
616
|
+
.padStart(2, "0")}:${now.getSeconds().toString().padStart(2, "0")}.${now
|
|
617
|
+
.getMilliseconds()
|
|
618
|
+
.toString()
|
|
619
|
+
.padStart(3, "0")}`;
|
|
620
|
+
context.fillText(timeText, 10, canvas.height - 20);
|
|
621
|
+
context.fillText(`rotation angle: ${rotationAngle.toFixed(2)}`, canvas.width - canvas.width / 2, canvas.height - 20);
|
|
622
|
+
rotationAngle += 0.01;
|
|
623
|
+
}
|
|
624
|
+
function fakeWebcamFrame(canvas) {
|
|
625
|
+
drawWebcamFrame(canvas);
|
|
626
|
+
setInterval(() => drawWebcamFrame(canvas), 50);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
const CANVAS_VIDEO_FPS = 24;
|
|
630
|
+
function getAudioTrack() {
|
|
631
|
+
const audioStream = fakeAudioStream();
|
|
632
|
+
return audioStream.getAudioTracks()[0];
|
|
633
|
+
}
|
|
634
|
+
function getVideoTrack({ canvas }) {
|
|
635
|
+
fakeWebcamFrame(canvas);
|
|
636
|
+
const videoStream = canvas.captureStream(CANVAS_VIDEO_FPS);
|
|
637
|
+
return videoStream.getVideoTracks()[0];
|
|
638
|
+
}
|
|
639
|
+
function getFakeMediaStream({ canvas, hasAudio }) {
|
|
640
|
+
const tracks = [getVideoTrack({ canvas }), ...(hasAudio ? [getAudioTrack()] : [])];
|
|
641
|
+
return new MediaStream(tracks);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
function debounce(fn, { delay = 500, edges } = {}) {
|
|
645
|
+
let timeout;
|
|
646
|
+
let nCalls = 0;
|
|
647
|
+
return (...args) => {
|
|
648
|
+
nCalls += 1;
|
|
649
|
+
if (edges && nCalls === 1) {
|
|
650
|
+
fn(...args);
|
|
651
|
+
}
|
|
652
|
+
clearTimeout(timeout);
|
|
653
|
+
timeout = setTimeout(() => {
|
|
654
|
+
if (!edges || nCalls > 1) {
|
|
655
|
+
fn(...args);
|
|
656
|
+
}
|
|
657
|
+
timeout = undefined;
|
|
658
|
+
nCalls = 0;
|
|
659
|
+
}, delay);
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
function parseRoomUrlAndSubdomain(roomAttribute, subdomainAttribute) {
|
|
664
|
+
if (!roomAttribute) {
|
|
665
|
+
throw new Error("Missing room attribute");
|
|
666
|
+
}
|
|
667
|
+
const m = /https:\/\/([^.]+)(\.whereby\.com|-ip-\d+-\d+-\d+-\d+.hereby.dev:4443)\/.+/.exec(roomAttribute);
|
|
668
|
+
const subdomain = (m && m[1]) || subdomainAttribute;
|
|
669
|
+
if (!subdomain) {
|
|
670
|
+
throw new Error("Missing subdomain attribute");
|
|
671
|
+
}
|
|
672
|
+
if (!m) {
|
|
673
|
+
throw new Error("Could not parse room URL");
|
|
674
|
+
}
|
|
675
|
+
const roomUrl = new URL(roomAttribute);
|
|
676
|
+
return {
|
|
677
|
+
subdomain,
|
|
678
|
+
roomUrl,
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
function parseUnverifiedRoomKeyData(roomKey) {
|
|
683
|
+
const [, roomKeyData] = /\.(.*)\./i.exec(roomKey) || [];
|
|
684
|
+
if (!roomKeyData) {
|
|
685
|
+
return {};
|
|
686
|
+
}
|
|
687
|
+
else {
|
|
688
|
+
try {
|
|
689
|
+
const base64DecodedJwtData = atob(roomKeyData);
|
|
690
|
+
return JSON.parse(base64DecodedJwtData);
|
|
691
|
+
}
|
|
692
|
+
catch (e) {
|
|
693
|
+
return {};
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const initialLocalMediaState = {
|
|
699
|
+
busyDeviceIds: [],
|
|
700
|
+
cameraEnabled: false,
|
|
701
|
+
currentSpeakerDeviceId: "default",
|
|
702
|
+
devices: [],
|
|
703
|
+
isSettingCameraDevice: false,
|
|
704
|
+
isSettingMicrophoneDevice: false,
|
|
705
|
+
isSettingSpeakerDevice: false,
|
|
706
|
+
isTogglingCamera: false,
|
|
707
|
+
lowDataMode: false,
|
|
708
|
+
microphoneEnabled: false,
|
|
709
|
+
status: "inactive",
|
|
710
|
+
stream: undefined,
|
|
711
|
+
isSwitchingStream: false,
|
|
712
|
+
};
|
|
713
|
+
const localMediaSlice = createSlice({
|
|
714
|
+
name: "localMedia",
|
|
715
|
+
initialState: initialLocalMediaState,
|
|
716
|
+
reducers: {
|
|
717
|
+
deviceBusy(state, action) {
|
|
718
|
+
if (state.busyDeviceIds.includes(action.payload.deviceId)) {
|
|
719
|
+
return state;
|
|
720
|
+
}
|
|
721
|
+
return Object.assign(Object.assign({}, state), { busyDeviceIds: [...state.busyDeviceIds, action.payload.deviceId] });
|
|
722
|
+
},
|
|
723
|
+
toggleCameraEnabled(state, action) {
|
|
724
|
+
var _a;
|
|
725
|
+
return Object.assign(Object.assign({}, state), { cameraEnabled: (_a = action.payload.enabled) !== null && _a !== void 0 ? _a : !state.cameraEnabled });
|
|
726
|
+
},
|
|
727
|
+
setCurrentCameraDeviceId(state, action) {
|
|
728
|
+
return Object.assign(Object.assign({}, state), { currentCameraDeviceId: action.payload.deviceId });
|
|
729
|
+
},
|
|
730
|
+
toggleMicrophoneEnabled(state, action) {
|
|
731
|
+
var _a;
|
|
732
|
+
return Object.assign(Object.assign({}, state), { microphoneEnabled: (_a = action.payload.enabled) !== null && _a !== void 0 ? _a : !state.microphoneEnabled });
|
|
733
|
+
},
|
|
734
|
+
setCurrentMicrophoneDeviceId(state, action) {
|
|
735
|
+
return Object.assign(Object.assign({}, state), { currentMicrophoneDeviceId: action.payload.deviceId });
|
|
736
|
+
},
|
|
737
|
+
setCurrentSpeakerDeviceId(state, action) {
|
|
738
|
+
var _a;
|
|
739
|
+
return Object.assign(Object.assign({}, state), { currentSpeakerDeviceId: (_a = action.payload.deviceId) !== null && _a !== void 0 ? _a : "default" });
|
|
740
|
+
},
|
|
741
|
+
toggleLowDataModeEnabled(state, action) {
|
|
742
|
+
var _a;
|
|
743
|
+
return Object.assign(Object.assign({}, state), { lowDataMode: (_a = action.payload.enabled) !== null && _a !== void 0 ? _a : !state.lowDataMode });
|
|
744
|
+
},
|
|
745
|
+
setDevices(state, action) {
|
|
746
|
+
return Object.assign(Object.assign({}, state), { devices: action.payload.devices });
|
|
747
|
+
},
|
|
748
|
+
setLocalMediaStream(state, action) {
|
|
749
|
+
return Object.assign(Object.assign({}, state), { stream: action.payload.stream });
|
|
750
|
+
},
|
|
751
|
+
setLocalMediaOptions(state, action) {
|
|
752
|
+
return Object.assign(Object.assign({}, state), { options: action.payload.options });
|
|
753
|
+
},
|
|
754
|
+
localMediaStopped(state) {
|
|
755
|
+
return Object.assign(Object.assign({}, state), { status: "stopped", stream: undefined });
|
|
756
|
+
},
|
|
757
|
+
localStreamMetadataUpdated(state, action) {
|
|
758
|
+
const { audio, video } = action.payload;
|
|
759
|
+
return Object.assign(Object.assign({}, state), { currentCameraDeviceId: video.deviceId, currentMicrophoneDeviceId: audio.deviceId, busyDeviceIds: state.busyDeviceIds.filter((id) => id !== audio.deviceId && id !== video.deviceId) });
|
|
760
|
+
},
|
|
761
|
+
},
|
|
762
|
+
extraReducers: (builder) => {
|
|
763
|
+
builder.addCase(doAppStart, (state, action) => {
|
|
764
|
+
return Object.assign(Object.assign({}, state), { options: action.payload.localMediaOptions });
|
|
765
|
+
});
|
|
766
|
+
builder.addCase(doSetDevice.pending, (state, action) => {
|
|
767
|
+
const { audio, video } = action.meta.arg;
|
|
768
|
+
return Object.assign(Object.assign({}, state), { isSettingCameraDevice: video, isSettingMicrophoneDevice: audio });
|
|
769
|
+
});
|
|
770
|
+
builder.addCase(doSetDevice.fulfilled, (state, action) => {
|
|
771
|
+
const { audio, video } = action.meta.arg;
|
|
772
|
+
return Object.assign(Object.assign({}, state), { isSettingCameraDevice: video ? false : state.isSettingCameraDevice, isSettingMicrophoneDevice: audio ? false : state.isSettingMicrophoneDevice });
|
|
773
|
+
});
|
|
774
|
+
builder.addCase(doSetDevice.rejected, (state, action) => {
|
|
775
|
+
const { audio, video } = action.meta.arg;
|
|
776
|
+
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 });
|
|
777
|
+
});
|
|
778
|
+
builder.addCase(doToggleCamera.pending, (state) => {
|
|
779
|
+
return Object.assign(Object.assign({}, state), { isTogglingCamera: true });
|
|
780
|
+
});
|
|
781
|
+
builder.addCase(doToggleCamera.fulfilled, (state) => {
|
|
782
|
+
return Object.assign(Object.assign({}, state), { isTogglingCamera: false });
|
|
783
|
+
});
|
|
784
|
+
builder.addCase(doUpdateDeviceList.fulfilled, (state, action) => {
|
|
785
|
+
return Object.assign(Object.assign({}, state), { devices: action.payload.devices });
|
|
786
|
+
});
|
|
787
|
+
builder.addCase(doStartLocalMedia.pending, (state) => {
|
|
788
|
+
return Object.assign(Object.assign({}, state), { status: "starting" });
|
|
789
|
+
});
|
|
790
|
+
builder.addCase(doStartLocalMedia.fulfilled, (state, { payload: { stream, onDeviceChange } }) => {
|
|
791
|
+
let cameraDeviceId = undefined;
|
|
792
|
+
let cameraEnabled = false;
|
|
793
|
+
let microphoneDeviceId = undefined;
|
|
794
|
+
let microphoneEnabled = false;
|
|
795
|
+
const audioTrack = stream.getAudioTracks()[0];
|
|
796
|
+
const videoTrack = stream.getVideoTracks()[0];
|
|
797
|
+
if (audioTrack) {
|
|
798
|
+
microphoneDeviceId = audioTrack.getSettings().deviceId;
|
|
799
|
+
microphoneEnabled = audioTrack.enabled;
|
|
800
|
+
}
|
|
801
|
+
if (videoTrack) {
|
|
802
|
+
cameraEnabled = videoTrack.enabled;
|
|
803
|
+
cameraDeviceId = videoTrack.getSettings().deviceId;
|
|
804
|
+
}
|
|
805
|
+
return Object.assign(Object.assign({}, state), { stream, status: "started", currentCameraDeviceId: cameraDeviceId, currentMicrophoneDeviceId: microphoneDeviceId, cameraEnabled,
|
|
806
|
+
microphoneEnabled,
|
|
807
|
+
onDeviceChange });
|
|
808
|
+
});
|
|
809
|
+
builder.addCase(doStartLocalMedia.rejected, (state, action) => {
|
|
810
|
+
return Object.assign(Object.assign({}, state), { status: "error", startError: action.error });
|
|
811
|
+
});
|
|
812
|
+
builder.addCase(doSwitchLocalStream.pending, (state) => {
|
|
813
|
+
return Object.assign(Object.assign({}, state), { isSwitchingStream: true });
|
|
814
|
+
});
|
|
815
|
+
builder.addCase(doSwitchLocalStream.fulfilled, (state) => {
|
|
816
|
+
return Object.assign(Object.assign({}, state), { isSwitchingStream: false });
|
|
817
|
+
});
|
|
818
|
+
builder.addCase(doSwitchLocalStream.rejected, (state) => {
|
|
819
|
+
return Object.assign(Object.assign({}, state), { isSwitchingStream: false });
|
|
820
|
+
});
|
|
821
|
+
},
|
|
822
|
+
});
|
|
823
|
+
const { deviceBusy, setCurrentCameraDeviceId, setCurrentMicrophoneDeviceId, setCurrentSpeakerDeviceId, toggleCameraEnabled, toggleMicrophoneEnabled, toggleLowDataModeEnabled, setLocalMediaOptions, setLocalMediaStream, localMediaStopped, localStreamMetadataUpdated, } = localMediaSlice.actions;
|
|
824
|
+
const doToggleCamera = createAppAsyncThunk("localMedia/doToggleCamera", (_, { getState, rejectWithValue }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
825
|
+
const state = getState();
|
|
826
|
+
const stream = selectLocalMediaStream(state);
|
|
827
|
+
if (!stream) {
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
let track = stream.getVideoTracks()[0];
|
|
831
|
+
const enabled = selectIsCameraEnabled(state);
|
|
832
|
+
try {
|
|
833
|
+
if (enabled) {
|
|
834
|
+
if (track) {
|
|
835
|
+
track.enabled = true;
|
|
836
|
+
}
|
|
837
|
+
else {
|
|
838
|
+
const constraintsOptions = selectLocalMediaConstraintsOptions(state);
|
|
839
|
+
const cameraDeviceId = selectCurrentCameraDeviceId(state);
|
|
840
|
+
yield getStream(Object.assign(Object.assign({}, constraintsOptions), { audioId: false, videoId: cameraDeviceId, type: "exact" }), { replaceStream: stream });
|
|
841
|
+
track = stream.getVideoTracks()[0];
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
else {
|
|
845
|
+
if (!track) {
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
track.enabled = false;
|
|
849
|
+
track.stop();
|
|
850
|
+
stream.removeTrack(track);
|
|
851
|
+
}
|
|
852
|
+
stream.dispatchEvent(new CustomEvent("stopresumevideo", { detail: { track, enable: enabled } }));
|
|
853
|
+
}
|
|
854
|
+
catch (error) {
|
|
855
|
+
return rejectWithValue(error);
|
|
856
|
+
}
|
|
857
|
+
}));
|
|
858
|
+
const doToggleMicrophone = createAppAsyncThunk("localMedia/doToggleMicrophone", (_, { getState }) => {
|
|
859
|
+
var _a;
|
|
860
|
+
const state = getState();
|
|
861
|
+
const stream = selectLocalMediaStream(state);
|
|
862
|
+
if (!stream) {
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
const enabled = selectIsMicrophoneEnabled(state);
|
|
866
|
+
const audioTrack = (_a = stream.getAudioTracks()) === null || _a === void 0 ? void 0 : _a[0];
|
|
867
|
+
if (!audioTrack) {
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
audioTrack.enabled = enabled;
|
|
871
|
+
});
|
|
872
|
+
const doToggleLowDataMode = createAppThunk(() => (dispatch, getState) => {
|
|
873
|
+
const state = getState();
|
|
874
|
+
const stream = selectLocalMediaStream(state);
|
|
875
|
+
if (!stream) {
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
const videoId = selectCurrentCameraDeviceId(state);
|
|
879
|
+
const audioId = selectCurrentMicrophoneDeviceId(state);
|
|
880
|
+
dispatch(doSwitchLocalStream({ audioId, videoId }));
|
|
881
|
+
});
|
|
882
|
+
const doSetDevice = createAppAsyncThunk("localMedia/reactSetDevice", ({ audio, video }, { getState, rejectWithValue }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
883
|
+
try {
|
|
884
|
+
const state = getState();
|
|
885
|
+
const stream = selectLocalMediaStream(state);
|
|
886
|
+
if (!stream) {
|
|
887
|
+
throw new Error("No stream");
|
|
888
|
+
}
|
|
889
|
+
const audioId = audio ? selectCurrentMicrophoneDeviceId(state) : false;
|
|
890
|
+
const videoId = video ? selectCurrentCameraDeviceId(state) : false;
|
|
891
|
+
const constraintsOptions = selectLocalMediaConstraintsOptions(state);
|
|
892
|
+
const { replacedTracks } = yield getStream(Object.assign(Object.assign({}, constraintsOptions), { audioId,
|
|
893
|
+
videoId, type: "exact" }), { replaceStream: stream });
|
|
894
|
+
const isAudioEnabled = selectIsMicrophoneEnabled(state);
|
|
895
|
+
stream.getAudioTracks().forEach((track) => (track.enabled = isAudioEnabled));
|
|
896
|
+
const isVideoEnabled = selectIsCameraEnabled(state);
|
|
897
|
+
stream.getVideoTracks().forEach((track) => (track.enabled = isVideoEnabled));
|
|
898
|
+
return { replacedTracks };
|
|
899
|
+
}
|
|
900
|
+
catch (error) {
|
|
901
|
+
return rejectWithValue(error);
|
|
902
|
+
}
|
|
903
|
+
}));
|
|
904
|
+
const doUpdateDeviceList = createAppAsyncThunk("localMedia/doUpdateDeviceList", (_, { getState, dispatch, rejectWithValue }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
905
|
+
var _a, _b, _c, _d;
|
|
906
|
+
const state = getState();
|
|
907
|
+
let newDevices = [];
|
|
908
|
+
let oldDevices = [];
|
|
909
|
+
const stream = selectLocalMediaStream(state);
|
|
910
|
+
try {
|
|
911
|
+
newDevices = yield navigator.mediaDevices.enumerateDevices();
|
|
912
|
+
oldDevices = selectLocalMediaDevices(state);
|
|
913
|
+
const shouldHandleDeviceUpdate = stream &&
|
|
914
|
+
!selectLocalMediaIsSwitchingStream(state) &&
|
|
915
|
+
newDevices &&
|
|
916
|
+
oldDevices &&
|
|
917
|
+
oldDevices.find((d) => d.deviceId);
|
|
918
|
+
if (!shouldHandleDeviceUpdate) {
|
|
919
|
+
return { devices: newDevices };
|
|
920
|
+
}
|
|
921
|
+
const { changedDevices, addedDevices } = getUpdatedDevices({
|
|
922
|
+
oldDevices,
|
|
923
|
+
newDevices,
|
|
924
|
+
});
|
|
925
|
+
let autoSwitchAudioId = (_a = changedDevices.audioinput) === null || _a === void 0 ? void 0 : _a.deviceId;
|
|
926
|
+
let autoSwitchVideoId = (_b = changedDevices.videoinput) === null || _b === void 0 ? void 0 : _b.deviceId;
|
|
927
|
+
if (autoSwitchAudioId === undefined) {
|
|
928
|
+
autoSwitchAudioId = (_c = addedDevices.audioinput) === null || _c === void 0 ? void 0 : _c.deviceId;
|
|
929
|
+
}
|
|
930
|
+
if (autoSwitchVideoId === undefined) {
|
|
931
|
+
autoSwitchVideoId = (_d = addedDevices.videoinput) === null || _d === void 0 ? void 0 : _d.deviceId;
|
|
932
|
+
}
|
|
933
|
+
if (autoSwitchAudioId !== undefined || autoSwitchVideoId !== undefined) {
|
|
934
|
+
dispatch(doSwitchLocalStream({ audioId: autoSwitchAudioId, videoId: autoSwitchVideoId }));
|
|
935
|
+
}
|
|
936
|
+
return { devices: newDevices };
|
|
937
|
+
}
|
|
938
|
+
catch (error) {
|
|
939
|
+
return rejectWithValue(error);
|
|
940
|
+
}
|
|
941
|
+
}));
|
|
942
|
+
const doSwitchLocalStream = createAppAsyncThunk("localMedia/doSwitchLocalStream", ({ audioId, videoId }, { dispatch, getState, rejectWithValue }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
943
|
+
const state = getState();
|
|
944
|
+
const replaceStream = selectLocalMediaStream(state);
|
|
945
|
+
const constraintsOptions = selectLocalMediaConstraintsOptions(state);
|
|
946
|
+
const onlySwitchingOne = !!(videoId && !audioId) || !!(!videoId && audioId);
|
|
947
|
+
if (!replaceStream) {
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
try {
|
|
951
|
+
const { replacedTracks } = yield getStream(Object.assign(Object.assign({}, constraintsOptions), { audioId: audioId === undefined ? false : audioId, videoId: videoId === undefined ? false : videoId, type: "exact" }), { replaceStream });
|
|
952
|
+
const deviceId = audioId || videoId;
|
|
953
|
+
if (onlySwitchingOne && deviceId) {
|
|
954
|
+
dispatch(deviceBusy({
|
|
955
|
+
deviceId,
|
|
956
|
+
}));
|
|
957
|
+
}
|
|
958
|
+
return { replacedTracks };
|
|
959
|
+
}
|
|
960
|
+
catch (error) {
|
|
961
|
+
console.error(error);
|
|
962
|
+
const deviceId = audioId || videoId;
|
|
963
|
+
if (onlySwitchingOne && deviceId) {
|
|
964
|
+
dispatch(deviceBusy({
|
|
965
|
+
deviceId,
|
|
966
|
+
}));
|
|
967
|
+
}
|
|
968
|
+
return rejectWithValue(error);
|
|
969
|
+
}
|
|
970
|
+
}));
|
|
971
|
+
const doStartLocalMedia = createAppAsyncThunk("localMedia/doStartLocalMedia", (payload, { getState, dispatch, rejectWithValue }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
972
|
+
const onDeviceChange = debounce(() => {
|
|
973
|
+
dispatch(doUpdateDeviceList());
|
|
974
|
+
}, { delay: 500 });
|
|
975
|
+
if (navigator.mediaDevices) {
|
|
976
|
+
navigator.mediaDevices.addEventListener("devicechange", onDeviceChange);
|
|
977
|
+
}
|
|
978
|
+
if ("getTracks" in payload) {
|
|
979
|
+
return Promise.resolve({ stream: payload, onDeviceChange });
|
|
980
|
+
}
|
|
981
|
+
if (!(payload.audio || payload.video)) {
|
|
982
|
+
return { stream: new MediaStream(), onDeviceChange };
|
|
983
|
+
}
|
|
984
|
+
else {
|
|
985
|
+
dispatch(setLocalMediaOptions({ options: payload }));
|
|
986
|
+
}
|
|
987
|
+
try {
|
|
988
|
+
yield dispatch(doUpdateDeviceList());
|
|
989
|
+
const state = getState();
|
|
990
|
+
const constraintsOptions = selectLocalMediaConstraintsOptions(state);
|
|
991
|
+
const { stream } = yield getStream(Object.assign(Object.assign({}, constraintsOptions), { audioId: payload.audio, videoId: payload.video }));
|
|
992
|
+
return { stream, onDeviceChange };
|
|
993
|
+
}
|
|
994
|
+
catch (error) {
|
|
995
|
+
return rejectWithValue(error);
|
|
996
|
+
}
|
|
997
|
+
}));
|
|
998
|
+
const doStopLocalMedia = createAppThunk(() => (dispatch, getState) => {
|
|
999
|
+
const stream = selectLocalMediaStream(getState());
|
|
1000
|
+
const onDeviceChange = selectLocalMediaRaw(getState()).onDeviceChange;
|
|
1001
|
+
stream === null || stream === void 0 ? void 0 : stream.getTracks().forEach((track) => {
|
|
1002
|
+
track.stop();
|
|
1003
|
+
});
|
|
1004
|
+
if (navigator.mediaDevices && onDeviceChange) {
|
|
1005
|
+
navigator.mediaDevices.removeEventListener("devicechange", onDeviceChange);
|
|
1006
|
+
}
|
|
1007
|
+
dispatch(localMediaStopped());
|
|
1008
|
+
});
|
|
1009
|
+
const selectBusyDeviceIds = (state) => state.localMedia.busyDeviceIds;
|
|
1010
|
+
const selectCameraDeviceError = (state) => state.localMedia.cameraDeviceError;
|
|
1011
|
+
const selectCurrentCameraDeviceId = (state) => state.localMedia.currentCameraDeviceId;
|
|
1012
|
+
const selectCurrentMicrophoneDeviceId = (state) => state.localMedia.currentMicrophoneDeviceId;
|
|
1013
|
+
const selectCurrentSpeakerDeviceId = (state) => state.localMedia.currentSpeakerDeviceId;
|
|
1014
|
+
const selectIsCameraEnabled = (state) => state.localMedia.cameraEnabled;
|
|
1015
|
+
const selectIsMicrophoneEnabled = (state) => state.localMedia.microphoneEnabled;
|
|
1016
|
+
const selectIsLowDataModeEnabled = (state) => state.localMedia.lowDataMode;
|
|
1017
|
+
const selectIsSettingCameraDevice = (state) => state.localMedia.isSettingCameraDevice;
|
|
1018
|
+
const selectIsSettingMicrophoneDevice = (state) => state.localMedia.isSettingMicrophoneDevice;
|
|
1019
|
+
const selectIsToggleCamera = (state) => state.localMedia.isTogglingCamera;
|
|
1020
|
+
const selectLocalMediaDevices = (state) => state.localMedia.devices;
|
|
1021
|
+
const selectLocalMediaOptions = (state) => state.localMedia.options;
|
|
1022
|
+
const selectLocalMediaOwnsStream = createSelector(selectLocalMediaOptions, (options) => !!options);
|
|
1023
|
+
const selectLocalMediaRaw = (state) => state.localMedia;
|
|
1024
|
+
const selectLocalMediaStatus = (state) => state.localMedia.status;
|
|
1025
|
+
const selectLocalMediaStream = (state) => state.localMedia.stream;
|
|
1026
|
+
const selectMicrophoneDeviceError = (state) => state.localMedia.microphoneDeviceError;
|
|
1027
|
+
const selectLocalMediaStartError = (state) => state.localMedia.startError;
|
|
1028
|
+
const selectLocalMediaIsSwitchingStream = (state) => state.localMedia.isSwitchingStream;
|
|
1029
|
+
const selectLocalMediaConstraintsOptions = createSelector(selectLocalMediaDevices, selectCurrentCameraDeviceId, selectCurrentMicrophoneDeviceId, selectIsLowDataModeEnabled, (devices, videoId, audioId, lowDataMode) => ({
|
|
1030
|
+
devices,
|
|
1031
|
+
videoId,
|
|
1032
|
+
audioId,
|
|
1033
|
+
options: {
|
|
1034
|
+
disableAEC: false,
|
|
1035
|
+
disableAGC: false,
|
|
1036
|
+
hd: true,
|
|
1037
|
+
lax: false,
|
|
1038
|
+
lowDataMode,
|
|
1039
|
+
simulcast: true,
|
|
1040
|
+
widescreen: true,
|
|
1041
|
+
},
|
|
1042
|
+
}));
|
|
1043
|
+
const selectIsLocalMediaStarting = createSelector(selectLocalMediaStatus, (status) => status === "starting");
|
|
1044
|
+
const selectCameraDevices = createSelector(selectLocalMediaDevices, selectBusyDeviceIds, (devices, busyDeviceIds) => devices.filter((d) => d.kind === "videoinput").filter((d) => !busyDeviceIds.includes(d.deviceId)));
|
|
1045
|
+
const selectMicrophoneDevices = createSelector(selectLocalMediaDevices, selectBusyDeviceIds, (devices, busyDeviceIds) => devices.filter((d) => d.kind === "audioinput").filter((d) => !busyDeviceIds.includes(d.deviceId)));
|
|
1046
|
+
const selectSpeakerDevices = createSelector(selectLocalMediaDevices, (devices) => devices.filter((d) => d.kind === "audiooutput"));
|
|
1047
|
+
const selectLocalMediaShouldStartWithOptions = createSelector(selectAppIsActive, selectLocalMediaStatus, selectLocalMediaOptions, (appIsActive, localMediaStatus, localMediaOptions) => {
|
|
1048
|
+
if (appIsActive && ["inactive", "stopped"].includes(localMediaStatus) && localMediaOptions) {
|
|
1049
|
+
return localMediaOptions;
|
|
1050
|
+
}
|
|
1051
|
+
});
|
|
1052
|
+
createReactor([selectLocalMediaShouldStartWithOptions], ({ dispatch }, options) => {
|
|
1053
|
+
if (options) {
|
|
1054
|
+
dispatch(doStartLocalMedia(options));
|
|
1055
|
+
}
|
|
1056
|
+
});
|
|
1057
|
+
const selectLocalMediaShouldStop = createSelector(selectAppIsActive, selectLocalMediaStatus, selectLocalMediaOptions, (appIsActive, localMediaStatus, localMediaOptions) => {
|
|
1058
|
+
return !appIsActive && localMediaStatus !== "inactive" && !!localMediaOptions;
|
|
1059
|
+
});
|
|
1060
|
+
createReactor([selectLocalMediaShouldStop], ({ dispatch }, localMediaShouldStop) => {
|
|
1061
|
+
if (localMediaShouldStop) {
|
|
1062
|
+
dispatch(doStopLocalMedia());
|
|
1063
|
+
}
|
|
1064
|
+
});
|
|
1065
|
+
startAppListening({
|
|
1066
|
+
predicate: (_action, currentState, previousState) => {
|
|
1067
|
+
const oldValue = selectIsMicrophoneEnabled(previousState);
|
|
1068
|
+
const newValue = selectIsMicrophoneEnabled(currentState);
|
|
1069
|
+
const isReady = selectLocalMediaStatus(previousState) === "started";
|
|
1070
|
+
return isReady && oldValue !== newValue;
|
|
1071
|
+
},
|
|
1072
|
+
effect: (_, { dispatch }) => {
|
|
1073
|
+
dispatch(doToggleMicrophone());
|
|
1074
|
+
},
|
|
1075
|
+
});
|
|
1076
|
+
startAppListening({
|
|
1077
|
+
predicate: (_action, currentState, previousState) => {
|
|
1078
|
+
const oldValue = selectIsLowDataModeEnabled(previousState);
|
|
1079
|
+
const newValue = selectIsLowDataModeEnabled(currentState);
|
|
1080
|
+
const isReady = selectLocalMediaStatus(previousState) === "started";
|
|
1081
|
+
return isReady && oldValue !== newValue;
|
|
1082
|
+
},
|
|
1083
|
+
effect: (_action, { dispatch }) => {
|
|
1084
|
+
dispatch(doToggleLowDataMode());
|
|
1085
|
+
},
|
|
1086
|
+
});
|
|
1087
|
+
startAppListening({
|
|
1088
|
+
predicate: (_action, currentState, previousState) => {
|
|
1089
|
+
const isToggling = selectIsToggleCamera(currentState);
|
|
1090
|
+
if (isToggling) {
|
|
1091
|
+
return false;
|
|
1092
|
+
}
|
|
1093
|
+
const oldValue = selectIsCameraEnabled(previousState);
|
|
1094
|
+
const newValue = selectIsCameraEnabled(currentState);
|
|
1095
|
+
const isReady = selectLocalMediaStatus(previousState) === "started";
|
|
1096
|
+
return isReady && oldValue !== newValue;
|
|
1097
|
+
},
|
|
1098
|
+
effect: (_action, { dispatch }) => {
|
|
1099
|
+
dispatch(doToggleCamera());
|
|
1100
|
+
},
|
|
1101
|
+
});
|
|
1102
|
+
startAppListening({
|
|
1103
|
+
predicate: (_action, currentState, previousState) => {
|
|
1104
|
+
const oldValue = selectCurrentCameraDeviceId(previousState);
|
|
1105
|
+
const newValue = selectCurrentCameraDeviceId(currentState);
|
|
1106
|
+
const isReady = selectLocalMediaStatus(previousState) === "started";
|
|
1107
|
+
return isReady && oldValue !== newValue;
|
|
1108
|
+
},
|
|
1109
|
+
effect: (_action, { dispatch }) => {
|
|
1110
|
+
dispatch(doSetDevice({ audio: false, video: true }));
|
|
1111
|
+
},
|
|
1112
|
+
});
|
|
1113
|
+
startAppListening({
|
|
1114
|
+
predicate: (_action, currentState, previousState) => {
|
|
1115
|
+
const oldValue = selectCurrentMicrophoneDeviceId(previousState);
|
|
1116
|
+
const newValue = selectCurrentMicrophoneDeviceId(currentState);
|
|
1117
|
+
const isReady = selectLocalMediaStatus(previousState) === "started";
|
|
1118
|
+
return isReady && oldValue !== newValue;
|
|
1119
|
+
},
|
|
1120
|
+
effect: (_action, { dispatch }) => {
|
|
1121
|
+
dispatch(doSetDevice({ audio: true, video: false }));
|
|
1122
|
+
},
|
|
1123
|
+
});
|
|
1124
|
+
startAppListening({
|
|
1125
|
+
matcher: isAnyOf(doStartLocalMedia.fulfilled, doUpdateDeviceList.fulfilled, doSwitchLocalStream.fulfilled, doSwitchLocalStream.rejected),
|
|
1126
|
+
effect: (_action, { dispatch, getState }) => {
|
|
1127
|
+
const state = getState();
|
|
1128
|
+
const stream = selectLocalMediaStream(state);
|
|
1129
|
+
const devices = selectLocalMediaDevices(state);
|
|
1130
|
+
if (!stream)
|
|
1131
|
+
return;
|
|
1132
|
+
const deviceData = getDeviceData({
|
|
1133
|
+
audioTrack: stream.getAudioTracks()[0],
|
|
1134
|
+
videoTrack: stream.getVideoTracks()[0],
|
|
1135
|
+
devices,
|
|
1136
|
+
});
|
|
1137
|
+
dispatch(localStreamMetadataUpdated(deviceData));
|
|
1138
|
+
},
|
|
1139
|
+
});
|
|
1140
|
+
startAppListening({
|
|
1141
|
+
actionCreator: signalEvents.audioEnableRequested,
|
|
1142
|
+
effect: ({ payload }, { dispatch }) => {
|
|
1143
|
+
const { enable } = payload;
|
|
1144
|
+
if (!enable) {
|
|
1145
|
+
dispatch(toggleMicrophoneEnabled({ enabled: false }));
|
|
1146
|
+
}
|
|
1147
|
+
},
|
|
1148
|
+
});
|
|
1149
|
+
startAppListening({
|
|
1150
|
+
actionCreator: signalEvents.videoEnableRequested,
|
|
1151
|
+
effect: ({ payload }, { dispatch }) => {
|
|
1152
|
+
const { enable } = payload;
|
|
1153
|
+
if (!enable) {
|
|
1154
|
+
dispatch(toggleCameraEnabled({ enabled: false }));
|
|
1155
|
+
}
|
|
1156
|
+
},
|
|
1157
|
+
});
|
|
1158
|
+
|
|
1159
|
+
const NON_PERSON_ROLES = ["recorder", "streamer"];
|
|
1160
|
+
|
|
1161
|
+
const initialState$a = {
|
|
1162
|
+
displayName: "",
|
|
1163
|
+
id: "",
|
|
1164
|
+
breakoutGroup: null,
|
|
1165
|
+
isAudioEnabled: true,
|
|
1166
|
+
isVideoEnabled: true,
|
|
1167
|
+
isLocalParticipant: true,
|
|
1168
|
+
stream: undefined,
|
|
1169
|
+
isScreenSharing: false,
|
|
1170
|
+
roleName: "none",
|
|
1171
|
+
clientClaim: undefined,
|
|
1172
|
+
stickyReaction: undefined,
|
|
1173
|
+
isDialIn: false,
|
|
1174
|
+
};
|
|
1175
|
+
const localParticipantSlice = createSlice({
|
|
1176
|
+
name: "localParticipant",
|
|
1177
|
+
initialState: initialState$a,
|
|
1178
|
+
reducers: {
|
|
1179
|
+
setDisplayName: (state, action) => {
|
|
1180
|
+
return Object.assign(Object.assign({}, state), { displayName: action.payload.displayName });
|
|
1181
|
+
},
|
|
1182
|
+
},
|
|
1183
|
+
extraReducers: (builder) => {
|
|
1184
|
+
builder.addCase(doAppStart, (state, action) => {
|
|
1185
|
+
return Object.assign(Object.assign({}, state), { displayName: action.payload.displayName });
|
|
1186
|
+
});
|
|
1187
|
+
builder.addCase(doEnableAudio.fulfilled, (state, action) => {
|
|
1188
|
+
return Object.assign(Object.assign({}, state), { isAudioEnabled: action.payload });
|
|
1189
|
+
});
|
|
1190
|
+
builder.addCase(doEnableVideo.fulfilled, (state, action) => {
|
|
1191
|
+
return Object.assign(Object.assign({}, state), { isVideoEnabled: action.payload });
|
|
1192
|
+
});
|
|
1193
|
+
builder.addCase(doSetLocalStickyReaction.fulfilled, (state, action) => {
|
|
1194
|
+
return Object.assign(Object.assign({}, state), { stickyReaction: action.payload });
|
|
1195
|
+
});
|
|
1196
|
+
builder.addCase(signalEvents.roomJoined, (state, action) => {
|
|
1197
|
+
var _a, _b;
|
|
1198
|
+
const client = (_b = (_a = action.payload) === null || _a === void 0 ? void 0 : _a.room) === null || _b === void 0 ? void 0 : _b.clients.find((c) => { var _a; return c.id === ((_a = action.payload) === null || _a === void 0 ? void 0 : _a.selfId); });
|
|
1199
|
+
return Object.assign(Object.assign({}, state), { id: action.payload.selfId, roleName: (client === null || client === void 0 ? void 0 : client.role.roleName) || "none", clientClaim: action.payload.clientClaim, breakoutGroup: (client === null || client === void 0 ? void 0 : client.breakoutGroup) || null });
|
|
1200
|
+
});
|
|
1201
|
+
builder.addCase(signalEvents.breakoutGroupJoined, (state, action) => {
|
|
1202
|
+
var _a, _b;
|
|
1203
|
+
if (((_a = action.payload) === null || _a === void 0 ? void 0 : _a.clientId) !== state.id) {
|
|
1204
|
+
return state;
|
|
1205
|
+
}
|
|
1206
|
+
return Object.assign(Object.assign({}, state), { breakoutGroup: (_b = action.payload) === null || _b === void 0 ? void 0 : _b.group });
|
|
1207
|
+
});
|
|
1208
|
+
},
|
|
1209
|
+
});
|
|
1210
|
+
const { setDisplayName } = localParticipantSlice.actions;
|
|
1211
|
+
const doSetDisplayName = createRoomConnectedThunk((payload) => (dispatch, getState) => {
|
|
1212
|
+
const state = getState();
|
|
1213
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
1214
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("send_client_metadata", {
|
|
1215
|
+
type: "UserData",
|
|
1216
|
+
payload: { displayName: payload.displayName },
|
|
1217
|
+
});
|
|
1218
|
+
dispatch(setDisplayName({ displayName: payload.displayName }));
|
|
1219
|
+
});
|
|
1220
|
+
const doEnableAudio = createAsyncRoomConnectedThunk("localParticipant/doEnableAudio", (payload, { dispatch, getState }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1221
|
+
const state = getState();
|
|
1222
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
1223
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("enable_audio", { enabled: payload.enabled });
|
|
1224
|
+
if (payload.enabled) {
|
|
1225
|
+
dispatch(doSetLocalStickyReaction({ enabled: false }));
|
|
1226
|
+
}
|
|
1227
|
+
return payload.enabled;
|
|
1228
|
+
}));
|
|
1229
|
+
const doEnableVideo = createAsyncRoomConnectedThunk("localParticipant/doEnableVideo", (payload, { getState }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1230
|
+
const state = getState();
|
|
1231
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
1232
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("enable_video", { enabled: payload.enabled });
|
|
1233
|
+
return payload.enabled;
|
|
1234
|
+
}));
|
|
1235
|
+
const doSetLocalStickyReaction = createAsyncRoomConnectedThunk("localParticipant/doSetLocalStickyReaction", (payload, { getState, rejectWithValue }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1236
|
+
var _a;
|
|
1237
|
+
const state = getState();
|
|
1238
|
+
const currentStickyReaction = selectLocalParticipantStickyReaction(state);
|
|
1239
|
+
const stickyReactionCurrentlyEnabled = Boolean(currentStickyReaction);
|
|
1240
|
+
const enabled = (_a = payload.enabled) !== null && _a !== void 0 ? _a : !stickyReactionCurrentlyEnabled;
|
|
1241
|
+
if (enabled === stickyReactionCurrentlyEnabled) {
|
|
1242
|
+
return rejectWithValue(currentStickyReaction);
|
|
1243
|
+
}
|
|
1244
|
+
const stickyReaction = enabled ? { reaction: "✋", timestamp: new Date().toISOString() } : null;
|
|
1245
|
+
return stickyReaction;
|
|
1246
|
+
}));
|
|
1247
|
+
const doSendClientMetadata = createRoomConnectedThunk(() => (_, getState) => {
|
|
1248
|
+
const state = getState();
|
|
1249
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
1250
|
+
const payload = {
|
|
1251
|
+
displayName: selectLocalParticipantDisplayName(state),
|
|
1252
|
+
stickyReaction: selectLocalParticipantStickyReaction(state),
|
|
1253
|
+
};
|
|
1254
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("send_client_metadata", {
|
|
1255
|
+
type: "UserData",
|
|
1256
|
+
payload,
|
|
1257
|
+
});
|
|
1258
|
+
});
|
|
1259
|
+
const selectLocalParticipantRaw = (state) => state.localParticipant;
|
|
1260
|
+
const selectSelfId = (state) => state.localParticipant.id;
|
|
1261
|
+
const selectLocalParticipantDisplayName = (state) => state.localParticipant.displayName;
|
|
1262
|
+
const selectLocalParticipantClientClaim = (state) => state.localParticipant.clientClaim;
|
|
1263
|
+
const selectLocalParticipantIsScreenSharing = (state) => state.localParticipant.isScreenSharing;
|
|
1264
|
+
const selectLocalParticipantStickyReaction = (state) => state.localParticipant.stickyReaction;
|
|
1265
|
+
const selectLocalParticipantView = createSelector(selectLocalParticipantRaw, selectLocalMediaStream, (participant, localStream) => {
|
|
1266
|
+
const clientView = {
|
|
1267
|
+
id: participant.id,
|
|
1268
|
+
clientId: participant.id,
|
|
1269
|
+
displayName: participant.displayName,
|
|
1270
|
+
stream: localStream,
|
|
1271
|
+
isLocalClient: true,
|
|
1272
|
+
isAudioEnabled: participant.isAudioEnabled,
|
|
1273
|
+
isVideoEnabled: participant.isVideoEnabled,
|
|
1274
|
+
};
|
|
1275
|
+
if (NON_PERSON_ROLES.includes(participant.roleName)) {
|
|
1276
|
+
return null;
|
|
1277
|
+
}
|
|
1278
|
+
return clientView;
|
|
1279
|
+
});
|
|
1280
|
+
startAppListening({
|
|
1281
|
+
actionCreator: toggleCameraEnabled,
|
|
1282
|
+
effect: ({ payload }, { dispatch, getState }) => {
|
|
1283
|
+
const { enabled } = payload;
|
|
1284
|
+
const { isVideoEnabled } = selectLocalParticipantRaw(getState());
|
|
1285
|
+
dispatch(doEnableVideo({ enabled: enabled || !isVideoEnabled }));
|
|
1286
|
+
},
|
|
1287
|
+
});
|
|
1288
|
+
startAppListening({
|
|
1289
|
+
actionCreator: toggleMicrophoneEnabled,
|
|
1290
|
+
effect: ({ payload }, { dispatch, getState }) => {
|
|
1291
|
+
const { enabled } = payload;
|
|
1292
|
+
const { isAudioEnabled } = selectLocalParticipantRaw(getState());
|
|
1293
|
+
dispatch(doEnableAudio({ enabled: enabled || !isAudioEnabled }));
|
|
1294
|
+
},
|
|
1295
|
+
});
|
|
1296
|
+
createReactor([selectLocalParticipantDisplayName, selectLocalParticipantStickyReaction], ({ dispatch }) => {
|
|
1297
|
+
dispatch(doSendClientMetadata());
|
|
1298
|
+
});
|
|
1299
|
+
|
|
1300
|
+
const initialState$9 = {
|
|
1301
|
+
session: null,
|
|
1302
|
+
status: "ready",
|
|
1303
|
+
error: null,
|
|
1304
|
+
};
|
|
1305
|
+
const roomConnectionSlice = createSlice({
|
|
1306
|
+
initialState: initialState$9,
|
|
1307
|
+
name: "roomConnection",
|
|
1308
|
+
reducers: {
|
|
1309
|
+
connectionStatusChanged: (state, action) => {
|
|
1310
|
+
return Object.assign(Object.assign({}, state), { status: action.payload });
|
|
1311
|
+
},
|
|
1312
|
+
},
|
|
1313
|
+
extraReducers: (builder) => {
|
|
1314
|
+
builder.addCase(signalEvents.roomJoined, (state, action) => {
|
|
1315
|
+
var _a, _b;
|
|
1316
|
+
const { error, isLocked } = action.payload;
|
|
1317
|
+
if (error === "room_locked" && isLocked) {
|
|
1318
|
+
return Object.assign(Object.assign({}, state), { status: "room_locked" });
|
|
1319
|
+
}
|
|
1320
|
+
if (error) {
|
|
1321
|
+
return Object.assign(Object.assign({}, state), { status: "disconnected", error });
|
|
1322
|
+
}
|
|
1323
|
+
return Object.assign(Object.assign({}, state), { status: "connected", session: (_b = (_a = action.payload.room) === null || _a === void 0 ? void 0 : _a.session) !== null && _b !== void 0 ? _b : null });
|
|
1324
|
+
});
|
|
1325
|
+
builder.addCase(signalEvents.disconnect, (state) => {
|
|
1326
|
+
if (["kicked", "left"].includes(state.status)) {
|
|
1327
|
+
return Object.assign({}, state);
|
|
1328
|
+
}
|
|
1329
|
+
return Object.assign(Object.assign({}, state), { status: "disconnected" });
|
|
1330
|
+
});
|
|
1331
|
+
builder.addCase(signalEvents.newClient, (state, action) => {
|
|
1332
|
+
var _a, _b;
|
|
1333
|
+
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 });
|
|
1334
|
+
});
|
|
1335
|
+
builder.addCase(signalEvents.roomSessionEnded, (state, action) => {
|
|
1336
|
+
var _a;
|
|
1337
|
+
if (((_a = state.session) === null || _a === void 0 ? void 0 : _a.id) !== action.payload.roomSessionId) {
|
|
1338
|
+
return state;
|
|
1339
|
+
}
|
|
1340
|
+
return Object.assign(Object.assign({}, state), { session: null });
|
|
1341
|
+
});
|
|
1342
|
+
builder.addCase(signalEvents.clientKicked, (state) => {
|
|
1343
|
+
return Object.assign(Object.assign({}, state), { status: "kicked" });
|
|
1344
|
+
});
|
|
1345
|
+
builder.addCase(signalEvents.roomLeft, (state) => {
|
|
1346
|
+
return Object.assign(Object.assign({}, state), { status: "left" });
|
|
1347
|
+
});
|
|
1348
|
+
builder.addCase(socketReconnecting, (state) => {
|
|
1349
|
+
return Object.assign(Object.assign({}, state), { status: "reconnecting" });
|
|
1350
|
+
});
|
|
1351
|
+
},
|
|
1352
|
+
});
|
|
1353
|
+
const { connectionStatusChanged } = roomConnectionSlice.actions;
|
|
1354
|
+
const doKnockRoom = createAppThunk(() => (dispatch, getState) => {
|
|
1355
|
+
const state = getState();
|
|
1356
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
1357
|
+
const roomName = selectAppRoomName(state);
|
|
1358
|
+
const roomKey = selectRoomKey(state);
|
|
1359
|
+
const displayName = selectAppDisplayName(state);
|
|
1360
|
+
const isDialIn = selectAppIsDialIn(state);
|
|
1361
|
+
const userAgent = selectAppUserAgent(state);
|
|
1362
|
+
const externalId = selectAppExternalId(state);
|
|
1363
|
+
const organizationId = selectOrganizationId(state);
|
|
1364
|
+
const connectionStatus = selectRoomConnectionStatus(state);
|
|
1365
|
+
if (connectionStatus !== "room_locked") {
|
|
1366
|
+
console.warn("Room is not locked, knock aborted");
|
|
1367
|
+
return;
|
|
1368
|
+
}
|
|
1369
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("knock_room", {
|
|
1370
|
+
avatarUrl: null,
|
|
1371
|
+
config: {
|
|
1372
|
+
isAudioEnabled: true,
|
|
1373
|
+
isVideoEnabled: true,
|
|
1374
|
+
},
|
|
1375
|
+
deviceCapabilities: { canScreenshare: true },
|
|
1376
|
+
displayName,
|
|
1377
|
+
isCoLocated: false,
|
|
1378
|
+
isDialIn,
|
|
1379
|
+
isDevicePermissionDenied: false,
|
|
1380
|
+
kickFromOtherRooms: false,
|
|
1381
|
+
organizationId,
|
|
1382
|
+
roomKey,
|
|
1383
|
+
roomName,
|
|
1384
|
+
userAgent,
|
|
1385
|
+
externalId,
|
|
1386
|
+
});
|
|
1387
|
+
dispatch(connectionStatusChanged("knocking"));
|
|
1388
|
+
});
|
|
1389
|
+
const doConnectRoom = createAppThunk(() => (dispatch, getState) => {
|
|
1390
|
+
const state = getState();
|
|
1391
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
1392
|
+
const roomName = selectAppRoomName(state);
|
|
1393
|
+
const roomKey = selectRoomKey(state);
|
|
1394
|
+
const displayName = selectAppDisplayName(state);
|
|
1395
|
+
const userAgent = selectAppUserAgent(state);
|
|
1396
|
+
const externalId = selectAppExternalId(state);
|
|
1397
|
+
const isDialIn = selectAppIsDialIn(state);
|
|
1398
|
+
const organizationId = selectOrganizationId(state);
|
|
1399
|
+
const isCameraEnabled = selectIsCameraEnabled(getState());
|
|
1400
|
+
const isMicrophoneEnabled = selectIsMicrophoneEnabled(getState());
|
|
1401
|
+
const clientClaim = selectLocalParticipantClientClaim(getState());
|
|
1402
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("join_room", Object.assign({ avatarUrl: null, config: {
|
|
1403
|
+
isAudioEnabled: isMicrophoneEnabled,
|
|
1404
|
+
isVideoEnabled: isCameraEnabled,
|
|
1405
|
+
}, deviceCapabilities: { canScreenshare: true }, displayName, isCoLocated: false, isDialIn, isDevicePermissionDenied: false, kickFromOtherRooms: false, organizationId,
|
|
1406
|
+
roomKey,
|
|
1407
|
+
roomName,
|
|
1408
|
+
userAgent,
|
|
1409
|
+
externalId }, (clientClaim && { clientClaim })));
|
|
1410
|
+
dispatch(connectionStatusChanged("connecting"));
|
|
1411
|
+
});
|
|
1412
|
+
const selectRoomConnectionRaw = (state) => state.roomConnection;
|
|
1413
|
+
const selectRoomConnectionSession = (state) => state.roomConnection.session;
|
|
1414
|
+
const selectRoomConnectionSessionId = (state) => { var _a; return (_a = state.roomConnection.session) === null || _a === void 0 ? void 0 : _a.id; };
|
|
1415
|
+
const selectRoomConnectionStatus = (state) => state.roomConnection.status;
|
|
1416
|
+
const selectRoomConnectionError = (state) => state.roomConnection.error;
|
|
1417
|
+
const selectShouldConnectRoom = createSelector([
|
|
1418
|
+
selectAppIsActive,
|
|
1419
|
+
selectOrganizationId,
|
|
1420
|
+
selectRoomConnectionStatus,
|
|
1421
|
+
selectSignalConnectionDeviceIdentified,
|
|
1422
|
+
selectLocalMediaStatus,
|
|
1423
|
+
selectRoomConnectionError,
|
|
1424
|
+
], (appIsActive, hasOrganizationIdFetched, roomConnectionStatus, signalConnectionDeviceIdentified, localMediaStatus, roomConnectionError) => {
|
|
1425
|
+
if (appIsActive &&
|
|
1426
|
+
localMediaStatus === "started" &&
|
|
1427
|
+
signalConnectionDeviceIdentified &&
|
|
1428
|
+
!!hasOrganizationIdFetched &&
|
|
1429
|
+
["ready", "reconnecting", "disconnected"].includes(roomConnectionStatus) &&
|
|
1430
|
+
!roomConnectionError) {
|
|
1431
|
+
return true;
|
|
1432
|
+
}
|
|
1433
|
+
return false;
|
|
1434
|
+
});
|
|
1435
|
+
createReactor([selectShouldConnectRoom], ({ dispatch }, shouldConnectRoom) => {
|
|
1436
|
+
if (shouldConnectRoom) {
|
|
1437
|
+
dispatch(doConnectRoom());
|
|
1438
|
+
}
|
|
1439
|
+
});
|
|
1440
|
+
startAppListening({
|
|
1441
|
+
actionCreator: signalEvents.knockHandled,
|
|
1442
|
+
effect: ({ payload }, { dispatch, getState }) => {
|
|
1443
|
+
const { clientId, resolution } = payload;
|
|
1444
|
+
const state = getState();
|
|
1445
|
+
const selfId = selectSelfId(state);
|
|
1446
|
+
if (clientId !== selfId) {
|
|
1447
|
+
return;
|
|
1448
|
+
}
|
|
1449
|
+
if (resolution === "accepted") {
|
|
1450
|
+
dispatch(setRoomKey(payload.metadata.roomKey));
|
|
1451
|
+
dispatch(doConnectRoom());
|
|
1452
|
+
}
|
|
1453
|
+
else if (resolution === "rejected") {
|
|
1454
|
+
dispatch(connectionStatusChanged("knock_rejected"));
|
|
1455
|
+
}
|
|
1456
|
+
},
|
|
1457
|
+
});
|
|
1458
|
+
startAppListening({
|
|
1459
|
+
actionCreator: doAppStop,
|
|
1460
|
+
effect: (_, { dispatch, getState }) => {
|
|
1461
|
+
const state = getState();
|
|
1462
|
+
const roomConnectionStatus = selectRoomConnectionStatus(state);
|
|
1463
|
+
if (roomConnectionStatus === "connected") {
|
|
1464
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
1465
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("leave_room");
|
|
1466
|
+
dispatch(connectionStatusChanged("leaving"));
|
|
1467
|
+
}
|
|
1468
|
+
else {
|
|
1469
|
+
doSignalDisconnect();
|
|
1470
|
+
}
|
|
1471
|
+
},
|
|
1472
|
+
});
|
|
1473
|
+
|
|
1474
|
+
function createRtcEventAction(name) {
|
|
1475
|
+
return createAction(`rtcConnection/event/${name}`);
|
|
1476
|
+
}
|
|
1477
|
+
const rtcEvents = {
|
|
1478
|
+
rtcManagerCreated: createRtcEventAction("rtcManagerCreated"),
|
|
1479
|
+
rtcManagerDestroyed: createRtcEventAction("rtcManagerDestroyed"),
|
|
1480
|
+
streamAdded: createRtcEventAction("streamAdded"),
|
|
1481
|
+
};
|
|
1482
|
+
|
|
1483
|
+
function createRemoteParticipant(client, newJoiner = false) {
|
|
1484
|
+
const { streams, role, breakoutGroup } = client, rest = __rest(client, ["streams", "role", "breakoutGroup"]);
|
|
1485
|
+
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 });
|
|
1486
|
+
}
|
|
1487
|
+
function findParticipant(state, participantId) {
|
|
1488
|
+
const index = state.remoteParticipants.findIndex((c) => c.id === participantId);
|
|
1489
|
+
return { index, participant: state.remoteParticipants[index] };
|
|
1490
|
+
}
|
|
1491
|
+
function updateParticipant(state, participantId, updates) {
|
|
1492
|
+
const { participant, index } = findParticipant(state, participantId);
|
|
1493
|
+
if (!participant) {
|
|
1494
|
+
console.error(`Did not find client for update ${participantId}`);
|
|
1495
|
+
return state;
|
|
1496
|
+
}
|
|
1497
|
+
return Object.assign(Object.assign({}, state), { remoteParticipants: [
|
|
1498
|
+
...state.remoteParticipants.slice(0, index),
|
|
1499
|
+
Object.assign(Object.assign({}, participant), updates),
|
|
1500
|
+
...state.remoteParticipants.slice(index + 1),
|
|
1501
|
+
] });
|
|
1502
|
+
}
|
|
1503
|
+
function addParticipant(state, participant) {
|
|
1504
|
+
const { participant: foundParticipant } = findParticipant(state, participant.id);
|
|
1505
|
+
if (foundParticipant) {
|
|
1506
|
+
console.warn(`Client already existing ${participant.id}. Ignoring`);
|
|
1507
|
+
return state;
|
|
1508
|
+
}
|
|
1509
|
+
return Object.assign(Object.assign({}, state), { remoteParticipants: [...state.remoteParticipants, participant] });
|
|
1510
|
+
}
|
|
1511
|
+
function updateStreamState(state, participantId, streamId, state_) {
|
|
1512
|
+
const { participant } = findParticipant(state, participantId);
|
|
1513
|
+
if (!participant) {
|
|
1514
|
+
console.error(`No client ${participantId} found to update stream ${streamId} ${state_}`);
|
|
1515
|
+
return state;
|
|
1516
|
+
}
|
|
1517
|
+
const idIdx = participant.streams.findIndex((s) => s.id === streamId);
|
|
1518
|
+
const streams = [...participant.streams];
|
|
1519
|
+
streams[idIdx] = Object.assign(Object.assign({}, streams[idIdx]), { state: state_ });
|
|
1520
|
+
return updateParticipant(state, participantId, { streams });
|
|
1521
|
+
}
|
|
1522
|
+
function removeClient(state, participantId) {
|
|
1523
|
+
return Object.assign(Object.assign({}, state), { remoteParticipants: state.remoteParticipants.filter((c) => c.id !== participantId) });
|
|
1524
|
+
}
|
|
1525
|
+
function addStreamId(state, participantId, streamId) {
|
|
1526
|
+
const { participant } = findParticipant(state, participantId);
|
|
1527
|
+
if (!participant || participant.streams.find((s) => s.id === streamId)) {
|
|
1528
|
+
console.warn(`No participant ${participantId} or stream ${streamId} already exists`);
|
|
1529
|
+
return state;
|
|
1530
|
+
}
|
|
1531
|
+
return updateParticipant(state, participantId, {
|
|
1532
|
+
streams: [...participant.streams, { id: streamId, state: "to_accept" }],
|
|
1533
|
+
});
|
|
1534
|
+
}
|
|
1535
|
+
function removeStreamId(state, participantId, streamId) {
|
|
1536
|
+
var _a, _b;
|
|
1537
|
+
const { participant } = findParticipant(state, participantId);
|
|
1538
|
+
if (!participant) {
|
|
1539
|
+
console.error(`No participant ${participantId} found to remove stream ${streamId}`);
|
|
1540
|
+
return state;
|
|
1541
|
+
}
|
|
1542
|
+
const currentStreamId = participant.stream && participant.stream.id;
|
|
1543
|
+
const presentationId = ((_a = participant.presentationStream) === null || _a === void 0 ? void 0 : _a.inboundId) || ((_b = participant.presentationStream) === null || _b === void 0 ? void 0 : _b.id);
|
|
1544
|
+
const idIdx = participant.streams.findIndex((s) => s.id === streamId);
|
|
1545
|
+
return updateParticipant(state, participantId, Object.assign(Object.assign({ streams: participant.streams.filter((_, i) => i !== idIdx) }, (currentStreamId === streamId && { stream: null })), (presentationId === streamId && { presentationStream: null })));
|
|
1546
|
+
}
|
|
1547
|
+
function addStream(state, payload) {
|
|
1548
|
+
const { clientId, stream, streamType } = payload;
|
|
1549
|
+
let { streamId } = payload;
|
|
1550
|
+
const { participant } = findParticipant(state, clientId);
|
|
1551
|
+
if (!participant) {
|
|
1552
|
+
console.error(`Did not find client ${clientId} for adding stream`);
|
|
1553
|
+
return state;
|
|
1554
|
+
}
|
|
1555
|
+
const remoteParticipants = state.remoteParticipants;
|
|
1556
|
+
if (!streamId) {
|
|
1557
|
+
streamId = stream.id;
|
|
1558
|
+
}
|
|
1559
|
+
const remoteParticipant = remoteParticipants.find((p) => p.id === clientId);
|
|
1560
|
+
if (!remoteParticipant) {
|
|
1561
|
+
return state;
|
|
1562
|
+
}
|
|
1563
|
+
const remoteParticipantStream = remoteParticipant.streams.find((s) => s.id === streamId);
|
|
1564
|
+
if ((remoteParticipant.stream &&
|
|
1565
|
+
(remoteParticipant.stream.id === streamId || remoteParticipant.stream.inboundId === streamId)) ||
|
|
1566
|
+
(!remoteParticipant.stream && streamType === "webcam") ||
|
|
1567
|
+
(!remoteParticipant.stream && !streamType && !remoteParticipantStream)) {
|
|
1568
|
+
return updateParticipant(state, clientId, { stream });
|
|
1569
|
+
}
|
|
1570
|
+
return updateParticipant(state, clientId, {
|
|
1571
|
+
presentationStream: stream,
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
const initialState$8 = {
|
|
1575
|
+
remoteParticipants: [],
|
|
1576
|
+
};
|
|
1577
|
+
const remoteParticipantsSlice = createSlice({
|
|
1578
|
+
name: "remoteParticipants",
|
|
1579
|
+
initialState: initialState$8,
|
|
1580
|
+
reducers: {
|
|
1581
|
+
streamStatusUpdated: (state, action) => {
|
|
1582
|
+
let newState = state;
|
|
1583
|
+
for (const { clientId, streamId, state } of action.payload) {
|
|
1584
|
+
newState = updateStreamState(newState, clientId, streamId, state);
|
|
1585
|
+
}
|
|
1586
|
+
return newState;
|
|
1587
|
+
},
|
|
1588
|
+
participantStreamAdded: (state, action) => {
|
|
1589
|
+
const { clientId, stream } = action.payload;
|
|
1590
|
+
return updateParticipant(state, clientId, {
|
|
1591
|
+
stream,
|
|
1592
|
+
});
|
|
1593
|
+
},
|
|
1594
|
+
participantStreamIdAdded: (state, action) => {
|
|
1595
|
+
const { clientId, streamId } = action.payload;
|
|
1596
|
+
return addStreamId(state, clientId, streamId);
|
|
1597
|
+
},
|
|
1598
|
+
},
|
|
1599
|
+
extraReducers: (builder) => {
|
|
1600
|
+
builder.addCase(signalEvents.roomJoined, (state, action) => {
|
|
1601
|
+
var _a;
|
|
1602
|
+
if (!((_a = action.payload) === null || _a === void 0 ? void 0 : _a.room))
|
|
1603
|
+
return state;
|
|
1604
|
+
const selfId = action.payload.selfId;
|
|
1605
|
+
const { clients } = action.payload.room;
|
|
1606
|
+
return Object.assign(Object.assign({}, state), { remoteParticipants: clients.filter((c) => c.id !== selfId).map((c) => createRemoteParticipant(c)) });
|
|
1607
|
+
});
|
|
1608
|
+
builder.addCase(rtcEvents.streamAdded, (state, action) => {
|
|
1609
|
+
return addStream(state, action.payload);
|
|
1610
|
+
});
|
|
1611
|
+
builder.addCase(signalEvents.newClient, (state, action) => {
|
|
1612
|
+
const { client } = action.payload;
|
|
1613
|
+
return addParticipant(state, createRemoteParticipant(client, true));
|
|
1614
|
+
});
|
|
1615
|
+
builder.addCase(signalEvents.clientLeft, (state, action) => {
|
|
1616
|
+
const { clientId } = action.payload;
|
|
1617
|
+
return removeClient(state, clientId);
|
|
1618
|
+
});
|
|
1619
|
+
builder.addCase(signalEvents.audioEnabled, (state, action) => {
|
|
1620
|
+
const { clientId, isAudioEnabled } = action.payload;
|
|
1621
|
+
return updateParticipant(state, clientId, {
|
|
1622
|
+
isAudioEnabled,
|
|
1623
|
+
});
|
|
1624
|
+
});
|
|
1625
|
+
builder.addCase(signalEvents.videoEnabled, (state, action) => {
|
|
1626
|
+
const { clientId, isVideoEnabled } = action.payload;
|
|
1627
|
+
return updateParticipant(state, clientId, {
|
|
1628
|
+
isVideoEnabled,
|
|
1629
|
+
});
|
|
1630
|
+
});
|
|
1631
|
+
builder.addCase(signalEvents.clientMetadataReceived, (state, action) => {
|
|
1632
|
+
const { error, payload } = action.payload;
|
|
1633
|
+
if (error || !payload) {
|
|
1634
|
+
console.warn(error || "Client metadata error received");
|
|
1635
|
+
return state;
|
|
1636
|
+
}
|
|
1637
|
+
const { clientId, displayName, stickyReaction } = payload;
|
|
1638
|
+
return updateParticipant(state, clientId, {
|
|
1639
|
+
displayName,
|
|
1640
|
+
stickyReaction,
|
|
1641
|
+
});
|
|
1642
|
+
});
|
|
1643
|
+
builder.addCase(signalEvents.breakoutGroupJoined, (state, action) => {
|
|
1644
|
+
const { clientId, group } = action.payload;
|
|
1645
|
+
return updateParticipant(state, clientId, {
|
|
1646
|
+
breakoutGroup: group || null,
|
|
1647
|
+
});
|
|
1648
|
+
});
|
|
1649
|
+
builder.addCase(signalEvents.screenshareStarted, (state, action) => {
|
|
1650
|
+
const { clientId, streamId } = action.payload;
|
|
1651
|
+
return addStreamId(state, clientId, streamId);
|
|
1652
|
+
});
|
|
1653
|
+
builder.addCase(signalEvents.screenshareStopped, (state, action) => {
|
|
1654
|
+
const { clientId, streamId } = action.payload;
|
|
1655
|
+
return removeStreamId(state, clientId, streamId);
|
|
1656
|
+
});
|
|
1657
|
+
},
|
|
1658
|
+
});
|
|
1659
|
+
const { participantStreamAdded, participantStreamIdAdded, streamStatusUpdated } = remoteParticipantsSlice.actions;
|
|
1660
|
+
const doRequestAudioEnable = createAuthorizedRoomConnectedThunk((state) => selectIsAuthorizedToRequestAudioEnable(state), (payload) => (_, getState) => {
|
|
1661
|
+
const state = getState();
|
|
1662
|
+
const canEnableRemoteAudio = selectIsAuthorizedToAskToSpeak(state);
|
|
1663
|
+
if (payload.enable && !canEnableRemoteAudio) {
|
|
1664
|
+
console.warn("Not authorized to perform this action");
|
|
1665
|
+
return;
|
|
1666
|
+
}
|
|
1667
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
1668
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("request_audio_enable", payload);
|
|
1669
|
+
});
|
|
1670
|
+
const doRequestVideoEnable = createAuthorizedRoomConnectedThunk((state) => selectIsAuthorizedToRequestVideoEnable(state), (payload) => (_, getState) => {
|
|
1671
|
+
const state = getState();
|
|
1672
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
1673
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("request_video_enable", payload);
|
|
1674
|
+
});
|
|
1675
|
+
const selectRemoteParticipantsRaw = (state) => state.remoteParticipants;
|
|
1676
|
+
const selectRemoteClients = (state) => state.remoteParticipants.remoteParticipants;
|
|
1677
|
+
const selectRemoteParticipants = createSelector(selectRemoteClients, (clients) => clients.filter((c) => !NON_PERSON_ROLES.includes(c.roleName)));
|
|
1678
|
+
const selectNumClients = createSelector(selectRemoteClients, (clients) => clients.length + 1);
|
|
1679
|
+
const selectNumParticipants = createSelector(selectRemoteParticipants, selectLocalParticipantRaw, (clients, localParticipant) => {
|
|
1680
|
+
if (NON_PERSON_ROLES.includes(localParticipant.roleName)) {
|
|
1681
|
+
return clients.length;
|
|
1682
|
+
}
|
|
1683
|
+
return clients.length + 1;
|
|
1684
|
+
});
|
|
1685
|
+
|
|
1686
|
+
const initialState$7 = {
|
|
1687
|
+
status: "inactive",
|
|
1688
|
+
stream: null,
|
|
1689
|
+
error: null,
|
|
1690
|
+
};
|
|
1691
|
+
const localScreenshareSlice = createSlice({
|
|
1692
|
+
name: "localScreenshare",
|
|
1693
|
+
initialState: initialState$7,
|
|
1694
|
+
reducers: {
|
|
1695
|
+
stopScreenshare(state, action) {
|
|
1696
|
+
return Object.assign(Object.assign({}, state), { status: "inactive", stream: null });
|
|
1697
|
+
},
|
|
1698
|
+
},
|
|
1699
|
+
extraReducers: (builder) => {
|
|
1700
|
+
builder.addCase(doStartScreenshare.pending, (state) => {
|
|
1701
|
+
return Object.assign(Object.assign({}, state), { status: "starting" });
|
|
1702
|
+
});
|
|
1703
|
+
builder.addCase(doStartScreenshare.fulfilled, (state, { payload: { stream } }) => {
|
|
1704
|
+
return Object.assign(Object.assign({}, state), { status: "active", stream });
|
|
1705
|
+
});
|
|
1706
|
+
builder.addCase(doStartScreenshare.rejected, (state, { payload }) => {
|
|
1707
|
+
return Object.assign(Object.assign({}, state), { error: payload, status: "inactive", stream: null });
|
|
1708
|
+
});
|
|
1709
|
+
},
|
|
1710
|
+
});
|
|
1711
|
+
const { stopScreenshare } = localScreenshareSlice.actions;
|
|
1712
|
+
const doStartScreenshare = createAsyncRoomConnectedThunk("localScreenshare/doStartScreenshare", (_, { dispatch, getState, rejectWithValue }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1713
|
+
var _a;
|
|
1714
|
+
try {
|
|
1715
|
+
const state = getState();
|
|
1716
|
+
const screenshareStream = selectLocalScreenshareStream(state);
|
|
1717
|
+
if (screenshareStream) {
|
|
1718
|
+
return { stream: screenshareStream };
|
|
1719
|
+
}
|
|
1720
|
+
const stream = yield navigator.mediaDevices.getDisplayMedia();
|
|
1721
|
+
const onEnded = () => {
|
|
1722
|
+
dispatch(doStopScreenshare());
|
|
1723
|
+
};
|
|
1724
|
+
if ("oninactive" in stream) {
|
|
1725
|
+
stream.addEventListener("inactive", onEnded);
|
|
1726
|
+
}
|
|
1727
|
+
else {
|
|
1728
|
+
(_a = stream.getVideoTracks()[0]) === null || _a === void 0 ? void 0 : _a.addEventListener("ended", onEnded);
|
|
1729
|
+
}
|
|
1730
|
+
return { stream };
|
|
1731
|
+
}
|
|
1732
|
+
catch (error) {
|
|
1733
|
+
return rejectWithValue(error);
|
|
1734
|
+
}
|
|
1735
|
+
}));
|
|
1736
|
+
const doStopScreenshare = createRoomConnectedThunk(() => (dispatch, getState) => {
|
|
1737
|
+
const state = getState();
|
|
1738
|
+
const screenshareStream = selectLocalScreenshareStream(state);
|
|
1739
|
+
if (!screenshareStream) {
|
|
1740
|
+
return;
|
|
1741
|
+
}
|
|
1742
|
+
screenshareStream.getTracks().forEach((track) => track.stop());
|
|
1743
|
+
dispatch(stopScreenshare({ stream: screenshareStream }));
|
|
1744
|
+
});
|
|
1745
|
+
const selectLocalScreenshareRaw = (state) => state.localScreenshare;
|
|
1746
|
+
const selectLocalScreenshareStatus = (state) => state.localScreenshare.status;
|
|
1747
|
+
const selectLocalScreenshareStream = (state) => state.localScreenshare.stream;
|
|
1748
|
+
startAppListening({
|
|
1749
|
+
actionCreator: localMediaStopped,
|
|
1750
|
+
effect: (_, { getState }) => {
|
|
1751
|
+
const state = getState();
|
|
1752
|
+
const screenshareStream = selectLocalScreenshareStream(state);
|
|
1753
|
+
if (!screenshareStream) {
|
|
1754
|
+
return;
|
|
1755
|
+
}
|
|
1756
|
+
screenshareStream === null || screenshareStream === void 0 ? void 0 : screenshareStream.getTracks().forEach((track) => {
|
|
1757
|
+
track.stop();
|
|
1758
|
+
});
|
|
1759
|
+
},
|
|
1760
|
+
});
|
|
1761
|
+
|
|
1762
|
+
const createWebRtcEmitter = (dispatch) => {
|
|
1763
|
+
return {
|
|
1764
|
+
emit: (eventName, data) => {
|
|
1765
|
+
if (eventName === "rtc_manager_created") {
|
|
1766
|
+
dispatch(doRtcManagerCreated(data));
|
|
1767
|
+
}
|
|
1768
|
+
else if (eventName === "stream_added") {
|
|
1769
|
+
dispatch(rtcEvents.streamAdded(data));
|
|
1770
|
+
}
|
|
1771
|
+
else if (eventName === "rtc_manager_destroyed") {
|
|
1772
|
+
dispatch(rtcManagerDestroyed());
|
|
1773
|
+
}
|
|
1774
|
+
else ;
|
|
1775
|
+
},
|
|
1776
|
+
};
|
|
1777
|
+
};
|
|
1778
|
+
const initialState$6 = {
|
|
1779
|
+
dispatcherCreated: false,
|
|
1780
|
+
error: null,
|
|
1781
|
+
isCreatingDispatcher: false,
|
|
1782
|
+
reportedStreamResolutions: {},
|
|
1783
|
+
rtcManager: null,
|
|
1784
|
+
rtcManagerDispatcher: null,
|
|
1785
|
+
rtcManagerInitialized: false,
|
|
1786
|
+
status: "inactive",
|
|
1787
|
+
isAcceptingStreams: false,
|
|
1788
|
+
};
|
|
1789
|
+
const rtcConnectionSlice = createSlice({
|
|
1790
|
+
name: "rtcConnection",
|
|
1791
|
+
initialState: initialState$6,
|
|
1792
|
+
reducers: {
|
|
1793
|
+
isAcceptingStreams: (state, action) => {
|
|
1794
|
+
return Object.assign(Object.assign({}, state), { isAcceptingStreams: action.payload });
|
|
1795
|
+
},
|
|
1796
|
+
resolutionReported: (state, action) => {
|
|
1797
|
+
const { streamId, width, height } = action.payload;
|
|
1798
|
+
return Object.assign(Object.assign({}, state), { reportedStreamResolutions: Object.assign(Object.assign({}, state.reportedStreamResolutions), { [streamId]: { width, height } }) });
|
|
1799
|
+
},
|
|
1800
|
+
rtcDisconnected: () => {
|
|
1801
|
+
return Object.assign({}, initialState$6);
|
|
1802
|
+
},
|
|
1803
|
+
rtcDispatcherCreated: (state, action) => {
|
|
1804
|
+
return Object.assign(Object.assign({}, state), { dispatcherCreated: true, rtcManagerDispatcher: action.payload });
|
|
1805
|
+
},
|
|
1806
|
+
rtcManagerCreated: (state, action) => {
|
|
1807
|
+
return Object.assign(Object.assign({}, state), { rtcManager: action.payload, status: "ready" });
|
|
1808
|
+
},
|
|
1809
|
+
rtcManagerDestroyed: (state) => {
|
|
1810
|
+
return Object.assign(Object.assign({}, state), { rtcManager: null });
|
|
1811
|
+
},
|
|
1812
|
+
rtcManagerInitialized: (state) => {
|
|
1813
|
+
return Object.assign(Object.assign({}, state), { rtcManagerInitialized: true });
|
|
1814
|
+
},
|
|
1815
|
+
},
|
|
1816
|
+
extraReducers: (builder) => {
|
|
1817
|
+
builder.addCase(socketReconnecting, (state) => {
|
|
1818
|
+
return Object.assign(Object.assign({}, state), { status: "reconnecting" });
|
|
1819
|
+
});
|
|
1820
|
+
builder.addCase(signalEvents.roomJoined, (state) => {
|
|
1821
|
+
return Object.assign(Object.assign({}, state), { status: state.status === "reconnecting" ? "ready" : state.status });
|
|
1822
|
+
});
|
|
1823
|
+
},
|
|
1824
|
+
});
|
|
1825
|
+
const { resolutionReported, rtcDispatcherCreated, rtcDisconnected, rtcManagerCreated, rtcManagerDestroyed, rtcManagerInitialized, isAcceptingStreams, } = rtcConnectionSlice.actions;
|
|
1826
|
+
const doConnectRtc = createAppThunk(() => (dispatch, getState) => {
|
|
1827
|
+
const state = getState();
|
|
1828
|
+
const socket = selectSignalConnectionRaw(state).socket;
|
|
1829
|
+
const dispatcher = selectRtcConnectionRaw(state).rtcManagerDispatcher;
|
|
1830
|
+
const isCameraEnabled = selectIsCameraEnabled(state);
|
|
1831
|
+
const isMicrophoneEnabled = selectIsMicrophoneEnabled(state);
|
|
1832
|
+
const isNodeSdk = selectAppIsNodeSdk(state);
|
|
1833
|
+
if (dispatcher || !socket) {
|
|
1834
|
+
return;
|
|
1835
|
+
}
|
|
1836
|
+
const webrtcProvider = {
|
|
1837
|
+
getMediaConstraints: () => ({
|
|
1838
|
+
audio: isMicrophoneEnabled,
|
|
1839
|
+
video: isCameraEnabled,
|
|
1840
|
+
}),
|
|
1841
|
+
deferrable(clientId) {
|
|
1842
|
+
return !clientId;
|
|
1843
|
+
},
|
|
1844
|
+
};
|
|
1845
|
+
const rtcManagerDispatcher = new RtcManagerDispatcher({
|
|
1846
|
+
emitter: createWebRtcEmitter(dispatch),
|
|
1847
|
+
serverSocket: socket,
|
|
1848
|
+
webrtcProvider,
|
|
1849
|
+
features: {
|
|
1850
|
+
isNodeSdk,
|
|
1851
|
+
lowDataModeEnabled: false,
|
|
1852
|
+
sfuServerOverrideHost: undefined,
|
|
1853
|
+
turnServerOverrideHost: undefined,
|
|
1854
|
+
useOnlyTURN: undefined,
|
|
1855
|
+
vp9On: false,
|
|
1856
|
+
h264On: false,
|
|
1857
|
+
simulcastScreenshareOn: false,
|
|
1858
|
+
deviceHandlerFactory: isNodeSdk ? Safari12.createFactory() : undefined,
|
|
1859
|
+
},
|
|
1860
|
+
});
|
|
1861
|
+
dispatch(rtcDispatcherCreated(rtcManagerDispatcher));
|
|
1862
|
+
});
|
|
1863
|
+
const doDisconnectRtc = createAppThunk(() => (dispatch, getState) => {
|
|
1864
|
+
const { rtcManager } = selectRtcConnectionRaw(getState());
|
|
1865
|
+
if (rtcManager) {
|
|
1866
|
+
rtcManager.disconnectAll();
|
|
1867
|
+
}
|
|
1868
|
+
dispatch(rtcDisconnected());
|
|
1869
|
+
});
|
|
1870
|
+
const doHandleAcceptStreams = createAppThunk((payload) => (dispatch, getState) => {
|
|
1871
|
+
var _a;
|
|
1872
|
+
dispatch(isAcceptingStreams(true));
|
|
1873
|
+
const state = getState();
|
|
1874
|
+
const rtcManager = selectRtcConnectionRaw(state).rtcManager;
|
|
1875
|
+
const remoteClients = selectRemoteClients(state);
|
|
1876
|
+
if (!rtcManager) {
|
|
1877
|
+
throw new Error("No rtc manager");
|
|
1878
|
+
}
|
|
1879
|
+
const activeBreakout = false;
|
|
1880
|
+
const shouldAcceptNewClients = (_a = rtcManager.shouldAcceptStreamsFromBothSides) === null || _a === void 0 ? void 0 : _a.call(rtcManager);
|
|
1881
|
+
const updates = [];
|
|
1882
|
+
for (const { clientId, streamId, state } of payload) {
|
|
1883
|
+
const participant = remoteClients.find((p) => p.id === clientId);
|
|
1884
|
+
if (!participant)
|
|
1885
|
+
continue;
|
|
1886
|
+
if (state === "to_accept" ||
|
|
1887
|
+
(state === "new_accept" && shouldAcceptNewClients) ||
|
|
1888
|
+
(state === "old_accept" && !shouldAcceptNewClients)) {
|
|
1889
|
+
rtcManager.acceptNewStream({
|
|
1890
|
+
streamId: streamId === "0" ? clientId : streamId,
|
|
1891
|
+
clientId,
|
|
1892
|
+
shouldAddLocalVideo: streamId === "0",
|
|
1893
|
+
activeBreakout,
|
|
1894
|
+
});
|
|
1895
|
+
}
|
|
1896
|
+
else if (state === "new_accept" || state === "old_accept") ;
|
|
1897
|
+
else if (state === "to_unaccept") {
|
|
1898
|
+
rtcManager === null || rtcManager === void 0 ? void 0 : rtcManager.disconnect(streamId === "0" ? clientId : streamId, activeBreakout);
|
|
1899
|
+
}
|
|
1900
|
+
else if (state !== "done_accept") {
|
|
1901
|
+
continue;
|
|
1902
|
+
}
|
|
1903
|
+
else ;
|
|
1904
|
+
updates.push({ clientId, streamId, state: state.replace(/to_|new_|old_/, "done_") });
|
|
1905
|
+
}
|
|
1906
|
+
dispatch(streamStatusUpdated(updates));
|
|
1907
|
+
dispatch(isAcceptingStreams(false));
|
|
1908
|
+
});
|
|
1909
|
+
const doRtcReportStreamResolution = createAppThunk(({ streamId, width, height }) => (dispatch, getState) => {
|
|
1910
|
+
const { reportedStreamResolutions, rtcManager } = selectRtcConnectionRaw(getState());
|
|
1911
|
+
const localStream = selectLocalMediaStream(getState());
|
|
1912
|
+
if (!rtcManager || (localStream === null || localStream === void 0 ? void 0 : localStream.id) === streamId) {
|
|
1913
|
+
return;
|
|
1914
|
+
}
|
|
1915
|
+
const old = reportedStreamResolutions[streamId];
|
|
1916
|
+
if (!old || old.width !== width || old.height !== height) {
|
|
1917
|
+
rtcManager.updateStreamResolution(streamId, null, { width: width || 1, height: height || 1 });
|
|
1918
|
+
}
|
|
1919
|
+
dispatch(resolutionReported({ streamId, width, height }));
|
|
1920
|
+
});
|
|
1921
|
+
const doRtcManagerCreated = createAppThunk((payload) => (dispatch) => {
|
|
1922
|
+
const { rtcManager } = payload;
|
|
1923
|
+
dispatch(rtcManagerCreated(rtcManager));
|
|
1924
|
+
});
|
|
1925
|
+
const doRtcManagerInitialize = createAppThunk(() => (dispatch, getState) => {
|
|
1926
|
+
const localMediaStream = selectLocalMediaStream(getState());
|
|
1927
|
+
const rtcManager = selectRtcConnectionRaw(getState()).rtcManager;
|
|
1928
|
+
const isCameraEnabled = selectIsCameraEnabled(getState());
|
|
1929
|
+
const isMicrophoneEnabled = selectIsMicrophoneEnabled(getState());
|
|
1930
|
+
if (localMediaStream && rtcManager) {
|
|
1931
|
+
rtcManager.addNewStream("0", localMediaStream, !isMicrophoneEnabled, !isCameraEnabled);
|
|
1932
|
+
}
|
|
1933
|
+
dispatch(rtcManagerInitialized());
|
|
1934
|
+
});
|
|
1935
|
+
const selectRtcConnectionRaw = (state) => state.rtcConnection;
|
|
1936
|
+
const selectRtcManagerInitialized = (state) => state.rtcConnection.rtcManagerInitialized;
|
|
1937
|
+
const selectRtcManager = (state) => state.rtcConnection.rtcManager;
|
|
1938
|
+
const selectRtcDispatcherCreated = (state) => state.rtcConnection.dispatcherCreated;
|
|
1939
|
+
const selectRtcIsCreatingDispatcher = (state) => state.rtcConnection.isCreatingDispatcher;
|
|
1940
|
+
const selectRtcStatus = (state) => state.rtcConnection.status;
|
|
1941
|
+
const selectIsAcceptingStreams = (state) => state.rtcConnection.isAcceptingStreams;
|
|
1942
|
+
startAppListening({
|
|
1943
|
+
actionCreator: doSetDevice.fulfilled,
|
|
1944
|
+
effect: ({ payload }, { getState }) => {
|
|
1945
|
+
const { replacedTracks } = payload;
|
|
1946
|
+
const { rtcManager } = selectRtcConnectionRaw(getState());
|
|
1947
|
+
const stream = selectLocalMediaStream(getState());
|
|
1948
|
+
const replace = (kind, oldTrack) => {
|
|
1949
|
+
const track = stream === null || stream === void 0 ? void 0 : stream.getTracks().find((t) => t.kind === kind);
|
|
1950
|
+
return track && (rtcManager === null || rtcManager === void 0 ? void 0 : rtcManager.replaceTrack(oldTrack, track));
|
|
1951
|
+
};
|
|
1952
|
+
replacedTracks === null || replacedTracks === void 0 ? void 0 : replacedTracks.forEach((t) => {
|
|
1953
|
+
replace(t.kind, t);
|
|
1954
|
+
});
|
|
1955
|
+
},
|
|
1956
|
+
});
|
|
1957
|
+
startAppListening({
|
|
1958
|
+
actionCreator: doStartScreenshare.fulfilled,
|
|
1959
|
+
effect: ({ payload }, { getState }) => {
|
|
1960
|
+
const { stream } = payload;
|
|
1961
|
+
const { rtcManager } = selectRtcConnectionRaw(getState());
|
|
1962
|
+
rtcManager === null || rtcManager === void 0 ? void 0 : rtcManager.addNewStream(stream.id, stream, false, true);
|
|
1963
|
+
},
|
|
1964
|
+
});
|
|
1965
|
+
startAppListening({
|
|
1966
|
+
actionCreator: doAppStop,
|
|
1967
|
+
effect: (_, { getState }) => {
|
|
1968
|
+
const rtcManager = selectRtcManager(getState());
|
|
1969
|
+
rtcManager === null || rtcManager === void 0 ? void 0 : rtcManager.rtcStatsDisconnect();
|
|
1970
|
+
},
|
|
1971
|
+
});
|
|
1972
|
+
startAppListening({
|
|
1973
|
+
actionCreator: stopScreenshare,
|
|
1974
|
+
effect: ({ payload }, { getState }) => {
|
|
1975
|
+
const { stream } = payload;
|
|
1976
|
+
const { rtcManager } = selectRtcConnectionRaw(getState());
|
|
1977
|
+
rtcManager === null || rtcManager === void 0 ? void 0 : rtcManager.removeStream(stream.id, stream, null);
|
|
1978
|
+
},
|
|
1979
|
+
});
|
|
1980
|
+
startAppListening({
|
|
1981
|
+
actionCreator: doSwitchLocalStream.fulfilled,
|
|
1982
|
+
effect: ({ payload }, { getState }) => {
|
|
1983
|
+
var _a;
|
|
1984
|
+
const stream = selectLocalMediaStream(getState());
|
|
1985
|
+
const { rtcManager } = selectRtcConnectionRaw(getState());
|
|
1986
|
+
if (stream && rtcManager) {
|
|
1987
|
+
const replace = (kind, oldTrack) => {
|
|
1988
|
+
const track = stream.getTracks().find((t) => t.kind === kind);
|
|
1989
|
+
return track && rtcManager.replaceTrack(oldTrack, track);
|
|
1990
|
+
};
|
|
1991
|
+
(_a = payload === null || payload === void 0 ? void 0 : payload.replacedTracks) === null || _a === void 0 ? void 0 : _a.forEach((t) => {
|
|
1992
|
+
replace(t.kind, t);
|
|
1993
|
+
});
|
|
1994
|
+
}
|
|
1995
|
+
},
|
|
1996
|
+
});
|
|
1997
|
+
const selectShouldConnectRtc = createSelector(selectRtcStatus, selectAppIsActive, selectRtcDispatcherCreated, selectRtcIsCreatingDispatcher, selectSignalConnectionSocket, (rtcStatus, appIsActive, dispatcherCreated, isCreatingDispatcher, signalSocket) => {
|
|
1998
|
+
if (appIsActive && rtcStatus === "inactive" && !dispatcherCreated && !isCreatingDispatcher && signalSocket) {
|
|
1999
|
+
return true;
|
|
2000
|
+
}
|
|
2001
|
+
return false;
|
|
2002
|
+
});
|
|
2003
|
+
createReactor([selectShouldConnectRtc], ({ dispatch }, shouldConnectRtc) => {
|
|
2004
|
+
if (shouldConnectRtc) {
|
|
2005
|
+
dispatch(doConnectRtc());
|
|
2006
|
+
}
|
|
2007
|
+
});
|
|
2008
|
+
const selectShouldInitializeRtc = createSelector(selectRtcManager, selectRtcManagerInitialized, selectLocalMediaStatus, (rtcManager, rtcManagerInitialized, localMediaStatus) => {
|
|
2009
|
+
if (localMediaStatus === "started" && rtcManager && !rtcManagerInitialized) {
|
|
2010
|
+
return true;
|
|
2011
|
+
}
|
|
2012
|
+
return false;
|
|
2013
|
+
});
|
|
2014
|
+
createReactor([selectShouldInitializeRtc], ({ dispatch }, shouldInitializeRtc) => {
|
|
2015
|
+
if (shouldInitializeRtc) {
|
|
2016
|
+
dispatch(doRtcManagerInitialize());
|
|
2017
|
+
}
|
|
2018
|
+
});
|
|
2019
|
+
const selectShouldDisconnectRtc = createSelector(selectRtcStatus, selectAppIsActive, (status, appIsActive) => {
|
|
2020
|
+
if (!appIsActive && !["inactive", "disconnected"].includes(status)) {
|
|
2021
|
+
return true;
|
|
2022
|
+
}
|
|
2023
|
+
return false;
|
|
2024
|
+
});
|
|
2025
|
+
createReactor([selectShouldDisconnectRtc], ({ dispatch }, shouldDisconnectRtc) => {
|
|
2026
|
+
if (shouldDisconnectRtc) {
|
|
2027
|
+
dispatch(doDisconnectRtc());
|
|
2028
|
+
}
|
|
2029
|
+
});
|
|
2030
|
+
const selectStreamsToAccept = createSelector(selectRtcStatus, selectRemoteClients, (rtcStatus, remoteParticipants) => {
|
|
2031
|
+
if (rtcStatus !== "ready") {
|
|
2032
|
+
return [];
|
|
2033
|
+
}
|
|
2034
|
+
const upd = [];
|
|
2035
|
+
for (const client of remoteParticipants) {
|
|
2036
|
+
const { streams, id: clientId, newJoiner } = client;
|
|
2037
|
+
for (let i = 0; i < streams.length; i++) {
|
|
2038
|
+
let streamId = streams[i].id;
|
|
2039
|
+
let state = streams[i].state;
|
|
2040
|
+
if ((streams === null || streams === void 0 ? void 0 : streams.length) > 1 && streams[1].id === "0") {
|
|
2041
|
+
if (i === 0) {
|
|
2042
|
+
streamId = streams[1].id;
|
|
2043
|
+
state = streams[1].state;
|
|
2044
|
+
}
|
|
2045
|
+
else if (i === 1) {
|
|
2046
|
+
streamId = streams[0].id;
|
|
2047
|
+
state = streams[0].state;
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
{
|
|
2051
|
+
if (state === "done_accept")
|
|
2052
|
+
continue;
|
|
2053
|
+
upd.push({
|
|
2054
|
+
clientId,
|
|
2055
|
+
streamId,
|
|
2056
|
+
state: `${newJoiner && streamId === "0" ? "new" : "to"}_accept`,
|
|
2057
|
+
});
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
return upd;
|
|
2062
|
+
});
|
|
2063
|
+
createReactor([selectStreamsToAccept, selectIsAcceptingStreams], ({ dispatch }, streamsToAccept, isAcceptingStreams) => {
|
|
2064
|
+
if (0 < streamsToAccept.length && !isAcceptingStreams) {
|
|
2065
|
+
dispatch(doHandleAcceptStreams(streamsToAccept));
|
|
2066
|
+
}
|
|
2067
|
+
});
|
|
2068
|
+
|
|
2069
|
+
function isStreamerClient(client) {
|
|
2070
|
+
return client.roleName === "streamer";
|
|
2071
|
+
}
|
|
2072
|
+
function isRecorderClient(client) {
|
|
2073
|
+
return client.roleName === "recorder";
|
|
2074
|
+
}
|
|
2075
|
+
const initialState$5 = {
|
|
2076
|
+
isLocked: false,
|
|
2077
|
+
};
|
|
2078
|
+
const roomSlice = createSlice({
|
|
2079
|
+
name: "room",
|
|
2080
|
+
initialState: initialState$5,
|
|
2081
|
+
reducers: {},
|
|
2082
|
+
extraReducers: (builder) => {
|
|
2083
|
+
builder.addCase(signalEvents.roomJoined, (state, action) => {
|
|
2084
|
+
const { error, isLocked } = action.payload;
|
|
2085
|
+
if (error) {
|
|
2086
|
+
return state;
|
|
2087
|
+
}
|
|
2088
|
+
return Object.assign(Object.assign({}, state), { isLocked: Boolean(isLocked) });
|
|
2089
|
+
});
|
|
2090
|
+
builder.addCase(signalEvents.roomLocked, (state, action) => {
|
|
2091
|
+
const { isLocked } = action.payload;
|
|
2092
|
+
return Object.assign(Object.assign({}, state), { isLocked: Boolean(isLocked) });
|
|
2093
|
+
});
|
|
2094
|
+
},
|
|
2095
|
+
});
|
|
2096
|
+
const doLockRoom = createAppAuthorizedThunk((state) => selectIsAuthorizedToLockRoom(state), (payload) => (_, getState) => {
|
|
2097
|
+
const state = getState();
|
|
2098
|
+
const { socket } = selectSignalConnectionRaw(state);
|
|
2099
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("set_lock", { locked: payload.locked });
|
|
2100
|
+
});
|
|
2101
|
+
const doKickParticipant = createAppAuthorizedThunk((state) => selectIsAuthorizedToKickClient(state), (payload) => (_, getState) => {
|
|
2102
|
+
const state = getState();
|
|
2103
|
+
const { socket } = selectSignalConnectionRaw(state);
|
|
2104
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("kick_client", { clientId: payload.clientId, reasonId: "kick" });
|
|
2105
|
+
});
|
|
2106
|
+
const doEndMeeting = createAppAuthorizedThunk((state) => selectIsAuthorizedToEndMeeting(state), (payload) => (dispatch, getState) => {
|
|
2107
|
+
const state = getState();
|
|
2108
|
+
const clientsToKick = selectRemoteClients(state).map((c) => c.id);
|
|
2109
|
+
if (clientsToKick.length) {
|
|
2110
|
+
const { socket } = selectSignalConnectionRaw(state);
|
|
2111
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("kick_client", { clientIds: clientsToKick, reasonId: "end-meeting" });
|
|
2112
|
+
}
|
|
2113
|
+
if (!payload.stayBehind) {
|
|
2114
|
+
dispatch(doAppStop());
|
|
2115
|
+
}
|
|
2116
|
+
});
|
|
2117
|
+
const selectRoomIsLocked = (state) => state.room.isLocked;
|
|
2118
|
+
const selectScreenshares = createSelector(selectLocalScreenshareStream, selectLocalParticipantRaw, selectRemoteParticipants, (localScreenshareStream, localParticipant, remoteParticipants) => {
|
|
2119
|
+
const screenshares = [];
|
|
2120
|
+
if (localScreenshareStream) {
|
|
2121
|
+
screenshares.push({
|
|
2122
|
+
id: localScreenshareStream.id || "local-screenshare",
|
|
2123
|
+
participantId: "local",
|
|
2124
|
+
hasAudioTrack: localScreenshareStream.getTracks().some((track) => track.kind === "audio"),
|
|
2125
|
+
breakoutGroup: localParticipant.breakoutGroup,
|
|
2126
|
+
stream: localScreenshareStream,
|
|
2127
|
+
isLocal: true,
|
|
2128
|
+
});
|
|
2129
|
+
}
|
|
2130
|
+
for (const participant of remoteParticipants) {
|
|
2131
|
+
if (participant.presentationStream) {
|
|
2132
|
+
screenshares.push({
|
|
2133
|
+
id: participant.presentationStream.id || `pres-${participant.id}`,
|
|
2134
|
+
participantId: participant.id,
|
|
2135
|
+
hasAudioTrack: participant.presentationStream.getTracks().some((track) => track.kind === "audio"),
|
|
2136
|
+
breakoutGroup: participant.breakoutGroup,
|
|
2137
|
+
stream: participant.presentationStream,
|
|
2138
|
+
isLocal: false,
|
|
2139
|
+
});
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
return screenshares;
|
|
2143
|
+
});
|
|
2144
|
+
const selectRemoteClientViews = createSelector(selectLocalScreenshareStream, selectLocalParticipantRaw, selectRemoteParticipants, (localScreenshareStream, localParticipant, remoteParticipants) => {
|
|
2145
|
+
const views = [];
|
|
2146
|
+
if (localScreenshareStream) {
|
|
2147
|
+
const isScreenshareAudioEnabled = !!localScreenshareStream.getAudioTracks().length;
|
|
2148
|
+
views.push({
|
|
2149
|
+
clientId: localParticipant.id,
|
|
2150
|
+
displayName: "Your screenshare",
|
|
2151
|
+
id: "local-screenshare",
|
|
2152
|
+
isAudioEnabled: isScreenshareAudioEnabled,
|
|
2153
|
+
isLocalClient: true,
|
|
2154
|
+
isPresentation: true,
|
|
2155
|
+
isVideoEnabled: true,
|
|
2156
|
+
stream: localScreenshareStream,
|
|
2157
|
+
});
|
|
2158
|
+
}
|
|
2159
|
+
for (const c of remoteParticipants) {
|
|
2160
|
+
if (isStreamerClient(c) || isRecorderClient(c)) {
|
|
2161
|
+
continue;
|
|
2162
|
+
}
|
|
2163
|
+
const { presentationStream } = c, clientView = __rest(c, ["presentationStream"]);
|
|
2164
|
+
const displayName = c.displayName || "Guest";
|
|
2165
|
+
const isPresentationActive = presentationStream && presentationStream.active;
|
|
2166
|
+
const presentationId = "pres-" + c.id;
|
|
2167
|
+
const isStreamActive = c.stream && c.stream.active;
|
|
2168
|
+
const isVideoEnabled = c.isVideoEnabled;
|
|
2169
|
+
views.push(Object.assign(Object.assign(Object.assign({}, clientView), { clientId: c.id, displayName, hasActivePresentation: !!isPresentationActive }), (c.isVideoEnabled ? { isVideoEnabled } : {})));
|
|
2170
|
+
if (isPresentationActive) {
|
|
2171
|
+
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 })));
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
return views;
|
|
2175
|
+
});
|
|
2176
|
+
const selectAllClientViews = createSelector(selectLocalParticipantView, selectRemoteClientViews, (localParticipant, remoteParticipants) => {
|
|
2177
|
+
return [...(localParticipant ? [localParticipant] : []), ...remoteParticipants];
|
|
2178
|
+
});
|
|
2179
|
+
|
|
2180
|
+
const initialState$4 = {
|
|
2181
|
+
running: false,
|
|
2182
|
+
};
|
|
2183
|
+
const connectionMonitorSlice = createSlice({
|
|
2184
|
+
name: "connectionMonitor",
|
|
2185
|
+
initialState: initialState$4,
|
|
2186
|
+
reducers: {
|
|
2187
|
+
connectionMonitorStarted: (state, action) => {
|
|
2188
|
+
return Object.assign(Object.assign({}, state), { running: true, stopCallbackFunction: action.payload.stopIssueSubscription });
|
|
2189
|
+
},
|
|
2190
|
+
connectionMonitorStopped: () => {
|
|
2191
|
+
return Object.assign({}, initialState$4);
|
|
2192
|
+
},
|
|
2193
|
+
},
|
|
2194
|
+
});
|
|
2195
|
+
const { connectionMonitorStarted, connectionMonitorStopped } = connectionMonitorSlice.actions;
|
|
2196
|
+
const doStartConnectionMonitor = createAppThunk(() => (dispatch, getState) => {
|
|
2197
|
+
setClientProvider(() => {
|
|
2198
|
+
const state = getState();
|
|
2199
|
+
const clientViews = selectAllClientViews(state).map((clientView) => {
|
|
2200
|
+
var _a, _b;
|
|
2201
|
+
return ({
|
|
2202
|
+
id: clientView.id,
|
|
2203
|
+
clientId: clientView.clientId,
|
|
2204
|
+
isLocalClient: clientView.isLocalClient,
|
|
2205
|
+
audio: {
|
|
2206
|
+
enabled: clientView.isAudioEnabled,
|
|
2207
|
+
track: (_a = clientView.stream) === null || _a === void 0 ? void 0 : _a.getAudioTracks()[0],
|
|
2208
|
+
},
|
|
2209
|
+
video: {
|
|
2210
|
+
enabled: clientView.isVideoEnabled,
|
|
2211
|
+
track: (_b = clientView.stream) === null || _b === void 0 ? void 0 : _b.getVideoTracks()[0],
|
|
2212
|
+
},
|
|
2213
|
+
isPresentation: clientView.isPresentation,
|
|
2214
|
+
});
|
|
2215
|
+
});
|
|
2216
|
+
return clientViews;
|
|
2217
|
+
});
|
|
2218
|
+
const issueMonitorSubscription = subscribeIssues({
|
|
2219
|
+
onUpdatedIssues: (issuesAndMetricsByClients) => {
|
|
2220
|
+
var _a;
|
|
2221
|
+
const state = getState();
|
|
2222
|
+
const rtcManager = selectRtcManager(state);
|
|
2223
|
+
if (!rtcManager) {
|
|
2224
|
+
return;
|
|
2225
|
+
}
|
|
2226
|
+
let lossSend = 0;
|
|
2227
|
+
let lossReceive = 0;
|
|
2228
|
+
let bitrateSend = 0;
|
|
2229
|
+
let bitrateReceive = 0;
|
|
2230
|
+
Object.entries(issuesAndMetricsByClients.aggregated.metrics).forEach(([key, value]) => {
|
|
2231
|
+
if (/loc.*packetloss/.test(key))
|
|
2232
|
+
lossSend = Math.max(lossSend, value.curMax);
|
|
2233
|
+
if (/rem.*packetloss/.test(key))
|
|
2234
|
+
lossReceive = Math.max(lossReceive, value.curMax);
|
|
2235
|
+
if (/loc.*bitrate/.test(key))
|
|
2236
|
+
bitrateSend += value.curSum;
|
|
2237
|
+
if (/rem.*bitrate/.test(key))
|
|
2238
|
+
bitrateReceive += value.curSum;
|
|
2239
|
+
});
|
|
2240
|
+
rtcManager.sendStatsCustomEvent("insightsStats", {
|
|
2241
|
+
ls: Math.round(lossSend * 1000) / 1000,
|
|
2242
|
+
lr: Math.round(lossReceive * 1000) / 1000,
|
|
2243
|
+
bs: Math.round(bitrateSend),
|
|
2244
|
+
br: Math.round(bitrateReceive),
|
|
2245
|
+
cpu: (_a = issuesAndMetricsByClients.aggregated.metrics["global-cpu-pressure"]) === null || _a === void 0 ? void 0 : _a.curSum,
|
|
2246
|
+
_time: Date.now(),
|
|
2247
|
+
});
|
|
2248
|
+
},
|
|
2249
|
+
});
|
|
2250
|
+
dispatch(connectionMonitorStarted({ stopIssueSubscription: issueMonitorSubscription.stop }));
|
|
2251
|
+
});
|
|
2252
|
+
const doStopConnectionMonitor = createAppThunk(() => (dispatch, getState) => {
|
|
2253
|
+
const state = getState();
|
|
2254
|
+
const stopCallbackFn = selectStopCallbackFunction(state);
|
|
2255
|
+
if (stopCallbackFn) {
|
|
2256
|
+
stopCallbackFn();
|
|
2257
|
+
}
|
|
2258
|
+
dispatch(connectionMonitorStopped());
|
|
2259
|
+
});
|
|
2260
|
+
const selectConnectionMonitorIsRunning = (state) => state.connectionMonitor.running;
|
|
2261
|
+
const selectStopCallbackFunction = (state) => state.connectionMonitor.stopCallbackFunction;
|
|
2262
|
+
const selectShouldStartConnectionMonitor = createSelector(selectRoomConnectionStatus, selectConnectionMonitorIsRunning, (roomConnectionStatus, isRunning) => {
|
|
2263
|
+
if (!isRunning && roomConnectionStatus === "connected") {
|
|
2264
|
+
return true;
|
|
2265
|
+
}
|
|
2266
|
+
return false;
|
|
2267
|
+
});
|
|
2268
|
+
const selectShouldStopConnectionMonitor = createSelector(selectRoomConnectionStatus, selectConnectionMonitorIsRunning, (roomConnectionStatus, isRunning) => {
|
|
2269
|
+
if (isRunning && ["kicked", "left"].includes(roomConnectionStatus)) {
|
|
2270
|
+
return true;
|
|
2271
|
+
}
|
|
2272
|
+
return false;
|
|
2273
|
+
});
|
|
2274
|
+
createReactor([selectShouldStartConnectionMonitor], ({ dispatch }, shouldStartConnectionMonitor) => {
|
|
2275
|
+
if (shouldStartConnectionMonitor) {
|
|
2276
|
+
dispatch(doStartConnectionMonitor());
|
|
2277
|
+
}
|
|
2278
|
+
});
|
|
2279
|
+
createReactor([selectShouldStopConnectionMonitor], ({ dispatch }, shouldStartConnectionMonitor) => {
|
|
2280
|
+
if (shouldStartConnectionMonitor) {
|
|
2281
|
+
dispatch(doStopConnectionMonitor());
|
|
2282
|
+
}
|
|
2283
|
+
});
|
|
2284
|
+
|
|
2285
|
+
const emitter = new EventEmitter();
|
|
2286
|
+
function createNotificationEvent(payload) {
|
|
2287
|
+
const notificationEvent = Object.assign(Object.assign({}, payload), { timestamp: Date.now() });
|
|
2288
|
+
return notificationEvent;
|
|
2289
|
+
}
|
|
2290
|
+
const initialNotificationsState = {
|
|
2291
|
+
emitter,
|
|
2292
|
+
events: [],
|
|
2293
|
+
};
|
|
2294
|
+
const notificationsSlice = createSlice({
|
|
2295
|
+
name: "notifications",
|
|
2296
|
+
initialState: initialNotificationsState,
|
|
2297
|
+
reducers: {
|
|
2298
|
+
addNotification: (state, action) => {
|
|
2299
|
+
return Object.assign(Object.assign({}, state), { events: [...state.events, Object.assign({}, action.payload)] });
|
|
2300
|
+
},
|
|
2301
|
+
doClearNotifications: (state) => {
|
|
2302
|
+
return Object.assign(Object.assign({}, state), { events: [] });
|
|
2303
|
+
},
|
|
2304
|
+
},
|
|
2305
|
+
});
|
|
2306
|
+
const { doClearNotifications } = notificationsSlice.actions;
|
|
2307
|
+
const doSetNotification = createAppThunk((payload) => (dispatch, getState) => {
|
|
2308
|
+
dispatch(notificationsSlice.actions.addNotification(payload));
|
|
2309
|
+
const state = getState();
|
|
2310
|
+
const emitter = selectNotificationsEmitter(state);
|
|
2311
|
+
emitter.emit(payload.type, payload);
|
|
2312
|
+
emitter.emit("*", payload);
|
|
2313
|
+
});
|
|
2314
|
+
const selectNotificationsRaw = (state) => state.notifications;
|
|
2315
|
+
const selectNotificationsEvents = (state) => state.notifications.events;
|
|
2316
|
+
const selectNotificationsEmitter = (state) => state.notifications.emitter;
|
|
2317
|
+
startAppListening({
|
|
2318
|
+
actionCreator: signalEvents.chatMessage,
|
|
2319
|
+
effect: ({ payload }, { dispatch, getState }) => {
|
|
2320
|
+
const state = getState();
|
|
2321
|
+
const client = selectRemoteParticipants(state).find(({ id }) => id === payload.senderId);
|
|
2322
|
+
if (!client) {
|
|
2323
|
+
console.warn("Could not find remote client that sent chat message");
|
|
2324
|
+
return;
|
|
2325
|
+
}
|
|
2326
|
+
dispatch(doSetNotification(createNotificationEvent({
|
|
2327
|
+
type: "chatMessageReceived",
|
|
2328
|
+
message: `${client.displayName} says: ${payload.text}`,
|
|
2329
|
+
props: {
|
|
2330
|
+
client,
|
|
2331
|
+
chatMessage: {
|
|
2332
|
+
senderId: payload.senderId,
|
|
2333
|
+
timestamp: payload.timestamp,
|
|
2334
|
+
text: payload.text,
|
|
2335
|
+
},
|
|
2336
|
+
},
|
|
2337
|
+
})));
|
|
2338
|
+
},
|
|
2339
|
+
});
|
|
2340
|
+
startAppListening({
|
|
2341
|
+
actionCreator: signalEvents.audioEnableRequested,
|
|
2342
|
+
effect: ({ payload }, { dispatch, getState }) => {
|
|
2343
|
+
const { enable, requestedByClientId } = payload;
|
|
2344
|
+
const state = getState();
|
|
2345
|
+
const client = selectRemoteParticipants(state).find(({ id }) => id === requestedByClientId);
|
|
2346
|
+
if (!client) {
|
|
2347
|
+
console.warn("Could not find remote client that requested a local audio change");
|
|
2348
|
+
return;
|
|
2349
|
+
}
|
|
2350
|
+
dispatch(doSetNotification(createNotificationEvent({
|
|
2351
|
+
type: enable ? "requestAudioEnable" : "requestAudioDisable",
|
|
2352
|
+
message: enable
|
|
2353
|
+
? `${client.displayName} has requested for you to speak`
|
|
2354
|
+
: `${client.displayName} has muted your microphone`,
|
|
2355
|
+
props: {
|
|
2356
|
+
client,
|
|
2357
|
+
enable,
|
|
2358
|
+
},
|
|
2359
|
+
})));
|
|
2360
|
+
},
|
|
2361
|
+
});
|
|
2362
|
+
startAppListening({
|
|
2363
|
+
actionCreator: signalEvents.videoEnableRequested,
|
|
2364
|
+
effect: ({ payload }, { dispatch, getState }) => {
|
|
2365
|
+
const { enable, requestedByClientId } = payload;
|
|
2366
|
+
const state = getState();
|
|
2367
|
+
const client = selectRemoteParticipants(state).find(({ id }) => id === requestedByClientId);
|
|
2368
|
+
if (!client) {
|
|
2369
|
+
console.warn("Could not find remote client that requested a local video change");
|
|
2370
|
+
return;
|
|
2371
|
+
}
|
|
2372
|
+
dispatch(doSetNotification(createNotificationEvent({
|
|
2373
|
+
type: enable ? "requestVideoEnable" : "requestVideoDisable",
|
|
2374
|
+
message: enable
|
|
2375
|
+
? `${client.displayName} has requested for you to start video`
|
|
2376
|
+
: `${client.displayName} has stopped your video`,
|
|
2377
|
+
props: {
|
|
2378
|
+
client,
|
|
2379
|
+
enable,
|
|
2380
|
+
},
|
|
2381
|
+
})));
|
|
2382
|
+
},
|
|
2383
|
+
});
|
|
2384
|
+
startAppListening({
|
|
2385
|
+
actionCreator: signalEvents.clientMetadataReceived,
|
|
2386
|
+
effect: (action, { dispatch, getOriginalState, getState }) => {
|
|
2387
|
+
var _a;
|
|
2388
|
+
const { error, payload } = action.payload;
|
|
2389
|
+
if (error || !payload) {
|
|
2390
|
+
return;
|
|
2391
|
+
}
|
|
2392
|
+
const { clientId, stickyReaction } = payload;
|
|
2393
|
+
const state = getState();
|
|
2394
|
+
const canAskToSpeak = selectIsAuthorizedToAskToSpeak(state);
|
|
2395
|
+
if (!canAskToSpeak) {
|
|
2396
|
+
return;
|
|
2397
|
+
}
|
|
2398
|
+
const client = selectRemoteParticipants(state).find(({ id }) => id === clientId);
|
|
2399
|
+
if (!client) {
|
|
2400
|
+
console.warn("Could not find remote client that provided updated metadata");
|
|
2401
|
+
return;
|
|
2402
|
+
}
|
|
2403
|
+
const previousState = getOriginalState();
|
|
2404
|
+
const previousClient = selectRemoteParticipants(previousState).find(({ id }) => id === clientId);
|
|
2405
|
+
if ((!stickyReaction && !(previousClient === null || previousClient === void 0 ? void 0 : previousClient.stickyReaction)) ||
|
|
2406
|
+
(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)) {
|
|
2407
|
+
return;
|
|
2408
|
+
}
|
|
2409
|
+
dispatch(doSetNotification(createNotificationEvent({
|
|
2410
|
+
type: stickyReaction ? "remoteHandRaised" : "remoteHandLowered",
|
|
2411
|
+
message: `${client.displayName} ${stickyReaction ? "raised" : "lowered"} their hand`,
|
|
2412
|
+
props: {
|
|
2413
|
+
client,
|
|
2414
|
+
stickyReaction,
|
|
2415
|
+
},
|
|
2416
|
+
})));
|
|
2417
|
+
},
|
|
2418
|
+
});
|
|
2419
|
+
createReactor([selectSignalStatus], ({ dispatch, getState }, signalStatus) => {
|
|
2420
|
+
const state = getState();
|
|
2421
|
+
const roomConnectionStatus = selectRoomConnectionStatus(state);
|
|
2422
|
+
if (["left", "kicked"].includes(roomConnectionStatus)) {
|
|
2423
|
+
return;
|
|
2424
|
+
}
|
|
2425
|
+
if (signalStatus === "disconnected") {
|
|
2426
|
+
dispatch(doSetNotification(createNotificationEvent({
|
|
2427
|
+
type: "signalTrouble",
|
|
2428
|
+
message: `Network connection lost. Trying to reconnect you...`,
|
|
2429
|
+
props: {},
|
|
2430
|
+
})));
|
|
2431
|
+
}
|
|
2432
|
+
else if (signalStatus === "connected") {
|
|
2433
|
+
dispatch(doSetNotification(createNotificationEvent({
|
|
2434
|
+
type: "signalOk",
|
|
2435
|
+
message: `Network connection available`,
|
|
2436
|
+
props: {},
|
|
2437
|
+
})));
|
|
2438
|
+
}
|
|
2439
|
+
});
|
|
2440
|
+
startAppListening({
|
|
2441
|
+
actionCreator: signalEvents.clientUnableToJoin,
|
|
2442
|
+
effect: (action, { dispatch }) => {
|
|
2443
|
+
var _a;
|
|
2444
|
+
if (((_a = action.payload) === null || _a === void 0 ? void 0 : _a.error) === "room_full") {
|
|
2445
|
+
dispatch(doSetNotification(createNotificationEvent({
|
|
2446
|
+
type: "clientUnableToJoinFullRoom",
|
|
2447
|
+
message: "Someone tried to join but the room is full and at capacity.",
|
|
2448
|
+
props: {},
|
|
2449
|
+
})));
|
|
2450
|
+
}
|
|
2451
|
+
},
|
|
2452
|
+
});
|
|
2453
|
+
|
|
2454
|
+
const rtcAnalyticsCustomEvents = {
|
|
2455
|
+
audioEnabled: {
|
|
2456
|
+
actions: [doEnableAudio.fulfilled],
|
|
2457
|
+
rtcEventName: "audioEnabled",
|
|
2458
|
+
getValue: (state) => selectIsMicrophoneEnabled(state),
|
|
2459
|
+
getOutput: (value) => ({ enabled: value }),
|
|
2460
|
+
},
|
|
2461
|
+
videoEnabled: {
|
|
2462
|
+
actions: [doEnableVideo.fulfilled],
|
|
2463
|
+
rtcEventName: "videoEnabled",
|
|
2464
|
+
getValue: (state) => selectIsCameraEnabled(state),
|
|
2465
|
+
getOutput: (value) => ({ enabled: value }),
|
|
2466
|
+
},
|
|
2467
|
+
localStream: {
|
|
2468
|
+
actions: [doSetDevice.fulfilled],
|
|
2469
|
+
rtcEventName: "localStream",
|
|
2470
|
+
getValue: (state) => {
|
|
2471
|
+
var _a;
|
|
2472
|
+
return (_a = selectLocalMediaStream(state)) === null || _a === void 0 ? void 0 : _a.getTracks().map((track) => ({ id: track.id, kind: track.kind, label: track.label }));
|
|
2473
|
+
},
|
|
2474
|
+
getOutput: (value) => ({ stream: value }),
|
|
2475
|
+
},
|
|
2476
|
+
localScreenshareStream: {
|
|
2477
|
+
actions: [doStartScreenshare.fulfilled],
|
|
2478
|
+
rtcEventName: "localScreenshareStream",
|
|
2479
|
+
getValue: (state) => {
|
|
2480
|
+
var _a;
|
|
2481
|
+
return (_a = selectLocalScreenshareStream(state)) === null || _a === void 0 ? void 0 : _a.getTracks().map((track) => ({ id: track.id, kind: track.kind, label: track.label }));
|
|
2482
|
+
},
|
|
2483
|
+
getOutput: (value) => ({ tracks: value }),
|
|
2484
|
+
},
|
|
2485
|
+
localScreenshareStreamStopped: {
|
|
2486
|
+
actions: [stopScreenshare],
|
|
2487
|
+
rtcEventName: "localScreenshareStream",
|
|
2488
|
+
getValue: () => () => null,
|
|
2489
|
+
getOutput: () => ({}),
|
|
2490
|
+
},
|
|
2491
|
+
displayName: {
|
|
2492
|
+
actions: [setDisplayName],
|
|
2493
|
+
rtcEventName: "displayName",
|
|
2494
|
+
getValue: (state) => selectAppDisplayName(state),
|
|
2495
|
+
getOutput: (value) => ({ displayName: value }),
|
|
2496
|
+
},
|
|
2497
|
+
clientId: {
|
|
2498
|
+
actions: null,
|
|
2499
|
+
rtcEventName: "clientId",
|
|
2500
|
+
getValue: (state) => selectSelfId(state),
|
|
2501
|
+
getOutput: (value) => ({ clientId: value }),
|
|
2502
|
+
},
|
|
2503
|
+
deviceId: {
|
|
2504
|
+
actions: null,
|
|
2505
|
+
rtcEventName: "deviceId",
|
|
2506
|
+
getValue: (state) => selectDeviceId(state),
|
|
2507
|
+
getOutput: (value) => ({ deviceId: value }),
|
|
2508
|
+
},
|
|
2509
|
+
externalId: {
|
|
2510
|
+
actions: null,
|
|
2511
|
+
rtcEventName: "externalId",
|
|
2512
|
+
getValue: (state) => selectAppExternalId(state),
|
|
2513
|
+
getOutput: (value) => ({ externalId: value }),
|
|
2514
|
+
},
|
|
2515
|
+
organizationId: {
|
|
2516
|
+
actions: null,
|
|
2517
|
+
rtcEventName: "organizationId",
|
|
2518
|
+
getValue: (state) => selectOrganizationId(state),
|
|
2519
|
+
getOutput: (value) => ({ organizationId: value }),
|
|
2520
|
+
},
|
|
2521
|
+
signalConnectionStatus: {
|
|
2522
|
+
actions: null,
|
|
2523
|
+
rtcEventName: "signalConnectionStatus",
|
|
2524
|
+
getValue: (state) => selectSignalStatus(state),
|
|
2525
|
+
getOutput: (value) => ({ status: value }),
|
|
2526
|
+
},
|
|
2527
|
+
roomSessionId: {
|
|
2528
|
+
actions: [
|
|
2529
|
+
signalEvents.newClient,
|
|
2530
|
+
signalEvents.roomJoined,
|
|
2531
|
+
signalEvents.roomSessionEnded,
|
|
2532
|
+
signalEvents.clientLeft,
|
|
2533
|
+
],
|
|
2534
|
+
rtcEventName: "roomSessionId",
|
|
2535
|
+
getValue: (state) => selectRoomConnectionSessionId(state),
|
|
2536
|
+
getOutput: (value) => ({ roomSessionId: value }),
|
|
2537
|
+
},
|
|
2538
|
+
rtcConnectionStatus: {
|
|
2539
|
+
actions: null,
|
|
2540
|
+
rtcEventName: "rtcConnectionStatus",
|
|
2541
|
+
getValue: (state) => selectRtcStatus(state),
|
|
2542
|
+
getOutput: (value) => ({ status: value }),
|
|
2543
|
+
},
|
|
2544
|
+
userRole: {
|
|
2545
|
+
actions: null,
|
|
2546
|
+
rtcEventName: "userRole",
|
|
2547
|
+
getValue: (state) => selectAuthorizationRoleName(state),
|
|
2548
|
+
getOutput: (value) => ({ userRole: value }),
|
|
2549
|
+
},
|
|
2550
|
+
};
|
|
2551
|
+
const rtcCustomEventActions = Object.values(rtcAnalyticsCustomEvents)
|
|
2552
|
+
.flatMap(({ actions }) => { var _a; return (_a = actions === null || actions === void 0 ? void 0 : actions.map((action) => action)) !== null && _a !== void 0 ? _a : null; })
|
|
2553
|
+
.filter((action) => action !== null);
|
|
2554
|
+
const makeComparable = (value) => {
|
|
2555
|
+
if (typeof value === "object")
|
|
2556
|
+
return JSON.stringify(value);
|
|
2557
|
+
return value;
|
|
2558
|
+
};
|
|
2559
|
+
const initialState$3 = {
|
|
2560
|
+
reportedValues: {},
|
|
2561
|
+
};
|
|
2562
|
+
const rtcAnalyticsSlice = createSlice({
|
|
2563
|
+
initialState: initialState$3,
|
|
2564
|
+
name: "rtcAnalytics",
|
|
2565
|
+
reducers: {
|
|
2566
|
+
updateReportedValues(state, action) {
|
|
2567
|
+
return Object.assign(Object.assign({}, state), { reportedValues: Object.assign(Object.assign({}, state.reportedValues), { [action.payload.rtcEventName]: action.payload.value }) });
|
|
2568
|
+
},
|
|
2569
|
+
},
|
|
2570
|
+
});
|
|
2571
|
+
const doRtcAnalyticsCustomEventsInitialize = createAppThunk(() => (dispatch, getState) => {
|
|
2572
|
+
const state = getState();
|
|
2573
|
+
const rtcManager = selectRtcConnectionRaw(state).rtcManager;
|
|
2574
|
+
if (!rtcManager)
|
|
2575
|
+
return;
|
|
2576
|
+
rtcManager.sendStatsCustomEvent("insightsStats", {
|
|
2577
|
+
_time: Date.now(),
|
|
2578
|
+
ls: 0,
|
|
2579
|
+
lr: 0,
|
|
2580
|
+
bs: 0,
|
|
2581
|
+
br: 0,
|
|
2582
|
+
cpu: 0,
|
|
2583
|
+
});
|
|
2584
|
+
Object.values(rtcAnalyticsCustomEvents).forEach(({ rtcEventName, getValue, getOutput }) => {
|
|
2585
|
+
var _a;
|
|
2586
|
+
const value = getValue(state);
|
|
2587
|
+
const output = Object.assign(Object.assign({}, getOutput(value)), { _time: Date.now() });
|
|
2588
|
+
const comparableValue = makeComparable(value);
|
|
2589
|
+
if (((_a = state.rtcAnalytics.reportedValues) === null || _a === void 0 ? void 0 : _a[rtcEventName]) !== comparableValue) {
|
|
2590
|
+
rtcManager.sendStatsCustomEvent(rtcEventName, output);
|
|
2591
|
+
dispatch(updateReportedValues({ rtcEventName, value }));
|
|
2592
|
+
}
|
|
2593
|
+
});
|
|
2594
|
+
});
|
|
2595
|
+
const { updateReportedValues } = rtcAnalyticsSlice.actions;
|
|
2596
|
+
startAppListening({
|
|
2597
|
+
matcher: isAnyOf(...rtcCustomEventActions),
|
|
2598
|
+
effect: ({ type }, { getState, dispatch }) => {
|
|
2599
|
+
var _a;
|
|
2600
|
+
const state = getState();
|
|
2601
|
+
const rtcManager = selectRtcConnectionRaw(state).rtcManager;
|
|
2602
|
+
if (!rtcManager)
|
|
2603
|
+
return;
|
|
2604
|
+
const rtcCustomEvent = Object.values(rtcAnalyticsCustomEvents).find(({ actions }) => actions === null || actions === void 0 ? void 0 : actions.map((a) => a.type).includes(type));
|
|
2605
|
+
if (!rtcCustomEvent)
|
|
2606
|
+
return;
|
|
2607
|
+
const { getValue, getOutput, rtcEventName } = rtcCustomEvent;
|
|
2608
|
+
const value = getValue(state);
|
|
2609
|
+
const comparableValue = makeComparable(value);
|
|
2610
|
+
const output = Object.assign(Object.assign({}, getOutput(value)), { _time: Date.now() });
|
|
2611
|
+
if (((_a = state.rtcAnalytics.reportedValues) === null || _a === void 0 ? void 0 : _a[rtcEventName]) !== comparableValue) {
|
|
2612
|
+
rtcManager.sendStatsCustomEvent(rtcEventName, output);
|
|
2613
|
+
dispatch(updateReportedValues({ rtcEventName, value }));
|
|
2614
|
+
}
|
|
2615
|
+
},
|
|
2616
|
+
});
|
|
2617
|
+
createReactor([selectRtcManagerInitialized], ({ dispatch }, selectRtcManagerInitialized) => {
|
|
2618
|
+
if (selectRtcManagerInitialized) {
|
|
2619
|
+
dispatch(doRtcAnalyticsCustomEventsInitialize());
|
|
2620
|
+
}
|
|
2621
|
+
});
|
|
2622
|
+
|
|
2623
|
+
function streamIdForClient({ isPresentation, stream }) {
|
|
2624
|
+
var _a, _b;
|
|
2625
|
+
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";
|
|
2626
|
+
}
|
|
2627
|
+
function isClientSpotlighted({ spotlights, isPresentation, clientId, stream, }) {
|
|
2628
|
+
return !!spotlights.find((s) => {
|
|
2629
|
+
const streamId = streamIdForClient({ isPresentation, stream });
|
|
2630
|
+
return s.clientId === clientId && s.streamId === streamId;
|
|
2631
|
+
});
|
|
2632
|
+
}
|
|
2633
|
+
function mergeSpotlight(spotlights, spotlight) {
|
|
2634
|
+
const found = spotlights.find((s) => s.clientId === spotlight.clientId && s.streamId === spotlight.streamId);
|
|
2635
|
+
if (found) {
|
|
2636
|
+
return spotlights;
|
|
2637
|
+
}
|
|
2638
|
+
return spotlights.concat(spotlight);
|
|
2639
|
+
}
|
|
2640
|
+
function mapSpotlightsToClientViews(spotlights, clientViews) {
|
|
2641
|
+
return spotlights.reduce((acc, s) => {
|
|
2642
|
+
const clientView = clientViews.find((c) => s.clientId === c.clientId && s.streamId === streamIdForClient(c));
|
|
2643
|
+
if (clientView && !acc.includes(clientView)) {
|
|
2644
|
+
acc.push(clientView);
|
|
2645
|
+
}
|
|
2646
|
+
return acc;
|
|
2647
|
+
}, []);
|
|
2648
|
+
}
|
|
2649
|
+
const initialState$2 = {
|
|
2650
|
+
sorted: [],
|
|
2651
|
+
};
|
|
2652
|
+
const spotlightsSlice = createSlice({
|
|
2653
|
+
name: "spotlights",
|
|
2654
|
+
initialState: initialState$2,
|
|
2655
|
+
reducers: {
|
|
2656
|
+
addSpotlight(state, action) {
|
|
2657
|
+
const { clientId, streamId } = action.payload;
|
|
2658
|
+
return Object.assign(Object.assign({}, state), { sorted: mergeSpotlight(state.sorted, { clientId, streamId }) });
|
|
2659
|
+
},
|
|
2660
|
+
removeSpotlight(state, action) {
|
|
2661
|
+
const { clientId, streamId } = action.payload;
|
|
2662
|
+
return Object.assign(Object.assign({}, state), { sorted: state.sorted.filter((s) => !(s.clientId === clientId && s.streamId === streamId)) });
|
|
2663
|
+
},
|
|
2664
|
+
},
|
|
2665
|
+
extraReducers: (builder) => {
|
|
2666
|
+
builder.addCase(signalEvents.roomJoined, (state, action) => {
|
|
2667
|
+
if (!action.payload.room) {
|
|
2668
|
+
return state;
|
|
2669
|
+
}
|
|
2670
|
+
const { spotlights } = action.payload.room;
|
|
2671
|
+
return Object.assign(Object.assign({}, state), { sorted: spotlights });
|
|
2672
|
+
});
|
|
2673
|
+
builder.addCase(signalEvents.spotlightAdded, (state, action) => {
|
|
2674
|
+
const { clientId, streamId } = action.payload;
|
|
2675
|
+
return Object.assign(Object.assign({}, state), { sorted: mergeSpotlight(state.sorted, { clientId, streamId }) });
|
|
2676
|
+
});
|
|
2677
|
+
builder.addCase(signalEvents.spotlightRemoved, (state, action) => {
|
|
2678
|
+
const { clientId, streamId } = action.payload;
|
|
2679
|
+
return Object.assign(Object.assign({}, state), { sorted: state.sorted.filter((s) => !(s.clientId === clientId && s.streamId === streamId)) });
|
|
2680
|
+
});
|
|
2681
|
+
builder.addMatcher(isAnyOf(signalEvents.clientKicked, signalEvents.clientLeft), (state, action) => {
|
|
2682
|
+
const { clientId } = action.payload;
|
|
2683
|
+
return Object.assign(Object.assign({}, state), { sorted: state.sorted.filter((s) => s.clientId !== clientId) });
|
|
2684
|
+
});
|
|
2685
|
+
},
|
|
2686
|
+
});
|
|
2687
|
+
const { addSpotlight, removeSpotlight } = spotlightsSlice.actions;
|
|
2688
|
+
const doSpotlightParticipant = createAppAuthorizedThunk((state) => selectIsAuthorizedToSpotlight(state), ({ id }) => (_, getState) => {
|
|
2689
|
+
const state = getState();
|
|
2690
|
+
const clientView = selectAllClientViews(state).find((c) => c.clientId === id);
|
|
2691
|
+
if (!clientView) {
|
|
2692
|
+
return;
|
|
2693
|
+
}
|
|
2694
|
+
const { socket } = selectSignalConnectionRaw(state);
|
|
2695
|
+
const streamId = streamIdForClient(clientView);
|
|
2696
|
+
const payload = { clientId: clientView.id, streamId: streamId !== null && streamId !== void 0 ? streamId : "0" };
|
|
2697
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("add_spotlight", payload);
|
|
2698
|
+
});
|
|
2699
|
+
const doRemoveSpotlight = createAppAuthorizedThunk((state) => selectIsAuthorizedToSpotlight(state), ({ id }) => (_, getState) => {
|
|
2700
|
+
const state = getState();
|
|
2701
|
+
const clientView = selectAllClientViews(state).find((c) => c.clientId === id);
|
|
2702
|
+
if (!clientView) {
|
|
2703
|
+
return;
|
|
2704
|
+
}
|
|
2705
|
+
const { socket } = selectSignalConnectionRaw(state);
|
|
2706
|
+
const streamId = streamIdForClient(clientView);
|
|
2707
|
+
const payload = { clientId: clientView.id, streamId: streamId !== null && streamId !== void 0 ? streamId : "0" };
|
|
2708
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("remove_spotlight", payload);
|
|
2709
|
+
});
|
|
2710
|
+
const selectSpotlightsRaw = (state) => state.spotlights;
|
|
2711
|
+
const selectSpotlights = (state) => state.spotlights.sorted;
|
|
2712
|
+
const selectIsLocalParticipantSpotlighted = createSelector(selectLocalParticipantRaw, selectSpotlights, (localParticipant, spotlights) => {
|
|
2713
|
+
return isClientSpotlighted({ clientId: localParticipant.id, stream: localParticipant.stream, spotlights });
|
|
2714
|
+
});
|
|
2715
|
+
const selectSpotlightedClientViews = createSelector(selectAllClientViews, selectSpotlights, (clientViews, spotlights) => {
|
|
2716
|
+
return mapSpotlightsToClientViews(spotlights, clientViews);
|
|
2717
|
+
});
|
|
2718
|
+
startAppListening({
|
|
2719
|
+
actionCreator: doStartScreenshare.fulfilled,
|
|
2720
|
+
effect: ({ payload }, { getState, dispatch }) => {
|
|
2721
|
+
const { stream } = payload;
|
|
2722
|
+
const state = getState();
|
|
2723
|
+
const localParticipant = selectLocalParticipantRaw(state);
|
|
2724
|
+
if (!localParticipant) {
|
|
2725
|
+
return;
|
|
2726
|
+
}
|
|
2727
|
+
dispatch(addSpotlight({ clientId: localParticipant.id, streamId: stream.id }));
|
|
2728
|
+
},
|
|
2729
|
+
});
|
|
2730
|
+
startAppListening({
|
|
2731
|
+
actionCreator: stopScreenshare,
|
|
2732
|
+
effect: ({ payload }, { getState, dispatch }) => {
|
|
2733
|
+
const { stream } = payload;
|
|
2734
|
+
const state = getState();
|
|
2735
|
+
const localParticipant = selectLocalParticipantRaw(state);
|
|
2736
|
+
if (!localParticipant) {
|
|
2737
|
+
return;
|
|
2738
|
+
}
|
|
2739
|
+
dispatch(removeSpotlight({ clientId: localParticipant.id, streamId: stream.id }));
|
|
2740
|
+
},
|
|
2741
|
+
});
|
|
2742
|
+
|
|
2743
|
+
const initialState$1 = {
|
|
2744
|
+
isStreaming: false,
|
|
2745
|
+
error: null,
|
|
2746
|
+
startedAt: undefined,
|
|
2747
|
+
};
|
|
2748
|
+
const streamingSlice = createSlice({
|
|
2749
|
+
name: "streaming",
|
|
2750
|
+
initialState: initialState$1,
|
|
2751
|
+
reducers: {
|
|
2752
|
+
doHandleStreamingStarted: (state) => {
|
|
2753
|
+
return Object.assign(Object.assign({}, state), { isStreaming: true, error: null, startedAt: new Date().getTime() });
|
|
2754
|
+
},
|
|
2755
|
+
doHandleStreamingStopped: (state) => {
|
|
2756
|
+
return Object.assign(Object.assign({}, state), { isStreaming: false });
|
|
2757
|
+
},
|
|
2758
|
+
},
|
|
2759
|
+
});
|
|
2760
|
+
const { doHandleStreamingStarted, doHandleStreamingStopped } = streamingSlice.actions;
|
|
2761
|
+
const selectStreamingRaw = (state) => state.streaming;
|
|
2762
|
+
|
|
2763
|
+
const initialState = {
|
|
2764
|
+
waitingParticipants: [],
|
|
2765
|
+
};
|
|
2766
|
+
const waitingParticipantsSlice = createSlice({
|
|
2767
|
+
name: "waitingParticipants",
|
|
2768
|
+
initialState,
|
|
2769
|
+
reducers: {},
|
|
2770
|
+
extraReducers: (builder) => {
|
|
2771
|
+
builder.addCase(signalEvents.roomJoined, (state, { payload }) => {
|
|
2772
|
+
var _a;
|
|
2773
|
+
if ((_a = payload.room) === null || _a === void 0 ? void 0 : _a.knockers.length) {
|
|
2774
|
+
return Object.assign(Object.assign({}, state), { waitingParticipants: payload.room.knockers.map((knocker) => ({
|
|
2775
|
+
id: knocker.clientId,
|
|
2776
|
+
displayName: knocker.displayName,
|
|
2777
|
+
})) });
|
|
2778
|
+
}
|
|
2779
|
+
else {
|
|
2780
|
+
return state;
|
|
2781
|
+
}
|
|
2782
|
+
});
|
|
2783
|
+
builder.addCase(signalEvents.roomKnocked, (state, action) => {
|
|
2784
|
+
const { clientId, displayName } = action.payload;
|
|
2785
|
+
return Object.assign(Object.assign({}, state), { waitingParticipants: [...state.waitingParticipants, { id: clientId, displayName }] });
|
|
2786
|
+
});
|
|
2787
|
+
builder.addCase(signalEvents.knockerLeft, (state, action) => {
|
|
2788
|
+
const { clientId } = action.payload;
|
|
2789
|
+
return Object.assign(Object.assign({}, state), { waitingParticipants: state.waitingParticipants.filter((p) => p.id !== clientId) });
|
|
2790
|
+
});
|
|
2791
|
+
},
|
|
2792
|
+
});
|
|
2793
|
+
const doAcceptWaitingParticipant = createRoomConnectedThunk((payload) => (dispatch, getState) => {
|
|
2794
|
+
const { participantId } = payload;
|
|
2795
|
+
const state = getState();
|
|
2796
|
+
const socket = selectSignalConnectionSocket(state);
|
|
2797
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("handle_knock", {
|
|
2798
|
+
action: "accept",
|
|
2799
|
+
clientId: participantId,
|
|
2800
|
+
response: {},
|
|
2801
|
+
});
|
|
2802
|
+
});
|
|
2803
|
+
const doRejectWaitingParticipant = createRoomConnectedThunk((payload) => (dispatch, getState) => {
|
|
2804
|
+
const { participantId } = payload;
|
|
2805
|
+
const state = getState();
|
|
2806
|
+
const socket = selectSignalConnectionSocket(state);
|
|
2807
|
+
socket === null || socket === void 0 ? void 0 : socket.emit("handle_knock", {
|
|
2808
|
+
action: "reject",
|
|
2809
|
+
clientId: participantId,
|
|
2810
|
+
response: {},
|
|
2811
|
+
});
|
|
2812
|
+
});
|
|
2813
|
+
const selectWaitingParticipantsRaw = (state) => state.waitingParticipants;
|
|
2814
|
+
const selectWaitingParticipants = (state) => state.waitingParticipants.waitingParticipants;
|
|
2815
|
+
|
|
2816
|
+
var _a;
|
|
2817
|
+
const IS_DEV = (_a = undefined === "true") !== null && _a !== void 0 ? _a : false;
|
|
2818
|
+
const appReducer = combineReducers({
|
|
2819
|
+
app: appSlice.reducer,
|
|
2820
|
+
authorization: authorizationSlice.reducer,
|
|
2821
|
+
chat: chatSlice.reducer,
|
|
2822
|
+
cloudRecording: cloudRecordingSlice.reducer,
|
|
2823
|
+
connectionMonitor: connectionMonitorSlice.reducer,
|
|
2824
|
+
deviceCredentials: deviceCredentialsSlice.reducer,
|
|
2825
|
+
localMedia: localMediaSlice.reducer,
|
|
2826
|
+
localParticipant: localParticipantSlice.reducer,
|
|
2827
|
+
localScreenshare: localScreenshareSlice.reducer,
|
|
2828
|
+
notifications: notificationsSlice.reducer,
|
|
2829
|
+
organization: organizationSlice.reducer,
|
|
2830
|
+
remoteParticipants: remoteParticipantsSlice.reducer,
|
|
2831
|
+
room: roomSlice.reducer,
|
|
2832
|
+
roomConnection: roomConnectionSlice.reducer,
|
|
2833
|
+
rtcAnalytics: rtcAnalyticsSlice.reducer,
|
|
2834
|
+
rtcConnection: rtcConnectionSlice.reducer,
|
|
2835
|
+
signalConnection: signalConnectionSlice.reducer,
|
|
2836
|
+
spotlights: spotlightsSlice.reducer,
|
|
2837
|
+
streaming: streamingSlice.reducer,
|
|
2838
|
+
waitingParticipants: waitingParticipantsSlice.reducer,
|
|
2839
|
+
});
|
|
2840
|
+
const rootReducer = (state, action) => {
|
|
2841
|
+
var _a;
|
|
2842
|
+
if (doAppStart.match(action)) {
|
|
2843
|
+
const resetState = {
|
|
2844
|
+
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 }),
|
|
2845
|
+
localMedia: Object.assign(Object.assign({}, localMediaSlice.getInitialState()), state === null || state === void 0 ? void 0 : state.localMedia),
|
|
2846
|
+
};
|
|
2847
|
+
return appReducer(resetState, action);
|
|
2848
|
+
}
|
|
2849
|
+
return appReducer(state, action);
|
|
2850
|
+
};
|
|
2851
|
+
const createStore = ({ preloadedState, injectServices, }) => {
|
|
2852
|
+
return configureStore({
|
|
2853
|
+
devTools: IS_DEV,
|
|
2854
|
+
reducer: rootReducer,
|
|
2855
|
+
middleware: (getDefaultMiddleware) => getDefaultMiddleware({
|
|
2856
|
+
thunk: {
|
|
2857
|
+
extraArgument: { services: injectServices },
|
|
2858
|
+
},
|
|
2859
|
+
serializableCheck: false,
|
|
2860
|
+
}).prepend(listenerMiddleware.middleware),
|
|
2861
|
+
preloadedState,
|
|
2862
|
+
});
|
|
2863
|
+
};
|
|
2864
|
+
const observeStore = (store, select, onChange) => {
|
|
2865
|
+
let currentState;
|
|
2866
|
+
function handleChange() {
|
|
2867
|
+
const nextState = select(store.getState());
|
|
2868
|
+
if (nextState !== currentState) {
|
|
2869
|
+
currentState = nextState;
|
|
2870
|
+
onChange(currentState);
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
const unsubscribe = store.subscribe(handleChange);
|
|
2874
|
+
handleChange();
|
|
2875
|
+
return unsubscribe;
|
|
2876
|
+
};
|
|
2877
|
+
|
|
2878
|
+
class Response {
|
|
2879
|
+
constructor(initialValues = {}) {
|
|
2880
|
+
this.data = initialValues.data === undefined ? {} : initialValues.data;
|
|
2881
|
+
this.headers = initialValues.headers || {};
|
|
2882
|
+
this.status = initialValues.status || 200;
|
|
2883
|
+
this.statusText = initialValues.statusText || "OK";
|
|
2884
|
+
this.url = initialValues.url || null;
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
|
|
2888
|
+
function assertTruthy(value, parameterName) {
|
|
2889
|
+
assert.ok(value, `${parameterName} is required`);
|
|
2890
|
+
return value;
|
|
2891
|
+
}
|
|
2892
|
+
function assertBoolean(value, parameterName) {
|
|
2893
|
+
assert.ok(typeof value === "boolean", `${parameterName}<boolean> is required`);
|
|
2894
|
+
return value;
|
|
2895
|
+
}
|
|
2896
|
+
function assertNumber(value, parameterName) {
|
|
2897
|
+
assert.ok(typeof value === "number", `${parameterName}<number> is required`);
|
|
2898
|
+
return value;
|
|
2899
|
+
}
|
|
2900
|
+
function assertString(value, parameterName) {
|
|
2901
|
+
assert.ok(typeof value === "string", `${parameterName}<string> is required`);
|
|
2902
|
+
return value;
|
|
2903
|
+
}
|
|
2904
|
+
function assertInstanceOf(value, type, parameterName) {
|
|
2905
|
+
const resolvedParameterName = parameterName || type.name[0].toLowerCase() + type.name.substring(1);
|
|
2906
|
+
assert.ok(value instanceof type, `${resolvedParameterName}<${type.name}> is required`);
|
|
2907
|
+
return value;
|
|
2908
|
+
}
|
|
2909
|
+
function assertRoomName(roomName, parameterName = "roomName") {
|
|
2910
|
+
assertString(roomName, parameterName);
|
|
2911
|
+
assert.ok(typeof roomName === "string" && roomName[0] === "/", `${parameterName} must begin with a '/'`);
|
|
2912
|
+
return roomName;
|
|
2913
|
+
}
|
|
2914
|
+
function assertArray(array, parameterName) {
|
|
2915
|
+
assert.ok(Array.isArray(array), `${parameterName}<array> is required`);
|
|
2916
|
+
return array;
|
|
2917
|
+
}
|
|
2918
|
+
function assertOneOf(value, allowedValues, parameterName) {
|
|
2919
|
+
assertTruthy(value, "value");
|
|
2920
|
+
assertArray(allowedValues, "allowedValues");
|
|
2921
|
+
const isAllowed = allowedValues.includes(value);
|
|
2922
|
+
if (!isAllowed) {
|
|
2923
|
+
throw new Error(`${parameterName}<string> must be one of the following: ${allowedValues.join(", ")}`);
|
|
2924
|
+
}
|
|
2925
|
+
return value;
|
|
2926
|
+
}
|
|
2927
|
+
function assertRecord(ref, name) {
|
|
2928
|
+
if (ref === null || ref === undefined || typeof ref !== "object" || Array.isArray(ref)) {
|
|
2929
|
+
throw new Error(`${name} must be a record. ${JSON.stringify(ref)}`);
|
|
2930
|
+
}
|
|
2931
|
+
return ref;
|
|
2932
|
+
}
|
|
2933
|
+
function assertNullOrType(ref, type, name, message) {
|
|
2934
|
+
assertString(name, "name");
|
|
2935
|
+
const errorMessage = message || `${name} must be null or of type ${type}`;
|
|
2936
|
+
assert.ok(ref === null || typeof ref === type, errorMessage);
|
|
2937
|
+
}
|
|
2938
|
+
function assertNullOrString(ref, name, message) {
|
|
2939
|
+
assertString(name, "name");
|
|
2940
|
+
assertNullOrType(ref, "string", name, message);
|
|
2941
|
+
}
|
|
2942
|
+
|
|
2943
|
+
function _getAbsoluteUrl({ baseUrl, url }) {
|
|
2944
|
+
return baseUrl ? baseUrl + url : url;
|
|
2945
|
+
}
|
|
2946
|
+
class HttpClient {
|
|
2947
|
+
constructor({ baseUrl }) {
|
|
2948
|
+
assertString(baseUrl, "baseUrl");
|
|
2949
|
+
this._baseUrl = baseUrl;
|
|
2950
|
+
}
|
|
2951
|
+
_requestAxios(url, options) {
|
|
2952
|
+
const axiosOptions = Object.assign({}, options, {
|
|
2953
|
+
url,
|
|
2954
|
+
baseURL: this._baseUrl,
|
|
2955
|
+
});
|
|
2956
|
+
return axios.request(axiosOptions);
|
|
2957
|
+
}
|
|
2958
|
+
request(url, options) {
|
|
2959
|
+
assertString(url, "url");
|
|
2960
|
+
assert.ok(url[0] === "/", 'url<String> only accepts relative URLs beginning with "/".');
|
|
2961
|
+
assert.ok(options, "options are required");
|
|
2962
|
+
return this._requestAxios(url, options)
|
|
2963
|
+
.then((response) => {
|
|
2964
|
+
const { data, headers, status, statusText, config } = response;
|
|
2965
|
+
const requestUrl = config && config.url ? _getAbsoluteUrl({ baseUrl: config.baseURL, url: config.url }) : null;
|
|
2966
|
+
return new Response({
|
|
2967
|
+
data,
|
|
2968
|
+
headers,
|
|
2969
|
+
status,
|
|
2970
|
+
statusText,
|
|
2971
|
+
url: requestUrl,
|
|
2972
|
+
});
|
|
2973
|
+
})
|
|
2974
|
+
.catch((error) => {
|
|
2975
|
+
const responseObject = error.response;
|
|
2976
|
+
if (!responseObject) {
|
|
2977
|
+
throw new Error("Could not make the request.");
|
|
2978
|
+
}
|
|
2979
|
+
const { data, headers, status, statusText, config } = responseObject;
|
|
2980
|
+
const requestUrl = config && config.url ? _getAbsoluteUrl({ baseUrl: config.baseURL, url: config.url }) : null;
|
|
2981
|
+
return Promise.reject(new Response({
|
|
2982
|
+
data,
|
|
2983
|
+
headers,
|
|
2984
|
+
status,
|
|
2985
|
+
statusText,
|
|
2986
|
+
url: requestUrl,
|
|
2987
|
+
}));
|
|
2988
|
+
});
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
|
|
2992
|
+
class MultipartHttpClient {
|
|
2993
|
+
constructor({ httpClient }) {
|
|
2994
|
+
assert.ok(httpClient, "httpClient is required");
|
|
2995
|
+
this._httpClient = httpClient;
|
|
2996
|
+
}
|
|
2997
|
+
static dataToFormData(data) {
|
|
2998
|
+
assert.ok(data, "data is required");
|
|
2999
|
+
const fd = new FormData();
|
|
3000
|
+
Object.keys(data).forEach((key) => {
|
|
3001
|
+
const value = data[key];
|
|
3002
|
+
fd.append(key, value);
|
|
3003
|
+
});
|
|
3004
|
+
return fd;
|
|
3005
|
+
}
|
|
3006
|
+
request(url, options = {}) {
|
|
3007
|
+
const headers = Object.assign(options.headers || {}, {
|
|
3008
|
+
"Content-Type": undefined,
|
|
3009
|
+
});
|
|
3010
|
+
return this._httpClient.request(url, Object.assign(options, {
|
|
3011
|
+
headers,
|
|
3012
|
+
transformRequest: MultipartHttpClient.dataToFormData,
|
|
3013
|
+
}));
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
3016
|
+
|
|
3017
|
+
let btoa;
|
|
3018
|
+
if (typeof window === "object") {
|
|
3019
|
+
btoa = window.btoa || nodeBtoa;
|
|
3020
|
+
}
|
|
3021
|
+
else if (typeof global === "object") {
|
|
3022
|
+
btoa = global.btoa || nodeBtoa;
|
|
3023
|
+
}
|
|
3024
|
+
else {
|
|
3025
|
+
btoa = nodeBtoa;
|
|
3026
|
+
}
|
|
3027
|
+
function _getAuthHeader(credentials) {
|
|
3028
|
+
if (credentials && credentials.credentials) {
|
|
3029
|
+
const btoaStr = `${credentials.credentials.uuid}:${credentials.hmac}`;
|
|
3030
|
+
return { Authorization: `Basic ${btoa(btoaStr)}` };
|
|
3031
|
+
}
|
|
3032
|
+
return {};
|
|
3033
|
+
}
|
|
3034
|
+
const noCredentials = () => Promise.resolve(null);
|
|
3035
|
+
class AuthenticatedHttpClient {
|
|
3036
|
+
constructor({ httpClient, fetchDeviceCredentials }) {
|
|
3037
|
+
this._httpClient = httpClient;
|
|
3038
|
+
this._fetchDeviceCredentials = fetchDeviceCredentials;
|
|
3039
|
+
}
|
|
3040
|
+
request(url, options) {
|
|
3041
|
+
return this._fetchDeviceCredentials().then((credentials) => {
|
|
3042
|
+
const headers = Object.assign({}, options.headers, _getAuthHeader(credentials), {
|
|
3043
|
+
"X-Appearin-Device-Platform": "web",
|
|
3044
|
+
});
|
|
3045
|
+
const httpClientOptions = Object.assign({}, options, { headers });
|
|
3046
|
+
return this._httpClient.request(url, httpClientOptions);
|
|
3047
|
+
});
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
class ApiClient {
|
|
3051
|
+
constructor({ baseUrl = "https://api.appearin.net", fetchDeviceCredentials = noCredentials, } = {}) {
|
|
3052
|
+
this.authenticatedHttpClient = new AuthenticatedHttpClient({
|
|
3053
|
+
httpClient: new HttpClient({
|
|
3054
|
+
baseUrl,
|
|
3055
|
+
}),
|
|
3056
|
+
fetchDeviceCredentials,
|
|
3057
|
+
});
|
|
3058
|
+
this.authenticatedFormDataHttpClient = new MultipartHttpClient({ httpClient: this.authenticatedHttpClient });
|
|
3059
|
+
}
|
|
3060
|
+
request(url, options) {
|
|
3061
|
+
assertString(url, "url");
|
|
3062
|
+
assert.ok(url[0] === "/", 'url<String> only accepts relative URLs beginning with "/".');
|
|
3063
|
+
assert.ok(options, "options are required");
|
|
3064
|
+
return this.authenticatedHttpClient.request(url, options);
|
|
3065
|
+
}
|
|
3066
|
+
requestMultipart(url, options) {
|
|
3067
|
+
assertString(url, "url");
|
|
3068
|
+
assert.ok(url[0] === "/", 'url<String> only accepts relative URLs beginning with "/".');
|
|
3069
|
+
assert.ok(options, "options are required");
|
|
3070
|
+
return this.authenticatedFormDataHttpClient.request(url, options);
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
|
|
3074
|
+
function nullOrExtract(extract) {
|
|
3075
|
+
return (data, propertyName) => {
|
|
3076
|
+
const record = assertRecord(data, "data");
|
|
3077
|
+
const value = record[propertyName];
|
|
3078
|
+
return value === null || value === undefined ? null : extract(data, propertyName);
|
|
3079
|
+
};
|
|
3080
|
+
}
|
|
3081
|
+
function extractString(data, propertyName) {
|
|
3082
|
+
const record = assertRecord(data, "data");
|
|
3083
|
+
return assertString(record[propertyName], propertyName);
|
|
3084
|
+
}
|
|
3085
|
+
const extractNullOrString = nullOrExtract(extractString);
|
|
3086
|
+
function extractDate(data, propertyName) {
|
|
3087
|
+
const dateString = extractString(data, propertyName);
|
|
3088
|
+
const d = new Date(dateString);
|
|
3089
|
+
if (isNaN(d.getTime())) {
|
|
3090
|
+
throw new Error(`Invalid date for ${dateString}`);
|
|
3091
|
+
}
|
|
3092
|
+
return d;
|
|
3093
|
+
}
|
|
3094
|
+
function extractArrayOfJson(data, propertyName) {
|
|
3095
|
+
const record = assertRecord(data, "data");
|
|
3096
|
+
return assertArray(record[propertyName], propertyName);
|
|
3097
|
+
}
|
|
3098
|
+
function extractArray(data, propertyName, transformer) {
|
|
3099
|
+
return extractArrayOfJson(data, propertyName).map((value) => transformer(value));
|
|
3100
|
+
}
|
|
3101
|
+
function extractJson(data, propertyName) {
|
|
3102
|
+
const record = assertRecord(data, "data");
|
|
3103
|
+
const value = record[propertyName];
|
|
3104
|
+
return value === undefined ? null : value;
|
|
3105
|
+
}
|
|
3106
|
+
|
|
3107
|
+
class Credentials {
|
|
3108
|
+
constructor(uuid, hmac, userId = undefined) {
|
|
3109
|
+
this.credentials = {
|
|
3110
|
+
uuid,
|
|
3111
|
+
};
|
|
3112
|
+
this.hmac = hmac;
|
|
3113
|
+
this.userId = userId;
|
|
3114
|
+
}
|
|
3115
|
+
toJson() {
|
|
3116
|
+
return Object.assign({ credentials: this.credentials, hmac: this.hmac }, (this.userId && { userId: this.userId }));
|
|
3117
|
+
}
|
|
3118
|
+
static fromJson(json) {
|
|
3119
|
+
return new Credentials(extractString(extractJson(json, "credentials"), "uuid"), extractString(json, "hmac"), extractNullOrString(json, "userId") || undefined);
|
|
3120
|
+
}
|
|
3121
|
+
}
|
|
3122
|
+
|
|
3123
|
+
class DeviceService {
|
|
3124
|
+
constructor({ apiClient }) {
|
|
3125
|
+
this._apiClient = apiClient;
|
|
3126
|
+
}
|
|
3127
|
+
getCredentials() {
|
|
3128
|
+
return this._apiClient
|
|
3129
|
+
.request("/devices", {
|
|
3130
|
+
method: "post",
|
|
3131
|
+
})
|
|
3132
|
+
.then(({ data }) => {
|
|
3133
|
+
return Credentials.fromJson(data);
|
|
3134
|
+
})
|
|
3135
|
+
.catch((error) => {
|
|
3136
|
+
if (error.response) {
|
|
3137
|
+
if (error.response.status === 404) {
|
|
3138
|
+
return null;
|
|
3139
|
+
}
|
|
3140
|
+
}
|
|
3141
|
+
throw error;
|
|
3142
|
+
});
|
|
3143
|
+
}
|
|
3144
|
+
}
|
|
3145
|
+
|
|
3146
|
+
class ChromeStorageStore {
|
|
3147
|
+
constructor(key, chromeStorage) {
|
|
3148
|
+
this._key = key;
|
|
3149
|
+
this._chromeStorage = chromeStorage;
|
|
3150
|
+
}
|
|
3151
|
+
loadOrDefault(defaultValue) {
|
|
3152
|
+
return new Promise((resolve) => {
|
|
3153
|
+
this._chromeStorage.get(this._key, (result) => {
|
|
3154
|
+
const unknownResult = result;
|
|
3155
|
+
resolve(unknownResult[this._key] || defaultValue);
|
|
3156
|
+
});
|
|
3157
|
+
});
|
|
3158
|
+
}
|
|
3159
|
+
save(value) {
|
|
3160
|
+
return new Promise((resolve) => {
|
|
3161
|
+
this._chromeStorage.set({ [this._key]: value }, () => {
|
|
3162
|
+
resolve();
|
|
3163
|
+
});
|
|
3164
|
+
});
|
|
3165
|
+
}
|
|
3166
|
+
}
|
|
3167
|
+
|
|
3168
|
+
class LocalStorageStore {
|
|
3169
|
+
constructor(key, localStorage) {
|
|
3170
|
+
assertTruthy(localStorage, "localStorage");
|
|
3171
|
+
this._key = assertString(key, "key");
|
|
3172
|
+
this._localStorage = localStorage;
|
|
3173
|
+
}
|
|
3174
|
+
loadOrDefault(defaultValue) {
|
|
3175
|
+
try {
|
|
3176
|
+
const value = this._localStorage.getItem(this._key);
|
|
3177
|
+
if (value) {
|
|
3178
|
+
try {
|
|
3179
|
+
return Promise.resolve(JSON.parse(value));
|
|
3180
|
+
}
|
|
3181
|
+
catch (e) {
|
|
3182
|
+
}
|
|
3183
|
+
}
|
|
3184
|
+
return Promise.resolve(defaultValue);
|
|
3185
|
+
}
|
|
3186
|
+
catch (e) {
|
|
3187
|
+
console.warn("Error getting access to storage. Are cookies blocked?", e);
|
|
3188
|
+
return Promise.resolve(defaultValue);
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
save(value) {
|
|
3192
|
+
try {
|
|
3193
|
+
this._localStorage.setItem(this._key, JSON.stringify(value));
|
|
3194
|
+
return Promise.resolve();
|
|
3195
|
+
}
|
|
3196
|
+
catch (e) {
|
|
3197
|
+
console.warn("Error getting access to storage. Are cookies blocked?", e);
|
|
3198
|
+
return Promise.reject(e);
|
|
3199
|
+
}
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
|
|
3203
|
+
let localStorage;
|
|
3204
|
+
try {
|
|
3205
|
+
localStorage = self.localStorage;
|
|
3206
|
+
}
|
|
3207
|
+
catch (e) {
|
|
3208
|
+
localStorage = {
|
|
3209
|
+
getItem: () => undefined,
|
|
3210
|
+
key: () => undefined,
|
|
3211
|
+
setItem: () => undefined,
|
|
3212
|
+
removeItem: () => undefined,
|
|
3213
|
+
hasOwnProperty: () => undefined,
|
|
3214
|
+
length: 0,
|
|
3215
|
+
};
|
|
3216
|
+
}
|
|
3217
|
+
var localStorage$1 = localStorage;
|
|
3218
|
+
|
|
3219
|
+
const events = {
|
|
3220
|
+
CREDENTIALS_SAVED: "credentials_saved",
|
|
3221
|
+
};
|
|
3222
|
+
class CredentialsService extends EventEmitter {
|
|
3223
|
+
constructor({ deviceService, credentialsStore, }) {
|
|
3224
|
+
super();
|
|
3225
|
+
this._deviceService = deviceService;
|
|
3226
|
+
this._credentialsStore = credentialsStore;
|
|
3227
|
+
}
|
|
3228
|
+
static create({ baseUrl, storeName = "CredentialsStorage", storeType = "localStorage", }) {
|
|
3229
|
+
const deviceService = new DeviceService({
|
|
3230
|
+
apiClient: new ApiClient({ baseUrl }),
|
|
3231
|
+
});
|
|
3232
|
+
let credentialsStore = null;
|
|
3233
|
+
if (storeType === "localStorage") {
|
|
3234
|
+
credentialsStore = new LocalStorageStore(storeName, localStorage$1);
|
|
3235
|
+
}
|
|
3236
|
+
else if (storeType === "chromeStorage") {
|
|
3237
|
+
credentialsStore = new ChromeStorageStore(storeName, window["chrome"].storage.local);
|
|
3238
|
+
}
|
|
3239
|
+
else {
|
|
3240
|
+
throw new Error(`Unknown store type: ${storeType}`);
|
|
3241
|
+
}
|
|
3242
|
+
return new CredentialsService({
|
|
3243
|
+
deviceService,
|
|
3244
|
+
credentialsStore,
|
|
3245
|
+
});
|
|
3246
|
+
}
|
|
3247
|
+
_fetchNewCredentialsFromApi() {
|
|
3248
|
+
const credentialsStore = this._credentialsStore;
|
|
3249
|
+
return new Promise((resolve) => {
|
|
3250
|
+
const fetchCredentials = () => {
|
|
3251
|
+
this._deviceService
|
|
3252
|
+
.getCredentials()
|
|
3253
|
+
.then((credentials) => {
|
|
3254
|
+
return credentialsStore
|
|
3255
|
+
.save(credentials ? credentials.toJson() : null)
|
|
3256
|
+
.then(() => resolve(credentials));
|
|
3257
|
+
})
|
|
3258
|
+
.catch(() => {
|
|
3259
|
+
setTimeout(fetchCredentials, 2000);
|
|
3260
|
+
});
|
|
3261
|
+
};
|
|
3262
|
+
fetchCredentials();
|
|
3263
|
+
});
|
|
3264
|
+
}
|
|
3265
|
+
getCurrentCredentials() {
|
|
3266
|
+
return this._credentialsStore.loadOrDefault(null).then((json) => (json ? Credentials.fromJson(json) : null));
|
|
3267
|
+
}
|
|
3268
|
+
getCredentials() {
|
|
3269
|
+
if (!this.credentialsPromise) {
|
|
3270
|
+
this.credentialsPromise = this.getCurrentCredentials().then((storedCredentials) => {
|
|
3271
|
+
if (storedCredentials) {
|
|
3272
|
+
return storedCredentials;
|
|
3273
|
+
}
|
|
3274
|
+
return this._fetchNewCredentialsFromApi();
|
|
3275
|
+
});
|
|
3276
|
+
}
|
|
3277
|
+
return this.credentialsPromise;
|
|
3278
|
+
}
|
|
3279
|
+
saveCredentials(credentials) {
|
|
3280
|
+
this.credentialsPromise = undefined;
|
|
3281
|
+
return this._credentialsStore.save(credentials.toJson()).then(() => {
|
|
3282
|
+
this.emit(events.CREDENTIALS_SAVED, credentials);
|
|
3283
|
+
return credentials;
|
|
3284
|
+
});
|
|
3285
|
+
}
|
|
3286
|
+
setUserId(userId) {
|
|
3287
|
+
return this.getCurrentCredentials()
|
|
3288
|
+
.then((storedCredentials) => {
|
|
3289
|
+
if (!storedCredentials) {
|
|
3290
|
+
console.error("Illegal state: no credentials to set user id for.");
|
|
3291
|
+
}
|
|
3292
|
+
const userIdChangedFromLocalStorage = storedCredentials === null || storedCredentials.userId !== userId;
|
|
3293
|
+
if (!userIdChangedFromLocalStorage) {
|
|
3294
|
+
return undefined;
|
|
3295
|
+
}
|
|
3296
|
+
return this._credentialsStore.save(Object.assign({}, storedCredentials === null || storedCredentials === void 0 ? void 0 : storedCredentials.toJson(), { userId }));
|
|
3297
|
+
})
|
|
3298
|
+
.then(() => undefined);
|
|
3299
|
+
}
|
|
3300
|
+
}
|
|
3301
|
+
|
|
3302
|
+
const noOrganization = () => Promise.resolve(undefined);
|
|
3303
|
+
class OrganizationApiClient {
|
|
3304
|
+
constructor({ apiClient, fetchOrganization = noOrganization, }) {
|
|
3305
|
+
this._apiClient = apiClient;
|
|
3306
|
+
this._fetchOrganization = fetchOrganization;
|
|
3307
|
+
this._apiClient = apiClient;
|
|
3308
|
+
}
|
|
3309
|
+
_callRequestMethod(method, url, options) {
|
|
3310
|
+
assertString(url, "url");
|
|
3311
|
+
assert.ok(url[0] === "/", 'url<String> only accepts relative URLs beginning with "/".');
|
|
3312
|
+
assert.ok(options, "options are required");
|
|
3313
|
+
return this._fetchOrganization().then((organization) => {
|
|
3314
|
+
if (!organization) {
|
|
3315
|
+
return this._apiClient[method](url, options);
|
|
3316
|
+
}
|
|
3317
|
+
const { organizationId } = organization;
|
|
3318
|
+
return this._apiClient[method](`/organizations/${encodeURIComponent(organizationId)}${url}`, options);
|
|
3319
|
+
});
|
|
3320
|
+
}
|
|
3321
|
+
request(url, options) {
|
|
3322
|
+
return this._callRequestMethod("request", url, options);
|
|
3323
|
+
}
|
|
3324
|
+
requestMultipart(url, options) {
|
|
3325
|
+
return this._callRequestMethod("requestMultipart", url, options);
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3328
|
+
|
|
3329
|
+
class EmbeddedFreeTierStatus {
|
|
3330
|
+
constructor({ isExhausted, renewsAt, totalMinutesLimit, totalMinutesUsed, }) {
|
|
3331
|
+
this.isExhausted = isExhausted;
|
|
3332
|
+
this.renewsAt = renewsAt;
|
|
3333
|
+
this.totalMinutesLimit = totalMinutesLimit;
|
|
3334
|
+
this.totalMinutesUsed = totalMinutesUsed;
|
|
3335
|
+
}
|
|
3336
|
+
static fromJson(data) {
|
|
3337
|
+
return new EmbeddedFreeTierStatus({
|
|
3338
|
+
isExhausted: assertBoolean(data.isExhausted, "isExhausted"),
|
|
3339
|
+
renewsAt: new Date(assertString(data.renewsAt, "renewsAt")),
|
|
3340
|
+
totalMinutesLimit: assertNumber(data.totalMinutesLimit, "totalMinutesLimit"),
|
|
3341
|
+
totalMinutesUsed: assertNumber(data.totalMinutesUsed, "totalMinutesUsed"),
|
|
3342
|
+
});
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3345
|
+
|
|
3346
|
+
class Account {
|
|
3347
|
+
constructor({ basePlanId, embeddedFreeTierStatus, isDeactivated, isOnTrial, onTrialUntil, trialStatus, }) {
|
|
3348
|
+
this.basePlanId = basePlanId;
|
|
3349
|
+
this.isDeactivated = isDeactivated;
|
|
3350
|
+
this.isOnTrial = isOnTrial;
|
|
3351
|
+
this.onTrialUntil = onTrialUntil || null;
|
|
3352
|
+
this.trialStatus = trialStatus || null;
|
|
3353
|
+
this.embeddedFreeTierStatus = embeddedFreeTierStatus || null;
|
|
3354
|
+
}
|
|
3355
|
+
static fromJson(data) {
|
|
3356
|
+
return new Account({
|
|
3357
|
+
basePlanId: typeof data.basePlanId === "string" ? data.basePlanId : null,
|
|
3358
|
+
isDeactivated: assertBoolean(data.isDeactivated, "isDeactivated"),
|
|
3359
|
+
isOnTrial: assertBoolean(data.isOnTrial, "isOnTrial"),
|
|
3360
|
+
onTrialUntil: typeof data.onTrialUntil === "string" ? new Date(data.onTrialUntil) : null,
|
|
3361
|
+
trialStatus: typeof data.trialStatus === "string" ? data.trialStatus : null,
|
|
3362
|
+
embeddedFreeTierStatus: data.embeddedFreeTierStatus
|
|
3363
|
+
? EmbeddedFreeTierStatus.fromJson(data.embeddedFreeTierStatus)
|
|
3364
|
+
: null,
|
|
3365
|
+
});
|
|
3366
|
+
}
|
|
3367
|
+
}
|
|
3368
|
+
|
|
3369
|
+
function hasValue(value) {
|
|
3370
|
+
return value !== null && value !== undefined;
|
|
3371
|
+
}
|
|
3372
|
+
function createOrganizationLimits(limits = {}) {
|
|
3373
|
+
return {
|
|
3374
|
+
maxNumberOfInvitationsAndUsers: hasValue(limits === null || limits === void 0 ? void 0 : limits.maxNumberOfInvitationsAndUsers)
|
|
3375
|
+
? Number(limits === null || limits === void 0 ? void 0 : limits.maxNumberOfInvitationsAndUsers)
|
|
3376
|
+
: null,
|
|
3377
|
+
maxNumberOfClaimedRooms: hasValue(limits === null || limits === void 0 ? void 0 : limits.maxNumberOfClaimedRooms)
|
|
3378
|
+
? Number(limits === null || limits === void 0 ? void 0 : limits.maxNumberOfClaimedRooms)
|
|
3379
|
+
: null,
|
|
3380
|
+
maxRoomLimitPerOrganization: hasValue(limits === null || limits === void 0 ? void 0 : limits.maxRoomLimitPerOrganization)
|
|
3381
|
+
? Number(limits === null || limits === void 0 ? void 0 : limits.maxRoomLimitPerOrganization)
|
|
3382
|
+
: null,
|
|
3383
|
+
trialMinutesLimit: hasValue(limits === null || limits === void 0 ? void 0 : limits.trialMinutesLimit) ? Number(limits === null || limits === void 0 ? void 0 : limits.trialMinutesLimit) : null,
|
|
3384
|
+
includedUnits: hasValue(limits === null || limits === void 0 ? void 0 : limits.includedUnits) ? Number(limits === null || limits === void 0 ? void 0 : limits.includedUnits) : null,
|
|
3385
|
+
};
|
|
3386
|
+
}
|
|
3387
|
+
class Organization {
|
|
3388
|
+
constructor(properties) {
|
|
3389
|
+
this.logoImageUrl = null;
|
|
3390
|
+
this.roomBackgroundImageUrl = null;
|
|
3391
|
+
this.roomBackgroundThumbnailUrl = null;
|
|
3392
|
+
this.roomKnockPageBackgroundImageUrl = null;
|
|
3393
|
+
this.roomKnockPageBackgroundThumbnailUrl = null;
|
|
3394
|
+
this.preferences = null;
|
|
3395
|
+
this.onboardingSurvey = null;
|
|
3396
|
+
this.type = null;
|
|
3397
|
+
assertInstanceOf(properties, Object, "properties");
|
|
3398
|
+
assertString(properties.organizationId, "organizationId");
|
|
3399
|
+
assertString(properties.organizationName, "organizationName");
|
|
3400
|
+
assertString(properties.subdomain, "subdomain");
|
|
3401
|
+
assertInstanceOf(properties.permissions, Object, "permissions");
|
|
3402
|
+
assertInstanceOf(properties.limits, Object, "limits");
|
|
3403
|
+
this.organizationId = properties.organizationId;
|
|
3404
|
+
this.organizationName = properties.organizationName;
|
|
3405
|
+
this.subdomain = properties.subdomain;
|
|
3406
|
+
this.permissions = properties.permissions;
|
|
3407
|
+
this.limits = properties.limits;
|
|
3408
|
+
this.account = properties.account ? new Account(properties.account) : null;
|
|
3409
|
+
this.logoImageUrl = properties.logoImageUrl;
|
|
3410
|
+
this.roomBackgroundImageUrl = properties.roomBackgroundImageUrl;
|
|
3411
|
+
this.roomBackgroundThumbnailUrl = properties.roomBackgroundThumbnailUrl;
|
|
3412
|
+
this.roomKnockPageBackgroundImageUrl = properties.roomKnockPageBackgroundImageUrl;
|
|
3413
|
+
this.roomKnockPageBackgroundThumbnailUrl = properties.roomKnockPageBackgroundThumbnailUrl;
|
|
3414
|
+
this.preferences = properties.preferences;
|
|
3415
|
+
this.onboardingSurvey = properties.onboardingSurvey;
|
|
3416
|
+
this.type = properties.type;
|
|
3417
|
+
}
|
|
3418
|
+
static fromJson(data) {
|
|
3419
|
+
const parsedData = assertInstanceOf(data, Object, "data");
|
|
3420
|
+
const preferences = ((parsedData === null || parsedData === void 0 ? void 0 : parsedData.preferences) || {});
|
|
3421
|
+
const onboardingSurvey = ((parsedData === null || parsedData === void 0 ? void 0 : parsedData.onboardingSurvey) || null);
|
|
3422
|
+
const permissions = assertInstanceOf(parsedData.permissions, Object, "permissions");
|
|
3423
|
+
return new Organization({
|
|
3424
|
+
organizationId: assertString(parsedData.organizationId, "organizationId"),
|
|
3425
|
+
organizationName: assertString(parsedData.organizationName, "organizationName"),
|
|
3426
|
+
subdomain: assertString(parsedData.subdomain, "subdomain"),
|
|
3427
|
+
permissions,
|
|
3428
|
+
limits: createOrganizationLimits(assertInstanceOf(parsedData.limits, Object, "limits")),
|
|
3429
|
+
account: parsedData.account ? Account.fromJson(parsedData.account) : null,
|
|
3430
|
+
logoImageUrl: typeof parsedData.logoImageUrl === "string" ? parsedData.logoImageUrl : null,
|
|
3431
|
+
roomBackgroundImageUrl: typeof parsedData.roomBackgroundImageUrl === "string" ? parsedData.roomBackgroundImageUrl : null,
|
|
3432
|
+
roomBackgroundThumbnailUrl: typeof parsedData.roomBackgroundThumbnailUrl === "string"
|
|
3433
|
+
? parsedData.roomBackgroundThumbnailUrl
|
|
3434
|
+
: null,
|
|
3435
|
+
roomKnockPageBackgroundImageUrl: typeof parsedData.roomKnockPageBackgroundImageUrl === "string"
|
|
3436
|
+
? parsedData.roomKnockPageBackgroundImageUrl
|
|
3437
|
+
: null,
|
|
3438
|
+
roomKnockPageBackgroundThumbnailUrl: typeof parsedData.roomKnockPageBackgroundThumbnailUrl === "string"
|
|
3439
|
+
? parsedData.roomKnockPageBackgroundThumbnailUrl
|
|
3440
|
+
: null,
|
|
3441
|
+
preferences,
|
|
3442
|
+
onboardingSurvey,
|
|
3443
|
+
type: typeof parsedData.type === "string" ? parsedData.type : null,
|
|
3444
|
+
});
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
Organization.GLOBAL_ORGANIZATION_ID = "1";
|
|
3448
|
+
|
|
3449
|
+
class OrganizationService {
|
|
3450
|
+
constructor({ apiClient }) {
|
|
3451
|
+
this._apiClient = apiClient;
|
|
3452
|
+
}
|
|
3453
|
+
createOrganization({ organizationName, subdomain, owner, }) {
|
|
3454
|
+
const { displayName, consents } = owner || {};
|
|
3455
|
+
const email = "email" in owner
|
|
3456
|
+
? {
|
|
3457
|
+
value: owner.email,
|
|
3458
|
+
verificationCode: assertString(owner.verificationCode, "owner.verificationCode"),
|
|
3459
|
+
}
|
|
3460
|
+
: null;
|
|
3461
|
+
const idToken = "idToken" in owner ? owner.idToken : null;
|
|
3462
|
+
assertString(subdomain, "subdomain");
|
|
3463
|
+
assertString(organizationName, "organizationName");
|
|
3464
|
+
assertString(displayName, "owner.displayName");
|
|
3465
|
+
assert.ok(email || idToken, "owner.email or owner.idToken is required");
|
|
3466
|
+
if (consents) {
|
|
3467
|
+
assertArray(consents, "consents");
|
|
3468
|
+
for (const { consentRevisionId, action } of consents) {
|
|
3469
|
+
assertString(consentRevisionId, "consentRevisionId");
|
|
3470
|
+
assertNullOrString(action, "action");
|
|
3471
|
+
}
|
|
3472
|
+
}
|
|
3473
|
+
return this._apiClient
|
|
3474
|
+
.request(`/organizations`, {
|
|
3475
|
+
method: "POST",
|
|
3476
|
+
data: {
|
|
3477
|
+
organizationName,
|
|
3478
|
+
type: "private",
|
|
3479
|
+
subdomain,
|
|
3480
|
+
owner: Object.assign(Object.assign(Object.assign(Object.assign({}, (email && { email })), (idToken && { idToken })), (consents && { consents })), { displayName }),
|
|
3481
|
+
},
|
|
3482
|
+
})
|
|
3483
|
+
.then(({ data }) => {
|
|
3484
|
+
return extractString(data, "organizationId");
|
|
3485
|
+
});
|
|
3486
|
+
}
|
|
3487
|
+
getOrganizationBySubdomain(subdomain) {
|
|
3488
|
+
assertString(subdomain, "subdomain");
|
|
3489
|
+
return this._apiClient
|
|
3490
|
+
.request(`/organization-subdomains/${encodeURIComponent(subdomain)}/?fields=permissions,account,onboardingSurvey`, {
|
|
3491
|
+
method: "GET",
|
|
3492
|
+
})
|
|
3493
|
+
.then(({ data }) => {
|
|
3494
|
+
return Organization.fromJson(data);
|
|
3495
|
+
})
|
|
3496
|
+
.catch((res) => {
|
|
3497
|
+
if (res instanceof Response) {
|
|
3498
|
+
if (res.status === 404) {
|
|
3499
|
+
return null;
|
|
3500
|
+
}
|
|
3501
|
+
throw new Error(res.statusText);
|
|
3502
|
+
}
|
|
3503
|
+
throw res;
|
|
3504
|
+
});
|
|
3505
|
+
}
|
|
3506
|
+
getOrganizationByOrganizationId(organizationId) {
|
|
3507
|
+
assertString(organizationId, "organizationId");
|
|
3508
|
+
return this._apiClient
|
|
3509
|
+
.request(`/organizations/${encodeURIComponent(organizationId)}?fields=permissions,account`, {
|
|
3510
|
+
method: "GET",
|
|
3511
|
+
})
|
|
3512
|
+
.then(({ data }) => {
|
|
3513
|
+
return Organization.fromJson(data);
|
|
3514
|
+
})
|
|
3515
|
+
.catch((res) => {
|
|
3516
|
+
if (res instanceof Response) {
|
|
3517
|
+
if (res.status === 404) {
|
|
3518
|
+
return null;
|
|
3519
|
+
}
|
|
3520
|
+
throw new Error(res.statusText);
|
|
3521
|
+
}
|
|
3522
|
+
throw res;
|
|
3523
|
+
});
|
|
3524
|
+
}
|
|
3525
|
+
getOrganizationsByContactPoint(options) {
|
|
3526
|
+
const { code } = options;
|
|
3527
|
+
const email = "email" in options ? options.email : null;
|
|
3528
|
+
const phoneNumber = "phoneNumber" in options ? options.phoneNumber : null;
|
|
3529
|
+
assert.ok((email || phoneNumber) && !(email && phoneNumber), "either email or phoneNumber is required");
|
|
3530
|
+
assertString(code, "code");
|
|
3531
|
+
const contactPoint = email ? { type: "email", value: email } : { type: "phoneNumber", value: phoneNumber };
|
|
3532
|
+
return this._apiClient
|
|
3533
|
+
.request("/organization-queries", {
|
|
3534
|
+
method: "POST",
|
|
3535
|
+
data: {
|
|
3536
|
+
contactPoint,
|
|
3537
|
+
code,
|
|
3538
|
+
},
|
|
3539
|
+
})
|
|
3540
|
+
.then(({ data }) => {
|
|
3541
|
+
return extractArray(data, "organizations", (organization) => Organization.fromJson(organization));
|
|
3542
|
+
});
|
|
3543
|
+
}
|
|
3544
|
+
getOrganizationsByIdToken({ idToken }) {
|
|
3545
|
+
assertString(idToken, "idToken");
|
|
3546
|
+
return this._apiClient
|
|
3547
|
+
.request("/organization-queries", {
|
|
3548
|
+
method: "POST",
|
|
3549
|
+
data: {
|
|
3550
|
+
idToken,
|
|
3551
|
+
},
|
|
3552
|
+
})
|
|
3553
|
+
.then(({ data }) => {
|
|
3554
|
+
return extractArray(data, "organizations", (organization) => {
|
|
3555
|
+
return Organization.fromJson(Object.assign({ permissions: {}, limits: {} }, assertRecord(organization, "organization")));
|
|
3556
|
+
});
|
|
3557
|
+
});
|
|
3558
|
+
}
|
|
3559
|
+
getOrganizationsByLoggedInUser() {
|
|
3560
|
+
return this._apiClient
|
|
3561
|
+
.request("/user/organizations", {
|
|
3562
|
+
method: "GET",
|
|
3563
|
+
})
|
|
3564
|
+
.then(({ data }) => {
|
|
3565
|
+
return extractArray(data, "organizations", (o) => {
|
|
3566
|
+
return Organization.fromJson(Object.assign({ permissions: {}, limits: {} }, assertRecord(o, "organization")));
|
|
3567
|
+
});
|
|
3568
|
+
});
|
|
3569
|
+
}
|
|
3570
|
+
getSubdomainAvailability(subdomain) {
|
|
3571
|
+
assertString(subdomain, "subdomain");
|
|
3572
|
+
return this._apiClient
|
|
3573
|
+
.request(`/organization-subdomains/${encodeURIComponent(subdomain)}/availability`, {
|
|
3574
|
+
method: "GET",
|
|
3575
|
+
})
|
|
3576
|
+
.then(({ data }) => {
|
|
3577
|
+
assertInstanceOf(data, Object, "data");
|
|
3578
|
+
return {
|
|
3579
|
+
status: extractString(data, "status"),
|
|
3580
|
+
};
|
|
3581
|
+
});
|
|
3582
|
+
}
|
|
3583
|
+
updatePreferences({ organizationId, preferences, }) {
|
|
3584
|
+
assertTruthy(organizationId, "organizationId");
|
|
3585
|
+
assertTruthy(preferences, "preferences");
|
|
3586
|
+
return this._apiClient
|
|
3587
|
+
.request(`/organizations/${encodeURIComponent(organizationId)}/preferences`, {
|
|
3588
|
+
method: "PATCH",
|
|
3589
|
+
data: preferences,
|
|
3590
|
+
})
|
|
3591
|
+
.then(() => undefined);
|
|
3592
|
+
}
|
|
3593
|
+
deleteOrganization({ organizationId }) {
|
|
3594
|
+
assertTruthy(organizationId, "organizationId");
|
|
3595
|
+
return this._apiClient
|
|
3596
|
+
.request(`/organizations/${encodeURIComponent(organizationId)}`, {
|
|
3597
|
+
method: "DELETE",
|
|
3598
|
+
})
|
|
3599
|
+
.then(() => undefined);
|
|
3600
|
+
}
|
|
3601
|
+
}
|
|
3602
|
+
|
|
3603
|
+
class OrganizationServiceCache {
|
|
3604
|
+
constructor({ organizationService, subdomain }) {
|
|
3605
|
+
this._organizationService = organizationService;
|
|
3606
|
+
this._subdomain = subdomain;
|
|
3607
|
+
this._organizationPromise = null;
|
|
3608
|
+
}
|
|
3609
|
+
initOrganization() {
|
|
3610
|
+
return this.fetchOrganization().then(() => undefined);
|
|
3611
|
+
}
|
|
3612
|
+
fetchOrganization() {
|
|
3613
|
+
if (!this._organizationPromise) {
|
|
3614
|
+
this._organizationPromise = this._organizationService.getOrganizationBySubdomain(this._subdomain);
|
|
3615
|
+
}
|
|
3616
|
+
return this._organizationPromise;
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3619
|
+
|
|
3620
|
+
class Room {
|
|
3621
|
+
constructor(properties = {}) {
|
|
3622
|
+
assert.ok(properties instanceof Object, "properties<object> must be empty or an object");
|
|
3623
|
+
this.isClaimed = false;
|
|
3624
|
+
this.isBanned = false;
|
|
3625
|
+
this.isLocked = false;
|
|
3626
|
+
this.knockPage = {
|
|
3627
|
+
backgroundImageUrl: null,
|
|
3628
|
+
backgroundThumbnailUrl: null,
|
|
3629
|
+
};
|
|
3630
|
+
this.logoUrl = null;
|
|
3631
|
+
this.backgroundImageUrl = null;
|
|
3632
|
+
this.backgroundThumbnailUrl = null;
|
|
3633
|
+
this.type = null;
|
|
3634
|
+
this.legacyRoomType = null;
|
|
3635
|
+
this.mode = null;
|
|
3636
|
+
this.product = null;
|
|
3637
|
+
this.roomName = null;
|
|
3638
|
+
this.theme = null;
|
|
3639
|
+
this.preferences = {};
|
|
3640
|
+
this.protectedPreferences = {};
|
|
3641
|
+
this.publicProfile = null;
|
|
3642
|
+
const validProperties = {};
|
|
3643
|
+
Object.getOwnPropertyNames(properties).forEach((prop) => {
|
|
3644
|
+
if (Object.getOwnPropertyNames(this).indexOf(prop) !== -1) {
|
|
3645
|
+
validProperties[prop] = properties[prop];
|
|
3646
|
+
}
|
|
3647
|
+
});
|
|
3648
|
+
if (properties.ownerId !== undefined) {
|
|
3649
|
+
this.ownerId = properties.ownerId;
|
|
3650
|
+
}
|
|
3651
|
+
if (properties.meeting !== undefined) {
|
|
3652
|
+
this.meeting = properties.meeting;
|
|
3653
|
+
}
|
|
3654
|
+
Object.assign(this, validProperties);
|
|
3655
|
+
}
|
|
3656
|
+
}
|
|
3657
|
+
|
|
3658
|
+
class Meeting {
|
|
3659
|
+
constructor({ meetingId, roomName, roomUrl, startDate, endDate, hostRoomUrl, viewerRoomUrl }) {
|
|
3660
|
+
assertString(meetingId, "meetingId");
|
|
3661
|
+
assertString(roomName, "roomName");
|
|
3662
|
+
assertString(roomUrl, "roomUrl");
|
|
3663
|
+
assertInstanceOf(startDate, Date, "startDate");
|
|
3664
|
+
assertInstanceOf(endDate, Date, "endDate");
|
|
3665
|
+
this.meetingId = meetingId;
|
|
3666
|
+
this.roomName = roomName;
|
|
3667
|
+
this.roomUrl = roomUrl;
|
|
3668
|
+
this.startDate = startDate;
|
|
3669
|
+
this.endDate = endDate;
|
|
3670
|
+
this.hostRoomUrl = hostRoomUrl;
|
|
3671
|
+
this.viewerRoomUrl = viewerRoomUrl;
|
|
3672
|
+
}
|
|
3673
|
+
static fromJson(data) {
|
|
3674
|
+
return new Meeting({
|
|
3675
|
+
meetingId: extractString(data, "meetingId"),
|
|
3676
|
+
roomName: extractString(data, "roomName"),
|
|
3677
|
+
roomUrl: extractString(data, "roomUrl"),
|
|
3678
|
+
startDate: extractDate(data, "startDate"),
|
|
3679
|
+
endDate: extractDate(data, "endDate"),
|
|
3680
|
+
hostRoomUrl: extractNullOrString(data, "hostRoomUrl"),
|
|
3681
|
+
viewerRoomUrl: extractNullOrString(data, "viewerRoomUrl"),
|
|
3682
|
+
});
|
|
3683
|
+
}
|
|
3684
|
+
}
|
|
3685
|
+
|
|
3686
|
+
function createRoomUrl(roomName, path = "") {
|
|
3687
|
+
const encodedDisplayName = encodeURIComponent(roomName.substring(1));
|
|
3688
|
+
return `/room/${encodedDisplayName}${path}`;
|
|
3689
|
+
}
|
|
3690
|
+
class RoomService {
|
|
3691
|
+
constructor({ organizationApiClient }) {
|
|
3692
|
+
this._organizationApiClient = assertInstanceOf(organizationApiClient, OrganizationApiClient);
|
|
3693
|
+
}
|
|
3694
|
+
getRooms({ types, fields = [] } = {}) {
|
|
3695
|
+
assertArray(types, "types");
|
|
3696
|
+
assertArray(fields, "fields");
|
|
3697
|
+
return this._organizationApiClient
|
|
3698
|
+
.request("/room", {
|
|
3699
|
+
method: "GET",
|
|
3700
|
+
params: { types: types.join(","), fields: fields.join(","), includeOnlyLegacyRoomType: "false" },
|
|
3701
|
+
})
|
|
3702
|
+
.then(({ data }) => data.rooms.map((room) => new Room(room)));
|
|
3703
|
+
}
|
|
3704
|
+
getRoom({ roomName, fields }) {
|
|
3705
|
+
assertRoomName(roomName);
|
|
3706
|
+
const encodedDisplayName = encodeURIComponent(roomName.substring(1));
|
|
3707
|
+
return this._organizationApiClient
|
|
3708
|
+
.request(`/rooms/${encodedDisplayName}`, {
|
|
3709
|
+
method: "GET",
|
|
3710
|
+
params: Object.assign({ includeOnlyLegacyRoomType: "false" }, (fields && { fields: fields.join(",") })),
|
|
3711
|
+
})
|
|
3712
|
+
.then(({ data }) => new Room(Object.assign({}, data, Object.assign({ roomName }, (data.meeting && { meeting: Meeting.fromJson(data.meeting) })))))
|
|
3713
|
+
.catch((response) => {
|
|
3714
|
+
if (response.status === 404) {
|
|
3715
|
+
return new Room({
|
|
3716
|
+
roomName,
|
|
3717
|
+
isClaimed: false,
|
|
3718
|
+
mode: "normal",
|
|
3719
|
+
product: {
|
|
3720
|
+
categoryName: "personal_free",
|
|
3721
|
+
},
|
|
3722
|
+
type: "personal",
|
|
3723
|
+
legacyRoomType: "free",
|
|
3724
|
+
});
|
|
3725
|
+
}
|
|
3726
|
+
if (response.status === 400 && response.data.error === "Banned room") {
|
|
3727
|
+
return new Room({ roomName, isBanned: true });
|
|
3728
|
+
}
|
|
3729
|
+
throw new Error(response.data ? response.data.error : "Could not fetch room information");
|
|
3730
|
+
});
|
|
3731
|
+
}
|
|
3732
|
+
claimRoom({ roomName, type, mode, isLocked }) {
|
|
3733
|
+
assertRoomName(roomName);
|
|
3734
|
+
assertString(type, "type");
|
|
3735
|
+
return this._organizationApiClient
|
|
3736
|
+
.request("/room/claim", {
|
|
3737
|
+
method: "POST",
|
|
3738
|
+
data: Object.assign(Object.assign({ roomName,
|
|
3739
|
+
type }, (typeof mode === "string" && { mode })), (typeof isLocked === "boolean" && { isLocked })),
|
|
3740
|
+
})
|
|
3741
|
+
.then(() => undefined)
|
|
3742
|
+
.catch((response) => {
|
|
3743
|
+
throw new Error(response.data.error || "Failed to claim room");
|
|
3744
|
+
});
|
|
3745
|
+
}
|
|
3746
|
+
unclaimRoom(roomName) {
|
|
3747
|
+
assertRoomName(roomName);
|
|
3748
|
+
const encodedDisplayName = encodeURIComponent(roomName.substring(1));
|
|
3749
|
+
return this._organizationApiClient
|
|
3750
|
+
.request(`/room/${encodedDisplayName}`, {
|
|
3751
|
+
method: "DELETE",
|
|
3752
|
+
})
|
|
3753
|
+
.then(() => undefined);
|
|
3754
|
+
}
|
|
3755
|
+
renameRoom({ roomName, newRoomName }) {
|
|
3756
|
+
assertRoomName(roomName);
|
|
3757
|
+
assertString(newRoomName, "newRoomName");
|
|
3758
|
+
const encodedRoomName = encodeURIComponent(roomName.substring(1));
|
|
3759
|
+
return this._organizationApiClient
|
|
3760
|
+
.request(`/room/${encodedRoomName}/roomName`, {
|
|
3761
|
+
method: "PUT",
|
|
3762
|
+
data: { newRoomName },
|
|
3763
|
+
})
|
|
3764
|
+
.then(() => undefined);
|
|
3765
|
+
}
|
|
3766
|
+
changeMode({ roomName, mode }) {
|
|
3767
|
+
assertRoomName(roomName);
|
|
3768
|
+
assertString(mode, "mode");
|
|
3769
|
+
const encodedDisplayName = encodeURIComponent(roomName.substring(1));
|
|
3770
|
+
return this._organizationApiClient
|
|
3771
|
+
.request(`/room/${encodedDisplayName}/mode`, {
|
|
3772
|
+
method: "PUT",
|
|
3773
|
+
data: { mode },
|
|
3774
|
+
})
|
|
3775
|
+
.then(() => undefined);
|
|
3776
|
+
}
|
|
3777
|
+
updatePreferences({ roomName, preferences }) {
|
|
3778
|
+
assertRoomName(roomName);
|
|
3779
|
+
assertInstanceOf(preferences, Object, "preferences");
|
|
3780
|
+
const encodedDisplayName = encodeURIComponent(roomName.substring(1));
|
|
3781
|
+
return this._organizationApiClient
|
|
3782
|
+
.request(`/room/${encodedDisplayName}/preferences`, {
|
|
3783
|
+
method: "PATCH",
|
|
3784
|
+
data: preferences,
|
|
3785
|
+
})
|
|
3786
|
+
.then(() => undefined);
|
|
3787
|
+
}
|
|
3788
|
+
updateProtectedPreferences({ roomName, preferences }) {
|
|
3789
|
+
assertRoomName(roomName);
|
|
3790
|
+
assertInstanceOf(preferences, Object, "preferences");
|
|
3791
|
+
const encodedDisplayName = encodeURIComponent(roomName.substring(1));
|
|
3792
|
+
return this._organizationApiClient
|
|
3793
|
+
.request(`/room/${encodedDisplayName}/protected-preferences`, {
|
|
3794
|
+
method: "PATCH",
|
|
3795
|
+
data: preferences,
|
|
3796
|
+
})
|
|
3797
|
+
.then(() => undefined);
|
|
3798
|
+
}
|
|
3799
|
+
getRoomPermissions(roomName, { roomKey } = {}) {
|
|
3800
|
+
assertRoomName(roomName);
|
|
3801
|
+
return this._organizationApiClient
|
|
3802
|
+
.request(createRoomUrl(roomName, "/permissions"), Object.assign({ method: "GET" }, (roomKey && { headers: { "X-Whereby-Room-Key": roomKey } })))
|
|
3803
|
+
.then((response) => {
|
|
3804
|
+
const { permissions, limits } = response.data;
|
|
3805
|
+
return {
|
|
3806
|
+
permissions,
|
|
3807
|
+
limits,
|
|
3808
|
+
};
|
|
3809
|
+
});
|
|
3810
|
+
}
|
|
3811
|
+
getRoomMetrics({ roomName, metrics, from, to }) {
|
|
3812
|
+
assertRoomName(roomName);
|
|
3813
|
+
assertString(metrics, "metrics");
|
|
3814
|
+
return this._organizationApiClient
|
|
3815
|
+
.request(createRoomUrl(roomName, "/metrics"), {
|
|
3816
|
+
method: "GET",
|
|
3817
|
+
params: { metrics, from, to },
|
|
3818
|
+
})
|
|
3819
|
+
.then((response) => response.data);
|
|
3820
|
+
}
|
|
3821
|
+
changeType({ roomName, type }) {
|
|
3822
|
+
assertRoomName(roomName);
|
|
3823
|
+
assertOneOf(type, ["personal", "personal_xl"], "type");
|
|
3824
|
+
const encodedDisplayName = encodeURIComponent(roomName.substring(1));
|
|
3825
|
+
return this._organizationApiClient
|
|
3826
|
+
.request(`/room/${encodedDisplayName}/type`, {
|
|
3827
|
+
method: "PUT",
|
|
3828
|
+
data: { type },
|
|
3829
|
+
})
|
|
3830
|
+
.then(() => undefined);
|
|
3831
|
+
}
|
|
3832
|
+
getForestSocialImage({ roomName, count }) {
|
|
3833
|
+
assertRoomName(roomName);
|
|
3834
|
+
assertNumber(count, "count");
|
|
3835
|
+
return this._organizationApiClient
|
|
3836
|
+
.request(createRoomUrl(roomName, `/forest-social-image/${count}`), {
|
|
3837
|
+
method: "GET",
|
|
3838
|
+
})
|
|
3839
|
+
.then((response) => response.data.imageUrl);
|
|
3840
|
+
}
|
|
3841
|
+
}
|
|
3842
|
+
|
|
3843
|
+
class RoomParticipant {
|
|
3844
|
+
constructor({ displayName, id, stream, isAudioEnabled, isVideoEnabled, breakoutGroup, stickyReaction, isDialIn, }) {
|
|
3845
|
+
this.isLocalParticipant = false;
|
|
3846
|
+
this.displayName = displayName;
|
|
3847
|
+
this.id = id;
|
|
3848
|
+
this.stream = stream;
|
|
3849
|
+
this.isAudioEnabled = isAudioEnabled;
|
|
3850
|
+
this.isVideoEnabled = isVideoEnabled;
|
|
3851
|
+
this.breakoutGroup = breakoutGroup;
|
|
3852
|
+
this.stickyReaction = stickyReaction;
|
|
3853
|
+
this.isDialIn = isDialIn;
|
|
3854
|
+
}
|
|
3855
|
+
}
|
|
3856
|
+
class LocalParticipant extends RoomParticipant {
|
|
3857
|
+
constructor({ displayName, id, stream, isAudioEnabled, isVideoEnabled, breakoutGroup, stickyReaction, isDialIn, }) {
|
|
3858
|
+
super({ displayName, id, stream, isAudioEnabled, isVideoEnabled, breakoutGroup, stickyReaction, isDialIn });
|
|
3859
|
+
this.isLocalParticipant = true;
|
|
3860
|
+
}
|
|
3861
|
+
}
|
|
3862
|
+
|
|
3863
|
+
const API_BASE_URL = "https://api.whereby.dev" ;
|
|
3864
|
+
function createServices() {
|
|
3865
|
+
const credentialsService = CredentialsService.create({ baseUrl: API_BASE_URL });
|
|
3866
|
+
const apiClient = new ApiClient({
|
|
3867
|
+
fetchDeviceCredentials: credentialsService.getCredentials.bind(credentialsService),
|
|
3868
|
+
baseUrl: API_BASE_URL,
|
|
3869
|
+
});
|
|
3870
|
+
const organizationService = new OrganizationService({ apiClient });
|
|
3871
|
+
const fetchOrganizationFromRoomUrl = (roomUrl) => {
|
|
3872
|
+
const roomUrlObj = new URL(roomUrl);
|
|
3873
|
+
const urls = fromLocation({ host: roomUrlObj.host });
|
|
3874
|
+
const organizationServiceCache = new OrganizationServiceCache({
|
|
3875
|
+
organizationService,
|
|
3876
|
+
subdomain: urls.subdomain,
|
|
3877
|
+
});
|
|
3878
|
+
return organizationServiceCache.fetchOrganization();
|
|
3879
|
+
};
|
|
3880
|
+
return {
|
|
3881
|
+
credentialsService,
|
|
3882
|
+
apiClient,
|
|
3883
|
+
organizationService,
|
|
3884
|
+
fetchOrganizationFromRoomUrl,
|
|
3885
|
+
};
|
|
3886
|
+
}
|
|
3887
|
+
|
|
3888
|
+
export { ApiClient, Credentials, CredentialsService, LocalParticipant, OrganizationApiClient, OrganizationService, OrganizationServiceCache, RoomService, addAppListener, addSpotlight, appSlice, authorizationSlice, chatSlice, cloudRecordingSlice, connectionMonitorSlice, connectionMonitorStarted, connectionMonitorStopped, createAppAsyncThunk, createAppAuthorizedThunk, createAppThunk, createAsyncRoomConnectedThunk, createAuthorizedRoomConnectedThunk, createReactor, createRemoteParticipant, createRoomConnectedThunk, createServices, createStore, createWebRtcEmitter, debounce, deviceBusy, deviceCredentialsSlice, deviceIdentified, deviceIdentifying, doAcceptWaitingParticipant, doAppStart, doAppStop, 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, getAudioTrack, getFakeMediaStream, getVideoTrack, hasValue, initialCloudRecordingState, initialLocalMediaState, initialNotificationsState, initialState$g as initialState, isAcceptingStreams, isClientSpotlighted, listenerMiddleware, localMediaSlice, localMediaStopped, localParticipantSlice, localScreenshareSlice, localStreamMetadataUpdated, notificationsSlice, observeStore, organizationSlice, parseRoomUrlAndSubdomain, parseUnverifiedRoomKeyData, participantStreamAdded, participantStreamIdAdded, recordingRequestStarted, remoteParticipantsSlice, removeSpotlight, resolutionReported, roomConnectionSlice, roomSlice, rootReducer, rtcAnalyticsCustomEvents, rtcAnalyticsSlice, rtcConnectionSlice, rtcDisconnected, rtcDispatcherCreated, rtcManagerCreated, rtcManagerDestroyed, rtcManagerInitialized, selectAllClientViews, selectAppDisplayName, selectAppExternalId, selectAppInitialConfig, selectAppIsActive, selectAppIsDialIn, selectAppIsNodeSdk, selectAppRaw, selectAppRoomName, selectAppRoomUrl, selectAppUserAgent, selectAuthorizationRoleName, 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, selectLocalParticipantClientClaim, selectLocalParticipantDisplayName, selectLocalParticipantIsScreenSharing, selectLocalParticipantRaw, selectLocalParticipantStickyReaction, selectLocalParticipantView, selectLocalScreenshareRaw, selectLocalScreenshareStatus, selectLocalScreenshareStream, selectMicrophoneDeviceError, selectMicrophoneDevices, selectNotificationsEmitter, selectNotificationsEvents, selectNotificationsRaw, selectNumClients, selectNumParticipants, selectOrganizationId, 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, setCurrentCameraDeviceId, setCurrentMicrophoneDeviceId, setCurrentSpeakerDeviceId, setDisplayName, setLocalMediaOptions, setLocalMediaStream, setRoomKey, signalConnectionSlice, signalEvents, socketConnected, socketConnecting, socketDisconnected, socketReconnecting, spotlightsSlice, startAppListening, stopScreenshare, streamIdForClient, streamStatusUpdated, streamingSlice, toggleCameraEnabled, toggleLowDataModeEnabled, toggleMicrophoneEnabled, updateReportedValues, waitingParticipantsSlice };
|