@whereby.com/core 0.31.4 → 0.32.0

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