@whereby.com/core 0.31.4 → 0.32.1

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.
@@ -73,9 +73,9 @@ const createReactor = (selectors, callback) => {
73
73
  });
74
74
  };
75
75
 
76
- const coreVersion = "0.31.4";
76
+ const coreVersion = "0.32.1";
77
77
 
78
- const initialState$g = {
78
+ const initialState$h = {
79
79
  isNodeSdk: false,
80
80
  isActive: false,
81
81
  isDialIn: false,
@@ -87,7 +87,7 @@ const initialState$g = {
87
87
  };
88
88
  const appSlice = createSlice({
89
89
  name: "app",
90
- initialState: initialState$g,
90
+ initialState: initialState$h,
91
91
  reducers: {
92
92
  doAppStart: (state, action) => {
93
93
  const url = new URL(action.payload.roomUrl);
@@ -117,6 +117,9 @@ const signalEvents = {
117
117
  audioEnabled: createSignalEventAction("audioEnabled"),
118
118
  audioEnableRequested: createSignalEventAction("audioEnableRequested"),
119
119
  breakoutGroupJoined: createSignalEventAction("breakoutGroupJoined"),
120
+ breakoutMoveToGroup: createSignalEventAction("breakoutMoveToGroup"),
121
+ breakoutMoveToMain: createSignalEventAction("breakoutMoveToMain"),
122
+ breakoutSessionUpdated: createSignalEventAction("breakoutSessionUpdated"),
120
123
  chatMessage: createSignalEventAction("chatMessage"),
121
124
  clientLeft: createSignalEventAction("clientLeft"),
122
125
  clientKicked: createSignalEventAction("clientKicked"),
@@ -153,13 +156,13 @@ const ROOM_ACTION_PERMISSIONS_BY_ROLE = {
153
156
  canAskToSpeak: ["host"],
154
157
  canSpotlight: ["host"],
155
158
  };
156
- const initialState$f = {
159
+ const initialState$g = {
157
160
  roomKey: null,
158
161
  roleName: "none",
159
162
  };
160
163
  const authorizationSlice = createSlice({
161
164
  name: "authorization",
162
- initialState: initialState$f,
165
+ initialState: initialState$g,
163
166
  reducers: {
164
167
  setRoomKey: (state, action) => {
165
168
  return Object.assign(Object.assign({}, state), { roomKey: action.payload });
@@ -231,13 +234,13 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
231
234
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
232
235
  };
233
236
 
234
- const initialState$e = {
237
+ const initialState$f = {
235
238
  isFetching: false,
236
239
  data: null,
237
240
  };
238
241
  const deviceCredentialsSlice = createSlice({
239
242
  name: "deviceCredentials",
240
- initialState: initialState$e,
243
+ initialState: initialState$f,
241
244
  reducers: {},
242
245
  extraReducers: (builder) => {
243
246
  builder.addCase(doGetDeviceCredentials.pending, (state) => {
@@ -304,6 +307,9 @@ function forwardSocketEvents(socket, dispatch) {
304
307
  socket.on("live_transcription_stopped", (payload) => dispatch(signalEvents.liveTranscriptionStopped(payload)));
305
308
  socket.on("video_enable_requested", (payload) => dispatch(signalEvents.videoEnableRequested(payload)));
306
309
  socket.on("breakout_group_joined", (payload) => dispatch(signalEvents.breakoutGroupJoined(payload)));
310
+ socket.on("breakout_session_updated", (payload) => dispatch(signalEvents.breakoutSessionUpdated(payload)));
311
+ socket.on("breakout_move_to_group", () => dispatch(signalEvents.breakoutMoveToGroup()));
312
+ socket.on("breakout_move_to_main", () => dispatch(signalEvents.breakoutMoveToMain()));
307
313
  }
308
314
  const SIGNAL_BASE_URL = "wss://signal.appearin.net" ;
309
315
  function createSocket() {
@@ -314,7 +320,7 @@ function createSocket() {
314
320
  };
315
321
  return new ServerSocket(socketHost, socketOverrides);
316
322
  }
317
- const initialState$d = {
323
+ const initialState$e = {
318
324
  deviceIdentified: false,
319
325
  isIdentifyingDevice: false,
320
326
  status: "ready",
@@ -322,7 +328,7 @@ const initialState$d = {
322
328
  };
323
329
  const signalConnectionSlice = createSlice({
324
330
  name: "signalConnection",
325
- initialState: initialState$d,
331
+ initialState: initialState$e,
326
332
  reducers: {
327
333
  socketConnecting: (state) => {
328
334
  return Object.assign(Object.assign({}, state), { status: "connecting" });
@@ -426,109 +432,6 @@ startAppListening({
426
432
  },
427
433
  });
428
434
 
429
- const initialState$c = {
430
- chatMessages: [],
431
- };
432
- const chatSlice = createSlice({
433
- name: "chat",
434
- initialState: initialState$c,
435
- reducers: {},
436
- extraReducers(builder) {
437
- builder.addCase(signalEvents.chatMessage, (state, action) => {
438
- const message = {
439
- senderId: action.payload.senderId,
440
- timestamp: action.payload.timestamp,
441
- text: action.payload.text,
442
- };
443
- return Object.assign(Object.assign({}, state), { chatMessages: [...state.chatMessages, message] });
444
- });
445
- },
446
- });
447
- const doSendChatMessage = createRoomConnectedThunk((payload) => (_, getState) => {
448
- const state = getState();
449
- const socket = selectSignalConnectionRaw(state).socket;
450
- socket === null || socket === void 0 ? void 0 : socket.emit("chat_message", { text: payload.text });
451
- });
452
- const selectChatRaw = (state) => state.chat;
453
- const selectChatMessages = (state) => state.chat.chatMessages;
454
-
455
- const initialCloudRecordingState = {
456
- isRecording: false,
457
- error: null,
458
- startedAt: undefined,
459
- };
460
- const cloudRecordingSlice = createSlice({
461
- name: "cloudRecording",
462
- initialState: initialCloudRecordingState,
463
- reducers: {
464
- recordingRequestStarted: (state) => {
465
- return Object.assign(Object.assign({}, state), { status: "requested" });
466
- },
467
- },
468
- extraReducers: (builder) => {
469
- builder.addCase(signalEvents.cloudRecordingStopped, (state) => {
470
- return Object.assign(Object.assign({}, state), { isRecording: false, status: undefined });
471
- });
472
- builder.addCase(signalEvents.cloudRecordingStarted, (state, action) => {
473
- const { payload } = action;
474
- if (!payload.error) {
475
- return state;
476
- }
477
- return Object.assign(Object.assign({}, state), { isRecording: false, status: "error", error: payload.error });
478
- });
479
- builder.addCase(signalEvents.newClient, (state, { payload }) => {
480
- var _a;
481
- const { client } = payload;
482
- if (((_a = client.role) === null || _a === void 0 ? void 0 : _a.roleName) === "recorder") {
483
- return Object.assign(Object.assign({}, state), { isRecording: true, status: "recording", startedAt: client.startedCloudRecordingAt
484
- ? new Date(client.startedCloudRecordingAt).getTime()
485
- : new Date().getTime() });
486
- }
487
- return state;
488
- });
489
- },
490
- });
491
- const { recordingRequestStarted } = cloudRecordingSlice.actions;
492
- const doStartCloudRecording = createRoomConnectedThunk(() => (dispatch, getState) => {
493
- const state = getState();
494
- const socket = selectSignalConnectionRaw(state).socket;
495
- const status = selectCloudRecordingStatus(state);
496
- if (status && ["recording", "requested"].includes(status)) {
497
- return;
498
- }
499
- socket === null || socket === void 0 ? void 0 : socket.emit("start_recording", {
500
- recording: "cloud",
501
- });
502
- dispatch(recordingRequestStarted());
503
- });
504
- const doStopCloudRecording = createRoomConnectedThunk(() => (dispatch, getState) => {
505
- const state = getState();
506
- const socket = selectSignalConnectionRaw(state).socket;
507
- socket === null || socket === void 0 ? void 0 : socket.emit("stop_recording");
508
- });
509
- const selectCloudRecordingRaw = (state) => state.cloudRecording;
510
- const selectCloudRecordingStatus = (state) => state.cloudRecording.status;
511
- const selectCloudRecordingStartedAt = (state) => state.cloudRecording.startedAt;
512
- const selectCloudRecordingError = (state) => state.cloudRecording.error;
513
- const selectIsCloudRecording = (state) => state.cloudRecording.isRecording;
514
-
515
- const selectRoomConnectionRaw = (state) => state.roomConnection;
516
- const selectRoomConnectionSession = (state) => state.roomConnection.session;
517
- const selectRoomConnectionSessionId = (state) => { var _a; return (_a = state.roomConnection.session) === null || _a === void 0 ? void 0 : _a.id; };
518
- const selectRoomConnectionStatus = (state) => state.roomConnection.status;
519
- const selectRoomConnectionError = (state) => state.roomConnection.error;
520
-
521
- function createRtcEventAction(name) {
522
- return createAction(`rtcConnection/event/${name}`);
523
- }
524
- const rtcEvents = {
525
- rtcManagerCreated: createRtcEventAction("rtcManagerCreated"),
526
- rtcManagerDestroyed: createRtcEventAction("rtcManagerDestroyed"),
527
- streamAdded: createRtcEventAction("streamAdded"),
528
- };
529
-
530
- const NON_PERSON_ROLES = ["recorder", "streamer"];
531
-
532
435
  function fakeAudioStream() {
533
436
  const audioCtx = new AudioContext();
534
437
  const oscillator = audioCtx.createOscillator();
@@ -1098,115 +1001,31 @@ startAppListening({
1098
1001
  }
1099
1002
  },
1100
1003
  });
1101
-
1102
- const initialState$b = {
1103
- displayName: "",
1104
- id: "",
1105
- breakoutGroup: null,
1106
- isAudioEnabled: true,
1107
- isVideoEnabled: true,
1108
- isLocalParticipant: true,
1109
- stream: undefined,
1110
- isScreenSharing: false,
1111
- roleName: "none",
1112
- clientClaim: undefined,
1113
- stickyReaction: undefined,
1114
- isDialIn: false,
1115
- };
1116
- const localParticipantSlice = createSlice({
1117
- name: "localParticipant",
1118
- initialState: initialState$b,
1119
- reducers: {
1120
- setDisplayName: (state, action) => {
1121
- return Object.assign(Object.assign({}, state), { displayName: action.payload.displayName });
1122
- },
1123
- },
1124
- extraReducers: (builder) => {
1125
- builder.addCase(doAppStart, (state, action) => {
1126
- return Object.assign(Object.assign({}, state), { displayName: action.payload.displayName });
1127
- });
1128
- builder.addCase(doEnableAudio.fulfilled, (state, action) => {
1129
- return Object.assign(Object.assign({}, state), { isAudioEnabled: action.payload });
1130
- });
1131
- builder.addCase(doEnableVideo.fulfilled, (state, action) => {
1132
- return Object.assign(Object.assign({}, state), { isVideoEnabled: action.payload });
1133
- });
1134
- builder.addCase(doSetLocalStickyReaction.fulfilled, (state, action) => {
1135
- return Object.assign(Object.assign({}, state), { stickyReaction: action.payload });
1136
- });
1137
- builder.addCase(signalEvents.roomJoined, (state, action) => {
1138
- var _a, _b;
1139
- 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); });
1140
- 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 });
1141
- });
1142
- builder.addCase(signalEvents.breakoutGroupJoined, (state, action) => {
1143
- var _a, _b;
1144
- if (((_a = action.payload) === null || _a === void 0 ? void 0 : _a.clientId) !== state.id) {
1145
- return state;
1146
- }
1147
- return Object.assign(Object.assign({}, state), { breakoutGroup: (_b = action.payload) === null || _b === void 0 ? void 0 : _b.group });
1148
- });
1004
+ startAppListening({
1005
+ actionCreator: signalEvents.breakoutSessionUpdated,
1006
+ effect: ({ payload }, { dispatch }) => {
1007
+ var _a;
1008
+ if (((_a = payload.initiatedBy) === null || _a === void 0 ? void 0 : _a.active) === false) {
1009
+ dispatch(toggleMicrophoneEnabled({ enabled: false }));
1010
+ }
1149
1011
  },
1150
1012
  });
1151
- const { setDisplayName } = localParticipantSlice.actions;
1152
- const doSetDisplayName = createRoomConnectedThunk((payload) => (dispatch, getState) => {
1153
- const state = getState();
1154
- const socket = selectSignalConnectionRaw(state).socket;
1155
- socket === null || socket === void 0 ? void 0 : socket.emit("send_client_metadata", {
1156
- type: "UserData",
1157
- payload: { displayName: payload.displayName },
1158
- });
1159
- dispatch(setDisplayName({ displayName: payload.displayName }));
1160
- });
1161
- const doEnableAudio = createAsyncRoomConnectedThunk("localParticipant/doEnableAudio", (payload, { dispatch, getState }) => __awaiter(void 0, void 0, void 0, function* () {
1162
- const state = getState();
1163
- const socket = selectSignalConnectionRaw(state).socket;
1164
- socket === null || socket === void 0 ? void 0 : socket.emit("enable_audio", { enabled: payload.enabled });
1165
- if (payload.enabled) {
1166
- dispatch(doSetLocalStickyReaction({ enabled: false }));
1167
- }
1168
- return payload.enabled;
1169
- }));
1170
- const doEnableVideo = createAsyncRoomConnectedThunk("localParticipant/doEnableVideo", (payload, { getState }) => __awaiter(void 0, void 0, void 0, function* () {
1171
- const state = getState();
1172
- const socket = selectSignalConnectionRaw(state).socket;
1173
- socket === null || socket === void 0 ? void 0 : socket.emit("enable_video", { enabled: payload.enabled });
1174
- return payload.enabled;
1175
- }));
1176
- const doSetLocalStickyReaction = createAsyncRoomConnectedThunk("localParticipant/doSetLocalStickyReaction", (payload, { getState, rejectWithValue }) => __awaiter(void 0, void 0, void 0, function* () {
1177
- var _a;
1178
- const state = getState();
1179
- const currentStickyReaction = selectLocalParticipantStickyReaction(state);
1180
- const stickyReactionCurrentlyEnabled = Boolean(currentStickyReaction);
1181
- const enabled = (_a = payload.enabled) !== null && _a !== void 0 ? _a : !stickyReactionCurrentlyEnabled;
1182
- if (enabled === stickyReactionCurrentlyEnabled) {
1183
- return rejectWithValue(currentStickyReaction);
1184
- }
1185
- const stickyReaction = enabled ? { reaction: "✋", timestamp: new Date().toISOString() } : null;
1186
- return stickyReaction;
1187
- }));
1188
- const doSendClientMetadata = createRoomConnectedThunk(() => (_, getState) => {
1189
- const state = getState();
1190
- const socket = selectSignalConnectionRaw(state).socket;
1191
- const payload = {
1192
- displayName: selectLocalParticipantDisplayName(state),
1193
- stickyReaction: selectLocalParticipantStickyReaction(state),
1194
- };
1195
- socket === null || socket === void 0 ? void 0 : socket.emit("send_client_metadata", {
1196
- type: "UserData",
1197
- payload,
1198
- });
1199
- });
1013
+
1014
+ const NON_PERSON_ROLES = ["recorder", "streamer"];
1015
+
1200
1016
  const selectLocalParticipantRaw = (state) => state.localParticipant;
1201
1017
  const selectSelfId = (state) => state.localParticipant.id;
1202
1018
  const selectLocalParticipantDisplayName = (state) => state.localParticipant.displayName;
1203
1019
  const selectLocalParticipantClientClaim = (state) => state.localParticipant.clientClaim;
1204
1020
  const selectLocalParticipantIsScreenSharing = (state) => state.localParticipant.isScreenSharing;
1205
1021
  const selectLocalParticipantStickyReaction = (state) => state.localParticipant.stickyReaction;
1022
+ const selectLocalParticipantBreakoutGroup = (state) => state.localParticipant.breakoutGroup;
1023
+ const selectLocalParticipantBreakoutAssigned = (state) => state.localParticipant.breakoutGroupAssigned;
1206
1024
  const selectLocalParticipantView = createSelector(selectLocalParticipantRaw, selectLocalMediaStream, (participant, localStream) => {
1207
1025
  const clientView = {
1208
1026
  id: participant.id,
1209
1027
  clientId: participant.id,
1028
+ breakoutGroup: participant.breakoutGroup,
1210
1029
  displayName: participant.displayName,
1211
1030
  stream: localStream,
1212
1031
  isLocalClient: true,
@@ -1218,35 +1037,202 @@ const selectLocalParticipantView = createSelector(selectLocalParticipantRaw, sel
1218
1037
  }
1219
1038
  return clientView;
1220
1039
  });
1040
+
1041
+ function createBreakout({ assignments, groups, startedAt, initiatedBy, breakoutStartedAt, breakoutEndedAt, breakoutNotification, breakoutTimerDuration, autoMoveToGroup, moveToGroupGracePeriod, autoMoveToMain, moveToMainGracePeriod, enforceAssignment, breakoutTimerSetting, } = {}) {
1042
+ return {
1043
+ assignments: assignments || null,
1044
+ groups: groups || null,
1045
+ startedAt: startedAt ? new Date(startedAt) : null,
1046
+ initiatedBy,
1047
+ breakoutStartedAt: breakoutStartedAt || null,
1048
+ breakoutEndedAt: breakoutEndedAt || null,
1049
+ breakoutNotification: breakoutNotification || null,
1050
+ breakoutTimerDuration: breakoutTimerDuration || 1800,
1051
+ autoMoveToGroup: autoMoveToGroup || false,
1052
+ moveToGroupGracePeriod: moveToGroupGracePeriod || 10,
1053
+ autoMoveToMain: autoMoveToMain || false,
1054
+ moveToMainGracePeriod: moveToMainGracePeriod || 30,
1055
+ enforceAssignment: enforceAssignment || false,
1056
+ breakoutTimerSetting: breakoutTimerSetting || false,
1057
+ };
1058
+ }
1059
+ const initialState$d = Object.assign(Object.assign({}, createBreakout()), { groupId: null });
1060
+ const breakoutSlice = createSlice({
1061
+ name: "breakout",
1062
+ initialState: initialState$d,
1063
+ reducers: {},
1064
+ extraReducers: (builder) => {
1065
+ builder.addCase(signalEvents.roomJoined, (state, action) => {
1066
+ if (action.payload.breakout) {
1067
+ return Object.assign(Object.assign({}, state), createBreakout(action.payload.breakout));
1068
+ }
1069
+ return state;
1070
+ });
1071
+ builder.addCase(signalEvents.breakoutSessionUpdated, (state, action) => {
1072
+ return Object.assign(Object.assign({}, state), createBreakout(action.payload));
1073
+ });
1074
+ builder.addCase(signalEvents.breakoutGroupJoined, (state, action) => {
1075
+ var _a;
1076
+ if (((_a = action.meta) === null || _a === void 0 ? void 0 : _a.localParticipantId) !== action.payload.clientId) {
1077
+ return state;
1078
+ }
1079
+ return Object.assign(Object.assign({}, state), { groupId: action.payload.group });
1080
+ });
1081
+ },
1082
+ });
1083
+ const doBreakoutJoin = createAppThunk((payload) => (_, getState) => {
1084
+ const state = getState();
1085
+ const { socket } = selectSignalConnectionRaw(state);
1086
+ socket === null || socket === void 0 ? void 0 : socket.emit("join_breakout_group", { group: payload.group });
1087
+ });
1088
+ const selectBreakoutRaw = (state) => state.breakout;
1089
+ const selectBreakoutInitiatedBy = (state) => state.breakout.initiatedBy;
1090
+ const selectBreakoutActive = (state) => !!state.breakout.startedAt;
1091
+ const selectBreakoutAssignments = (state) => state.breakout.assignments;
1092
+ const selectBreakoutGroups = (state) => state.breakout.groups;
1093
+ const selectBreakoutCurrentId = createSelector(selectBreakoutRaw, selectLocalParticipantBreakoutGroup, (raw, localParticipantBreakoutGroup) => {
1094
+ return raw.groupId || localParticipantBreakoutGroup || "";
1095
+ });
1096
+ const selectBreakoutCurrentGroup = createSelector(selectBreakoutRaw, selectBreakoutCurrentId, (raw, breakoutCurrentId) => {
1097
+ var _a;
1098
+ const name = (_a = raw.groups) === null || _a === void 0 ? void 0 : _a[breakoutCurrentId];
1099
+ if (!name)
1100
+ return null;
1101
+ return { id: breakoutCurrentId, name };
1102
+ });
1221
1103
  startAppListening({
1222
- actionCreator: toggleCameraEnabled,
1223
- effect: ({ payload }, { dispatch, getState }) => {
1224
- const { enabled } = payload;
1225
- const { isVideoEnabled } = selectLocalParticipantRaw(getState());
1226
- const roomConnectionStatus = selectRoomConnectionStatus(getState());
1227
- if (roomConnectionStatus !== "connected") {
1228
- return;
1104
+ actionCreator: signalEvents.breakoutMoveToGroup,
1105
+ effect: (_, { dispatch, getState }) => {
1106
+ const state = getState();
1107
+ const localParticipant = selectLocalParticipantRaw(state);
1108
+ const breakoutGroupAssigned = localParticipant.breakoutGroupAssigned;
1109
+ if (breakoutGroupAssigned) {
1110
+ dispatch(doBreakoutJoin({ group: breakoutGroupAssigned }));
1229
1111
  }
1230
- dispatch(doEnableVideo({ enabled: enabled || !isVideoEnabled }));
1231
1112
  },
1232
1113
  });
1233
1114
  startAppListening({
1234
- actionCreator: toggleMicrophoneEnabled,
1115
+ actionCreator: signalEvents.breakoutMoveToMain,
1116
+ effect: (_, { dispatch }) => {
1117
+ dispatch(doBreakoutJoin({ group: "" }));
1118
+ },
1119
+ });
1120
+ startAppListening({
1121
+ actionCreator: signalEvents.breakoutSessionUpdated,
1235
1122
  effect: ({ payload }, { dispatch, getState }) => {
1236
- const { enabled } = payload;
1237
- const { isAudioEnabled } = selectLocalParticipantRaw(getState());
1238
- const roomConnectionStatus = selectRoomConnectionStatus(getState());
1239
- if (roomConnectionStatus !== "connected") {
1240
- return;
1123
+ var _a;
1124
+ const state = getState();
1125
+ const autoMoveToMain = selectBreakoutRaw(state).autoMoveToMain;
1126
+ if (((_a = payload.initiatedBy) === null || _a === void 0 ? void 0 : _a.active) === false) {
1127
+ if (!autoMoveToMain) {
1128
+ dispatch(doBreakoutJoin({ group: "" }));
1129
+ }
1241
1130
  }
1242
- dispatch(doEnableAudio({ enabled: enabled || !isAudioEnabled }));
1243
1131
  },
1244
1132
  });
1245
- createReactor([selectLocalParticipantDisplayName, selectLocalParticipantStickyReaction, selectRoomConnectionStatus], ({ dispatch }, diplayName, stickyReaction, roomConnectionStatus) => {
1246
- if (roomConnectionStatus === "connected") {
1247
- dispatch(doSendClientMetadata());
1133
+
1134
+ const initialState$c = {
1135
+ chatMessages: [],
1136
+ };
1137
+ const chatSlice = createSlice({
1138
+ name: "chat",
1139
+ initialState: initialState$c,
1140
+ reducers: {},
1141
+ extraReducers(builder) {
1142
+ builder.addCase(signalEvents.chatMessage, (state, action) => {
1143
+ const message = {
1144
+ senderId: action.payload.senderId,
1145
+ timestamp: action.payload.timestamp,
1146
+ text: action.payload.text,
1147
+ };
1148
+ return Object.assign(Object.assign({}, state), { chatMessages: [...state.chatMessages, message] });
1149
+ });
1150
+ },
1151
+ });
1152
+ const doSendChatMessage = createRoomConnectedThunk((payload) => (_, getState) => {
1153
+ const state = getState();
1154
+ const socket = selectSignalConnectionRaw(state).socket;
1155
+ const breakoutCurrentId = selectBreakoutCurrentId(state);
1156
+ socket === null || socket === void 0 ? void 0 : socket.emit("chat_message", Object.assign(Object.assign({ text: payload.text }, (breakoutCurrentId && { breakoutGroup: breakoutCurrentId })), (payload.isBroadcast && { broadcast: true })));
1157
+ });
1158
+ const selectChatRaw = (state) => state.chat;
1159
+ const selectChatMessages = (state) => state.chat.chatMessages;
1160
+
1161
+ const initialCloudRecordingState = {
1162
+ isRecording: false,
1163
+ error: null,
1164
+ startedAt: undefined,
1165
+ };
1166
+ const cloudRecordingSlice = createSlice({
1167
+ name: "cloudRecording",
1168
+ initialState: initialCloudRecordingState,
1169
+ reducers: {
1170
+ recordingRequestStarted: (state) => {
1171
+ return Object.assign(Object.assign({}, state), { status: "requested" });
1172
+ },
1173
+ },
1174
+ extraReducers: (builder) => {
1175
+ builder.addCase(signalEvents.cloudRecordingStopped, (state) => {
1176
+ return Object.assign(Object.assign({}, state), { isRecording: false, status: undefined });
1177
+ });
1178
+ builder.addCase(signalEvents.cloudRecordingStarted, (state, action) => {
1179
+ const { payload } = action;
1180
+ if (!payload.error) {
1181
+ return state;
1182
+ }
1183
+ return Object.assign(Object.assign({}, state), { isRecording: false, status: "error", error: payload.error });
1184
+ });
1185
+ builder.addCase(signalEvents.newClient, (state, { payload }) => {
1186
+ var _a;
1187
+ const { client } = payload;
1188
+ if (((_a = client.role) === null || _a === void 0 ? void 0 : _a.roleName) === "recorder") {
1189
+ return Object.assign(Object.assign({}, state), { isRecording: true, status: "recording", startedAt: client.startedCloudRecordingAt
1190
+ ? new Date(client.startedCloudRecordingAt).getTime()
1191
+ : new Date().getTime() });
1192
+ }
1193
+ return state;
1194
+ });
1195
+ },
1196
+ });
1197
+ const { recordingRequestStarted } = cloudRecordingSlice.actions;
1198
+ const doStartCloudRecording = createRoomConnectedThunk(() => (dispatch, getState) => {
1199
+ const state = getState();
1200
+ const socket = selectSignalConnectionRaw(state).socket;
1201
+ const status = selectCloudRecordingStatus(state);
1202
+ if (status && ["recording", "requested"].includes(status)) {
1203
+ return;
1248
1204
  }
1205
+ socket === null || socket === void 0 ? void 0 : socket.emit("start_recording", {
1206
+ recording: "cloud",
1207
+ });
1208
+ dispatch(recordingRequestStarted());
1249
1209
  });
1210
+ const doStopCloudRecording = createRoomConnectedThunk(() => (dispatch, getState) => {
1211
+ const state = getState();
1212
+ const socket = selectSignalConnectionRaw(state).socket;
1213
+ socket === null || socket === void 0 ? void 0 : socket.emit("stop_recording");
1214
+ });
1215
+ const selectCloudRecordingRaw = (state) => state.cloudRecording;
1216
+ const selectCloudRecordingStatus = (state) => state.cloudRecording.status;
1217
+ const selectCloudRecordingStartedAt = (state) => state.cloudRecording.startedAt;
1218
+ const selectCloudRecordingError = (state) => state.cloudRecording.error;
1219
+ const selectIsCloudRecording = (state) => state.cloudRecording.isRecording;
1220
+
1221
+ const selectRoomConnectionRaw = (state) => state.roomConnection;
1222
+ const selectRoomConnectionSession = (state) => state.roomConnection.session;
1223
+ const selectRoomConnectionSessionId = (state) => { var _a; return (_a = state.roomConnection.session) === null || _a === void 0 ? void 0 : _a.id; };
1224
+ const selectRoomConnectionStatus = (state) => state.roomConnection.status;
1225
+ const selectRoomConnectionError = (state) => state.roomConnection.error;
1226
+
1227
+ function createRtcEventAction(name) {
1228
+ return createAction(`rtcConnection/event/${name}`);
1229
+ }
1230
+ const rtcEvents = {
1231
+ rtcManagerCreated: createRtcEventAction("rtcManagerCreated"),
1232
+ rtcManagerDestroyed: createRtcEventAction("rtcManagerDestroyed"),
1233
+ streamAdded: createRtcEventAction("streamAdded"),
1234
+ clientConnectionStatusChanged: createRtcEventAction("clientConnectionStatusChanged"),
1235
+ };
1250
1236
 
1251
1237
  function createRemoteParticipant(client, newJoiner = false) {
1252
1238
  const { streams, role, breakoutGroup } = client, rest = __rest(client, ["streams", "role", "breakoutGroup"]);
@@ -1339,12 +1325,12 @@ function addStream(state, payload) {
1339
1325
  presentationStream: stream,
1340
1326
  });
1341
1327
  }
1342
- const initialState$a = {
1328
+ const initialState$b = {
1343
1329
  remoteParticipants: [],
1344
1330
  };
1345
1331
  const remoteParticipantsSlice = createSlice({
1346
1332
  name: "remoteParticipants",
1347
- initialState: initialState$a,
1333
+ initialState: initialState$b,
1348
1334
  reducers: {
1349
1335
  streamStatusUpdated: (state, action) => {
1350
1336
  let newState = state;
@@ -1451,14 +1437,14 @@ const selectNumParticipants = createSelector(selectRemoteParticipants, selectLoc
1451
1437
  return clients.length + 1;
1452
1438
  });
1453
1439
 
1454
- const initialState$9 = {
1440
+ const initialState$a = {
1455
1441
  status: "inactive",
1456
1442
  stream: null,
1457
1443
  error: null,
1458
1444
  };
1459
1445
  const localScreenshareSlice = createSlice({
1460
1446
  name: "localScreenshare",
1461
- initialState: initialState$9,
1447
+ initialState: initialState$a,
1462
1448
  reducers: {
1463
1449
  stopScreenshare(state, action) {
1464
1450
  return Object.assign(Object.assign({}, state), { status: "inactive", stream: null });
@@ -1500,33 +1486,296 @@ const doStartScreenshare = createAsyncRoomConnectedThunk("localScreenshare/doSta
1500
1486
  catch (error) {
1501
1487
  return rejectWithValue(error);
1502
1488
  }
1503
- }));
1504
- const doStopScreenshare = createRoomConnectedThunk(() => (dispatch, getState) => {
1489
+ }));
1490
+ const doStopScreenshare = createRoomConnectedThunk(() => (dispatch, getState) => {
1491
+ const state = getState();
1492
+ const screenshareStream = selectLocalScreenshareStream(state);
1493
+ if (!screenshareStream) {
1494
+ return;
1495
+ }
1496
+ screenshareStream.getTracks().forEach((track) => track.stop());
1497
+ dispatch(stopScreenshare({ stream: screenshareStream }));
1498
+ });
1499
+ const selectLocalScreenshareRaw = (state) => state.localScreenshare;
1500
+ const selectLocalScreenshareStatus = (state) => state.localScreenshare.status;
1501
+ const selectLocalScreenshareStream = (state) => state.localScreenshare.stream;
1502
+ startAppListening({
1503
+ actionCreator: localMediaStopped,
1504
+ effect: (_, { getState }) => {
1505
+ const state = getState();
1506
+ const screenshareStream = selectLocalScreenshareStream(state);
1507
+ if (!screenshareStream) {
1508
+ return;
1509
+ }
1510
+ screenshareStream === null || screenshareStream === void 0 ? void 0 : screenshareStream.getTracks().forEach((track) => {
1511
+ track.stop();
1512
+ });
1513
+ },
1514
+ });
1515
+
1516
+ function isStreamerClient(client) {
1517
+ return client.roleName === "streamer";
1518
+ }
1519
+ function isRecorderClient(client) {
1520
+ return client.roleName === "recorder";
1521
+ }
1522
+ const initialState$9 = {
1523
+ isLocked: false,
1524
+ };
1525
+ const roomSlice = createSlice({
1526
+ name: "room",
1527
+ initialState: initialState$9,
1528
+ reducers: {},
1529
+ extraReducers: (builder) => {
1530
+ builder.addCase(signalEvents.roomJoined, (state, action) => {
1531
+ const { error, isLocked } = action.payload;
1532
+ if (error) {
1533
+ return state;
1534
+ }
1535
+ return Object.assign(Object.assign({}, state), { isLocked: Boolean(isLocked) });
1536
+ });
1537
+ builder.addCase(signalEvents.roomLocked, (state, action) => {
1538
+ const { isLocked } = action.payload;
1539
+ return Object.assign(Object.assign({}, state), { isLocked: Boolean(isLocked) });
1540
+ });
1541
+ },
1542
+ });
1543
+ const doLockRoom = createAppAuthorizedThunk((state) => selectIsAuthorizedToLockRoom(state), (payload) => (_, getState) => {
1544
+ const state = getState();
1545
+ const { socket } = selectSignalConnectionRaw(state);
1546
+ socket === null || socket === void 0 ? void 0 : socket.emit("set_lock", { locked: payload.locked });
1547
+ });
1548
+ const doKickParticipant = createAppAuthorizedThunk((state) => selectIsAuthorizedToKickClient(state), (payload) => (_, getState) => {
1549
+ const state = getState();
1550
+ const { socket } = selectSignalConnectionRaw(state);
1551
+ socket === null || socket === void 0 ? void 0 : socket.emit("kick_client", { clientId: payload.clientId, reasonId: "kick" });
1552
+ });
1553
+ const doEndMeeting = createAppAuthorizedThunk((state) => selectIsAuthorizedToEndMeeting(state), (payload) => (dispatch, getState) => {
1554
+ const state = getState();
1555
+ const clientsToKick = selectRemoteClients(state).map((c) => c.id);
1556
+ if (clientsToKick.length) {
1557
+ const { socket } = selectSignalConnectionRaw(state);
1558
+ socket === null || socket === void 0 ? void 0 : socket.emit("kick_client", { clientIds: clientsToKick, reasonId: "end-meeting" });
1559
+ }
1560
+ if (!payload.stayBehind) {
1561
+ dispatch(doAppStop());
1562
+ }
1563
+ });
1564
+ const selectRoomIsLocked = (state) => state.room.isLocked;
1565
+ const selectScreenshares = createSelector(selectLocalScreenshareStream, selectLocalParticipantRaw, selectRemoteParticipants, (localScreenshareStream, localParticipant, remoteParticipants) => {
1566
+ const screenshares = [];
1567
+ if (localScreenshareStream) {
1568
+ screenshares.push({
1569
+ id: localScreenshareStream.id || "local-screenshare",
1570
+ participantId: "local",
1571
+ hasAudioTrack: localScreenshareStream.getTracks().some((track) => track.kind === "audio"),
1572
+ breakoutGroup: localParticipant.breakoutGroup,
1573
+ stream: localScreenshareStream,
1574
+ isLocal: true,
1575
+ });
1576
+ }
1577
+ for (const participant of remoteParticipants) {
1578
+ if (participant.presentationStream) {
1579
+ screenshares.push({
1580
+ id: participant.presentationStream.id || `pres-${participant.id}`,
1581
+ participantId: participant.id,
1582
+ hasAudioTrack: participant.presentationStream.getTracks().some((track) => track.kind === "audio"),
1583
+ breakoutGroup: participant.breakoutGroup,
1584
+ stream: participant.presentationStream,
1585
+ isLocal: false,
1586
+ });
1587
+ }
1588
+ }
1589
+ return screenshares;
1590
+ });
1591
+ const selectRemoteClientViews = createSelector(selectLocalScreenshareStream, selectLocalParticipantRaw, selectRemoteParticipants, selectBreakoutCurrentId, selectBreakoutAssignments, (localScreenshareStream, localParticipant, remoteParticipants, breakoutCurrentId, breakoutAssignments) => {
1592
+ const views = [];
1593
+ if (localScreenshareStream) {
1594
+ const isScreenshareAudioEnabled = !!localScreenshareStream.getAudioTracks().length;
1595
+ views.push({
1596
+ clientId: localParticipant.id,
1597
+ displayName: "Your screenshare",
1598
+ id: "local-screenshare",
1599
+ isAudioEnabled: isScreenshareAudioEnabled,
1600
+ isLocalClient: true,
1601
+ isPresentation: true,
1602
+ isVideoEnabled: true,
1603
+ stream: localScreenshareStream,
1604
+ breakoutGroup: breakoutCurrentId || "",
1605
+ });
1606
+ }
1607
+ for (const c of remoteParticipants) {
1608
+ if (isStreamerClient(c) || isRecorderClient(c)) {
1609
+ continue;
1610
+ }
1611
+ const { presentationStream } = c, clientView = __rest(c, ["presentationStream"]);
1612
+ const displayName = c.displayName || "Guest";
1613
+ const isPresentationActive = presentationStream && presentationStream.active;
1614
+ const presentationId = "pres-" + c.id;
1615
+ const isStreamActive = c.stream && c.stream.active;
1616
+ const isVideoEnabled = c.isVideoEnabled;
1617
+ views.push(Object.assign(Object.assign(Object.assign({}, clientView), { breakoutGroupAssigned: (breakoutAssignments === null || breakoutAssignments === void 0 ? void 0 : breakoutAssignments[c.deviceId]) || "", clientId: c.id, displayName, hasActivePresentation: !!isPresentationActive }), (c.isVideoEnabled ? { isVideoEnabled } : {})));
1618
+ if (isPresentationActive) {
1619
+ 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 })));
1620
+ }
1621
+ }
1622
+ return views;
1623
+ });
1624
+ const selectAllClientViews = createSelector(selectLocalParticipantView, selectRemoteClientViews, (localParticipant, remoteParticipants) => {
1625
+ return [...(localParticipant ? [localParticipant] : []), ...remoteParticipants];
1626
+ });
1627
+ const selectAllClientViewsInCurrentGroup = createSelector(selectAllClientViews, selectBreakoutActive, selectBreakoutCurrentId, (allClientViews, breakoutActive, breakoutCurrentId) => {
1628
+ if (!breakoutActive || !breakoutCurrentId) {
1629
+ return allClientViews;
1630
+ }
1631
+ return allClientViews.filter((client) => client.isLocalClient ||
1632
+ client.breakoutGroup === (breakoutCurrentId || "") ||
1633
+ (client.breakoutGroupAssigned && client.breakoutGroupAssigned === breakoutCurrentId));
1634
+ });
1635
+ const selectBreakoutGroupedParticipants = createSelector(selectAllClientViews, selectBreakoutActive, selectBreakoutGroups, (clientViews, breakoutActive, breakoutGroups) => {
1636
+ if (!breakoutActive || !breakoutGroups)
1637
+ return [];
1638
+ const clientsInMainRoom = clientViews.filter((client) => !client.breakoutGroup || client.breakoutGroup === "");
1639
+ const mainRoom = {
1640
+ clients: clientsInMainRoom,
1641
+ group: { id: "", name: "" },
1642
+ };
1643
+ const groups = Object.entries(breakoutGroups).map(([id, name]) => ({
1644
+ clients: clientViews.filter((client) => !client.isPresentation && client.breakoutGroup === id),
1645
+ group: { id, name },
1646
+ }));
1647
+ return [mainRoom, ...groups];
1648
+ });
1649
+
1650
+ function streamIdForClient({ isPresentation, stream }) {
1651
+ var _a, _b;
1652
+ 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";
1653
+ }
1654
+ function isClientSpotlighted({ spotlights, isPresentation, clientId, stream, }) {
1655
+ return !!spotlights.find((s) => {
1656
+ const streamId = streamIdForClient({ isPresentation, stream });
1657
+ return s.clientId === clientId && s.streamId === streamId;
1658
+ });
1659
+ }
1660
+ function mergeSpotlight(spotlights, spotlight) {
1661
+ const found = spotlights.find((s) => s.clientId === spotlight.clientId && s.streamId === spotlight.streamId);
1662
+ if (found) {
1663
+ return spotlights;
1664
+ }
1665
+ return spotlights.concat(spotlight);
1666
+ }
1667
+ function mapSpotlightsToClientViews(spotlights, clientViews) {
1668
+ return spotlights.reduce((acc, s) => {
1669
+ const clientView = clientViews.find((c) => s.clientId === c.clientId && s.streamId === streamIdForClient(c));
1670
+ if (clientView && !acc.includes(clientView)) {
1671
+ acc.push(clientView);
1672
+ }
1673
+ return acc;
1674
+ }, []);
1675
+ }
1676
+ const initialState$8 = {
1677
+ sorted: [],
1678
+ };
1679
+ const spotlightsSlice = createSlice({
1680
+ name: "spotlights",
1681
+ initialState: initialState$8,
1682
+ reducers: {
1683
+ addSpotlight(state, action) {
1684
+ const { clientId, streamId } = action.payload;
1685
+ return Object.assign(Object.assign({}, state), { sorted: mergeSpotlight(state.sorted, { clientId, streamId }) });
1686
+ },
1687
+ removeSpotlight(state, action) {
1688
+ const { clientId, streamId } = action.payload;
1689
+ return Object.assign(Object.assign({}, state), { sorted: state.sorted.filter((s) => !(s.clientId === clientId && s.streamId === streamId)) });
1690
+ },
1691
+ },
1692
+ extraReducers: (builder) => {
1693
+ builder.addCase(signalEvents.roomJoined, (state, action) => {
1694
+ if (!action.payload.room) {
1695
+ return state;
1696
+ }
1697
+ const { spotlights } = action.payload.room;
1698
+ return Object.assign(Object.assign({}, state), { sorted: spotlights });
1699
+ });
1700
+ builder.addCase(signalEvents.spotlightAdded, (state, action) => {
1701
+ const { clientId, streamId } = action.payload;
1702
+ return Object.assign(Object.assign({}, state), { sorted: mergeSpotlight(state.sorted, { clientId, streamId }) });
1703
+ });
1704
+ builder.addCase(signalEvents.spotlightRemoved, (state, action) => {
1705
+ const { clientId, streamId } = action.payload;
1706
+ return Object.assign(Object.assign({}, state), { sorted: state.sorted.filter((s) => !(s.clientId === clientId && s.streamId === streamId)) });
1707
+ });
1708
+ builder.addMatcher(isAnyOf(signalEvents.clientKicked, signalEvents.clientLeft), (state, action) => {
1709
+ const { clientId } = action.payload;
1710
+ return Object.assign(Object.assign({}, state), { sorted: state.sorted.filter((s) => s.clientId !== clientId) });
1711
+ });
1712
+ },
1713
+ });
1714
+ const { addSpotlight, removeSpotlight } = spotlightsSlice.actions;
1715
+ const doSpotlightParticipant = createAppAuthorizedThunk((state) => selectIsAuthorizedToSpotlight(state), ({ id }) => (_, getState) => {
1716
+ const state = getState();
1717
+ const clientView = selectAllClientViews(state).find((c) => c.clientId === id);
1718
+ if (!clientView) {
1719
+ return;
1720
+ }
1721
+ const { socket } = selectSignalConnectionRaw(state);
1722
+ const streamId = streamIdForClient(clientView);
1723
+ const payload = { clientId: clientView.id, streamId: streamId !== null && streamId !== void 0 ? streamId : "0" };
1724
+ socket === null || socket === void 0 ? void 0 : socket.emit("add_spotlight", payload);
1725
+ });
1726
+ const doRemoveSpotlight = createAppAuthorizedThunk((state) => selectIsAuthorizedToSpotlight(state), ({ id }) => (_, getState) => {
1505
1727
  const state = getState();
1506
- const screenshareStream = selectLocalScreenshareStream(state);
1507
- if (!screenshareStream) {
1728
+ const clientView = selectAllClientViews(state).find((c) => c.clientId === id);
1729
+ if (!clientView) {
1508
1730
  return;
1509
1731
  }
1510
- screenshareStream.getTracks().forEach((track) => track.stop());
1511
- dispatch(stopScreenshare({ stream: screenshareStream }));
1732
+ const { socket } = selectSignalConnectionRaw(state);
1733
+ const streamId = streamIdForClient(clientView);
1734
+ const payload = { clientId: clientView.id, streamId: streamId !== null && streamId !== void 0 ? streamId : "0" };
1735
+ socket === null || socket === void 0 ? void 0 : socket.emit("remove_spotlight", payload);
1736
+ });
1737
+ const selectSpotlightsRaw = (state) => state.spotlights;
1738
+ const selectSpotlights = (state) => state.spotlights.sorted;
1739
+ const selectIsLocalParticipantSpotlighted = createSelector(selectLocalParticipantRaw, selectSpotlights, (localParticipant, spotlights) => {
1740
+ return isClientSpotlighted({ clientId: localParticipant.id, stream: localParticipant.stream, spotlights });
1741
+ });
1742
+ const selectSpotlightedClientViews = createSelector(selectAllClientViews, selectSpotlights, (clientViews, spotlights) => {
1743
+ return mapSpotlightsToClientViews(spotlights, clientViews);
1512
1744
  });
1513
- const selectLocalScreenshareRaw = (state) => state.localScreenshare;
1514
- const selectLocalScreenshareStatus = (state) => state.localScreenshare.status;
1515
- const selectLocalScreenshareStream = (state) => state.localScreenshare.stream;
1516
1745
  startAppListening({
1517
- actionCreator: localMediaStopped,
1518
- effect: (_, { getState }) => {
1746
+ actionCreator: doStartScreenshare.fulfilled,
1747
+ effect: ({ payload }, { getState, dispatch }) => {
1748
+ const { stream } = payload;
1519
1749
  const state = getState();
1520
- const screenshareStream = selectLocalScreenshareStream(state);
1521
- if (!screenshareStream) {
1750
+ const localParticipant = selectLocalParticipantRaw(state);
1751
+ if (!localParticipant) {
1522
1752
  return;
1523
1753
  }
1524
- screenshareStream === null || screenshareStream === void 0 ? void 0 : screenshareStream.getTracks().forEach((track) => {
1525
- track.stop();
1526
- });
1754
+ dispatch(addSpotlight({ clientId: localParticipant.id, streamId: stream.id }));
1755
+ },
1756
+ });
1757
+ startAppListening({
1758
+ actionCreator: stopScreenshare,
1759
+ effect: ({ payload }, { getState, dispatch }) => {
1760
+ const { stream } = payload;
1761
+ const state = getState();
1762
+ const localParticipant = selectLocalParticipantRaw(state);
1763
+ if (!localParticipant) {
1764
+ return;
1765
+ }
1766
+ dispatch(removeSpotlight({ clientId: localParticipant.id, streamId: stream.id }));
1527
1767
  },
1528
1768
  });
1529
1769
 
1770
+ function isDeferrable({ client, breakoutCurrentId }) {
1771
+ if (!client)
1772
+ return false;
1773
+ if (!breakoutCurrentId && client.breakoutGroup)
1774
+ return true;
1775
+ if (!client.isAudioEnabled && !client.isVideoEnabled)
1776
+ return true;
1777
+ return false;
1778
+ }
1530
1779
  const createWebRtcEmitter = (dispatch) => {
1531
1780
  return {
1532
1781
  emit: (eventName, data) => {
@@ -1539,11 +1788,14 @@ const createWebRtcEmitter = (dispatch) => {
1539
1788
  else if (eventName === "rtc_manager_destroyed") {
1540
1789
  dispatch(rtcManagerDestroyed());
1541
1790
  }
1791
+ else if (eventName === "client_connection_status_changed") {
1792
+ dispatch(rtcEvents.clientConnectionStatusChanged(data));
1793
+ }
1542
1794
  else ;
1543
1795
  },
1544
1796
  };
1545
1797
  };
1546
- const initialState$8 = {
1798
+ const initialState$7 = {
1547
1799
  dispatcherCreated: false,
1548
1800
  error: null,
1549
1801
  isCreatingDispatcher: false,
@@ -1556,7 +1808,7 @@ const initialState$8 = {
1556
1808
  };
1557
1809
  const rtcConnectionSlice = createSlice({
1558
1810
  name: "rtcConnection",
1559
- initialState: initialState$8,
1811
+ initialState: initialState$7,
1560
1812
  reducers: {
1561
1813
  isAcceptingStreams: (state, action) => {
1562
1814
  return Object.assign(Object.assign({}, state), { isAcceptingStreams: action.payload });
@@ -1566,7 +1818,7 @@ const rtcConnectionSlice = createSlice({
1566
1818
  return Object.assign(Object.assign({}, state), { reportedStreamResolutions: Object.assign(Object.assign({}, state.reportedStreamResolutions), { [streamId]: { width, height } }) });
1567
1819
  },
1568
1820
  rtcDisconnected: () => {
1569
- return Object.assign({}, initialState$8);
1821
+ return Object.assign({}, initialState$7);
1570
1822
  },
1571
1823
  rtcDispatcherCreated: (state, action) => {
1572
1824
  return Object.assign(Object.assign({}, state), { dispatcherCreated: true, rtcManagerDispatcher: action.payload });
@@ -1580,6 +1832,19 @@ const rtcConnectionSlice = createSlice({
1580
1832
  rtcManagerInitialized: (state) => {
1581
1833
  return Object.assign(Object.assign({}, state), { rtcManagerInitialized: true });
1582
1834
  },
1835
+ rtcClientConnectionStatusChanged: {
1836
+ reducer: (state) => {
1837
+ return state;
1838
+ },
1839
+ prepare: (payload) => {
1840
+ return {
1841
+ payload: {},
1842
+ meta: {
1843
+ localParticipantId: payload.localParticipantId,
1844
+ },
1845
+ };
1846
+ },
1847
+ },
1583
1848
  },
1584
1849
  extraReducers: (builder) => {
1585
1850
  builder.addCase(socketReconnecting, (state) => {
@@ -1590,7 +1855,7 @@ const rtcConnectionSlice = createSlice({
1590
1855
  });
1591
1856
  },
1592
1857
  });
1593
- const { resolutionReported, rtcDispatcherCreated, rtcDisconnected, rtcManagerCreated, rtcManagerDestroyed, rtcManagerInitialized, isAcceptingStreams, } = rtcConnectionSlice.actions;
1858
+ const { resolutionReported, rtcDispatcherCreated, rtcDisconnected, rtcManagerCreated, rtcManagerDestroyed, rtcManagerInitialized, isAcceptingStreams, rtcClientConnectionStatusChanged, } = rtcConnectionSlice.actions;
1594
1859
  const doConnectRtc = createAppThunk(() => (dispatch, getState) => {
1595
1860
  const state = getState();
1596
1861
  const socket = selectSignalConnectionRaw(state).socket;
@@ -1607,7 +1872,9 @@ const doConnectRtc = createAppThunk(() => (dispatch, getState) => {
1607
1872
  video: isCameraEnabled,
1608
1873
  }),
1609
1874
  deferrable(clientId) {
1610
- return !clientId;
1875
+ const client = selectRemoteParticipants(getState()).find((p) => p.id === clientId);
1876
+ const breakoutCurrentId = selectBreakoutCurrentId(getState()) || "";
1877
+ return isDeferrable({ client, breakoutCurrentId });
1611
1878
  },
1612
1879
  };
1613
1880
  const rtcManagerDispatcher = new RtcManagerDispatcher({
@@ -1643,7 +1910,7 @@ const doHandleAcceptStreams = createAppThunk((payload) => (dispatch, getState) =
1643
1910
  if (!rtcManager) {
1644
1911
  throw new Error("No rtc manager");
1645
1912
  }
1646
- const activeBreakout = false;
1913
+ const activeBreakout = selectBreakoutActive(state);
1647
1914
  const shouldAcceptNewClients = (_a = rtcManager.shouldAcceptStreamsFromBothSides) === null || _a === void 0 ? void 0 : _a.call(rtcManager);
1648
1915
  const updates = [];
1649
1916
  for (const { clientId, streamId, state } of payload) {
@@ -1761,6 +2028,13 @@ startAppListening({
1761
2028
  }
1762
2029
  },
1763
2030
  });
2031
+ startAppListening({
2032
+ actionCreator: rtcEvents.clientConnectionStatusChanged,
2033
+ effect: (_, { dispatch, getState }) => {
2034
+ const localParticipant = selectLocalParticipantRaw(getState());
2035
+ dispatch(rtcClientConnectionStatusChanged({ localParticipantId: localParticipant.id }));
2036
+ },
2037
+ });
1764
2038
  const selectShouldConnectRtc = createSelector(selectRtcStatus, selectAppIsActive, selectRtcDispatcherCreated, selectRtcIsCreatingDispatcher, selectSignalConnectionSocket, (rtcStatus, appIsActive, dispatcherCreated, isCreatingDispatcher, signalSocket) => {
1765
2039
  if (appIsActive && rtcStatus === "inactive" && !dispatcherCreated && !isCreatingDispatcher && signalSocket) {
1766
2040
  return true;
@@ -1794,13 +2068,14 @@ createReactor([selectShouldDisconnectRtc], ({ dispatch }, shouldDisconnectRtc) =
1794
2068
  dispatch(doDisconnectRtc());
1795
2069
  }
1796
2070
  });
1797
- const selectStreamsToAccept = createSelector(selectRtcStatus, selectRemoteClients, (rtcStatus, remoteParticipants) => {
2071
+ const selectStreamsToAccept = createSelector(selectRtcStatus, selectRemoteClients, selectBreakoutCurrentId, selectSpotlights, (rtcStatus, remoteParticipants, breakoutCurrentId, spotlights) => {
1798
2072
  if (rtcStatus !== "ready") {
1799
2073
  return [];
1800
2074
  }
1801
2075
  const upd = [];
1802
2076
  for (const client of remoteParticipants) {
1803
2077
  const { streams, id: clientId, newJoiner } = client;
2078
+ const clientSpotlight = spotlights.find((s) => s.clientId === client.id && s.streamId === "0");
1804
2079
  for (let i = 0; i < streams.length; i++) {
1805
2080
  let streamId = streams[i].id;
1806
2081
  let state = streams[i].state;
@@ -1814,7 +2089,9 @@ const selectStreamsToAccept = createSelector(selectRtcStatus, selectRemoteClient
1814
2089
  state = streams[0].state;
1815
2090
  }
1816
2091
  }
1817
- {
2092
+ if ((!client.breakoutGroup && !breakoutCurrentId) ||
2093
+ client.breakoutGroup === breakoutCurrentId ||
2094
+ ("" === client.breakoutGroup && clientSpotlight)) {
1818
2095
  if (state === "done_accept")
1819
2096
  continue;
1820
2097
  upd.push({
@@ -1823,6 +2100,11 @@ const selectStreamsToAccept = createSelector(selectRtcStatus, selectRemoteClient
1823
2100
  state: `${newJoiner && streamId === "0" ? "new" : "to"}_accept`,
1824
2101
  });
1825
2102
  }
2103
+ else {
2104
+ if (state === "done_unaccept")
2105
+ continue;
2106
+ upd.push({ clientId, streamId, state: "to_unaccept" });
2107
+ }
1826
2108
  }
1827
2109
  }
1828
2110
  return upd;
@@ -1833,117 +2115,6 @@ createReactor([selectStreamsToAccept, selectIsAcceptingStreams], ({ dispatch },
1833
2115
  }
1834
2116
  });
1835
2117
 
1836
- function isStreamerClient(client) {
1837
- return client.roleName === "streamer";
1838
- }
1839
- function isRecorderClient(client) {
1840
- return client.roleName === "recorder";
1841
- }
1842
- const initialState$7 = {
1843
- isLocked: false,
1844
- };
1845
- const roomSlice = createSlice({
1846
- name: "room",
1847
- initialState: initialState$7,
1848
- reducers: {},
1849
- extraReducers: (builder) => {
1850
- builder.addCase(signalEvents.roomJoined, (state, action) => {
1851
- const { error, isLocked } = action.payload;
1852
- if (error) {
1853
- return state;
1854
- }
1855
- return Object.assign(Object.assign({}, state), { isLocked: Boolean(isLocked) });
1856
- });
1857
- builder.addCase(signalEvents.roomLocked, (state, action) => {
1858
- const { isLocked } = action.payload;
1859
- return Object.assign(Object.assign({}, state), { isLocked: Boolean(isLocked) });
1860
- });
1861
- },
1862
- });
1863
- const doLockRoom = createAppAuthorizedThunk((state) => selectIsAuthorizedToLockRoom(state), (payload) => (_, getState) => {
1864
- const state = getState();
1865
- const { socket } = selectSignalConnectionRaw(state);
1866
- socket === null || socket === void 0 ? void 0 : socket.emit("set_lock", { locked: payload.locked });
1867
- });
1868
- const doKickParticipant = createAppAuthorizedThunk((state) => selectIsAuthorizedToKickClient(state), (payload) => (_, getState) => {
1869
- const state = getState();
1870
- const { socket } = selectSignalConnectionRaw(state);
1871
- socket === null || socket === void 0 ? void 0 : socket.emit("kick_client", { clientId: payload.clientId, reasonId: "kick" });
1872
- });
1873
- const doEndMeeting = createAppAuthorizedThunk((state) => selectIsAuthorizedToEndMeeting(state), (payload) => (dispatch, getState) => {
1874
- const state = getState();
1875
- const clientsToKick = selectRemoteClients(state).map((c) => c.id);
1876
- if (clientsToKick.length) {
1877
- const { socket } = selectSignalConnectionRaw(state);
1878
- socket === null || socket === void 0 ? void 0 : socket.emit("kick_client", { clientIds: clientsToKick, reasonId: "end-meeting" });
1879
- }
1880
- if (!payload.stayBehind) {
1881
- dispatch(doAppStop());
1882
- }
1883
- });
1884
- const selectRoomIsLocked = (state) => state.room.isLocked;
1885
- const selectScreenshares = createSelector(selectLocalScreenshareStream, selectLocalParticipantRaw, selectRemoteParticipants, (localScreenshareStream, localParticipant, remoteParticipants) => {
1886
- const screenshares = [];
1887
- if (localScreenshareStream) {
1888
- screenshares.push({
1889
- id: localScreenshareStream.id || "local-screenshare",
1890
- participantId: "local",
1891
- hasAudioTrack: localScreenshareStream.getTracks().some((track) => track.kind === "audio"),
1892
- breakoutGroup: localParticipant.breakoutGroup,
1893
- stream: localScreenshareStream,
1894
- isLocal: true,
1895
- });
1896
- }
1897
- for (const participant of remoteParticipants) {
1898
- if (participant.presentationStream) {
1899
- screenshares.push({
1900
- id: participant.presentationStream.id || `pres-${participant.id}`,
1901
- participantId: participant.id,
1902
- hasAudioTrack: participant.presentationStream.getTracks().some((track) => track.kind === "audio"),
1903
- breakoutGroup: participant.breakoutGroup,
1904
- stream: participant.presentationStream,
1905
- isLocal: false,
1906
- });
1907
- }
1908
- }
1909
- return screenshares;
1910
- });
1911
- const selectRemoteClientViews = createSelector(selectLocalScreenshareStream, selectLocalParticipantRaw, selectRemoteParticipants, (localScreenshareStream, localParticipant, remoteParticipants) => {
1912
- const views = [];
1913
- if (localScreenshareStream) {
1914
- const isScreenshareAudioEnabled = !!localScreenshareStream.getAudioTracks().length;
1915
- views.push({
1916
- clientId: localParticipant.id,
1917
- displayName: "Your screenshare",
1918
- id: "local-screenshare",
1919
- isAudioEnabled: isScreenshareAudioEnabled,
1920
- isLocalClient: true,
1921
- isPresentation: true,
1922
- isVideoEnabled: true,
1923
- stream: localScreenshareStream,
1924
- });
1925
- }
1926
- for (const c of remoteParticipants) {
1927
- if (isStreamerClient(c) || isRecorderClient(c)) {
1928
- continue;
1929
- }
1930
- const { presentationStream } = c, clientView = __rest(c, ["presentationStream"]);
1931
- const displayName = c.displayName || "Guest";
1932
- const isPresentationActive = presentationStream && presentationStream.active;
1933
- const presentationId = "pres-" + c.id;
1934
- const isStreamActive = c.stream && c.stream.active;
1935
- const isVideoEnabled = c.isVideoEnabled;
1936
- views.push(Object.assign(Object.assign(Object.assign({}, clientView), { clientId: c.id, displayName, hasActivePresentation: !!isPresentationActive }), (c.isVideoEnabled ? { isVideoEnabled } : {})));
1937
- if (isPresentationActive) {
1938
- 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 })));
1939
- }
1940
- }
1941
- return views;
1942
- });
1943
- const selectAllClientViews = createSelector(selectLocalParticipantView, selectRemoteClientViews, (localParticipant, remoteParticipants) => {
1944
- return [...(localParticipant ? [localParticipant] : []), ...remoteParticipants];
1945
- });
1946
-
1947
2118
  const initialState$6 = {
1948
2119
  running: false,
1949
2120
  };
@@ -2014,39 +2185,178 @@ const doStartConnectionMonitor = createAppThunk(() => (dispatch, getState) => {
2014
2185
  });
2015
2186
  },
2016
2187
  });
2017
- dispatch(connectionMonitorStarted({ stopIssueSubscription: issueMonitorSubscription.stop }));
2188
+ dispatch(connectionMonitorStarted({ stopIssueSubscription: issueMonitorSubscription.stop }));
2189
+ });
2190
+ const doStopConnectionMonitor = createAppThunk(() => (dispatch, getState) => {
2191
+ const state = getState();
2192
+ const stopCallbackFn = selectStopCallbackFunction(state);
2193
+ if (stopCallbackFn) {
2194
+ stopCallbackFn();
2195
+ }
2196
+ dispatch(connectionMonitorStopped());
2197
+ });
2198
+ const selectConnectionMonitorIsRunning = (state) => state.connectionMonitor.running;
2199
+ const selectStopCallbackFunction = (state) => state.connectionMonitor.stopCallbackFunction;
2200
+ const selectShouldStartConnectionMonitor = createSelector(selectRoomConnectionStatus, selectConnectionMonitorIsRunning, (roomConnectionStatus, isRunning) => {
2201
+ if (!isRunning && roomConnectionStatus === "connected") {
2202
+ return true;
2203
+ }
2204
+ return false;
2205
+ });
2206
+ const selectShouldStopConnectionMonitor = createSelector(selectRoomConnectionStatus, selectConnectionMonitorIsRunning, (roomConnectionStatus, isRunning) => {
2207
+ if (isRunning && ["kicked", "left"].includes(roomConnectionStatus)) {
2208
+ return true;
2209
+ }
2210
+ return false;
2211
+ });
2212
+ createReactor([selectShouldStartConnectionMonitor], ({ dispatch }, shouldStartConnectionMonitor) => {
2213
+ if (shouldStartConnectionMonitor) {
2214
+ dispatch(doStartConnectionMonitor());
2215
+ }
2216
+ });
2217
+ createReactor([selectShouldStopConnectionMonitor], ({ dispatch }, shouldStartConnectionMonitor) => {
2218
+ if (shouldStartConnectionMonitor) {
2219
+ dispatch(doStopConnectionMonitor());
2220
+ }
2221
+ });
2222
+
2223
+ const initialState$5 = {
2224
+ displayName: "",
2225
+ id: "",
2226
+ breakoutGroup: null,
2227
+ breakoutGroupAssigned: "",
2228
+ isAudioEnabled: true,
2229
+ isVideoEnabled: true,
2230
+ isLocalParticipant: true,
2231
+ stream: undefined,
2232
+ isScreenSharing: false,
2233
+ roleName: "none",
2234
+ clientClaim: undefined,
2235
+ stickyReaction: undefined,
2236
+ isDialIn: false,
2237
+ };
2238
+ const localParticipantSlice = createSlice({
2239
+ name: "localParticipant",
2240
+ initialState: initialState$5,
2241
+ reducers: {
2242
+ setDisplayName: (state, action) => {
2243
+ return Object.assign(Object.assign({}, state), { displayName: action.payload.displayName });
2244
+ },
2245
+ setBreakoutGroupAssigned: (state, action) => {
2246
+ return Object.assign(Object.assign({}, state), { breakoutGroupAssigned: action.payload.breakoutGroupAssigned });
2247
+ },
2248
+ },
2249
+ extraReducers: (builder) => {
2250
+ builder.addCase(doAppStart, (state, action) => {
2251
+ return Object.assign(Object.assign({}, state), { displayName: action.payload.displayName });
2252
+ });
2253
+ builder.addCase(doEnableAudio.fulfilled, (state, action) => {
2254
+ return Object.assign(Object.assign({}, state), { isAudioEnabled: action.payload });
2255
+ });
2256
+ builder.addCase(doEnableVideo.fulfilled, (state, action) => {
2257
+ return Object.assign(Object.assign({}, state), { isVideoEnabled: action.payload });
2258
+ });
2259
+ builder.addCase(doSetLocalStickyReaction.fulfilled, (state, action) => {
2260
+ return Object.assign(Object.assign({}, state), { stickyReaction: action.payload });
2261
+ });
2262
+ builder.addCase(signalEvents.roomJoined, (state, action) => {
2263
+ var _a, _b;
2264
+ 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); });
2265
+ 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 });
2266
+ });
2267
+ builder.addCase(signalEvents.breakoutGroupJoined, (state, action) => {
2268
+ var _a, _b;
2269
+ if (((_a = action.payload) === null || _a === void 0 ? void 0 : _a.clientId) !== state.id) {
2270
+ return state;
2271
+ }
2272
+ return Object.assign(Object.assign({}, state), { breakoutGroup: (_b = action.payload) === null || _b === void 0 ? void 0 : _b.group });
2273
+ });
2274
+ },
2275
+ });
2276
+ const { setDisplayName, setBreakoutGroupAssigned } = localParticipantSlice.actions;
2277
+ const doSetDisplayName = createRoomConnectedThunk((payload) => (dispatch, getState) => {
2278
+ const state = getState();
2279
+ const socket = selectSignalConnectionRaw(state).socket;
2280
+ socket === null || socket === void 0 ? void 0 : socket.emit("send_client_metadata", {
2281
+ type: "UserData",
2282
+ payload: { displayName: payload.displayName },
2283
+ });
2284
+ dispatch(setDisplayName({ displayName: payload.displayName }));
2018
2285
  });
2019
- const doStopConnectionMonitor = createAppThunk(() => (dispatch, getState) => {
2286
+ const doEnableAudio = createAsyncRoomConnectedThunk("localParticipant/doEnableAudio", (payload, { dispatch, getState }) => __awaiter(void 0, void 0, void 0, function* () {
2020
2287
  const state = getState();
2021
- const stopCallbackFn = selectStopCallbackFunction(state);
2022
- if (stopCallbackFn) {
2023
- stopCallbackFn();
2288
+ const socket = selectSignalConnectionRaw(state).socket;
2289
+ socket === null || socket === void 0 ? void 0 : socket.emit("enable_audio", { enabled: payload.enabled });
2290
+ if (payload.enabled) {
2291
+ dispatch(doSetLocalStickyReaction({ enabled: false }));
2024
2292
  }
2025
- dispatch(connectionMonitorStopped());
2026
- });
2027
- const selectConnectionMonitorIsRunning = (state) => state.connectionMonitor.running;
2028
- const selectStopCallbackFunction = (state) => state.connectionMonitor.stopCallbackFunction;
2029
- const selectShouldStartConnectionMonitor = createSelector(selectRoomConnectionStatus, selectConnectionMonitorIsRunning, (roomConnectionStatus, isRunning) => {
2030
- if (!isRunning && roomConnectionStatus === "connected") {
2031
- return true;
2293
+ return payload.enabled;
2294
+ }));
2295
+ const doEnableVideo = createAsyncRoomConnectedThunk("localParticipant/doEnableVideo", (payload, { getState }) => __awaiter(void 0, void 0, void 0, function* () {
2296
+ const state = getState();
2297
+ const socket = selectSignalConnectionRaw(state).socket;
2298
+ socket === null || socket === void 0 ? void 0 : socket.emit("enable_video", { enabled: payload.enabled });
2299
+ return payload.enabled;
2300
+ }));
2301
+ const doSetLocalStickyReaction = createAsyncRoomConnectedThunk("localParticipant/doSetLocalStickyReaction", (payload, { getState, rejectWithValue }) => __awaiter(void 0, void 0, void 0, function* () {
2302
+ var _a;
2303
+ const state = getState();
2304
+ const currentStickyReaction = selectLocalParticipantStickyReaction(state);
2305
+ const stickyReactionCurrentlyEnabled = Boolean(currentStickyReaction);
2306
+ const enabled = (_a = payload.enabled) !== null && _a !== void 0 ? _a : !stickyReactionCurrentlyEnabled;
2307
+ if (enabled === stickyReactionCurrentlyEnabled) {
2308
+ return rejectWithValue(currentStickyReaction);
2032
2309
  }
2033
- return false;
2310
+ const stickyReaction = enabled ? { reaction: "✋", timestamp: new Date().toISOString() } : null;
2311
+ return stickyReaction;
2312
+ }));
2313
+ const doSendClientMetadata = createRoomConnectedThunk(() => (_, getState) => {
2314
+ const state = getState();
2315
+ const socket = selectSignalConnectionRaw(state).socket;
2316
+ const payload = {
2317
+ displayName: selectLocalParticipantDisplayName(state),
2318
+ stickyReaction: selectLocalParticipantStickyReaction(state),
2319
+ };
2320
+ socket === null || socket === void 0 ? void 0 : socket.emit("send_client_metadata", {
2321
+ type: "UserData",
2322
+ payload,
2323
+ });
2034
2324
  });
2035
- const selectShouldStopConnectionMonitor = createSelector(selectRoomConnectionStatus, selectConnectionMonitorIsRunning, (roomConnectionStatus, isRunning) => {
2036
- if (isRunning && ["kicked", "left"].includes(roomConnectionStatus)) {
2037
- return true;
2038
- }
2039
- return false;
2325
+ startAppListening({
2326
+ actionCreator: toggleCameraEnabled,
2327
+ effect: ({ payload }, { dispatch, getState }) => {
2328
+ const { enabled } = payload;
2329
+ const { isVideoEnabled } = selectLocalParticipantRaw(getState());
2330
+ const roomConnectionStatus = selectRoomConnectionStatus(getState());
2331
+ if (roomConnectionStatus !== "connected") {
2332
+ return;
2333
+ }
2334
+ dispatch(doEnableVideo({ enabled: enabled || !isVideoEnabled }));
2335
+ },
2040
2336
  });
2041
- createReactor([selectShouldStartConnectionMonitor], ({ dispatch }, shouldStartConnectionMonitor) => {
2042
- if (shouldStartConnectionMonitor) {
2043
- dispatch(doStartConnectionMonitor());
2337
+ startAppListening({
2338
+ actionCreator: toggleMicrophoneEnabled,
2339
+ effect: ({ payload }, { dispatch, getState }) => {
2340
+ const { enabled } = payload;
2341
+ const { isAudioEnabled } = selectLocalParticipantRaw(getState());
2342
+ const roomConnectionStatus = selectRoomConnectionStatus(getState());
2343
+ if (roomConnectionStatus !== "connected") {
2344
+ return;
2345
+ }
2346
+ dispatch(doEnableAudio({ enabled: enabled || !isAudioEnabled }));
2347
+ },
2348
+ });
2349
+ createReactor([selectLocalParticipantDisplayName, selectLocalParticipantStickyReaction, selectRoomConnectionStatus], ({ dispatch }, diplayName, stickyReaction, roomConnectionStatus) => {
2350
+ if (roomConnectionStatus === "connected") {
2351
+ dispatch(doSendClientMetadata());
2044
2352
  }
2045
2353
  });
2046
- createReactor([selectShouldStopConnectionMonitor], ({ dispatch }, shouldStartConnectionMonitor) => {
2047
- if (shouldStartConnectionMonitor) {
2048
- dispatch(doStopConnectionMonitor());
2354
+ createReactor([selectBreakoutAssignments, selectDeviceId, selectLocalParticipantRaw], ({ dispatch }, breakoutAssignments, deviceId, localParticipant) => {
2355
+ const breakoutGroupAssigned = (breakoutAssignments === null || breakoutAssignments === void 0 ? void 0 : breakoutAssignments[deviceId || ""]) || "";
2356
+ if (localParticipant.breakoutGroupAssigned === breakoutGroupAssigned) {
2357
+ return;
2049
2358
  }
2359
+ dispatch(setBreakoutGroupAssigned({ breakoutGroupAssigned }));
2050
2360
  });
2051
2361
 
2052
2362
  const emitter = new EventEmitter();
@@ -2218,13 +2528,13 @@ startAppListening({
2218
2528
  },
2219
2529
  });
2220
2530
 
2221
- const initialState$5 = {
2531
+ const initialState$4 = {
2222
2532
  data: null,
2223
2533
  isFetching: false,
2224
2534
  error: null,
2225
2535
  };
2226
2536
  const organizationSlice = createSlice({
2227
- initialState: initialState$5,
2537
+ initialState: initialState$4,
2228
2538
  name: "organization",
2229
2539
  reducers: {},
2230
2540
  extraReducers: (builder) => {
@@ -2272,13 +2582,13 @@ createReactor([selectShouldFetchOrganization], ({ dispatch }, shouldFetchOrganiz
2272
2582
  }
2273
2583
  });
2274
2584
 
2275
- const initialState$4 = {
2585
+ const initialState$3 = {
2276
2586
  session: null,
2277
2587
  status: "ready",
2278
2588
  error: null,
2279
2589
  };
2280
2590
  const roomConnectionSlice = createSlice({
2281
- initialState: initialState$4,
2591
+ initialState: initialState$3,
2282
2592
  name: "roomConnection",
2283
2593
  reducers: {
2284
2594
  connectionStatusChanged: (state, action) => {
@@ -2546,11 +2856,11 @@ const makeComparable = (value) => {
2546
2856
  return JSON.stringify(value);
2547
2857
  return value;
2548
2858
  };
2549
- const initialState$3 = {
2859
+ const initialState$2 = {
2550
2860
  reportedValues: {},
2551
2861
  };
2552
2862
  const rtcAnalyticsSlice = createSlice({
2553
- initialState: initialState$3,
2863
+ initialState: initialState$2,
2554
2864
  name: "rtcAnalytics",
2555
2865
  reducers: {
2556
2866
  updateReportedValues(state, action) {
@@ -2610,126 +2920,6 @@ createReactor([selectRtcManagerInitialized], ({ dispatch }, selectRtcManagerInit
2610
2920
  }
2611
2921
  });
2612
2922
 
2613
- function streamIdForClient({ isPresentation, stream }) {
2614
- var _a, _b;
2615
- 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";
2616
- }
2617
- function isClientSpotlighted({ spotlights, isPresentation, clientId, stream, }) {
2618
- return !!spotlights.find((s) => {
2619
- const streamId = streamIdForClient({ isPresentation, stream });
2620
- return s.clientId === clientId && s.streamId === streamId;
2621
- });
2622
- }
2623
- function mergeSpotlight(spotlights, spotlight) {
2624
- const found = spotlights.find((s) => s.clientId === spotlight.clientId && s.streamId === spotlight.streamId);
2625
- if (found) {
2626
- return spotlights;
2627
- }
2628
- return spotlights.concat(spotlight);
2629
- }
2630
- function mapSpotlightsToClientViews(spotlights, clientViews) {
2631
- return spotlights.reduce((acc, s) => {
2632
- const clientView = clientViews.find((c) => s.clientId === c.clientId && s.streamId === streamIdForClient(c));
2633
- if (clientView && !acc.includes(clientView)) {
2634
- acc.push(clientView);
2635
- }
2636
- return acc;
2637
- }, []);
2638
- }
2639
- const initialState$2 = {
2640
- sorted: [],
2641
- };
2642
- const spotlightsSlice = createSlice({
2643
- name: "spotlights",
2644
- initialState: initialState$2,
2645
- reducers: {
2646
- addSpotlight(state, action) {
2647
- const { clientId, streamId } = action.payload;
2648
- return Object.assign(Object.assign({}, state), { sorted: mergeSpotlight(state.sorted, { clientId, streamId }) });
2649
- },
2650
- removeSpotlight(state, action) {
2651
- const { clientId, streamId } = action.payload;
2652
- return Object.assign(Object.assign({}, state), { sorted: state.sorted.filter((s) => !(s.clientId === clientId && s.streamId === streamId)) });
2653
- },
2654
- },
2655
- extraReducers: (builder) => {
2656
- builder.addCase(signalEvents.roomJoined, (state, action) => {
2657
- if (!action.payload.room) {
2658
- return state;
2659
- }
2660
- const { spotlights } = action.payload.room;
2661
- return Object.assign(Object.assign({}, state), { sorted: spotlights });
2662
- });
2663
- builder.addCase(signalEvents.spotlightAdded, (state, action) => {
2664
- const { clientId, streamId } = action.payload;
2665
- return Object.assign(Object.assign({}, state), { sorted: mergeSpotlight(state.sorted, { clientId, streamId }) });
2666
- });
2667
- builder.addCase(signalEvents.spotlightRemoved, (state, action) => {
2668
- const { clientId, streamId } = action.payload;
2669
- return Object.assign(Object.assign({}, state), { sorted: state.sorted.filter((s) => !(s.clientId === clientId && s.streamId === streamId)) });
2670
- });
2671
- builder.addMatcher(isAnyOf(signalEvents.clientKicked, signalEvents.clientLeft), (state, action) => {
2672
- const { clientId } = action.payload;
2673
- return Object.assign(Object.assign({}, state), { sorted: state.sorted.filter((s) => s.clientId !== clientId) });
2674
- });
2675
- },
2676
- });
2677
- const { addSpotlight, removeSpotlight } = spotlightsSlice.actions;
2678
- const doSpotlightParticipant = createAppAuthorizedThunk((state) => selectIsAuthorizedToSpotlight(state), ({ id }) => (_, getState) => {
2679
- const state = getState();
2680
- const clientView = selectAllClientViews(state).find((c) => c.clientId === id);
2681
- if (!clientView) {
2682
- return;
2683
- }
2684
- const { socket } = selectSignalConnectionRaw(state);
2685
- const streamId = streamIdForClient(clientView);
2686
- const payload = { clientId: clientView.id, streamId: streamId !== null && streamId !== void 0 ? streamId : "0" };
2687
- socket === null || socket === void 0 ? void 0 : socket.emit("add_spotlight", payload);
2688
- });
2689
- const doRemoveSpotlight = createAppAuthorizedThunk((state) => selectIsAuthorizedToSpotlight(state), ({ id }) => (_, getState) => {
2690
- const state = getState();
2691
- const clientView = selectAllClientViews(state).find((c) => c.clientId === id);
2692
- if (!clientView) {
2693
- return;
2694
- }
2695
- const { socket } = selectSignalConnectionRaw(state);
2696
- const streamId = streamIdForClient(clientView);
2697
- const payload = { clientId: clientView.id, streamId: streamId !== null && streamId !== void 0 ? streamId : "0" };
2698
- socket === null || socket === void 0 ? void 0 : socket.emit("remove_spotlight", payload);
2699
- });
2700
- const selectSpotlightsRaw = (state) => state.spotlights;
2701
- const selectSpotlights = (state) => state.spotlights.sorted;
2702
- const selectIsLocalParticipantSpotlighted = createSelector(selectLocalParticipantRaw, selectSpotlights, (localParticipant, spotlights) => {
2703
- return isClientSpotlighted({ clientId: localParticipant.id, stream: localParticipant.stream, spotlights });
2704
- });
2705
- const selectSpotlightedClientViews = createSelector(selectAllClientViews, selectSpotlights, (clientViews, spotlights) => {
2706
- return mapSpotlightsToClientViews(spotlights, clientViews);
2707
- });
2708
- startAppListening({
2709
- actionCreator: doStartScreenshare.fulfilled,
2710
- effect: ({ payload }, { getState, dispatch }) => {
2711
- const { stream } = payload;
2712
- const state = getState();
2713
- const localParticipant = selectLocalParticipantRaw(state);
2714
- if (!localParticipant) {
2715
- return;
2716
- }
2717
- dispatch(addSpotlight({ clientId: localParticipant.id, streamId: stream.id }));
2718
- },
2719
- });
2720
- startAppListening({
2721
- actionCreator: stopScreenshare,
2722
- effect: ({ payload }, { getState, dispatch }) => {
2723
- const { stream } = payload;
2724
- const state = getState();
2725
- const localParticipant = selectLocalParticipantRaw(state);
2726
- if (!localParticipant) {
2727
- return;
2728
- }
2729
- dispatch(removeSpotlight({ clientId: localParticipant.id, streamId: stream.id }));
2730
- },
2731
- });
2732
-
2733
2923
  const initialState$1 = {
2734
2924
  isStreaming: false,
2735
2925
  error: null,
@@ -2808,6 +2998,7 @@ const IS_DEV = (_a = undefined === "true") !== null && _a !== void 0 ? _a : fals
2808
2998
  const appReducer = combineReducers({
2809
2999
  app: appSlice.reducer,
2810
3000
  authorization: authorizationSlice.reducer,
3001
+ breakout: breakoutSlice.reducer,
2811
3002
  chat: chatSlice.reducer,
2812
3003
  cloudRecording: cloudRecordingSlice.reducer,
2813
3004
  connectionMonitor: connectionMonitorSlice.reducer,
@@ -3875,4 +4066,4 @@ function createServices() {
3875
4066
  };
3876
4067
  }
3877
4068
 
3878
- 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, 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 };
4069
+ export { ApiClient, Credentials, CredentialsService, LocalParticipant, OrganizationApiClient, OrganizationService, OrganizationServiceCache, RoomService, addAppListener, addSpotlight, appSlice, authorizationSlice, breakoutSlice, chatSlice, cloudRecordingSlice, connectionMonitorSlice, connectionMonitorStarted, connectionMonitorStopped, createAppAsyncThunk, createAppAuthorizedThunk, createAppThunk, createAsyncRoomConnectedThunk, createAuthorizedRoomConnectedThunk, createReactor, createRemoteParticipant, createRoomConnectedThunk, createServices, createStore, createWebRtcEmitter, debounce, deviceBusy, deviceCredentialsSlice, deviceIdentified, deviceIdentifying, doAcceptWaitingParticipant, doAppStart, doAppStop, doBreakoutJoin, doClearNotifications, doConnectRoom, doConnectRtc, doDisconnectRtc, doEnableAudio, doEnableVideo, doEndMeeting, doGetDeviceCredentials, doHandleAcceptStreams, doHandleStreamingStarted, doHandleStreamingStopped, doKickParticipant, doKnockRoom, doLockRoom, doOrganizationFetch, doRejectWaitingParticipant, doRemoveSpotlight, doRequestAudioEnable, doRequestVideoEnable, doRtcAnalyticsCustomEventsInitialize, doRtcManagerCreated, doRtcManagerInitialize, doRtcReportStreamResolution, doSendChatMessage, doSendClientMetadata, doSetDevice, doSetDisplayName, doSetLocalStickyReaction, doSetNotification, doSignalConnect, doSignalDisconnect, doSignalIdentifyDevice, doSpotlightParticipant, doStartCloudRecording, doStartConnectionMonitor, doStartLocalMedia, doStartScreenshare, doStopCloudRecording, doStopConnectionMonitor, doStopLocalMedia, doStopScreenshare, doSwitchLocalStream, doToggleCamera, doToggleLowDataMode, doUpdateDeviceList, getAudioTrack, getFakeMediaStream, getVideoTrack, hasValue, initialCloudRecordingState, initialLocalMediaState, initialNotificationsState, initialState$h as initialState, isAcceptingStreams, isClientSpotlighted, listenerMiddleware, localMediaSlice, localMediaStopped, localParticipantSlice, localScreenshareSlice, localStreamMetadataUpdated, notificationsSlice, observeStore, organizationSlice, parseUnverifiedRoomKeyData, participantStreamAdded, participantStreamIdAdded, recordingRequestStarted, remoteParticipantsSlice, removeSpotlight, resolutionReported, roomConnectionSlice, roomSlice, rootReducer, rtcAnalyticsCustomEvents, rtcAnalyticsSlice, rtcClientConnectionStatusChanged, rtcConnectionSlice, rtcDisconnected, rtcDispatcherCreated, rtcManagerCreated, rtcManagerDestroyed, rtcManagerInitialized, selectAllClientViews, selectAllClientViewsInCurrentGroup, selectAppDisplayName, selectAppExternalId, selectAppInitialConfig, selectAppIsActive, selectAppIsDialIn, selectAppIsNodeSdk, selectAppRaw, selectAppRoomName, selectAppRoomUrl, selectAppUserAgent, selectAuthorizationRoleName, selectBreakoutActive, selectBreakoutAssignments, selectBreakoutCurrentGroup, selectBreakoutCurrentId, selectBreakoutGroupedParticipants, selectBreakoutGroups, selectBreakoutInitiatedBy, selectBreakoutRaw, selectBusyDeviceIds, selectCameraDeviceError, selectCameraDevices, selectChatMessages, selectChatRaw, selectCloudRecordingError, selectCloudRecordingRaw, selectCloudRecordingStartedAt, selectCloudRecordingStatus, selectConnectionMonitorIsRunning, selectCurrentCameraDeviceId, selectCurrentMicrophoneDeviceId, selectCurrentSpeakerDeviceId, selectDeviceCredentialsRaw, selectDeviceId, selectHasFetchedDeviceCredentials, selectIsAcceptingStreams, selectIsAuthorizedToAskToSpeak, selectIsAuthorizedToEndMeeting, selectIsAuthorizedToKickClient, selectIsAuthorizedToLockRoom, selectIsAuthorizedToRequestAudioEnable, selectIsAuthorizedToRequestVideoEnable, selectIsAuthorizedToSpotlight, selectIsCameraEnabled, selectIsCloudRecording, selectIsLocalMediaStarting, selectIsLocalParticipantSpotlighted, selectIsLowDataModeEnabled, selectIsMicrophoneEnabled, selectIsSettingCameraDevice, selectIsSettingMicrophoneDevice, selectIsToggleCamera, selectLocalMediaConstraintsOptions, selectLocalMediaDevices, selectLocalMediaIsSwitchingStream, selectLocalMediaOptions, selectLocalMediaOwnsStream, selectLocalMediaRaw, selectLocalMediaShouldStartWithOptions, selectLocalMediaShouldStop, selectLocalMediaStartError, selectLocalMediaStatus, selectLocalMediaStream, selectLocalParticipantBreakoutAssigned, selectLocalParticipantBreakoutGroup, selectLocalParticipantClientClaim, selectLocalParticipantDisplayName, selectLocalParticipantIsScreenSharing, selectLocalParticipantRaw, selectLocalParticipantStickyReaction, selectLocalParticipantView, selectLocalScreenshareRaw, selectLocalScreenshareStatus, selectLocalScreenshareStream, selectMicrophoneDeviceError, selectMicrophoneDevices, selectNotificationsEmitter, selectNotificationsEvents, selectNotificationsRaw, selectNumClients, selectNumParticipants, selectOrganizationId, selectOrganizationRaw, selectRemoteClientViews, selectRemoteClients, selectRemoteParticipants, selectRemoteParticipantsRaw, selectRoomConnectionError, selectRoomConnectionRaw, selectRoomConnectionSession, selectRoomConnectionSessionId, selectRoomConnectionStatus, selectRoomIsLocked, selectRoomKey, selectRtcConnectionRaw, selectRtcDispatcherCreated, selectRtcIsCreatingDispatcher, selectRtcManager, selectRtcManagerInitialized, selectRtcStatus, selectScreenshares, selectSelfId, selectShouldConnectRoom, selectShouldConnectRtc, selectShouldConnectSignal, selectShouldDisconnectRtc, selectShouldFetchDeviceCredentials, selectShouldFetchOrganization, selectShouldIdentifyDevice, selectShouldInitializeRtc, selectShouldStartConnectionMonitor, selectShouldStopConnectionMonitor, selectSignalConnectionDeviceIdentified, selectSignalConnectionRaw, selectSignalConnectionSocket, selectSignalIsIdentifyingDevice, selectSignalStatus, selectSpeakerDevices, selectSpotlightedClientViews, selectSpotlights, selectSpotlightsRaw, selectStopCallbackFunction, selectStreamingRaw, selectStreamsToAccept, selectWaitingParticipants, selectWaitingParticipantsRaw, setBreakoutGroupAssigned, setCurrentCameraDeviceId, setCurrentMicrophoneDeviceId, setCurrentSpeakerDeviceId, setDisplayName, setLocalMediaOptions, setLocalMediaStream, setRoomKey, signalConnectionSlice, signalEvents, socketConnected, socketConnecting, socketDisconnected, socketReconnecting, spotlightsSlice, startAppListening, stopScreenshare, streamIdForClient, streamStatusUpdated, streamingSlice, toggleCameraEnabled, toggleLowDataModeEnabled, toggleMicrophoneEnabled, updateReportedValues, waitingParticipantsSlice };